Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
Expand Up @@ -2762,7 +2762,7 @@ public ThreadService getThreadService() {
}

public ThreadContext getCurrentContext() {
return threadService.getCurrentContext();
return ThreadService.getCurrentContext(threadService);
}

/**
Expand Down Expand Up @@ -3413,8 +3413,8 @@ public void tearDown(boolean systemExit) {
printProfileData(profileCollection);
}

// tear down thread references
getThreadService().teardown();
// swap with a new service to dereference all thread constructs
threadService = new ThreadService(this);

// shut down executors
getJITCompiler().shutdown();
Expand Down Expand Up @@ -5076,7 +5076,7 @@ private MRIRecursionGuard oldRecursionGuard() {
1 /* concurrency level - mostly reads here so this can be 1 */);

private final Invalidator checkpointInvalidator;
private final ThreadService threadService;
private ThreadService threadService;

private final POSIX posix;

Expand Down
65 changes: 33 additions & 32 deletions core/src/main/java/org/jruby/internal/runtime/ThreadService.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
import org.jruby.runtime.ThreadContext;

/**
* ThreadService maintains lists ofall the JRuby-specific thread data structures
* ThreadService maintains references to all JRuby-specific thread data structures
* needed for Ruby's threading API and for JRuby's execution. The main
* structures are:
*
Expand All @@ -66,7 +66,7 @@
* with these structures are:
*
* <ul>
* <li>ThreadService has a hard reference to a ThreadLocal, which holds a soft reference
* <li>ThreadService is itself a ThreadLocal, which holds a soft reference
* to a ThreadContext. So the thread's locals softly reference ThreadContext.
* We use a soft reference to keep ThreadContext instances from going away too
* quickly when a Java thread leaves Ruby space completely, which would otherwise
Expand Down Expand Up @@ -106,23 +106,14 @@
* releasing the hard reference to the Thread itself.</li>
* <ul>
*/
public class ThreadService {
public class ThreadService extends ThreadLocal<SoftReference<ThreadContext>> {
private final Ruby runtime;
/**
* A hard reference to the "main" context, so we always have one waiting for
* "main" thread execution.
*/
private ThreadContext mainContext;

/**
* A thread-local soft reference to the current thread's ThreadContext. We
* use a soft reference so that the ThreadContext is still collectible but
* will not immediately disappear once dereferenced, to avoid churning
* through ThreadContext instances every time a Java thread enters and exits
* Ruby space.
*/
private ThreadLocal<SoftReference<ThreadContext>> localContext;

/**
* The Java thread group into which we register all Ruby threads. This is
* distinct from the RubyThreadGroup, which is simply a mutable collection
Expand All @@ -144,7 +135,6 @@ public class ThreadService {

public ThreadService(final Ruby runtime) {
this.runtime = runtime;
this.localContext = new ThreadLocal<>();

try {
this.rubyThreadGroup = new ThreadGroup("Ruby Threads#" + runtime.hashCode());
Expand All @@ -156,9 +146,6 @@ public ThreadService(final Ruby runtime) {
}

public void teardown() {
// clear all thread-local context references
localContext = new ThreadLocal<>();

// clear main context reference
mainContext = null;

Expand All @@ -170,7 +157,7 @@ public void initMainThread() {
this.mainContext = ThreadContext.newContext(runtime);

// Must be called from main thread (it is currently, but this bothers me)
localContext.set(new SoftReference<>(mainContext));
set(new SoftReference<>(mainContext));
}

/**
Expand All @@ -197,24 +184,38 @@ public void initMainThread() {
* collected.
*/
public final ThreadContext getCurrentContext() {
return getCurrentContext(this);
}

public static ThreadContext getCurrentContext(ThreadService service) {
ThreadContext context;

// keep trying until we have a context
do {
SoftReference<ThreadContext> ref = localContext.get();
if (ref == null) {
context = adoptCurrentThread().getContext(); // registerNewThread will localContext.set(...)
} else {
if ((context = ref.get()) == null) {
// context is null, wipe out the SoftReference (this could be done with a reference queue)
localContext.set(null);
}
}
} while (context == null);
context = adoptLoop(service);

if (context == null) return getCurrentContext(service);

return context;
}

private static ThreadContext adoptLoop(ThreadService service) {
SoftReference<ThreadContext> ref = service.get();
if (ref == null) {
return contextFromAdopt(service); // registerNewThread will localContext.set(...)
} else {
ThreadContext context;
if ((context = ref.get()) == null) {
// context is null, wipe out the SoftReference (this could be done with a reference queue)
service.remove();
}
return context;
}
}

private static ThreadContext contextFromAdopt(ThreadService service) {
return service.adoptCurrentThread().getContext();
}

private RubyThread adoptCurrentThread() {
return RubyThread.adopt(runtime, this, Thread.currentThread());
}
Expand All @@ -224,7 +225,7 @@ public ThreadContext registerNewThread(RubyThread thread) {
ThreadContext context = ThreadContext.newContext(runtime);
context.setThread(thread);
ThreadFiber.initRootFiber(context, thread);
localContext.set(new SoftReference<>(context));
set(new SoftReference<>(context));
return context;
}

Expand Down Expand Up @@ -287,9 +288,9 @@ private void unregisterThreadImpl(ThreadContext context, Thread nativeThread) {
if (thread != null) thread.clearContext(); // help GC - clear context-ref
}

SoftReference<ThreadContext> ref = localContext.get();
SoftReference<ThreadContext> ref = get();
if (ref != null) ref.clear(); // help GC
localContext.remove();
remove();
}

@Deprecated // use unregisterCurrentThread
Expand Down Expand Up @@ -327,7 +328,7 @@ public synchronized void dissociateThread(Object threadOrFuture) {

@Deprecated
public final void setCurrentContext(ThreadContext context) {
localContext.set(new SoftReference<ThreadContext>(context));
set(new SoftReference<ThreadContext>(context));
}

@Deprecated
Expand Down