1 /*
   2  * Copyright (c) 2015, 2016, 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 
  26 package jdk.internal.ref;
  27 
  28 import java.lang.ref.Cleaner;
  29 import java.lang.ref.Reference;
  30 import java.lang.ref.SoftReference;
  31 import java.util.Objects;
  32 
  33 /**
  34  * SoftCleanable subclasses efficiently encapsulate cleanup state and
  35  * the cleaning action.
  36  * Subclasses implement the abstract {@link #performCleanup()}  method
  37  * to provide the cleaning action.
  38  * When constructed, the object reference and the {@link Cleaner.Cleanable Cleanable}
  39  * are registered with the {@link Cleaner}.
  40  * The Cleaner invokes {@link Cleaner.Cleanable#clean() clean} after the
  41  * referent becomes softly reachable.
  42  */
  43 public abstract class SoftCleanable<T> extends SoftReference<T>
  44         implements Cleaner.Cleanable {
  45 
  46     /**
  47      * Links to previous and next in a doubly-linked list.
  48      */
  49     SoftCleanable<?> prev = this, next = this;
  50 
  51     /**
  52      * The list of SoftCleanable; synchronizes insert and remove.
  53      */
  54     private final SoftCleanable<?> list;
  55 
  56     /**
  57      * Constructs new {@code SoftCleanableReference} with
  58      * {@code non-null referent} and {@code non-null cleaner}.
  59      * The {@code cleaner} is not retained by this reference; it is only used
  60      * to register the newly constructed {@link Cleaner.Cleanable Cleanable}.
  61      *
  62      * @param referent the referent to track
  63      * @param cleaner  the {@code Cleaner} to register with
  64      */
  65     public SoftCleanable(T referent, Cleaner cleaner) {
  66         super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
  67         list = CleanerImpl.getCleanerImpl(cleaner).softCleanableList;
  68         insert();
  69 
  70         // Ensure referent and cleaner remain accessible
  71         Reference.reachabilityFence(referent);
  72         Reference.reachabilityFence(cleaner);
  73     }
  74 
  75     /**
  76      * Construct a new root of the list; not inserted.
  77      */
  78     SoftCleanable() {
  79         super(null, null);
  80         this.list = this;
  81     }
  82 
  83     /**
  84      * Insert this SoftCleanableReference after the list head.
  85      */
  86     private void insert() {
  87         synchronized (list) {
  88             prev = list;
  89             next = list.next;
  90             next.prev = this;
  91             list.next = this;
  92         }
  93     }
  94 
  95     /**
  96      * Remove this SoftCleanableReference from the list.
  97      *
  98      * @return true if Cleanable was removed or false if not because
  99      * it had already been removed before
 100      */
 101     private boolean remove() {
 102         synchronized (list) {
 103             if (next != this) {
 104                 next.prev = prev;
 105                 prev.next = next;
 106                 prev = this;
 107                 next = this;
 108                 return true;
 109             }
 110             return false;
 111         }
 112     }
 113 
 114     /**
 115      * Returns true if the list's next reference refers to itself.
 116      *
 117      * @return true if the list is empty
 118      */
 119     boolean isListEmpty() {
 120         synchronized (list) {
 121             return list == list.next;
 122         }
 123     }
 124 
 125     /**
 126      * Unregister this SoftCleanable reference and invoke {@link #performCleanup()},
 127      * ensuring at-most-once semantics.
 128      */
 129     @Override
 130     public final void clean() {
 131         if (remove()) {
 132             super.clear();
 133             performCleanup();
 134         }
 135     }
 136 
 137     /**
 138      * Unregister this SoftCleanable and clear the reference.
 139      * Due to inherent concurrency, {@link #performCleanup()} may still be invoked.
 140      */
 141     @Override
 142     public void clear() {
 143         if (remove()) {
 144             super.clear();
 145         }
 146     }
 147 
 148     /**
 149      * The {@code performCleanup} abstract method is overridden
 150      * to implement the cleaning logic.
 151      * The {@code performCleanup} method should not be called except
 152      * by the {@link #clean} method which ensures at most once semantics.
 153      */
 154     protected abstract void performCleanup();
 155 
 156     /**
 157      * This method always throws {@link UnsupportedOperationException}.
 158      * Enqueuing details of {@link Cleaner.Cleanable}
 159      * are a private implementation detail.
 160      *
 161      * @throws UnsupportedOperationException always
 162      */
 163     @Override
 164     public final boolean isEnqueued() {
 165         throw new UnsupportedOperationException("isEnqueued");
 166     }
 167 
 168     /**
 169      * This method always throws {@link UnsupportedOperationException}.
 170      * Enqueuing details of {@link Cleaner.Cleanable}
 171      * are a private implementation detail.
 172      *
 173      * @throws UnsupportedOperationException always
 174      */
 175     @Override
 176     public final boolean enqueue() {
 177         throw new UnsupportedOperationException("enqueue");
 178     }
 179 }