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