Skip to content

Commit

Permalink
Showing 24 changed files with 366 additions and 193 deletions.
22 changes: 20 additions & 2 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -61,9 +61,11 @@
import org.jruby.javasupport.JavaSupport;
import org.jruby.javasupport.JavaSupportImpl;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.management.Caches;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.invokedynamic.InvokeDynamicSupport;
import org.jruby.runtime.opto.ConstantInvalidator;
import org.jruby.util.MRIRecursionGuard;
import org.jruby.util.StrptimeParser;
import org.jruby.util.StrptimeToken;
@@ -174,6 +176,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.ref.WeakReference;
import java.net.BindException;
import java.net.PortUnreachableException;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.Charset;
import java.security.SecureRandom;
@@ -266,6 +269,7 @@ private Ruby(RubyInstanceConfig config) {
this.beanManager = BeanManagerFactory.create(this, config.isManagementEnabled());
this.jitCompiler = new JITCompiler(this);
this.parserStats = new ParserStats(this);
this.caches = new Caches();

Random myRandom;
try {
@@ -292,7 +296,7 @@ private Ruby(RubyInstanceConfig config) {
this.runtimeCache = new RuntimeCache();
runtimeCache.initMethodCache(ClassIndex.MAX_CLASSES.ordinal() * MethodNames.values().length - 1);

checkpointInvalidator = OptoFactory.newConstantInvalidator();
checkpointInvalidator = OptoFactory.newConstantInvalidator(this);

if (config.isObjectSpaceEnabled()) {
objectSpacer = ENABLED_OBJECTSPACE;
@@ -311,6 +315,7 @@ public void registerMBeans() {
this.beanManager.register(configBean);
this.beanManager.register(parserStats);
this.beanManager.register(runtimeBean);
this.beanManager.register(caches);
}

void reinitialize(boolean reinitCore) {
@@ -890,6 +895,15 @@ public JITCompiler getJITCompiler() {
return jitCompiler;
}

/**
* Get the Caches management object.
*
* @return the current runtime's Caches management object
*/
public Caches getCaches() {
return caches;
}

/**
* @deprecated use #newInstance()
*/
@@ -4365,7 +4379,7 @@ public Invalidator getConstantInvalidator(String constantName) {
}

private Invalidator addConstantInvalidator(String constantName) {
Invalidator invalidator = OptoFactory.newConstantInvalidator();
final Invalidator invalidator = OptoFactory.newConstantInvalidator(this);
constantNameInvalidators.putIfAbsent(constantName, invalidator);

// fetch the invalidator back from the ConcurrentHashMap to ensure that
@@ -4958,6 +4972,9 @@ private MRIRecursionGuard oldRecursionGuard() {
// Compilation
private final JITCompiler jitCompiler;

// Cache invalidation
private final Caches caches;

// Note: this field and the following static initializer
// must be located be in this order!
private volatile static boolean securityRestricted = false;
@@ -5190,4 +5207,5 @@ private void deprecatedNetworkStackProperty() {
+ "Use JAVA_OPTS=-Djava.net.preferIPv4Stack=true OR prepend -J as a JRuby option.");
}
}

}
29 changes: 18 additions & 11 deletions core/src/main/java/org/jruby/RubyEnumerator.java
Original file line number Diff line number Diff line change
@@ -745,20 +745,28 @@ public synchronized void shutdown() {

// mark for death
die = true;
if (dissociateNexterThread(true)) doneObject = null;
}

private synchronized boolean dissociateNexterThread(boolean interrupt) {
Thread nexterThread = thread;

Thread myThread = thread;
if (myThread != null) {
if (DEBUG) System.out.println("clearing for shutdown");
if (nexterThread != null) {
if (DEBUG) System.out.println("dissociating nexter thread, interrupt: " + interrupt);

// we interrupt twice, to break out of iteration and
// (potentially) break out of final exchange
myThread.interrupt();
myThread.interrupt();
if (interrupt) {
// we interrupt twice, to break out of iteration and
// (potentially) break out of final exchange
nexterThread.interrupt();
nexterThread.interrupt();
}

// release references
thread = null;
doneObject = null;
return true;
}

return false;
}

@Override
@@ -910,9 +918,8 @@ public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block)
if (!die) out.put(finalObject);
}
catch (InterruptedException ie) { /* ignore */ }
}
finally {
thread = null; // disassociate this Nexter with the thread running it
} finally {
dissociateNexterThread(false); // disassociate this Nexter with the thread running it
}
}
}
107 changes: 85 additions & 22 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -53,6 +53,7 @@
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.java.proxies.ConcreteJavaProxy;
import org.jruby.platform.Platform;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
@@ -1055,49 +1056,72 @@ public Binding convertToBinding(IRubyObject scope) {
}
};

