From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/d3dkmt.c | 87 +++++++++++++++++++-- dlls/win32u/tests/d3dkmt.c | 42 +++++------ server/d3dkmt.c | 151 +++++++++++++++++++++++++++++++++++++ server/object.h | 1 + server/protocol.def | 25 ++++++ server/thread.c | 1 + server/thread.h | 1 + 7 files changed, 282 insertions(+), 26 deletions(-)
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c index f9bcfcfc047..e89127a9103 100644 --- a/dlls/win32u/d3dkmt.c +++ b/dlls/win32u/d3dkmt.c @@ -45,6 +45,7 @@ struct d3dkmt_object struct d3dkmt_mutex { struct d3dkmt_object obj; + BOOL owned; };
struct d3dkmt_resource @@ -1418,12 +1419,28 @@ NTSTATUS WINAPI NtGdiDdDDICreateKeyedMutex( D3DKMT_CREATEKEYEDMUTEX *params ) NTSTATUS WINAPI NtGdiDdDDIDestroyKeyedMutex( const D3DKMT_DESTROYKEYEDMUTEX *params ) { struct d3dkmt_mutex *mutex; + BOOL owned;
TRACE( "params %p\n", params );
if (!(mutex = get_d3dkmt_object( params->hKeyedMutex, D3DKMT_MUTEX ))) return STATUS_INVALID_PARAMETER; - d3dkmt_object_free( &mutex->obj ); + pthread_mutex_lock( &d3dkmt_lock ); + owned = mutex->owned; + mutex->owned = FALSE; + pthread_mutex_unlock( &d3dkmt_lock ); + + if (owned) + { + SERVER_START_REQ( d3dkmt_mutex_release ) + { + req->mutex = mutex->obj.global; + req->abandon = 1; + wine_server_call( req ); + } + SERVER_END_REQ; + }
+ d3dkmt_object_free( &mutex->obj ); return STATUS_SUCCESS; }
@@ -1501,8 +1518,46 @@ failed: */ NTSTATUS WINAPI NtGdiDdDDIAcquireKeyedMutex2( D3DKMT_ACQUIREKEYEDMUTEX2 *params ) { - FIXME( "params %p stub!\n", params ); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS status = STATUS_SUCCESS; + LARGE_INTEGER now, *timeout; + struct d3dkmt_mutex *mutex; + HANDLE wait_handle = NULL; + + TRACE( "params %p\n", params ); + + if ((timeout = params->pTimeout) && timeout->QuadPart < 0) + { + NtQuerySystemTime( &now ); + now.QuadPart -= timeout->QuadPart; + timeout = &now; + } + + if (!(mutex = get_d3dkmt_object( params->hKeyedMutex, D3DKMT_MUTEX ))) return STATUS_INVALID_PARAMETER; + + do + { + if (wait_handle) status = NtWaitForSingleObject( wait_handle, FALSE, timeout ); + SERVER_START_REQ( d3dkmt_mutex_acquire ) + { + req->mutex = mutex->obj.global; + req->key_value = params->Key; + req->wait_handle = wine_server_obj_handle( wait_handle ); + req->wait_status = status; + + status = wine_server_call( req ); + params->FenceValue = reply->fence_value; + wait_handle = wine_server_ptr_handle( reply->wait_handle ); + } + SERVER_END_REQ; + } while (status == STATUS_PENDING); + + if (!status) + { + pthread_mutex_lock( &d3dkmt_lock ); + mutex->owned = TRUE; + pthread_mutex_unlock( &d3dkmt_lock ); + } + return status; }
/****************************************************************************** @@ -1531,8 +1586,30 @@ NTSTATUS WINAPI NtGdiDdDDIAcquireKeyedMutex( D3DKMT_ACQUIREKEYEDMUTEX *params ) */ NTSTATUS WINAPI NtGdiDdDDIReleaseKeyedMutex2( D3DKMT_RELEASEKEYEDMUTEX2 *params ) { - FIXME( "params %p stub!\n", params ); - return STATUS_NOT_IMPLEMENTED; + struct d3dkmt_mutex *mutex; + NTSTATUS status; + + TRACE( "params %p\n", params ); + + if (!(mutex = get_d3dkmt_object( params->hKeyedMutex, D3DKMT_MUTEX ))) return STATUS_INVALID_PARAMETER; + + SERVER_START_REQ( d3dkmt_mutex_release ) + { + req->mutex = mutex->obj.global; + req->key_value = params->Key; + req->fence_value = params->FenceValue; + status = wine_server_call( req ); + } + SERVER_END_REQ; + + if (!status) + { + pthread_mutex_lock( &d3dkmt_lock ); + mutex->owned = FALSE; + pthread_mutex_unlock( &d3dkmt_lock ); + } + + return status; }
/****************************************************************************** diff --git a/dlls/win32u/tests/d3dkmt.c b/dlls/win32u/tests/d3dkmt.c index 4c9e6e5704d..b3a9baf4785 100644 --- a/dlls/win32u/tests/d3dkmt.c +++ b/dlls/win32u/tests/d3dkmt.c @@ -2074,7 +2074,7 @@ static void test_D3DKMTCreateKeyedMutex( void ) static DWORD WINAPI test_acquire_mutex( void *arg ) { NTSTATUS status = D3DKMTAcquireKeyedMutex( arg ); - todo_wine ok_nt( STATUS_ABANDONED, status ); + ok_nt( STATUS_ABANDONED, status ); return 0; }
@@ -2104,7 +2104,7 @@ static void test_D3DKMTAcquireKeyedMutex( void ) acquire.pTimeout = &timeout; acquire.FenceValue = 0xdeadbeef; status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + ok_nt( STATUS_INVALID_PARAMETER, status ); ok_x4( acquire.Key, ==, 0xdeadbeef ); ok_x8( acquire.FenceValue, ==, 0xdeadbeef );
@@ -2112,17 +2112,17 @@ static void test_D3DKMTAcquireKeyedMutex( void ) acquire.Key = 0xdeadbeef; acquire.FenceValue = 0xdeadbeef; status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_TIMEOUT, status ); + ok_nt( STATUS_TIMEOUT, status ); ok_x4( acquire.Key, ==, 0xdeadbeef ); - todo_wine ok_x8( acquire.FenceValue, ==, 0 ); + ok_x8( acquire.FenceValue, ==, 0 );
acquire.hKeyedMutex = create.hKeyedMutex; acquire.Key = 0; acquire.FenceValue = 0xdeadbeef; status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); ok_x4( acquire.Key, ==, 0 ); - todo_wine ok_x8( acquire.FenceValue, ==, 0 ); + ok_x8( acquire.FenceValue, ==, 0 );
status = D3DKMTReleaseKeyedMutex( NULL ); ok_nt( STATUS_INVALID_PARAMETER, status ); @@ -2130,42 +2130,42 @@ static void test_D3DKMTAcquireKeyedMutex( void ) release.Key = 0xdeadbeef; release.FenceValue = 0xdeadbeef; status = D3DKMTReleaseKeyedMutex( &release ); - todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + ok_nt( STATUS_INVALID_PARAMETER, status ); ok_x4( release.Key, ==, 0xdeadbeef ); ok_x8( release.FenceValue, ==, 0xdeadbeef ); release.hKeyedMutex = create.hKeyedMutex; release.Key = 1; release.FenceValue = 1234; status = D3DKMTReleaseKeyedMutex( &release ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); ok_x4( release.Key, ==, 1 ); ok_x8( release.FenceValue, ==, 1234 ); release.FenceValue = 0xdeadbeef; status = D3DKMTReleaseKeyedMutex( &release ); - todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + ok_nt( STATUS_INVALID_PARAMETER, status ); ok_x4( release.Key, ==, 1 ); ok_x8( release.FenceValue, ==, 0xdeadbeef );
acquire.Key = 0; acquire.FenceValue = 0xdeadbeef; status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_TIMEOUT, status ); - todo_wine ok_x8( acquire.FenceValue, ==, 0 ); + ok_nt( STATUS_TIMEOUT, status ); + ok_x8( acquire.FenceValue, ==, 0 ); acquire.Key = 1; acquire.FenceValue = 0xdeadbeef; status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine ok_x8( acquire.FenceValue, ==, 1234 ); + ok_nt( STATUS_SUCCESS, status ); + ok_x8( acquire.FenceValue, ==, 1234 );
release.Key = 0; release.FenceValue = 0; status = D3DKMTReleaseKeyedMutex( &release ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); acquire.Key = 0; acquire.FenceValue = 0xdeadbeef; status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine ok_x8( acquire.FenceValue, ==, 0 ); + ok_nt( STATUS_SUCCESS, status ); + ok_x8( acquire.FenceValue, ==, 0 );
open.hSharedHandle = create.hSharedHandle; open.hKeyedMutex = 0xdeadbeef; @@ -2180,7 +2180,7 @@ static void test_D3DKMTAcquireKeyedMutex( void ) thread = CreateThread( NULL, 0, test_acquire_mutex, &acquire, 0, NULL ); ok_ptr( thread, !=, NULL ); ret = WaitForSingleObject( thread, 100 ); - todo_wine ok_u4( ret, ==, WAIT_TIMEOUT ); + ok_u4( ret, ==, WAIT_TIMEOUT );
destroy.hKeyedMutex = create.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); @@ -2193,11 +2193,11 @@ static void test_D3DKMTAcquireKeyedMutex( void ) acquire.FenceValue = 0xdeadbeef; timeout.QuadPart = 100 * -10000; status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_ABANDONED, status ); - todo_wine ok_x8( acquire.FenceValue, ==, 0 ); + ok_nt( STATUS_ABANDONED, status ); + ok_x8( acquire.FenceValue, ==, 0 ); status = D3DKMTAcquireKeyedMutex( &acquire ); - todo_wine ok_nt( STATUS_ABANDONED, status ); - todo_wine ok_x8( acquire.FenceValue, ==, 0 ); + ok_nt( STATUS_ABANDONED, status ); + ok_x8( acquire.FenceValue, ==, 0 );
destroy.hKeyedMutex = open.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); diff --git a/server/d3dkmt.c b/server/d3dkmt.c index 47824a8f1a0..6c8c971dc44 100644 --- a/server/d3dkmt.c +++ b/server/d3dkmt.c @@ -95,10 +95,23 @@ static const struct fd_ops d3dkmt_fd_ops = default_fd_reselect_async /* reselect_async */ };
+struct keyed_wait +{ + struct list entry; + int key; + int waiters; + struct object *sync; +}; + struct d3dkmt_mutex { struct d3dkmt_object base; unsigned int key_value; /* last released key value */ + unsigned __int64 fence_value; /* last released fence value */ + bool abandoned; /* mutex has been abandonned */ + struct thread *owner; /* current owner thread */ + struct list waits; /* list of pending keyed_waits */ + struct list entry; /* entry in owner d3dkmt_mutexes */ };
static void d3dkmt_mutex_dump( struct object *obj, int verbose ); @@ -415,8 +428,17 @@ static void d3dkmt_mutex_dump( struct object *obj, int verbose ) static void d3dkmt_mutex_destroy( struct object *obj ) { struct d3dkmt_mutex *mutex = (struct d3dkmt_mutex *)obj; + struct keyed_wait *wait, *next; + assert( obj->ops == &d3dkmt_mutex_ops );
+ LIST_FOR_EACH_ENTRY_SAFE( wait, next, &mutex->waits, struct keyed_wait, entry ) + { + release_object( wait->sync ); + list_remove( &wait->entry ); + free( wait ); + } + if (mutex->base.global) free_object_handle( mutex->base.global ); free( mutex->base.runtime ); } @@ -431,6 +453,10 @@ static struct d3dkmt_object *d3dkmt_mutex_create( unsigned int key_value, data_s object->base.runtime_size = runtime_size; object->base.fd = NULL; object->key_value = key_value; + object->fence_value = 0; + object->abandoned = false; + object->owner = NULL; + list_init( &object->waits );
if (!(object->base.runtime = memdup( runtime, runtime_size )) || !(object->base.global = alloc_object_handle( &object->base ))) @@ -442,6 +468,80 @@ static struct d3dkmt_object *d3dkmt_mutex_create( unsigned int key_value, data_s return &object->base; }
+static struct object *keyed_wait_grab( struct d3dkmt_mutex *mutex, int key ) +{ + struct keyed_wait *wait; + + LIST_FOR_EACH_ENTRY( wait, &mutex->waits, struct keyed_wait, entry ) + { + if (wait->key != key) continue; + wait->waiters++; + return grab_object( wait->sync ); + } + + if (!(wait = mem_alloc( sizeof(*wait) ))) return NULL; + wait->key = key; + wait->waiters = 1; + if (!(wait->sync = create_internal_sync( 0, 0 ))) + { + free( wait ); + return NULL; + } + + list_add_tail( &mutex->waits, &wait->entry ); + return grab_object( wait->sync ); +} + +static void keyed_wait_release( struct d3dkmt_mutex *mutex, int key ) +{ + struct keyed_wait *wait; + + LIST_FOR_EACH_ENTRY( wait, &mutex->waits, struct keyed_wait, entry ) + { + if (wait->key == key && !--wait->waiters) + { + release_object( wait->sync ); + list_remove( &wait->entry ); + free( wait ); + break; + } + } +} + +static void mutex_grab( struct d3dkmt_mutex *mutex ) +{ + grab_object( mutex ); + list_add_tail( ¤t->d3dkmt_mutexes, &mutex->entry ); + mutex->owner = current; +} + +static void mutex_release( struct d3dkmt_mutex *mutex, bool abandon ) +{ + struct keyed_wait *wait; + + LIST_FOR_EACH_ENTRY( wait, &mutex->waits, struct keyed_wait, entry ) + { + if (abandon || wait->key == mutex->key_value) + { + signal_sync( wait->sync ); + if (!abandon) break; + } + } + if (abandon) mutex->abandoned = true; + + mutex->owner = NULL; + list_remove( &mutex->entry ); + release_object( mutex ); +} + +void abandon_d3dkmt_mutexes( struct thread *thread ) +{ + struct d3dkmt_mutex *mutex, *next; + + LIST_FOR_EACH_ENTRY_SAFE( mutex, next, &thread->d3dkmt_mutexes, struct d3dkmt_mutex, entry ) + mutex_release( mutex, true ); +} + /* return a pointer to a d3dkmt object from its global handle */ static void *d3dkmt_object_open( d3dkmt_handle_t global, enum d3dkmt_type type ) { @@ -646,3 +746,54 @@ DECL_HANDLER(d3dkmt_object_open_name) break; } } + +/* Acquire a global d3dkmt keyed mutex */ +DECL_HANDLER(d3dkmt_mutex_acquire) +{ + struct d3dkmt_mutex *mutex; + struct object *sync; + + if (!(mutex = d3dkmt_object_open( req->mutex, D3DKMT_MUTEX ))) goto done; + + if (req->wait_status) set_error( req->wait_status ); + else if (mutex->abandoned) set_error( STATUS_ABANDONED ); + else if (mutex->key_value == req->key_value && !mutex->owner) + { + reply->fence_value = mutex->fence_value; + mutex_grab( mutex ); + } + else if ((reply->wait_handle = req->wait_handle)) set_error( STATUS_PENDING ); + else if ((sync = keyed_wait_grab( mutex, req->key_value ))) + { + if ((reply->wait_handle = alloc_handle( current->process, sync, SYNCHRONIZE, 0 ))) set_error( STATUS_PENDING ); + release_object( sync ); + } + + release_object( mutex ); + +done: + if (get_error() != STATUS_PENDING && req->wait_handle) + { + close_handle( current->process, req->wait_handle ); + if (mutex) keyed_wait_release( mutex, req->key_value ); + } +} + +/* Release a global d3dkmt keyed mutex */ +DECL_HANDLER(d3dkmt_mutex_release) +{ + struct d3dkmt_mutex *mutex; + + if (!(mutex = d3dkmt_object_open( req->mutex, D3DKMT_MUTEX ))) return; + + if (mutex->abandoned) set_error( STATUS_ABANDONED ); + else if (mutex->owner != current) set_error( STATUS_INVALID_PARAMETER ); + else + { + mutex->key_value = req->key_value; + mutex->fence_value = req->fence_value; + mutex_release( mutex, req->abandon ); + } + + release_object( mutex ); +} diff --git a/server/object.h b/server/object.h index 2a0ecfc2cf2..cab8ad25a59 100644 --- a/server/object.h +++ b/server/object.h @@ -240,6 +240,7 @@ extern void reset_event( struct event *event ); /* mutex functions */
extern void abandon_mutexes( struct thread *thread ); +extern void abandon_d3dkmt_mutexes( struct thread *thread );
/* in-process synchronization functions */
diff --git a/server/protocol.def b/server/protocol.def index ec6738169ed..93e574b61a3 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4240,3 +4240,28 @@ enum inproc_sync_type @REPLY obj_handle_t handle; /* shared object handle */ @END + + +/* Acquire a global d3dkmt keyed mutex */ +@REQ(d3dkmt_mutex_acquire) + d3dkmt_handle_t mutex; /* mutex global handle */ + unsigned int key_value; /* the key to acquire/wait for */ + obj_handle_t wait_handle; /* previously returned wait handle */ + unsigned int wait_status; /* status returned from previous wait */ +@REPLY + unsigned __int64 fence_value; /* semaphore fence value */ + data_size_t runtime_size; /* size of client runtime data */ + obj_handle_t wait_handle; /* wait handle for pending acquire */ + VARARG(runtime,bytes); /* client runtime data */ +@END + + +/* Release a global d3dkmt keyed mutex */ +@REQ(d3dkmt_mutex_release) + d3dkmt_handle_t mutex; /* mutex global handle */ + int abandon; /* mutex is being abandonned */ + unsigned int key_value; /* the key to release */ + unsigned __int64 fence_value; /* semaphore fence value */ + data_size_t runtime_size; /* size of client runtime data */ + VARARG(runtime,bytes); /* client runtime data */ +@END diff --git a/server/thread.c b/server/thread.c index 6a1b1f8c559..3aed496450a 100644 --- a/server/thread.c +++ b/server/thread.c @@ -434,6 +434,7 @@ static inline void init_thread_structure( struct thread *thread ) thread->completion_wait = NULL;
list_init( &thread->mutex_list ); + list_init( &thread->d3dkmt_mutexes ); list_init( &thread->system_apc ); list_init( &thread->user_apc ); list_init( &thread->kernel_object ); diff --git a/server/thread.h b/server/thread.h index fb77901ba7c..77ea355483d 100644 --- a/server/thread.h +++ b/server/thread.h @@ -58,6 +58,7 @@ struct thread struct process *process; thread_id_t id; /* thread id */ struct list mutex_list; /* list of currently owned mutexes */ + struct list d3dkmt_mutexes;/* list of currently owned d3dkmt mutexes */ unsigned int system_regs; /* which system regs have been set */ struct msg_queue *queue; /* message queue */ struct thread_wait *wait; /* current wait condition if sleeping */