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: 5bc6c5099ac3
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c591f4f8df13
Choose a head ref
  • 9 commits
  • 7 files changed
  • 1 contributor

Commits on May 15, 2015

  1. Copy the full SHA
    1e2d2dd View commit details
  2. Copy the full SHA
    3514211 View commit details
  3. unnecessary (int) cast

    kares committed May 15, 2015
    Copy the full SHA
    57140ca View commit details
  4. Copy the full SHA
    76b6992 View commit details
  5. Copy the full SHA
    139a558 View commit details
  6. Copy the full SHA
    e80996f View commit details
  7. less (int) casting noiz

    kares committed May 15, 2015
    Copy the full SHA
    800554b View commit details
  8. Copy the full SHA
    b9966b5 View commit details
  9. Copy the full SHA
    c591f4f View commit details
315 changes: 158 additions & 157 deletions core/src/main/java/org/jruby/RubyArray.java

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -1776,10 +1776,10 @@ public IRubyObject evalUnder(final ThreadContext context, RubyModule under, Ruby
* operation.
*/
public static class Finalizer implements Finalizable {
private RubyFixnum id;
private final RubyFixnum id;
private final AtomicBoolean finalized;
private IRubyObject firstFinalizer;
private List<IRubyObject> finalizers;
private AtomicBoolean finalized;

public Finalizer(RubyFixnum id) {
this.id = id;
20 changes: 11 additions & 9 deletions core/src/main/java/org/jruby/RubyInstanceConfig.java
Original file line number Diff line number Diff line change
@@ -142,7 +142,7 @@ public RubyInstanceConfig() {
try {
environment = System.getenv();
} catch (SecurityException se) {
environment = new HashMap();
environment = new HashMap<String,String>();
}
}

@@ -407,8 +407,8 @@ public InputStream getScriptSource() {
}
return getInput();
} else {
String script = getScriptFileName();
InputStream stream = null;
final String script = getScriptFileName();
final InputStream stream;
if (script.startsWith("file:") && script.indexOf(".jar!/") != -1) {
stream = new URL("jar:" + script).openStream();
} else if (script.startsWith("classpath:")) {
@@ -437,7 +437,7 @@ public InputStream getScriptSource() {
}

private static InputStream findScript(File file) throws IOException {
StringBuffer buf = new StringBuffer();
StringBuilder buf = new StringBuilder();
BufferedReader br = new BufferedReader(new FileReader(file));
String currentLine = br.readLine();
while (currentLine != null && !isRubyShebangLine(currentLine)) {
@@ -450,8 +450,8 @@ private static InputStream findScript(File file) throws IOException {
do {
currentLine = br.readLine();
if (currentLine != null) {
buf.append(currentLine);
buf.append("\n");
buf.append(currentLine);
buf.append("\n");
}
} while (!(currentLine == null || currentLine.contains("__END__") || currentLine.contains("\026")));
return new BufferedInputStream(new ByteArrayInputStream(buf.toString().getBytes()), 8192);
@@ -719,8 +719,10 @@ public boolean isSiphashEnabled() {
}

public void setEnvironment(Map newEnvironment) {
if (newEnvironment == null) newEnvironment = new HashMap();
environment = newEnvironment;
if (newEnvironment == null) {
newEnvironment = new HashMap<String, String>();
}
this.environment = newEnvironment;
}

public Map getEnvironment() {
@@ -1460,7 +1462,7 @@ public void setProfilingService( String service ) {
private String currentDirectory;

/** Environment variables; defaults to System.getenv() in constructor */
private Map environment;
private Map<String, String> environment;
private String[] argv = {};

private final boolean jitLogging;
56 changes: 28 additions & 28 deletions core/src/main/java/org/jruby/RubyInteger.java
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
* Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -59,7 +59,7 @@
* @author jpetersen
*/
@JRubyClass(name="Integer", parent="Numeric", include="Precision")
public abstract class RubyInteger extends RubyNumeric {
public abstract class RubyInteger extends RubyNumeric {

public static RubyClass createIntegerClass(Ruby runtime) {
RubyClass integer = runtime.defineClass("Integer", runtime.getNumeric(),
@@ -68,7 +68,7 @@ public static RubyClass createIntegerClass(Ruby runtime) {

integer.index = ClassIndex.INTEGER;
integer.setReifiedClass(RubyInteger.class);

integer.kindOf = new RubyModule.JavaClassKindOf(RubyInteger.class);

integer.getSingletonClass().undefineMethod("new");
@@ -78,7 +78,7 @@ public static RubyClass createIntegerClass(Ruby runtime) {
}

integer.defineAnnotatedMethods(RubyInteger.class);

return integer;
}

@@ -89,15 +89,15 @@ public RubyInteger(Ruby runtime, RubyClass rubyClass) {
public RubyInteger(RubyClass rubyClass) {
super(rubyClass);
}

public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace) {
super(runtime, rubyClass, useObjectSpace);
}
}

@Deprecated
public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace, boolean canBeTainted) {
super(runtime, rubyClass, useObjectSpace, canBeTainted);
}
}

@Override
public RubyInteger convertToInteger() {
@@ -111,11 +111,11 @@ protected RubyFloat toFloat() {

/* ================
* Instance Methods
* ================
* ================
*/

/** int_int_p
*
*
*/
@Override
@JRubyMethod(name = "integer?")
@@ -124,7 +124,7 @@ public IRubyObject integer_p() {
}

/** int_upto
*
*
*/
@JRubyMethod
public IRubyObject upto(ThreadContext context, IRubyObject to, Block block) {
@@ -177,7 +177,7 @@ private static void duckUpto(ThreadContext context, IRubyObject from, IRubyObjec
}

/** int_downto
*
*
*/
// TODO: Make callCoerced work in block context...then fix downto, step, and upto.
@JRubyMethod
@@ -250,7 +250,7 @@ public IRubyObject times(ThreadContext context, Block block) {
}

/** int_succ
*
*
*/
@JRubyMethod(name = {"succ", "next"})
public IRubyObject succ(ThreadContext context) {
@@ -279,7 +279,7 @@ public IRubyObject succ(ThreadContext context) {
}

/** int_chr
*
*
*/
@JRubyMethod(name = "chr", compat = CompatVersion.RUBY1_8)
public RubyString chr(ThreadContext context) {
@@ -302,7 +302,7 @@ public RubyString chr19(ThreadContext context) {
throw runtime.newRangeError(this.toString() + " out of char range");
} else {
if (enc == null) enc = USASCIIEncoding.INSTANCE;
return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, (int)value), enc, 0);
return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, value), enc, 0);
}
}
}
@@ -332,7 +332,7 @@ private ByteList fromEncodedBytes(Ruby runtime, Encoding enc, int value) {
}

if (n <= 0) throw runtime.newRangeError(this.toString() + " out of char range");

ByteList bytes = new ByteList(n);

try {
@@ -345,15 +345,15 @@ private ByteList fromEncodedBytes(Ruby runtime, Encoding enc, int value) {
}

/** int_ord
*
*
*/
@JRubyMethod(name = "ord")
public IRubyObject ord(ThreadContext context) {
return this;
}

/** int_to_i
*
*
*/
@JRubyMethod(name = {"to_i", "to_int", "floor", "ceil", "truncate"})
public IRubyObject to_i() {
@@ -377,14 +377,14 @@ public IRubyObject round19(ThreadContext context, IRubyObject arg) {
if (ndigits > 0) return RubyKernel.new_float(this, this);
if (ndigits == 0) return this;
Ruby runtime = context.runtime;

long bytes = (this instanceof RubyFixnum) ? 8 : RubyFixnum.fix2long(callMethod("size"));
/* If 10**N/2 > this, return 0 */
/* We have log_256(10) > 0.415241 and log_256(1/2)=-0.125 */
if (-0.415241 * ndigits - 0.125 > bytes) {
return RubyFixnum.zero(runtime);
}

IRubyObject f = Numeric.int_pow(context, 10, -ndigits);

if (this instanceof RubyFixnum && f instanceof RubyFixnum) {
@@ -408,7 +408,7 @@ public IRubyObject round19(ThreadContext context, IRubyObject arg) {
}

/** integer_to_r
*
*
*/
@JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
public IRubyObject to_r(ThreadContext context) {
@@ -422,7 +422,7 @@ public IRubyObject to_r(ThreadContext context) {
public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {
return to_r(context);
}


@JRubyMethod(name = "odd?")
public RubyBoolean odd_p(ThreadContext context) {
@@ -448,25 +448,25 @@ public IRubyObject pred(ThreadContext context) {
}

/** rb_gcd
*
*
*/
@JRubyMethod(name = "gcd", compat = CompatVersion.RUBY1_9)
public IRubyObject gcd(ThreadContext context, IRubyObject other) {
checkInteger(context, other);
return f_gcd(context, this, RubyRational.intValue(context, other));
}
}

/** rb_lcm
*
*
*/
@JRubyMethod(name = "lcm", compat = CompatVersion.RUBY1_9)
public IRubyObject lcm(ThreadContext context, IRubyObject other) {
checkInteger(context, other);
return f_lcm(context, this, RubyRational.intValue(context, other));
}
}

/** rb_gcdlcm
*
*
*/
@JRubyMethod(name = "gcdlcm", compat = CompatVersion.RUBY1_9)
public IRubyObject gcdlcm(ThreadContext context, IRubyObject other) {
@@ -489,11 +489,11 @@ public IRubyObject denominator(ThreadContext context) {

/* ================
* Singleton Methods
* ================
* ================
*/

/** rb_int_induced_from
*
*
*/
@JRubyMethod(name = "induced_from", meta = true, compat = CompatVersion.RUBY1_8)
public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject other) {
48 changes: 24 additions & 24 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -264,11 +264,14 @@ private synchronized Map<String, Autoload> getAutoloadMapForWrite() {
return autoloads == Collections.EMPTY_MAP ? autoloads = new ConcurrentHashMap<String, Autoload>(4, 0.9f, 1) : autoloads;
}

@SuppressWarnings("unchecked")
public void addIncludingHierarchy(IncludedModuleWrapper hierarchy) {
synchronized (getRuntime().getHierarchyLock()) {
Set<RubyClass> oldIncludingHierarchies = includingHierarchies;
if (oldIncludingHierarchies == Collections.EMPTY_SET) includingHierarchies = oldIncludingHierarchies = new WeakHashSet(4);
oldIncludingHierarchies.add(hierarchy);
Set<RubyClass> including = this.includingHierarchies;
if (including == Collections.EMPTY_SET) {
including = this.includingHierarchies = new WeakHashSet(4);
}
including.add(hierarchy);
}
}

@@ -3324,9 +3327,8 @@ public void setInternalModuleVariable(final String name, final IRubyObject value
protected Map<String, IRubyObject> getClassVariables() {
if (CLASSVARS_UPDATER == null) {
return getClassVariablesForWriteSynchronized();
} else {
return getClassVariablesForWriteAtomic();
}
return getClassVariablesForWriteAtomic();
}

/**
@@ -3336,19 +3338,17 @@ protected Map<String, IRubyObject> getClassVariables() {
* @return the class vars map, ready for assignment
*/
private Map<String,IRubyObject> getClassVariablesForWriteSynchronized() {
Map myClassVars = classVariables;
if (myClassVars == Collections.EMPTY_MAP) {
Map<String, IRubyObject> myClassVars = classVariables;
if ( myClassVars == Collections.EMPTY_MAP ) {
synchronized (this) {
myClassVars = classVariables;

if (myClassVars == Collections.EMPTY_MAP) {
if ( myClassVars == Collections.EMPTY_MAP ) {
return classVariables = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);
} else {
return myClassVars;
}
return myClassVars;
}
}

return myClassVars;
}

@@ -3361,14 +3361,12 @@ private Map<String,IRubyObject> getClassVariablesForWriteSynchronized() {
*/
private Map<String,IRubyObject> getClassVariablesForWriteAtomic() {
while (true) {
Map myClassVars = classVariables;
Map newClassVars;
Map<String, IRubyObject> myClassVars = classVariables;

if (myClassVars == Collections.EMPTY_MAP) {
newClassVars = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);
} else {
return myClassVars;
}
if ( myClassVars != Collections.EMPTY_MAP ) return myClassVars;

Map<String, IRubyObject> newClassVars;
newClassVars = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);

// proceed with atomic update of table, or retry
if (CLASSVARS_UPDATER.compareAndSet(this, myClassVars, newClassVars)) {
@@ -4096,17 +4094,19 @@ public void defineFastPublicModuleFunction(String name, org.jruby.runtime.callba

private volatile Map<String, IRubyObject> classVariables = Collections.EMPTY_MAP;

private static final AtomicReferenceFieldUpdater CLASSVARS_UPDATER;
private static final AtomicReferenceFieldUpdater<RubyModule, Map> CLASSVARS_UPDATER;

static {
AtomicReferenceFieldUpdater updater = null;
AtomicReferenceFieldUpdater<RubyModule, Map> updater = null;
try {
updater = AtomicReferenceFieldUpdater.newUpdater(RubyModule.class, Map.class, "classVariables");
} catch (RuntimeException re) {
if (re.getCause() instanceof AccessControlException) {
}
catch (final RuntimeException ex) {
if (ex.getCause() instanceof AccessControlException) {
// security prevented creation; fall back on synchronized assignment
} else {
throw re;
}
else {
throw ex;
}
}
CLASSVARS_UPDATER = updater;
6 changes: 2 additions & 4 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -2471,8 +2471,6 @@ public IRubyObject inspect19() {
result.cat('"');
int prev = p;
while (p < end) {
int cc = 0;

int n = StringSupport.preciseLength(enc, bytes, p, end);
if (n <= 0) {
if (p > prev) result.cat(bytes, prev, p - prev);
@@ -2484,7 +2482,7 @@ public IRubyObject inspect19() {
}
continue;
}
int c = enc.mbcToCode(bytes, p, end);
final int c = enc.mbcToCode(bytes, p, end); int cc = 0;
p += n;
if ((asciiCompat || isUnicode) &&
(c == '"' || c == '\\' ||
@@ -2641,7 +2639,7 @@ private RubyString concatNumeric(Ruby runtime, int c) {
modify19(value.getRealSize() + cl);

if (enc == USASCIIEncoding.INSTANCE) {
if (c > 0xff) runtime.newRangeError(c + " out of char range");
if (c > 0xff) throw runtime.newRangeError(c + " out of char range");
if (c > 0x79) {
value.setEncoding(ASCIIEncoding.INSTANCE);
enc = value.getEncoding();
195 changes: 118 additions & 77 deletions core/src/main/java/org/jruby/ext/timeout/Timeout.java
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -35,6 +35,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyException;
import org.jruby.RubyFixnum;
import org.jruby.RubyKernel;
import org.jruby.RubyModule;
@@ -63,8 +64,8 @@ public void load(Ruby runtime, boolean wrap) throws IOException {
// Here we create an "anonymous" exception type used for unrolling the stack.
// MRI creates a new one for *every call* to timeout, which can be costly.
// We opt to use a single exception type for all cases to avoid this overhead.
RubyClass anonEx = runtime.defineClassUnder("AnonymousException", runtime.getException(), runtime.getException().getAllocator(), timeout);
anonEx.setBaseName(null); // clear basename so it's anonymous when raising
RubyClass anonException = runtime.defineClassUnder("AnonymousException", runtime.getException(), runtime.getException().getAllocator(), timeout);
anonException.setBaseName(null); // clear basename so it's anonymous when raising

// These are not really used by timeout, but exposed for compatibility
timeout.defineConstant("THIS_FILE", RubyRegexp.newRegexp(runtime, "timeout\\.rb", new RegexpOptions()));
@@ -83,8 +84,8 @@ public void load(Ruby runtime, boolean wrap) throws IOException {
public static class TimeoutToplevel {
@JRubyMethod(required = 1, optional = 1, visibility = PRIVATE)
public static IRubyObject timeout(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
RubyModule timeout = context.runtime.getModule("Timeout");
final RubyModule timeout = context.runtime.getModule("Timeout");

switch (args.length) {
case 1:
return Timeout.timeout(context, timeout, args[0], block);
@@ -115,24 +116,14 @@ public static IRubyObject timeout(final ThreadContext context, IRubyObject timeo
final AtomicBoolean latch = new AtomicBoolean(false);

IRubyObject id = new RubyObject(runtime, runtime.getObject());
Runnable timeoutRunnable = prepareRunnable(currentThread, runtime, latch, id);
Future timeoutFuture = null;
Runnable timeoutRunnable = TimeoutTask.newAnnonymousTask(currentThread, timeout, latch, id);

try {
try {
timeoutFuture = timeoutExecutor.schedule(timeoutRunnable,
(long)(seconds.convertToFloat().getDoubleValue() * 1000000), TimeUnit.MICROSECONDS);

return block.yield(context, seconds);
} finally {
killTimeoutThread(context, timeoutFuture, latch);
}
} catch (RaiseException re) {
if (re.getException().getInternalVariable("__identifier__") == id) {
return raiseTimeoutError(context, re);
} else {
throw re;
}
return yieldWithTimeout(context, seconds, block, timeoutRunnable, latch);
}
catch (RaiseException re) {
raiseTimeoutErrorIfMatches(context, timeout, re, id);
throw re;
}
}

@@ -154,30 +145,19 @@ public static IRubyObject timeout(final ThreadContext context, IRubyObject timeo
final AtomicBoolean latch = new AtomicBoolean(false);

IRubyObject id = new RubyObject(runtime, runtime.getObject());
RubyClass anonException = (RubyClass)runtime.getClassFromPath("Timeout::AnonymousException");
Runnable timeoutRunnable = exceptionType.isNil() ?
prepareRunnable(currentThread, runtime, latch, id) :
prepareRunnableWithException(currentThread, exceptionType, runtime, latch);
Future timeoutFuture = null;
TimeoutTask.newAnnonymousTask(currentThread, timeout, latch, id) :
TimeoutTask.newTaskWithException(currentThread, timeout, latch, exceptionType);

try {
try {
timeoutFuture = timeoutExecutor.schedule(timeoutRunnable,
(long)(seconds.convertToFloat().getDoubleValue() * 1000000), TimeUnit.MICROSECONDS);

return block.yield(context, seconds);
} finally {
killTimeoutThread(context, timeoutFuture, latch);
}
} catch (RaiseException re) {
return yieldWithTimeout(context, seconds, block, timeoutRunnable, latch);
}
catch (RaiseException re) {
// if it's the exception we're expecting
if (re.getException().getMetaClass() == anonException) {
if (re.getException().getMetaClass() == getAnonymousException(timeout)) {
// and we were not given a specific exception
if (exceptionType.isNil()) {
// and it's the exception intended for us
if (re.getException().getInternalVariable("__identifier__") == id) {
return raiseTimeoutError(context, re);
}
if ( exceptionType.isNil() ) {
raiseTimeoutErrorIfMatches(context, timeout, re, id);
}
}

@@ -186,36 +166,75 @@ public static IRubyObject timeout(final ThreadContext context, IRubyObject timeo
}
}

private static Runnable prepareRunnable(final RubyThread currentThread, final Ruby runtime, final AtomicBoolean latch, final IRubyObject id) {
Runnable timeoutRunnable = new Runnable() {
public void run() {
if (latch.compareAndSet(false, true)) {
if (currentThread.alive_p().isTrue()) {
RubyClass anonException = (RubyClass)runtime.getClassFromPath("Timeout::AnonymousException");
IRubyObject anonExceptionObj = anonException.newInstance(runtime.getCurrentContext(), runtime.newString("execution expired"), Block.NULL_BLOCK);
anonExceptionObj.getInternalVariables().setInternalVariable("__identifier__", id);
currentThread.internalRaise(new IRubyObject[] {anonExceptionObj});
}
}
}
};
return timeoutRunnable;
private static IRubyObject yieldWithTimeout(ThreadContext context,
final IRubyObject seconds, final Block block,
final Runnable runnable, final AtomicBoolean latch) throws RaiseException {
final double secs = seconds.convertToFloat().getDoubleValue();
Future timeoutFuture = null;
try {
timeoutFuture = timeoutExecutor.schedule(runnable, (long) (secs * 1000000), TimeUnit.MICROSECONDS);
return block.yield(context, seconds);
}
finally {
if ( timeoutFuture != null ) killTimeoutThread(context, timeoutFuture, latch);
// ... when timeoutFuture == null there's likely an error thrown from schedule
}
}

private static Runnable prepareRunnableWithException(final RubyThread currentThread, final IRubyObject exception, final Ruby runtime, final AtomicBoolean latch) {
Runnable timeoutRunnable = new Runnable() {
public void run() {
if (latch.compareAndSet(false, true)) {
if (currentThread.alive_p().isTrue()) {
currentThread.internalRaise(new IRubyObject[]{exception, runtime.newString("execution expired")});
}
private static class TimeoutTask implements Runnable {

final RubyThread currentThread;
final AtomicBoolean latch;

final IRubyObject timeout; // Timeout module
final IRubyObject id; // needed for 'anonymous' timeout (no exception passed)
final IRubyObject exception; // if there's exception (type) passed to timeout

private TimeoutTask(final RubyThread currentThread, final IRubyObject timeout,
final AtomicBoolean latch, final IRubyObject id, final IRubyObject exception) {
this.currentThread = currentThread;
this.timeout = timeout;
this.latch = latch;
this.id = id;
this.exception = exception;
}

static TimeoutTask newAnnonymousTask(final RubyThread currentThread, final IRubyObject timeout,
final AtomicBoolean latch, final IRubyObject id) {
return new TimeoutTask(currentThread, timeout, latch, id, null);
}

static TimeoutTask newTaskWithException(final RubyThread currentThread, final IRubyObject timeout,
final AtomicBoolean latch, final IRubyObject exception) {
return new TimeoutTask(currentThread, timeout, latch, null, exception);
}

public void run() {
if ( latch.compareAndSet(false, true) ) {
if ( exception == null ) {
raiseAnnonymous();
}
else {
raiseException();
}
}
};
return timeoutRunnable;
}

private void raiseAnnonymous() {
final Ruby runtime = timeout.getRuntime();
IRubyObject anonException = getAnonymousException(timeout).newInstance(runtime.getCurrentContext(), runtime.newString("execution expired"), Block.NULL_BLOCK);
anonException.getInternalVariables().setInternalVariable("__identifier__", id);
currentThread.internalRaise(new IRubyObject[] { anonException });
}

private void raiseException() {
final Ruby runtime = timeout.getRuntime();
currentThread.internalRaise(new IRubyObject[]{ exception, runtime.newString("execution expired") });
}

}

private static void killTimeoutThread(ThreadContext context, Future timeoutFuture, AtomicBoolean latch) {
private static void killTimeoutThread(ThreadContext context, final Future timeoutFuture, final AtomicBoolean latch) {
if (latch.compareAndSet(false, true) && timeoutFuture.cancel(false)) {
// ok, exception will not fire
if (timeoutExecutor instanceof ScheduledThreadPoolExecutor && timeoutFuture instanceof Runnable) {
@@ -225,10 +244,9 @@ private static void killTimeoutThread(ThreadContext context, Future timeoutFutur
// future is not cancellable, wait for it to run and then poll
try {
timeoutFuture.get();
} catch (ExecutionException ex) {
} catch (InterruptedException ex) {
}

catch (ExecutionException ex) {}
catch (InterruptedException ex) {}
// poll to propagate exception from child thread
context.pollThreadEvents();
}
@@ -237,19 +255,42 @@ private static void killTimeoutThread(ThreadContext context, Future timeoutFutur
private static IRubyObject raiseBecauseCritical(ThreadContext context) {
Ruby runtime = context.runtime;

return RubyKernel.raise(context, runtime.getKernel(), new IRubyObject[]{runtime.getThreadError(), runtime.newString("timeout within critical section")}, Block.NULL_BLOCK);
}

private static IRubyObject raiseTimeoutError(ThreadContext context, RaiseException re) {
Ruby runtime = context.runtime;

return RubyKernel.raise(
context,
runtime.getKernel(),
new IRubyObject[]{
runtime.getClassFromPath("Timeout::Error"),
re.getException().callMethod(context, "message"),
re.getException().callMethod(context, "backtrace")},
new IRubyObject[] {
runtime.getThreadError(),
runtime.newString("timeout within critical section")
},
Block.NULL_BLOCK);
}

private static IRubyObject raiseTimeoutErrorIfMatches(ThreadContext context,
final IRubyObject timeout, final RaiseException ex, final IRubyObject id) {
// check if it's the exception intended for us (@see prepareRunnable):
if ( ex.getException().getInternalVariable("__identifier__") == id ) {
final RubyException rubyException = ex.getException();

return RubyKernel.raise( // throws
context,
context.runtime.getKernel(),
new IRubyObject[] {
getClassFrom(timeout, "Error"), // Timeout::Error
rubyException.callMethod(context, "message"),
rubyException.callMethod(context, "backtrace")
},
Block.NULL_BLOCK);
}
return null;
}

// Timeout::AnonymousException (@see above)
private static RubyClass getAnonymousException(final IRubyObject timeout) {
return getClassFrom(timeout, "AnonymousException");
}

private static RubyClass getClassFrom(final IRubyObject timeout, final String name) {
return ((RubyModule) timeout).getClass(name); // Timeout::[name]
}

}