public static IRubyObject caller(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return caller20(context, recv, args, block);
@JRubyMethod(name = "caller", module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller(ThreadContext context, IRubyObject recv) {
return callerInternal(context, recv, null, null);
}

public static IRubyObject caller19(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return caller20(context, recv, args, block);
@JRubyMethod(name = "caller", module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller(ThreadContext context, IRubyObject recv, IRubyObject level) {
return callerInternal(context, recv, level, null);
}

@JRubyMethod(name = "caller", optional = 2, module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller20(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
@JRubyMethod(name = "caller", module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller(ThreadContext context, IRubyObject recv, IRubyObject level, IRubyObject length) {
return callerInternal(context, recv, level, length);
}

private static IRubyObject callerInternal(ThreadContext context, IRubyObject recv, IRubyObject level, IRubyObject length) {
Ruby runtime = context.runtime;
Integer[] ll = levelAndLengthFromArgs(runtime, args, 1);
Integer level = ll[0], length = ll[1];
Integer[] ll = levelAndLengthFromArgs(runtime, level, length, 1);
Integer levelInt = ll[0], lengthInt = ll[1];

return context.createCallerBacktrace(level, length, Thread.currentThread().getStackTrace());
return context.createCallerBacktrace(levelInt, lengthInt, Thread.currentThread().getStackTrace());
}

@JRubyMethod(optional = 2, module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller_locations(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
@JRubyMethod(module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller_locations(ThreadContext context, IRubyObject recv) {
return callerLocationsInternal(context, null, null);
}

@JRubyMethod(module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller_locations(ThreadContext context, IRubyObject recv, IRubyObject level) {
return callerLocationsInternal(context, level, null);
}

@JRubyMethod(module = true, visibility = PRIVATE, omit = true)
public static IRubyObject caller_locations(ThreadContext context, IRubyObject recv, IRubyObject level, IRubyObject length) {
return callerLocationsInternal(context, level, length);
}

private static IRubyObject callerLocationsInternal(ThreadContext context, IRubyObject level, IRubyObject length) {
Ruby runtime = context.runtime;
Integer[] ll = levelAndLengthFromArgs(runtime, args, 1);
Integer level = ll[0], length = ll[1];
Integer[] ll = levelAndLengthFromArgs(runtime, level, length, 1);
Integer levelInt = ll[0], lengthInt = ll[1];

return context.createCallerLocations(level, length, Thread.currentThread().getStackTrace());
return context.createCallerLocations(levelInt, lengthInt, Thread.currentThread().getStackTrace());
}

static Integer[] levelAndLengthFromArgs(Ruby runtime, IRubyObject[] args, int defaultLevel) {
/**
* Retrieve the level and length from given args, if non-null.
*/
static Integer[] levelAndLengthFromArgs(Ruby runtime, IRubyObject _level, IRubyObject _length, int defaultLevel) {
int level;
Integer length = null;
if (args.length > 1) {
level = RubyNumeric.fix2int(args[0]);
length = RubyNumeric.fix2int(args[1]);
} else if (args.length > 0 && args[0] instanceof RubyRange) {
RubyRange range = (RubyRange) args[0];
if (_length != null) {
level = RubyNumeric.fix2int(_level);
length = RubyNumeric.fix2int(_length);
} else if (_level != null && _level instanceof RubyRange) {
RubyRange range = (RubyRange) _level;
ThreadContext context = runtime.getCurrentContext();
level = RubyNumeric.fix2int(range.first(context));
length = RubyNumeric.fix2int(range.last(context)) - level;
if (!range.exclude_end_p().isTrue()){
length++;
}
length = length < 0 ? 0 : length;
} else if (args.length > 0) {
level = RubyNumeric.fix2int(args[0]);
} else if (_level != null) {
level = RubyNumeric.fix2int(_level);
} else {
level = defaultLevel;
}
@@ -2168,4 +2192,43 @@ public static IRubyObject methodMissing(ThreadContext context, IRubyObject recv,
return methodMissing(context, recv, name, lastVis, lastCallType, args);
}

@Deprecated
public static IRubyObject caller20(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return caller(context, recv, args, block);
}

@Deprecated
public static IRubyObject caller19(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return caller(context, recv, args, block);
}

@Deprecated
private static IRubyObject caller(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
switch (args.length) {
case 0:
return caller(context, recv);
case 1:
return caller(context, recv, args[0]);
case 2:
return caller(context, recv, args[0], args[1]);
default:
Arity.checkArgumentCount(context.runtime, args, 0, 2);
return null; // not reached
}
}

@Deprecated
public static IRubyObject caller_locations(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
switch (args.length) {
case 0:
return caller_locations(context, recv);
case 1:
return caller_locations(context, recv, args[0]);
case 2:
return caller_locations(context, recv, args[0], args[1]);
default:
Arity.checkArgumentCount(context.runtime, args, 0, 2);
return null; // not reached
}
}
}
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -1537,6 +1537,8 @@ protected DynamicMethod searchMethodCommon(String name) {
public void invalidateCacheDescendants() {
LOG.debug("{} invalidating descendants", baseName);

getRuntime().getCaches().incrementMethodInvalidations();

if (includingHierarchies.isEmpty()) {
// it's only us; just invalidate directly
methodInvalidator.invalidate();
215 changes: 140 additions & 75 deletions core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
@@ -70,6 +70,7 @@
import org.jruby.internal.runtime.ThreadService;
import org.jruby.java.proxies.ConcreteJavaProxy;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.Helpers;
@@ -1566,51 +1567,66 @@ public IRubyObject safe_level() {
throw getRuntime().newNotImplementedError("Thread-specific SAFE levels are not supported");
}

@JRubyMethod(name = "backtrace")
public IRubyObject backtrace(ThreadContext context) {
return backtrace20(context, NULL_ARRAY);
return backtraceInternal(context, null, null);
}

@JRubyMethod(name = "backtrace", optional = 2)
public IRubyObject backtrace20(ThreadContext context, IRubyObject[] args) {
ThreadContext myContext = getContext();
@JRubyMethod(name = "backtrace")
public IRubyObject backtrace(ThreadContext context, IRubyObject level) {
return backtraceInternal(context, level, null);
}

// context can be nil if we have not started or GC has claimed our context
if (myContext == null) return context.nil;
@JRubyMethod(name = "backtrace")
public IRubyObject backtrace(ThreadContext context, IRubyObject level, IRubyObject length) {
return backtraceInternal(context, level, length);
}

private IRubyObject backtraceInternal(ThreadContext context, IRubyObject level, IRubyObject length) {
ThreadContext myContext = getContext();
Thread nativeThread = getNativeThread();

// context can be nil if we have not started or GC has claimed our context
// nativeThread can be null if the thread has terminated and GC has claimed it
if (nativeThread == null) return context.nil;

// nativeThread may have finished
if (!nativeThread.isAlive()) return context.nil;
if (myContext == null || nativeThread == null || !nativeThread.isAlive()) return context.nil;

Ruby runtime = context.runtime;
Integer[] ll = RubyKernel.levelAndLengthFromArgs(runtime, args, 0);
Integer level = ll[0], length = ll[1];
Integer[] ll = RubyKernel.levelAndLengthFromArgs(runtime, level, length, 0);
Integer levelInt = ll[0], lengthInt = ll[1];

return myContext.createCallerBacktrace(level, length, getNativeThread().getStackTrace());
return myContext.createCallerBacktrace(levelInt, lengthInt, getNativeThread().getStackTrace());
}

@JRubyMethod(optional = 2)
public IRubyObject backtrace_locations(ThreadContext context, IRubyObject[] args) {
ThreadContext myContext = getContext();
@JRubyMethod
public IRubyObject backtrace_locations(ThreadContext context) {
return backtraceLocationsInternal(context, null, null);
}

@JRubyMethod
public IRubyObject backtrace_locations(ThreadContext context, IRubyObject level) {
return backtraceLocationsInternal(context, level, null);
}

if (myContext == null) return context.nil;
@JRubyMethod
public IRubyObject backtrace_locations(ThreadContext context, IRubyObject level, IRubyObject length) {
return backtraceLocationsInternal(context, level, length);
}

private IRubyObject backtraceLocationsInternal(ThreadContext context, IRubyObject level, IRubyObject length) {
ThreadContext myContext = getContext();
Thread nativeThread = getNativeThread();

// context can be nil if we have not started or GC has claimed our context
// nativeThread can be null if the thread has terminated and GC has claimed it
if (nativeThread == null) return context.nil;

// nativeThread may have finished
if (!nativeThread.isAlive()) return context.nil;
if (myContext == null || nativeThread == null || !nativeThread.isAlive()) return context.nil;

Ruby runtime = context.runtime;
Integer[] ll = RubyKernel.levelAndLengthFromArgs(runtime, args, 0);
Integer level = ll[0], length = ll[1];
Integer[] ll = RubyKernel.levelAndLengthFromArgs(runtime, level, length, 0);
Integer levelInt = ll[0], lengthInt = ll[1];

return myContext.createCallerLocations(level, length, getNativeThread().getStackTrace());
return myContext.createCallerLocations(levelInt, lengthInt, getNativeThread().getStackTrace());
}

@JRubyMethod(name = "report_on_exception=")
@@ -1811,72 +1827,86 @@ public boolean select(Channel channel, OpenFile fptr, int ops, long timeout) {
if (channel instanceof SelectableChannel && fd != null) {
SelectableChannel selectable = (SelectableChannel)channel;

synchronized (selectable.blockingLock()) {
boolean oldBlocking = selectable.isBlocking();
// ensure we have fptr locked, but release it to avoid deadlock
boolean locked = false;
if (fptr != null) {
locked = fptr.lock();
fptr.unlock();
}
try {
synchronized (selectable.blockingLock()) {
boolean oldBlocking = selectable.isBlocking();

SelectionKey key;
try {
selectable.configureBlocking(false);

SelectionKey key;
try {
selectable.configureBlocking(false);
if (fptr != null) fptr.addBlockingThread(this);
currentSelector = getRuntime().getSelectorPool().get(selectable.provider());

if (fptr != null) fptr.addBlockingThread(this);
currentSelector = getRuntime().getSelectorPool().get(selectable.provider());
key = selectable.register(currentSelector, ops);

key = selectable.register(currentSelector, ops);
beforeBlockingCall();
int result;

beforeBlockingCall();
int result;
if (timeout < 0) {
result = currentSelector.select();
} else if (timeout == 0) {
result = currentSelector.selectNow();
} else {
result = currentSelector.select(timeout);
}
if (timeout < 0) {
result = currentSelector.select();
} else if (timeout == 0) {
result = currentSelector.selectNow();
} else {
result = currentSelector.select(timeout);
}

// check for thread events, in case we've been woken up to die
pollThreadEvents();
// check for thread events, in case we've been woken up to die
pollThreadEvents();

if (result == 1) {
Set<SelectionKey> keySet = currentSelector.selectedKeys();
if (result == 1) {
Set<SelectionKey> keySet = currentSelector.selectedKeys();

if (keySet.iterator().next() == key) {
return true;
if (keySet.iterator().next() == key) {
return true;
}
}
}

return false;
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
} finally {
// Note: I don't like ignoring these exceptions, but it's
// unclear how likely they are to happen or what damage we
// might do by ignoring them. Note that the pieces are separate
// so that we can ensure one failing does not affect the others
// running.

// shut down and null out the selector
try {
if (currentSelector != null) {
getRuntime().getSelectorPool().put(currentSelector);
}
} catch (Exception e) {
// ignore
return false;
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
} finally {
currentSelector = null;
}
// Note: I don't like ignoring these exceptions, but it's
// unclear how likely they are to happen or what damage we
// might do by ignoring them. Note that the pieces are separate
// so that we can ensure one failing does not affect the others
// running.

// shut down and null out the selector
try {
if (currentSelector != null) {
getRuntime().getSelectorPool().put(currentSelector);
}
} catch (Exception e) {
// ignore
} finally {
currentSelector = null;
}

// remove this thread as a blocker against the given IO
if (fptr != null) fptr.removeBlockingThread(this);
// remove this thread as a blocker against the given IO
if (fptr != null) fptr.removeBlockingThread(this);

// go back to previous blocking state on the selectable
try {
selectable.configureBlocking(oldBlocking);
} catch (Exception e) {
// ignore
}
// go back to previous blocking state on the selectable
try {
selectable.configureBlocking(oldBlocking);
} catch (Exception e) {
// ignore
}

// clear thread state from blocking call
afterBlockingCall();
// clear thread state from blocking call
afterBlockingCall();
}
}
} finally {
if (fptr != null) {
fptr.lock();
if (locked) fptr.unlock();
}
}
} else {
@@ -2115,4 +2145,39 @@ public static IRubyObject exclusive(ThreadContext context, IRubyObject recv, Blo
ts.setCritical(critical);
}
}

@Deprecated
public IRubyObject backtrace20(ThreadContext context, IRubyObject[] args) {
return backtrace(context);
}

@Deprecated
public IRubyObject backtrace(ThreadContext context, IRubyObject[] args) {
switch (args.length) {
case 0:
return backtrace(context);
case 1:
return backtrace(context, args[0]);
case 2:
return backtrace(context, args[0], args[1]);
default:
Arity.checkArgumentCount(context.runtime, args, 0, 2);
return null; // not reached
}
}

@Deprecated
public IRubyObject backtrace_locations(ThreadContext context, IRubyObject[] args) {
switch (args.length) {
case 0:
return backtrace_locations(context);
case 1:
return backtrace_locations(context, args[0]);
case 2:
return backtrace_locations(context, args[0], args[1]);
default:
Arity.checkArgumentCount(context.runtime, args, 0, 2);
return null; // not reached
}
}
}
12 changes: 12 additions & 0 deletions core/src/main/java/org/jruby/ext/jruby/JRubyUtilLibrary.java
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
@@ -107,4 +108,15 @@ public static IRubyObject unseeded_hash(ThreadContext context, IRubyObject recv)
return CoreExt.String.unseeded_hash(context, recv);
}
}

@JRubyMethod(name = "cache_stats", module = true)
public static IRubyObject cache_stats(ThreadContext context, IRubyObject self) {
Ruby runtime = context.runtime;

RubyHash stat = RubyHash.newHash(runtime);
stat.op_aset(context, runtime.newSymbol("method_invalidation_count"), runtime.newFixnum(runtime.getCaches().getMethodInvalidationCount()));
stat.op_aset(context, runtime.newSymbol("constant_invalidation_count"), runtime.newFixnum(runtime.getCaches().getConstantInvalidationCount()));

return stat;
}
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/management/BeanManager.java
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ public interface BeanManager {

void register(ParserStatsMBean parserStats);

void register(MethodCacheMBean methodCache);
void register(CachesMBean methodCache);

void register(Runtime runtime);

Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ private static class DummyBeanManager implements BeanManager {
public void register(JITCompilerMBean jitCompiler) {}
public void register(ConfigMBean config) {}
public void register(ParserStatsMBean parserStats) {}
public void register(MethodCacheMBean methodCache) {}
public void register(CachesMBean methodCache) {}
public void register(Runtime runtime) {}
public void unregisterCompiler() {}
public void unregisterConfig() {}
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/management/BeanManagerImpl.java
Original file line number Diff line number Diff line change
@@ -46,8 +46,8 @@ public void register(ParserStatsMBean parserStats) {
if (managementEnabled) register(base + "service=ParserStats", parserStats);
}

public void register(MethodCacheMBean methodCache) {
if (managementEnabled) register(base + "service=MethodCache", methodCache);
public void register(CachesMBean caches) {
if (managementEnabled) register(base + "service=Caches", caches);
}

public void register(Runtime runtime) {
26 changes: 26 additions & 0 deletions core/src/main/java/org/jruby/management/Caches.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.jruby.management;

import java.util.concurrent.atomic.AtomicLong;

public class Caches implements CachesMBean {
private final AtomicLong methodInvalidations = new AtomicLong(0);
private final AtomicLong constantInvalidations = new AtomicLong(0);

@Override
public long getMethodInvalidationCount() {
return methodInvalidations.get();
}

@Override
public long getConstantInvalidationCount() {
return constantInvalidations.get();
}

public long incrementMethodInvalidations() {
return methodInvalidations.incrementAndGet();
}

public long incrementConstantInvalidations() {
return constantInvalidations.incrementAndGet();
}
}
6 changes: 6 additions & 0 deletions core/src/main/java/org/jruby/management/CachesMBean.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.jruby.management;

public interface CachesMBean {
public long getMethodInvalidationCount();
public long getConstantInvalidationCount();
}
12 changes: 0 additions & 12 deletions core/src/main/java/org/jruby/management/MethodCacheMBean.java

This file was deleted.

9 changes: 6 additions & 3 deletions core/src/main/java/org/jruby/runtime/Helpers.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.jruby.runtime;

import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Member;

import java.net.PortUnreachableException;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -184,7 +184,8 @@ public static Errno errnoFromException(Throwable t) {
// All errors to sysread should be SystemCallErrors, but on a closed stream
// Ruby returns an IOError. Java throws same exception for all errors so
// we resort to this hack...
switch ( errorMessage ) {

switch (errorMessage) {
case "Bad file descriptor":
return Errno.EBADF;
case "File not open":
@@ -215,6 +216,8 @@ public static Errno errnoFromException(Throwable t) {
case "Is a directory":
return Errno.EISDIR;
}
} else if (t instanceof PortUnreachableException) {
return Errno.ECONNREFUSED;
}
return null;
}
8 changes: 6 additions & 2 deletions core/src/main/java/org/jruby/runtime/ThreadContext.java
Original file line number Diff line number Diff line change
@@ -738,7 +738,9 @@ public IRubyObject createCallerBacktrace(int level, Integer length, StackTraceEl
RubyStackTraceElement[] fullTrace = getFullTrace(length, stacktrace);

int traceLength = safeLength(level, length, fullTrace);
if (traceLength < 0) return nil;

// MRI started returning [] instead of nil some time after 1.9 (#4891)
if (traceLength < 0) return runtime.newEmptyArray();

final RubyClass stringClass = runtime.getString();
final IRubyObject[] traceArray = new IRubyObject[traceLength];
@@ -766,7 +768,9 @@ public IRubyObject createCallerLocations(int level, Integer length, StackTraceEl
RubyStackTraceElement[] fullTrace = getFullTrace(length, stacktrace);

int traceLength = safeLength(level, length, fullTrace);
if (traceLength < 0) return nil;

// MRI started returning [] instead of nil some time after 1.9 (#4891)
if (traceLength < 0) return runtime.newEmptyArray();

RubyArray backTrace = RubyThread.Location.newLocationArray(runtime, fullTrace, level, traceLength);
if (RubyInstanceConfig.LOG_CALLERS) TraceType.logCaller(backTrace);
30 changes: 30 additions & 0 deletions core/src/main/java/org/jruby/runtime/opto/ConstantInvalidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.jruby.runtime.opto;

import org.jruby.management.Caches;

import java.util.List;

/**
* A validator specific to how we manage Ruby constants.
*/
public class ConstantInvalidator extends SwitchPointInvalidator {
private final Caches caches;

public ConstantInvalidator(Caches caches) {
this.caches = caches;
}

@Override
public void invalidate() {
// order is important; invalidate before increment
super.invalidate();
caches.incrementConstantInvalidations();
}

@Override
public void invalidateAll(List<Invalidator> invalidators) {
// order is important; invalidate before increment
super.invalidateAll(invalidators);
caches.incrementConstantInvalidations();
}
}
9 changes: 3 additions & 6 deletions core/src/main/java/org/jruby/runtime/opto/OptoFactory.java
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
***** END LICENSE BLOCK *****/
package org.jruby.runtime.opto;

import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.runtime.ThreadContext;
import org.jruby.util.cli.Options;
@@ -48,8 +49,8 @@ public static final Object newConstantWrapper(Class type, Object object) {
return OptoFactory.CONSTANT_FACTORY.create(type, object);
}

public static Invalidator newConstantInvalidator() {
return new SwitchPointInvalidator();
public static Invalidator newConstantInvalidator(Ruby runtime) {
return new ConstantInvalidator(runtime.getCaches());
}

private static Boolean indyEnabled() {
@@ -74,10 +75,6 @@ public static Invalidator newMethodInvalidator(RubyModule module) {
return new GenerationInvalidator(module);
}

private static Boolean indyConstants() {
return Options.INVOKEDYNAMIC_CACHE_CONSTANTS.load();
}

private static void disableIndy() {
Options.COMPILE_INVOKEDYNAMIC.force("false");
}
2 changes: 1 addition & 1 deletion lib/pom.rb
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ def initialize( name, version, default_spec = true )
default_gems =
[
ImportedGem.new( 'jruby-openssl', '0.9.21' ),
ImportedGem.new( 'jruby-readline', '1.1.1' ),
ImportedGem.new( 'jruby-readline', '1.2.0' ),
ImportedGem.new( 'rake', '${rake.version}' ),
ImportedGem.new( 'rdoc', '${rdoc.version}' ),
ImportedGem.new( 'minitest', '${minitest.version}' ),
2 changes: 1 addition & 1 deletion lib/pom.xml
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ DO NOT MODIFIY - GENERATED CODE
<dependency>
<groupId>rubygems</groupId>
<artifactId>jruby-readline</artifactId>
<version>1.1.1</version>
<version>1.2.0</version>
<type>gem</type>
<scope>provided</scope>
<exclusions>
48 changes: 0 additions & 48 deletions lib/ruby/stdlib/ffi/platform/ppc-darwin/syslog.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/ruby/stdlib/racc/parser.rb
Original file line number Diff line number Diff line change
@@ -322,7 +322,7 @@ def _racc_do_parse_rb(arg, in_debug)
# RECEIVER#METHOD_ID is a method to get next token.
# It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
def yyparse(recv, mid)
__send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true)
__send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), false)
end

def _racc_yyparse_rb(recv, mid, arg, c_debug)
4 changes: 2 additions & 2 deletions spec/tags/ruby/library/weakref/__getobj___tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
fails(travis):WeakRef#__getobj__ returns the object if it is reachable
fails(travis):WeakRef#__getobj__ raises WeakRef::RefError if the object is no longer reachable
fails(depends on GC behavior, fails intermittently):WeakRef#__getobj__ raises WeakRef::RefError if the object is no longer reachable
fails(depends on GC behavior, fails intermittently):WeakRef#__getobj__ returns the object if it is reachable
4 changes: 2 additions & 2 deletions spec/tags/ruby/library/weakref/weakref_alive_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
fails:WeakRef#weakref_alive? returns true if the object is reachable
fails:WeakRef#weakref_alive? returns a falsey value if the object is no longer reachable
fails(depends on GC behavior, fails intermittently):WeakRef#weakref_alive? returns true if the object is reachable
fails(depends on GC behavior, fails intermittently):WeakRef#weakref_alive? returns a falsey value if the object is no longer reachable
2 changes: 1 addition & 1 deletion test/mri/excludes/TestProcess.rb
Original file line number Diff line number Diff line change
@@ -47,7 +47,6 @@
exclude :test_gid_sid_available?, "unimplemented"
exclude :test_kill_at_spawn_failure, "thread lifecycle at process boundaries?"
exclude :test_no_curdir, "won't work due changed wd detection (since 1e30600bdbbf483a)"
exclude :test_popen_cloexec, "unsupported"
exclude :test_popen_exit, "terminates test run"
exclude :test_popen_noshell, "Fails on Travis"
exclude :test_popen_wordsplit, "needs investigation"
@@ -66,3 +65,4 @@
exclude :test_to_hash_on_arguments, "needs investigation"
exclude :test_uid_re_exchangeable_p, "unimplemented"
exclude :test_uid_sid_available?, "unimplemented"
exclude :test_wait_exception, "causes spurious interrupts due to racey thread state checks"

0 comments on commit 76be88a

Please sign in to comment.