mirror of
https://github.com/bitwarden/help
synced 2025-12-16 00:03:41 +00:00
Promote to Master (#748)
* initial commit
* adding quotes for the array error
* Create Gemfile
* Create Gemfile.lock
* add .nvmrc and .node-version
* removed /article from URL
* update links to work with netlify
* more fixed links
* link fixes
* update bad links
* Update netlify.toml
toml test for redirects
* article redirect
* link fixes
* Update index.html
* Update netlify.toml
* Update _config.yml
* Update netlify.toml
* Update netlify.toml
* Update netlify.toml
* Update netlify.toml
* Update netlify.toml
* add article back into URL for launch
* Update netlify.toml
* Update netlify.toml
* add order to categories front matter
* Update netlify.toml
* update
* sidemenu update
* Revert "sidemenu update"
This reverts commit 5441c3d35c.
* update order prop
* Navbar updates per Gary and compiler warnings
* font/style tweaks
* Update sidebar.html
* Stage Release Documentation (#739)
* initial drafts
* rewrite Custom Fields article to prioritize new context-menu option & better organize ancillary information
* edit
* edit
* Custom Field Context Menu & CAPTCHA item in release notes
* SSO relink event
* update rn
* small edits
* improve release notes titles
* fix side menu
* Edits courtest of mportune!
* update order
* link fixes
* link cleanup
* image updates and a link
* fix trailing slash
Co-authored-by: DanHillesheim <79476558+DanHillesheim@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
63f78e8979
commit
906e2ca0dd
@@ -0,0 +1,17 @@
|
||||
import org.jruby.Ruby;
|
||||
import org.jruby.runtime.load.BasicLibraryService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ConcurrentRubyService implements BasicLibraryService {
|
||||
|
||||
public boolean basicLoad(final Ruby runtime) throws IOException {
|
||||
new com.concurrent_ruby.ext.AtomicReferenceLibrary().load(runtime, false);
|
||||
new com.concurrent_ruby.ext.JavaAtomicBooleanLibrary().load(runtime, false);
|
||||
new com.concurrent_ruby.ext.JavaAtomicFixnumLibrary().load(runtime, false);
|
||||
new com.concurrent_ruby.ext.JavaSemaphoreLibrary().load(runtime, false);
|
||||
new com.concurrent_ruby.ext.SynchronizationLibrary().load(runtime, false);
|
||||
new com.concurrent_ruby.ext.JRubyMapBackendLibrary().load(runtime, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.concurrent_ruby.ext;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||
import org.jruby.Ruby;
|
||||
import org.jruby.RubyClass;
|
||||
import org.jruby.RubyModule;
|
||||
import org.jruby.RubyNumeric;
|
||||
import org.jruby.RubyObject;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.runtime.load.Library;
|
||||
|
||||
/**
|
||||
* This library adds an atomic reference type to JRuby for use in the atomic
|
||||
* library. We do a native version to avoid the implicit value coercion that
|
||||
* normally happens through JI.
|
||||
*
|
||||
* @author headius
|
||||
*/
|
||||
public class AtomicReferenceLibrary implements Library {
|
||||
public void load(Ruby runtime, boolean wrap) throws IOException {
|
||||
RubyModule concurrentMod = runtime.defineModule("Concurrent");
|
||||
RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicReference", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR);
|
||||
try {
|
||||
sun.misc.Unsafe.class.getMethod("getAndSetObject", Object.class);
|
||||
atomicCls.setAllocator(JRUBYREFERENCE8_ALLOCATOR);
|
||||
} catch (Exception e) {
|
||||
// leave it as Java 6/7 version
|
||||
}
|
||||
atomicCls.defineAnnotatedMethods(JRubyReference.class);
|
||||
}
|
||||
|
||||
private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JRubyReference(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
private static final ObjectAllocator JRUBYREFERENCE8_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JRubyReference8(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
@JRubyClass(name="JRubyReference", parent="Object")
|
||||
public static class JRubyReference extends RubyObject {
|
||||
volatile IRubyObject reference;
|
||||
|
||||
static final sun.misc.Unsafe UNSAFE;
|
||||
static final long referenceOffset;
|
||||
|
||||
static {
|
||||
try {
|
||||
UNSAFE = UnsafeHolder.U;
|
||||
Class k = JRubyReference.class;
|
||||
referenceOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("reference"));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public JRubyReference(Ruby runtime, RubyClass klass) {
|
||||
super(runtime, klass);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context) {
|
||||
UNSAFE.putObject(this, referenceOffset, context.nil);
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context, IRubyObject value) {
|
||||
UNSAFE.putObject(this, referenceOffset, value);
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"get", "value"})
|
||||
public IRubyObject get() {
|
||||
return reference;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"set", "value="})
|
||||
public IRubyObject set(IRubyObject newValue) {
|
||||
UNSAFE.putObjectVolatile(this, referenceOffset, newValue);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"compare_and_set", "compare_and_swap"})
|
||||
public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
|
||||
Ruby runtime = context.runtime;
|
||||
|
||||
if (expectedValue instanceof RubyNumeric) {
|
||||
// numerics are not always idempotent in Ruby, so we need to do slower logic
|
||||
return compareAndSetNumeric(context, expectedValue, newValue);
|
||||
}
|
||||
|
||||
return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"get_and_set", "swap"})
|
||||
public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
|
||||
// less-efficient version for Java 6 and 7
|
||||
while (true) {
|
||||
IRubyObject oldValue = get();
|
||||
if (UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue)) {
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
|
||||
Ruby runtime = context.runtime;
|
||||
|
||||
// loop until:
|
||||
// * reference CAS would succeed for same-valued objects
|
||||
// * current and expected have different values as determined by #equals
|
||||
while (true) {
|
||||
IRubyObject current = reference;
|
||||
|
||||
if (!(current instanceof RubyNumeric)) {
|
||||
// old value is not numeric, CAS fails
|
||||
return runtime.getFalse();
|
||||
}
|
||||
|
||||
RubyNumeric currentNumber = (RubyNumeric)current;
|
||||
if (!currentNumber.equals(expectedValue)) {
|
||||
// current number does not equal expected, fail CAS
|
||||
return runtime.getFalse();
|
||||
}
|
||||
|
||||
// check that current has not changed, or else allow loop to repeat
|
||||
boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue);
|
||||
if (success) {
|
||||
// value is same and did not change in interim...success
|
||||
return runtime.getTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UnsafeHolder {
|
||||
private UnsafeHolder(){}
|
||||
|
||||
public static final sun.misc.Unsafe U = loadUnsafe();
|
||||
|
||||
private static sun.misc.Unsafe loadUnsafe() {
|
||||
try {
|
||||
Class unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||
Field f = unsafeClass.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
return (sun.misc.Unsafe) f.get(null);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class JRubyReference8 extends JRubyReference {
|
||||
public JRubyReference8(Ruby runtime, RubyClass klass) {
|
||||
super(runtime, klass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
|
||||
// efficient version for Java 8
|
||||
return (IRubyObject)UNSAFE.getAndSetObject(this, referenceOffset, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
package com.concurrent_ruby.ext;
|
||||
|
||||
import org.jruby.*;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap;
|
||||
import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8;
|
||||
import com.concurrent_ruby.ext.jsr166e.nounsafe.*;
|
||||
import org.jruby.runtime.Block;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.runtime.load.Library;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jruby.runtime.Visibility.PRIVATE;
|
||||
|
||||
/**
|
||||
* Native Java implementation to avoid the JI overhead.
|
||||
*
|
||||
* @author thedarkone
|
||||
*/
|
||||
public class JRubyMapBackendLibrary implements Library {
|
||||
public void load(Ruby runtime, boolean wrap) throws IOException {
|
||||
|
||||
RubyModule concurrentMod = runtime.defineModule("Concurrent");
|
||||
RubyModule thread_safeMod = concurrentMod.defineModuleUnder("Collection");
|
||||
RubyClass jrubyRefClass = thread_safeMod.defineClassUnder("JRubyMapBackend", runtime.getObject(), BACKEND_ALLOCATOR);
|
||||
jrubyRefClass.setAllocator(BACKEND_ALLOCATOR);
|
||||
jrubyRefClass.defineAnnotatedMethods(JRubyMapBackend.class);
|
||||
}
|
||||
|
||||
private static final ObjectAllocator BACKEND_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JRubyMapBackend(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
@JRubyClass(name="JRubyMapBackend", parent="Object")
|
||||
public static class JRubyMapBackend extends RubyObject {
|
||||
// Defaults used by the CHM
|
||||
static final int DEFAULT_INITIAL_CAPACITY = 16;
|
||||
static final float DEFAULT_LOAD_FACTOR = 0.75f;
|
||||
|
||||
public static final boolean CAN_USE_UNSAFE_CHM = canUseUnsafeCHM();
|
||||
|
||||
private ConcurrentHashMap<IRubyObject, IRubyObject> map;
|
||||
|
||||
private static ConcurrentHashMap<IRubyObject, IRubyObject> newCHM(int initialCapacity, float loadFactor) {
|
||||
if (CAN_USE_UNSAFE_CHM) {
|
||||
return new ConcurrentHashMapV8<IRubyObject, IRubyObject>(initialCapacity, loadFactor);
|
||||
} else {
|
||||
return new com.concurrent_ruby.ext.jsr166e.nounsafe.ConcurrentHashMapV8<IRubyObject, IRubyObject>(initialCapacity, loadFactor);
|
||||
}
|
||||
}
|
||||
|
||||
private static ConcurrentHashMap<IRubyObject, IRubyObject> newCHM() {
|
||||
return newCHM(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
|
||||
}
|
||||
|
||||
private static boolean canUseUnsafeCHM() {
|
||||
try {
|
||||
new com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8(); // force class load and initialization
|
||||
return true;
|
||||
} catch (Throwable t) { // ensuring we really do catch everything
|
||||
// Doug's Unsafe setup errors always have this "Could not ini.." message
|
||||
if (isCausedBySecurityException(t)) {
|
||||
return false;
|
||||
}
|
||||
throw (t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCausedBySecurityException(Throwable t) {
|
||||
while (t != null) {
|
||||
if ((t.getMessage() != null && t.getMessage().contains("Could not initialize intrinsics")) || t instanceof SecurityException) {
|
||||
return true;
|
||||
}
|
||||
t = t.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public JRubyMapBackend(Ruby runtime, RubyClass klass) {
|
||||
super(runtime, klass);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context) {
|
||||
map = newCHM();
|
||||
return context.getRuntime().getNil();
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context, IRubyObject options) {
|
||||
map = toCHM(context, options);
|
||||
return context.getRuntime().getNil();
|
||||
}
|
||||
|
||||
private ConcurrentHashMap<IRubyObject, IRubyObject> toCHM(ThreadContext context, IRubyObject options) {
|
||||
Ruby runtime = context.getRuntime();
|
||||
if (!options.isNil() && options.respondsTo("[]")) {
|
||||
IRubyObject rInitialCapacity = options.callMethod(context, "[]", runtime.newSymbol("initial_capacity"));
|
||||
IRubyObject rLoadFactor = options.callMethod(context, "[]", runtime.newSymbol("load_factor"));
|
||||
int initialCapacity = !rInitialCapacity.isNil() ? RubyNumeric.num2int(rInitialCapacity.convertToInteger()) : DEFAULT_INITIAL_CAPACITY;
|
||||
float loadFactor = !rLoadFactor.isNil() ? (float)RubyNumeric.num2dbl(rLoadFactor.convertToFloat()) : DEFAULT_LOAD_FACTOR;
|
||||
return newCHM(initialCapacity, loadFactor);
|
||||
} else {
|
||||
return newCHM();
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "[]", required = 1)
|
||||
public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
|
||||
IRubyObject value;
|
||||
return ((value = map.get(key)) == null) ? context.getRuntime().getNil() : value;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"[]="}, required = 2)
|
||||
public IRubyObject op_aset(IRubyObject key, IRubyObject value) {
|
||||
map.put(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject put_if_absent(IRubyObject key, IRubyObject value) {
|
||||
IRubyObject result = map.putIfAbsent(key, value);
|
||||
return result == null ? getRuntime().getNil() : result;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject compute_if_absent(final ThreadContext context, final IRubyObject key, final Block block) {
|
||||
return map.computeIfAbsent(key, new ConcurrentHashMap.Fun<IRubyObject, IRubyObject>() {
|
||||
@Override
|
||||
public IRubyObject apply(IRubyObject key) {
|
||||
return block.yieldSpecific(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject compute_if_present(final ThreadContext context, final IRubyObject key, final Block block) {
|
||||
IRubyObject result = map.computeIfPresent(key, new ConcurrentHashMap.BiFun<IRubyObject, IRubyObject, IRubyObject>() {
|
||||
@Override
|
||||
public IRubyObject apply(IRubyObject key, IRubyObject oldValue) {
|
||||
IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue);
|
||||
return result.isNil() ? null : result;
|
||||
}
|
||||
});
|
||||
return result == null ? context.getRuntime().getNil() : result;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject compute(final ThreadContext context, final IRubyObject key, final Block block) {
|
||||
IRubyObject result = map.compute(key, new ConcurrentHashMap.BiFun<IRubyObject, IRubyObject, IRubyObject>() {
|
||||
@Override
|
||||
public IRubyObject apply(IRubyObject key, IRubyObject oldValue) {
|
||||
IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue);
|
||||
return result.isNil() ? null : result;
|
||||
}
|
||||
});
|
||||
return result == null ? context.getRuntime().getNil() : result;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject merge_pair(final ThreadContext context, final IRubyObject key, final IRubyObject value, final Block block) {
|
||||
IRubyObject result = map.merge(key, value, new ConcurrentHashMap.BiFun<IRubyObject, IRubyObject, IRubyObject>() {
|
||||
@Override
|
||||
public IRubyObject apply(IRubyObject oldValue, IRubyObject newValue) {
|
||||
IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue);
|
||||
return result.isNil() ? null : result;
|
||||
}
|
||||
});
|
||||
return result == null ? context.getRuntime().getNil() : result;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public RubyBoolean replace_pair(IRubyObject key, IRubyObject oldValue, IRubyObject newValue) {
|
||||
return getRuntime().newBoolean(map.replace(key, oldValue, newValue));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "key?", required = 1)
|
||||
public RubyBoolean has_key_p(IRubyObject key) {
|
||||
return map.containsKey(key) ? getRuntime().getTrue() : getRuntime().getFalse();
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject key(IRubyObject value) {
|
||||
final IRubyObject key = map.findKey(value);
|
||||
return key == null ? getRuntime().getNil() : key;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject replace_if_exists(IRubyObject key, IRubyObject value) {
|
||||
IRubyObject result = map.replace(key, value);
|
||||
return result == null ? getRuntime().getNil() : result;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject get_and_set(IRubyObject key, IRubyObject value) {
|
||||
IRubyObject result = map.put(key, value);
|
||||
return result == null ? getRuntime().getNil() : result;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject delete(IRubyObject key) {
|
||||
IRubyObject result = map.remove(key);
|
||||
return result == null ? getRuntime().getNil() : result;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public RubyBoolean delete_pair(IRubyObject key, IRubyObject value) {
|
||||
return getRuntime().newBoolean(map.remove(key, value));
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject clear() {
|
||||
map.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject each_pair(ThreadContext context, Block block) {
|
||||
for (Map.Entry<IRubyObject,IRubyObject> entry : map.entrySet()) {
|
||||
block.yieldSpecific(context, entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public RubyFixnum size(ThreadContext context) {
|
||||
return context.getRuntime().newFixnum(map.size());
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject get_or_default(IRubyObject key, IRubyObject defaultValue) {
|
||||
return map.getValueOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@JRubyMethod(visibility = PRIVATE)
|
||||
public JRubyMapBackend initialize_copy(ThreadContext context, IRubyObject other) {
|
||||
map = newCHM();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.concurrent_ruby.ext;
|
||||
|
||||
import org.jruby.Ruby;
|
||||
import org.jruby.RubyBoolean;
|
||||
import org.jruby.RubyClass;
|
||||
import org.jruby.RubyModule;
|
||||
import org.jruby.RubyNil;
|
||||
import org.jruby.RubyObject;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.runtime.load.Library;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class JavaAtomicBooleanLibrary implements Library {
|
||||
|
||||
public void load(Ruby runtime, boolean wrap) throws IOException {
|
||||
RubyModule concurrentMod = runtime.defineModule("Concurrent");
|
||||
RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicBoolean", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR);
|
||||
atomicCls.defineAnnotatedMethods(JavaAtomicBoolean.class);
|
||||
}
|
||||
|
||||
private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JavaAtomicBoolean(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
@JRubyClass(name = "JavaAtomicBoolean", parent = "Object")
|
||||
public static class JavaAtomicBoolean extends RubyObject {
|
||||
|
||||
private AtomicBoolean atomicBoolean;
|
||||
|
||||
public JavaAtomicBoolean(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context, IRubyObject value) {
|
||||
atomicBoolean = new AtomicBoolean(convertRubyBooleanToJavaBoolean(value));
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context) {
|
||||
atomicBoolean = new AtomicBoolean();
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "value")
|
||||
public IRubyObject value() {
|
||||
return getRuntime().newBoolean(atomicBoolean.get());
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "true?")
|
||||
public IRubyObject isAtomicTrue() {
|
||||
return getRuntime().newBoolean(atomicBoolean.get());
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "false?")
|
||||
public IRubyObject isAtomicFalse() {
|
||||
return getRuntime().newBoolean((atomicBoolean.get() == false));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "value=")
|
||||
public IRubyObject setAtomic(ThreadContext context, IRubyObject newValue) {
|
||||
atomicBoolean.set(convertRubyBooleanToJavaBoolean(newValue));
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "make_true")
|
||||
public IRubyObject makeTrue() {
|
||||
return getRuntime().newBoolean(atomicBoolean.compareAndSet(false, true));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "make_false")
|
||||
public IRubyObject makeFalse() {
|
||||
return getRuntime().newBoolean(atomicBoolean.compareAndSet(true, false));
|
||||
}
|
||||
|
||||
private boolean convertRubyBooleanToJavaBoolean(IRubyObject newValue) {
|
||||
if (newValue instanceof RubyBoolean.False || newValue instanceof RubyNil) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.concurrent_ruby.ext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.jruby.Ruby;
|
||||
import org.jruby.RubyClass;
|
||||
import org.jruby.RubyFixnum;
|
||||
import org.jruby.RubyModule;
|
||||
import org.jruby.RubyObject;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.runtime.load.Library;
|
||||
import org.jruby.runtime.Block;
|
||||
|
||||
public class JavaAtomicFixnumLibrary implements Library {
|
||||
|
||||
public void load(Ruby runtime, boolean wrap) throws IOException {
|
||||
RubyModule concurrentMod = runtime.defineModule("Concurrent");
|
||||
RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicFixnum", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR);
|
||||
|
||||
atomicCls.defineAnnotatedMethods(JavaAtomicFixnum.class);
|
||||
}
|
||||
|
||||
private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JavaAtomicFixnum(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
@JRubyClass(name = "JavaAtomicFixnum", parent = "Object")
|
||||
public static class JavaAtomicFixnum extends RubyObject {
|
||||
|
||||
private AtomicLong atomicLong;
|
||||
|
||||
public JavaAtomicFixnum(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context) {
|
||||
this.atomicLong = new AtomicLong(0);
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context, IRubyObject value) {
|
||||
this.atomicLong = new AtomicLong(rubyFixnumToLong(value));
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "value")
|
||||
public IRubyObject getValue() {
|
||||
return getRuntime().newFixnum(atomicLong.get());
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "value=")
|
||||
public IRubyObject setValue(ThreadContext context, IRubyObject newValue) {
|
||||
atomicLong.set(rubyFixnumToLong(newValue));
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"increment", "up"})
|
||||
public IRubyObject increment() {
|
||||
return getRuntime().newFixnum(atomicLong.incrementAndGet());
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"increment", "up"})
|
||||
public IRubyObject increment(IRubyObject value) {
|
||||
long delta = rubyFixnumToLong(value);
|
||||
return getRuntime().newFixnum(atomicLong.addAndGet(delta));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"decrement", "down"})
|
||||
public IRubyObject decrement() {
|
||||
return getRuntime().newFixnum(atomicLong.decrementAndGet());
|
||||
}
|
||||
|
||||
@JRubyMethod(name = {"decrement", "down"})
|
||||
public IRubyObject decrement(IRubyObject value) {
|
||||
long delta = rubyFixnumToLong(value);
|
||||
return getRuntime().newFixnum(atomicLong.addAndGet(-delta));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "compare_and_set")
|
||||
public IRubyObject compareAndSet(ThreadContext context, IRubyObject expect, IRubyObject update) {
|
||||
return getRuntime().newBoolean(atomicLong.compareAndSet(rubyFixnumToLong(expect), rubyFixnumToLong(update)));
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject update(ThreadContext context, Block block) {
|
||||
for (;;) {
|
||||
long _oldValue = atomicLong.get();
|
||||
IRubyObject oldValue = getRuntime().newFixnum(_oldValue);
|
||||
IRubyObject newValue = block.yield(context, oldValue);
|
||||
if (atomicLong.compareAndSet(_oldValue, rubyFixnumToLong(newValue))) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long rubyFixnumToLong(IRubyObject value) {
|
||||
if (value instanceof RubyFixnum) {
|
||||
RubyFixnum fixNum = (RubyFixnum) value;
|
||||
return fixNum.getLongValue();
|
||||
} else {
|
||||
throw getRuntime().newArgumentError("value must be a Fixnum");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.concurrent_ruby.ext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import org.jruby.Ruby;
|
||||
import org.jruby.RubyClass;
|
||||
import org.jruby.RubyFixnum;
|
||||
import org.jruby.RubyModule;
|
||||
import org.jruby.RubyNumeric;
|
||||
import org.jruby.RubyObject;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
|
||||
public class JavaSemaphoreLibrary {
|
||||
|
||||
public void load(Ruby runtime, boolean wrap) throws IOException {
|
||||
RubyModule concurrentMod = runtime.defineModule("Concurrent");
|
||||
RubyClass atomicCls = concurrentMod.defineClassUnder("JavaSemaphore", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR);
|
||||
|
||||
atomicCls.defineAnnotatedMethods(JavaSemaphore.class);
|
||||
}
|
||||
|
||||
private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JavaSemaphore(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
@JRubyClass(name = "JavaSemaphore", parent = "Object")
|
||||
public static class JavaSemaphore extends RubyObject {
|
||||
|
||||
private JRubySemaphore semaphore;
|
||||
|
||||
public JavaSemaphore(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject initialize(ThreadContext context, IRubyObject value) {
|
||||
this.semaphore = new JRubySemaphore(rubyFixnumInt(value, "count"));
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject acquire(ThreadContext context, IRubyObject value) throws InterruptedException {
|
||||
this.semaphore.acquire(rubyFixnumToPositiveInt(value, "permits"));
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "available_permits")
|
||||
public IRubyObject availablePermits(ThreadContext context) {
|
||||
return getRuntime().newFixnum(this.semaphore.availablePermits());
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "drain_permits")
|
||||
public IRubyObject drainPermits(ThreadContext context) {
|
||||
return getRuntime().newFixnum(this.semaphore.drainPermits());
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject acquire(ThreadContext context) throws InterruptedException {
|
||||
this.semaphore.acquire(1);
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "try_acquire")
|
||||
public IRubyObject tryAcquire(ThreadContext context) throws InterruptedException {
|
||||
return getRuntime().newBoolean(semaphore.tryAcquire(1));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "try_acquire")
|
||||
public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits) throws InterruptedException {
|
||||
return getRuntime().newBoolean(semaphore.tryAcquire(rubyFixnumToPositiveInt(permits, "permits")));
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "try_acquire")
|
||||
public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout) throws InterruptedException {
|
||||
return getRuntime().newBoolean(
|
||||
semaphore.tryAcquire(
|
||||
rubyFixnumToPositiveInt(permits, "permits"),
|
||||
rubyNumericToLong(timeout, "timeout"),
|
||||
java.util.concurrent.TimeUnit.SECONDS)
|
||||
);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject release(ThreadContext context) {
|
||||
this.semaphore.release(1);
|
||||
return getRuntime().newBoolean(true);
|
||||
}
|
||||
|
||||
@JRubyMethod
|
||||
public IRubyObject release(ThreadContext context, IRubyObject value) {
|
||||
this.semaphore.release(rubyFixnumToPositiveInt(value, "permits"));
|
||||
return getRuntime().newBoolean(true);
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "reduce_permits")
|
||||
public IRubyObject reducePermits(ThreadContext context, IRubyObject reduction) throws InterruptedException {
|
||||
this.semaphore.publicReducePermits(rubyFixnumToNonNegativeInt(reduction, "reduction"));
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
private int rubyFixnumInt(IRubyObject value, String paramName) {
|
||||
if (value instanceof RubyFixnum) {
|
||||
RubyFixnum fixNum = (RubyFixnum) value;
|
||||
return (int) fixNum.getLongValue();
|
||||
} else {
|
||||
throw getRuntime().newArgumentError(paramName + " must be integer");
|
||||
}
|
||||
}
|
||||
|
||||
private int rubyFixnumToNonNegativeInt(IRubyObject value, String paramName) {
|
||||
if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() >= 0) {
|
||||
RubyFixnum fixNum = (RubyFixnum) value;
|
||||
return (int) fixNum.getLongValue();
|
||||
} else {
|
||||
throw getRuntime().newArgumentError(paramName + " must be a non-negative integer");
|
||||
}
|
||||
}
|
||||
|
||||
private int rubyFixnumToPositiveInt(IRubyObject value, String paramName) {
|
||||
if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() > 0) {
|
||||
RubyFixnum fixNum = (RubyFixnum) value;
|
||||
return (int) fixNum.getLongValue();
|
||||
} else {
|
||||
throw getRuntime().newArgumentError(paramName + " must be an integer greater than zero");
|
||||
}
|
||||
}
|
||||
|
||||
private long rubyNumericToLong(IRubyObject value, String paramName) {
|
||||
if (value instanceof RubyNumeric && ((RubyNumeric) value).getDoubleValue() > 0) {
|
||||
RubyNumeric fixNum = (RubyNumeric) value;
|
||||
return fixNum.getLongValue();
|
||||
} else {
|
||||
throw getRuntime().newArgumentError(paramName + " must be a float greater than zero");
|
||||
}
|
||||
}
|
||||
|
||||
class JRubySemaphore extends Semaphore {
|
||||
|
||||
public JRubySemaphore(int permits) {
|
||||
super(permits);
|
||||
}
|
||||
|
||||
public JRubySemaphore(int permits, boolean value) {
|
||||
super(permits, value);
|
||||
}
|
||||
|
||||
public void publicReducePermits(int i) {
|
||||
reducePermits(i);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package com.concurrent_ruby.ext;
|
||||
|
||||
import org.jruby.Ruby;
|
||||
import org.jruby.RubyBasicObject;
|
||||
import org.jruby.RubyClass;
|
||||
import org.jruby.RubyModule;
|
||||
import org.jruby.RubyObject;
|
||||
import org.jruby.RubyThread;
|
||||
import org.jruby.anno.JRubyClass;
|
||||
import org.jruby.anno.JRubyMethod;
|
||||
import org.jruby.runtime.Block;
|
||||
import org.jruby.runtime.ObjectAllocator;
|
||||
import org.jruby.runtime.ThreadContext;
|
||||
import org.jruby.runtime.Visibility;
|
||||
import org.jruby.runtime.builtin.IRubyObject;
|
||||
import org.jruby.runtime.load.Library;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class SynchronizationLibrary implements Library {
|
||||
|
||||
private static final Unsafe UNSAFE = loadUnsafe();
|
||||
private static final boolean FULL_FENCE = supportsFences();
|
||||
|
||||
private static Unsafe loadUnsafe() {
|
||||
try {
|
||||
Class ncdfe = Class.forName("sun.misc.Unsafe");
|
||||
Field f = ncdfe.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
return (Unsafe) f.get((java.lang.Object) null);
|
||||
} catch (Exception var2) {
|
||||
return null;
|
||||
} catch (NoClassDefFoundError var3) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean supportsFences() {
|
||||
if (UNSAFE == null) {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
Method m = UNSAFE.getClass().getDeclaredMethod("fullFence", new Class[0]);
|
||||
if (m != null) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception var1) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ObjectAllocator JRUBY_OBJECT_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JRubyObject(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
private static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new Object(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
private static final ObjectAllocator ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new AbstractLockableObject(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
private static final ObjectAllocator JRUBY_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() {
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JRubyLockableObject(runtime, klazz);
|
||||
}
|
||||
};
|
||||
|
||||
public void load(Ruby runtime, boolean wrap) throws IOException {
|
||||
RubyModule synchronizationModule = runtime.
|
||||
defineModule("Concurrent").
|
||||
defineModuleUnder("Synchronization");
|
||||
|
||||
RubyModule jrubyAttrVolatileModule = synchronizationModule.defineModuleUnder("JRubyAttrVolatile");
|
||||
jrubyAttrVolatileModule.defineAnnotatedMethods(JRubyAttrVolatile.class);
|
||||
|
||||
defineClass(runtime, synchronizationModule, "AbstractObject", "JRubyObject",
|
||||
JRubyObject.class, JRUBY_OBJECT_ALLOCATOR);
|
||||
|
||||
defineClass(runtime, synchronizationModule, "JRubyObject", "Object",
|
||||
Object.class, OBJECT_ALLOCATOR);
|
||||
|
||||
defineClass(runtime, synchronizationModule, "Object", "AbstractLockableObject",
|
||||
AbstractLockableObject.class, ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR);
|
||||
|
||||
defineClass(runtime, synchronizationModule, "AbstractLockableObject", "JRubyLockableObject",
|
||||
JRubyLockableObject.class, JRUBY_LOCKABLE_OBJECT_ALLOCATOR);
|
||||
|
||||
defineClass(runtime, synchronizationModule, "Object", "JRuby",
|
||||
JRuby.class, new ObjectAllocator() {
|
||||
@Override
|
||||
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
||||
return new JRuby(runtime, klazz);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private RubyClass defineClass(
|
||||
Ruby runtime,
|
||||
RubyModule namespace,
|
||||
String parentName,
|
||||
String name,
|
||||
Class javaImplementation,
|
||||
ObjectAllocator allocator) {
|
||||
final RubyClass parentClass = namespace.getClass(parentName);
|
||||
|
||||
if (parentClass == null) {
|
||||
System.out.println("not found " + parentName);
|
||||
throw runtime.newRuntimeError(namespace.toString() + "::" + parentName + " is missing");
|
||||
}
|
||||
|
||||
final RubyClass newClass = namespace.defineClassUnder(name, parentClass, allocator);
|
||||
newClass.defineAnnotatedMethods(javaImplementation);
|
||||
return newClass;
|
||||
}
|
||||
|
||||
// Facts:
|
||||
// - all ivar reads are without any synchronisation of fences see
|
||||
// https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/VariableAccessor.java#L110-110
|
||||
// - writes depend on UnsafeHolder.U, null -> SynchronizedVariableAccessor, !null -> StampedVariableAccessor
|
||||
// SynchronizedVariableAccessor wraps with synchronized block, StampedVariableAccessor uses fullFence or
|
||||
// volatilePut
|
||||
// TODO (pitr 16-Sep-2015): what do we do in Java 9 ?
|
||||
|
||||
// module JRubyAttrVolatile
|
||||
public static class JRubyAttrVolatile {
|
||||
|
||||
// volatile threadContext is used as a memory barrier per the JVM memory model happens-before semantic
|
||||
// on volatile fields. any volatile field could have been used but using the thread context is an
|
||||
// attempt to avoid code elimination.
|
||||
private static volatile int volatileField;
|
||||
|
||||
@JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC)
|
||||
public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject self) {
|
||||
// Prevent reordering of ivar writes with publication of this instance
|
||||
if (!FULL_FENCE) {
|
||||
// Assuming that following volatile read and write is not eliminated it simulates fullFence.
|
||||
// If it's eliminated it'll cause problems only on non-x86 platforms.
|
||||
// http://shipilev.net/blog/2014/jmm-pragmatics/#_happens_before_test_your_understanding
|
||||
final int volatileRead = volatileField;
|
||||
volatileField = context.getLine();
|
||||
} else {
|
||||
UNSAFE.fullFence();
|
||||
}
|
||||
return context.nil;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC)
|
||||
public static IRubyObject instanceVariableGetVolatile(
|
||||
ThreadContext context,
|
||||
IRubyObject self,
|
||||
IRubyObject name) {
|
||||
// Ensure we ses latest value with loadFence
|
||||
if (!FULL_FENCE) {
|
||||
// piggybacking on volatile read, simulating loadFence
|
||||
final int volatileRead = volatileField;
|
||||
return ((RubyBasicObject) self).instance_variable_get(context, name);
|
||||
} else {
|
||||
UNSAFE.loadFence();
|
||||
return ((RubyBasicObject) self).instance_variable_get(context, name);
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC)
|
||||
public static IRubyObject InstanceVariableSetVolatile(
|
||||
ThreadContext context,
|
||||
IRubyObject self,
|
||||
IRubyObject name,
|
||||
IRubyObject value) {
|
||||
// Ensure we make last update visible
|
||||
if (!FULL_FENCE) {
|
||||
// piggybacking on volatile write, simulating storeFence
|
||||
final IRubyObject result = ((RubyBasicObject) self).instance_variable_set(name, value);
|
||||
volatileField = context.getLine();
|
||||
return result;
|
||||
} else {
|
||||
// JRuby uses StampedVariableAccessor which calls fullFence
|
||||
// so no additional steps needed.
|
||||
// See https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/StampedVariableAccessor.java#L151-L159
|
||||
return ((RubyBasicObject) self).instance_variable_set(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyClass(name = "JRubyObject", parent = "AbstractObject")
|
||||
public static class JRubyObject extends RubyObject {
|
||||
|
||||
public JRubyObject(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyClass(name = "Object", parent = "JRubyObject")
|
||||
public static class Object extends JRubyObject {
|
||||
|
||||
public Object(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyClass(name = "AbstractLockableObject", parent = "Object")
|
||||
public static class AbstractLockableObject extends Object {
|
||||
|
||||
public AbstractLockableObject(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyClass(name = "JRubyLockableObject", parent = "AbstractLockableObject")
|
||||
public static class JRubyLockableObject extends JRubyObject {
|
||||
|
||||
public JRubyLockableObject(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "synchronize", visibility = Visibility.PROTECTED)
|
||||
public IRubyObject rubySynchronize(ThreadContext context, Block block) {
|
||||
synchronized (this) {
|
||||
return block.yield(context, null);
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "ns_wait", optional = 1, visibility = Visibility.PROTECTED)
|
||||
public IRubyObject nsWait(ThreadContext context, IRubyObject[] args) {
|
||||
Ruby runtime = context.runtime;
|
||||
if (args.length > 1) {
|
||||
throw runtime.newArgumentError(args.length, 1);
|
||||
}
|
||||
Double timeout = null;
|
||||
if (args.length > 0 && !args[0].isNil()) {
|
||||
timeout = args[0].convertToFloat().getDoubleValue();
|
||||
if (timeout < 0) {
|
||||
throw runtime.newArgumentError("time interval must be positive");
|
||||
}
|
||||
}
|
||||
if (Thread.interrupted()) {
|
||||
throw runtime.newConcurrencyError("thread interrupted");
|
||||
}
|
||||
boolean success = false;
|
||||
try {
|
||||
success = context.getThread().wait_timeout(this, timeout);
|
||||
} catch (InterruptedException ie) {
|
||||
throw runtime.newConcurrencyError(ie.getLocalizedMessage());
|
||||
} finally {
|
||||
// An interrupt or timeout may have caused us to miss
|
||||
// a notify that we consumed, so do another notify in
|
||||
// case someone else is available to pick it up.
|
||||
if (!success) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "ns_signal", visibility = Visibility.PROTECTED)
|
||||
public IRubyObject nsSignal(ThreadContext context) {
|
||||
notify();
|
||||
return this;
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "ns_broadcast", visibility = Visibility.PROTECTED)
|
||||
public IRubyObject nsBroadcast(ThreadContext context) {
|
||||
notifyAll();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@JRubyClass(name = "JRuby")
|
||||
public static class JRuby extends RubyObject {
|
||||
public JRuby(Ruby runtime, RubyClass metaClass) {
|
||||
super(runtime, metaClass);
|
||||
}
|
||||
|
||||
@JRubyMethod(name = "sleep_interruptibly", visibility = Visibility.PUBLIC, module = true)
|
||||
public static IRubyObject sleepInterruptibly(final ThreadContext context, IRubyObject receiver, final Block block) {
|
||||
try {
|
||||
context.getThread().executeBlockingTask(new RubyThread.BlockingTask() {
|
||||
@Override
|
||||
public void run() throws InterruptedException {
|
||||
block.call(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wakeup() {
|
||||
context.getThread().getNativeThread().interrupt();
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
throw context.runtime.newThreadError("interrupted in Concurrent::Synchronization::JRuby.sleep_interruptibly");
|
||||
}
|
||||
return context.nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.concurrent_ruby.ext.jsr166e;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ConcurrentHashMap<K, V> {
|
||||
/** Interface describing a function of one argument */
|
||||
public interface Fun<A,T> { T apply(A a); }
|
||||
/** Interface describing a function of two arguments */
|
||||
public interface BiFun<A,B,T> { T apply(A a, B b); }
|
||||
|
||||
public V get(K key);
|
||||
public V put(K key, V value);
|
||||
public V putIfAbsent(K key, V value);
|
||||
public V computeIfAbsent(K key, Fun<? super K, ? extends V> mf);
|
||||
public V computeIfPresent(K key, BiFun<? super K, ? super V, ? extends V> mf);
|
||||
public V compute(K key, BiFun<? super K, ? super V, ? extends V> mf);
|
||||
public V merge(K key, V value, BiFun<? super V, ? super V, ? extends V> mf);
|
||||
public boolean replace(K key, V oldVal, V newVal);
|
||||
public V replace(K key, V value);
|
||||
public boolean containsKey(K key);
|
||||
public boolean remove(Object key, Object value);
|
||||
public V remove(K key);
|
||||
public void clear();
|
||||
public Set<Map.Entry<K,V>> entrySet();
|
||||
public int size();
|
||||
public V getValueOrDefault(Object key, V defaultValue);
|
||||
|
||||
public boolean containsValue(V value);
|
||||
public K findKey(V value);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This is based on 1.9 version.
|
||||
|
||||
package com.concurrent_ruby.ext.jsr166e;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
/**
|
||||
* One or more variables that together maintain an initially zero
|
||||
* {@code long} sum. When updates (method {@link #add}) are contended
|
||||
* across threads, the set of variables may grow dynamically to reduce
|
||||
* contention. Method {@link #sum} (or, equivalently, {@link
|
||||
* #longValue}) returns the current total combined across the
|
||||
* variables maintaining the sum.
|
||||
*
|
||||
* <p>This class is usually preferable to {@link AtomicLong} when
|
||||
* multiple threads update a common sum that is used for purposes such
|
||||
* as collecting statistics, not for fine-grained synchronization
|
||||
* control. Under low update contention, the two classes have similar
|
||||
* characteristics. But under high contention, expected throughput of
|
||||
* this class is significantly higher, at the expense of higher space
|
||||
* consumption.
|
||||
*
|
||||
* <p>This class extends {@link Number}, but does <em>not</em> define
|
||||
* methods such as {@code hashCode} and {@code compareTo} because
|
||||
* instances are expected to be mutated, and so are not useful as
|
||||
* collection keys.
|
||||
*
|
||||
* <p><em>jsr166e note: This class is targeted to be placed in
|
||||
* java.util.concurrent.atomic.</em>
|
||||
*
|
||||
* @since 1.8
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class LongAdder extends Striped64 implements Serializable {
|
||||
private static final long serialVersionUID = 7249069246863182397L;
|
||||
|
||||
/**
|
||||
* Version of plus for use in retryUpdate
|
||||
*/
|
||||
final long fn(long v, long x) { return v + x; }
|
||||
|
||||
/**
|
||||
* Creates a new adder with initial sum of zero.
|
||||
*/
|
||||
public LongAdder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value.
|
||||
*
|
||||
* @param x the value to add
|
||||
*/
|
||||
public void add(long x) {
|
||||
Cell[] as; long b, v; HashCode hc; Cell a; int n;
|
||||
if ((as = cells) != null || !casBase(b = base, b + x)) {
|
||||
boolean uncontended = true;
|
||||
int h = (hc = threadHashCode.get()).code;
|
||||
if (as == null || (n = as.length) < 1 ||
|
||||
(a = as[(n - 1) & h]) == null ||
|
||||
!(uncontended = a.cas(v = a.value, v + x)))
|
||||
retryUpdate(x, hc, uncontended);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@code add(1)}.
|
||||
*/
|
||||
public void increment() {
|
||||
add(1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@code add(-1)}.
|
||||
*/
|
||||
public void decrement() {
|
||||
add(-1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sum. The returned value is <em>NOT</em> an
|
||||
* atomic snapshot: Invocation in the absence of concurrent
|
||||
* updates returns an accurate result, but concurrent updates that
|
||||
* occur while the sum is being calculated might not be
|
||||
* incorporated.
|
||||
*
|
||||
* @return the sum
|
||||
*/
|
||||
public long sum() {
|
||||
long sum = base;
|
||||
Cell[] as = cells;
|
||||
if (as != null) {
|
||||
int n = as.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Cell a = as[i];
|
||||
if (a != null)
|
||||
sum += a.value;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets variables maintaining the sum to zero. This method may
|
||||
* be a useful alternative to creating a new adder, but is only
|
||||
* effective if there are no concurrent updates. Because this
|
||||
* method is intrinsically racy, it should only be used when it is
|
||||
* known that no threads are concurrently updating.
|
||||
*/
|
||||
public void reset() {
|
||||
internalReset(0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent in effect to {@link #sum} followed by {@link
|
||||
* #reset}. This method may apply for example during quiescent
|
||||
* points between multithreaded computations. If there are
|
||||
* updates concurrent with this method, the returned value is
|
||||
* <em>not</em> guaranteed to be the final value occurring before
|
||||
* the reset.
|
||||
*
|
||||
* @return the sum
|
||||
*/
|
||||
public long sumThenReset() {
|
||||
long sum = base;
|
||||
Cell[] as = cells;
|
||||
base = 0L;
|
||||
if (as != null) {
|
||||
int n = as.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Cell a = as[i];
|
||||
if (a != null) {
|
||||
sum += a.value;
|
||||
a.value = 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the String representation of the {@link #sum}.
|
||||
* @return the String representation of the {@link #sum}
|
||||
*/
|
||||
public String toString() {
|
||||
return Long.toString(sum());
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@link #sum}.
|
||||
*
|
||||
* @return the sum
|
||||
*/
|
||||
public long longValue() {
|
||||
return sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #sum} as an {@code int} after a narrowing
|
||||
* primitive conversion.
|
||||
*/
|
||||
public int intValue() {
|
||||
return (int)sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #sum} as a {@code float}
|
||||
* after a widening primitive conversion.
|
||||
*/
|
||||
public float floatValue() {
|
||||
return (float)sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #sum} as a {@code double} after a widening
|
||||
* primitive conversion.
|
||||
*/
|
||||
public double doubleValue() {
|
||||
return (double)sum();
|
||||
}
|
||||
|
||||
private void writeObject(java.io.ObjectOutputStream s)
|
||||
throws java.io.IOException {
|
||||
s.defaultWriteObject();
|
||||
s.writeLong(sum());
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
s.defaultReadObject();
|
||||
busy = 0;
|
||||
cells = null;
|
||||
base = s.readLong();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This is based on 1.5 version.
|
||||
|
||||
package com.concurrent_ruby.ext.jsr166e;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* A package-local class holding common representation and mechanics
|
||||
* for classes supporting dynamic striping on 64bit values. The class
|
||||
* extends Number so that concrete subclasses must publicly do so.
|
||||
*/
|
||||
abstract class Striped64 extends Number {
|
||||
/*
|
||||
* This class maintains a lazily-initialized table of atomically
|
||||
* updated variables, plus an extra "base" field. The table size
|
||||
* is a power of two. Indexing uses masked per-thread hash codes.
|
||||
* Nearly all declarations in this class are package-private,
|
||||
* accessed directly by subclasses.
|
||||
*
|
||||
* Table entries are of class Cell; a variant of AtomicLong padded
|
||||
* to reduce cache contention on most processors. Padding is
|
||||
* overkill for most Atomics because they are usually irregularly
|
||||
* scattered in memory and thus don't interfere much with each
|
||||
* other. But Atomic objects residing in arrays will tend to be
|
||||
* placed adjacent to each other, and so will most often share
|
||||
* cache lines (with a huge negative performance impact) without
|
||||
* this precaution.
|
||||
*
|
||||
* In part because Cells are relatively large, we avoid creating
|
||||
* them until they are needed. When there is no contention, all
|
||||
* updates are made to the base field. Upon first contention (a
|
||||
* failed CAS on base update), the table is initialized to size 2.
|
||||
* The table size is doubled upon further contention until
|
||||
* reaching the nearest power of two greater than or equal to the
|
||||
* number of CPUS. Table slots remain empty (null) until they are
|
||||
* needed.
|
||||
*
|
||||
* A single spinlock ("busy") is used for initializing and
|
||||
* resizing the table, as well as populating slots with new Cells.
|
||||
* There is no need for a blocking lock: When the lock is not
|
||||
* available, threads try other slots (or the base). During these
|
||||
* retries, there is increased contention and reduced locality,
|
||||
* which is still better than alternatives.
|
||||
*
|
||||
* Per-thread hash codes are initialized to random values.
|
||||
* Contention and/or table collisions are indicated by failed
|
||||
* CASes when performing an update operation (see method
|
||||
* retryUpdate). Upon a collision, if the table size is less than
|
||||
* the capacity, it is doubled in size unless some other thread
|
||||
* holds the lock. If a hashed slot is empty, and lock is
|
||||
* available, a new Cell is created. Otherwise, if the slot
|
||||
* exists, a CAS is tried. Retries proceed by "double hashing",
|
||||
* using a secondary hash (Marsaglia XorShift) to try to find a
|
||||
* free slot.
|
||||
*
|
||||
* The table size is capped because, when there are more threads
|
||||
* than CPUs, supposing that each thread were bound to a CPU,
|
||||
* there would exist a perfect hash function mapping threads to
|
||||
* slots that eliminates collisions. When we reach capacity, we
|
||||
* search for this mapping by randomly varying the hash codes of
|
||||
* colliding threads. Because search is random, and collisions
|
||||
* only become known via CAS failures, convergence can be slow,
|
||||
* and because threads are typically not bound to CPUS forever,
|
||||
* may not occur at all. However, despite these limitations,
|
||||
* observed contention rates are typically low in these cases.
|
||||
*
|
||||
* It is possible for a Cell to become unused when threads that
|
||||
* once hashed to it terminate, as well as in the case where
|
||||
* doubling the table causes no thread to hash to it under
|
||||
* expanded mask. We do not try to detect or remove such cells,
|
||||
* under the assumption that for long-running instances, observed
|
||||
* contention levels will recur, so the cells will eventually be
|
||||
* needed again; and for short-lived ones, it does not matter.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Padded variant of AtomicLong supporting only raw accesses plus CAS.
|
||||
* The value field is placed between pads, hoping that the JVM doesn't
|
||||
* reorder them.
|
||||
*
|
||||
* JVM intrinsics note: It would be possible to use a release-only
|
||||
* form of CAS here, if it were provided.
|
||||
*/
|
||||
static final class Cell {
|
||||
volatile long p0, p1, p2, p3, p4, p5, p6;
|
||||
volatile long value;
|
||||
volatile long q0, q1, q2, q3, q4, q5, q6;
|
||||
Cell(long x) { value = x; }
|
||||
|
||||
final boolean cas(long cmp, long val) {
|
||||
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final sun.misc.Unsafe UNSAFE;
|
||||
private static final long valueOffset;
|
||||
static {
|
||||
try {
|
||||
UNSAFE = getUnsafe();
|
||||
Class<?> ak = Cell.class;
|
||||
valueOffset = UNSAFE.objectFieldOffset
|
||||
(ak.getDeclaredField("value"));
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for the thread-local hash code. The code is initially
|
||||
* random, but may be set to a different value upon collisions.
|
||||
*/
|
||||
static final class HashCode {
|
||||
static final Random rng = new Random();
|
||||
int code;
|
||||
HashCode() {
|
||||
int h = rng.nextInt(); // Avoid zero to allow xorShift rehash
|
||||
code = (h == 0) ? 1 : h;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The corresponding ThreadLocal class
|
||||
*/
|
||||
static final class ThreadHashCode extends ThreadLocal<HashCode> {
|
||||
public HashCode initialValue() { return new HashCode(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Static per-thread hash codes. Shared across all instances to
|
||||
* reduce ThreadLocal pollution and because adjustments due to
|
||||
* collisions in one table are likely to be appropriate for
|
||||
* others.
|
||||
*/
|
||||
static final ThreadHashCode threadHashCode = new ThreadHashCode();
|
||||
|
||||
/** Number of CPUS, to place bound on table size */
|
||||
static final int NCPU = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
/**
|
||||
* Table of cells. When non-null, size is a power of 2.
|
||||
*/
|
||||
transient volatile Cell[] cells;
|
||||
|
||||
/**
|
||||
* Base value, used mainly when there is no contention, but also as
|
||||
* a fallback during table initialization races. Updated via CAS.
|
||||
*/
|
||||
transient volatile long base;
|
||||
|
||||
/**
|
||||
* Spinlock (locked via CAS) used when resizing and/or creating Cells.
|
||||
*/
|
||||
transient volatile int busy;
|
||||
|
||||
/**
|
||||
* Package-private default constructor
|
||||
*/
|
||||
Striped64() {
|
||||
}
|
||||
|
||||
/**
|
||||
* CASes the base field.
|
||||
*/
|
||||
final boolean casBase(long cmp, long val) {
|
||||
return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* CASes the busy field from 0 to 1 to acquire lock.
|
||||
*/
|
||||
final boolean casBusy() {
|
||||
return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the function of current and new value. Subclasses
|
||||
* should open-code this update function for most uses, but the
|
||||
* virtualized form is needed within retryUpdate.
|
||||
*
|
||||
* @param currentValue the current value (of either base or a cell)
|
||||
* @param newValue the argument from a user update call
|
||||
* @return result of the update function
|
||||
*/
|
||||
abstract long fn(long currentValue, long newValue);
|
||||
|
||||
/**
|
||||
* Handles cases of updates involving initialization, resizing,
|
||||
* creating new Cells, and/or contention. See above for
|
||||
* explanation. This method suffers the usual non-modularity
|
||||
* problems of optimistic retry code, relying on rechecked sets of
|
||||
* reads.
|
||||
*
|
||||
* @param x the value
|
||||
* @param hc the hash code holder
|
||||
* @param wasUncontended false if CAS failed before call
|
||||
*/
|
||||
final void retryUpdate(long x, HashCode hc, boolean wasUncontended) {
|
||||
int h = hc.code;
|
||||
boolean collide = false; // True if last slot nonempty
|
||||
for (;;) {
|
||||
Cell[] as; Cell a; int n; long v;
|
||||
if ((as = cells) != null && (n = as.length) > 0) {
|
||||
if ((a = as[(n - 1) & h]) == null) {
|
||||
if (busy == 0) { // Try to attach new Cell
|
||||
Cell r = new Cell(x); // Optimistically create
|
||||
if (busy == 0 && casBusy()) {
|
||||
boolean created = false;
|
||||
try { // Recheck under lock
|
||||
Cell[] rs; int m, j;
|
||||
if ((rs = cells) != null &&
|
||||
(m = rs.length) > 0 &&
|
||||
rs[j = (m - 1) & h] == null) {
|
||||
rs[j] = r;
|
||||
created = true;
|
||||
}
|
||||
} finally {
|
||||
busy = 0;
|
||||
}
|
||||
if (created)
|
||||
break;
|
||||
continue; // Slot is now non-empty
|
||||
}
|
||||
}
|
||||
collide = false;
|
||||
}
|
||||
else if (!wasUncontended) // CAS already known to fail
|
||||
wasUncontended = true; // Continue after rehash
|
||||
else if (a.cas(v = a.value, fn(v, x)))
|
||||
break;
|
||||
else if (n >= NCPU || cells != as)
|
||||
collide = false; // At max size or stale
|
||||
else if (!collide)
|
||||
collide = true;
|
||||
else if (busy == 0 && casBusy()) {
|
||||
try {
|
||||
if (cells == as) { // Expand table unless stale
|
||||
Cell[] rs = new Cell[n << 1];
|
||||
for (int i = 0; i < n; ++i)
|
||||
rs[i] = as[i];
|
||||
cells = rs;
|
||||
}
|
||||
} finally {
|
||||
busy = 0;
|
||||
}
|
||||
collide = false;
|
||||
continue; // Retry with expanded table
|
||||
}
|
||||
h ^= h << 13; // Rehash
|
||||
h ^= h >>> 17;
|
||||
h ^= h << 5;
|
||||
}
|
||||
else if (busy == 0 && cells == as && casBusy()) {
|
||||
boolean init = false;
|
||||
try { // Initialize table
|
||||
if (cells == as) {
|
||||
Cell[] rs = new Cell[2];
|
||||
rs[h & 1] = new Cell(x);
|
||||
cells = rs;
|
||||
init = true;
|
||||
}
|
||||
} finally {
|
||||
busy = 0;
|
||||
}
|
||||
if (init)
|
||||
break;
|
||||
}
|
||||
else if (casBase(v = base, fn(v, x)))
|
||||
break; // Fall back on using base
|
||||
}
|
||||
hc.code = h; // Record index for next time
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets base and all cells to the given value.
|
||||
*/
|
||||
final void internalReset(long initialValue) {
|
||||
Cell[] as = cells;
|
||||
base = initialValue;
|
||||
if (as != null) {
|
||||
int n = as.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Cell a = as[i];
|
||||
if (a != null)
|
||||
a.value = initialValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
private static final sun.misc.Unsafe UNSAFE;
|
||||
private static final long baseOffset;
|
||||
private static final long busyOffset;
|
||||
static {
|
||||
try {
|
||||
UNSAFE = getUnsafe();
|
||||
Class<?> sk = Striped64.class;
|
||||
baseOffset = UNSAFE.objectFieldOffset
|
||||
(sk.getDeclaredField("base"));
|
||||
busyOffset = UNSAFE.objectFieldOffset
|
||||
(sk.getDeclaredField("busy"));
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package.
|
||||
* Replace with a simple call to Unsafe.getUnsafe when integrating
|
||||
* into a jdk.
|
||||
*
|
||||
* @return a sun.misc.Unsafe
|
||||
*/
|
||||
private static sun.misc.Unsafe getUnsafe() {
|
||||
try {
|
||||
return sun.misc.Unsafe.getUnsafe();
|
||||
} catch (SecurityException se) {
|
||||
try {
|
||||
return java.security.AccessController.doPrivileged
|
||||
(new java.security
|
||||
.PrivilegedExceptionAction<sun.misc.Unsafe>() {
|
||||
public sun.misc.Unsafe run() throws Exception {
|
||||
java.lang.reflect.Field f = sun.misc
|
||||
.Unsafe.class.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
return (sun.misc.Unsafe) f.get(null);
|
||||
}});
|
||||
} catch (java.security.PrivilegedActionException e) {
|
||||
throw new RuntimeException("Could not initialize intrinsics",
|
||||
e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This is based on 1.9 version.
|
||||
|
||||
package com.concurrent_ruby.ext.jsr166e.nounsafe;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
/**
|
||||
* One or more variables that together maintain an initially zero
|
||||
* {@code long} sum. When updates (method {@link #add}) are contended
|
||||
* across threads, the set of variables may grow dynamically to reduce
|
||||
* contention. Method {@link #sum} (or, equivalently, {@link
|
||||
* #longValue}) returns the current total combined across the
|
||||
* variables maintaining the sum.
|
||||
*
|
||||
* <p>This class is usually preferable to {@link AtomicLong} when
|
||||
* multiple threads update a common sum that is used for purposes such
|
||||
* as collecting statistics, not for fine-grained synchronization
|
||||
* control. Under low update contention, the two classes have similar
|
||||
* characteristics. But under high contention, expected throughput of
|
||||
* this class is significantly higher, at the expense of higher space
|
||||
* consumption.
|
||||
*
|
||||
* <p>This class extends {@link Number}, but does <em>not</em> define
|
||||
* methods such as {@code hashCode} and {@code compareTo} because
|
||||
* instances are expected to be mutated, and so are not useful as
|
||||
* collection keys.
|
||||
*
|
||||
* <p><em>jsr166e note: This class is targeted to be placed in
|
||||
* java.util.concurrent.atomic.</em>
|
||||
*
|
||||
* @since 1.8
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class LongAdder extends Striped64 implements Serializable {
|
||||
private static final long serialVersionUID = 7249069246863182397L;
|
||||
|
||||
/**
|
||||
* Version of plus for use in retryUpdate
|
||||
*/
|
||||
final long fn(long v, long x) { return v + x; }
|
||||
|
||||
/**
|
||||
* Creates a new adder with initial sum of zero.
|
||||
*/
|
||||
public LongAdder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value.
|
||||
*
|
||||
* @param x the value to add
|
||||
*/
|
||||
public void add(long x) {
|
||||
Cell[] as; long b, v; HashCode hc; Cell a; int n;
|
||||
if ((as = cells) != null || !casBase(b = base, b + x)) {
|
||||
boolean uncontended = true;
|
||||
int h = (hc = threadHashCode.get()).code;
|
||||
if (as == null || (n = as.length) < 1 ||
|
||||
(a = as[(n - 1) & h]) == null ||
|
||||
!(uncontended = a.cas(v = a.value, v + x)))
|
||||
retryUpdate(x, hc, uncontended);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@code add(1)}.
|
||||
*/
|
||||
public void increment() {
|
||||
add(1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@code add(-1)}.
|
||||
*/
|
||||
public void decrement() {
|
||||
add(-1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sum. The returned value is <em>NOT</em> an
|
||||
* atomic snapshot: Invocation in the absence of concurrent
|
||||
* updates returns an accurate result, but concurrent updates that
|
||||
* occur while the sum is being calculated might not be
|
||||
* incorporated.
|
||||
*
|
||||
* @return the sum
|
||||
*/
|
||||
public long sum() {
|
||||
long sum = base;
|
||||
Cell[] as = cells;
|
||||
if (as != null) {
|
||||
int n = as.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Cell a = as[i];
|
||||
if (a != null)
|
||||
sum += a.value;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets variables maintaining the sum to zero. This method may
|
||||
* be a useful alternative to creating a new adder, but is only
|
||||
* effective if there are no concurrent updates. Because this
|
||||
* method is intrinsically racy, it should only be used when it is
|
||||
* known that no threads are concurrently updating.
|
||||
*/
|
||||
public void reset() {
|
||||
internalReset(0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent in effect to {@link #sum} followed by {@link
|
||||
* #reset}. This method may apply for example during quiescent
|
||||
* points between multithreaded computations. If there are
|
||||
* updates concurrent with this method, the returned value is
|
||||
* <em>not</em> guaranteed to be the final value occurring before
|
||||
* the reset.
|
||||
*
|
||||
* @return the sum
|
||||
*/
|
||||
public long sumThenReset() {
|
||||
long sum = base;
|
||||
Cell[] as = cells;
|
||||
base = 0L;
|
||||
if (as != null) {
|
||||
int n = as.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Cell a = as[i];
|
||||
if (a != null) {
|
||||
sum += a.value;
|
||||
a.value = 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the String representation of the {@link #sum}.
|
||||
* @return the String representation of the {@link #sum}
|
||||
*/
|
||||
public String toString() {
|
||||
return Long.toString(sum());
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@link #sum}.
|
||||
*
|
||||
* @return the sum
|
||||
*/
|
||||
public long longValue() {
|
||||
return sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #sum} as an {@code int} after a narrowing
|
||||
* primitive conversion.
|
||||
*/
|
||||
public int intValue() {
|
||||
return (int)sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #sum} as a {@code float}
|
||||
* after a widening primitive conversion.
|
||||
*/
|
||||
public float floatValue() {
|
||||
return (float)sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link #sum} as a {@code double} after a widening
|
||||
* primitive conversion.
|
||||
*/
|
||||
public double doubleValue() {
|
||||
return (double)sum();
|
||||
}
|
||||
|
||||
private void writeObject(java.io.ObjectOutputStream s)
|
||||
throws java.io.IOException {
|
||||
s.defaultWriteObject();
|
||||
s.writeLong(sum());
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
s.defaultReadObject();
|
||||
busy = 0;
|
||||
cells = null;
|
||||
base = s.readLong();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This is based on 1.5 version.
|
||||
|
||||
package com.concurrent_ruby.ext.jsr166e.nounsafe;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
|
||||
|
||||
/**
|
||||
* A package-local class holding common representation and mechanics
|
||||
* for classes supporting dynamic striping on 64bit values. The class
|
||||
* extends Number so that concrete subclasses must publicly do so.
|
||||
*/
|
||||
abstract class Striped64 extends Number {
|
||||
/*
|
||||
* This class maintains a lazily-initialized table of atomically
|
||||
* updated variables, plus an extra "base" field. The table size
|
||||
* is a power of two. Indexing uses masked per-thread hash codes.
|
||||
* Nearly all declarations in this class are package-private,
|
||||
* accessed directly by subclasses.
|
||||
*
|
||||
* Table entries are of class Cell; a variant of AtomicLong padded
|
||||
* to reduce cache contention on most processors. Padding is
|
||||
* overkill for most Atomics because they are usually irregularly
|
||||
* scattered in memory and thus don't interfere much with each
|
||||
* other. But Atomic objects residing in arrays will tend to be
|
||||
* placed adjacent to each other, and so will most often share
|
||||
* cache lines (with a huge negative performance impact) without
|
||||
* this precaution.
|
||||
*
|
||||
* In part because Cells are relatively large, we avoid creating
|
||||
* them until they are needed. When there is no contention, all
|
||||
* updates are made to the base field. Upon first contention (a
|
||||
* failed CAS on base update), the table is initialized to size 2.
|
||||
* The table size is doubled upon further contention until
|
||||
* reaching the nearest power of two greater than or equal to the
|
||||
* number of CPUS. Table slots remain empty (null) until they are
|
||||
* needed.
|
||||
*
|
||||
* A single spinlock ("busy") is used for initializing and
|
||||
* resizing the table, as well as populating slots with new Cells.
|
||||
* There is no need for a blocking lock: When the lock is not
|
||||
* available, threads try other slots (or the base). During these
|
||||
* retries, there is increased contention and reduced locality,
|
||||
* which is still better than alternatives.
|
||||
*
|
||||
* Per-thread hash codes are initialized to random values.
|
||||
* Contention and/or table collisions are indicated by failed
|
||||
* CASes when performing an update operation (see method
|
||||
* retryUpdate). Upon a collision, if the table size is less than
|
||||
* the capacity, it is doubled in size unless some other thread
|
||||
* holds the lock. If a hashed slot is empty, and lock is
|
||||
* available, a new Cell is created. Otherwise, if the slot
|
||||
* exists, a CAS is tried. Retries proceed by "double hashing",
|
||||
* using a secondary hash (Marsaglia XorShift) to try to find a
|
||||
* free slot.
|
||||
*
|
||||
* The table size is capped because, when there are more threads
|
||||
* than CPUs, supposing that each thread were bound to a CPU,
|
||||
* there would exist a perfect hash function mapping threads to
|
||||
* slots that eliminates collisions. When we reach capacity, we
|
||||
* search for this mapping by randomly varying the hash codes of
|
||||
* colliding threads. Because search is random, and collisions
|
||||
* only become known via CAS failures, convergence can be slow,
|
||||
* and because threads are typically not bound to CPUS forever,
|
||||
* may not occur at all. However, despite these limitations,
|
||||
* observed contention rates are typically low in these cases.
|
||||
*
|
||||
* It is possible for a Cell to become unused when threads that
|
||||
* once hashed to it terminate, as well as in the case where
|
||||
* doubling the table causes no thread to hash to it under
|
||||
* expanded mask. We do not try to detect or remove such cells,
|
||||
* under the assumption that for long-running instances, observed
|
||||
* contention levels will recur, so the cells will eventually be
|
||||
* needed again; and for short-lived ones, it does not matter.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Padded variant of AtomicLong supporting only raw accesses plus CAS.
|
||||
* The value field is placed between pads, hoping that the JVM doesn't
|
||||
* reorder them.
|
||||
*
|
||||
* JVM intrinsics note: It would be possible to use a release-only
|
||||
* form of CAS here, if it were provided.
|
||||
*/
|
||||
static final class Cell {
|
||||
volatile long p0, p1, p2, p3, p4, p5, p6;
|
||||
volatile long value;
|
||||
volatile long q0, q1, q2, q3, q4, q5, q6;
|
||||
|
||||
static AtomicLongFieldUpdater<Cell> VALUE_UPDATER = AtomicLongFieldUpdater.newUpdater(Cell.class, "value");
|
||||
|
||||
Cell(long x) { value = x; }
|
||||
|
||||
final boolean cas(long cmp, long val) {
|
||||
return VALUE_UPDATER.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for the thread-local hash code. The code is initially
|
||||
* random, but may be set to a different value upon collisions.
|
||||
*/
|
||||
static final class HashCode {
|
||||
static final Random rng = new Random();
|
||||
int code;
|
||||
HashCode() {
|
||||
int h = rng.nextInt(); // Avoid zero to allow xorShift rehash
|
||||
code = (h == 0) ? 1 : h;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The corresponding ThreadLocal class
|
||||
*/
|
||||
static final class ThreadHashCode extends ThreadLocal<HashCode> {
|
||||
public HashCode initialValue() { return new HashCode(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Static per-thread hash codes. Shared across all instances to
|
||||
* reduce ThreadLocal pollution and because adjustments due to
|
||||
* collisions in one table are likely to be appropriate for
|
||||
* others.
|
||||
*/
|
||||
static final ThreadHashCode threadHashCode = new ThreadHashCode();
|
||||
|
||||
/** Number of CPUS, to place bound on table size */
|
||||
static final int NCPU = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
/**
|
||||
* Table of cells. When non-null, size is a power of 2.
|
||||
*/
|
||||
transient volatile Cell[] cells;
|
||||
|
||||
/**
|
||||
* Base value, used mainly when there is no contention, but also as
|
||||
* a fallback during table initialization races. Updated via CAS.
|
||||
*/
|
||||
transient volatile long base;
|
||||
|
||||
/**
|
||||
* Spinlock (locked via CAS) used when resizing and/or creating Cells.
|
||||
*/
|
||||
transient volatile int busy;
|
||||
|
||||
AtomicLongFieldUpdater<Striped64> BASE_UPDATER = AtomicLongFieldUpdater.newUpdater(Striped64.class, "base");
|
||||
AtomicIntegerFieldUpdater<Striped64> BUSY_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Striped64.class, "busy");
|
||||
|
||||
/**
|
||||
* Package-private default constructor
|
||||
*/
|
||||
Striped64() {
|
||||
}
|
||||
|
||||
/**
|
||||
* CASes the base field.
|
||||
*/
|
||||
final boolean casBase(long cmp, long val) {
|
||||
return BASE_UPDATER.compareAndSet(this, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* CASes the busy field from 0 to 1 to acquire lock.
|
||||
*/
|
||||
final boolean casBusy() {
|
||||
return BUSY_UPDATER.compareAndSet(this, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the function of current and new value. Subclasses
|
||||
* should open-code this update function for most uses, but the
|
||||
* virtualized form is needed within retryUpdate.
|
||||
*
|
||||
* @param currentValue the current value (of either base or a cell)
|
||||
* @param newValue the argument from a user update call
|
||||
* @return result of the update function
|
||||
*/
|
||||
abstract long fn(long currentValue, long newValue);
|
||||
|
||||
/**
|
||||
* Handles cases of updates involving initialization, resizing,
|
||||
* creating new Cells, and/or contention. See above for
|
||||
* explanation. This method suffers the usual non-modularity
|
||||
* problems of optimistic retry code, relying on rechecked sets of
|
||||
* reads.
|
||||
*
|
||||
* @param x the value
|
||||
* @param hc the hash code holder
|
||||
* @param wasUncontended false if CAS failed before call
|
||||
*/
|
||||
final void retryUpdate(long x, HashCode hc, boolean wasUncontended) {
|
||||
int h = hc.code;
|
||||
boolean collide = false; // True if last slot nonempty
|
||||
for (;;) {
|
||||
Cell[] as; Cell a; int n; long v;
|
||||
if ((as = cells) != null && (n = as.length) > 0) {
|
||||
if ((a = as[(n - 1) & h]) == null) {
|
||||
if (busy == 0) { // Try to attach new Cell
|
||||
Cell r = new Cell(x); // Optimistically create
|
||||
if (busy == 0 && casBusy()) {
|
||||
boolean created = false;
|
||||
try { // Recheck under lock
|
||||
Cell[] rs; int m, j;
|
||||
if ((rs = cells) != null &&
|
||||
(m = rs.length) > 0 &&
|
||||
rs[j = (m - 1) & h] == null) {
|
||||
rs[j] = r;
|
||||
created = true;
|
||||
}
|
||||
} finally {
|
||||
busy = 0;
|
||||
}
|
||||
if (created)
|
||||
break;
|
||||
continue; // Slot is now non-empty
|
||||
}
|
||||
}
|
||||
collide = false;
|
||||
}
|
||||
else if (!wasUncontended) // CAS already known to fail
|
||||
wasUncontended = true; // Continue after rehash
|
||||
else if (a.cas(v = a.value, fn(v, x)))
|
||||
break;
|
||||
else if (n >= NCPU || cells != as)
|
||||
collide = false; // At max size or stale
|
||||
else if (!collide)
|
||||
collide = true;
|
||||
else if (busy == 0 && casBusy()) {
|
||||
try {
|
||||
if (cells == as) { // Expand table unless stale
|
||||
Cell[] rs = new Cell[n << 1];
|
||||
for (int i = 0; i < n; ++i)
|
||||
rs[i] = as[i];
|
||||
cells = rs;
|
||||
}
|
||||
} finally {
|
||||
busy = 0;
|
||||
}
|
||||
collide = false;
|
||||
continue; // Retry with expanded table
|
||||
}
|
||||
h ^= h << 13; // Rehash
|
||||
h ^= h >>> 17;
|
||||
h ^= h << 5;
|
||||
}
|
||||
else if (busy == 0 && cells == as && casBusy()) {
|
||||
boolean init = false;
|
||||
try { // Initialize table
|
||||
if (cells == as) {
|
||||
Cell[] rs = new Cell[2];
|
||||
rs[h & 1] = new Cell(x);
|
||||
cells = rs;
|
||||
init = true;
|
||||
}
|
||||
} finally {
|
||||
busy = 0;
|
||||
}
|
||||
if (init)
|
||||
break;
|
||||
}
|
||||
else if (casBase(v = base, fn(v, x)))
|
||||
break; // Fall back on using base
|
||||
}
|
||||
hc.code = h; // Record index for next time
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets base and all cells to the given value.
|
||||
*/
|
||||
final void internalReset(long initialValue) {
|
||||
Cell[] as = cells;
|
||||
base = initialValue;
|
||||
if (as != null) {
|
||||
int n = as.length;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Cell a = as[i];
|
||||
if (a != null)
|
||||
a.value = initialValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This is based on 1.16 version
|
||||
|
||||
package com.concurrent_ruby.ext.jsr166y;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* A random number generator isolated to the current thread. Like the
|
||||
* global {@link java.util.Random} generator used by the {@link
|
||||
* java.lang.Math} class, a {@code ThreadLocalRandom} is initialized
|
||||
* with an internally generated seed that may not otherwise be
|
||||
* modified. When applicable, use of {@code ThreadLocalRandom} rather
|
||||
* than shared {@code Random} objects in concurrent programs will
|
||||
* typically encounter much less overhead and contention. Use of
|
||||
* {@code ThreadLocalRandom} is particularly appropriate when multiple
|
||||
* tasks (for example, each a {@link ForkJoinTask}) use random numbers
|
||||
* in parallel in thread pools.
|
||||
*
|
||||
* <p>Usages of this class should typically be of the form:
|
||||
* {@code ThreadLocalRandom.current().nextX(...)} (where
|
||||
* {@code X} is {@code Int}, {@code Long}, etc).
|
||||
* When all usages are of this form, it is never possible to
|
||||
* accidently share a {@code ThreadLocalRandom} across multiple threads.
|
||||
*
|
||||
* <p>This class also provides additional commonly used bounded random
|
||||
* generation methods.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Doug Lea
|
||||
*/
|
||||
public class ThreadLocalRandom extends Random {
|
||||
// same constants as Random, but must be redeclared because private
|
||||
private static final long multiplier = 0x5DEECE66DL;
|
||||
private static final long addend = 0xBL;
|
||||
private static final long mask = (1L << 48) - 1;
|
||||
|
||||
/**
|
||||
* The random seed. We can't use super.seed.
|
||||
*/
|
||||
private long rnd;
|
||||
|
||||
/**
|
||||
* Initialization flag to permit calls to setSeed to succeed only
|
||||
* while executing the Random constructor. We can't allow others
|
||||
* since it would cause setting seed in one part of a program to
|
||||
* unintentionally impact other usages by the thread.
|
||||
*/
|
||||
boolean initialized;
|
||||
|
||||
// Padding to help avoid memory contention among seed updates in
|
||||
// different TLRs in the common case that they are located near
|
||||
// each other.
|
||||
private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
|
||||
|
||||
/**
|
||||
* The actual ThreadLocal
|
||||
*/
|
||||
private static final ThreadLocal<ThreadLocalRandom> localRandom =
|
||||
new ThreadLocal<ThreadLocalRandom>() {
|
||||
protected ThreadLocalRandom initialValue() {
|
||||
return new ThreadLocalRandom();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructor called only by localRandom.initialValue.
|
||||
*/
|
||||
ThreadLocalRandom() {
|
||||
super();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current thread's {@code ThreadLocalRandom}.
|
||||
*
|
||||
* @return the current thread's {@code ThreadLocalRandom}
|
||||
*/
|
||||
public static ThreadLocalRandom current() {
|
||||
return localRandom.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws {@code UnsupportedOperationException}. Setting seeds in
|
||||
* this generator is not supported.
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public void setSeed(long seed) {
|
||||
if (initialized)
|
||||
throw new UnsupportedOperationException();
|
||||
rnd = (seed ^ multiplier) & mask;
|
||||
}
|
||||
|
||||
protected int next(int bits) {
|
||||
rnd = (rnd * multiplier + addend) & mask;
|
||||
return (int) (rnd >>> (48-bits));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom, uniformly distributed value between the
|
||||
* given least value (inclusive) and bound (exclusive).
|
||||
*
|
||||
* @param least the least value returned
|
||||
* @param bound the upper bound (exclusive)
|
||||
* @throws IllegalArgumentException if least greater than or equal
|
||||
* to bound
|
||||
* @return the next value
|
||||
*/
|
||||
public int nextInt(int least, int bound) {
|
||||
if (least >= bound)
|
||||
throw new IllegalArgumentException();
|
||||
return nextInt(bound - least) + least;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom, uniformly distributed value
|
||||
* between 0 (inclusive) and the specified value (exclusive).
|
||||
*
|
||||
* @param n the bound on the random number to be returned. Must be
|
||||
* positive.
|
||||
* @return the next value
|
||||
* @throws IllegalArgumentException if n is not positive
|
||||
*/
|
||||
public long nextLong(long n) {
|
||||
if (n <= 0)
|
||||
throw new IllegalArgumentException("n must be positive");
|
||||
// Divide n by two until small enough for nextInt. On each
|
||||
// iteration (at most 31 of them but usually much less),
|
||||
// randomly choose both whether to include high bit in result
|
||||
// (offset) and whether to continue with the lower vs upper
|
||||
// half (which makes a difference only if odd).
|
||||
long offset = 0;
|
||||
while (n >= Integer.MAX_VALUE) {
|
||||
int bits = next(2);
|
||||
long half = n >>> 1;
|
||||
long nextn = ((bits & 2) == 0) ? half : n - half;
|
||||
if ((bits & 1) == 0)
|
||||
offset += n - nextn;
|
||||
n = nextn;
|
||||
}
|
||||
return offset + nextInt((int) n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom, uniformly distributed value between the
|
||||
* given least value (inclusive) and bound (exclusive).
|
||||
*
|
||||
* @param least the least value returned
|
||||
* @param bound the upper bound (exclusive)
|
||||
* @return the next value
|
||||
* @throws IllegalArgumentException if least greater than or equal
|
||||
* to bound
|
||||
*/
|
||||
public long nextLong(long least, long bound) {
|
||||
if (least >= bound)
|
||||
throw new IllegalArgumentException();
|
||||
return nextLong(bound - least) + least;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom, uniformly distributed {@code double} value
|
||||
* between 0 (inclusive) and the specified value (exclusive).
|
||||
*
|
||||
* @param n the bound on the random number to be returned. Must be
|
||||
* positive.
|
||||
* @return the next value
|
||||
* @throws IllegalArgumentException if n is not positive
|
||||
*/
|
||||
public double nextDouble(double n) {
|
||||
if (n <= 0)
|
||||
throw new IllegalArgumentException("n must be positive");
|
||||
return nextDouble() * n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudorandom, uniformly distributed value between the
|
||||
* given least value (inclusive) and bound (exclusive).
|
||||
*
|
||||
* @param least the least value returned
|
||||
* @param bound the upper bound (exclusive)
|
||||
* @return the next value
|
||||
* @throws IllegalArgumentException if least greater than or equal
|
||||
* to bound
|
||||
*/
|
||||
public double nextDouble(double least, double bound) {
|
||||
if (least >= bound)
|
||||
throw new IllegalArgumentException();
|
||||
return nextDouble() * (bound - least) + least;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -5851777807851030925L;
|
||||
}
|
||||
Reference in New Issue
Block a user