From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/msvcr120/tests/msvcr120.c | 2 +- dlls/msvcrt/concurrency.c | 59 +++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c index 0a4fb383e0e..51a4e856562 100644 --- a/dlls/msvcr120/tests/msvcr120.c +++ b/dlls/msvcr120/tests/msvcr120.c @@ -1587,7 +1587,7 @@ static void test_StructuredTaskCollection(void) ok(b, "SetEvent failed\n");
status = p__StructuredTaskCollection__RunAndWait(&task_coll, NULL); - todo_wine ok(status == 2, "_StructuredTaskCollection::_RunAndWait failed: %d\n", status); + ok(status == 2, "_StructuredTaskCollection::_RunAndWait failed: %d\n", status); call_func1(p__StructuredTaskCollection_dtor, &task_coll);
CloseHandle(chore_start_evt); diff --git a/dlls/msvcrt/concurrency.c b/dlls/msvcrt/concurrency.c index 116b130f818..b584dce9fad 100644 --- a/dlls/msvcrt/concurrency.c +++ b/dlls/msvcrt/concurrency.c @@ -1967,6 +1967,9 @@ struct execute_chore_data { _StructuredTaskCollection *task_collection; };
+void __thiscall _StructuredTaskCollection__Cancel( + _StructuredTaskCollection *this); + static LONG CALLBACK execute_chore_except(EXCEPTION_POINTERS *pexc, void *_data) { struct execute_chore_data *data = _data; @@ -1976,6 +1979,8 @@ static LONG CALLBACK execute_chore_except(EXCEPTION_POINTERS *pexc, void *_data) if (pexc->ExceptionRecord->ExceptionCode != CXX_EXCEPTION) return EXCEPTION_CONTINUE_SEARCH;
+ _StructuredTaskCollection__Cancel(data->task_collection); + ptr = operator_new(sizeof(*ptr)); __ExceptionPtrCreate(ptr); exception_ptr_from_record(ptr, pexc->ExceptionRecord); @@ -2216,6 +2221,8 @@ _TaskCollectionStatus __stdcall _StructuredTaskCollection__RunAndWait( } __FINALLY_CTX(exception_ptr_rethrow_finally, ep) } + if (exception & STRUCTURED_TASK_COLLECTION_CANCELLED) + return TASK_COLLECTION_CANCELLED; return TASK_COLLECTION_SUCCESS; }
@@ -2226,7 +2233,57 @@ DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection__Cancel, 4) void __thiscall _StructuredTaskCollection__Cancel( _StructuredTaskCollection *this) { - FIXME("(%p): stub!\n", this); + ExternalContextBase* context; + ThreadScheduler *scheduler; + void *prev_exception, *new_exception; + struct scheduled_chore *sc, *next; + LONG removed = 0; + LONG prev_finished, new_finished; + + TRACE("(%p)\n", this); + + context = (ExternalContextBase*)this->context; + if (!context) + return; + scheduler = get_thread_scheduler_from_context((Context*)context); + if (!scheduler) + return; + + new_exception = this->exception; + do { + prev_exception = new_exception; + if ((ULONG_PTR)prev_exception & STRUCTURED_TASK_COLLECTION_CANCELLED) + return; + new_exception = (void*)((ULONG_PTR)prev_exception | + STRUCTURED_TASK_COLLECTION_CANCELLED); + } while ((new_exception = InterlockedCompareExchangePointer( + &this->exception, new_exception, prev_exception)) + != prev_exception); + + EnterCriticalSection(&scheduler->cs); + LIST_FOR_EACH_ENTRY_SAFE(sc, next, &scheduler->scheduled_chores, + struct scheduled_chore, entry) { + if (sc->chore->task_collection != this) + continue; + sc->chore->task_collection = NULL; + list_remove(&sc->entry); + removed++; + operator_delete(sc); + } + LeaveCriticalSection(&scheduler->cs); + if (!removed) + return; + + new_finished = this->finished; + do { + prev_finished = new_finished; + if (prev_finished == FINISHED_INITIAL) + new_finished = removed; + else + new_finished = prev_finished + removed; + } while ((new_finished = InterlockedCompareExchange(&this->finished, + new_finished, prev_finished)) != prev_finished); + RtlWakeAddressAll((LONG*)&this->finished); }
/* ?_IsCanceling@_StructuredTaskCollection@details@Concurrency@@QAA_NXZ */