Skip to content

Commit 6ae531e

Browse files
fix some concurrency issues
1 parent f5fa957 commit 6ae531e

File tree

2 files changed

+42
-30
lines changed

2 files changed

+42
-30
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrThrottler.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public JfrThrottler() {
7878
*/
7979
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
8080
private void normalize(long samplesPerPeriod, double periodMs) {
81-
assert rwlock.isWriteOwner();
81+
assert rwlock.isCurrentThreadWriteOwner();
8282
// Do we want more than 10samples/s ? If so convert to samples/s
8383
double periodsPerSecond = 1000.0 / periodMs;
8484
double samplesPerSecond = samplesPerPeriod * periodsPerSecond;
@@ -157,20 +157,20 @@ public boolean sample() {
157157
*/
158158
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
159159
private void rotateWindow() {
160-
assert rwlock.isWriteOwner();
160+
assert rwlock.isCurrentThreadWriteOwner();
161161
configure();
162162
installNextWindow();
163163
}
164164

165165
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
166166
private void installNextWindow() {
167-
assert rwlock.isWriteOwner();
167+
assert rwlock.isCurrentThreadWriteOwner();
168168
activeWindow = getNextWindow();
169169
}
170170

171171
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
172172
private JfrThrottlerWindow getNextWindow() {
173-
assert rwlock.isWriteOwner();
173+
assert rwlock.isCurrentThreadWriteOwner();
174174
if (window0 == activeWindow) {
175175
return window1;
176176
}
@@ -179,7 +179,7 @@ private JfrThrottlerWindow getNextWindow() {
179179

180180
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
181181
private long computeAccumulatedDebtCarryLimit(long windowDurationNs) {
182-
assert rwlock.isWriteOwner();
182+
assert rwlock.isCurrentThreadWriteOwner();
183183
if (periodNs == 0 || windowDurationNs >= TimeUtils.nanosPerSecond) {
184184
return 1;
185185
}
@@ -188,7 +188,7 @@ private long computeAccumulatedDebtCarryLimit(long windowDurationNs) {
188188

189189
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
190190
private long amortizeDebt(JfrThrottlerWindow lastWindow) {
191-
assert rwlock.isWriteOwner();
191+
assert rwlock.isCurrentThreadWriteOwner();
192192
if (accumulatedDebtCarryCount == accumulatedDebtCarryLimit) {
193193
accumulatedDebtCarryCount = 1;
194194
return 0; // reset because new settings have been applied
@@ -203,7 +203,7 @@ private long amortizeDebt(JfrThrottlerWindow lastWindow) {
203203
*/
204204
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
205205
private void setSamplePointsAndWindowDuration() {
206-
assert rwlock.isWriteOwner();
206+
assert rwlock.isCurrentThreadWriteOwner();
207207
assert reconfigure;
208208
JfrThrottlerWindow next = getNextWindow();
209209
long samplesPerWindow = eventSampleSize / WINDOW_DIVISOR;
@@ -224,7 +224,7 @@ private void setSamplePointsAndWindowDuration() {
224224

225225
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
226226
private void configure() {
227-
assert rwlock.isWriteOwner();
227+
assert rwlock.isCurrentThreadWriteOwner();
228228
JfrThrottlerWindow next = getNextWindow();
229229

230230
// Store updated parameters to both windows.
@@ -257,7 +257,7 @@ protected static double windowLookback(JfrThrottlerWindow window) {
257257

258258
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
259259
private double projectPopulationSize(long lastWindowMeasuredPop) {
260-
assert rwlock.isWriteOwner();
260+
assert rwlock.isCurrentThreadWriteOwner();
261261
avgPopulationSize = exponentiallyWeightedMovingAverage(lastWindowMeasuredPop, ewmaPopulationSizeAlpha, avgPopulationSize);
262262
return avgPopulationSize;
263263
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/utils/JfrReadWriteLock.java

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public class JfrReadWriteLock {
3737
private static final long CURRENTLY_WRITING = Long.MAX_VALUE;
3838
private final UninterruptibleUtils.AtomicLong ownerCount;
3939
private final UninterruptibleUtils.AtomicLong waitingWriters;
40-
private volatile long writeOwnerTid;
40+
private volatile long writeOwnerTid; // If this is set, then a writer owns the lock. Otherwise
41+
// -1.
4142

4243
public JfrReadWriteLock() {
4344
ownerCount = new UninterruptibleUtils.AtomicLong(0);
@@ -65,39 +66,42 @@ public void readTryLock(int retries) {
6566
int yields = 0;
6667
for (int i = 0; i < retries; i++) {
6768
long readers = ownerCount.get();
68-
// Only attempt to enter the critical section if no writers are waiting or writes
69-
// in-progress.
69+
// Only begin the attempt to enter the critical section if no writers are waiting or
70+
// writes are in-progress.
7071
if (waitingWriters.get() > 0 || readers == CURRENTLY_WRITING) {
7172
yields = maybeYield(i, yields);
7273
} else {
73-
// Attempt to take the lock
74+
// Attempt to take the lock.
7475
if (ownerCount.compareAndSet(readers, readers + 1)) {
7576
return;
7677
}
7778
}
7879
}
7980
}
8081

81-
@Uninterruptible(reason = "This method does not do a transition, so the whole critical section must be uninterruptible.", callerMustBe = true)
82+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
8283
public void writeTryLock(int retries) {
83-
int yields = 0;
84-
// Increment the writer to count to signal intent.
84+
// Increment the writer count to signal our intent to acquire the lock.
8585
waitingWriters.incrementAndGet();
86-
for (int i = 0; i < retries; i++) {
87-
long readers = ownerCount.get();
88-
// Only enter the critical section if all in-progress readers have finished.
89-
if (readers != 0) {
90-
yields = maybeYield(i, yields);
91-
} else {
92-
// Attempt to take the lock
93-
if (ownerCount.compareAndSet(0, CURRENTLY_WRITING)) {
94-
// Success. Signal no longer waiting.
95-
long waiters = waitingWriters.decrementAndGet();
96-
assert waiters >= 0;
97-
writeOwnerTid = SubstrateJVM.getCurrentThreadId();
98-
return;
86+
try {
87+
int yields = 0;
88+
for (int i = 0; i < retries; i++) {
89+
long readers = ownerCount.get();
90+
// Only enter the critical section if all in-progress readers have finished.
91+
if (readers != 0) {
92+
yields = maybeYield(i, yields);
93+
} else {
94+
// Attempt to acquire the lock.
95+
if (ownerCount.compareAndSet(0, CURRENTLY_WRITING)) {
96+
writeOwnerTid = SubstrateJVM.getCurrentThreadId();
97+
return;
98+
}
9999
}
100100
}
101+
} finally {
102+
// Regardless of whether we eventually acquired the lock, signal we are done waiting.
103+
long waiters = waitingWriters.decrementAndGet();
104+
assert waiters >= 0;
101105
}
102106
}
103107

@@ -122,12 +126,20 @@ private static int maybeYield(int retryCount, int yields) {
122126

123127
@Uninterruptible(reason = "Used in locking without transition, so the whole critical section must be uninterruptible.", callerMustBe = true)
124128
public void unlock() {
129+
if (writeOwnerTid < 0) {
130+
// Readers own the lock.
131+
long readerCount = ownerCount.decrementAndGet();
132+
assert readerCount >= 0;
133+
return;
134+
}
135+
// A writer owns the lock.
136+
assert isCurrentThreadWriteOwner();
125137
writeOwnerTid = -1;
126138
ownerCount.set(0);
127139
}
128140

129141
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
130-
public boolean isWriteOwner() {
142+
public boolean isCurrentThreadWriteOwner() {
131143
return writeOwnerTid == SubstrateJVM.getCurrentThreadId();
132144
}
133145
}

0 commit comments

Comments
 (0)