/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.sun.webkit; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; /** * This class is used for registering and disposing the native * data associated with java objects. * * The object can register itself by calling the addRecord method and * providing a descendant of the DisposerRecord class with overridden * dispose() method. * * When the object becomes unreachable, the dispose() method * of the associated DisposerRecord object will be called. * * @see DisposerRecord */ public final class Disposer implements Runnable { private static final ReferenceQueue queue = new ReferenceQueue(); private static final Disposer disposerInstance = new Disposer(); private static final Set records = new HashSet(); static { java.security.AccessController.doPrivileged((PrivilegedAction) () -> { /* * The thread must be a member of a thread group * which will not get GCed before VM exit. * Make its parent the top-level thread group. */ ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); Thread t = new Thread(tg, disposerInstance, "Disposer"); t.setDaemon(true); t.setPriority(Thread.MAX_PRIORITY); t.start(); return null; }); } /** * Registers the object and the native data for later disposal. * @param target Object to be registered * @param rec the associated DisposerRecord object * @see DisposerRecord */ public static void addRecord(Object target, DisposerRecord rec) { disposerInstance.add(target, rec); } /** * Performs the actual registration of the target object to be disposed. * @param target Object to be registered * @param rec the associated DisposerRecord object * @see DisposerRecord */ private synchronized void add(Object target, DisposerRecord rec) { records.add(new WeakDisposerRecord(target, rec)); } public void run() { while (true) { try { WeakDisposerRecord obj = (WeakDisposerRecord) queue.remove(); obj.clear(); DisposerRunnable.getInstance().enqueue(obj); } catch (Exception e) { System.out.println("Exception while removing reference: " + e); e.printStackTrace(); } } } private static final class DisposerRunnable implements Runnable { private static final DisposerRunnable theInstance = new DisposerRunnable(); private static DisposerRunnable getInstance() { return theInstance; } private boolean isRunning = false; private final Object disposerLock = new Object(); private final LinkedBlockingQueue disposerQueue = new LinkedBlockingQueue(); private void enqueueAll(Collection objs) { synchronized (disposerLock) { disposerQueue.addAll(objs); if (!isRunning) { Invoker.getInvoker().invokeOnEventThread(this); isRunning = true; } } } private void enqueue(WeakDisposerRecord obj) { enqueueAll(Arrays.asList(obj)); } @Override public void run() { while (true) { WeakDisposerRecord obj; synchronized (disposerLock) { obj = disposerQueue.poll(); if (obj == null) { isRunning = false; break; } } // Check if the object has not yet been removed & disposed. if (records.contains(obj)) { records.remove(obj); obj.dispose(); } } } } public static class WeakDisposerRecord extends WeakReference implements DisposerRecord { protected WeakDisposerRecord(Object referent) { super(referent, Disposer.queue); this.record = null; } private WeakDisposerRecord(Object referent, DisposerRecord record) { super(referent, Disposer.queue); this.record = record; } private final DisposerRecord record; @Override public void dispose() { record.dispose(); } } }