Thanks, that clears up a lot!
I see you're using the semaphore as just a mutex instead of synchronizing on an object. Is that just so you can use the non-blocking tryAcquire? It seems like you wouldn't need lock-busting logic for a synchronized object unless there's a bug in the JVM. Have you seen otherwise? Something like:
@Field static Object mutex = new Object()
def f() {
synchronized (mutex) { ... }
}
If I were to instead synchronize on this, what would this refer to? I seem to remember reading that the platform creates a separate instance per event, so I'm guessing it wouldn't protect the critical section across events, right?
Is there a way to scope the mutex to f?
def f() {
static Object mutex = new Object() // Illegal
synchronized (mutex) { ... }
}
This isn't legal, but would be slightly desirable for readability and for preventing mutex abuse (e.g., if I simply want to prevent two instances of a handler function from running concurrently).
Finally, one question about atomic state: can I assume that if a read of state.something occurs after state.something has been set by another thread, the read will see the new value rather than the previous value? Rephrased in code:
@Field static Object mutex = new Object()
def handler() {
synchronized (mutex) {
if (state.everythingIsDone) { return }
// Using atomic state, is this guaranteed
// to execute at most once?
state.everythingIsDone = true
}
}
Thanks for your help!