From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/concrt140/Makefile.in | 4 +- dlls/concrt140/concrt140.c | 17 +++ dlls/msvcr120/tests/msvcr120.c | 6 +- dlls/msvcrt/concurrency.c | 183 +++++++++++++++++++++++++++++++-- dlls/msvcrt/cxx.h | 4 + dlls/msvcrt/exception_ptr.c | 12 ++- run-test | 17 +++ 7 files changed, 228 insertions(+), 15 deletions(-) create mode 100755 run-test
diff --git a/dlls/concrt140/Makefile.in b/dlls/concrt140/Makefile.in index aac1ed7b34a..05258adf7ee 100644 --- a/dlls/concrt140/Makefile.in +++ b/dlls/concrt140/Makefile.in @@ -1,7 +1,9 @@ +EXTRADEFS = -D_CONCRT= MODULE = concrt140.dll PARENTSRC = ../msvcrt
C_SRCS = \ concrt140.c \ concurrency.c \ - details.c + details.c \ + exception_ptr.c diff --git a/dlls/concrt140/concrt140.c b/dlls/concrt140/concrt140.c index 0a770c9dae6..aa2fdd7c981 100644 --- a/dlls/concrt140/concrt140.c +++ b/dlls/concrt140/concrt140.c @@ -105,6 +105,23 @@ void DECLSPEC_NORETURN throw_range_error(const char *str) _CxxThrowException(&e, &range_error_exception_type); }
+#ifdef __i386__ +__ASM_GLOBAL_FUNC( call_copy_ctor, + "pushl %ebp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") + "movl %esp, %ebp\n\t" + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") + "pushl $1\n\t" + "movl 12(%ebp), %ecx\n\t" + "pushl 16(%ebp)\n\t" + "call *8(%ebp)\n\t" + "leave\n" + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") + __ASM_CFI(".cfi_same_value %ebp\n\t") + "ret" ); +#endif + static BOOL init_cxx_funcs(void) { msvcp140 = LoadLibraryA("msvcp140.dll"); diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c index ac35076af03..24e4914d98a 100644 --- a/dlls/msvcr120/tests/msvcr120.c +++ b/dlls/msvcr120/tests/msvcr120.c @@ -170,7 +170,7 @@ typedef struct _StructuredTaskCollection Context *context; volatile LONG count; volatile LONG finished; - void *unk4; + void *exception; void *event; } _StructuredTaskCollection;
@@ -1439,8 +1439,8 @@ static void test_StructuredTaskCollection(void) "_StructuredTaskCollection ctor set wrong count: %ld != 0\n", task_coll.count); ok(task_coll.finished == LONG_MIN, "_StructuredTaskCollection ctor set wrong finished: %ld != %ld\n", task_coll.finished, LONG_MIN); - ok(task_coll.unk4 == NULL, - "_StructuredTaskCollection ctor set wrong unk4: %p != NULL\n", task_coll.unk4); + ok(task_coll.exception == NULL, + "_StructuredTaskCollection ctor set wrong exception: %p != NULL\n", task_coll.exception);
chore_start_evt = CreateEventW(NULL, TRUE, FALSE, NULL); ok(chore_start_evt != NULL, "CreateEvent failed: 0x%lx\n", GetLastError()); diff --git a/dlls/msvcrt/concurrency.c b/dlls/msvcrt/concurrency.c index 8c69c62fa3e..029baaca2e4 100644 --- a/dlls/msvcrt/concurrency.c +++ b/dlls/msvcrt/concurrency.c @@ -22,8 +22,11 @@ #include <stdbool.h>
#include "windef.h" +#include "winbase.h" #include "winternl.h" #include "wine/debug.h" +#include "wine/exception.h" +#include "wine/list.h" #include "msvcrt.h" #include "cxx.h"
@@ -89,12 +92,21 @@ struct scheduler_list { struct scheduler_list *next; };
+struct _UnrealizedChore; + +struct scheduled_chore { + struct list entry; + struct _UnrealizedChore *chore; +}; + typedef struct { Context context; LONG ref; struct scheduler_list scheduler; unsigned int id; union allocator_cache_entry *allocator_cache[8]; + struct list scheduled_chores; + CRITICAL_SECTION scheduled_chores_cs; } ExternalContextBase; extern const vtable_ptr ExternalContextBase_vtable; static void ExternalContextBase_ctor(ExternalContextBase*); @@ -166,14 +178,28 @@ typedef struct yield_func yield_func; } SpinWait;
-typedef struct +struct _StructuredTaskCollection; + +typedef struct _UnrealizedChore { - char dummy; + const vtable_ptr *vtable; + void (__cdecl *chore_proc)(struct _UnrealizedChore*); + struct _StructuredTaskCollection *task_collection; + void (__cdecl *chore_wrapper)(struct _UnrealizedChore*); + void *unk[6]; } _UnrealizedChore;
-typedef struct +#define FINISHED_INITIAL 0x80000000 +typedef struct _StructuredTaskCollection { - char dummy; + void *unk1; + unsigned int unk2; + void *unk3; + Context *context; + volatile LONG count; + volatile LONG finished; + void *exception; + void *event; } _StructuredTaskCollection;
/* keep in sync with msvcp90/msvcp90.h */ @@ -631,6 +657,7 @@ DEFINE_RTTI_DATA1(scheduler_resource_allocation_error, 0, &cexception_rtti_base_ DEFINE_CXX_DATA1(improper_lock, &cexception_cxx_type_info, cexception_dtor) DEFINE_CXX_DATA1(improper_scheduler_attach, &cexception_cxx_type_info, cexception_dtor) DEFINE_CXX_DATA1(improper_scheduler_detach, &cexception_cxx_type_info, cexception_dtor) +DEFINE_CXX_DATA1(invalid_multiple_scheduling, &cexception_cxx_type_info, cexception_dtor) DEFINE_CXX_DATA1(invalid_scheduler_policy_key, &cexception_cxx_type_info, cexception_dtor) DEFINE_CXX_DATA1(invalid_scheduler_policy_thread_specification, &cexception_cxx_type_info, cexception_dtor) DEFINE_CXX_DATA1(invalid_scheduler_policy_value, &cexception_cxx_type_info, cexception_dtor) @@ -864,6 +891,12 @@ static void ExternalContextBase_dtor(ExternalContextBase *this) operator_delete(scheduler_cur); } } + + EnterCriticalSection(&this->scheduled_chores_cs); + if (!list_empty(&this->scheduled_chores)) + ERR("scheduled chore list is not empty\n"); + LeaveCriticalSection(&this->scheduled_chores_cs); + DeleteCriticalSection(&this->scheduled_chores_cs); }
DEFINE_THISCALL_WRAPPER(ExternalContextBase_vector_dtor, 8) @@ -894,12 +927,27 @@ static void ExternalContextBase_ctor(ExternalContextBase *this) this->context.vtable = &ExternalContextBase_vtable; this->ref = 1; this->id = InterlockedIncrement(&context_id); + list_init(&this->scheduled_chores); + InitializeCriticalSection(&this->scheduled_chores_cs);
create_default_scheduler(); this->scheduler.scheduler = &default_scheduler->scheduler; call_Scheduler_Reference(&default_scheduler->scheduler); }
+static void reference_context(ExternalContextBase *context) +{ + if (context->context.vtable == &ExternalContextBase_vtable) + InterlockedIncrement(&context->ref); +} + +static void release_context(ExternalContextBase *context) +{ + if (context->context.vtable != &ExternalContextBase_vtable || + InterlockedDecrement(&context->ref) == 0) + call_Context_dtor((Context*)context, 1); +} + /* ?Alloc@Concurrency@@YAPAXI@Z */ /* ?Alloc@Concurrency@@YAPEAX_K@Z */ void * CDECL Concurrency_Alloc(size_t size) @@ -1823,10 +1871,114 @@ DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection_dtor, 4) void __thiscall _StructuredTaskCollection_dtor(_StructuredTaskCollection *this) { FIXME("(%p): stub!\n", this); + if (this->context) + release_context((ExternalContextBase*)this->context); }
#endif /* _MSVCR_VER >= 120 */
+static void CALLBACK chore_wrapper_finally(BOOL normal, void *data) +{ + _UnrealizedChore *chore = data; + LONG prev_finished, new_finished; + volatile LONG *ptr; + + TRACE("(%u %p)\n", normal, data); + + if (!chore->task_collection) + return; + ptr = &chore->task_collection->finished; + chore->task_collection = NULL; + + do { + prev_finished = *ptr; + if (prev_finished == FINISHED_INITIAL) + new_finished = 1; + else + new_finished = prev_finished + 1; + } while (InterlockedCompareExchange(ptr, new_finished, prev_finished) + != prev_finished); +} + +static void __cdecl chore_wrapper(_UnrealizedChore *chore) +{ + TRACE("(%p)\n", chore); + + __TRY + { + if (chore->chore_proc) + chore->chore_proc(chore); + } + __FINALLY_CTX(chore_wrapper_finally, chore) +} + +static void __cdecl _StructuredTaskCollection_scheduler_cb(void *data) +{ + ExternalContextBase *context = data; + struct list *entry; + struct scheduled_chore *sc; + _UnrealizedChore *chore; + + TRACE("(%p)\n", context); + + EnterCriticalSection(&context->scheduled_chores_cs); + entry = list_head(&context->scheduled_chores); + if (entry) + list_remove(entry); + LeaveCriticalSection(&context->scheduled_chores_cs); + if (!entry) + return; + + sc = LIST_ENTRY(entry, struct scheduled_chore, entry); + chore = sc->chore; + operator_delete(sc); + + chore->chore_wrapper(chore); +} + +static bool schedule_chore(_StructuredTaskCollection *this, + _UnrealizedChore *chore, ExternalContextBase **context) +{ + struct scheduled_chore *sc; + + if (chore->task_collection) { + invalid_multiple_scheduling e; + invalid_multiple_scheduling_ctor_str(&e, "Chore scheduled multiple times"); + _CxxThrowException(&e, &invalid_multiple_scheduling_exception_type); + return FALSE; + } + + sc = operator_new(sizeof(*sc)); + sc->chore = chore; + + *context = (ExternalContextBase*)this->context; + if (*context == NULL) { + Context *ctx; + *context = (ExternalContextBase*)get_current_context(); + if ((*context)->context.vtable != &ExternalContextBase_vtable) { + ERR("unknown context set\n"); + operator_delete(sc); + return FALSE; + } + reference_context(*context); + ctx = InterlockedCompareExchangePointer((void**)&this->context, *context, NULL); + if (ctx != NULL) + { + release_context(*context); + *context = (ExternalContextBase*)ctx; + } + } + + chore->task_collection = this; + chore->chore_wrapper = chore_wrapper; + InterlockedIncrement(&this->count); + + EnterCriticalSection(&(*context)->scheduled_chores_cs); + list_add_head(&(*context)->scheduled_chores, &sc->entry); + LeaveCriticalSection(&(*context)->scheduled_chores_cs); + return TRUE; +} + #if _MSVCR_VER >= 110
/* ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAAXPAV_UnrealizedChore@23@PAVlocation@3@@Z */ @@ -1837,7 +1989,14 @@ void __thiscall _StructuredTaskCollection__Schedule_loc( _StructuredTaskCollection *this, _UnrealizedChore *chore, /*location*/void *placement) { - FIXME("(%p %p %p): stub!\n", this, chore, placement); + ExternalContextBase *context; + + FIXME("(%p %p %p): semi-stub\n", this, chore, placement); + + if (schedule_chore(this, chore, &context)) + call_Scheduler_ScheduleTask_loc( + get_scheduler_from_context((Context*)context), + _StructuredTaskCollection_scheduler_cb, context, placement); }
#endif /* _MSVCR_VER >= 110 */ @@ -1849,7 +2008,14 @@ DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection__Schedule, 8) void __thiscall _StructuredTaskCollection__Schedule( _StructuredTaskCollection *this, _UnrealizedChore *chore) { - FIXME("(%p %p): stub!\n", this, chore); + ExternalContextBase *context; + + TRACE("(%p %p)\n", this, chore); + + if (schedule_chore(this, chore, &context)) + call_Scheduler_ScheduleTask( + get_scheduler_from_context((Context*)context), + _StructuredTaskCollection_scheduler_cb, context); }
/* ?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAA?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z */ @@ -3108,6 +3274,7 @@ void msvcrt_init_concurrency(void *base) init_improper_lock_cxx(base); init_improper_scheduler_attach_cxx(base); init_improper_scheduler_detach_cxx(base); + init_invalid_multiple_scheduling_cxx(base); init_invalid_scheduler_policy_key_cxx(base); init_invalid_scheduler_policy_thread_specification_cxx(base); init_invalid_scheduler_policy_value_cxx(base); @@ -3134,9 +3301,7 @@ void msvcrt_free_scheduler_thread(void) { ExternalContextBase *context = (ExternalContextBase*)try_get_current_context(); if (!context) return; - if (context->context.vtable != &ExternalContextBase_vtable || - InterlockedDecrement(&context->ref) == 0) - call_Context_dtor(context, 1); + release_context(context); }
#endif /* _MSVCR_VER >= 100 */ diff --git a/dlls/msvcrt/cxx.h b/dlls/msvcrt/cxx.h index 4051229c626..b8fa2c2efda 100644 --- a/dlls/msvcrt/cxx.h +++ b/dlls/msvcrt/cxx.h @@ -316,3 +316,7 @@ typedef struct } exception_ptr;
void throw_exception(const char*) DECLSPEC_HIDDEN; +void exception_ptr_from_record(exception_ptr*,EXCEPTION_RECORD*) DECLSPEC_HIDDEN; + +void __cdecl __ExceptionPtrCreate(exception_ptr*); +void __cdecl __ExceptionPtrDestroy(exception_ptr*); diff --git a/dlls/msvcrt/exception_ptr.c b/dlls/msvcrt/exception_ptr.c index 3540a8fb53d..9d727266e77 100644 --- a/dlls/msvcrt/exception_ptr.c +++ b/dlls/msvcrt/exception_ptr.c @@ -87,6 +87,8 @@ void __cdecl __ExceptionPtrDestroy(exception_ptr *ep) } }
+#ifndef _CONCRT + /********************************************************************* * ?__ExceptionPtrCopy@@YAXPAXPBX@Z * ?__ExceptionPtrCopy@@YAXPEAXPEBX@Z @@ -136,6 +138,8 @@ void __cdecl __ExceptionPtrRethrow(const exception_ptr *ep) ep->rec->NumberParameters, ep->rec->ExceptionInformation); }
+#endif + #ifdef __i386__ extern void call_copy_ctor( void *func, void *this, void *src, int has_vbase ); #else @@ -150,7 +154,7 @@ static inline void call_copy_ctor( void *func, void *this, void *src, int has_vb #endif
#ifndef __x86_64__ -static void exception_ptr_from_record(exception_ptr *ep, EXCEPTION_RECORD *rec) +void exception_ptr_from_record(exception_ptr *ep, EXCEPTION_RECORD *rec) { TRACE("(%p)\n", ep);
@@ -194,7 +198,7 @@ static void exception_ptr_from_record(exception_ptr *ep, EXCEPTION_RECORD *rec) return; } #else -static void exception_ptr_from_record(exception_ptr *ep, EXCEPTION_RECORD *rec) +void exception_ptr_from_record(exception_ptr *ep, EXCEPTION_RECORD *rec) { TRACE("(%p)\n", ep);
@@ -240,6 +244,8 @@ static void exception_ptr_from_record(exception_ptr *ep, EXCEPTION_RECORD *rec) } #endif
+#ifndef _CONCRT + /********************************************************************* * ?__ExceptionPtrCurrentException@@YAXPAX@Z * ?__ExceptionPtrCurrentException@@YAXPEAX@Z @@ -346,3 +352,5 @@ bool __cdecl __ExceptionPtrCompare(const exception_ptr *ep1, const exception_ptr { return ep1->rec == ep2->rec; } + +#endif diff --git a/run-test b/run-test new file mode 100755 index 00000000000..e0acd568de6 --- /dev/null +++ b/run-test @@ -0,0 +1,17 @@ +#!/bin/bash + +set -ex + +make -j28 >/dev/null 2>&1 +make -j28 -C build32 >/dev/null 2>&1 + +for dir in dlls/msvcr*; do + make -j28 -C "$dir" clean + make -j28 -C build32/"$dir" clean +done +make -j28 CFLAGS="-g -O2 -Werror" CROSSCFLAGS="-g -O2 -Werror" >/dev/null 2>&1 +make -j28 -C build32 CFLAGS="-g -O2 -Werror" CROSSCFLAGS="-g -O2 -Werror" >/dev/null 2>&1 + +cd dlls/msvcr120/tests +make -j28 +WINEDEBUG="-all" ../../../wine msvcr120_test.exe