Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1544fa7c2ff9
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 84b978fc62f8
Choose a head ref
  • 3 commits
  • 4 files changed
  • 1 contributor

Commits on Mar 18, 2016

  1. Copy the full SHA
    6e98a54 View commit details
  2. Copy the full SHA
    166d111 View commit details
  3. Attempt once to GC and submit to executor again.

    This may help cases where lots of Enumerator#next or Fiber threads
    are being abandoned rather than run to completion, which requires
    finalization to clear up. The GC call is not guaranteed to work
    but it may help some cases of runaway coroutine use.
    headius committed Mar 18, 2016
    Copy the full SHA
    84b978f View commit details
Showing with 92 additions and 59 deletions.
  1. +0 −4 bin/jruby.bash
  2. +18 −7 core/src/main/java/org/jruby/Main.java
  3. +12 −1 core/src/main/java/org/jruby/RubyEnumerator.java
  4. +62 −47 core/src/main/java/org/jruby/ext/fiber/ThreadFiber.java
4 changes: 0 additions & 4 deletions bin/jruby.bash
Original file line number Diff line number Diff line change
@@ -91,10 +91,6 @@ if [ -z "$JAVACMD" ] ; then
fi
fi

if [ -z "$JAVA_MEM" ] ; then
JAVA_MEM=-Xmx500m
fi

