This allows *BSDs to also have a fast path similar to futexes for thread-ID alerts. Also a kevent with `EV_CLEAR` and `NOTE_TRIGGER` maps perfectly to the thread alertable state, fixing the issue of the current mach-semaphore implementation not correctly waiting in the test case below: ``` NtAlertThreadByThreadId((HANDLE)GetCurrentThreadId()); NtAlertThreadByThreadId((HANDLE)GetCurrentThreadId()); NtWaitForAlertByThreadId(NULL, NULL); NtWaitForAlertByThreadId(NULL, NULL); ``` I took the liberty to remove this mach semaphore implementation, since all versions of OSX have supported kqueue().
From: Marc-Aurel Zent marc_aurel@me.com
--- dlls/ntdll/unix/sync.c | 118 +++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 41 deletions(-)
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index d1e4e5ee111..0b8c17c63d6 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -49,11 +49,11 @@ #include <stdlib.h> #include <time.h> #ifdef __APPLE__ -# include <mach/mach.h> -# include <mach/task.h> -# include <mach/semaphore.h> # include <mach/mach_time.h> #endif +#ifdef HAVE_KQUEUE +# include <sys/event.h> +#endif
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -2354,8 +2354,8 @@ NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS cl
union tid_alert_entry { -#ifdef __APPLE__ - semaphore_t sem; +#ifdef HAVE_KQUEUE + int kq; #else HANDLE event; #ifdef __linux__ @@ -2396,15 +2396,35 @@ static union tid_alert_entry *get_tid_alert_entry( HANDLE tid )
entry = &tid_alert_blocks[block_idx][idx % TID_ALERT_BLOCK_SIZE];
-#ifdef __APPLE__ - if (!entry->sem) +#ifdef HAVE_KQUEUE + if (!entry->kq) { - semaphore_t sem; + int kq = kqueue(); + static const struct kevent init_event = + { + .ident = 1, + .filter = EVFILT_USER, + .flags = EV_ADD | EV_CLEAR, + .fflags = 0, + .data = 0, + .udata = NULL + }; + + if (kq == -1) + { + ERR("kqueue failed with error: %d (%s)\n", errno, strerror( errno )); + return NULL; + }
- if (semaphore_create( mach_task_self(), &sem, SYNC_POLICY_FIFO, 0 )) + if (kevent( kq, &init_event, 1, NULL, 0, NULL) == -1) + { + ERR("kevent creation failed with error: %d (%s)\n", errno, strerror( errno )); + close( kq ); return NULL; - if (InterlockedCompareExchange( (LONG *)&entry->sem, sem, 0 )) - semaphore_destroy( mach_task_self(), sem ); + } + + if (InterlockedCompareExchange( (LONG*)&entry->kq, kq, 0 )) + close( kq ); } #else #ifdef __linux__ @@ -2438,8 +2458,18 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid )
if (!entry) return STATUS_INVALID_CID;
-#ifdef __APPLE__ - semaphore_signal( entry->sem ); +#ifdef HAVE_KQUEUE + static const struct kevent signal_event = + { + .ident = 1, + .filter = EVFILT_USER, + .flags = 0, + .fflags = NOTE_TRIGGER, + .data = 0, + .udata = NULL + }; + + kevent( entry->kq, &signal_event, 1, NULL, 0, NULL ); return STATUS_SUCCESS; #else #ifdef __linux__ @@ -2457,7 +2487,7 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) }
-#if defined(__linux__) || defined(__APPLE__) +#ifdef __linux__ static LONGLONG get_absolute_timeout( const LARGE_INTEGER *timeout ) { LARGE_INTEGER now; @@ -2480,52 +2510,58 @@ static LONGLONG update_timeout( ULONGLONG end ) #endif
-#ifdef __APPLE__ +#ifdef HAVE_KQUEUE
/*********************************************************************** * NtWaitForAlertByThreadId (NTDLL.@) */ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEGER *timeout ) { + int ret; + struct timespec timespec; + struct kevent wait_event; union tid_alert_entry *entry = get_tid_alert_entry( NtCurrentTeb()->ClientId.UniqueThread ); - semaphore_t sem; - ULONGLONG end; - kern_return_t ret;
TRACE( "%p %s\n", address, debugstr_timeout( timeout ) );
if (!entry) return STATUS_INVALID_CID; - sem = entry->sem; + + if (timeout && timeout->QuadPart == TIMEOUT_INFINITE) + timeout = NULL;
if (timeout) { - if (timeout->QuadPart == TIMEOUT_INFINITE) - timeout = NULL; - else - end = get_absolute_timeout( timeout ); - } + LARGE_INTEGER now; + LONGLONG timeleft;
- for (;;) - { - if (timeout) + if (timeout->QuadPart >= 0) { - LONGLONG timeleft = update_timeout( end ); - mach_timespec_t timespec; - - timespec.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; - timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100; - ret = semaphore_timedwait( sem, timespec ); + NtQuerySystemTime( &now ); + timeleft = timeout->QuadPart - now.QuadPart; + if (timeleft < 0) timeleft = 0; } else - ret = semaphore_wait( sem ); + timeleft = -timeout->QuadPart;
- switch (ret) - { - case KERN_SUCCESS: return STATUS_ALERTED; - case KERN_ABORTED: continue; - case KERN_OPERATION_TIMED_OUT: return STATUS_TIMEOUT; - default: return STATUS_INVALID_HANDLE; - } + timespec.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100; + + /* kevent only handles 32-bit signed numbers for seconds correctly */ + if (timespec.tv_sec > 0x7FFFFFFF) timeout = NULL; + } + + ret = kevent( entry->kq, NULL, 0, &wait_event, 1, timeout ? ×pec : NULL ); + + switch (ret) + { + case 1: + return STATUS_ALERTED; + case 0: + return STATUS_TIMEOUT; + default: + assert( ret == -1 ); + ERR("kevent failed with error: %d (%s)\n", errno, strerror(errno)); + return STATUS_INVALID_HANDLE; } }