6060import com .oracle .svm .core .feature .AutomaticallyRegisteredImageSingleton ;
6161import com .oracle .svm .core .graal .stackvalue .UnsafeStackValue ;
6262import com .oracle .svm .core .log .Log ;
63+ import com .oracle .svm .core .monitor .JavaMonitor ;
6364import com .oracle .svm .core .os .IsDefined ;
6465import com .oracle .svm .core .posix .PosixUtils ;
6566import com .oracle .svm .core .posix .headers .Errno ;
7576import com .oracle .svm .core .thread .ParkEvent ;
7677import com .oracle .svm .core .thread .ParkEvent .ParkEventFactory ;
7778import com .oracle .svm .core .thread .PlatformThreads ;
79+ import com .oracle .svm .core .util .TimeUtils ;
7880import com .oracle .svm .core .util .UnsignedUtils ;
7981import com .oracle .svm .core .util .VMError ;
8082
83+ import jdk .internal .misc .Unsafe ;
84+
8185@ AutomaticallyRegisteredImageSingleton (PlatformThreads .class )
8286public final class PosixPlatformThreads extends PlatformThreads {
8387
@@ -287,16 +291,32 @@ final class Target_java_lang_Thread {
287291 Pthread .pthread_t pthreadIdentifier ;
288292}
289293
290- class PosixParkEvent extends ParkEvent {
294+ /**
295+ * {@link PosixParkEvent} is based on HotSpot class {@code Parker} in {@code os_posix.cpp}, as of
296+ * JDK 19 (git commit hash: f640fc5a1eb876a657d0de011dcd9b9a42b88eec, JDK tag: jdk-19+30).
297+ * <p>
298+ * HotSpot has two constructs with a similar purpose: {@code ParkEvent} and {@code Parker}. The
299+ * latter implements JSR 166 synchronization primitives {@link Unsafe#park} and
300+ * {@link Unsafe#unpark}, just like we do here, therefore we base this implementation on
301+ * {@code Parker}. Our implementation of Java object monitors, {@link JavaMonitor}, uses the JSR 166
302+ * primitives, so it can potentially experience interference from unrelated calls to
303+ * {@link Unsafe#unpark}. This is a difference to HotSpot's {@code ObjectMonitor}, which uses a
304+ * separate HotSpot {@code ParkEvent} instance. Another difference is that {@code Parker} and the
305+ * code below return control to the caller on spurious wakeups, unlike HotSpot's {@code ParkEvent}.
306+ * This does not affect correctness.
307+ */
308+ final class PosixParkEvent extends ParkEvent {
309+ private static final Unsafe U = Unsafe .getUnsafe ();
310+ private static final long EVENT_OFFSET = U .objectFieldOffset (PosixParkEvent .class , "event" );
291311
292312 private Pthread .pthread_mutex_t mutex ;
293313 private Pthread .pthread_cond_t cond ;
294314
295- /**
296- * The ticket: false implies unavailable, true implies available. Volatile so it can be safely
297- * updated in {@link #reset()} without holding the lock.
298- */
299- protected volatile boolean event ;
315+ /** Permit: 1 if an unpark is pending, otherwise 0. */
316+ private volatile int event = 0 ;
317+
318+ /** Whether the owner is currently parked. Guarded by {@link #mutex}. */
319+ private boolean parked = false ;
300320
301321 PosixParkEvent () {
302322 // Allocate mutex and condition in a single step so that they are adjacent in memory.
@@ -308,61 +328,79 @@ class PosixParkEvent extends ParkEvent {
308328 final Pthread .pthread_mutexattr_t mutexAttr = WordFactory .nullPointer ();
309329 PosixUtils .checkStatusIs0 (Pthread .pthread_mutex_init (mutex , mutexAttr ), "mutex initialization" );
310330 PosixUtils .checkStatusIs0 (PthreadConditionUtils .initCondition (cond ), "condition variable initialization" );
331+ // Note: HotSpot has another pthread_cond_t without CLOCK_MONOTONIC for absolute timed waits
311332 }
312333
313334 @ Override
314335 protected void reset () {
315- event = false ;
336+ event = 0 ;
316337 }
317338
318339 @ Override
319340 protected void condWait () {
320- StackOverflowCheck .singleton ().makeYellowZoneAvailable ();
321- try {
322- PosixUtils .checkStatusIs0 (Pthread .pthread_mutex_lock (mutex ), "park(): mutex lock" );
323- try {
324- while (!event ) {
325- int status = Pthread .pthread_cond_wait (cond , mutex );
326- PosixUtils .checkStatusIs0 (status , "park(): condition variable wait" );
327- }
328- event = false ;
329- } finally {
330- PosixUtils .checkStatusIs0 (Pthread .pthread_mutex_unlock (mutex ), "park(): mutex unlock" );
331- }
332- } finally {
333- StackOverflowCheck .singleton ().protectYellowZone ();
341+ park (false , 0 );
342+ }
343+
344+ @ Override
345+ protected void condTimedWait (long durationNanos ) {
346+ if (durationNanos > 0 ) {
347+ park (false , durationNanos );
334348 }
335349 }
336350
337351 @ Override
338- protected void condTimedWait (long delayNanos ) {
352+ protected boolean tryFastPark () {
353+ // We depend on getAndSet having full barrier semantics since we are not locking
354+ return U .getAndSetInt (this , EVENT_OFFSET , 0 ) != 0 ;
355+ }
356+
357+ @ Override
358+ protected void park (boolean isAbsolute , long time ) {
359+ if (time < 0 || (isAbsolute && time == 0 )) {
360+ return ; // don't wait at all
361+ }
339362 StackOverflowCheck .singleton ().makeYellowZoneAvailable ();
340363 try {
341- /* Encode the delay as a deadline in a Time.timespec. */
342- Time . timespec deadlineTimespec = UnsafeStackValue . get ( Time . timespec . class );
343- PthreadConditionUtils . delayNanosToDeadlineTimespec ( delayNanos , deadlineTimespec );
344-
345- PosixUtils .checkStatusIs0 (Pthread . pthread_mutex_lock ( mutex ) , "park(long) : mutex lock " );
364+ int status = Pthread . pthread_mutex_trylock_no_transition ( mutex );
365+ if ( status == Errno . EBUSY ()) {
366+ return ; // can only mean another thread is unparking us: don't wait
367+ }
368+ PosixUtils .checkStatusIs0 (status , "park: mutex trylock " );
346369 try {
347- while (!event ) {
348- int status = Pthread .pthread_cond_timedwait (cond , mutex , deadlineTimespec );
349- if (status == Errno .ETIMEDOUT ()) {
350- break ;
351- } else if (status != 0 ) {
352- Log .log ().newline ()
353- .string ("[PosixParkEvent.condTimedWait(delayNanos: " ).signed (delayNanos ).string ("): Should not reach here." )
354- .string (" mutex: " ).hex (mutex )
355- .string (" cond: " ).hex (cond )
356- .string (" deadlineTimeSpec.tv_sec: " ).signed (deadlineTimespec .tv_sec ())
357- .string (" deadlineTimespec.tv_nsec: " ).signed (deadlineTimespec .tv_nsec ())
358- .string (" status: " ).signed (status ).string (" " ).string (Errno .strerror (status ))
359- .string ("]" ).newline ();
360- PosixUtils .checkStatusIs0 (status , "park(long): condition variable timed wait" );
370+ if (event == 0 ) {
371+ assert !parked ;
372+ parked = true ;
373+ if (!isAbsolute && time == 0 ) {
374+ status = Pthread .pthread_cond_wait (cond , mutex );
375+ PosixUtils .checkStatusIs0 (status , "park(): condition variable wait" );
376+ } else {
377+ long durationNanos = TimeUtils .durationNanos (isAbsolute , time );
378+ Time .timespec deadlineTimespec = UnsafeStackValue .get (Time .timespec .class );
379+ PthreadConditionUtils .durationNanosToDeadlineTimespec (durationNanos , deadlineTimespec );
380+
381+ status = Pthread .pthread_cond_timedwait (cond , mutex , deadlineTimespec );
382+ if (status != 0 && status != Errno .ETIMEDOUT ()) {
383+ Log .log ().newline ()
384+ .string ("[PosixParkEvent.park(durationNanos: " ).signed (durationNanos ).string ("): Should not reach here." )
385+ .string (" mutex: " ).hex (mutex )
386+ .string (" cond: " ).hex (cond )
387+ .string (" deadlineTimeSpec.tv_sec: " ).signed (deadlineTimespec .tv_sec ())
388+ .string (" deadlineTimespec.tv_nsec: " ).signed (deadlineTimespec .tv_nsec ())
389+ .string (" status: " ).signed (status ).string (" " ).string (Errno .strerror (status ))
390+ .string ("]" ).newline ();
391+ PosixUtils .checkStatusIs0 (status , "park(boolean, long): condition variable timed wait" );
392+ }
361393 }
394+ parked = false ;
362395 }
363- event = false ;
396+ event = 0 ;
397+
364398 } finally {
365- PosixUtils .checkStatusIs0 (Pthread .pthread_mutex_unlock (mutex ), "park(long): mutex unlock" );
399+ PosixUtils .checkStatusIs0 (Pthread .pthread_mutex_unlock (mutex ), "park: mutex unlock" );
400+
401+ // Paranoia to ensure our locked and lock-free paths interact
402+ // correctly with each other and Java-level accesses.
403+ U .fullFence ();
366404 }
367405 } finally {
368406 StackOverflowCheck .singleton ().protectYellowZone ();
@@ -373,13 +411,23 @@ protected void condTimedWait(long delayNanos) {
373411 protected void unpark () {
374412 StackOverflowCheck .singleton ().makeYellowZoneAvailable ();
375413 try {
414+ int s ;
415+ boolean p ;
376416 PosixUtils .checkStatusIs0 (Pthread .pthread_mutex_lock (mutex ), "PosixParkEvent.unpark(): mutex lock" );
377417 try {
378- event = true ;
379- PosixUtils .checkStatusIs0 (Pthread .pthread_cond_broadcast (cond ), "PosixParkEvent.unpark(): condition variable broadcast" );
418+ s = event ;
419+ event = 1 ;
420+ p = parked ;
380421 } finally {
381422 PosixUtils .checkStatusIs0 (Pthread .pthread_mutex_unlock (mutex ), "PosixParkEvent.unpark(): mutex unlock" );
382423 }
424+ if (s == 0 && p ) {
425+ /*
426+ * Signal without holding the mutex, which is safe and avoids futile wakeups if the
427+ * platform does not implement wait morphing.
428+ */
429+ PosixUtils .checkStatusIs0 (Pthread .pthread_cond_signal (cond ), "PosixParkEvent.unpark(): condition variable signal" );
430+ }
383431 } finally {
384432 StackOverflowCheck .singleton ().protectYellowZone ();
385433 }
@@ -400,9 +448,4 @@ class PosixParkEventFactory implements ParkEventFactory {
400448 public ParkEvent acquire () {
401449 return new PosixParkEvent ();
402450 }
403-
404- @ Override
405- public boolean usesParkEventList () {
406- return false ;
407- }
408451}
0 commit comments