1 /* 2 * Copyright (c) 2011, 2014, 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 /* 27 * To change this template, choose Tools | Templates 28 * and open the template in the editor. 29 */ 30 31 package com.sun.webkit; 32 33 import java.lang.ref.ReferenceQueue; 34 import java.lang.ref.WeakReference; 35 import java.security.PrivilegedAction; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.HashSet; 39 import java.util.Set; 40 import java.util.concurrent.LinkedBlockingQueue; 41 42 /** 43 * This class is used for registering and disposing the native 44 * data associated with java objects. 45 * 46 * The object can register itself by calling the addRecord method and 47 * providing a descendant of the DisposerRecord class with overridden 48 * dispose() method. 49 * 50 * When the object becomes unreachable, the dispose() method 51 * of the associated DisposerRecord object will be called. 52 * 53 * @see DisposerRecord 54 */ 55 public final class Disposer implements Runnable { 56 private static final ReferenceQueue queue = new ReferenceQueue(); 57 private static final Disposer disposerInstance = new Disposer(); 58 private static final Set<WeakDisposerRecord> records = 59 new HashSet<WeakDisposerRecord>(); 60 61 static { 62 java.security.AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 63 /* 64 * The thread must be a member of a thread group 65 * which will not get GCed before VM exit. 66 * Make its parent the top-level thread group. 67 */ 68 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 69 for (ThreadGroup tgn = tg; 70 tgn != null; 71 tg = tgn, tgn = tg.getParent()); 72 73 Thread t = new Thread(tg, disposerInstance, "Disposer"); 74 t.setDaemon(true); 75 t.setPriority(Thread.MAX_PRIORITY); 76 t.start(); 77 return null; 78 }); 79 } 80 81 /** 82 * Registers the object and the native data for later disposal. 83 * @param target Object to be registered 84 * @param rec the associated DisposerRecord object 85 * @see DisposerRecord 86 */ 87 public static void addRecord(Object target, DisposerRecord rec) { 88 disposerInstance.add(target, rec); 89 } 90 91 /** 92 * Performs the actual registration of the target object to be disposed. 93 * @param target Object to be registered 94 * @param rec the associated DisposerRecord object 95 * @see DisposerRecord 96 */ 97 private synchronized void add(Object target, DisposerRecord rec) { 98 records.add(new WeakDisposerRecord(target, rec)); 99 } 100 101 public void run() { 102 while (true) { 103 try { 104 WeakDisposerRecord obj = (WeakDisposerRecord) queue.remove(); 105 obj.clear(); 106 DisposerRunnable.getInstance().enqueue(obj); 107 } catch (Exception e) { 108 System.out.println("Exception while removing reference: " + e); 109 e.printStackTrace(); 110 } 111 } 112 } 113 114 private static final class DisposerRunnable implements Runnable { 115 private static final DisposerRunnable theInstance = new DisposerRunnable(); 116 117 private static DisposerRunnable getInstance() { 118 return theInstance; 119 } 120 121 private boolean isRunning = false; 122 private final Object disposerLock = new Object(); 123 private final LinkedBlockingQueue<WeakDisposerRecord> disposerQueue 124 = new LinkedBlockingQueue<WeakDisposerRecord>(); 125 126 private void enqueueAll(Collection<WeakDisposerRecord> objs) { 127 synchronized (disposerLock) { 128 disposerQueue.addAll(objs); 129 if (!isRunning) { 130 Invoker.getInvoker().invokeOnEventThread(this); 131 isRunning = true; 132 } 133 } 134 } 135 136 private void enqueue(WeakDisposerRecord obj) { 137 enqueueAll(Arrays.asList(obj)); 138 } 139 140 @Override public void run() { 141 while (true) { 142 WeakDisposerRecord obj; 143 synchronized (disposerLock) { 144 obj = disposerQueue.poll(); 145 if (obj == null) { 146 isRunning = false; 147 break; 148 } 149 } 150 // Check if the object has not yet been removed & disposed. 151 if (records.contains(obj)) { 152 records.remove(obj); 153 obj.dispose(); 154 } 155 } 156 } 157 } 158 159 public static class WeakDisposerRecord 160 extends WeakReference 161 implements DisposerRecord 162 { 163 protected WeakDisposerRecord(Object referent) { 164 super(referent, Disposer.queue); 165 this.record = null; 166 } 167 168 private WeakDisposerRecord(Object referent, DisposerRecord record) { 169 super(referent, Disposer.queue); 170 this.record = record; 171 } 172 173 private final DisposerRecord record; 174 175 @Override 176 public void dispose() { 177 record.dispose(); 178 } 179 } 180 }