From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/sysparams.c | 1 + dlls/win32u/win32u_private.h | 19 +++++ dlls/win32u/winstation.c | 144 +++++++++++++++++++++++++++++++++-- 4 files changed, 160 insertions(+), 5 deletions(-)
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index bb2169998b6..c0027f44c2f 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -127,6 +127,7 @@ struct user_thread_info UINT spy_indent; /* Current spy indent */ BOOL clipping_cursor; /* thread is currently clipping */ DWORD clipping_reset; /* time when clipping was last reset */ + struct session_thread_data *session_data; /* shared session thread data */ };
C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) ); diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 26bddad7b59..899722b9ee1 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6210,6 +6210,7 @@ static void thread_detach(void) destroy_thread_windows(); cleanup_imm_thread(); NtClose( thread_info->server_queue ); + free( thread_info->session_data );
exiting_thread_id = 0; } diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index ff861db4dd9..0c800e4d10e 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -194,6 +194,25 @@ extern void user_check_not_lock(void); extern BOOL get_vulkan_uuid_from_luid( const LUID *luid, GUID *uuid );
/* winstation.c */ + +struct shared_session; + +struct object_lock +{ + UINT64 id; + UINT64 seq; + struct shared_session *session; /* only valid when locked */ +}; +#define OBJECT_LOCK_INIT {0} + +/* Get shared session object's data pointer, must be called in a loop while STATUS_PENDING + * is returned, lock must be initialized with OBJECT_LOCK_INIT. + * + * The data read from the objects may be transient and no logic should be executed based + * on it, within the loop, or after, unless the function has returned STATUS_SUCCESS. + */ +extern NTSTATUS get_shared_desktop( struct object_lock *lock, const desktop_shm_t **desktop_shm ); + extern BOOL is_virtual_desktop(void);
/* window.c */ diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 790b3e8b3c5..5abbdd6e067 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -25,6 +25,7 @@ #include <assert.h> #include <stdarg.h> #include <stddef.h> + #include <pthread.h>
#include "ntstatus.h" @@ -44,6 +45,11 @@ WINE_DECLARE_DEBUG_CHANNEL(win);
#define DESKTOP_ALL_ACCESS 0x01ff
+struct session_thread_data +{ + obj_locator_t shared_desktop; /* thread desktop shared session object locator */ +}; + struct shared_session { LONG ref; @@ -54,6 +60,32 @@ struct shared_session static pthread_mutex_t session_lock = PTHREAD_MUTEX_INITIALIZER; static struct shared_session *shared_session;
+static struct session_thread_data *get_session_thread_data(void) +{ + struct user_thread_info *thread_info = get_user_thread_info(); + if (!thread_info->session_data) thread_info->session_data = calloc(1, sizeof(*thread_info->session_data)); + return thread_info->session_data; +} + +#if defined(__i386__) || defined(__x86_64__) +/* this prevents compilers from incorrectly reordering non-volatile reads (e.g., memcpy) from shared memory */ +#define __SHARED_READ_FENCE do { __asm__ __volatile__( "" ::: "memory" ); } while (0) +#else +#define __SHARED_READ_FENCE __atomic_thread_fence( __ATOMIC_ACQUIRE ) +#endif + +static void session_object_acquire_seqlock( const session_object_t *object, UINT64 *seq ) +{ + while ((*seq = ReadNoFence64( &object->seq )) & 1) YieldProcessor(); + __SHARED_READ_FENCE; +} + +static BOOL session_object_release_seqlock( const session_object_t *object, UINT64 seq ) +{ + __SHARED_READ_FENCE; + return ReadNoFence64( &object->seq ) == seq; +} + static struct shared_session *shared_session_acquire( struct shared_session *session ) { int ref = InterlockedIncrement( &session->ref ); @@ -135,6 +167,104 @@ static NTSTATUS get_shared_session( BOOL force, struct shared_session **ret ) return STATUS_SUCCESS; }
+enum object_type +{ + OBJECT_TYPE_DESKTOP = 1, +}; + +static NTSTATUS get_thread_session_object_locator( UINT tid, enum object_type type, obj_locator_t *locator ) +{ + NTSTATUS status; + + TRACE( "tid %04x, type %u, locator %p\n", tid, type, locator ); + + switch (type) + { + case OBJECT_TYPE_DESKTOP: + SERVER_START_REQ( get_thread_desktop ) + { + req->tid = tid; + if (!(status = wine_server_call( req ))) + { + if (!reply->locator.id) status = STATUS_INVALID_HANDLE; + else *locator = reply->locator; + } + } + SERVER_END_REQ; + break; + default: + ERR( "Invalid session object type %u\n", type ); + return STATUS_INVALID_PARAMETER; + } + + return status; +} + +/* return a locked session object for a thread id and type */ +static NTSTATUS get_thread_session_object( UINT tid, enum object_type type, obj_locator_t *locator, + struct object_lock *lock, const object_shm_t **object_shm ) +{ + struct shared_session *session; + BOOL session_valid = TRUE; + NTSTATUS status; + + assert( !lock->id || *object_shm ); + + TRACE( "tid %04x, type %u, locator %p, lock %p, object_shm %p\n", tid, type, locator, lock, object_shm ); + + if (lock->id) + { + /* lock was previously acquired, finish reading and check data consistency */ + const session_object_t *object = CONTAINING_RECORD( *object_shm, session_object_t, shm ); + BOOL valid = lock->id == object->id; + + if (!session_object_release_seqlock( object, lock->seq )) + { + /* retry if seq doesn't match, wineserver has written to it */ + session_object_acquire_seqlock( object, &lock->seq ); + return STATUS_PENDING; + } + + shared_session_release( lock->session ); + memset( lock, 0, sizeof(*lock) ); + + if (valid) return STATUS_SUCCESS; + locator->id = 0; /* invalidate locator if the lock was abandoned due to id mismatch */ + } + + while (!(status = get_shared_session( !session_valid, &session ))) + { + if (!locator->id && (status = get_thread_session_object_locator( tid, type, locator ))) break; + if ((session_valid = locator->index < session->object_capacity)) + { + const session_object_t *object = &session->objects[locator->index]; + lock->id = locator->id; + lock->session = session; + *object_shm = &object->shm; + session_object_acquire_seqlock( object, &lock->seq ); + return STATUS_PENDING; + } + shared_session_release( session ); + memset( locator, 0, sizeof(*locator) ); + } + + WARN( "Failed to find object type %u for thread %04x\n", type, tid ); + if (!status) shared_session_release( session ); + memset( locator, 0, sizeof(*locator) ); + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS get_shared_desktop( struct object_lock *lock, const desktop_shm_t **desktop_shm ) +{ + struct session_thread_data *data = get_session_thread_data(); + obj_locator_t *locator = &data->shared_desktop; + + TRACE( "lock %p, desktop_shm %p\n", lock, desktop_shm ); + + return get_thread_session_object( GetCurrentThreadId(), OBJECT_TYPE_DESKTOP, locator, + lock, (const object_shm_t **)desktop_shm ); +} + BOOL is_virtual_desktop(void) { HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); @@ -356,9 +486,18 @@ BOOL WINAPI NtUserSetThreadDesktop( HDESK handle ) { struct user_thread_info *thread_info = get_user_thread_info(); struct user_key_state_info *key_state_info = thread_info->key_state; + obj_locator_t *locator = &get_session_thread_data()->shared_desktop; + struct object_lock lock = OBJECT_LOCK_INIT; + const desktop_shm_t *desktop_shm; + thread_info->client_info.top_window = 0; thread_info->client_info.msg_window = 0; if (key_state_info) key_state_info->time = 0; + memset( locator, 0, sizeof(*locator) ); + + while (get_shared_desktop( &lock, &desktop_shm ) == STATUS_PENDING) + /* nothing */; + if (was_virtual_desktop != is_virtual_desktop()) update_display_cache( TRUE ); } return ret; @@ -725,17 +864,12 @@ void winstation_init(void) { RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters; WCHAR *winstation = NULL, *desktop = NULL, *buffer = NULL; - struct shared_session *session; HANDLE handle, dir = NULL; OBJECT_ATTRIBUTES attr; UNICODE_STRING str; - NTSTATUS status;
static const WCHAR winsta0[] = {'W','i','n','S','t','a','0',0};
- if (!(status = get_shared_session( FALSE, &session ))) - shared_session_release( session ); - if (params->Desktop.Length) { buffer = malloc( params->Desktop.Length + sizeof(WCHAR) );