Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.rmi/share/classes/sun/rmi/transport/DGCClient.java
41154 views
1
/*
2
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package sun.rmi.transport;
26
27
import java.io.InvalidClassException;
28
import java.lang.ref.PhantomReference;
29
import java.lang.ref.ReferenceQueue;
30
import java.net.SocketPermission;
31
import java.rmi.UnmarshalException;
32
import java.security.AccessController;
33
import java.security.PrivilegedAction;
34
import java.util.HashMap;
35
import java.util.HashSet;
36
import java.util.Iterator;
37
import java.util.List;
38
import java.util.Map;
39
import java.util.Set;
40
import java.rmi.ConnectException;
41
import java.rmi.RemoteException;
42
import java.rmi.dgc.DGC;
43
import java.rmi.dgc.Lease;
44
import java.rmi.dgc.VMID;
45
import java.rmi.server.ObjID;
46
47
import sun.rmi.runtime.Log;
48
import sun.rmi.runtime.NewThreadAction;
49
import sun.rmi.server.UnicastRef;
50
import sun.rmi.server.Util;
51
52
import java.security.AccessControlContext;
53
import java.security.Permissions;
54
import java.security.ProtectionDomain;
55
56
/**
57
* DGCClient implements the client-side of the RMI distributed garbage
58
* collection system.
59
*
60
* The external interface to DGCClient is the "registerRefs" method.
61
* When a LiveRef to a remote object enters the VM, it needs to be
62
* registered with the DGCClient to participate in distributed garbage
63
* collection.
64
*
65
* When the first LiveRef to a particular remote object is registered,
66
* a "dirty" call is made to the server-side distributed garbage
67
* collector for the remote object, which returns a lease guaranteeing
68
* that the server-side DGC will not collect the remote object for a
69
* certain period of time. While LiveRef instances to remote objects
70
* on a particular server exist, the DGCClient periodically sends more
71
* "dirty" calls to renew its lease.
72
*
73
* The DGCClient tracks the local reachability of registered LiveRef
74
* instances (using phantom references). When the LiveRef instance
75
* for a particular remote object becomes garbage collected locally,
76
* a "clean" call is made to the server-side distributed garbage
77
* collector, indicating that the server no longer needs to keep the
78
* remote object alive for this client.
79
*
80
* @see java.rmi.dgc.DGC, sun.rmi.transport.DGCImpl
81
*
82
* @author Ann Wollrath
83
* @author Peter Jones
84
*/
85
@SuppressWarnings("removal")
86
final class DGCClient {
87
88
/** next sequence number for DGC calls (access synchronized on class) */
89
private static long nextSequenceNum = Long.MIN_VALUE;
90
91
/** unique identifier for this VM as a client of DGC */
92
private static VMID vmid = new VMID();
93
94
/** lease duration to request (usually ignored by server) */
95
private static final long leaseValue = // default 10 minutes
96
AccessController.doPrivileged((PrivilegedAction<Long>) () ->
97
Long.getLong("java.rmi.dgc.leaseValue", 600000));
98
99
/** maximum interval between retries of failed clean calls */
100
private static final long cleanInterval = // default 3 minutes
101
AccessController.doPrivileged((PrivilegedAction<Long>) () ->
102
Long.getLong("sun.rmi.dgc.cleanInterval", 180000));
103
104
/** maximum interval between complete garbage collections of local heap */
105
private static final long gcInterval = // default 1 hour
106
AccessController.doPrivileged((PrivilegedAction<Long>) () ->
107
Long.getLong("sun.rmi.dgc.client.gcInterval", 3600000));
108
109
/** minimum retry count for dirty calls that fail */
110
private static final int dirtyFailureRetries = 5;
111
112
/** retry count for clean calls that fail with ConnectException */
113
private static final int cleanFailureRetries = 5;
114
115
/** constant empty ObjID array for lease renewal optimization */
116
private static final ObjID[] emptyObjIDArray = new ObjID[0];
117
118
/** ObjID for server-side DGC object */
119
private static final ObjID dgcID = new ObjID(ObjID.DGC_ID);
120
121
/**
122
* An AccessControlContext with only socket permissions,
123
* suitable for an RMIClientSocketFactory.
124
*/
125
private static final AccessControlContext SOCKET_ACC;
126
static {
127
Permissions perms = new Permissions();
128
perms.add(new SocketPermission("*", "connect,resolve"));
129
ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
130
SOCKET_ACC = new AccessControlContext(pd);
131
}
132
133
/*
134
* Disallow anyone from creating one of these.
135
*/
136
private DGCClient() {}
137
138
/**
139
* Register the LiveRef instances in the supplied list to participate
140
* in distributed garbage collection.
141
*
142
* All of the LiveRefs in the list must be for remote objects at the
143
* given endpoint.
144
*/
145
static void registerRefs(Endpoint ep, List<LiveRef> refs) {
146
/*
147
* Look up the given endpoint and register the refs with it.
148
* The retrieved entry may get removed from the global endpoint
149
* table before EndpointEntry.registerRefs() is able to acquire
150
* its lock; in this event, it returns false, and we loop and
151
* try again.
152
*/
153
EndpointEntry epEntry;
154
do {
155
epEntry = EndpointEntry.lookup(ep);
156
} while (!epEntry.registerRefs(refs));
157
}
158
159
/**
160
* Get the next sequence number to be used for a dirty or clean
161
* operation from this VM. This method should only be called while
162
* synchronized on the EndpointEntry whose data structures the
163
* operation affects.
164
*/
165
private static synchronized long getNextSequenceNum() {
166
return nextSequenceNum++;
167
}
168
169
/**
170
* Given the length of a lease and the time that it was granted,
171
* compute the absolute time at which it should be renewed, giving
172
* room for reasonable computational and communication delays.
173
*/
174
private static long computeRenewTime(long grantTime, long duration) {
175
/*
176
* REMIND: This algorithm should be more sophisticated, waiting
177
* a longer fraction of the lease duration for longer leases.
178
*/
179
return grantTime + (duration / 2);
180
}
181
182
/**
183
* EndpointEntry encapsulates the client-side DGC information specific
184
* to a particular Endpoint. Of most significance is the table that
185
* maps LiveRef value to RefEntry objects and the renew/clean thread
186
* that handles asynchronous client-side DGC operations.
187
*/
188
private static class EndpointEntry {
189
190
/** the endpoint that this entry is for */
191
private Endpoint endpoint;
192
/** synthesized reference to the remote server-side DGC */
193
private DGC dgc;
194
195
/** table of refs held for endpoint: maps LiveRef to RefEntry */
196
private Map<LiveRef, RefEntry> refTable = new HashMap<>(5);
197
/** set of RefEntry instances from last (failed) dirty call */
198
private Set<RefEntry> invalidRefs = new HashSet<>(5);
199
200
/** true if this entry has been removed from the global table */
201
private boolean removed = false;
202
203
/** absolute time to renew current lease to this endpoint */
204
private long renewTime = Long.MAX_VALUE;
205
/** absolute time current lease to this endpoint will expire */
206
private long expirationTime = Long.MIN_VALUE;
207
/** count of recent dirty calls that have failed */
208
private int dirtyFailures = 0;
209
/** absolute time of first recent failed dirty call */
210
private long dirtyFailureStartTime;
211
/** (average) elapsed time for recent failed dirty calls */
212
private long dirtyFailureDuration;
213
214
/** renew/clean thread for handling lease renewals and clean calls */
215
private Thread renewCleanThread;
216
/** true if renew/clean thread may be interrupted */
217
private boolean interruptible = false;
218
219
/** reference queue for phantom references */
220
private ReferenceQueue<LiveRef> refQueue = new ReferenceQueue<>();
221
/** set of clean calls that need to be made */
222
private Set<CleanRequest> pendingCleans = new HashSet<>(5);
223
224
/** global endpoint table: maps Endpoint to EndpointEntry */
225
private static Map<Endpoint,EndpointEntry> endpointTable = new HashMap<>(5);
226
/** handle for GC latency request (for future cancellation) */
227
private static GC.LatencyRequest gcLatencyRequest = null;
228
229
/**
230
* Look up the EndpointEntry for the given Endpoint. An entry is
231
* created if one does not already exist.
232
*/
233
public static EndpointEntry lookup(Endpoint ep) {
234
synchronized (endpointTable) {
235
EndpointEntry entry = endpointTable.get(ep);
236
if (entry == null) {
237
entry = new EndpointEntry(ep);
238
endpointTable.put(ep, entry);
239
/*
240
* While we are tracking live remote references registered
241
* in this VM, request a maximum latency for inspecting the
242
* entire heap from the local garbage collector, to place
243
* an upper bound on the time to discover remote references
244
* that have become unreachable (see bugid 4171278).
245
*/
246
if (gcLatencyRequest == null) {
247
gcLatencyRequest = GC.requestLatency(gcInterval);
248
}
249
}
250
return entry;
251
}
252
}
253
254
private EndpointEntry(final Endpoint endpoint) {
255
this.endpoint = endpoint;
256
try {
257
LiveRef dgcRef = new LiveRef(dgcID, endpoint, false);
258
dgc = (DGC) Util.createProxy(DGCImpl.class,
259
new UnicastRef(dgcRef), true);
260
} catch (RemoteException e) {
261
throw new Error("internal error creating DGC stub");
262
}
263
renewCleanThread = AccessController.doPrivileged(
264
new NewThreadAction(new RenewCleanThread(),
265
"RenewClean-" + endpoint, true));
266
renewCleanThread.start();
267
}
268
269
/**
270
* Register the LiveRef instances in the supplied list to participate
271
* in distributed garbage collection.
272
*
273
* This method returns false if this entry was removed from the
274
* global endpoint table (because it was empty) before these refs
275
* could be registered. In that case, a new EndpointEntry needs
276
* to be looked up.
277
*
278
* This method must NOT be called while synchronized on this entry.
279
*/
280
public boolean registerRefs(List<LiveRef> refs) {
281
assert !Thread.holdsLock(this);
282
283
Set<RefEntry> refsToDirty = null; // entries for refs needing dirty
284
long sequenceNum; // sequence number for dirty call
285
286
synchronized (this) {
287
if (removed) {
288
return false;
289
}
290
291
Iterator<LiveRef> iter = refs.iterator();
292
while (iter.hasNext()) {
293
LiveRef ref = iter.next();
294
assert ref.getEndpoint().equals(endpoint);
295
296
RefEntry refEntry = refTable.get(ref);
297
if (refEntry == null) {
298
LiveRef refClone = (LiveRef) ref.clone();
299
refEntry = new RefEntry(refClone);
300
refTable.put(refClone, refEntry);
301
if (refsToDirty == null) {
302
refsToDirty = new HashSet<>(5);
303
}
304
refsToDirty.add(refEntry);
305
}
306
307
refEntry.addInstanceToRefSet(ref);
308
}
309
310
if (refsToDirty == null) {
311
return true;
312
}
313
314
refsToDirty.addAll(invalidRefs);
315
invalidRefs.clear();
316
317
sequenceNum = getNextSequenceNum();
318
}
319
320
makeDirtyCall(refsToDirty, sequenceNum);
321
return true;
322
}
323
324
/**
325
* Remove the given RefEntry from the ref table. If that makes
326
* the ref table empty, remove this entry from the global endpoint
327
* table.
328
*
329
* This method must ONLY be called while synchronized on this entry.
330
*/
331
private void removeRefEntry(RefEntry refEntry) {
332
assert Thread.holdsLock(this);
333
assert !removed;
334
assert refTable.containsKey(refEntry.getRef());
335
336
refTable.remove(refEntry.getRef());
337
invalidRefs.remove(refEntry);
338
if (refTable.isEmpty()) {
339
synchronized (endpointTable) {
340
endpointTable.remove(endpoint);
341
Transport transport = endpoint.getOutboundTransport();
342
transport.free(endpoint);
343
/*
344
* If there are no longer any live remote references
345
* registered, we are no longer concerned with the
346
* latency of local garbage collection here.
347
*/
348
if (endpointTable.isEmpty()) {
349
assert gcLatencyRequest != null;
350
gcLatencyRequest.cancel();
351
gcLatencyRequest = null;
352
}
353
removed = true;
354
}
355
}
356
}
357
358
/**
359
* Make a DGC dirty call to this entry's endpoint, for the ObjIDs
360
* corresponding to the given set of refs and with the given
361
* sequence number.
362
*
363
* This method must NOT be called while synchronized on this entry.
364
*/
365
private void makeDirtyCall(Set<RefEntry> refEntries, long sequenceNum) {
366
assert !Thread.holdsLock(this);
367
368
ObjID[] ids;
369
if (refEntries != null) {
370
ids = createObjIDArray(refEntries);
371
} else {
372
ids = emptyObjIDArray;
373
}
374
375
long startTime = System.currentTimeMillis();
376
try {
377
Lease lease =
378
dgc.dirty(ids, sequenceNum, new Lease(vmid, leaseValue));
379
long duration = lease.getValue();
380
381
long newRenewTime = computeRenewTime(startTime, duration);
382
long newExpirationTime = startTime + duration;
383
384
synchronized (this) {
385
dirtyFailures = 0;
386
setRenewTime(newRenewTime);
387
expirationTime = newExpirationTime;
388
}
389
390
} catch (Exception e) {
391
long endTime = System.currentTimeMillis();
392
393
synchronized (this) {
394
dirtyFailures++;
395
396
if (e instanceof UnmarshalException
397
&& e.getCause() instanceof InvalidClassException) {
398
DGCImpl.dgcLog.log(Log.BRIEF, "InvalidClassException exception in DGC dirty call", e);
399
return; // protocol error, do not register these refs
400
}
401
402
if (dirtyFailures == 1) {
403
/*
404
* If this was the first recent failed dirty call,
405
* reschedule another one immediately, in case there
406
* was just a transient network problem, and remember
407
* the start time and duration of this attempt for
408
* future calculations of the delays between retries.
409
*/
410
dirtyFailureStartTime = startTime;
411
dirtyFailureDuration = endTime - startTime;
412
setRenewTime(endTime);
413
} else {
414
/*
415
* For each successive failed dirty call, wait for a
416
* (binary) exponentially increasing delay before
417
* retrying, to avoid network congestion.
418
*/
419
int n = dirtyFailures - 2;
420
if (n == 0) {
421
/*
422
* Calculate the initial retry delay from the
423
* average time elapsed for each of the first
424
* two failed dirty calls. The result must be
425
* at least 1000ms, to prevent a tight loop.
426
*/
427
dirtyFailureDuration =
428
Math.max((dirtyFailureDuration +
429
(endTime - startTime)) >> 1, 1000);
430
}
431
long newRenewTime =
432
endTime + (dirtyFailureDuration << n);
433
434
/*
435
* Continue if the last known held lease has not
436
* expired, or else at least a fixed number of times,
437
* or at least until we've tried for a fixed amount
438
* of time (the default lease value we request).
439
*/
440
if (newRenewTime < expirationTime ||
441
dirtyFailures < dirtyFailureRetries ||
442
newRenewTime < dirtyFailureStartTime + leaseValue)
443
{
444
setRenewTime(newRenewTime);
445
} else {
446
/*
447
* Give up: postpone lease renewals until next
448
* ref is registered for this endpoint.
449
*/
450
setRenewTime(Long.MAX_VALUE);
451
}
452
}
453
454
if (refEntries != null) {
455
/*
456
* Add all of these refs to the set of refs for this
457
* endpoint that may be invalid (this VM may not be in
458
* the server's referenced set), so that we will
459
* attempt to explicitly dirty them again in the
460
* future.
461
*/
462
invalidRefs.addAll(refEntries);
463
464
/*
465
* Record that a dirty call has failed for all of these
466
* refs, so that clean calls for them in the future
467
* will be strong.
468
*/
469
Iterator<RefEntry> iter = refEntries.iterator();
470
while (iter.hasNext()) {
471
RefEntry refEntry = iter.next();
472
refEntry.markDirtyFailed();
473
}
474
}
475
476
/*
477
* If the last known held lease will have expired before
478
* the next renewal, all refs might be invalid.
479
*/
480
if (renewTime >= expirationTime) {
481
invalidRefs.addAll(refTable.values());
482
}
483
}
484
}
485
}
486
487
/**
488
* Set the absolute time at which the lease for this entry should
489
* be renewed.
490
*
491
* This method must ONLY be called while synchronized on this entry.
492
*/
493
private void setRenewTime(long newRenewTime) {
494
assert Thread.holdsLock(this);
495
496
if (newRenewTime < renewTime) {
497
renewTime = newRenewTime;
498
if (interruptible) {
499
AccessController.doPrivileged(
500
new PrivilegedAction<Void>() {
501
public Void run() {
502
renewCleanThread.interrupt();
503
return null;
504
}
505
});
506
}
507
} else {
508
renewTime = newRenewTime;
509
}
510
}
511
512
/**
513
* RenewCleanThread handles the asynchronous client-side DGC activity
514
* for this entry: renewing the leases and making clean calls.
515
*/
516
private class RenewCleanThread implements Runnable {
517
518
public void run() {
519
do {
520
long timeToWait;
521
RefEntry.PhantomLiveRef phantom = null;
522
boolean needRenewal = false;
523
Set<RefEntry> refsToDirty = null;
524
long sequenceNum = Long.MIN_VALUE;
525
526
synchronized (EndpointEntry.this) {
527
/*
528
* Calculate time to block (waiting for phantom
529
* reference notifications). It is the time until the
530
* lease renewal should be done, bounded on the low
531
* end by 1 ms so that the reference queue will always
532
* get processed, and if there are pending clean
533
* requests (remaining because some clean calls
534
* failed), bounded on the high end by the maximum
535
* clean call retry interval.
536
*/
537
long timeUntilRenew =
538
renewTime - System.currentTimeMillis();
539
timeToWait = Math.max(timeUntilRenew, 1);
540
if (!pendingCleans.isEmpty()) {
541
timeToWait = Math.min(timeToWait, cleanInterval);
542
}
543
544
/*
545
* Set flag indicating that it is OK to interrupt this
546
* thread now, such as if a earlier lease renewal time
547
* is set, because we are only going to be blocking
548
* and can deal with interrupts.
549
*/
550
interruptible = true;
551
}
552
553
try {
554
/*
555
* Wait for the duration calculated above for any of
556
* our phantom references to be enqueued.
557
*/
558
phantom = (RefEntry.PhantomLiveRef)
559
refQueue.remove(timeToWait);
560
} catch (InterruptedException e) {
561
}
562
563
synchronized (EndpointEntry.this) {
564
/*
565
* Set flag indicating that it is NOT OK to interrupt
566
* this thread now, because we may be undertaking I/O
567
* operations that should not be interrupted (and we
568
* will not be blocking arbitrarily).
569
*/
570
interruptible = false;
571
Thread.interrupted(); // clear interrupted state
572
573
/*
574
* If there was a phantom reference enqueued, process
575
* it and all the rest on the queue, generating
576
* clean requests as necessary.
577
*/
578
if (phantom != null) {
579
processPhantomRefs(phantom);
580
}
581
582
/*
583
* Check if it is time to renew this entry's lease.
584
*/
585
long currentTime = System.currentTimeMillis();
586
if (currentTime > renewTime) {
587
needRenewal = true;
588
if (!invalidRefs.isEmpty()) {
589
refsToDirty = invalidRefs;
590
invalidRefs = new HashSet<>(5);
591
}
592
sequenceNum = getNextSequenceNum();
593
}
594
}
595
596
boolean needRenewal_ = needRenewal;
597
Set<RefEntry> refsToDirty_ = refsToDirty;
598
long sequenceNum_ = sequenceNum;
599
AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
600
if (needRenewal_) {
601
makeDirtyCall(refsToDirty_, sequenceNum_);
602
}
603
604
if (!pendingCleans.isEmpty()) {
605
makeCleanCalls();
606
}
607
return null;
608
}, SOCKET_ACC);
609
} while (!removed || !pendingCleans.isEmpty());
610
}
611
}
612
613
/**
614
* Process the notification of the given phantom reference and any
615
* others that are on this entry's reference queue. Each phantom
616
* reference is removed from its RefEntry's ref set. All ref
617
* entries that have no more registered instances are collected
618
* into up to two batched clean call requests: one for refs
619
* requiring a "strong" clean call, and one for the rest.
620
*
621
* This method must ONLY be called while synchronized on this entry.
622
*/
623
private void processPhantomRefs(RefEntry.PhantomLiveRef phantom) {
624
assert Thread.holdsLock(this);
625
626
Set<RefEntry> strongCleans = null;
627
Set<RefEntry> normalCleans = null;
628
629
do {
630
RefEntry refEntry = phantom.getRefEntry();
631
refEntry.removeInstanceFromRefSet(phantom);
632
if (refEntry.isRefSetEmpty()) {
633
if (refEntry.hasDirtyFailed()) {
634
if (strongCleans == null) {
635
strongCleans = new HashSet<>(5);
636
}
637
strongCleans.add(refEntry);
638
} else {
639
if (normalCleans == null) {
640
normalCleans = new HashSet<>(5);
641
}
642
normalCleans.add(refEntry);
643
}
644
removeRefEntry(refEntry);
645
}
646
} while ((phantom =
647
(RefEntry.PhantomLiveRef) refQueue.poll()) != null);
648
649
if (strongCleans != null) {
650
pendingCleans.add(
651
new CleanRequest(createObjIDArray(strongCleans),
652
getNextSequenceNum(), true));
653
}
654
if (normalCleans != null) {
655
pendingCleans.add(
656
new CleanRequest(createObjIDArray(normalCleans),
657
getNextSequenceNum(), false));
658
}
659
}
660
661
/**
662
* CleanRequest holds the data for the parameters of a clean call
663
* that needs to be made.
664
*/
665
private static class CleanRequest {
666
667
final ObjID[] objIDs;
668
final long sequenceNum;
669
final boolean strong;
670
671
/** how many times this request has failed */
672
int failures = 0;
673
674
CleanRequest(ObjID[] objIDs, long sequenceNum, boolean strong) {
675
this.objIDs = objIDs;
676
this.sequenceNum = sequenceNum;
677
this.strong = strong;
678
}
679
}
680
681
/**
682
* Make all of the clean calls described by the clean requests in
683
* this entry's set of "pending cleans". Clean requests for clean
684
* calls that succeed are removed from the "pending cleans" set.
685
*
686
* This method must NOT be called while synchronized on this entry.
687
*/
688
private void makeCleanCalls() {
689
assert !Thread.holdsLock(this);
690
691
Iterator<CleanRequest> iter = pendingCleans.iterator();
692
while (iter.hasNext()) {
693
CleanRequest request = iter.next();
694
try {
695
dgc.clean(request.objIDs, request.sequenceNum, vmid,
696
request.strong);
697
iter.remove();
698
} catch (Exception e) {
699
/*
700
* Many types of exceptions here could have been
701
* caused by a transient failure, so try again a
702
* few times, but not forever.
703
*/
704
if (++request.failures >= cleanFailureRetries) {
705
iter.remove();
706
}
707
}
708
}
709
}
710
711
/**
712
* Create an array of ObjIDs (needed for the DGC remote calls)
713
* from the ids in the given set of refs.
714
*/
715
private static ObjID[] createObjIDArray(Set<RefEntry> refEntries) {
716
ObjID[] ids = new ObjID[refEntries.size()];
717
Iterator<RefEntry> iter = refEntries.iterator();
718
for (int i = 0; i < ids.length; i++) {
719
ids[i] = iter.next().getRef().getObjID();
720
}
721
return ids;
722
}
723
724
/**
725
* RefEntry encapsulates the client-side DGC information specific
726
* to a particular LiveRef value. In particular, it contains a
727
* set of phantom references to all of the instances of the LiveRef
728
* value registered in the system (but not garbage collected
729
* locally).
730
*/
731
private class RefEntry {
732
733
/** LiveRef value for this entry (not a registered instance) */
734
private LiveRef ref;
735
/** set of phantom references to registered instances */
736
private Set<PhantomLiveRef> refSet = new HashSet<>(5);
737
/** true if a dirty call containing this ref has failed */
738
private boolean dirtyFailed = false;
739
740
public RefEntry(LiveRef ref) {
741
this.ref = ref;
742
}
743
744
/**
745
* Return the LiveRef value for this entry (not a registered
746
* instance).
747
*/
748
public LiveRef getRef() {
749
return ref;
750
}
751
752
/**
753
* Add a LiveRef to the set of registered instances for this entry.
754
*
755
* This method must ONLY be invoked while synchronized on this
756
* RefEntry's EndpointEntry.
757
*/
758
public void addInstanceToRefSet(LiveRef ref) {
759
assert Thread.holdsLock(EndpointEntry.this);
760
assert ref.equals(this.ref);
761
762
/*
763
* Only keep a phantom reference to the registered instance,
764
* so that it can be garbage collected normally (and we can be
765
* notified when that happens).
766
*/
767
refSet.add(new PhantomLiveRef(ref));
768
}
769
770
/**
771
* Remove a PhantomLiveRef from the set of registered instances.
772
*
773
* This method must ONLY be invoked while synchronized on this
774
* RefEntry's EndpointEntry.
775
*/
776
public void removeInstanceFromRefSet(PhantomLiveRef phantom) {
777
assert Thread.holdsLock(EndpointEntry.this);
778
assert refSet.contains(phantom);
779
refSet.remove(phantom);
780
}
781
782
/**
783
* Return true if there are no registered LiveRef instances for
784
* this entry still reachable in this VM.
785
*
786
* This method must ONLY be invoked while synchronized on this
787
* RefEntry's EndpointEntry.
788
*/
789
public boolean isRefSetEmpty() {
790
assert Thread.holdsLock(EndpointEntry.this);
791
return refSet.size() == 0;
792
}
793
794
/**
795
* Record that a dirty call that explicitly contained this
796
* entry's ref has failed.
797
*
798
* This method must ONLY be invoked while synchronized on this
799
* RefEntry's EndpointEntry.
800
*/
801
public void markDirtyFailed() {
802
assert Thread.holdsLock(EndpointEntry.this);
803
dirtyFailed = true;
804
}
805
806
/**
807
* Return true if a dirty call that explicitly contained this
808
* entry's ref has failed (and therefore a clean call for this
809
* ref needs to be marked "strong").
810
*
811
* This method must ONLY be invoked while synchronized on this
812
* RefEntry's EndpointEntry.
813
*/
814
public boolean hasDirtyFailed() {
815
assert Thread.holdsLock(EndpointEntry.this);
816
return dirtyFailed;
817
}
818
819
/**
820
* PhantomLiveRef is a PhantomReference to a LiveRef instance,
821
* used to detect when the LiveRef becomes permanently
822
* unreachable in this VM.
823
*/
824
private class PhantomLiveRef extends PhantomReference<LiveRef> {
825
826
public PhantomLiveRef(LiveRef ref) {
827
super(ref, EndpointEntry.this.refQueue);
828
}
829
830
public RefEntry getRefEntry() {
831
return RefEntry.this;
832
}
833
}
834
}
835
}
836
}
837
838