/*
 * Copyright (c) 2010, 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.
 */

package com.sun.media.jfxmediaimpl;

import com.sun.media.jfxmedia.logging.Logger;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

This guy sits and waits for PhantomReferences to appear in it's queue and invokes associated disposers when they do.
/** * This guy sits and waits for PhantomReferences to appear in it's queue and * invokes associated disposers when they do. */
public class MediaDisposer {
Common interface for objects that can be disposed of directly.
/** * Common interface for objects that can be disposed of directly. */
public static interface Disposable {
dispose() is called when an associated object is garbage collected and no longer reachable. Note that the associated object cannot be accessed by the time this method is invoked.
/** * dispose() is called when an associated object is garbage collected * and no longer reachable. Note that the associated object cannot be * accessed by the time this method is invoked. */
public void dispose(); }
Common interface for objects that can dispose of other objects.
/** * Common interface for objects that can dispose of other objects. */
public static interface ResourceDisposer {
Dispose the provided resource.
Params:
  • resource – the resource that needs to be disposed of.
/** * Dispose the provided resource. * @param resource the resource that needs to be disposed of. */
public void disposeResource(Object resource); }
Register a resource to be dispose of when the referent becomes collected.
Params:
  • referent – when this object is garbage collected, the resource will be disposed
  • resource – generic object to be disposed, this is passed as-is as an argument to the disposer
  • disposer – resource disposer class instance that will be used to dispose the provided resource
/** * Register a resource to be dispose of when the referent becomes collected. * @param referent when this object is garbage collected, the resource will * be disposed * @param resource generic object to be disposed, this is passed as-is as an * argument to the disposer * @param disposer resource disposer class instance that will be used to * dispose the provided resource */
public static void addResourceDisposer(Object referent, Object resource, ResourceDisposer disposer) { disposinator().implAddResourceDisposer(referent, resource, disposer); }
Remove ALL previously registered resource disposers associated with the given resource. You use this when the resource will be otherwise safely disposed of and no longer need to rely on this disposer to clean up.
Params:
  • resource – the resource that was previously registered, equals() is used to determine if it's the same object or not
/** * Remove ALL previously registered resource disposers associated with the * given resource. You use this when the resource will be otherwise safely * disposed of and no longer need to rely on this disposer to clean up. * @param resource the resource that was previously registered, * {@code equals()} is used to determine if it's the same object or not */
public static void removeResourceDisposer(Object resource) { disposinator().implRemoveResourceDisposer(resource); }
Registers a disposable object that will be disposed when the referent is garbage collected.
Params:
  • referent – the object that the disposable object is associated with
  • disposable – the object to be disposed when referent is collected
/** * Registers a disposable object that will be disposed when the referent is * garbage collected. * @param referent the object that the disposable object is associated with * @param disposable the object to be disposed when referent is collected */
public static void addDisposable(Object referent, Disposable disposable) { disposinator().implAddDisposable(referent, disposable); } private final ReferenceQueue<Object> purgatory; private final Map<Reference,Disposable> disposers; private static MediaDisposer theDisposinator; private static synchronized MediaDisposer disposinator() { if (null == theDisposinator) { theDisposinator = new MediaDisposer(); // start a background thread that blocks on purgatory and runs indefinitely Thread disposerThread = new Thread( () -> { theDisposinator.disposerLoop(); }, "Media Resource Disposer"); disposerThread.setDaemon(true); disposerThread.start(); } return theDisposinator; } private MediaDisposer() { purgatory = new ReferenceQueue(); // disposers is accessed by multiple threads potentially simultaneously, // so make it synchronized disposers = new HashMap<Reference,Disposable>(); } private void disposerLoop() { // FIXME: make this interruptable? while (true) { try { Reference denizen = purgatory.remove(); Disposable disposer; synchronized (disposers) { disposer = disposers.remove(denizen); } denizen.clear(); if (null != disposer) { disposer.dispose(); } denizen = null; disposer = null; } catch (InterruptedException ex) { if (Logger.canLog(Logger.DEBUG)) { Logger.logMsg(Logger.DEBUG, MediaDisposer.class.getName(), "disposerLoop", "Disposer loop interrupted, terminating"); } } } } private void implAddResourceDisposer(Object referent, Object resource, ResourceDisposer disposer) { Reference denizen = new PhantomReference(referent, purgatory); synchronized (disposers) { disposers.put(denizen, new ResourceDisposerRecord(resource, disposer)); } } private void implRemoveResourceDisposer(Object resource) { Reference resourceKey = null; synchronized (disposers) { for (Map.Entry<Reference, Disposable> entry : disposers.entrySet()) { Disposable disposer = entry.getValue(); if (disposer instanceof ResourceDisposerRecord) { ResourceDisposerRecord rd = (ResourceDisposerRecord)disposer; if (rd.resource.equals(resource)) { resourceKey = entry.getKey(); break; // no need to continue } } } if (null != resourceKey) { disposers.remove(resourceKey); } } } private void implAddDisposable(Object referent, Disposable disposer) { Reference denizen = new PhantomReference(referent, purgatory); synchronized (disposers) { disposers.put(denizen, disposer); } } private static class ResourceDisposerRecord implements Disposable { Object resource; ResourceDisposer disposer; public ResourceDisposerRecord(Object resource, ResourceDisposer disposer) { this.resource = resource; this.disposer = disposer; } public void dispose() { disposer.disposeResource(resource); } } }