1/*
2 * Copyright (c) 2002, 2015, 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 sun.java2d;
27
28import sun.awt.util.ThreadGroupUtils;
29
30import java.lang.ref.Reference;
31import java.lang.ref.ReferenceQueue;
32import java.lang.ref.PhantomReference;
33import java.lang.ref.WeakReference;
34import java.security.AccessController;
35import java.security.PrivilegedAction;
36import java.util.ArrayList;
37import java.util.Hashtable;
38
39/**
40 * This class is used for registering and disposing the native
41 * data associated with java objects.
42 *
43 * The object can register itself by calling one of the addRecord
44 * methods and providing either the pointer to the native disposal
45 * method or a descendant of the DisposerRecord class with overridden
46 * dispose() method.
47 *
48 * When the object becomes unreachable, the dispose() method
49 * of the associated DisposerRecord object will be called.
50 *
51 * @see DisposerRecord
52 */
53public class Disposer implements Runnable {
54    private static final ReferenceQueue<Object> queue = new ReferenceQueue<>();
55    private static final Hashtable<java.lang.ref.Reference<Object>, DisposerRecord> records =
56        new Hashtable<>();
57
58    private static Disposer disposerInstance;
59    public static final int WEAK = 0;
60    public static final int PHANTOM = 1;
61    public static int refType = PHANTOM;
62
63    static {
64        java.security.AccessController.doPrivileged(
65            new java.security.PrivilegedAction<Void>() {
66                public Void run() {
67                    System.loadLibrary("awt");
68                    return null;
69                }
70            });
71        initIDs();
72        String type = java.security.AccessController.doPrivileged(
73                new sun.security.action.GetPropertyAction("sun.java2d.reftype"));
74        if (type != null) {
75            if (type.equals("weak")) {
76                refType = WEAK;
77                System.err.println("Using WEAK refs");
78            } else {
79                refType = PHANTOM;
80                System.err.println("Using PHANTOM refs");
81            }
82        }
83        disposerInstance = new Disposer();
84        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
85            String name = "Java2D Disposer";
86            ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
87            Thread t = new Thread(rootTG, disposerInstance, name, 0, false);
88            t.setContextClassLoader(null);
89            t.setDaemon(true);
90            t.setPriority(Thread.MAX_PRIORITY);
91            t.start();
92            return null;
93        });
94    }
95
96    /**
97     * Registers the object and the native data for later disposal.
98     * @param target Object to be registered
99     * @param disposeMethod pointer to the native disposal method
100     * @param pData pointer to the data to be passed to the
101     *              native disposal method
102     */
103    public static void addRecord(Object target,
104                                 long disposeMethod, long pData)
105    {
106        disposerInstance.add(target,
107                             new DefaultDisposerRecord(disposeMethod, pData));
108    }
109
110    /**
111     * Registers the object and the native data for later disposal.
112     * @param target Object to be registered
113     * @param rec the associated DisposerRecord object
114     * @see DisposerRecord
115     */
116    public static void addRecord(Object target, DisposerRecord rec) {
117        disposerInstance.add(target, rec);
118    }
119
120    /**
121     * Performs the actual registration of the target object to be disposed.
122     * @param target Object to be registered, or if target is an instance
123     *               of DisposerTarget, its associated disposer referent
124     *               will be the Object that is registered
125     * @param rec the associated DisposerRecord object
126     * @see DisposerRecord
127     */
128    synchronized void add(Object target, DisposerRecord rec) {
129        if (target instanceof DisposerTarget) {
130            target = ((DisposerTarget)target).getDisposerReferent();
131        }
132        java.lang.ref.Reference<Object> ref;
133        if (refType == PHANTOM) {
134            ref = new PhantomReference<>(target, queue);
135        } else {
136            ref = new WeakReference<>(target, queue);
137        }
138        records.put(ref, rec);
139    }
140
141    public void run() {
142        while (true) {
143            try {
144                Object obj = queue.remove();
145                ((Reference)obj).clear();
146                DisposerRecord rec = records.remove(obj);
147                rec.dispose();
148                obj = null;
149                rec = null;
150                clearDeferredRecords();
151            } catch (Exception e) {
152                System.out.println("Exception while removing reference.");
153            }
154        }
155    }
156
157    /*
158     * This is a marker interface that, if implemented, means it
159     * doesn't acquire any special locks, and is safe to
160     * be disposed in the poll loop on whatever thread
161     * which happens to be the Toolkit thread, is in use.
162     */
163    public static interface PollDisposable {
164    };
165
166    private static ArrayList<DisposerRecord> deferredRecords = null;
167
168    private static void clearDeferredRecords() {
169        if (deferredRecords == null || deferredRecords.isEmpty()) {
170            return;
171        }
172        for (int i=0;i<deferredRecords.size(); i++) {
173            try {
174                DisposerRecord rec = deferredRecords.get(i);
175                rec.dispose();
176            } catch (Exception e) {
177                System.out.println("Exception while disposing deferred rec.");
178            }
179        }
180        deferredRecords.clear();
181    }
182
183    /*
184     * Set to indicate the queue is presently being polled.
185     */
186    public static volatile boolean pollingQueue = false;
187
188    /*
189     * The pollRemove() method is called back from a dispose method
190     * that is running on the toolkit thread and wants to
191     * dispose any pending refs that are safe to be disposed
192     * on that thread.
193     */
194    public static void pollRemove() {
195
196        /* This should never be called recursively, so this check
197         * is just a safeguard against the unexpected.
198         */
199        if (pollingQueue) {
200            return;
201        }
202        Object obj;
203        pollingQueue = true;
204        int freed = 0;
205        int deferred = 0;
206        try {
207            while ( freed < 10000 && deferred < 100 &&
208                    (obj = queue.poll()) != null ) {
209                freed++;
210                ((Reference)obj).clear();
211                DisposerRecord rec = records.remove(obj);
212                if (rec instanceof PollDisposable) {
213                    rec.dispose();
214                    obj = null;
215                    rec = null;
216                } else {
217                    if (rec == null) { // shouldn't happen, but just in case.
218                        continue;
219                    }
220                    deferred++;
221                    if (deferredRecords == null) {
222                      deferredRecords = new ArrayList<DisposerRecord>(5);
223                    }
224                    deferredRecords.add(rec);
225                }
226            }
227        } catch (Exception e) {
228            System.out.println("Exception while removing reference.");
229        } finally {
230            pollingQueue = false;
231        }
232    }
233
234    private static native void initIDs();
235
236    /*
237     * This was added for use by the 2D font implementation to avoid creation
238     * of an additional disposer thread.
239     * WARNING: this thread class monitors a specific queue, so a reference
240     * added here must have been created with this queue. Failure to do
241     * so will clutter the records hashmap and no one will be cleaning up
242     * the reference queue.
243     */
244    @SuppressWarnings("unchecked")
245    public static void addReference(Reference<Object> ref, DisposerRecord rec) {
246        records.put(ref, rec);
247    }
248
249    public static void addObjectRecord(Object obj, DisposerRecord rec) {
250        records.put(new WeakReference<>(obj, queue) , rec);
251    }
252
253    /* This is intended for use in conjunction with addReference(..)
254     */
255    public static ReferenceQueue<Object> getQueue() {
256        return queue;
257    }
258
259}
260