Skip to content

Instantly share code, notes, and snippets.

@headius
Created October 1, 2026 12:09
Show Gist options
  • Select an option

  • Save headius/657cd8bf8199c1c7166a3a3c120c553f to your computer and use it in GitHub Desktop.

Select an option

Save headius/657cd8bf8199c1c7166a3a3c120c553f to your computer and use it in GitHub Desktop.
/*
**** BEGIN LICENSE BLOCK *****
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v20.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.specialized.RubyArrayOneObject;
import org.jruby.specialized.RubyArrayTwoObject;
import org.jruby.test.Base;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.jruby.runtime.Helpers.arrayOf;
/**
* Test case for functionality in RubyArray
*/
public class TestRubyArraySpecialized extends Base {
public TestRubyArraySpecialized(final String name) {
super(name);
}
private static final int THREADS = 8;
private static final int UNPACK_ROUNDS = 20_000;
// Automatically converted from Ruby code in jruby/jruby#9755
public void testConcurrentUnpackOfSmallArray() throws Exception {
CyclicBarrier start = new CyclicBarrier(THREADS + 1);
CyclicBarrier end = new CyclicBarrier(THREADS + 1);
var array1 = new AtomicReference<RubyArrayOneObject>();
var array2 = new AtomicReference<RubyArrayTwoObject>();
var array3 = new AtomicReference<RubyArray<IRubyObject>>();
var failure = new AtomicReference<Throwable>();
Thread[] workers = new Thread[THREADS];
IRubyObject tru = context.tru;
for (int i = 0; i < THREADS; i++) {
workers[i] = new Thread(() -> {
for (int round = 0; round < UNPACK_ROUNDS; round++) {
try {
start.await(30, TimeUnit.SECONDS);
} catch (Throwable throwable) {
failure.compareAndSet(null, throwable);
return;
}
try {
checkAry1(array1.get());
checkAry2(array2.get());
checkAry3(array3.get());
} catch (Throwable throwable) {
failure.compareAndSet(null, throwable);
}
try {
end.await(30, TimeUnit.SECONDS);
} catch (Throwable throwable) {
failure.compareAndSet(null, throwable);
return;
}
}
}, "concurrent-array-unpack-" + i);
workers[i].setDaemon(true);
workers[i].start();
}
Ruby runtime = context.runtime;
for (int round = 0; round < UNPACK_ROUNDS; round++) {
var oneElementArray = new RubyArrayOneObject(runtime, tru);
array1.set(oneElementArray);
var twoElementArray = new RubyArrayTwoObject(runtime, tru, tru);
array2.set(twoElementArray);
var threeElementArray = new RubyArray(runtime, arrayOf(tru, tru, tru), 0, 3);
array3.set(threeElementArray);
start.await(30, TimeUnit.SECONDS);
end.await(30, TimeUnit.SECONDS);
try {
checkAry1(oneElementArray);
checkAry2(twoElementArray);
checkAry3(threeElementArray);
} catch (Throwable t) {
failure.compareAndSet(null, t);
}
}
for (Thread worker : workers) {
worker.join(TimeUnit.SECONDS.toMillis(30));
assertFalse("concurrent array worker did not terminate", worker.isAlive());
}
if (failure.get() != null) {
AssertionError error = new AssertionError("concurrent unpack failed", failure.get());
throw error;
}
}
private void checkAry1(RubyArray ary) {
ary.unpack(context);
if (ary.size() != 1 || ary.eltInternal(0) != context.tru) {
throw new AssertionError("one-element RubyArray was corrupted");
}
}
private void checkAry2(RubyArray ary) {
IRubyObject tru = context.tru;
ary.unpack(context);
if (ary.size() != 2 || ary.eltInternal(0) != tru || ary.eltInternal(1) != tru) {
throw new AssertionError("two-element RubyArray was corrupted");
}
}
private void checkAry3(RubyArray ary) {
IRubyObject tru = context.tru;
ary.unpack(context);
if (ary.size() != 3 || ary.eltInternal(0) != tru || ary.eltInternal(1) != tru || ary.eltInternal(2) != tru) {
throw new AssertionError("three-element RubyArray was corrupted");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment