Skip to content

Commit

Permalink
Do lazy instruction deserialization against dup'ed reader
Browse files Browse the repository at this point in the history
Instruction reading is done lazily to avoid the cost of
deserializing method bodies that will never be called. However the
buffer associated with the original file was being shared across
all of these lazy deserializations, causing concurrency issues.

This patch modifies the lazy deserialization lambda to operate
against a duplicate of the original reader, isolating its changes
in a separate buffer and avoiding the concurrency issues.

Fixes jruby#6210
  • Loading branch information
headius committed Jun 9, 2020
1 parent e5d98e4 commit a040b30
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 8 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/persistence/IRReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static IRScope load(IRManager manager, final IRReaderDecoder file) throws
int instructionsOffset = file.decodeInt();
int poolOffset = file.decodeInt();

scope.allocateInterpreterContext(() -> file.decodeInstructionsAt(scope, poolOffset, instructionsOffset));
scope.allocateInterpreterContext(() -> file.dup().decodeInstructionsAt(scope, poolOffset, instructionsOffset));
}

return firstScope; // topmost scope;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,11 @@ public interface IRReaderDecoder {

public TemporaryVariableType decodeTemporaryVariableType();
public ByteList getFilename();

/**
* Duplicate this decoder to isolate any state changes.
*
* @return An identical decoder that's isolated from the original
*/
public IRReaderDecoder dup();
}
23 changes: 16 additions & 7 deletions core/src/main/java/org/jruby/ir/persistence/IRReaderStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,34 @@
public class IRReaderStream implements IRReaderDecoder, IRPersistenceValues {
private final ByteBuffer buf;
private final IRManager manager;
private final List<IRScope> scopes = new ArrayList<>();
private IRScope currentScope = null; // FIXME: This is not thread-safe and more than a little gross
private final List<IRScope> scopes;
private IRScope currentScope; // FIXME: This is not thread-safe and more than a little gross
/** Filename to use for the script */
private final ByteList filename;
private RubySymbol[] constantPool;

public IRReaderStream(IRManager manager, byte[] bytes, ByteList filename) {
this.manager = manager;
this.buf = ByteBuffer.wrap(bytes);
this.filename = filename;
this(ByteBuffer.wrap(bytes), manager, new ArrayList<>(), null, filename, null);
}

public IRReaderStream(IRManager manager, File file, ByteList filename) {
this(readingIntoBuffer(file), manager, new ArrayList<>(), null, filename, null);
}

private IRReaderStream(ByteBuffer buf, IRManager manager, List<IRScope> scopes, IRScope currentScope, ByteList filename, RubySymbol[] constantPool) {
this.buf = buf;
this.manager = manager;
this.buf = readingIntoBuffer(file);
this.scopes = scopes;
this.currentScope = currentScope;
this.filename = filename;
this.constantPool = constantPool;
}

public IRReaderDecoder dup() {
return new IRReaderStream(buf.duplicate(), manager, new ArrayList(scopes), currentScope, filename, constantPool);
}

private ByteBuffer readingIntoBuffer(File file) {
private static ByteBuffer readingIntoBuffer(File file) {
try {
return ByteBuffer.wrap(Files.readAllBytes(file.toPath()));
} catch (IOException e) {
Expand Down

0 comments on commit a040b30

Please sign in to comment.