Skip to content

Commit

Permalink
Add a JRuby::Synchronized module that makes a class's methods (and it…
Browse files Browse the repository at this point in the history
…s subclasses' methods) be wrapped with synchronization.
  • Loading branch information
headius committed Mar 24, 2010
1 parent 0ab11d4 commit 10a114f
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 10 deletions.
4 changes: 1 addition & 3 deletions src/org/jruby/Ruby.java
Expand Up @@ -44,8 +44,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
Expand Down Expand Up @@ -128,7 +126,6 @@
import com.kenai.constantine.ConstantSet;
import com.kenai.constantine.platform.Errno;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.util.EnumSet;
import java.util.concurrent.atomic.AtomicLong;
Expand Down Expand Up @@ -1378,6 +1375,7 @@ private void initBuiltins() {
addLazyBuiltin("jruby/util.rb", "jruby/util", "org.jruby.RubyJRuby$UtilLibrary");
addLazyBuiltin("jruby/core_ext.rb", "jruby/core_ext", "org.jruby.RubyJRuby$CoreExtLibrary");
addLazyBuiltin("jruby/type.rb", "jruby/type", "org.jruby.RubyJRuby$TypeLibrary");
addLazyBuiltin("jruby/synchronized.rb", "jruby/synchronized", "org.jruby.RubyJRuby$SynchronizedLibrary");
addLazyBuiltin("iconv.jar", "iconv", "org.jruby.libraries.IConvLibrary");
addLazyBuiltin("nkf.jar", "nkf", "org.jruby.libraries.NKFLibrary");
addLazyBuiltin("stringio.jar", "stringio", "org.jruby.libraries.StringIOLibrary");
Expand Down
23 changes: 17 additions & 6 deletions src/org/jruby/RubyClass.java
Expand Up @@ -311,7 +311,7 @@ public static RubyClass createBootstrapClass(Ruby runtime, String name, RubyClas
protected RubyClass(Ruby runtime, RubyClass superClass, boolean objectSpace) {
super(runtime, runtime.getClassClass(), objectSpace);
this.runtime = runtime;
this.superClass = superClass; // this is the only case it might be null here (in MetaClass construction)
setSuperClass(superClass); // this is the only case it might be null here (in MetaClass construction)
}

/** separate path for MetaClass and IncludedModuleWrapper construction
Expand All @@ -321,7 +321,7 @@ protected RubyClass(Ruby runtime, RubyClass superClass, boolean objectSpace) {
protected RubyClass(Ruby runtime, RubyClass superClass, Generation generation, boolean objectSpace) {
super(runtime, runtime.getClassClass(), generation, objectSpace);
this.runtime = runtime;
this.superClass = superClass; // this is the only case it might be null here (in MetaClass construction)
setSuperClass(superClass); // this is the only case it might be null here (in MetaClass construction)
}

/** used by CLASS_ALLOCATOR (any Class' class will be a Class!)
Expand All @@ -338,7 +338,7 @@ protected RubyClass(Ruby runtime) {
*/
protected RubyClass(Ruby runtime, RubyClass superClazz) {
this(runtime);
superClass = superClazz;
setSuperClass(superClazz);
marshal = superClazz.marshal; // use parent's marshal
superClazz.addSubclass(this);
allocator = superClazz.allocator;
Expand All @@ -351,7 +351,7 @@ protected RubyClass(Ruby runtime, RubyClass superClazz) {
*/
protected RubyClass(Ruby runtime, RubyClass superClazz, CallSite[] extraCallSites) {
this(runtime);
this.superClass = superClazz;
setSuperClass(superClazz);
this.marshal = superClazz.marshal; // use parent's marshal
superClazz.addSubclass(this);

Expand Down Expand Up @@ -819,7 +819,7 @@ public IRubyObject initialize19(ThreadContext context, IRubyObject superObject,
}

private IRubyObject initializeCommon(RubyClass superClazz, Block block, boolean callInheritBeforeSuper) {
superClass = superClazz;
setSuperClass(superClazz);
allocator = superClazz.allocator;
makeMetaClass(superClazz.getMetaClass());

Expand Down Expand Up @@ -858,7 +858,7 @@ protected void setModuleSuperClass(RubyClass superClass) {
// add us to new superclass's child classes
superClass.addSubclass(this);
// update superclass reference
this.superClass = superClass;
setSuperClass(superClass);
}

public Collection subclasses(boolean includeDescendants) {
Expand Down Expand Up @@ -932,6 +932,17 @@ public synchronized void replaceSubclass(RubyClass subclass, RubyClass newSubcla
}
}

public void becomeSynchronized() {
// make this class and all subclasses sync
synchronized (getRuntime().getHierarchyLock()) {
super.becomeSynchronized();
Set<RubyClass> mySubclasses = subclasses;
if (mySubclasses != null) for (RubyClass subclass : mySubclasses) {
subclass.becomeSynchronized();
}
}
}

/**
* Invalidate all subclasses of this class by walking the set of all
* subclasses and asking them to invalidate themselves.
Expand Down
19 changes: 19 additions & 0 deletions src/org/jruby/RubyJRuby.java
Expand Up @@ -119,6 +119,18 @@ public static void createJRubyCoreExt(Ruby runtime) {
runtime.getString().defineAnnotatedMethods(JRubyStringExtensions.class);
}

public static class JRubySynchronizedMeta {
@JRubyMethod
public static IRubyObject append_features(IRubyObject self, IRubyObject target) {
if (target instanceof RubyClass && self instanceof RubyModule) { // should always be true
RubyClass targetModule = ((RubyClass)target);
targetModule.becomeSynchronized();
return ((RubyModule)self).append_features(target);
}
throw target.getRuntime().newTypeError(self + " can only be included into classes");
}
}

public static class ExtLibrary implements Library {
public void load(Ruby runtime, boolean wrap) throws IOException {
RubyJRuby.createJRubyExt(runtime);
Expand All @@ -132,6 +144,13 @@ public void load(Ruby runtime, boolean wrap) throws IOException {
RubyJRuby.createJRubyCoreExt(runtime);
}
}

public static class SynchronizedLibrary implements Library {
public void load(Ruby runtime, boolean wrap) throws IOException {
RubyModule syncModule = runtime.getOrCreateModule("JRuby").defineModuleUnder("Synchronized");
syncModule.getSingletonClass().defineAnnotatedMethods(JRubySynchronizedMeta.class);
}
}

public static class TypeLibrary implements Library {
public void load(Ruby runtime, boolean wrap) throws IOException {
Expand Down
31 changes: 30 additions & 1 deletion src/org/jruby/RubyModule.java
Expand Up @@ -76,6 +76,7 @@
import org.jruby.internal.runtime.methods.MethodMethod;
import org.jruby.internal.runtime.methods.ProcMethod;
import org.jruby.internal.runtime.methods.SimpleCallbackMethod;
import org.jruby.internal.runtime.methods.SynchronizedDynamicMethod;
import org.jruby.internal.runtime.methods.UndefinedMethod;
import org.jruby.internal.runtime.methods.WrapperMethod;
import org.jruby.javasupport.util.RuntimeHelpers;
Expand Down Expand Up @@ -330,6 +331,7 @@ public RubyClass getSuperClass() {
protected void setSuperClass(RubyClass superClass) {
// update superclass reference
this.superClass = superClass;
if (superClass != null && superClass.isSynchronized()) becomeSynchronized();
}

public RubyModule getParent() {
Expand Down Expand Up @@ -923,8 +925,35 @@ private CacheEntry cacheHit(String name) {
return null;
}

protected static abstract class CacheEntryFactory {
public abstract CacheEntry newCacheEntry(DynamicMethod method, Object token);
}

protected static CacheEntryFactory NormalCacheEntryFactory = new CacheEntryFactory() {
public CacheEntry newCacheEntry(DynamicMethod method, Object token) {
return new CacheEntry(method, token);
}
};

protected static CacheEntryFactory SynchronizedCacheEntryFactory = new CacheEntryFactory() {
public CacheEntry newCacheEntry(DynamicMethod method, Object token) {
return new CacheEntry(new SynchronizedDynamicMethod(method), token);
}
};

private volatile CacheEntryFactory cacheEntryFactory = NormalCacheEntryFactory;

// modifies this class only; used to make the Synchronized module synchronized
public void becomeSynchronized() {
cacheEntryFactory = SynchronizedCacheEntryFactory;
}

public boolean isSynchronized() {
return cacheEntryFactory == SynchronizedCacheEntryFactory;
}

private CacheEntry addToCache(String name, DynamicMethod method, Object token) {
CacheEntry entry = new CacheEntry(method, token);
CacheEntry entry = cacheEntryFactory.newCacheEntry(method, token);
getCachedMethodsForWrite().put(name, entry);

return entry;
Expand Down
@@ -0,0 +1,37 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package org.jruby.internal.runtime.methods;

import org.jruby.RubyModule;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

/**
*
* @author headius
*/
public class SynchronizedDynamicMethod extends DynamicMethod {
private final DynamicMethod delegate;

public SynchronizedDynamicMethod(DynamicMethod delegate) {
super(delegate.getImplementationClass(), delegate.getVisibility(), delegate.getCallConfig());
this.delegate = delegate;
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
synchronized (self) {
return delegate.call(context, self, clazz, name, args, block);
}
}

@Override
public DynamicMethod dup() {
return new SynchronizedDynamicMethod(delegate);
}

}

0 comments on commit 10a114f

Please sign in to comment.