-- v5: server: Allocate global D3DKMT handles for objects. server: Create server-side global D3DKMT objects. win32u: Use array indexes for d3dkmt handles. win32u/tests: Test that global d3dkmt handles aren't leaked.
From: Rémi Bernon rbernon@codeweavers.com
When a process exits with an open handle, it loses its references on the object. --- dlls/win32u/tests/d3dkmt.c | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+)
diff --git a/dlls/win32u/tests/d3dkmt.c b/dlls/win32u/tests/d3dkmt.c index 167071f5b0c..9de2dc3d76a 100644 --- a/dlls/win32u/tests/d3dkmt.c +++ b/dlls/win32u/tests/d3dkmt.c @@ -67,6 +67,33 @@ static const char *debugstr_ok( const char *cond ) #define ok_ptr( r, op, e ) ok_ex( r, op, e, const void *, "%p" ) #define ok_nt( e, r ) ok_ex( r, ==, e, NTSTATUS, "%#lx" )
+#define run_in_process( a ) run_in_process_( __FILE__, __LINE__, a ) +static void run_in_process_( const char *file, int line, const char *args ) +{ + char cmdline[MAX_PATH * 2], test[MAX_PATH], *tmp, **argv; + STARTUPINFOA startup = {.cb = sizeof(STARTUPINFOA)}; + PROCESS_INFORMATION info = {0}; + const char *name; + DWORD ret; + int argc; + + name = file; + if ((tmp = strrchr( name, '\' ))) name = tmp; + if ((tmp = strrchr( name, '/' ))) name = tmp; + if ((tmp = strrchr( test, '.' ))) *tmp = 0; + strcpy( test, name ); + + argc = winetest_get_mainargs( &argv ); + sprintf( cmdline, "%s %s %s", argv[0], argc > 1 ? argv[1] : test, args ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); + ok_(file, line)( ret, "CreateProcessA failed, error %lu\n", GetLastError() ); + if (!ret) return; + + wait_child_process( info.hProcess ); + CloseHandle( info.hThread ); + CloseHandle( info.hProcess ); +} + #define check_d3dkmt_global( a ) check_d3dkmt_global_( __LINE__, a ) static void check_d3dkmt_global_( int line, UINT_PTR handle ) { @@ -1193,6 +1220,21 @@ static void test_D3DKMTQueryAdapterInfo(void) ok( status == STATUS_SUCCESS, "Got unexpected return code %#lx.\n", status ); }
+static void test_D3DKMTCreateSynchronizationObject_process( const char *arg ) +{ + D3DKMT_OPENSYNCHRONIZATIONOBJECT open = {0}; + D3DKMT_HANDLE global = atoi( arg ); + D3DKMT_HANDLE next_local = 0; + NTSTATUS status; + + open.hSharedHandle = global; + open.hSyncObject = 0x1eadbeed; + status = D3DKMTOpenSynchronizationObject( &open ); + todo_wine ok_nt( STATUS_SUCCESS, status ); + todo_wine check_d3dkmt_local( open.hSyncObject, &next_local ); + /* leak the object */ +} + static void test_D3DKMTCreateSynchronizationObject( void ) { D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter = {0}; @@ -1204,6 +1246,7 @@ static void test_D3DKMTCreateSynchronizationObject( void ) D3DKMT_CREATEDEVICE create_device = {0}; D3DKMT_CLOSEADAPTER close_adapter = {0}; D3DKMT_HANDLE next_local = 0; + char buffer[256]; NTSTATUS status;
wcscpy( open_adapter.DeviceName, L"\\.\DISPLAY1" ); @@ -1426,6 +1469,25 @@ static void test_D3DKMTCreateSynchronizationObject( void ) todo_wine ok_nt( STATUS_SUCCESS, status );
+ create2.Info.Flags.NtSecuritySharing = 0; + create2.hSyncObject = create2.Info.SharedHandle = 0x1eadbeed; + status = D3DKMTCreateSynchronizationObject2( &create2 ); + todo_wine ok_nt( STATUS_SUCCESS, status ); + todo_wine check_d3dkmt_local( create2.hSyncObject, &next_local ); + todo_wine check_d3dkmt_global( create2.Info.SharedHandle ); + + sprintf( buffer, "test_D3DKMTCreateSynchronizationObject %u", create2.Info.SharedHandle ); + run_in_process( buffer ); + + destroy.hSyncObject = create2.hSyncObject; + status = D3DKMTDestroySynchronizationObject( &destroy ); + todo_wine ok_nt( STATUS_SUCCESS, status ); + + open.hSharedHandle = create2.Info.SharedHandle; + status = D3DKMTOpenSynchronizationObject( &open ); + todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + + destroy_device.hDevice = create_device.hDevice; status = D3DKMTDestroyDevice( &destroy_device ); ok_nt( STATUS_SUCCESS, status ); @@ -2606,9 +2668,16 @@ static void test_D3DKMTShareObjects( void )
START_TEST( d3dkmt ) { + char **argv; + int argc; + /* native win32u.dll fails if user32 is not loaded, so make sure it's fully initialized */ GetDesktopWindow();
+ argc = winetest_get_mainargs( &argv ); + if (argc > 3 && !strcmp( argv[2], "test_D3DKMTCreateSynchronizationObject" )) + return test_D3DKMTCreateSynchronizationObject_process( argv[3] ); + test_D3DKMTOpenAdapterFromGdiDisplayName(); test_D3DKMTOpenAdapterFromHdc(); test_D3DKMTEnumAdapters2();
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/d3dkmt.c | 89 +++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 39 deletions(-)
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c index 13f05f2d069..c91ed0309ef 100644 --- a/dlls/win32u/d3dkmt.c +++ b/dlls/win32u/d3dkmt.c @@ -69,80 +69,91 @@ struct d3dkmt_vidpn_source static pthread_mutex_t d3dkmt_lock = PTHREAD_MUTEX_INITIALIZER; static struct list d3dkmt_vidpn_sources = LIST_INIT( d3dkmt_vidpn_sources ); /* VidPN source information list */
-static struct d3dkmt_object **objects; -static unsigned int object_count, object_capacity, last_index; +static struct d3dkmt_object **objects, **objects_end, **objects_next;
-/* return the position of the first object which handle is not less than - * the given local handle, d3dkmt_lock must be held. */ -static unsigned int object_handle_lower_bound( D3DKMT_HANDLE local ) +#define D3DKMT_HANDLE_BIT 0x40000000 + +static D3DKMT_HANDLE index_to_handle( int index ) { - unsigned int begin = 0, end = object_count, mid; + return (index << 6) | D3DKMT_HANDLE_BIT; +}
- while (begin < end) - { - mid = begin + (end - begin) / 2; - if (objects[mid]->local < local) begin = mid + 1; - else end = mid; - } +static int handle_to_index( D3DKMT_HANDLE handle ) +{ + return (handle & ~0xc0000000) >> 6; +} + +static NTSTATUS init_handle_table(void) +{ + if (!(objects = calloc( 1024, sizeof(*objects) ))) return STATUS_NO_MEMORY; + objects_end = objects + 1024; + objects_next = objects; + return STATUS_SUCCESS; +} + +static struct d3dkmt_object **grow_handle_table(void) +{ + size_t old_capacity = objects_end - objects, max_capacity = handle_to_index( D3DKMT_HANDLE_BIT - 1 ); + unsigned int new_capacity = old_capacity * 3 / 2; + struct d3dkmt_object **tmp; + + if (new_capacity > max_capacity) new_capacity = max_capacity; + if (new_capacity <= old_capacity) return NULL; /* exhausted handle capacity */ + + if (!(tmp = realloc( objects, new_capacity * sizeof(*objects) ))) return NULL; + memset( tmp + old_capacity, 0, (new_capacity - old_capacity) * sizeof(*tmp) ); + + objects = tmp; + objects_end = tmp + new_capacity; + objects_next = tmp + old_capacity;
- return begin; + return objects_next; }
/* allocate a d3dkmt object with a local handle */ static NTSTATUS alloc_object_handle( struct d3dkmt_object *object ) { - D3DKMT_HANDLE handle = 0; - unsigned int index; + struct d3dkmt_object **entry;
pthread_mutex_lock( &d3dkmt_lock ); + if (!objects && init_handle_table()) goto done;
- if (object_count >= object_capacity) + for (entry = objects_next; entry < objects_end; entry++) if (!*entry) break; + if (entry == objects_end) { - unsigned int capacity = max( 32, object_capacity * 3 / 2 ); - struct d3dkmt_object **tmp; - assert( capacity > object_capacity ); - - if (capacity >= 0xffff) goto done; - if (!(tmp = realloc( objects, capacity * sizeof(*objects) ))) goto done; - object_capacity = capacity; - objects = tmp; + for (entry = objects; entry < objects_next; entry++) if (!*entry) break; + if (entry == objects_next && !(entry = grow_handle_table())) goto done; }
- last_index += 0x40; - handle = object->local = (last_index & ~0xc0000000) | 0x40000000; - index = object_handle_lower_bound( object->local ); - if (index < object_count) memmove( objects + index + 1, objects, (object_count - index) * sizeof(*objects) ); - objects[index] = object; - object_count++; + object->local = index_to_handle( entry - objects ); + objects_next = entry + 1; + *entry = object;
done: pthread_mutex_unlock( &d3dkmt_lock ); - return handle ? STATUS_SUCCESS : STATUS_NO_MEMORY; + return object->local ? STATUS_SUCCESS : STATUS_NO_MEMORY; }
/* free a d3dkmt local object handle */ static void free_object_handle( struct d3dkmt_object *object ) { - unsigned int index; + unsigned int index = handle_to_index( object->local );
pthread_mutex_lock( &d3dkmt_lock ); - index = object_handle_lower_bound( object->local ); - assert( index < object_count && objects[index] == object ); - object_count--; + assert( objects + index < objects_end && objects[index] == object ); + objects[index] = NULL; object->local = 0; - memmove( objects + index, objects + index + 1, (object_count - index) * sizeof(*objects) ); pthread_mutex_unlock( &d3dkmt_lock ); }
/* return a pointer to a d3dkmt object from its local handle */ static void *get_d3dkmt_object( D3DKMT_HANDLE local, enum d3dkmt_type type ) { + unsigned int index = handle_to_index( local ); struct d3dkmt_object *object; - unsigned int index;
pthread_mutex_lock( &d3dkmt_lock ); - index = object_handle_lower_bound( local ); - if (index >= object_count) object = NULL; + if (objects + index >= objects_end) object = NULL; else object = objects[index]; pthread_mutex_unlock( &d3dkmt_lock );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/d3dkmt.c | 76 ++++++++++++++++++++++----- dlls/win32u/tests/d3dkmt.c | 63 ++++++++++++---------- dlls/wow64win/gdi.c | 3 ++ server/Makefile.in | 1 + server/d3dkmt.c | 105 +++++++++++++++++++++++++++++++++++++ server/protocol.def | 17 ++++++ 6 files changed, 222 insertions(+), 43 deletions(-) create mode 100644 server/d3dkmt.c
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c index c91ed0309ef..e88bbae21b3 100644 --- a/dlls/win32u/d3dkmt.c +++ b/dlls/win32u/d3dkmt.c @@ -31,19 +31,13 @@ #include "win32u_private.h" #include "ntuser_private.h"
-WINE_DEFAULT_DEBUG_CHANNEL(vulkan); - -enum d3dkmt_type -{ - D3DKMT_ADAPTER = 1, - D3DKMT_DEVICE = 2, - D3DKMT_SOURCE = 3, -}; +WINE_DEFAULT_DEBUG_CHANNEL(d3dkmt);
struct d3dkmt_object { enum d3dkmt_type type; /* object type */ D3DKMT_HANDLE local; /* object local handle */ + HANDLE handle; /* internal handle of the server object */ };
struct d3dkmt_adapter @@ -172,9 +166,31 @@ static NTSTATUS d3dkmt_object_alloc( UINT size, enum d3dkmt_type type, void **ob return STATUS_SUCCESS; }
+/* create a global D3DKMT object, either with a global handle or later shareable */ +static NTSTATUS d3dkmt_object_create( struct d3dkmt_object *object, BOOL shared ) +{ + NTSTATUS status; + + SERVER_START_REQ( d3dkmt_object_create ) + { + req->type = object->type; + status = wine_server_call( req ); + object->handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + + if (!status) status = alloc_object_handle( object ); + + if (status) WARN( "Failed to create global object for %p, status %#x\n", object, status ); + else TRACE( "Created global object for %p/%#x\n", object, object->local ); + return status; +} + static void d3dkmt_object_free( struct d3dkmt_object *object ) { + TRACE( "object %p/%#x\n", object, object->local ); if (object->local) free_object_handle( object ); + if (object->handle) NtClose( object->handle ); free( object ); }
@@ -841,8 +857,23 @@ NTSTATUS WINAPI NtGdiDdDDIQueryResourceInfoFromNtHandle( D3DKMT_QUERYRESOURCEINF */ NTSTATUS WINAPI NtGdiDdDDICreateKeyedMutex2( D3DKMT_CREATEKEYEDMUTEX2 *params ) { - FIXME( "params %p stub!\n", params ); - return STATUS_NOT_IMPLEMENTED; + struct d3dkmt_object *mutex; + NTSTATUS status; + + FIXME( "params %p semi-stub!\n", params ); + + if (!params) return STATUS_INVALID_PARAMETER; + + if ((status = d3dkmt_object_alloc( sizeof(*mutex), D3DKMT_MUTEX, (void **)&mutex ))) return status; + if ((status = d3dkmt_object_create( mutex, params->Flags.NtSecuritySharing ))) goto failed; + + params->hSharedHandle = 0; + params->hKeyedMutex = mutex->local; + return STATUS_SUCCESS; + +failed: + d3dkmt_object_free( mutex ); + return status; }
/****************************************************************************** @@ -850,8 +881,18 @@ NTSTATUS WINAPI NtGdiDdDDICreateKeyedMutex2( D3DKMT_CREATEKEYEDMUTEX2 *params ) */ NTSTATUS WINAPI NtGdiDdDDICreateKeyedMutex( D3DKMT_CREATEKEYEDMUTEX *params ) { - FIXME( "params %p stub!\n", params ); - return STATUS_NOT_IMPLEMENTED; + D3DKMT_CREATEKEYEDMUTEX2 params2 = {0}; + NTSTATUS status; + + TRACE( "params %p\n", params ); + + if (!params) return STATUS_INVALID_PARAMETER; + + params2.InitialValue = params->InitialValue; + status = NtGdiDdDDICreateKeyedMutex2( ¶ms2 ); + params->hSharedHandle = params2.hSharedHandle; + params->hKeyedMutex = params2.hKeyedMutex; + return status; }
/****************************************************************************** @@ -859,8 +900,15 @@ NTSTATUS WINAPI NtGdiDdDDICreateKeyedMutex( D3DKMT_CREATEKEYEDMUTEX *params ) */ NTSTATUS WINAPI NtGdiDdDDIDestroyKeyedMutex( const D3DKMT_DESTROYKEYEDMUTEX *params ) { - FIXME( "params %p stub!\n", params ); - return STATUS_NOT_IMPLEMENTED; + struct d3dkmt_object *mutex; + + TRACE( "params %p\n", params ); + + if (!(mutex = get_d3dkmt_object( params->hKeyedMutex, D3DKMT_MUTEX ))) + return STATUS_INVALID_PARAMETER; + d3dkmt_object_free( mutex ); + + return STATUS_SUCCESS; }
/****************************************************************************** diff --git a/dlls/win32u/tests/d3dkmt.c b/dlls/win32u/tests/d3dkmt.c index 9de2dc3d76a..b90045affe3 100644 --- a/dlls/win32u/tests/d3dkmt.c +++ b/dlls/win32u/tests/d3dkmt.c @@ -1508,11 +1508,11 @@ static void test_D3DKMTCreateKeyedMutex( void ) NTSTATUS status;
status = D3DKMTCreateKeyedMutex( NULL ); - todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + ok_nt( STATUS_INVALID_PARAMETER, status ); create.hKeyedMutex = create.hSharedHandle = 0x1eadbeed; status = D3DKMTCreateKeyedMutex( &create ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine check_d3dkmt_local( create.hKeyedMutex, &next_local ); + ok_nt( STATUS_SUCCESS, status ); + check_d3dkmt_local( create.hKeyedMutex, &next_local ); todo_wine check_d3dkmt_global( create.hSharedHandle );
status = D3DKMTOpenKeyedMutex( &open ); @@ -1525,9 +1525,10 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTOpenKeyedMutex( &open ); todo_wine ok_nt( STATUS_SUCCESS, status ); todo_wine check_d3dkmt_local( open.hKeyedMutex, &next_local ); + next_local = 0;
status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + ok_nt( STATUS_INVALID_PARAMETER, status );
if (0) { @@ -1542,11 +1543,11 @@ static void test_D3DKMTCreateKeyedMutex( void ) todo_wine ok_nt( STATUS_SUCCESS, status ); /* destroying multiple times fails */ status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + ok_nt( STATUS_INVALID_PARAMETER, status );
destroy.hKeyedMutex = create.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status );
/* the global D3DKMT_HANDLE is destroyed with last reference */ status = D3DKMTOpenKeyedMutex( &open ); @@ -1554,18 +1555,18 @@ static void test_D3DKMTCreateKeyedMutex( void )
status = D3DKMTCreateKeyedMutex2( NULL ); - todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); + ok_nt( STATUS_INVALID_PARAMETER, status ); create2.hKeyedMutex = create2.hSharedHandle = 0x1eadbeed; status = D3DKMTCreateKeyedMutex2( &create2 ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine check_d3dkmt_local( create2.hKeyedMutex, &next_local ); + ok_nt( STATUS_SUCCESS, status ); + check_d3dkmt_local( create2.hKeyedMutex, &next_local ); todo_wine check_d3dkmt_global( create2.hSharedHandle ); destroy.hKeyedMutex = create2.hKeyedMutex;
create2.hKeyedMutex = create2.hSharedHandle = 0x1eadbeed; status = D3DKMTCreateKeyedMutex2( &create2 ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine check_d3dkmt_local( create2.hKeyedMutex, &next_local ); + ok_nt( STATUS_SUCCESS, status ); + check_d3dkmt_local( create2.hKeyedMutex, &next_local ); todo_wine check_d3dkmt_global( create2.hSharedHandle );
status = D3DKMTOpenKeyedMutex2( &open2 ); @@ -1578,12 +1579,13 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTOpenKeyedMutex2( &open2 ); todo_wine ok_nt( STATUS_SUCCESS, status ); todo_wine check_d3dkmt_local( open2.hKeyedMutex, &next_local ); + next_local = 0;
status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); destroy.hKeyedMutex = create2.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); destroy.hKeyedMutex = open2.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); todo_wine ok_nt( STATUS_SUCCESS, status ); @@ -1592,8 +1594,8 @@ static void test_D3DKMTCreateKeyedMutex( void ) /* PrivateRuntimeDataSize must be 0 if no buffer is provided */
status = D3DKMTCreateKeyedMutex2( &create2 ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine check_d3dkmt_local( create2.hKeyedMutex, &next_local ); + ok_nt( STATUS_SUCCESS, status ); + check_d3dkmt_local( create2.hKeyedMutex, &next_local ); todo_wine check_d3dkmt_global( create2.hSharedHandle );
open2.hKeyedMutex = 0x1eadbeed; @@ -1605,6 +1607,7 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTOpenKeyedMutex2( &open2 ); todo_wine ok_nt( STATUS_SUCCESS, status ); todo_wine check_d3dkmt_local( open2.hKeyedMutex, &next_local ); + next_local = 0; ok_x4( open2.PrivateRuntimeDataSize, ==, sizeof(buffer) );
destroy.hKeyedMutex = open2.hKeyedMutex; @@ -1612,14 +1615,14 @@ static void test_D3DKMTCreateKeyedMutex( void ) todo_wine ok_nt( STATUS_SUCCESS, status ); destroy.hKeyedMutex = create2.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status );
create2.PrivateRuntimeDataSize = sizeof(runtime_data); create2.pPrivateRuntimeData = runtime_data; status = D3DKMTCreateKeyedMutex2( &create2 ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine check_d3dkmt_local( create2.hKeyedMutex, &next_local ); + ok_nt( STATUS_SUCCESS, status ); + check_d3dkmt_local( create2.hKeyedMutex, &next_local ); todo_wine check_d3dkmt_global( create2.hSharedHandle );
open2.hKeyedMutex = 0x1eadbeed; @@ -1629,6 +1632,7 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTOpenKeyedMutex2( &open2 ); todo_wine ok_nt( STATUS_SUCCESS, status ); todo_wine check_d3dkmt_local( open2.hKeyedMutex, &next_local ); + next_local = 0; ok_x4( open2.PrivateRuntimeDataSize, ==, 0 );
open2.PrivateRuntimeDataSize = sizeof(buffer); @@ -1643,6 +1647,7 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTOpenKeyedMutex2( &open2 ); todo_wine ok_nt( STATUS_SUCCESS, status ); todo_wine check_d3dkmt_local( open2.hKeyedMutex, &next_local ); + next_local = 0; ok_x4( open2.PrivateRuntimeDataSize, ==, sizeof(runtime_data) ); ok_u1( buffer[0], ==, 0xcd );
@@ -1651,20 +1656,20 @@ static void test_D3DKMTCreateKeyedMutex( void ) todo_wine ok_nt( STATUS_SUCCESS, status ); destroy.hKeyedMutex = create2.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status );
/* doesn't return a global D3DKMT_HANDLE with NtSecuritySharing = 1 */ create2.Flags.NtSecuritySharing = 1; create2.hKeyedMutex = create2.hSharedHandle = 0x1eadbeed; status = D3DKMTCreateKeyedMutex2( &create2 ); - todo_wine ok_nt( STATUS_SUCCESS, status ); - todo_wine check_d3dkmt_local( create2.hKeyedMutex, &next_local ); - todo_wine ok_x4( create2.hSharedHandle, ==, 0 ); + ok_nt( STATUS_SUCCESS, status ); + check_d3dkmt_local( create2.hKeyedMutex, &next_local ); + ok_x4( create2.hSharedHandle, ==, 0 );
destroy.hKeyedMutex = create2.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); }
static void test_D3DKMTCreateAllocation( void ) @@ -2313,23 +2318,23 @@ static void test_D3DKMTShareObjects( void )
/* D3DKMTShareObjects doesn't work with keyed mutex objects alone */ status = D3DKMTCreateKeyedMutex( &create_mutex ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); status = D3DKMTShareObjects( 1, &create_mutex.hKeyedMutex, &attr, STANDARD_RIGHTS_WRITE, &handle ); todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); status = D3DKMTShareObjects( 1, &create_mutex.hSharedHandle, &attr, STANDARD_RIGHTS_WRITE, &handle ); todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); destroy_mutex.hKeyedMutex = create_mutex.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy_mutex ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status );
create_mutex2.Flags.NtSecuritySharing = 1; status = D3DKMTCreateKeyedMutex2( &create_mutex2 ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); status = D3DKMTShareObjects( 1, &create_mutex2.hKeyedMutex, &attr, STANDARD_RIGHTS_WRITE, &handle ); todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); destroy_mutex.hKeyedMutex = create_mutex2.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy_mutex ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status );
/* NtSecuritySharing = 1 is required for D3DKMTShareObjects */ @@ -2524,7 +2529,7 @@ static void test_D3DKMTShareObjects( void ) create_mutex2.PrivateRuntimeDataSize = sizeof(expect_mutex_data); create_mutex2.pPrivateRuntimeData = expect_mutex_data; status = D3DKMTCreateKeyedMutex2( &create_mutex2 ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status );
objects[0] = create_alloc.hResource; objects[1] = create_alloc.hResource; @@ -2572,7 +2577,7 @@ static void test_D3DKMTShareObjects( void )
destroy_mutex.hKeyedMutex = create_mutex2.hKeyedMutex; status = D3DKMTDestroyKeyedMutex( &destroy_mutex ); - todo_wine ok_nt( STATUS_SUCCESS, status ); + ok_nt( STATUS_SUCCESS, status ); create_mutex2.hKeyedMutex = 0;
destroy_sync.hSyncObject = create_sync2.hSyncObject; diff --git a/dlls/wow64win/gdi.c b/dlls/wow64win/gdi.c index 30c4a5b6d62..f8e3056c932 100644 --- a/dlls/wow64win/gdi.c +++ b/dlls/wow64win/gdi.c @@ -726,6 +726,8 @@ NTSTATUS WINAPI wow64_NtGdiDdDDICreateKeyedMutex2( UINT *args ) D3DKMT_CREATEKEYEDMUTEX2 desc; NTSTATUS status;
+ if (!desc32) return STATUS_INVALID_PARAMETER; + desc.InitialValue = desc32->InitialValue; desc.hSharedHandle = desc32->hSharedHandle; desc.hKeyedMutex = desc32->hKeyedMutex; @@ -734,6 +736,7 @@ NTSTATUS WINAPI wow64_NtGdiDdDDICreateKeyedMutex2( UINT *args ) desc.Flags = desc32->Flags; status = NtGdiDdDDICreateKeyedMutex2( &desc ); desc32->hKeyedMutex = desc.hKeyedMutex; + desc32->hSharedHandle = desc.hSharedHandle; return status; }
diff --git a/server/Makefile.in b/server/Makefile.in index 57250fd0332..d3d50b9aa5a 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -8,6 +8,7 @@ SOURCES = \ clipboard.c \ completion.c \ console.c \ + d3dkmt.c \ debugger.c \ device.c \ directory.c \ diff --git a/server/d3dkmt.c b/server/d3dkmt.c new file mode 100644 index 00000000000..64301548784 --- /dev/null +++ b/server/d3dkmt.c @@ -0,0 +1,105 @@ +/* + * Server-side D3DKMT resource management + * + * Copyright 2025 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +#include <unistd.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "ddk/wdm.h" + +#include "file.h" +#include "handle.h" +#include "request.h" +#include "security.h" + +struct d3dkmt_object +{ + struct object obj; /* object header */ + enum d3dkmt_type type; /* object type */ +}; + +static void d3dkmt_object_dump( struct object *obj, int verbose ); +static void d3dkmt_object_destroy( struct object *obj ); + +static const struct object_ops d3dkmt_object_ops = +{ + sizeof(struct d3dkmt_object), /* size */ + &no_type, /* type */ + d3dkmt_object_dump, /* dump */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + default_get_sync, /* get_sync */ + default_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + no_get_full_name, /* get_full_name */ + no_lookup_name, /* lookup_name */ + no_link_name, /* link_name */ + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + d3dkmt_object_destroy, /* destroy */ +}; + +static void d3dkmt_object_dump( struct object *obj, int verbose ) +{ + struct d3dkmt_object *object = (struct d3dkmt_object *)obj; + assert( obj->ops == &d3dkmt_object_ops ); + + fprintf( stderr, "type=%#x\n", object->type ); +} + +static void d3dkmt_object_destroy( struct object *obj ) +{ + assert( obj->ops == &d3dkmt_object_ops ); +} + +static struct d3dkmt_object *d3dkmt_object_create( enum d3dkmt_type type ) +{ + struct d3dkmt_object *object; + + if (!(object = alloc_object( &d3dkmt_object_ops ))) return NULL; + object->type = type; + + return object; +} + +/* create a global d3dkmt object */ +DECL_HANDLER(d3dkmt_object_create) +{ + struct d3dkmt_object *object; + + if (!(object = d3dkmt_object_create( req->type ))) return; + reply->handle = alloc_handle( current->process, object, STANDARD_RIGHTS_ALL, OBJ_INHERIT ); + release_object( object ); +} diff --git a/server/protocol.def b/server/protocol.def index 59c6436fa88..9fbd90a0877 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -968,6 +968,14 @@ union udp_endpoint } ipv6; };
+enum d3dkmt_type +{ + D3DKMT_ADAPTER = 1, + D3DKMT_DEVICE = 2, + D3DKMT_SOURCE = 3, + D3DKMT_MUTEX = 4, +}; + /****************************************************************/ /* shared session mapping structures */
@@ -4137,3 +4145,12 @@ enum inproc_sync_type int type; /* inproc sync type */ unsigned int access; /* handle access rights */ @END + + +/* Create a global d3dkmt object */ +@REQ(d3dkmt_object_create) + unsigned int type; /* d3dkmt object type */ +@REPLY + obj_handle_t handle; /* internal handle of the server object */ +@END +
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/d3dkmt.c | 10 ++-- dlls/win32u/tests/d3dkmt.c | 10 ++-- server/d3dkmt.c | 97 ++++++++++++++++++++++++++++++++++++-- server/protocol.def | 2 + tools/make_requests | 1 + 5 files changed, 107 insertions(+), 13 deletions(-)
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c index e88bbae21b3..e1e12564688 100644 --- a/dlls/win32u/d3dkmt.c +++ b/dlls/win32u/d3dkmt.c @@ -37,6 +37,8 @@ struct d3dkmt_object { enum d3dkmt_type type; /* object type */ D3DKMT_HANDLE local; /* object local handle */ + D3DKMT_HANDLE global; /* object global handle */ + BOOL shared; /* object is shared using nt handles */ HANDLE handle; /* internal handle of the server object */ };
@@ -176,19 +178,21 @@ static NTSTATUS d3dkmt_object_create( struct d3dkmt_object *object, BOOL shared req->type = object->type; status = wine_server_call( req ); object->handle = wine_server_ptr_handle( reply->handle ); + object->global = reply->global; + object->shared = shared; } SERVER_END_REQ;
if (!status) status = alloc_object_handle( object );
if (status) WARN( "Failed to create global object for %p, status %#x\n", object, status ); - else TRACE( "Created global object for %p/%#x\n", object, object->local ); + else TRACE( "Created global object %#x for %p/%#x\n", object->global, object, object->local ); return status; }
static void d3dkmt_object_free( struct d3dkmt_object *object ) { - TRACE( "object %p/%#x\n", object, object->local ); + TRACE( "object %p/%#x, global %#x\n", obj, obj->local, obj->global ); if (object->local) free_object_handle( object ); if (object->handle) NtClose( object->handle ); free( object ); @@ -867,7 +871,7 @@ NTSTATUS WINAPI NtGdiDdDDICreateKeyedMutex2( D3DKMT_CREATEKEYEDMUTEX2 *params ) if ((status = d3dkmt_object_alloc( sizeof(*mutex), D3DKMT_MUTEX, (void **)&mutex ))) return status; if ((status = d3dkmt_object_create( mutex, params->Flags.NtSecuritySharing ))) goto failed;
- params->hSharedHandle = 0; + params->hSharedHandle = mutex->shared ? 0 : mutex->global; params->hKeyedMutex = mutex->local; return STATUS_SUCCESS;
diff --git a/dlls/win32u/tests/d3dkmt.c b/dlls/win32u/tests/d3dkmt.c index b90045affe3..60168c3f316 100644 --- a/dlls/win32u/tests/d3dkmt.c +++ b/dlls/win32u/tests/d3dkmt.c @@ -1513,7 +1513,7 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTCreateKeyedMutex( &create ); ok_nt( STATUS_SUCCESS, status ); check_d3dkmt_local( create.hKeyedMutex, &next_local ); - todo_wine check_d3dkmt_global( create.hSharedHandle ); + check_d3dkmt_global( create.hSharedHandle );
status = D3DKMTOpenKeyedMutex( &open ); todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); @@ -1560,14 +1560,14 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTCreateKeyedMutex2( &create2 ); ok_nt( STATUS_SUCCESS, status ); check_d3dkmt_local( create2.hKeyedMutex, &next_local ); - todo_wine check_d3dkmt_global( create2.hSharedHandle ); + check_d3dkmt_global( create2.hSharedHandle ); destroy.hKeyedMutex = create2.hKeyedMutex;
create2.hKeyedMutex = create2.hSharedHandle = 0x1eadbeed; status = D3DKMTCreateKeyedMutex2( &create2 ); ok_nt( STATUS_SUCCESS, status ); check_d3dkmt_local( create2.hKeyedMutex, &next_local ); - todo_wine check_d3dkmt_global( create2.hSharedHandle ); + check_d3dkmt_global( create2.hSharedHandle );
status = D3DKMTOpenKeyedMutex2( &open2 ); todo_wine ok_nt( STATUS_INVALID_PARAMETER, status ); @@ -1596,7 +1596,7 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTCreateKeyedMutex2( &create2 ); ok_nt( STATUS_SUCCESS, status ); check_d3dkmt_local( create2.hKeyedMutex, &next_local ); - todo_wine check_d3dkmt_global( create2.hSharedHandle ); + check_d3dkmt_global( create2.hSharedHandle );
open2.hKeyedMutex = 0x1eadbeed; open2.hSharedHandle = create2.hSharedHandle; @@ -1623,7 +1623,7 @@ static void test_D3DKMTCreateKeyedMutex( void ) status = D3DKMTCreateKeyedMutex2( &create2 ); ok_nt( STATUS_SUCCESS, status ); check_d3dkmt_local( create2.hKeyedMutex, &next_local ); - todo_wine check_d3dkmt_global( create2.hSharedHandle ); + check_d3dkmt_global( create2.hSharedHandle );
open2.hKeyedMutex = 0x1eadbeed; open2.hSharedHandle = create2.hSharedHandle; diff --git a/server/d3dkmt.c b/server/d3dkmt.c index 64301548784..b99fabd3292 100644 --- a/server/d3dkmt.c +++ b/server/d3dkmt.c @@ -21,11 +21,9 @@ #include "config.h"
#include <assert.h> -#include <stdarg.h> +#include <stdbool.h> #include <stdio.h>
-#include <unistd.h> - #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" @@ -41,6 +39,7 @@ struct d3dkmt_object { struct object obj; /* object header */ enum d3dkmt_type type; /* object type */ + d3dkmt_handle_t global; /* object global handle */ };
static void d3dkmt_object_dump( struct object *obj, int verbose ); @@ -71,17 +70,97 @@ static const struct object_ops d3dkmt_object_ops = d3dkmt_object_destroy, /* destroy */ };
+static struct d3dkmt_object **objects, **objects_end, **objects_next; + +#define D3DKMT_HANDLE_BIT 0x40000000 + +static d3dkmt_handle_t index_to_handle( int index ) +{ + return (index << 6) | D3DKMT_HANDLE_BIT | 2; +} + +static int handle_to_index( d3dkmt_handle_t handle ) +{ + return (handle & ~0xc000003f) >> 6; +} + +static bool init_handle_table(void) +{ + static const size_t initial_capacity = 1024; + + if (!(objects = mem_alloc( initial_capacity * sizeof(*objects) ))) return false; + memset( objects, 0, initial_capacity * sizeof(*objects) ); + objects_end = objects + initial_capacity; + objects_next = objects; + + return true; +} + +static struct d3dkmt_object **grow_handle_table(void) +{ + size_t old_capacity = objects_end - objects, max_capacity = handle_to_index( D3DKMT_HANDLE_BIT - 1 ); + unsigned int new_capacity = old_capacity * 3 / 2; + struct d3dkmt_object **tmp; + + if (new_capacity > max_capacity) new_capacity = max_capacity; + if (new_capacity <= old_capacity) return NULL; /* exhausted handle capacity */ + + if (!(tmp = realloc( objects, new_capacity * sizeof(*objects) ))) return NULL; + memset( tmp + old_capacity, 0, (new_capacity - old_capacity) * sizeof(*tmp) ); + + objects = tmp; + objects_end = tmp + new_capacity; + objects_next = tmp + old_capacity; + + return objects_next; +} + +/* allocate a d3dkmt object with a global handle */ +static d3dkmt_handle_t alloc_object_handle( struct d3dkmt_object *object ) +{ + struct d3dkmt_object **entry; + d3dkmt_handle_t handle = 0; + + if (!objects && !init_handle_table()) goto done; + + for (entry = objects_next; entry < objects_end; entry++) if (!*entry) break; + if (entry == objects_end) + { + for (entry = objects; entry < objects_next; entry++) if (!*entry) break; + if (entry == objects_next && !(entry = grow_handle_table())) goto done; + } + + handle = index_to_handle( entry - objects ); + objects_next = entry + 1; + *entry = object; + +done: + if (!handle) set_error( STATUS_NO_MEMORY ); + return handle; +} + +/* free a d3dkmt global object handle */ +static void free_object_handle( d3dkmt_handle_t global ) +{ + unsigned int index = handle_to_index( global ); + assert( objects + index < objects_end ); + objects[index] = NULL; +} + static void d3dkmt_object_dump( struct object *obj, int verbose ) { struct d3dkmt_object *object = (struct d3dkmt_object *)obj; assert( obj->ops == &d3dkmt_object_ops );
- fprintf( stderr, "type=%#x\n", object->type ); + fprintf( stderr, "type=%#x global=%#x\n", object->type, object->global ); }
static void d3dkmt_object_destroy( struct object *obj ) { + struct d3dkmt_object *object = (struct d3dkmt_object *)obj; assert( obj->ops == &d3dkmt_object_ops ); + + if (object->global) free_object_handle( object->global ); }
static struct d3dkmt_object *d3dkmt_object_create( enum d3dkmt_type type ) @@ -89,7 +168,14 @@ static struct d3dkmt_object *d3dkmt_object_create( enum d3dkmt_type type ) struct d3dkmt_object *object;
if (!(object = alloc_object( &d3dkmt_object_ops ))) return NULL; - object->type = type; + object->type = type; + object->global = 0; + + if (!(object->global = alloc_object_handle( object ))) + { + release_object( object ); + return NULL; + }
return object; } @@ -101,5 +187,6 @@ DECL_HANDLER(d3dkmt_object_create)
if (!(object = d3dkmt_object_create( req->type ))) return; reply->handle = alloc_handle( current->process, object, STANDARD_RIGHTS_ALL, OBJ_INHERIT ); + reply->global = object->global; release_object( object ); } diff --git a/server/protocol.def b/server/protocol.def index 9fbd90a0877..a6e0590d4d6 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -34,6 +34,7 @@
typedef unsigned int obj_handle_t; typedef unsigned int user_handle_t; +typedef unsigned int d3dkmt_handle_t; typedef unsigned int atom_t; typedef unsigned int process_id_t; typedef unsigned int thread_id_t; @@ -4151,6 +4152,7 @@ enum inproc_sync_type @REQ(d3dkmt_object_create) unsigned int type; /* d3dkmt object type */ @REPLY + d3dkmt_handle_t global; /* global d3dkmt handle */ obj_handle_t handle; /* internal handle of the server object */ @END
diff --git a/tools/make_requests b/tools/make_requests index f87664c9690..17e425537b5 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -34,6 +34,7 @@ my %formats = "atom_t" => [ 4, 4, "%04x" ], "process_id_t" => [ 4, 4, "%04x" ], "thread_id_t" => [ 4, 4, "%04x" ], + "d3dkmt_handle_t"=>[ 4, 4, "%08x" ], "unsigned __int64" => [ 8, 8, "&uint64" ], "timeout_t" => [ 8, 8 ], "abstime_t" => [ 8, 8 ],
v5: Reduce the MR to only global handle allocation, add a test to show that global handles references aren't leaked on process exit, justifying the use of a separate internal handle to keep track of server object references and release owned d3dkmt object refs on process exit.