Skip to content

Commit 4476ec4

Browse files
committed
Merge pull request #13 in G/truffle from smarr-fix-race-in-breakpoint-construction to master
* commit '9a91ef538a1fd7761e22602d5b21bbb473706bff': Fix race condition between Breakpoint construction and access from listeners
2 parents ca42151 + 9a91ef5 commit 4476ec4

File tree

1 file changed

+49
-33
lines changed

1 file changed

+49
-33
lines changed

truffle/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/BreakpointFactory.java

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ Breakpoint create(int ignoreCount, LineLocation lineLocation, boolean oneShot) t
157157
if (breakpoint == null) {
158158
final SourceSectionFilter query = SourceSectionFilter.newBuilder().sourceIs(lineLocation.getSource()).lineStartsIn(IndexRange.byLength(lineLocation.getLineNumber(), 1)).tagIs(
159159
StandardTags.StatementTag.class).build();
160-
breakpoint = new BreakpointImpl(lineLocation, query, ignoreCount, oneShot);
160+
breakpoint = createBreakpoint(lineLocation, query, ignoreCount, oneShot);
161161
if (TRACE) {
162162
trace("NEW " + breakpoint.getShortDescription());
163163
}
@@ -192,7 +192,7 @@ Breakpoint create(int ignoreCount, Class<?> tag, boolean oneShot) throws IOExcep
192192
BreakpointImpl breakpoint = breakpoints.get(tag);
193193
if (breakpoint == null) {
194194
final SourceSectionFilter query = SourceSectionFilter.newBuilder().tagIs(tag).build();
195-
breakpoint = new BreakpointImpl(tag, query, ignoreCount, oneShot);
195+
breakpoint = createBreakpoint(tag, query, ignoreCount, oneShot);
196196
if (TRACE) {
197197
trace("NEW " + breakpoint.getShortDescription());
198198
}
@@ -253,6 +253,16 @@ private void forget(BreakpointImpl breakpoint) {
253253
breakpoints.remove(breakpoint.getKey());
254254
}
255255

256+
BreakpointImpl createBreakpoint(Object key, SourceSectionFilter query, int ignoreCount, boolean isOneShot) {
257+
BreakpointImpl breakpoint = new BreakpointImpl(key, query, ignoreCount, isOneShot);
258+
// Register listener after breakpoint has been constructed and JMM
259+
// allows for safe publication. Otherwise, we can't be sure that the
260+
// assumption fields are visible by other threads, which would lead to
261+
// a race with object initialization.
262+
breakpoint.binding = instrumenter.attachListener(breakpoint.locationQuery, new BreakpointListener(breakpoint));
263+
return breakpoint;
264+
}
265+
256266
private final class BreakpointImpl extends Breakpoint implements ExecutionEventNodeFactory {
257267

258268
private static final String SHOULD_NOT_HAPPEN = "BreakpointImpl: should not happen";
@@ -266,24 +276,24 @@ private final class BreakpointImpl extends Breakpoint implements ExecutionEventN
266276
@SuppressWarnings("rawtypes") private EventBinding binding;
267277

268278
// Cached assumption that the global status of line breakpoint activity has not changed.
269-
private Assumption breakpointsActiveAssumption;
279+
@CompilationFinal private Assumption breakpointsActiveAssumption;
270280

271281
// Whether this breakpoint is enable/disabled
272282
@CompilationFinal private boolean isEnabled;
273-
private Assumption enabledUnchangedAssumption;
283+
@CompilationFinal private Assumption enabledUnchangedAssumption;
274284

275285
private Source conditionSource;
276286
@SuppressWarnings("rawtypes") private Class<? extends TruffleLanguage> condLangClass;
277287

278-
BreakpointImpl(Object key, SourceSectionFilter query, int ignoreCount, boolean isOneShot) {
288+
private BreakpointImpl(Object key, SourceSectionFilter query, int ignoreCount, boolean isOneShot) {
279289
super();
280290
this.ignoreCount = ignoreCount;
281291
this.isOneShot = isOneShot;
282292
this.locationKey = key;
283293
this.locationQuery = query;
284-
this.binding = instrumenter.attachListener(locationQuery, new BreakpointListener());
285-
this.breakpointsActiveAssumption = BreakpointFactory.this.breakpointsActiveUnchanged.getAssumption();
286294
this.isEnabled = true;
295+
296+
this.breakpointsActiveAssumption = BreakpointFactory.this.breakpointsActiveUnchanged.getAssumption();
287297
this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption("Breakpoint enabled state unchanged");
288298
}
289299

@@ -347,7 +357,7 @@ public void setCondition(String expr) throws IOException {
347357
binding.dispose();
348358
if (expr == null) {
349359
conditionSource = null;
350-
binding = instrumenter.attachListener(locationQuery, new BreakpointListener());
360+
binding = instrumenter.attachListener(locationQuery, new BreakpointListener(this));
351361
} else {
352362
conditionSource = Source.fromText(expr, "breakpoint condition from text: " + expr);
353363
binding = instrumenter.attachFactory(locationQuery, this);
@@ -482,31 +492,6 @@ private void addExceptionWarning(Exception ex) {
482492
warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage()));
483493
}
484494

485-
/** Attached to implement an unconditional breakpoint. */
486-
private final class BreakpointListener implements ExecutionEventListener {
487-
488-
@Override
489-
public void onEnter(EventContext context, VirtualFrame frame) {
490-
if (TRACE) {
491-
trace("hit breakpoint " + getShortDescription());
492-
}
493-
BreakpointImpl.this.nodeEnter(context, frame);
494-
}
495-
496-
@Override
497-
public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
498-
}
499-
500-
@Override
501-
public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
502-
}
503-
504-
@Override
505-
public String toString() {
506-
return getShortDescription();
507-
}
508-
}
509-
510495
/** Attached to implement a conditional breakpoint. */
511496
private class BreakpointConditionEventNode extends ExecutionEventNode {
512497
@Child DirectCallNode callNode;
@@ -551,4 +536,35 @@ public String toString() {
551536
return sb.toString();
552537
}
553538
}
539+
540+
/** Attached to implement an unconditional breakpoint. */
541+
private static final class BreakpointListener implements ExecutionEventListener {
542+
543+
private final BreakpointImpl breakpoint;
544+
545+
BreakpointListener(BreakpointImpl breakpoint) {
546+
this.breakpoint = breakpoint;
547+
}
548+
549+
@Override
550+
public void onEnter(EventContext context, VirtualFrame frame) {
551+
if (TRACE) {
552+
trace("hit breakpoint " + breakpoint.getShortDescription());
553+
}
554+
breakpoint.nodeEnter(context, frame);
555+
}
556+
557+
@Override
558+
public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
559+
}
560+
561+
@Override
562+
public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
563+
}
564+
565+
@Override
566+
public String toString() {
567+
return breakpoint.getShortDescription();
568+
}
569+
}
554570
}

0 commit comments

Comments
 (0)