From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ntdll/unix/sync.c | 234 +++++++++++++++++++++++++++++++-- dlls/ntdll/unix/unix_private.h | 1 + server/event.c | 3 + server/inproc_sync.c | 89 +++++++++++++ server/mutex.c | 4 + server/object.h | 4 + server/semaphore.c | 2 + 7 files changed, 328 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 7095aef30f2..bd0fe666bd1 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -30,9 +30,11 @@ #include <assert.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <limits.h> #include <signal.h> #include <sys/types.h> +#include <sys/ioctl.h> #include <sys/mman.h> #ifdef HAVE_SYS_SYSCALL_H #include <sys/syscall.h> @@ -48,6 +50,7 @@ #endif #include <string.h> #include <stdarg.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <time.h> @@ -306,6 +309,196 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at return STATUS_SUCCESS; }
+#ifdef NTSYNC_IOC_EVENT_READ + +static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count ) +{ + if (ioctl( obj, NTSYNC_IOC_SEM_RELEASE, &count ) < 0) + { + if (errno == EOVERFLOW) return STATUS_SEMAPHORE_LIMIT_EXCEEDED; + return errno_to_status( errno ); + } + if (prev_count) *prev_count = count; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_query_semaphore_obj( int obj, SEMAPHORE_BASIC_INFORMATION *info ) +{ + struct ntsync_sem_args args = {0}; + if (ioctl( obj, NTSYNC_IOC_SEM_READ, &args ) < 0) return errno_to_status( errno ); + info->CurrentCount = args.count; + info->MaximumCount = args.max; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_set_event_obj( int obj, LONG *prev_state ) +{ + __u32 prev; + if (ioctl( obj, NTSYNC_IOC_EVENT_SET, &prev ) < 0) return errno_to_status( errno ); + if (prev_state) *prev_state = prev; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_reset_event_obj( int obj, LONG *prev_state ) +{ + __u32 prev; + if (ioctl( obj, NTSYNC_IOC_EVENT_RESET, &prev ) < 0) return errno_to_status( errno ); + if (prev_state) *prev_state = prev; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_pulse_event_obj( int obj, LONG *prev_state ) +{ + __u32 prev; + if (ioctl( obj, NTSYNC_IOC_EVENT_PULSE, &prev ) < 0) return errno_to_status( errno ); + if (prev_state) *prev_state = prev; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_query_event_obj( int obj, enum inproc_sync_type type, EVENT_BASIC_INFORMATION *info ) +{ + struct ntsync_event_args args = {0}; + if (ioctl( obj, NTSYNC_IOC_EVENT_READ, &args ) < 0) return errno_to_status( errno ); + info->EventType = args.manual ? NotificationEvent : SynchronizationEvent; + info->EventState = args.signaled; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count ) +{ + struct ntsync_mutex_args args = {.owner = GetCurrentThreadId()}; + if (ioctl( obj, NTSYNC_IOC_MUTEX_UNLOCK, &args ) < 0) + { + if (errno == EOVERFLOW) return STATUS_MUTANT_LIMIT_EXCEEDED; + if (errno == EPERM) return STATUS_MUTANT_NOT_OWNED; + return errno_to_status( errno ); + } + if (prev_count) *prev_count = 1 - args.count; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_query_mutex_obj( int obj, MUTANT_BASIC_INFORMATION *info ) +{ + struct ntsync_mutex_args args = {0}; + if (ioctl( obj, NTSYNC_IOC_MUTEX_READ, &args ) < 0) + { + if (errno == EOWNERDEAD) + { + info->AbandonedState = TRUE; + info->OwnedByCaller = FALSE; + info->CurrentCount = 1; + return STATUS_SUCCESS; + } + return errno_to_status( errno ); + } + info->AbandonedState = FALSE; + info->OwnedByCaller = (args.owner == GetCurrentThreadId()); + info->CurrentCount = 1 - args.count; + return STATUS_SUCCESS; +} + +static NTSTATUS linux_wait_objs( int device, const DWORD count, const int *objs, + BOOLEAN wait_any, int alert_fd, const LARGE_INTEGER *timeout ) +{ + struct ntsync_wait_args args = {0}; + unsigned long request; + struct timespec now; + int ret; + + if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) + { + args.timeout = ~(__u64)0; + } + else if (timeout->QuadPart <= 0) + { + clock_gettime( CLOCK_MONOTONIC, &now ); + args.timeout = (now.tv_sec * NSECPERSEC) + now.tv_nsec + (-timeout->QuadPart * 100); + } + else + { + args.timeout = (timeout->QuadPart * 100) - (SECS_1601_TO_1970 * NSECPERSEC); + args.flags |= NTSYNC_WAIT_REALTIME; + } + + args.objs = (uintptr_t)objs; + args.count = count; + args.owner = GetCurrentThreadId(); + args.index = ~0u; + args.alert = alert_fd; + + if (wait_any || count == 1) request = NTSYNC_IOC_WAIT_ANY; + else request = NTSYNC_IOC_WAIT_ALL; + + do { ret = ioctl( device, request, &args ); } + while (ret < 0 && errno == EINTR); + + if (!ret) + { + if (args.index == count) + { + static const LARGE_INTEGER timeout; + + ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout ); + assert( ret == STATUS_USER_APC ); + return ret; + } + + return wait_any ? args.index : 0; + } + if (errno == EOWNERDEAD) return STATUS_ABANDONED + (wait_any ? args.index : 0); + if (errno == ETIMEDOUT) return STATUS_TIMEOUT; + return errno_to_status( errno ); +} + +#else /* NTSYNC_IOC_EVENT_READ */ + +static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_query_semaphore_obj( int obj, SEMAPHORE_BASIC_INFORMATION *info ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_set_event_obj( int obj, LONG *prev_state ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_reset_event_obj( int obj, LONG *prev_state ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_pulse_event_obj( int obj, LONG *prev_state ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_query_event_obj( int obj, enum inproc_sync_type type, EVENT_BASIC_INFORMATION *info ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_query_mutex_obj( int obj, MUTANT_BASIC_INFORMATION *info ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS linux_wait_objs( int device, const DWORD count, const int *objs, + BOOLEAN wait_any, int alert_fd, const LARGE_INTEGER *timeout ) +{ + return STATUS_NOT_IMPLEMENTED; +} + +#endif /* NTSYNC_IOC_EVENT_READ */
struct inproc_sync { @@ -397,6 +590,7 @@ static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *pre
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; if ((ret = get_inproc_sync( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_MODIFY_STATE, &stack ))) return ret; + ret = linux_release_semaphore_obj( sync->fd, count, prev_count ); release_inproc_sync( sync ); return ret; } @@ -408,6 +602,7 @@ static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATI
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; if ((ret = get_inproc_sync( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_QUERY_STATE, &stack ))) return ret; + ret = linux_query_semaphore_obj( sync->fd, info ); release_inproc_sync( sync ); return ret; } @@ -418,7 +613,8 @@ static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state ) NTSTATUS ret;
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; - if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, sync ))) return ret; + if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, &stack ))) return ret; + ret = linux_set_event_obj( sync->fd, prev_state ); release_inproc_sync( sync ); return ret; } @@ -429,7 +625,8 @@ static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state ) NTSTATUS ret;
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; - if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, sync ))) return ret; + if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, &stack ))) return ret; + ret = linux_reset_event_obj( sync->fd, prev_state ); release_inproc_sync( sync ); return ret; } @@ -440,7 +637,8 @@ static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state ) NTSTATUS ret;
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; - if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, sync ))) return ret; + if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_MODIFY_STATE, &stack ))) return ret; + ret = linux_pulse_event_obj( sync->fd, prev_state ); release_inproc_sync( sync ); return ret; } @@ -451,7 +649,8 @@ static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info NTSTATUS ret;
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; - if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_QUERY_STATE, sync ))) return ret; + if ((ret = get_inproc_sync( handle, INPROC_SYNC_EVENT, EVENT_QUERY_STATE, &stack ))) return ret; + ret = linux_query_event_obj( sync->fd, sync->type, info ); release_inproc_sync( sync ); return ret; } @@ -463,6 +662,7 @@ static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; if ((ret = get_inproc_sync( handle, INPROC_SYNC_MUTEX, 0, &stack ))) return ret; + ret = linux_release_mutex_obj( sync->fd, prev_count ); release_inproc_sync( sync ); return ret; } @@ -474,6 +674,7 @@ static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *inf
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; if ((ret = get_inproc_sync( handle, INPROC_SYNC_MUTEX, MUTANT_QUERY_STATE, &stack ))) return ret; + ret = linux_query_mutex_obj( sync->fd, info ); release_inproc_sync( sync ); return ret; } @@ -509,6 +710,7 @@ static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_an BOOLEAN alertable, const LARGE_INTEGER *timeout ) { struct inproc_sync *syncs[64], stack[ARRAY_SIZE(syncs)]; + int objs[ARRAY_SIZE(syncs)], alert_fd = 0; NTSTATUS ret;
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; @@ -521,19 +723,22 @@ static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_an while (i--) release_inproc_sync( syncs[i] ); return ret; } - syncs[i] = stack + i; + syncs[i] = &stack[i]; + objs[i] = syncs[i]->fd; }
- if (alertable) get_inproc_alert_fd(); + if (alertable) alert_fd = get_inproc_alert_fd(); + ret = linux_wait_objs( inproc_device_fd, count, objs, wait_any, alert_fd, timeout );
while (count--) release_inproc_sync( syncs[count] ); - return STATUS_NOT_IMPLEMENTED; + return ret; }
static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable, const LARGE_INTEGER *timeout ) { struct inproc_sync stack_signal, stack_wait, *signal_sync = &stack_signal, *wait_sync = &stack_wait; + int alert_fd = 0; NTSTATUS ret;
if (inproc_device_fd < 0) return STATUS_NOT_IMPLEMENTED; @@ -543,8 +748,19 @@ static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
if ((ret = get_inproc_sync( wait, INPROC_SYNC_UNKNOWN, SYNCHRONIZE, wait_sync ))) goto done;
- if (alertable) get_inproc_alert_fd(); - ret = STATUS_NOT_IMPLEMENTED; + switch (signal_sync->type) + { + case INPROC_SYNC_EVENT: ret = linux_set_event_obj( signal_sync->fd, NULL ); break; + case INPROC_SYNC_MUTEX: ret = linux_release_mutex_obj( signal_sync->fd, NULL ); break; + case INPROC_SYNC_SEMAPHORE: ret = linux_release_semaphore_obj( signal_sync->fd, 1, NULL ); break; + default: assert( 0 ); break; + } + + if (!ret) + { + if (alertable) alert_fd = get_inproc_alert_fd(); + ret = linux_wait_objs( inproc_device_fd, 1, &wait_sync->fd, TRUE, alert_fd, timeout ); + }
release_inproc_sync( wait_sync ); done: diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 67cd3aede42..3ef9041d64e 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -398,6 +398,7 @@ extern void call_raise_user_exception_dispatcher(void); #define IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE 0x0010 /* Wine extension */
#define TICKSPERSEC 10000000 +#define NSECPERSEC 1000000000 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)86400)
static inline ULONGLONG ticks_from_time_t( time_t time ) diff --git a/server/event.c b/server/event.c index 47a7d5a9b88..b70e9e06e2e 100644 --- a/server/event.c +++ b/server/event.c @@ -91,6 +91,8 @@ static struct object *create_event_sync( int manual, int signaled ) { struct event_sync *event;
+ if (get_inproc_device_fd() >= 0) return (struct object *)create_inproc_event_sync( manual, signaled ); + if (!(event = alloc_object( &event_sync_ops ))) return NULL; event->manual = manual; event->signaled = signaled; @@ -111,6 +113,7 @@ struct event_sync *create_server_internal_sync( int manual, int signaled )
struct object *create_internal_sync( int manual, int signaled ) { + if (get_inproc_device_fd() >= 0) return (struct object *)create_inproc_internal_sync( manual, signaled ); return (struct object *)create_server_internal_sync( manual, signaled ); }
diff --git a/server/inproc_sync.c b/server/inproc_sync.c index 46222d56a45..2b6b37cd2e0 100644 --- a/server/inproc_sync.c +++ b/server/inproc_sync.c @@ -57,8 +57,11 @@ struct inproc_sync struct object obj; /* object header */ enum inproc_sync_type type; int fd; + struct list entry; };
+static struct list inproc_mutexes = LIST_INIT( inproc_mutexes ); + static void inproc_sync_dump( struct object *obj, int verbose ); static int inproc_sync_signal( struct object *obj, unsigned int access, int signal ); static void inproc_sync_destroy( struct object *obj ); @@ -102,6 +105,7 @@ struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ) if (!(event = alloc_object( &inproc_sync_ops ))) return NULL; event->type = INPROC_SYNC_INTERNAL; event->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_EVENT, &args ); + list_init( &event->entry );
if (event->fd == -1) { @@ -112,6 +116,63 @@ struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ) return event; }
+struct inproc_sync *create_inproc_event_sync( int manual, int signaled ) +{ + struct ntsync_event_args args = {.signaled = signaled, .manual = manual}; + struct inproc_sync *event; + + if (!(event = alloc_object( &inproc_sync_ops ))) return NULL; + event->type = INPROC_SYNC_EVENT; + event->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_EVENT, &args ); + list_init( &event->entry ); + + if (event->fd == -1) + { + set_error( STATUS_NO_MORE_FILES ); + release_object( event ); + return NULL; + } + return event; +} + +struct inproc_sync *create_inproc_mutex_sync( thread_id_t owner, unsigned int count ) +{ + struct ntsync_mutex_args args = {.owner = owner, .count = count}; + struct inproc_sync *mutex; + + if (!(mutex = alloc_object( &inproc_sync_ops ))) return NULL; + mutex->type = INPROC_SYNC_MUTEX; + mutex->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_MUTEX, &args ); + list_add_tail( &inproc_mutexes, &mutex->entry ); + + if (mutex->fd == -1) + { + set_error( STATUS_NO_MORE_FILES ); + release_object( mutex ); + return NULL; + } + return mutex; +} + +struct inproc_sync *create_inproc_semaphore_sync( unsigned int initial, unsigned int max ) +{ + struct ntsync_sem_args args = {.count = initial, .max = max}; + struct inproc_sync *sem; + + if (!(sem = alloc_object( &inproc_sync_ops ))) return NULL; + sem->type = INPROC_SYNC_SEMAPHORE; + sem->fd = ioctl( get_inproc_device_fd(), NTSYNC_IOC_CREATE_SEM, &args ); + list_init( &sem->entry ); + + if (sem->fd == -1) + { + set_error( STATUS_NO_MORE_FILES ); + release_object( sem ); + return NULL; + } + return sem; +} + static void inproc_sync_dump( struct object *obj, int verbose ) { struct inproc_sync *sync = (struct inproc_sync *)obj; @@ -150,9 +211,18 @@ static void inproc_sync_destroy( struct object *obj ) { struct inproc_sync *sync = (struct inproc_sync *)obj; assert( obj->ops == &inproc_sync_ops ); + list_remove( &sync->entry ); close( sync->fd ); }
+void abandon_inproc_mutexes( thread_id_t tid ) +{ + struct inproc_sync *mutex; + + LIST_FOR_EACH_ENTRY( mutex, &inproc_mutexes, struct inproc_sync, entry ) + ioctl( mutex->fd, NTSYNC_IOC_MUTEX_KILL, &tid ); +} + static int get_obj_inproc_sync( struct object *obj, int *type ) { struct object *sync; @@ -187,6 +257,21 @@ struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ) return NULL; }
+struct inproc_sync *create_inproc_event_sync( int manual, int signaled ) +{ + return NULL; +} + +struct inproc_sync *create_inproc_mutex_sync( thread_id_t owner, unsigned int count ) +{ + return NULL; +} + +struct inproc_sync *create_inproc_semaphore_sync( unsigned int initial, unsigned int max ) +{ + return NULL; +} + void signal_inproc_sync( struct inproc_sync *sync ) { } @@ -195,6 +280,10 @@ void reset_inproc_sync( struct inproc_sync *sync ) { }
+void abandon_inproc_mutexes( thread_id_t tid ) +{ +} + static int get_obj_inproc_sync( struct object *obj, int *type ) { return -1; diff --git a/server/mutex.c b/server/mutex.c index 4932e07e96d..a1684536dc2 100644 --- a/server/mutex.c +++ b/server/mutex.c @@ -157,6 +157,8 @@ static struct object *create_mutex_sync( int owned ) { struct mutex_sync *mutex;
+ if (get_inproc_device_fd() >= 0) return (struct object *)create_inproc_mutex_sync( owned ? current->id : 0, owned ? 1 : 0 ); + if (!(mutex = alloc_object( &mutex_sync_ops ))) return NULL; mutex->count = 0; mutex->owner = NULL; @@ -235,6 +237,8 @@ void abandon_mutexes( struct thread *thread ) mutex->abandoned = 1; do_release( mutex, thread, mutex->count ); } + + abandon_inproc_mutexes( thread->id ); }
static void mutex_dump( struct object *obj, int verbose ) diff --git a/server/object.h b/server/object.h index bf242d0ad6d..2a0ecfc2cf2 100644 --- a/server/object.h +++ b/server/object.h @@ -247,6 +247,10 @@ struct inproc_sync; extern int get_inproc_device_fd(void); extern int get_inproc_sync_fd( struct inproc_sync *sync ); extern struct inproc_sync *create_inproc_internal_sync( int manual, int signaled ); +extern struct inproc_sync *create_inproc_event_sync( int manual, int signaled ); +extern struct inproc_sync *create_inproc_semaphore_sync( unsigned int initial, unsigned int max ); +extern struct inproc_sync *create_inproc_mutex_sync( thread_id_t owner, unsigned int count ); +extern void abandon_inproc_mutexes( thread_id_t owner ); extern void signal_inproc_sync( struct inproc_sync *sync ); extern void reset_inproc_sync( struct inproc_sync *sync );
diff --git a/server/semaphore.c b/server/semaphore.c index 1b9109e1f98..69e4cef987e 100644 --- a/server/semaphore.c +++ b/server/semaphore.c @@ -134,6 +134,8 @@ static struct object *create_semaphore_sync( unsigned int initial, unsigned int { struct semaphore_sync *sem;
+ if (get_inproc_device_fd() >= 0) return (struct object *)create_inproc_semaphore_sync( initial, max ); + if (!(sem = alloc_object( &semaphore_sync_ops ))) return NULL; sem->count = initial; sem->max = max;