if [ -z "$JAVA_STACK" ] ; then
JAVA_STACK=-Xss2048k
fi
25 changes: 18 additions & 7 deletions core/src/main/java/org/jruby/Main.java
Original file line number Diff line number Diff line change
@@ -358,21 +358,32 @@ private Status handleOutOfMemory(OutOfMemoryError oome) {
System.gc(); // try to clean up a bit of space, hopefully, so we can report this error

String oomeMessage = oome.getMessage();
boolean heapError = false;

if (oomeMessage != null) {
if (oomeMessage.contains("PermGen")) {
// report permgen memory error
config.getError().println("Error: Your application exhausted PermGen area of the heap.");
config.getError().println("Specify -J-XX:MaxPermSize=###M to increase it (### = PermGen size in MB).");
} else if (oomeMessage.contains("unable to create new native thread")) {
// report thread exhaustion error
config.getError().println("Error: Your application demanded too many live threads, perhaps for Fiber or Enumerator.");
config.getError().println("Ensure your old Fibers and Enumerators are being cleaned up.");
} else {
heapError = true;
}
}

if (oomeMessage != null && oomeMessage.contains("PermGen")) { // report permgen memory error
config.getError().println("Error: Your application exhausted PermGen area of the heap.");
config.getError().println("Specify -J-XX:MaxPermSize=###M to increase it (### = PermGen size in MB).");

} else { // report heap memory error
if (heapError) { // report heap memory error

String memoryMax = getRuntimeFlagValue("-Xmx");

if (memoryMax != null) {
config.getError().println("Error: Your application used more memory than the safety cap of " + memoryMax + ".");
} else {
config.getError().println("Error: Your application used more memory than the default safety cap.");
config.getError().println("Error: Your application used more memory than the automatic cap of " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB.");
}
config.getError().println("Specify -J-Xmx####m to increase it (#### = cap size in MB).");
config.getError().println("Specify -J-Xmx####M to increase it (#### = cap size in MB).");
}

if (config.isVerbose()) {
13 changes: 12 additions & 1 deletion core/src/main/java/org/jruby/RubyEnumerator.java
Original file line number Diff line number Diff line change
@@ -700,7 +700,18 @@ public synchronized IRubyObject peek() {
}

private void ensureStarted() {
if (thread == null) future = runtime.getFiberExecutor().submit(this);
try {
if (thread == null) future = runtime.getFiberExecutor().submit(this);
} catch (OutOfMemoryError oome) {
String oomeMessage = oome.getMessage();
if (oomeMessage != null && oomeMessage.contains("unable to create new native thread")) {
// try to clean out stale enumerator threads by forcing GC
System.gc();
future = runtime.getFiberExecutor().submit(this);
} else {
throw oome;
}
}
}

private IRubyObject peekTake() {
109 changes: 62 additions & 47 deletions core/src/main/java/org/jruby/ext/fiber/ThreadFiber.java
Original file line number Diff line number Diff line change
@@ -238,61 +238,76 @@ boolean alive() {

static RubyThread createThread(final Ruby runtime, final FiberData data, final FiberQueue queue, final Block block) {
final AtomicReference<RubyThread> fiberThread = new AtomicReference();
runtime.getFiberExecutor().execute(new Runnable() {
public void run() {
ThreadContext context = runtime.getCurrentContext();
context.setFiber(data.fiber.get());
fiberThread.set(context.getThread());
context.getThread().setFiberCurrentThread(data.parent);

try {
IRubyObject init = data.queue.pop(context);

// retry with GC once
boolean retried = true;

try {
runtime.getFiberExecutor().execute(new Runnable() {
public void run() {
ThreadContext context = runtime.getCurrentContext();
context.setFiber(data.fiber.get());
fiberThread.set(context.getThread());
context.getThread().setFiberCurrentThread(data.parent);

try {
IRubyObject result;
IRubyObject init = data.queue.pop(context);

if (init == NEVER) {
result = block.yieldSpecific(context);
} else {
result = block.yieldArray(context, init, null);
}
try {
IRubyObject result;

if (init == NEVER) {
result = block.yieldSpecific(context);
} else {
result = block.yieldArray(context, init, null);
}

data.prev.data.queue.push(context, new IRubyObject[] { result });
data.prev.data.queue.push(context, new IRubyObject[]{result});
} finally {
data.queue.shutdown();
runtime.getThreadService().disposeCurrentThread();
}
} catch (JumpException.FlowControlException fce) {
if (data.prev != null) {
data.prev.thread.raise(fce.buildException(runtime).getException());
}
} catch (IRBreakJump bj) {
// This is one of the rare cases where IR flow-control jumps
// leaks into the runtime impl.
if (data.prev != null) {
data.prev.thread.raise(((RaiseException) IRException.BREAK_LocalJumpError.getException(runtime)).getException());
}
} catch (IRReturnJump rj) {
// This is one of the rare cases where IR flow-control jumps
// leaks into the runtime impl.
if (data.prev != null) {
data.prev.thread.raise(((RaiseException) IRException.RETURN_LocalJumpError.getException(runtime)).getException());
}
} catch (RaiseException re) {
if (data.prev != null) {
data.prev.thread.raise(re.getException());
}
} catch (Throwable t) {
if (data.prev != null) {
data.prev.thread.raise(JavaUtil.convertJavaToUsableRubyObject(runtime, t));
}
} finally {
data.queue.shutdown();
runtime.getThreadService().disposeCurrentThread();
}
} catch (JumpException.FlowControlException fce) {
if (data.prev != null) {
data.prev.thread.raise(fce.buildException(runtime).getException());
}
} catch (IRBreakJump bj) {
// This is one of the rare cases where IR flow-control jumps
// leaks into the runtime impl.
if (data.prev != null) {
data.prev.thread.raise(((RaiseException)IRException.BREAK_LocalJumpError.getException(runtime)).getException());
// clear reference to the fiber's thread
ThreadFiber tf = data.fiber.get();
if (tf != null) tf.thread = null;
}
} catch (IRReturnJump rj) {
// This is one of the rare cases where IR flow-control jumps
// leaks into the runtime impl.
if (data.prev != null) {
data.prev.thread.raise(((RaiseException)IRException.RETURN_LocalJumpError.getException(runtime)).getException());
}
} catch (RaiseException re) {
if (data.prev != null) {
data.prev.thread.raise(re.getException());
}
} catch (Throwable t) {
if (data.prev != null) {
data.prev.thread.raise(JavaUtil.convertJavaToUsableRubyObject(runtime, t));
}
} finally {
// clear reference to the fiber's thread
ThreadFiber tf = data.fiber.get();
if (tf != null) tf.thread = null;
}
});
} catch (OutOfMemoryError oome) {
String oomeMessage = oome.getMessage();
if (!retried && oomeMessage != null && oomeMessage.contains("unable to create new native thread")) {
// try to clean out stale enumerator threads by forcing GC
System.gc();
retried = true;
} else {
throw oome;
}
});
}

while (fiberThread.get() == null) {Thread.yield();}