Skip to content

Commit

Permalink
Cleanup ThreadLocals on completion.
Browse files Browse the repository at this point in the history
  • Loading branch information
lincolnthree committed Nov 4, 2013
1 parent a74f053 commit 43934cf
Show file tree
Hide file tree
Showing 16 changed files with 287 additions and 220 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@

package org.jboss.forge.furnace.util;

import java.lang.ref.Reference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.logging.Level;
import java.util.logging.Logger;

public final class SecurityActions
{
private static final Logger log = Logger.getLogger(SecurityActions.class.getName());

private SecurityActions()
{
// forbidden inheritance
Expand Down Expand Up @@ -81,4 +88,55 @@ public Object run()
}
}

/**
* Cleanup {@link ThreadLocal} instances of the given {@link Thread}.
*
* @param thread The {@link Thread} to clean up.
*/
public static void cleanupThreadLocals(Thread thread)
{
try
{
cleanField(thread, Thread.class.getDeclaredField("threadLocals"));
cleanField(thread, Thread.class.getDeclaredField("inheritableThreadLocals"));
}
catch (Exception e)
{
log.log(Level.WARNING, "Failed to cleanup ThreadLocal instances for Thread [" + thread + "]", e);
}
}

private static void cleanField(Thread thread, Field threadLocalsField) throws IllegalAccessException,
ClassNotFoundException, NoSuchFieldException
{
threadLocalsField.setAccessible(true);
Object threadLocalTable = threadLocalsField.get(thread);

// Get a reference to the array holding the thread local variables inside the
// ThreadLocalMap of the current thread
Class<?> threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = threadLocalMapClass.getDeclaredField("table");
tableField.setAccessible(true);
Object table = tableField.get(threadLocalTable);

// The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
// is a reference to the actual ThreadLocal variable
Field referentField = Reference.class.getDeclaredField("referent");
referentField.setAccessible(true);

for (int i = 0; i < Array.getLength(table); i++)
{
// Each entry in the table array of ThreadLocalMap is an Entry object
// representing the thread local reference and its value
Object entry = Array.get(table, i);
if (entry != null)
{
// Get a reference to the thread local object and remove it from the table
ThreadLocal<?> threadLocal = (ThreadLocal<?>) referentField.get(entry);
if (threadLocal != null)
threadLocal.remove();
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,22 @@ public Furnace start(ClassLoader loader)
}

fireAfterContainerStoppedEvent();
cleanup();
return this;
}

private void cleanup()
{
for (ListenerRegistration<ContainerLifecycleListener> registation : loadedListenerRegistrations)
{
registation.removeListener();
}
return this;
registeredListeners.clear();
lastRepoVersionSeen.clear();
loader = null;
manager.dispose();
manager = null;
repositories.clear();
}

private void fireBeforeConfigurationScanEvent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ public class AddonLifecycleManager
private static final Logger logger = Logger.getLogger(AddonLifecycleManager.class.getName());

private final LockManager lock;
private FurnaceImpl furnace;
private AddonLoader loader;
private AddonStateManager stateManager;
private final FurnaceImpl furnace;
private final AddonLoader loader;
private final AddonStateManager stateManager;

private Set<Addon> addons = Sets.getConcurrentSet();
private final Set<Addon> addons = Sets.getConcurrentSet();
private final Map<AddonView, Long> views = new ConcurrentHashMap<AddonView, Long>();
private final AtomicInteger starting = new AtomicInteger(-1);
private final ExecutorService executor = Executors.newCachedThreadPool();

private AddonModuleLoader moduleLoader;
private final AddonModuleLoader moduleLoader;

public AddonLifecycleManager(FurnaceImpl furnace)
{
Expand All @@ -73,6 +73,18 @@ public AddonLifecycleManager(FurnaceImpl furnace)
logger.log(Level.FINE, "Instantiated AddonRTegistryImpl: " + this);
}

public void dispose()
{
for (AddonView view : views.keySet())
{
view.dispose();
}
this.views.clear();
this.stateManager.dispose();
this.loader.dispose();
this.moduleLoader.dispose();
}

public long getVersion(AddonView view)
{
Long version = views.get(view);
Expand Down Expand Up @@ -266,11 +278,6 @@ public boolean isStartingAddons()
return starting.get() > 0;
}

public void dispose(AddonView view)
{
furnace.disposeAddonView(view);
}

public void startAddon(Addon addon)
{
Assert.notNull(addon, "Addon to start must not be null.");
Expand Down Expand Up @@ -317,6 +324,7 @@ public void removeView(AddonView view)
{
if (!views.keySet().contains(view))
throw new IllegalArgumentException("The given view does not belong to this Furnace instance.");
views.remove(view);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@ public class AddonLoader
private AddonStateManager stateManager;
private AddonModuleLoader loader;

public AddonLoader(Furnace furnace, AddonLifecycleManager lifecycleManager, AddonStateManager stateManager, AddonModuleLoader loader)
public AddonLoader(Furnace furnace, AddonLifecycleManager lifecycleManager, AddonStateManager stateManager,
AddonModuleLoader loader)
{
this.lock = furnace.getLockManager();
this.lifecycleManager = lifecycleManager;
this.stateManager = stateManager;
this.loader = loader;
}

public void dispose()
{
this.lock = null;
this.lifecycleManager = null;
this.stateManager = null;
this.loader = null;
}

public void loadAddon(Addon addon)
{
Assert.notNull(addon, "Addon to load must not be null.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,10 @@ public class AddonRegistryImpl implements AddonRegistry
{
private static final Logger logger = Logger.getLogger(AddonRegistryImpl.class.getName());

private final LockManager lock;
private LockManager lock;
private List<AddonRepository> repositories;

private AddonLifecycleManager manager;

private String name;
private final String name;

public AddonRegistryImpl(LockManager lock, AddonLifecycleManager manager, List<AddonRepository> repositories,
String name)
Expand All @@ -62,7 +60,10 @@ public AddonRegistryImpl(LockManager lock, AddonLifecycleManager manager, List<A
@Override
public void dispose()
{
manager.dispose(this);
manager.removeView(this);
repositories = null;
manager = null;
lock = null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.jboss.forge.furnace.repositories.AddonRepository;
import org.jboss.forge.furnace.util.Addons;
import org.jboss.forge.furnace.util.ClassLoaders;
import org.jboss.forge.furnace.util.SecurityActions;

/**
* Loads an {@link Addon}
Expand All @@ -35,11 +36,11 @@ public final class AddonRunnable implements Runnable
private static final Logger logger = Logger.getLogger(AddonRunnable.class.getName());

private boolean shutdownRequested = false;
private Furnace furnace;
private Addon addon;
private final Furnace furnace;
private final Addon addon;

private AddonLifecycleManager lifecycleManager;
private AddonStateManager stateManager;
private final AddonLifecycleManager lifecycleManager;
private final AddonStateManager stateManager;

private AddonLifecycleProviderEntry lifecycleProviderEntry;

Expand Down Expand Up @@ -108,6 +109,7 @@ public Void call() throws Exception
finally
{
lifecycleManager.finishedStarting(addon);
SecurityActions.cleanupThreadLocals(Thread.currentThread());
currentThread.setName(name);
currentThread.setContextClassLoader(null);
}
Expand Down Expand Up @@ -327,8 +329,8 @@ else if (!addon.equals(other.addon))

private class AddonLifecycleProviderEntry
{
private AddonLifecycleProvider provider;
private Addon addon;
private final AddonLifecycleProvider provider;
private final Addon addon;

public AddonLifecycleProviderEntry(Addon addon, AddonLifecycleProvider value)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,22 @@ public class AddonStateManager
{
private LockManager lock;
private MasterGraph graph;
private Map<Addon, AddonState> states = new HashMap<Addon, AddonState>();
private final Map<Addon, AddonState> states = new HashMap<Addon, AddonState>();
private AddonModuleLoader loader;

public AddonStateManager(LockManager lock)
{
this.lock = lock;
}

public void dispose()
{
this.lock = null;
this.graph = null;
this.states.clear();
this.loader = null;
}

public void setModuleLoader(AddonModuleLoader loader)
{
this.loader = loader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
class AddonModuleJarFileCache
{
private static final Logger logger = Logger.getLogger(AddonModuleJarFileCache.class.getName());
private Map<ModuleIdentifier, Set<JarFile>> map = new ConcurrentHashMap<ModuleIdentifier, Set<JarFile>>();
private final Map<ModuleIdentifier, Set<JarFile>> map = new ConcurrentHashMap<ModuleIdentifier, Set<JarFile>>();

public void closeJarFileReferences(ModuleIdentifier id)
{
Expand Down Expand Up @@ -64,4 +64,13 @@ public void addJarFileReference(ModuleIdentifier id, JarFile file)
files.add(file);
}

public void dispose()
{
for (ModuleIdentifier id : map.keySet())
{
closeJarFileReferences(id);
}
map.clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@
*/
class AddonModuleIdentifierCache
{
private Map<Addon, ModuleIdentifier> map = new HashMap<Addon, ModuleIdentifier>();
private final Map<Addon, ModuleIdentifier> map = new HashMap<Addon, ModuleIdentifier>();

public void clear(Addon addon)
{
map.remove(addon);
}

public void dispose()
{
map.clear();
}

public ModuleIdentifier getModuleId(Addon addon)
{
if (!map.containsKey(addon))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ public class AddonModuleLoader extends ModuleLoader

private Iterable<ModuleSpecProvider> moduleProviders;

private AddonModuleIdentifierCache moduleCache;
private AddonModuleJarFileCache moduleJarFileCache;
private final AddonModuleIdentifierCache moduleCache;
private final AddonModuleJarFileCache moduleJarFileCache;

private AddonLifecycleManager lifecycleManager;
private AddonStateManager stateManager;

private ThreadLocal<Addon> currentAddon = new ThreadLocal<Addon>();
private final ThreadLocal<Addon> currentAddon = new ThreadLocal<Addon>();

private Furnace furnace;

Expand All @@ -73,6 +73,16 @@ public AddonModuleLoader(Furnace furnace, AddonLifecycleManager lifecycleManager
installModuleMBeanServer();
}

public void dispose()
{
this.furnace = null;
this.lifecycleManager = null;
this.stateManager = null;
this.moduleCache.dispose();
this.moduleJarFileCache.dispose();
this.moduleProviders = null;
}

/**
* Loads a module for the given Addon.
*/
Expand Down
Loading

0 comments on commit 43934cf

Please sign in to comment.