From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/sysparams.c | 1 + dlls/win32u/win32u_private.h | 18 +++++ dlls/win32u/winstation.c | 138 ++++++++++++++++++++++++++++++++++- 4 files changed, 155 insertions(+), 3 deletions(-)
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 11b1a3ff8a1..960e82dc15f 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -128,6 +128,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 6993373ca54..891b2ca8589 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6201,6 +6201,7 @@ static void thread_detach(void)
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..9d9d36b0a30 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -194,6 +194,24 @@ 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; +}; +#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 cfa5121a80c..c4f3b5d8322 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -45,6 +45,11 @@ WINE_DECLARE_DEBUG_CHANNEL(win);
#define DESKTOP_ALL_ACCESS 0x01ff
+struct session_thread_data +{ + const shared_object_t *shared_desktop; /* thread desktop shared session cached object */ +}; + struct session_block { struct list entry; /* entry in the session block list */ @@ -56,6 +61,32 @@ struct session_block static pthread_mutex_t session_lock = PTHREAD_MUTEX_INITIALIZER; static struct list session_blocks = LIST_INIT(session_blocks);
+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 shared_object_t *object, UINT64 *seq ) +{ + while ((*seq = ReadNoFence64( &object->seq )) & 1) YieldProcessor(); + __SHARED_READ_FENCE; +} + +static BOOL session_object_release_seqlock( const shared_object_t *object, UINT64 seq ) +{ + __SHARED_READ_FENCE; + return ReadNoFence64( &object->seq ) == seq; +} + static NTSTATUS map_shared_session_block( SIZE_T offset, SIZE_T size, struct session_block **ret ) { static const WCHAR nameW[] = @@ -135,6 +166,102 @@ static NTSTATUS set_shared_object_cache( obj_locator_t locator, const shared_obj return status; }
+enum object_type +{ + OBJECT_TYPE_DESKTOP = 1, +}; + +static NTSTATUS find_shared_session_object( UINT tid, enum object_type type, const shared_object_t **object_cache, UINT64 *id ) +{ + obj_locator_t locator = {0}; + NTSTATUS status; + + TRACE( "tid %04x, type %u, object_cache %p\n", tid, type, object_cache ); + + switch (type) + { + case OBJECT_TYPE_DESKTOP: + SERVER_START_REQ( get_thread_desktop ) + { + req->tid = tid; + if (!(status = wine_server_call( req ))) locator = reply->locator; + } + SERVER_END_REQ; + break; + default: + ERR( "Invalid session object type %u\n", type ); + return STATUS_INVALID_PARAMETER; + } + + if (!status) status = set_shared_object_cache( locator, object_cache, id ); + 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, const shared_object_t **object_cache, + struct object_lock *lock, const object_shm_t **object_shm ) +{ + const shared_object_t *object = *object_cache; + NTSTATUS status; + + assert( !lock->id || object ); + + TRACE( "tid %04x, type %u, object_cache %p, lock %p, object_shm %p\n", tid, type, object_cache, lock, object_shm ); + + if (!lock->id && object) lock->id = object->id; + else if (lock->id) + { + /* lock was previously acquired, finish reading and check data consistency */ + 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; + } + + memset( lock, 0, sizeof(*lock) ); + if (valid) return STATUS_SUCCESS; + + *object_cache = NULL; /* invalidate object cache if the lock was abandoned due to id mismatch */ + } + + if (*object_cache || !(status = find_shared_session_object( tid, type, object_cache, &lock->id ))) + { + object = *object_cache; + *object_shm = &object->shm; + session_object_acquire_seqlock( object, &lock->seq ); + return STATUS_PENDING; + } + + WARN( "Failed to find object type %u for thread %04x\n", type, tid ); + 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(); + const shared_object_t **cache = &data->shared_desktop; + + TRACE( "lock %p, desktop_shm %p\n", lock, desktop_shm ); + + return get_thread_session_object( GetCurrentThreadId(), OBJECT_TYPE_DESKTOP, cache, + lock, (const object_shm_t **)desktop_shm ); +} + +static void set_shared_desktop_cache( obj_locator_t locator ) +{ + struct session_thread_data *data = get_session_thread_data(); + UINT status; + UINT64 id; + + if ((status = set_shared_object_cache( locator, &data->shared_desktop, &id ))) + WARN( "Failed to get shared desktop object, status %#x\n", status ); + else if (id != data->shared_desktop->id) + data->shared_desktop = NULL; +} + BOOL is_virtual_desktop(void) { HANDLE desktop = NtUserGetThreadDesktop( GetCurrentThreadId() ); @@ -357,12 +484,17 @@ 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; - const shared_object_t *cache; - UINT64 id; - set_shared_object_cache( locator, &cache, &id ); + struct object_lock lock = OBJECT_LOCK_INIT; + const desktop_shm_t *desktop_shm; + + set_shared_desktop_cache( locator ); thread_info->client_info.top_window = 0; thread_info->client_info.msg_window = 0; if (key_state_info) key_state_info->time = 0; + + while (get_shared_desktop( &lock, &desktop_shm ) == STATUS_PENDING) + /* nothing */; + if (was_virtual_desktop != is_virtual_desktop()) update_display_cache( FALSE ); } return ret;