sync rostests to r44455
[reactos.git] / rostests / winetests / kernel32 / thread.c
index 7d83b25..5f98b8c 100755 (executable)
@@ -21,6 +21,7 @@
 /* Define _WIN32_WINNT to get SetThreadIdealProcessor on Windows */
 #define _WIN32_WINNT 0x0500
 
+#include <assert.h>
 #include <stdarg.h>
 #include <stdio.h>
 
@@ -99,6 +100,61 @@ In addition there are no checks that the inheritance works properly in
 CreateThread
 */
 
+/* Functions to ensure that from a group of threads, only one executes
+   certain chunks of code at a time, and we know which one is executing
+   it.  It basically makes multithreaded execution linear, which defeats
+   the purpose of multiple threads, but makes testing easy.  */
+static HANDLE start_event, stop_event;
+static LONG num_synced;
+
+static void init_thread_sync_helpers(void)
+{
+  start_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+  ok(start_event != NULL, "CreateEvent failed\n");
+  stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+  ok(stop_event != NULL, "CreateEvent failed\n");
+  num_synced = -1;
+}
+
+static BOOL sync_threads_and_run_one(DWORD sync_id, DWORD my_id)
+{
+  LONG num = InterlockedIncrement(&num_synced);
+  assert(-1 <= num && num <= 1);
+  if (num == 1)
+  {
+      ResetEvent( stop_event );
+      SetEvent( start_event );
+  }
+  else
+  {
+    DWORD ret = WaitForSingleObject(start_event, 10000);
+    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed %x\n",ret);
+  }
+  return sync_id == my_id;
+}
+
+static void resync_after_run(void)
+{
+  LONG num = InterlockedDecrement(&num_synced);
+  assert(-1 <= num && num <= 1);
+  if (num == -1)
+  {
+      ResetEvent( start_event );
+      SetEvent( stop_event );
+  }
+  else
+  {
+    DWORD ret = WaitForSingleObject(stop_event, 10000);
+    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+  }
+}
+
+static void cleanup_thread_sync_helpers(void)
+{
+  CloseHandle(start_event);
+  CloseHandle(stop_event);
+}
+
 DWORD tlsIndex;
 
 typedef struct {
@@ -127,11 +183,11 @@ INT obeying_ars = 0; /* -1 == no, 0 == dunno yet, 1 == yes */
 */
 static DWORD WINAPI threadFunc1(LPVOID p)
 {
-    t1Struct *tstruct = (t1Struct *)p;
+   t1Struct *tstruct = p;
    int i;
 /* write our thread # into shared memory */
    tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId();
-   ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0,
+   ok(TlsSetValue(tlsIndex,(LPVOID)(INT_PTR)(tstruct->threadnum+1))!=0,
       "TlsSetValue failed\n");
 /* The threads synchronize before terminating.  This is done by
    Signaling an event, and waiting for all events to occur
@@ -149,7 +205,7 @@ static DWORD WINAPI threadFunc1(LPVOID p)
    ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" );
 
 /* Check that no one changed our tls memory */
-   ok((int)TlsGetValue(tlsIndex)-1==tstruct->threadnum,
+   ok((INT_PTR)TlsGetValue(tlsIndex)-1==tstruct->threadnum,
       "TlsGetValue failed\n");
    return NUM_THREADS+tstruct->threadnum;
 }
@@ -169,7 +225,7 @@ static DWORD WINAPI threadFunc3(LPVOID p)
 
 static DWORD WINAPI threadFunc4(LPVOID p)
 {
-    HANDLE event = (HANDLE)p;
+   HANDLE event = p;
    if(event != NULL) {
      SetEvent(event);
    }
@@ -180,7 +236,7 @@ static DWORD WINAPI threadFunc4(LPVOID p)
 #if CHECK_STACK
 static DWORD WINAPI threadFunc5(LPVOID p)
 {
-  DWORD *exitCode = (DWORD *)p;
+  DWORD *exitCode = p;
   SYSTEM_INFO sysInfo;
   sysInfo.dwPageSize=0;
   GetSystemInfo(&sysInfo);
@@ -199,13 +255,13 @@ static DWORD WINAPI threadFunc5(LPVOID p)
 
 static DWORD WINAPI threadFunc_SetEvent(LPVOID p)
 {
-    SetEvent((HANDLE) p);
+    SetEvent(p);
     return 0;
 }
 
 static DWORD WINAPI threadFunc_CloseHandle(LPVOID p)
 {
-    CloseHandle((HANDLE) p);
+    CloseHandle(p);
     return 0;
 }
 
@@ -252,7 +308,7 @@ static VOID test_CreateRemoteThread(void)
                                  hRemoteEvent, CREATE_SUSPENDED, &tid);
     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
     {
-        skip("CreateRemoteThread is not implemented\n");
+        win_skip("CreateRemoteThread is not implemented\n");
         goto cleanup;
     }
     ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError());
@@ -320,6 +376,7 @@ static VOID test_CreateThread_basic(void)
    int error;
    DWORD i,j;
    DWORD GLE, ret;
+   DWORD tid;
 
    /* lstrlenA contains an exception handler so this makes sure exceptions work in the main thread */
    ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" );
@@ -377,7 +434,7 @@ static VOID test_CreateThread_basic(void)
 
   /* Test how passing NULL as a pointer to threadid works */
   SetLastError(0xFACEaBAD);
-  thread[0] = CreateThread(NULL,0,threadFunc2,NULL,0,NULL);
+  thread[0] = CreateThread(NULL,0,threadFunc2,NULL,0,&tid);
   GLE = GetLastError();
   if (thread[0]) { /* NT */
     ok(GLE==0xFACEaBAD, "CreateThread set last error to %d, expected 4207848365\n", GLE);
@@ -398,6 +455,7 @@ static VOID test_CreateThread_suspended(void)
 {
   HANDLE thread;
   DWORD threadId;
+  DWORD suspend_count;
   int error;
 
   thread = CreateThread(NULL,0,threadFunc2,NULL,
@@ -419,6 +477,15 @@ static VOID test_CreateThread_suspended(void)
   if(error!=WAIT_OBJECT_0) {
     TerminateThread(thread,1);
   }
+
+  suspend_count = SuspendThread(thread);
+  ok(suspend_count == -1, "SuspendThread returned %d, expected -1\n", suspend_count);
+
+  suspend_count = ResumeThread(thread);
+  ok(suspend_count == 0 ||
+     broken(suspend_count == -1), /* win9x */
+     "ResumeThread returned %d, expected 0\n", suspend_count);
+
   ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
 }
 
@@ -483,8 +550,7 @@ static VOID test_TerminateThread(void)
   HANDLE thread,access_thread,event;
   DWORD threadId,exitCode;
   event=CreateEventA(NULL,TRUE,FALSE,NULL);
-  thread = CreateThread(NULL,0,threadFunc4,
-                        (LPVOID)event, 0,&threadId);
+  thread = CreateThread(NULL,0,threadFunc4,event,0,&threadId);
   ok(thread!=NULL,"Create Thread failed\n");
 /* TerminateThread has a race condition in Wine.  If the thread is terminated
    before it starts, it leaves a process behind.  Therefore, we wait for the
@@ -627,9 +693,11 @@ static VOID test_thread_priority(void)
    SetLastError(0xdeadbeef);
    rc=pGetThreadPriorityBoost(curthread,&disabled);
    if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
-     return; /* WinME */
+   {
+      win_skip("GetThreadPriorityBoost is not implemented on WinME\n");
+      return;
+   }
 
-   todo_wine
      ok(rc!=0,"error=%d\n",GetLastError());
 
    if (pOpenThread) {
@@ -640,7 +708,7 @@ static VOID test_thread_priority(void)
      ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
      if (access_thread!=NULL) {
        obey_ar(pSetThreadPriorityBoost(access_thread,1)==0);
-       obey_ar(pGetThreadPriorityBoost(access_thread,&disabled)==0);
+       todo_wine obey_ar(pGetThreadPriorityBoost(access_thread,&disabled)==0);
        ok(CloseHandle(access_thread),"Error Closing thread handle\n");
      }
    }
@@ -654,11 +722,11 @@ static VOID test_thread_priority(void)
 
      rc = pSetThreadPriorityBoost(curthread,0);
      ok( rc != 0, "error=%d\n",GetLastError());
+   }
      rc=pGetThreadPriorityBoost(curthread,&disabled);
      ok(rc!=0 && disabled==0,
         "rc=%d error=%d disabled=%d\n",rc,GetLastError(),disabled);
    }
-}
 
 /* check the GetThreadTimes function */
 static VOID test_GetThreadTimes(void)
@@ -689,7 +757,10 @@ static VOID test_GetThreadTimes(void)
 /* GetThreadTimes should set all of the parameters passed to it */
      error=GetThreadTimes(thread,&creationTime,&exitTime,
                           &kernelTime,&userTime);
-     if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+
+     if (error == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+       win_skip("GetThreadTimes is not implemented\n");
+     else {
        ok(error!=0,"GetThreadTimes failed\n");
        ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99,
           "creationTime was invalid\n");
@@ -740,24 +811,26 @@ static VOID test_thread_processor(void)
       "SetThreadAffinityMask passed for an illegal processor\n");
 /* NOTE: This only works on WinNT/2000/XP) */
    if (pSetThreadIdealProcessor) {
-     todo_wine {
-       SetLastError(0);
+     SetLastError(0xdeadbeef);
        error=pSetThreadIdealProcessor(curthread,0);
-       if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
-         ok(error!=-1, "SetThreadIdealProcessor failed\n");
-       }
+     if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+     {
+       win_skip("SetThreadIdealProcessor is not implemented\n");
+       return;
      }
-     if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+         ok(error!=-1, "SetThreadIdealProcessor failed\n");
+
+     SetLastError(0xdeadbeef);
        error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
        ok(error==-1,
           "SetThreadIdealProcessor succeeded with an illegal processor #\n");
-       todo_wine {
+     ok(GetLastError()==ERROR_INVALID_PARAMETER,
+        "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
          error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
          ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n");
        }
      }
-   }
-}
 
 static VOID test_GetThreadExitCode(void)
 {
@@ -862,7 +935,7 @@ static DWORD CALLBACK work_function(void *p)
 
 static void test_QueueUserWorkItem(void)
 {
-    int i;
+    INT_PTR i;
     DWORD wait_result;
     DWORD before, after;
 
@@ -911,7 +984,7 @@ static void test_RegisterWaitForSingleObject(void)
 
     if (!pRegisterWaitForSingleObject || !pUnregisterWait)
     {
-        skip("RegisterWaitForSingleObject or UnregisterWait not implemented\n");
+        win_skip("RegisterWaitForSingleObject or UnregisterWait not implemented\n");
         return;
     }
 
@@ -953,6 +1026,183 @@ static void test_RegisterWaitForSingleObject(void)
     ok(ret, "UnregisterWait failed with error %d\n", GetLastError());
 }
 
+static DWORD TLS_main;
+static DWORD TLS_index0, TLS_index1;
+
+static DWORD WINAPI TLS_InheritanceProc(LPVOID p)
+{
+  /* We should NOT inherit the TLS values from our parent or from the
+     main thread.  */
+  LPVOID val;
+
+  val = TlsGetValue(TLS_main);
+  ok(val == NULL, "TLS inheritance failed\n");
+
+  val = TlsGetValue(TLS_index0);
+  ok(val == NULL, "TLS inheritance failed\n");
+
+  val = TlsGetValue(TLS_index1);
+  ok(val == NULL, "TLS inheritance failed\n");
+
+  return 0;
+}
+
+/* Basic TLS usage test.  Make sure we can create slots and the values we
+   store in them are separate among threads.  Also test TLS value
+   inheritance with TLS_InheritanceProc.  */
+static DWORD WINAPI TLS_ThreadProc(LPVOID p)
+{
+  LONG_PTR id = (LONG_PTR) p;
+  LPVOID val;
+  BOOL ret;
+
+  if (sync_threads_and_run_one(0, id))
+  {
+    TLS_index0 = TlsAlloc();
+    ok(TLS_index0 != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n");
+  }
+  resync_after_run();
+
+  if (sync_threads_and_run_one(1, id))
+  {
+    TLS_index1 = TlsAlloc();
+    ok(TLS_index1 != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n");
+
+    /* Slot indices should be different even if created in different
+       threads.  */
+    ok(TLS_index0 != TLS_index1, "TlsAlloc failed\n");
+
+    /* Both slots should be initialized to NULL */
+    val = TlsGetValue(TLS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == NULL, "TLS slot not initialized correctly\n");
+
+    val = TlsGetValue(TLS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == NULL, "TLS slot not initialized correctly\n");
+  }
+  resync_after_run();
+
+  if (sync_threads_and_run_one(0, id))
+  {
+    val = TlsGetValue(TLS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == NULL, "TLS slot not initialized correctly\n");
+
+    val = TlsGetValue(TLS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == NULL, "TLS slot not initialized correctly\n");
+
+    ret = TlsSetValue(TLS_index0, (LPVOID) 1);
+    ok(ret, "TlsSetValue failed\n");
+
+    ret = TlsSetValue(TLS_index1, (LPVOID) 2);
+    ok(ret, "TlsSetValue failed\n");
+
+    val = TlsGetValue(TLS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == (LPVOID) 1, "TLS slot not initialized correctly\n");
+
+    val = TlsGetValue(TLS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == (LPVOID) 2, "TLS slot not initialized correctly\n");
+  }
+  resync_after_run();
+
+  if (sync_threads_and_run_one(1, id))
+  {
+    val = TlsGetValue(TLS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == NULL, "TLS slot not initialized correctly\n");
+
+    val = TlsGetValue(TLS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == NULL, "TLS slot not initialized correctly\n");
+
+    ret = TlsSetValue(TLS_index0, (LPVOID) 3);
+    ok(ret, "TlsSetValue failed\n");
+
+    ret = TlsSetValue(TLS_index1, (LPVOID) 4);
+    ok(ret, "TlsSetValue failed\n");
+
+    val = TlsGetValue(TLS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == (LPVOID) 3, "TLS slot not initialized correctly\n");
+
+    val = TlsGetValue(TLS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == (LPVOID) 4, "TLS slot not initialized correctly\n");
+  }
+  resync_after_run();
+
+  if (sync_threads_and_run_one(0, id))
+  {
+    HANDLE thread;
+    DWORD waitret, tid;
+
+    val = TlsGetValue(TLS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == (LPVOID) 1, "TLS slot not initialized correctly\n");
+
+    val = TlsGetValue(TLS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
+    ok(val == (LPVOID) 2, "TLS slot not initialized correctly\n");
+
+    thread = CreateThread(NULL, 0, TLS_InheritanceProc, 0, 0, &tid);
+    ok(thread != NULL, "CreateThread failed\n");
+    waitret = WaitForSingleObject(thread, 60000);
+    ok(waitret == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+    CloseHandle(thread);
+
+    ret = TlsFree(TLS_index0);
+    ok(ret, "TlsFree failed\n");
+  }
+  resync_after_run();
+
+  if (sync_threads_and_run_one(1, id))
+  {
+    ret = TlsFree(TLS_index1);
+    ok(ret, "TlsFree failed\n");
+  }
+  resync_after_run();
+
+  return 0;
+}
+
+static void test_TLS(void)
+{
+  HANDLE threads[2];
+  LONG_PTR i;
+  DWORD ret;
+  BOOL suc;
+
+  init_thread_sync_helpers();
+
+  /* Allocate a TLS slot in the main thread to test for inheritance.  */
+  TLS_main = TlsAlloc();
+  ok(TLS_main != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n");
+  suc = TlsSetValue(TLS_main, (LPVOID) 4114);
+  ok(suc, "TlsSetValue failed\n");
+
+  for (i = 0; i < 2; ++i)
+  {
+    DWORD tid;
+
+    threads[i] = CreateThread(NULL, 0, TLS_ThreadProc, (LPVOID) i, 0, &tid);
+    ok(threads[i] != NULL, "CreateThread failed\n");
+  }
+
+  ret = WaitForMultipleObjects(2, threads, TRUE, 60000);
+  ok(ret == WAIT_OBJECT_0 || ret == WAIT_OBJECT_0+1 /* nt4 */, "WaitForMultipleObjects failed %u\n",ret);
+
+  for (i = 0; i < 2; ++i)
+    CloseHandle(threads[i]);
+
+  suc = TlsFree(TLS_main);
+  ok(suc, "TlsFree failed\n");
+  cleanup_thread_sync_helpers();
+}
+
 START_TEST(thread)
 {
    HINSTANCE lib;
@@ -986,7 +1236,8 @@ START_TEST(thread)
        while (1)
        {
            HANDLE hThread;
-           hThread = CreateThread(NULL, 0, threadFunc2, NULL, 0, NULL);
+           DWORD tid;
+           hThread = CreateThread(NULL, 0, threadFunc2, NULL, 0, &tid);
            ok(hThread != NULL, "CreateThread failed, error %u\n",
               GetLastError());
            ok(WaitForSingleObject(hThread, 200) == WAIT_OBJECT_0,
@@ -1012,4 +1263,5 @@ START_TEST(thread)
 #endif
    test_QueueUserWorkItem();
    test_RegisterWaitForSingleObject();
+   test_TLS();
 }