1. /*
  2. * @(#)Reference.java 1.26 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.lang.ref;
  8. /**
  9. * Abstract base class for reference objects. This class defines the
  10. * operations common to all reference objects. Because reference objects are
  11. * implemented in close cooperation with the garbage collector, this class may
  12. * not be subclassed directly.
  13. *
  14. * @version 1.26, 01/11/29
  15. * @author Mark Reinhold
  16. * @since JDK1.2
  17. */
  18. public abstract class Reference {
  19. /* A Reference instance is in one of four possible internal states:
  20. *
  21. * Active: Subject to special treatment by the garbage collector. Some
  22. * time after the collector detects that the reachability of the
  23. * referent has changed to the appropriate state, it changes the
  24. * instance's state to either Pending or Inactive, depending upon
  25. * whether or not the instance was registered with a queue when it was
  26. * created. In the former case it also adds the instance to the
  27. * pending-Reference list. Newly-created instances are Active unless
  28. * their referents are null, in which case they are Inactive.
  29. *
  30. * Pending: An element of the pending-Reference list, waiting to be
  31. * enqueued by the Reference-handler thread. Unregistered instances
  32. * are never in this state.
  33. *
  34. * Enqueued: An element of the queue with which the instance was
  35. * registered when it was created. When an instance is removed from
  36. * its ReferenceQueue, it is made Inactive. Unregistered instances are
  37. * never in this state.
  38. *
  39. * Inactive: Nothing more to do. Once an instance becomes Inactive its
  40. * state will never change again.
  41. *
  42. * The state is encoded in the queue and next fields as follows:
  43. *
  44. * Active: queue = ReferenceQueue with which instance is registered, or
  45. * ReferenceQueue.NULL if it was not registered with a queue; next =
  46. * null.
  47. *
  48. * Pending: queue = ReferenceQueue with which instance is registered;
  49. * next = Following instance in queue, or this if at end of list.
  50. *
  51. * Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance
  52. * in queue, or this if at end of list.
  53. *
  54. * Inactive: queue = ReferenceQueue.NULL; next = this.
  55. *
  56. * With this scheme the collector need only examine the next field in order
  57. * to determine whether a Reference instance requires special treatment: If
  58. * the next field is null then the instance is active; if it is non-null,
  59. * then the collector should treat the instance normally.
  60. */
  61. private Object referent; /* Treated specially by GC */
  62. ReferenceQueue queue;
  63. Reference next;
  64. /* Object used to synchronize with the garbage collector. The collector
  65. * must acquire this lock at the beginning of each collection cycle. It is
  66. * therefore critical that any code holding this lock complete as quickly
  67. * as possible, allocate no new objects, and avoid calling user code.
  68. */
  69. static private class Lock { };
  70. private static Lock lock = new Lock();
  71. /* List of References waiting to be enqueued. The collector adds
  72. * References to this list, while the Reference-handler thread removes
  73. * them. This list is protected by the above lock object.
  74. */
  75. private static Reference pending = null;
  76. /* High-priority thread to enqueue pending References
  77. */
  78. private static class ReferenceHandler extends Thread {
  79. ReferenceHandler(ThreadGroup g, String name) {
  80. super(g, name);
  81. }
  82. public void run() {
  83. for (;;) {
  84. Reference r;
  85. synchronized (lock) {
  86. if (pending != null) {
  87. r = pending;
  88. Reference rn = r.next;
  89. pending = (rn == r) ? null : rn;
  90. r.next = r;
  91. } else {
  92. try {
  93. lock.wait();
  94. } catch (InterruptedException x) { }
  95. continue;
  96. }
  97. }
  98. ReferenceQueue q = r.queue;
  99. if (q != ReferenceQueue.NULL) q.enqueue(r);
  100. }
  101. }
  102. }
  103. static {
  104. ThreadGroup tg = Thread.currentThread().getThreadGroup();
  105. for (ThreadGroup tgn = tg;
  106. tgn != null;
  107. tg = tgn, tgn = tg.getParent());
  108. Thread handler = new ReferenceHandler(tg, "Reference Handler");
  109. /* If there were a special system-only priority greater than
  110. * MAX_PRIORITY, it would be used here
  111. */
  112. handler.setPriority(Thread.MAX_PRIORITY);
  113. handler.setDaemon(true);
  114. handler.start();
  115. }
  116. /* -- Referent accessor and setters -- */
  117. /**
  118. * Returns this reference object's referent. If this reference object has
  119. * been cleared, either by the program or by the garbage collector, then
  120. * this method returns <code>null</code>.
  121. *
  122. * @return The object to which this reference refers, or
  123. * <code>null</code> if this reference object has been cleared
  124. */
  125. public Object get() {
  126. return this.referent;
  127. }
  128. /**
  129. * Clears this reference object. Invoking this method will not cause this
  130. * object to be enqueued.
  131. */
  132. public void clear() {
  133. this.referent = null;
  134. }
  135. /* -- Queue operations -- */
  136. /**
  137. * Tells whether or not this reference object has been enqueued, either by
  138. * the program or by the garbage collector. If this reference object was
  139. * not registered with a queue when it was created, then this method will
  140. * always return <code>false</code>.
  141. *
  142. * @return <code>true</code> if and only if this reference object has
  143. * been enqueued
  144. */
  145. public boolean isEnqueued() {
  146. /* In terms of the internal states, this predicate actually tests
  147. whether the instance is either Pending or Enqueued */
  148. synchronized (this) {
  149. return (this.queue != ReferenceQueue.NULL) && (this.next != null);
  150. }
  151. }
  152. /**
  153. * Adds this reference object to the queue with which it is registered,
  154. * if any.
  155. *
  156. * @return <code>true</code> if this reference object was successfully
  157. * enqueued; <code>false</code> if it was already enqueued or if
  158. * it was not registered with a queue when it was created
  159. */
  160. public boolean enqueue() {
  161. return this.queue.enqueue(this);
  162. }
  163. /* -- Constructors -- */
  164. Reference(Object referent) {
  165. this(referent, null);
  166. }
  167. Reference(Object referent, ReferenceQueue queue) {
  168. this.referent = referent;
  169. if (referent == null) {
  170. /* Immediately make this instance inactive */
  171. this.queue = ReferenceQueue.NULL;
  172. this.next = this;
  173. } else {
  174. this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
  175. this.next = null;
  176. }
  177. }
  178. }