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