[PATCH] mstask/tests: Work around race conditions with the task scheduler process.

Francois Gouget fgouget at codeweavers.com
Thu May 20 17:13:52 CDT 2021


When a task is created the process scheduler locks and loads it. This
can cause ITaskScheduler_Delete() to fail with a sharing violation if
it is called at the wrong time. Just retry a bit later when that
happens.

This fixes failures mostly in Wine but also in Windows 10 from time
to time in both task and task_scheduler.

Signed-off-by: Francois Gouget <fgouget at icodeweavers.com>
---
The failures are much less frequent on Windows but still happen:
https://test.winehq.org/data/39263558a2088940aaacd6eda19ca23d40b63495/vista_newtb-wvistau64-64/mstask:task.html
https://test.winehq.org/data/092c7a09a5afde3f11b71b1816388e80d062e8ec/win2004_newtb-w10pro64-ja-64/mstask:task_scheduler.html

I take that to mean that ITaskScheduler_Activate() is right to pass
STGM_SHARE_DENY_WRITE to IPersistFile_Load().

So the reason for the lower falure frequency on Windows may have to do
with Windows' coarser default scheduling granularity (15.6ms). But I
suspect it's higher in Wine because of the wineserver ping-pong:
* After creating the new task file the test gets blocked in some
  wineserver call.
* The wineserver handles that call but also sends a notification of
  the new file to the task scheduler. Then the Linux scheduler 
  preferentially gives the next time slice to the task scheduler rather 
  than to the test (because the task scheduler was blocked for a long 
  time?).
* The task scheduler ends up making a call to the wineserver to lock the
  file and blocks.
* After wineserver handles that call the Linux scheduler gives the
  next time slice to the test.
* This lets the test continue until it calls DeleteFile() which fails
  due to the locking.

This depends on many factors (cores) and timing which could also explain
why the failure frequency is not exactly the same on debiant2 and
cw-gtx560/cw-rx460. Interestingly, adding a Sleep(10) just before the
calls to ITaskScheduler_Delete() helps in Wine (but makes no discernible
difference in Windows).
---
 dlls/mstask/tests/task.c           |  7 +++++--
 dlls/mstask/tests/task_scheduler.c | 31 +++++++++++++++++++++++++-----
 2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/dlls/mstask/tests/task.c b/dlls/mstask/tests/task.c
index a9957117e9b..0c7f85394ea 100644
--- a/dlls/mstask/tests/task.c
+++ b/dlls/mstask/tests/task.c
@@ -29,6 +29,9 @@ static ITaskScheduler *scheduler;
 static const WCHAR task_name[] = {'T','e','s','t','i','n','g',0};
 static const WCHAR empty[] = {0};
 
+extern HRESULT taskscheduler_delete(ITaskScheduler*, const WCHAR*);
+
+
 static LPCWSTR path_resolve_name(LPCWSTR base_name)
 {
     static WCHAR buffer[MAX_PATH];
@@ -552,7 +555,7 @@ static void test_Run(void)
     HRESULT hr, status;
 
     /* cleanup after previous runs */
-    ITaskScheduler_Delete(scheduler, wine_test_runW);
+    taskscheduler_delete(scheduler, wine_test_runW);
 
     hr = ITaskScheduler_NewWorkItem(scheduler, wine_test_runW, &CLSID_CTask,
                                     &IID_ITask, (IUnknown **)&task);
@@ -679,7 +682,7 @@ static void test_Run(void)
     else
         ITask_Release(task);
 
-    hr = ITaskScheduler_Delete(scheduler, wine_test_runW);
+    hr = taskscheduler_delete(scheduler, wine_test_runW);
     ok(hr == S_OK, "got %#x\n", hr);
 }
 
diff --git a/dlls/mstask/tests/task_scheduler.c b/dlls/mstask/tests/task_scheduler.c
index 5abee75e23c..fafad585f53 100644
--- a/dlls/mstask/tests/task_scheduler.c
+++ b/dlls/mstask/tests/task_scheduler.c
@@ -31,6 +31,27 @@ static ITaskScheduler *test_task_scheduler;
 
 static const WCHAR does_not_existW[] = {'\\','\\','d','o','e','s','_','n','o','t','_','e','x','i','s','t',0};
 
+
+HRESULT taskscheduler_delete(ITaskScheduler *scheduler, const WCHAR *name)
+{
+    HRESULT hr;
+    int i = 0;
+
+    while (1)
+    {
+        hr = ITaskScheduler_Delete(scheduler, name);
+        if (hr != HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)) break;
+        if (++i == 5) break;
+
+        /* The task scheduler locked the file to load the task (see
+         * ITaskScheduler_Activate()). It should be done soon.
+         */
+        trace("Got a sharing violation deleting %s, retrying...\n", wine_dbgstr_w(name));
+        Sleep(100);
+    }
+    return hr;
+}
+
 static void test_NewWorkItem(void)
 {
     HRESULT hres;
@@ -225,7 +246,7 @@ static void test_Enum(void)
     ok(hr == S_OK, "got %#x\n", hr);
 
     /* cleanup after previous runs */
-    ITaskScheduler_Delete(scheduler, Task1);
+    taskscheduler_delete(scheduler, Task1);
 
     hr = ITaskScheduler_NewWorkItem(scheduler, Task1, &CLSID_CTask, &IID_ITask, (IUnknown **)&task);
     ok(hr == S_OK, "got %#x\n", hr);
@@ -291,7 +312,7 @@ static void test_Enum(void)
 
     IEnumWorkItems_Release(tasks);
 
-    hr = ITaskScheduler_Delete(scheduler, Task1);
+    hr = taskscheduler_delete(scheduler, Task1);
     ok(hr == S_OK, "got %#x\n", hr);
 
     ITaskScheduler_Release(scheduler);
@@ -494,11 +515,11 @@ static void test_task_storage(void)
 
     test_save_task_curfile(task);
 
-    hr = ITaskScheduler_Delete(scheduler, Task1);
+    hr = taskscheduler_delete(scheduler, Task1);
     ok(hr == S_OK, "got %#x\n", hr);
-    hr = ITaskScheduler_Delete(scheduler, Task2);
+    hr = taskscheduler_delete(scheduler, Task2);
     ok(hr == S_OK, "got %#x\n", hr);
-    hr = ITaskScheduler_Delete(scheduler, Task3);
+    hr = taskscheduler_delete(scheduler, Task3);
     ok(hr == S_OK, "got %#x\n", hr);
 
     ITask_Release(task);
-- 
2.20.1



More information about the wine-devel mailing list