From a6a12dd636221a75345d856176606f74f968ba1a Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Mon, 12 Dec 2016 16:44:44 -0800 Subject: [PATCH v2 2/3] msvcrt: Implement Concurrency::event v2: don't use separate thread Signed-off-by: Daniel Lehman --- dlls/msvcrt/lock.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++--- dlls/msvcrt/main.c | 3 + dlls/msvcrt/msvcrt.h | 19 +++++ 3 files changed, 235 insertions(+), 12 deletions(-) diff --git a/dlls/msvcrt/lock.c b/dlls/msvcrt/lock.c index d9fe05d..c30f756 100644 --- a/dlls/msvcrt/lock.c +++ b/dlls/msvcrt/lock.c @@ -22,6 +22,7 @@ #include #include "wine/debug.h" +#include "wine/list.h" #include "windef.h" #include "winbase.h" #include "winternl.h" @@ -550,19 +551,182 @@ unsigned int __cdecl _GetConcurrency(void) return val; } +static RTL_RUN_ONCE evt_init_once = RTL_RUN_ONCE_INIT; +static critical_section evt_cs; + typedef struct { - volatile void *wait; - void *reset; - critical_section cs; + int signaled; + struct list *waiters; } event; +struct thread_wait; +typedef struct +{ + struct thread_wait *wait; + struct list entry; +} thread_wait_entry; + +typedef struct thread_wait +{ + int count; + int wait_all; + size_t result; + event **events; + struct list entry; + thread_wait_entry entries[1]; +} thread_wait; + +static thread_wait *evt_thread_wait_alloc(int count, event **evts, int wait_all) +{ + thread_wait *wait; + int i; + + wait = heap_alloc(FIELD_OFFSET(thread_wait, entries[count])); + if(!wait) + throw_bad_alloc("bad allocation"); + wait->count = count; + wait->wait_all = wait_all; + wait->result = -1; + wait->events = evts; + for(i = 0; i < count; i++) { + wait->entries[i].wait = wait; + list_init(&wait->entries[i].entry); + } + + return wait; +} + +static void evt_remove_queue(thread_wait *wait) +{ + int i; + + for(i = 0; i < wait->count; i++) { + list_remove(&wait->entries[i].entry); + if(wait->events[i]->waiters == &wait->entries[i].entry) + wait->events[i]->waiters = list_head(wait->events[i]->waiters); + } +} + +static void evt_add_wait_queue(thread_wait *wait) +{ + int i; + + for(i = 0; i < wait->count; i++) { + if(wait->events[i]->waiters) + list_add_tail(wait->events[i]->waiters, &wait->entries[i].entry); + else + wait->events[i]->waiters = &wait->entries[i].entry; + } +} + +static size_t evt_check_wait(thread_wait *wait) +{ + int i; + + if(wait->wait_all) { + for(i = 0; i < wait->count; i++) { + if(!wait->events[i]->signaled) + return -1; + } + return wait->count-1; + } else { + for(i = 0; i < wait->count; i++) { + if(wait->events[i]->signaled) + return i; + } + return -1; + } +} + +static void evt_wake(event *this) +{ + size_t ret; + struct list *ptr; + thread_wait *wait; + thread_wait_entry *cur; + + ptr = this->waiters; + while(ptr) { + cur = LIST_ENTRY(ptr, thread_wait_entry, entry); + wait = cur->wait; + + ret = evt_check_wait(wait); + if(ret != -1) { + evt_remove_queue(wait); + wait->result = ret; + critical_section_unlock(&evt_cs); + + NtReleaseKeyedEvent(keyed_event, wait, 0, NULL); + + /* restart at head of the list since a + wake up can change the wait queue */ + critical_section_lock(&evt_cs); + ptr = this->waiters; + } else { + ptr = list_next(this->waiters, ptr); + } + } +} + +static inline PLARGE_INTEGER evt_timeout(PLARGE_INTEGER pTime, unsigned int timeout) +{ + if(timeout == COOPERATIVE_TIMEOUT_INFINITE) return NULL; + pTime->QuadPart = (ULONGLONG)timeout * -10000; + return pTime; +} + +static size_t evt_wait(thread_wait *wait, unsigned int timeout) +{ + LARGE_INTEGER ntto; + NTSTATUS status; + size_t ret; + + ret = evt_check_wait(wait); + if(ret == -1) { + evt_add_wait_queue(wait); + critical_section_unlock(&evt_cs); + + status = NtWaitForKeyedEvent(keyed_event, wait, 0, evt_timeout(&ntto, timeout)); + + /* wait can time out but another thread can signal wait + and remove us from waiting list here */ + + critical_section_lock(&evt_cs); + if(status && wait->result == -1) { + evt_remove_queue(wait); + ret = COOPERATIVE_WAIT_TIMEOUT; + } else + ret = wait->result; + } + + return ret; +} + +static BOOL WINAPI evt_init(INIT_ONCE *init_once, void *parm, void **ctx) +{ + critical_section_ctor(&evt_cs); + return TRUE; +} + +void msvcrt_term_event(void) +{ + critical_section_dtor(&evt_cs); +} + /* ??0event@Concurrency@@QAE@XZ */ /* ??0event@Concurrency@@QEAA@XZ */ DEFINE_THISCALL_WRAPPER(event_ctor, 4) event* __thiscall event_ctor(event *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this); + + this->signaled = 0; + this->waiters = NULL; + + if(!keyed_event) + InitOnceExecuteOnce(&evt_init_once, evt_init, NULL, NULL); + return this; } @@ -571,7 +735,7 @@ event* __thiscall event_ctor(event *this) DEFINE_THISCALL_WRAPPER(event_dtor, 4) void __thiscall event_dtor(event *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this); } /* ?reset@event@Concurrency@@QAEXXZ */ @@ -579,7 +743,11 @@ void __thiscall event_dtor(event *this) DEFINE_THISCALL_WRAPPER(event_reset, 4) void __thiscall event_reset(event *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this); + + critical_section_lock(&evt_cs); + this->signaled = 0; + critical_section_unlock(&evt_cs); } /* ?set@event@Concurrency@@QAEXXZ */ @@ -587,7 +755,14 @@ void __thiscall event_reset(event *this) DEFINE_THISCALL_WRAPPER(event_set, 4) void __thiscall event_set(event *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this); + + critical_section_lock(&evt_cs); + if(!this->signaled) { + this->signaled = 1; + evt_wake(this); + } + critical_section_unlock(&evt_cs); } /* ?wait@event@Concurrency@@QAEII@Z */ @@ -595,18 +770,44 @@ void __thiscall event_set(event *this) DEFINE_THISCALL_WRAPPER(event_wait, 8) size_t __thiscall event_wait(event *this, unsigned int timeout) { - FIXME("(%p %u) stub\n", this, timeout); - return COOPERATIVE_WAIT_TIMEOUT; + thread_wait *wait; + size_t ret; + + TRACE("(%p %u)\n", this, timeout); + + wait = evt_thread_wait_alloc(1, &this, FALSE); + + critical_section_lock(&evt_cs); + ret = this->signaled ? 0 : evt_wait(wait, timeout); + critical_section_unlock(&evt_cs); + + heap_free(wait); + + return ret; } /* ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z */ /* ?wait_for_multiple@event@Concurrency@@SA_KPEAPEAV12@_K_NI@Z */ int __cdecl event_wait_for_multiple(event **events, MSVCRT_size_t count, MSVCRT_bool wait_all, unsigned int timeout) { - FIXME("(%p %ld %d %u) stub\n", events, count, wait_all, timeout); - return COOPERATIVE_WAIT_TIMEOUT; -} + thread_wait *wait; + size_t ret; + + TRACE("(%p %ld %d %u)\n", events, count, wait_all, timeout); + if(count == 0) + return 0; + + wait = evt_thread_wait_alloc(count, events, wait_all); + + critical_section_lock(&evt_cs); + ret = evt_wait(wait, timeout); + critical_section_unlock(&evt_cs); + + heap_free(wait); + + return ret; +} #endif #if _MSVCR_VER >= 110 diff --git a/dlls/msvcrt/main.c b/dlls/msvcrt/main.c index a930a32..31c1d4a 100644 --- a/dlls/msvcrt/main.c +++ b/dlls/msvcrt/main.c @@ -127,6 +127,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) break; case DLL_PROCESS_DETACH: msvcrt_free_io(); +#if _MSVCR_VER >= 100 + msvcrt_term_event(); +#endif if (lpvReserved) break; msvcrt_free_popen_data(); msvcrt_free_locks(); diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h index 3caa43c..de81ba1 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -329,6 +329,10 @@ extern void msvcrt_free_popen_data(void) DECLSPEC_HIDDEN; extern BOOL msvcrt_init_heap(void) DECLSPEC_HIDDEN; extern void msvcrt_destroy_heap(void) DECLSPEC_HIDDEN; +#if _MSVCR_VER >= 100 +extern void msvcrt_term_event(void) DECLSPEC_HIDDEN; +#endif + extern unsigned msvcrt_create_io_inherit_block(WORD*, BYTE**) DECLSPEC_HIDDEN; extern unsigned int __cdecl _control87(unsigned int, unsigned int); @@ -1181,6 +1185,7 @@ extern char* __cdecl __unDName(char *,const char*,int,malloc_func_t,free_func_t, #define UCRTBASE_SCANF_MASK (0x0007) +#define COOPERATIVE_TIMEOUT_INFINITE ((unsigned int)-1) #define COOPERATIVE_WAIT_TIMEOUT ~0 typedef enum { @@ -1380,4 +1385,18 @@ typedef struct { _FPIEEE_VALUE Result; } _FPIEEE_RECORD, *_PFPIEEE_RECORD; +static inline void* __WINE_ALLOC_SIZE(1) heap_alloc(size_t len) +{ + return HeapAlloc(GetProcessHeap(), 0, len); +} + +static inline void* __WINE_ALLOC_SIZE(1) heap_alloc_zero(size_t len) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); +} + +static inline BOOL heap_free(void *mem) +{ + return HeapFree(GetProcessHeap(), 0, mem); +} #endif /* __WINE_MSVCRT_H */ -- 1.9.5