summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.cpp')
-rw-r--r--src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.cpp226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.cpp b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.cpp
new file mode 100644
index 0000000000..47659a1c18
--- /dev/null
+++ b/src/pal/tests/palsuite/threading/CriticalSectionFunctions/test2/test2.cpp
@@ -0,0 +1,226 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Source: CriticalSectionFunctions/test2/test2.c
+**
+** Purpose: Test that we are able to nest critical section calls.
+** The initial thread makes a call to EnterCriticalSection once,
+** blocking on a CRITICAL_SECTION object and creates a new thread.
+** The newly created thread blocks on the same CRITICAL_SECTION object.
+** The first thread now makes a call to LeaveCriticalSection.
+** Test to see that the new thread doesn't get unblocked.
+**
+** Dependencies: CreateThread
+** InitializeCriticalSection
+** EnterCriticalSection
+** LeaveCriticalSection
+** DeleteCriticalSection
+** WaitForSingleObject
+**
+
+**
+**=========================================================*/
+
+#include <palsuite.h>
+
+CRITICAL_SECTION CriticalSection;
+
+volatile BOOL t0_tflag = FAIL; /* thread 0 timeout flag */
+volatile BOOL t1_aflag = FAIL; /* thread 1 access flag */
+volatile BOOL t1_cflag = FAIL; /* thread 1 critical section flag */
+volatile BOOL bTestResult = FAIL;
+
+DWORD PALAPI Thread(LPVOID lpParam)
+{
+ t1_aflag = PASS;
+ EnterCriticalSection(&CriticalSection);
+ t1_cflag = PASS;
+ LeaveCriticalSection(&CriticalSection);
+ return 0;
+}
+
+int __cdecl main (int argc, char **argv)
+{
+ HANDLE hThread;
+ DWORD dwThreadId;
+ DWORD dwRet;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return (bTestResult);
+ }
+
+ /*
+ * Create critical section object and enter it
+ */
+ InitializeCriticalSection ( &CriticalSection );
+ EnterCriticalSection(&CriticalSection);
+
+ /*
+ * Create a suspended thread
+ */
+ hThread = CreateThread(NULL,
+ 0,
+ &Thread,
+ (LPVOID) NULL,
+ CREATE_SUSPENDED,
+ &dwThreadId);
+
+ if (hThread == NULL)
+ {
+ Trace("PALSUITE ERROR: CreateThread call failed. GetLastError "
+ "returned %d.\n", GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ EnterCriticalSection(&CriticalSection);
+ /*
+ * Set priority of the thread to greater than that of the currently
+ * running thread so it is guaranteed to run.
+ */
+ dwRet = (DWORD) SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL);
+
+ if (0 == dwRet)
+ {
+ Trace("PALSUITE ERROR: SetThreadPriority (%p, %d) call failed.\n"
+ "GetLastError returned %d.\n", hThread,
+ THREAD_PRIORITY_NORMAL, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+
+ dwRet = ResumeThread(hThread);
+
+ if (-1 == dwRet)
+ {
+ Trace("PALSUITE ERROR: ResumeThread(%p) call failed.\nGetLastError "
+ "returned %d.\n", hThread, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ /*
+ * Sleep until we know the thread has been invoked. This sleep in
+ * combination with the higher priority of the other thread should
+ * guarantee both threads block on the critical section.
+ */
+ while (t1_aflag == FAIL)
+ {
+ Sleep(1);
+ }
+
+ LeaveCriticalSection(&CriticalSection);
+
+ switch ((WaitForSingleObject(
+ hThread,
+ 10000))) /* Wait 10 seconds */
+ {
+ case WAIT_OBJECT_0:
+ /* Object (thread) is signaled */
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_TIMEOUT ('%d'), instead it returned "
+ "WAIT_OBJECT_0 ('%d').\nA nested LeaveCriticalSection(%p) "
+ "call released both threads that were waiting on it!\n",
+ hThread, 10000, WAIT_TIMEOUT, WAIT_OBJECT_0, &CriticalSection);
+ break;
+ case WAIT_ABANDONED:
+ /*
+ * Object was mutex object whose owning
+ * thread has terminated. Shouldn't occur.
+ */
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_TIMEOUT ('%d'), instead it returned "
+ "WAIT_ABANDONED ('%d').\nGetLastError returned '%d'\n",
+ hThread, 10000, WAIT_TIMEOUT, WAIT_ABANDONED, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ break;
+ case WAIT_FAILED: /* WaitForSingleObject function failed */
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned\nWAIT_TIMEOUT ('%d'), instead it returned "
+ "WAIT_FAILED ('%d').\nGetLastError returned '%d'\n",
+ hThread, 10000, WAIT_TIMEOUT, WAIT_FAILED, GetLastError());
+ LeaveCriticalSection(&CriticalSection);
+ CloseHandle(hThread);
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ break;
+ case WAIT_TIMEOUT:
+ /*
+ * We expect this thread to timeout waiting for the
+ * critical section object to become available.
+ */
+ t0_tflag = PASS;
+ break;
+ }
+
+ LeaveCriticalSection(&CriticalSection);
+
+ if (WAIT_OBJECT_0 != WaitForSingleObject (hThread, 10000))
+ {
+ if (0 == CloseHandle(hThread))
+ {
+ Trace("PALSUITE ERROR: CloseHandle(%p) call failed.\n"
+ "WaitForSingleObject(%p,%d) should have returned "
+ "WAIT_OBJECT_0 ('%d').\nBoth calls failed. "
+ "Deleted CRITICAL_SECTION object which likely means\n"
+ "thread %p is now in an undefined state. GetLastError "
+ "returned '%d'.\n", hThread, hThread, 10000, WAIT_OBJECT_0,
+ hThread, GetLastError());
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ else
+ {
+ Trace("PALSUITE ERROR: WaitForSingleObject(%p,%d) should have "
+ "returned WAIT_OBJECT_0 ('%d').\n GetLastError returned "
+ "'%d'.\n", hThread, hThread, 10000, WAIT_OBJECT_0,
+ hThread, GetLastError());
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+ }
+ }
+
+ if (0 == CloseHandle(hThread))
+ {
+ Trace("PALSUITE ERROR: CloseHandle(%p) call failed.\n"
+ "Deleted CRITICAL_SECTION object which likely means\n"
+ "thread %p is now in an undefined state. GetLastError "
+ "returned '%d'.\n", hThread, hThread, GetLastError());
+ DeleteCriticalSection(&CriticalSection);
+ Fail("");
+
+ }
+ DeleteCriticalSection(&CriticalSection);
+ /*
+ * Ensure both thread 0 experienced a wait timeout and thread 1
+ * accessed the critical section or fail the test, otherwise pass it.
+ */
+ if ((t0_tflag == FAIL) || (t1_cflag == FAIL))
+ {
+ Trace("PALSUITE ERROR: Thread 0 returned %d when %d was expected.\n"
+ "Thread 1 returned %d when %d was expected.\n", t0_tflag,
+ PASS, t1_cflag, PASS);
+ bTestResult=FAIL;
+ }
+ else
+ {
+ bTestResult=PASS;
+ }
+
+ PAL_TerminateEx(bTestResult);
+ return (bTestResult);
+}