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 }