summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/composite/object_management
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/tests/palsuite/composite/object_management')
-rw-r--r--src/pal/tests/palsuite/composite/object_management/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/composite/object_management/event/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/composite/object_management/event/nonshared/CMakeLists.txt21
-rw-r--r--src/pal/tests/palsuite/composite/object_management/event/nonshared/event.c358
-rw-r--r--src/pal/tests/palsuite/composite/object_management/event/nonshared/main.c228
-rw-r--r--src/pal/tests/palsuite/composite/object_management/event/shared/CMakeLists.txt21
-rw-r--r--src/pal/tests/palsuite/composite/object_management/event/shared/event.c373
-rw-r--r--src/pal/tests/palsuite/composite/object_management/event/shared/main.c265
-rw-r--r--src/pal/tests/palsuite/composite/object_management/mutex/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/composite/object_management/mutex/nonshared/CMakeLists.txt21
-rw-r--r--src/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.c230
-rw-r--r--src/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.c340
-rw-r--r--src/pal/tests/palsuite/composite/object_management/mutex/shared/CMakeLists.txt21
-rw-r--r--src/pal/tests/palsuite/composite/object_management/mutex/shared/main.c265
-rw-r--r--src/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.c354
-rw-r--r--src/pal/tests/palsuite/composite/object_management/readme.txt29
-rw-r--r--src/pal/tests/palsuite/composite/object_management/semaphore/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/CMakeLists.txt21
-rw-r--r--src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/main.c228
-rw-r--r--src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/semaphore.c342
-rw-r--r--src/pal/tests/palsuite/composite/object_management/semaphore/shared/CMakeLists.txt21
-rw-r--r--src/pal/tests/palsuite/composite/object_management/semaphore/shared/main.c278
-rw-r--r--src/pal/tests/palsuite/composite/object_management/semaphore/shared/semaphore.c351
23 files changed, 3788 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/composite/object_management/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/CMakeLists.txt
new file mode 100644
index 0000000000..5fd88b0046
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(event)
+add_subdirectory(mutex)
+add_subdirectory(semaphore)
+
diff --git a/src/pal/tests/palsuite/composite/object_management/event/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/event/CMakeLists.txt
new file mode 100644
index 0000000000..2534564f95
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/event/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(nonshared)
+add_subdirectory(shared)
+
diff --git a/src/pal/tests/palsuite/composite/object_management/event/nonshared/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/event/nonshared/CMakeLists.txt
new file mode 100644
index 0000000000..c6c00377e1
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/event/nonshared/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ event.c
+ main.c
+)
+
+add_executable(paltest_event_nonshared
+ ${SOURCES}
+)
+
+add_dependencies(paltest_event_nonshared coreclrpal)
+
+target_link_libraries(paltest_event_nonshared
+ pthread
+ rt
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/composite/object_management/event/nonshared/event.c b/src/pal/tests/palsuite/composite/object_management/event/nonshared/event.c
new file mode 100644
index 0000000000..69ad9a30e3
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/event/nonshared/event.c
@@ -0,0 +1,358 @@
+// 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 Code: main.c and event.c
+** main.c creates process and waits for all processes to get over
+** event.c creates a event and then calls threads which will contend for the event
+**
+** This test is for Object Management Test case for event where Object type is shareable.
+** Algorithm
+** o Main Process Creates OBJECT_TYPE Object
+** o Create PROCESS_COUNT processes aware of the Shared Object
+**
+** Author: ShamitP
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resultbuffer.h"
+#include "resulttime.h"
+
+#define TIMEOUT 5000
+/* Test Input Variables */
+unsigned int USE_PROCESS_COUNT = 0;
+unsigned int THREAD_COUNT = 0;
+unsigned int REPEAT_COUNT = 0;
+unsigned int RELATION_ID= 0;
+
+/* Event variables */
+//unsigned long lInitialCount = 1; /* Signaled */
+//unsigned long lMaximumCount = 1; /* Maximum value of 1 */
+
+/* Capture statistics at per thread basis */
+struct statistics{
+ unsigned int processId;
+ unsigned int operationsFailed;
+ unsigned int operationsPassed;
+ unsigned int operationsTotal;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+struct ProcessStats{
+ unsigned int processId;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+HANDLE StartTestsEvHandle = NULL;
+HANDLE hEventHandle = NULL;
+
+/* Results Buffer */
+ResultBuffer *resultBuffer = NULL;
+
+int testStatus;
+
+const char sTmpEventName[MAX_PATH_FNAME] = "StartTestEvent";
+
+void PALAPI Run_Thread(LPVOID lpParam);
+
+int GetParameters( int argc, char **argv)
+{
+ if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management Event Test\n");
+ printf("Usage:\n");
+ printf("Event\n\t[USE_PROCESS_COUNT [greater than 1] \n");
+ printf("\t[THREAD_COUNT [greater than 1] \n");
+ printf("\t[REPEAT_COUNT [greater than 1]\n");
+ printf("\t[RELATION_ID [greater than or Equal to 1]\n");
+
+ return -1;
+ }
+
+ // Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]);
+
+ USE_PROCESS_COUNT = atoi(argv[1]);
+ if( USE_PROCESS_COUNT < 0)
+ {
+ printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hThread[MAXIMUM_WAIT_OBJECTS];
+ DWORD threadId[MAXIMUM_WAIT_OBJECTS];
+ int returnCode = 0;
+
+ DWORD dwParam = 0;
+
+ /* Variables to capture the file name and the file pointer at thread level*/
+ char fileName[MAX_LONGPATH];
+ FILE *pFile = NULL;
+ struct statistics* buffer = NULL;
+ int statisticsSize = 0;
+
+ /* Variables to capture the file name and the file pointer at process level*/
+ char processFileName[MAX_LONGPATH];
+ FILE *pProcessFile = NULL;
+ struct ProcessStats processStats;
+ DWORD dwStartTime;
+
+ testStatus = PASS;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+// Trace("Process created, value of process count is [%d]\n", USE_PROCESS_COUNT);
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ processStats.relationId = RELATION_ID;
+ processStats.processId = USE_PROCESS_COUNT;
+
+ _snprintf(processFileName, MAX_LONGPATH, "%d_process_event_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pProcessFile = fopen(processFileName, "w+");
+ if(pProcessFile == NULL)
+ {
+ Fail("Error in opening process File file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+
+ statisticsSize = sizeof(struct statistics);
+
+ _snprintf(fileName, MAX_LONGPATH, "%d_thread_event_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pFile = fopen(fileName, "w+");
+
+ if(pFile == NULL)
+ {
+ Fail("Error in opening thread File for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+ // For each thread we will log operations failed (int), passed (int), total (int)
+ // and number of ticks (DWORD) for the operations
+ resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize);
+
+ StartTestsEvHandle = CreateEvent(
+ NULL, /* lpEventAttributes*/
+ TRUE, /* bManualReset */
+ FALSE, /* bInitialState */
+ NULL /* name of Event */
+ );
+
+ if( StartTestsEvHandle == NULL )
+ {
+ Fail("Error:%d: Unexpected failure "
+ "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT );
+
+ }
+
+ /* Create StartTest Event */
+
+ hEventHandle = CreateEvent(
+ NULL, /* lpEventAttributes, inheritable to child processes*/
+ TRUE, /* bAutomaticReset */
+ TRUE, /* bInitialState */
+ NULL
+ );
+
+ if( hEventHandle == NULL)
+ {
+ Fail("Unable to create Event handle for process id [%d], returned error [%d]\n", i, GetLastError());
+ }
+ /* We already assume that the Event was created previously*/
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ }
+
+ if (!SetEvent(StartTestsEvHandle))
+ {
+ Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+ /* Test running */
+ returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE);
+
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError());
+ testStatus = FAIL;
+ }
+
+ processStats.operationTime = GetTimeDiff(dwStartTime);
+
+ /* Write to a file*/
+ if(pFile!= NULL)
+ {
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ buffer = (struct statistics *)resultBuffer->getResultBuffer(i);
+ returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId );
+ //Trace("Iteration %d over\n", i);
+
+ }
+ }
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile\n");
+ testStatus = FAIL;
+ }
+
+ fprintf(pProcessFile, "%d,%d,%d\n", USE_PROCESS_COUNT, processStats.operationTime, processStats.relationId );
+ if(fclose(pProcessFile))
+ {
+ Trace("Error: fclose failed for pProcessFile at Process %d\n", USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ /* Logging for the test case over, clean up the handles */
+
+// Trace("Test Thread %d done\n", USE_PROCESS_COUNT);
+ /* Clean Up */
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ if(!CloseHandle(hThread[i]) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i);
+ testStatus = FAIL;
+ }
+ }
+
+ if(!CloseHandle(StartTestsEvHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ if(!CloseHandle(hEventHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hEventHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ PAL_Terminate();
+ return testStatus;
+
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ unsigned int i = 0;
+ DWORD dwWaitResult;
+
+ struct statistics stats;
+ DWORD dwStartTime;
+
+ stats.relationId = RELATION_ID;
+ stats.processId = USE_PROCESS_COUNT;
+ stats.operationsFailed = 0;
+ stats.operationsPassed = 0;
+ stats.operationsTotal = 0;
+ stats.operationTime = 0;
+
+ int Id=(int)lpParam;
+
+ dwWaitResult = WaitForSingleObject(
+ StartTestsEvHandle, // handle to start test handle
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+ Fail("Error while waiting for StartTest Event@ thread %d, RC is %d, Error is %d\n", Id, dwWaitResult, GetLastError());
+ }
+
+ dwStartTime = GetTickCount();
+
+ for( i = 0; i < REPEAT_COUNT; i++ )
+ {
+ dwWaitResult = WaitForSingleObject(
+ hEventHandle, // handle to Event
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+// Trace("Error while waiting for onject @ thread %d, # iter %d, RC is %d, Error is %d\n", Id, i, dwWaitResult, GetLastError());
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ testStatus = FAIL;
+ continue;
+ }
+
+ if (! SetEvent(hEventHandle))
+ {
+ // Deal with error.
+// Trace("Error while setting Event @ thread %d # iter %d\n", Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ // Do we need to have while true loop to attempt to set event?
+ testStatus = FAIL;
+ continue;
+ }
+
+ stats.operationsTotal += 1;
+ stats.operationsPassed += 1;
+// Trace("Successs while setting Event @ iteration %d -> thread %d -> Process %d\n", i, Id, USE_PROCESS_COUNT);
+
+ }
+
+ stats.operationTime = GetTimeDiff(dwStartTime);
+ if(resultBuffer->LogResult(Id, (char *)&stats))
+ {
+ Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT);
+ }
+ //Trace("Thread %d over for process %d\n", Id, USE_PROCESS_COUNT);
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/event/nonshared/main.c b/src/pal/tests/palsuite/composite/object_management/event/nonshared/main.c
new file mode 100644
index 0000000000..7b61e91737
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/event/nonshared/main.c
@@ -0,0 +1,228 @@
+// 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 Code: main.c and event.c
+** main.c creates process and waits for all processes to get over
+** event.c creates a event and then calls threads which will contend for the event
+**
+** This test is for Object Management Test case for event where Object type is not shareable.
+** Algorithm
+** o Create PROCESS_COUNT processes.
+** o Main Thread of each process creates OBJECT_TYPE Object
+**
+** Author: ShamitP
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resulttime.h"
+
+/* Test Input Variables */
+unsigned int PROCESS_COUNT = 10;
+unsigned int THREAD_COUNT = 20;
+unsigned int REPEAT_COUNT = 20000;
+unsigned int RELATION_ID = 1001;
+
+
+struct TestStats{
+ DWORD operationTime;
+ unsigned int relationId;
+ unsigned int processCount;
+ unsigned int threadCount;
+ unsigned int repeatCount;
+ char* buildNumber;
+
+};
+
+
+int GetParameters( int argc, char **argv)
+{
+ if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management event Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[PROCESS_COUNT [greater than 1] \n");
+ printf("\t[THREAD_COUNT [greater than 1] \n");
+ printf("\t[REPEAT_COUNT [greater than 1]\n");
+ printf("\t[RELATION_ID [greater than or Equal to 1]\n");
+ return -1;
+ }
+
+ PROCESS_COUNT = atoi(argv[1]);
+ if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hProcess[MAXIMUM_WAIT_OBJECTS];
+
+ STARTUPINFO si[MAXIMUM_WAIT_OBJECTS];
+ PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS];
+
+ char lpCommandLine[MAX_LONGPATH] = "";
+ const char *ObjName = "Event";
+
+ int returnCode = 0;
+ DWORD processReturnCode = 0;
+ int testReturnCode = PASS;
+
+ char fileName[MAX_LONGPATH];
+ FILE *pFile = NULL;
+ DWORD dwStartTime = 0;
+ struct TestStats testStats;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ testStats.relationId = RELATION_ID;
+ testStats.processCount = PROCESS_COUNT;
+ testStats.threadCount = THREAD_COUNT;
+ testStats.repeatCount = REPEAT_COUNT;
+ testStats.buildNumber = getBuildNumber();
+
+
+ _snprintf(fileName, MAX_LONGPATH, "main_event_%d_.txt", RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening main file for write\n");
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+
+ ZeroMemory( lpCommandLine, MAX_PATH );
+ if ( _snprintf( lpCommandLine, MAX_LONGPATH-1, "event %d %d %d %d", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID) < 0 )
+ {
+ Fail ("Error: Insufficient Event name string length for %s for iteration [%d]\n", ObjName, i);
+ }
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi[i], sizeof(pi[i]) );
+ ZeroMemory ( &si[i], sizeof(si[i]) );
+
+ /* Set the process flags and standard io handles */
+ si[i].cb = sizeof(si[i]);
+
+ //Create Process
+ if(!CreateProcess( NULL, /* lpApplicationName*/
+ lpCommandLine, /* lpCommandLine */
+ NULL, /* lpProcessAttributes */
+ NULL, /* lpThreadAttributes */
+ TRUE, /* bInheritHandles */
+ 0, /* dwCreationFlags, */
+ NULL, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ &si[i], /* lpStartupInfo */
+ &pi[i] /* lpProcessInformation */
+ ))
+ {
+ Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError());
+ }
+ else
+ {
+ hProcess[i] = pi[i].hProcess;
+ //Trace("Process created for [%d]\n", i);
+
+ }
+
+ }
+
+ returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) )
+ {
+ Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n",
+ i, GetLastError() );
+
+ testReturnCode = FAIL;
+ }
+
+ if(processReturnCode == FAIL)
+ {
+ Trace( "Process [%d] failed and returned FAIL\n", i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hThread))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hProcess) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+ }
+
+ testStats.operationTime = GetTimeDiff(dwStartTime);
+ fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber);
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile\n");
+ testReturnCode = FAIL;
+ }
+
+ if( testReturnCode == PASS)
+ {
+ Trace("Test Passed\n");
+ }
+ else
+ {
+ Trace("Test Failed\n");
+ }
+ PAL_Terminate();
+ return testReturnCode;
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/event/shared/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/event/shared/CMakeLists.txt
new file mode 100644
index 0000000000..d326e3a42b
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/event/shared/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ event.c
+ main.c
+)
+
+add_executable(paltest_event_shared
+ ${SOURCES}
+)
+
+add_dependencies(paltest_event_shared coreclrpal)
+
+target_link_libraries(paltest_event_shared
+ pthread
+ rt
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/composite/object_management/event/shared/event.c b/src/pal/tests/palsuite/composite/object_management/event/shared/event.c
new file mode 100644
index 0000000000..83d5fce27e
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/event/shared/event.c
@@ -0,0 +1,373 @@
+// 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 Code: main.c and event.c
+** main.c creates process and waits for all processes to get over
+** event.c creates a event and then calls threads which will contend for the event
+**
+** This test is for Object Management Test case for event where Object type is shareable.
+** Algorithm
+** o Main Process Creates OBJECT_TYPE Object
+** o Create PROCESS_COUNT processes aware of the Shared Object
+**
+** Author: ShamitP
+** Author: ShamitP
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resultbuffer.h"
+#include "resulttime.h"
+
+#define TIMEOUT 5000
+/* Test Input Variables */
+unsigned int USE_PROCESS_COUNT = 0;
+unsigned int THREAD_COUNT = 0;
+unsigned int REPEAT_COUNT = 0;
+unsigned int RELATION_ID = 0;
+
+/* Capture statistics at per thread basis */
+struct statistics{
+ unsigned int processId;
+ unsigned int operationsFailed;
+ unsigned int operationsPassed;
+ unsigned int operationsTotal;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+struct ProcessStats{
+ unsigned int processId;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+HANDLE StartTestsEvHandle = NULL;
+HANDLE hEventHandle = NULL;
+
+/* Results Buffer */
+ResultBuffer *resultBuffer= NULL;
+
+int testStatus;
+
+const char sTmpEventName[MAX_PATH] = "StartTestEvent";
+char objectSuffix[MAX_PATH];
+
+void PALAPI Run_Thread(LPVOID lpParam);
+
+int GetParameters( int argc, char **argv)
+{
+ if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management event Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[USE_PROCESS_COUNT (greater than 1)] \n");
+ printf("\t[THREAD_COUNT (greater than 1)] \n");
+ printf("\t[REPEAT_COUNT (greater than 1)]\n");
+ printf("\t[RELATION_ID [greater than or equal to 1]\n");
+ printf("\t[Object Name Suffix]\n");
+
+ return -1;
+ }
+
+ // Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]);
+
+ USE_PROCESS_COUNT = atoi(argv[1]);
+ if( USE_PROCESS_COUNT < 0)
+ {
+ printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+ if(argc == 6)
+ {
+ strncpy(objectSuffix, argv[5], MAX_PATH-1);
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hThread[MAXIMUM_WAIT_OBJECTS];
+ DWORD threadId[MAXIMUM_WAIT_OBJECTS];
+
+ WCHAR *wcObjName = NULL;
+
+ char ObjName[MAX_PATH] = "SHARED_EVENT";
+ DWORD dwParam = 0;
+
+ int returnCode = 0;
+
+ /* Variables to capture the file name and the file pointer at thread level*/
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ struct statistics* buffer = NULL;
+ int statisticsSize = 0;
+
+ /* Variables to capture the file name and the file pointer at process level*/
+ char processFileName[MAX_PATH];
+ FILE *pProcessFile = NULL;
+ struct ProcessStats processStats;
+ DWORD dwStartTime;
+
+ testStatus = PASS;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ZeroMemory( objectSuffix, MAX_PATH );
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+// Trace("Process created, value of process count is [%d]\n", USE_PROCESS_COUNT);
+
+ if(argc == 5)
+ {
+ strncat(ObjName, objectSuffix, MAX_PATH - (sizeof(ObjName) + 1) );
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ processStats.relationId = RELATION_ID;
+ processStats.processId = USE_PROCESS_COUNT;
+
+ _snprintf(processFileName, MAX_PATH, "%d_process_event_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pProcessFile = fopen(processFileName, "w+");
+ if(pProcessFile == NULL)
+ {
+ Fail("Error:%d: in opening Process File for write for process [%d]\n", GetLastError(), USE_PROCESS_COUNT);
+ }
+
+ statisticsSize = sizeof(struct statistics);
+
+ _snprintf(fileName, MAX_PATH, "%d_thread_event_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pFile = fopen(fileName, "w+");
+
+ if(pFile == NULL)
+ {
+ Fail("Error:%d: in opening thread file for write for process [%d]\n", GetLastError(), USE_PROCESS_COUNT);
+ }
+ // For each thread we will log operations failed (int), passed (int), total (int)
+ // and number of ticks (DWORD) for the operations
+ resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize);
+
+ wcObjName = convert(ObjName);
+
+ StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/
+ TRUE, /* bManualReset */
+ FALSE, /* bInitialState */
+ NULL); /* name of Event */
+
+ if( StartTestsEvHandle == NULL )
+ {
+ Fail("Error:%d: Unexpected failure "
+ "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT );
+
+ }
+
+ /* Create StartTest Event */
+
+ hEventHandle = OpenEventW(
+ EVENT_ALL_ACCESS, /* lpEventAttributes, inheritable to child processes*/
+ FALSE, /* bAutomaticReset */
+ wcObjName
+ );
+
+ if( hEventHandle == NULL)
+ {
+ Fail("Unable to create Event handle for process id [%d], returned error [%d]\n", i, GetLastError());
+ }
+ /* We already assume that the Event was created previously*/
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ }
+
+ if (!SetEvent(StartTestsEvHandle))
+ {
+ Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ /* Test running */
+ returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE);
+
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError());
+ testStatus = FAIL;
+ }
+
+ processStats.operationTime = GetTimeDiff(dwStartTime);
+
+ /* Write to a file*/
+ if(pFile!= NULL)
+ {
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ buffer = (struct statistics *)resultBuffer->getResultBuffer(i);
+ returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId );
+// Trace("Iteration %d over\n", i);
+
+ }
+ }
+
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile at Process %d\n", USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ fprintf(pProcessFile, "%d,%d,%d\n", USE_PROCESS_COUNT, processStats.operationTime, processStats.relationId );
+ if(fclose(pProcessFile))
+ {
+ Trace("Error: fclose failed for pProcessFile at Process %d\n", USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+ /* Logging for the test case over, clean up the handles */
+
+// Trace("Test Thread %d done\n", USE_PROCESS_COUNT);
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ if(!CloseHandle(hThread[i]) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i);
+ testStatus = FAIL;
+ }
+ }
+
+ if(!CloseHandle(StartTestsEvHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ if(!CloseHandle(hEventHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hEventHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ free(wcObjName);
+ PAL_Terminate();
+ return testStatus;
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ unsigned int i = 0;
+ DWORD dwWaitResult;
+
+ struct statistics stats;
+ DWORD dwStartTime;
+
+ stats.relationId = RELATION_ID;
+ stats.processId = USE_PROCESS_COUNT;
+ stats.operationsFailed = 0;
+ stats.operationsPassed = 0;
+ stats.operationsTotal = 0;
+ stats.operationTime = 0;
+
+ int Id=(int)lpParam;
+
+ dwWaitResult = WaitForSingleObject(
+ StartTestsEvHandle, // handle to start test handle
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+ Trace("Error:%d: while waiting for StartTest Event@ thread %d\n", GetLastError(), Id);
+ testStatus = FAIL;
+ }
+
+ dwStartTime = GetTickCount();
+
+ for( i = 0; i < REPEAT_COUNT; i++ )
+ {
+ dwWaitResult = WaitForSingleObject(
+ hEventHandle, // handle to Event
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+ //Trace("Error:%d: while waiting for onject @ thread %d, # iter %d\n", GetLastError(), Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ testStatus = FAIL;
+ continue;
+ }
+
+ if (! SetEvent(hEventHandle))
+ {
+ // Deal with error.
+// Trace("Error while setting Event @ thread %d # iter %d\n", Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ // do we need to have while true loop to attempt to set event...?
+ testStatus = FAIL;
+ continue;
+ }
+
+ stats.operationsTotal += 1;
+ stats.operationsPassed += 1;
+ // Trace("Successs while setting Event @ iteration %d -> thread %d -> Process %d for handle %d\n", i, Id, USE_PROCESS_COUNT, hEventHandle);
+
+ }
+
+ stats.operationTime = GetTimeDiff(dwStartTime);
+ //Trace("OPeration time is %d", stats.operationTime );
+ if(resultBuffer->LogResult(Id, (char *)&stats))
+ {
+ Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT);
+ }
+ //Trace("Thread %d over for process %d\n", Id, USE_PROCESS_COUNT);
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/event/shared/main.c b/src/pal/tests/palsuite/composite/object_management/event/shared/main.c
new file mode 100644
index 0000000000..c4a4067b5d
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/event/shared/main.c
@@ -0,0 +1,265 @@
+// 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 Code: main.c and event.c
+** main.c creates process and waits for all processes to get over
+** event.c creates a event and then calls threads which will contend for the event
+**
+** This test is for Object Management Test case for event where Object type is shareable.
+** Algorithm
+** o Main Process Creates OBJECT_TYPE Object
+** o Create PROCESS_COUNT processes aware of the Shared Object
+**
+** Author: ShamitP
+**
+**
+**============================================================
+*/
+#include <palsuite.h>
+#include "resulttime.h"
+
+/* Test Input Variables */
+unsigned int PROCESS_COUNT = 2;
+unsigned int THREAD_COUNT = 20;
+unsigned int REPEAT_COUNT = 200;
+unsigned int RELATION_ID = 1001;
+
+
+char objectSuffix[MAX_PATH_FNAME];
+
+struct TestStats{
+ DWORD operationTime;
+ unsigned int relationId;
+ unsigned int processCount;
+ unsigned int threadCount;
+ unsigned int repeatCount;
+ char* buildNumber;
+
+};
+
+int GetParameters( int argc, char **argv)
+{
+ if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management event Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[PROCESS_COUNT (greater than 1)] \n");
+ printf("\t[THREAD_COUNT (greater than 1)] \n");
+ printf("\t[REPEAT_COUNT (greater than 1)]\n");
+ printf("\t[RELATION_ID [greater than or equal to 1]\n");
+ printf("\t[Object Name Suffix]\n");
+ return -1;
+ }
+
+ PROCESS_COUNT = atoi(argv[1]);
+ if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+
+ if(argc == 6)
+ {
+ strncpy(objectSuffix, argv[5], MAX_PATH_FNAME-1);
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hProcess[MAXIMUM_WAIT_OBJECTS];
+ HANDLE hEventHandle;
+
+ STARTUPINFO si[MAXIMUM_WAIT_OBJECTS];
+ PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS];
+
+ char lpCommandLine[MAX_LONGPATH] = "";
+ char ObjName[MAX_PATH_FNAME] = "SHARED_EVENT";
+
+ int returnCode = 0;
+ DWORD processReturnCode = 0;
+ int testReturnCode = PASS;
+
+ char fileName[MAX_PATH_FNAME];
+ FILE *pFile = NULL;
+ DWORD dwStartTime;
+ struct TestStats testStats;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ZeroMemory( objectSuffix, MAX_PATH_FNAME );
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+
+ if(argc == 5)
+ {
+ strncat(ObjName, objectSuffix, MAX_PATH_FNAME - (sizeof(ObjName) + 1) );
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ testStats.relationId = RELATION_ID;
+ testStats.processCount = PROCESS_COUNT;
+ testStats.threadCount = THREAD_COUNT;
+ testStats.repeatCount = REPEAT_COUNT;
+ testStats.buildNumber = getBuildNumber();
+
+
+ _snprintf(fileName, MAX_PATH_FNAME, "main_event_%d_.txt", RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening main file for write\n");
+ }
+
+ hEventHandle = CreateEvent(
+ NULL, /* lpEventAttributes, inheritable to child processes*/
+ TRUE, /* bAutomaticReset */
+ TRUE, /* bInitialState */
+ ObjName
+ );
+
+ if( hEventHandle == NULL)
+ {
+ Fail("Unable to create Event handle, returned error [%d]\n", GetLastError());
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+
+ ZeroMemory( lpCommandLine, MAX_PATH_FNAME );
+ if ( _snprintf( lpCommandLine, MAX_PATH_FNAME-1, "event %d %d %d %d %s", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID, objectSuffix) < 0 )
+ {
+ Fail ("Error: Insufficient Event name string length for %s for iteration [%d]\n", ObjName, i);
+ }
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi[i], sizeof(pi[i]) );
+ ZeroMemory ( &si[i], sizeof(si[i]) );
+
+ /* Set the process flags and standard io handles */
+ si[i].cb = sizeof(si[i]);
+
+ if(!CreateProcess( NULL, /* lpApplicationName*/
+ lpCommandLine, /* lpCommandLine */
+ NULL, /* lpProcessAttributes */
+ NULL, /* lpThreadAttributes */
+ TRUE, /* bInheritHandles */
+ 0, /* dwCreationFlags, */
+ NULL, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ &si[i], /* lpStartupInfo */
+ &pi[i] /* lpProcessInformation */
+ ))
+ {
+ Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError());
+ }
+ else
+ {
+ hProcess[i] = pi[i].hProcess;
+// Trace("Process created for [%d]\n", i);
+
+ }
+
+ //Create Process
+
+ }
+
+ returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+// Trace("Test over\n");
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) )
+ {
+ Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n",
+ i, GetLastError() );
+
+ testReturnCode = FAIL;
+ }
+
+ if(processReturnCode == FAIL)
+ {
+ Trace( "Process [%d] failed and returned FAIL\n", i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hThread))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hProcess) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+ }
+
+ testStats.operationTime = GetTimeDiff(dwStartTime);
+ fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber);
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile\n");
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(hEventHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for hEventHandle\n", GetLastError());
+ testReturnCode = FAIL;
+ }
+
+ if( testReturnCode == PASS)
+ {
+ Trace("Test Passed\n");
+ }
+ else
+ {
+ Trace("Test Failed\n");
+ }
+
+ PAL_Terminate();
+ return testReturnCode;
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/mutex/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/mutex/CMakeLists.txt
new file mode 100644
index 0000000000..2534564f95
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/mutex/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(nonshared)
+add_subdirectory(shared)
+
diff --git a/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/CMakeLists.txt
new file mode 100644
index 0000000000..7859cd4653
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ main.c
+ mutex.c
+)
+
+add_executable(paltest_mutex_nonshared
+ ${SOURCES}
+)
+
+add_dependencies(paltest_mutex_nonshared coreclrpal)
+
+target_link_libraries(paltest_mutex_nonshared
+ pthread
+ rt
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.c b/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.c
new file mode 100644
index 0000000000..80f31aad6e
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.c
@@ -0,0 +1,230 @@
+// 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 Code: main.c and mutex.c
+** main.c creates process and waits for all processes to get over
+** mutex.c creates a mutex and then calls threads which will contend for the mutex
+**
+** This test is for Object Management Test case for Mutex where Object type is not shareable.
+** Algorithm
+** o Create PROCESS_COUNT processes.
+** o Main Thread of each process creates OBJECT_TYPE Object
+**
+** Author: ShamitP
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resulttime.h"
+
+/* Test Input Variables */
+unsigned int PROCESS_COUNT = 2;
+unsigned int THREAD_COUNT = 20;
+unsigned int REPEAT_COUNT = 4000;
+unsigned int RELATION_ID = 1001;
+
+
+struct TestStats{
+ DWORD operationTime;
+ unsigned int relationId;
+ unsigned int processCount;
+ unsigned int threadCount;
+ unsigned int repeatCount;
+ char* buildNumber;
+
+};
+
+int GetParameters( int argc, char **argv)
+{
+ if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management Mutex Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[PROCESS_COUNT [greater than 1] \n");
+ printf("\t[THREAD_COUNT [greater than 1] \n");
+ printf("\t[REPEAT_COUNT [greater than 1]\n");
+ printf("\t[RELATION_ID [greater than 1]\n");
+
+
+ return -1;
+ }
+
+ PROCESS_COUNT = atoi(argv[1]);
+ if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hProcess[MAXIMUM_WAIT_OBJECTS];
+ HANDLE hMutexHandle[MAXIMUM_WAIT_OBJECTS];
+
+ STARTUPINFO si[MAXIMUM_WAIT_OBJECTS];
+ PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS];
+
+ const char *ObjName = "Mutex";
+ char lpCommandLine[MAX_PATH] = "";
+
+ int returnCode = 0;
+ DWORD processReturnCode = 0;
+ int testReturnCode = PASS;
+
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ DWORD dwStartTime;
+ struct TestStats testStats;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ testStats.relationId = RELATION_ID;
+ testStats.processCount = PROCESS_COUNT;
+ testStats.threadCount = THREAD_COUNT;
+ testStats.repeatCount = REPEAT_COUNT;
+ testStats.buildNumber = getBuildNumber();
+
+
+ _snprintf(fileName, MAX_PATH, "main_mutex_%d_.txt", RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening main file for write\n");
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ ZeroMemory( lpCommandLine, MAX_PATH );
+ if ( _snprintf( lpCommandLine, MAX_PATH-1, "mutex %d %d %d %d", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID) < 0 )
+ {
+ Fail("Error Insufficient mutex name string length for %s for iteration [%d]\n", ObjName, i);
+ }
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi[i], sizeof(pi[i]) );
+ ZeroMemory ( &si[i], sizeof(si[i]) );
+
+ /* Set the process flags and standard io handles */
+ si[i].cb = sizeof(si[i]);
+
+ if(!CreateProcess( NULL, /* lpApplicationName*/
+ lpCommandLine, /* lpCommandLine */
+ NULL, /* lpProcessAttributes */
+ NULL, /* lpThreadAttributes */
+ TRUE, /* bInheritHandles */
+ 0, /* dwCreationFlags, */
+ NULL, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ &si[i], /* lpStartupInfo */
+ &pi[i] /* lpProcessInformation */
+ ))
+ {
+ Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError());
+ }
+ else
+ {
+ hProcess[i] = pi[i].hProcess;
+// Trace("Process created for [%d]\n", i);
+
+ }
+
+ //Create Process
+
+ }
+
+ returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) )
+ {
+ Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n",
+ i, GetLastError() );
+
+ testReturnCode = FAIL;
+ }
+
+ if(processReturnCode == FAIL)
+ {
+ Trace( "Process [%d] failed and returned FAIL\n", i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hThread))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hProcess) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+ }
+
+ testStats.operationTime = GetTimeDiff(dwStartTime);
+ fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber);
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile\n");
+ testReturnCode = FAIL;
+ }
+
+ if( testReturnCode == PASS)
+ {
+ Trace("Test Passed\n");
+ }
+ else
+ {
+ Trace("Test Failed\n");
+ }
+
+ PAL_Terminate();
+ return testReturnCode;
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.c b/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.c
new file mode 100644
index 0000000000..7f1f659f92
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.c
@@ -0,0 +1,340 @@
+// 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 Code: main.c and mutex.c
+** main.c creates process and waits for all processes to get over
+** mutex.c creates a mutex and then calls threads which will contend for the mutex
+**
+** This test is for Object Management Test case for Mutex where Object type is not shareable.
+** Algorithm
+** o Create PROCESS_COUNT processes.
+** o Main Thread of each process creates OBJECT_TYPE Object
+**
+** Author: ShamitP
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resultbuffer.h"
+#include "resulttime.h"
+
+#define TIMEOUT 5000
+/* Test Input Variables */
+unsigned int USE_PROCESS_COUNT = 0;
+unsigned int THREAD_COUNT = 0;
+unsigned int REPEAT_COUNT = 0;
+unsigned int RELATION_ID = 1001;
+
+/* Capture statistics at per thread basis */
+struct statistics{
+ unsigned int processId;
+ unsigned int operationsFailed;
+ unsigned int operationsPassed;
+ unsigned int operationsTotal;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+struct ProcessStats{
+ unsigned int processId;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+HANDLE StartTestsEvHandle = NULL;
+HANDLE hMutexHandle = NULL;
+
+/* Results Buffer */
+ResultBuffer *resultBuffer = NULL;
+
+int testStatus;
+
+void PALAPI Run_Thread(LPVOID lpParam);
+
+int GetParameters( int argc, char **argv)
+{
+ if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management Mutex Test\n");
+ printf("Usage:\n");
+ printf("mutex\n\t[USE_PROCESS_COUNT ( greater than 1] \n");
+ printf("\t[THREAD_COUNT ( greater than 1] \n");
+ printf("\t[REPEAT_COUNT ( greater than 1]\n");
+ printf("\t[RELATION_ID [greater than 1]\n");
+ return -1;
+ }
+
+ // Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]);
+
+ USE_PROCESS_COUNT = atoi(argv[1]);
+ if( USE_PROCESS_COUNT < 0)
+ {
+ printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hThread[MAXIMUM_WAIT_OBJECTS];
+ DWORD threadId[MAXIMUM_WAIT_OBJECTS];
+
+ const char sTmpEventName[MAX_PATH] = "StartTestEvent";
+
+ DWORD dwParam = 0;
+
+ int returnCode = 0;
+
+ /* Variables to capture the file name and the file pointer at thread level*/
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ struct statistics* buffer = NULL;
+ int statisticsSize = 0;
+
+ /* Variables to capture the file name and the file pointer at process level*/
+ char processFileName[MAX_PATH];
+ FILE *pProcessFile = NULL;
+ struct ProcessStats processStats;
+ DWORD dwStartTime;
+
+ testStatus = PASS;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+// Trace("Process created, value of process count is [%d]\n", USE_PROCESS_COUNT);
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ processStats.relationId = RELATION_ID;
+ processStats.processId = USE_PROCESS_COUNT;
+
+ _snprintf(processFileName, MAX_PATH, "%d_process_mutex_%d_.txt", USE_PROCESS_COUNT,RELATION_ID);
+ pProcessFile = fopen(processFileName, "w+");
+ if(pProcessFile == NULL)
+ {
+ Fail("Error in opening process File file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+
+ statisticsSize = sizeof(struct statistics);
+
+ _snprintf(fileName, MAX_PATH, "%d_thread_mutex_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+ // For each thread we will log operations failed (int), passed (int), total (int)
+ // and number of ticks (DWORD) for the operations
+ resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize);
+
+ StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/
+ TRUE, /* bManualReset */
+ FALSE, /* bInitialState */
+ NULL
+ ); /* name of Event */
+
+ if( StartTestsEvHandle == NULL )
+ {
+ Fail("Error:%d: Unexpected failure "
+ "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT );
+
+ }
+
+ /* Create StartTest Event */
+
+ hMutexHandle = CreateMutex(
+ NULL,
+ FALSE, /* bInitialOwner, owns initially */
+ NULL
+ );
+
+ if( hMutexHandle == NULL)
+ {
+ Fail("Unable to create Mutex handle for process id [%d], returned error [%d]\n", i, GetLastError());
+ }
+ /* We already assume that the mutex was created previously*/
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ }
+
+ if (!SetEvent(StartTestsEvHandle))
+ {
+ Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ /* Test running */
+ returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE);
+
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError());
+ testStatus = FAIL;
+ }
+
+ processStats.operationTime = GetTimeDiff(dwStartTime);
+
+ /* Write to a file*/
+ if(pFile!= NULL)
+ {
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ buffer = (struct statistics *)resultBuffer->getResultBuffer(i);
+ returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId );
+// Trace("Iteration %d over\n", i);
+
+ }
+ }
+ fclose(pFile);
+
+ fprintf(pProcessFile, "%d,%d,%d\n", USE_PROCESS_COUNT, processStats.operationTime, processStats.relationId );
+ fclose(pProcessFile);
+
+ /* Logging for the test case over, clean up the handles */
+
+// Trace("Test Thread %d done\n", USE_PROCESS_COUNT);
+
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ if(!CloseHandle(hThread[i]) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i);
+ testStatus = FAIL;
+ }
+ }
+
+ if(!CloseHandle(StartTestsEvHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ if(!CloseHandle(hMutexHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hMutexHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ PAL_Terminate();
+ return testStatus;
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ unsigned int i = 0;
+ DWORD dwWaitResult;
+
+ struct statistics stats;
+ DWORD dwStartTime;
+
+ stats.relationId = RELATION_ID;
+ stats.processId = USE_PROCESS_COUNT;
+ stats.operationsFailed = 0;
+ stats.operationsPassed = 0;
+ stats.operationsTotal = 0;
+ stats.operationTime = 0;
+
+ int Id=(int)lpParam;
+
+ dwWaitResult = WaitForSingleObject(
+ StartTestsEvHandle, // handle to mutex
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+ Trace("Error while waiting for StartTest Event@ thread %d\n", Id);
+ testStatus = FAIL;
+ }
+
+ dwStartTime = GetTickCount();
+
+ for( i = 0; i < REPEAT_COUNT; i++ )
+ {
+ dwWaitResult = WaitForSingleObject(
+ hMutexHandle, // handle to mutex
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+// Trace("Error while waiting for onject @ thread %d, # iter %d\n", Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ testStatus = FAIL;
+ continue;
+ }
+ if (! ReleaseMutex(hMutexHandle))
+ {
+ // Deal with error.
+// Trace("Error while releasing mutex @ thread %d # iter %d\n", Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ // Probably need to have while true loop to attempt to release mutex...
+ testStatus = FAIL;
+ continue;
+ }
+
+ stats.operationsTotal += 1;
+ stats.operationsPassed += 1;
+ // Trace("Successs while releasing mutex @ iteration %d -> thread %d -> Process %d\n", i, Id, USE_PROCESS_COUNT);
+
+ }
+
+ stats.operationTime = GetTimeDiff(dwStartTime);
+ //Trace("OPeration time is %d", stats.operationTime );
+ if(resultBuffer->LogResult(Id, (char *)&stats))
+ {
+ Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT);
+ }
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/mutex/shared/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/mutex/shared/CMakeLists.txt
new file mode 100644
index 0000000000..cf33d0b464
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/mutex/shared/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ main.c
+ mutex.c
+)
+
+add_executable(paltest_mutex_shared
+ ${SOURCES}
+)
+
+add_dependencies(paltest_mutex_shared coreclrpal)
+
+target_link_libraries(paltest_mutex_shared
+ pthread
+ rt
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/composite/object_management/mutex/shared/main.c b/src/pal/tests/palsuite/composite/object_management/mutex/shared/main.c
new file mode 100644
index 0000000000..aa98855565
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/mutex/shared/main.c
@@ -0,0 +1,265 @@
+// 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.
+
+/*============================================================
+**
+** This test is for Object Management Test case for Mutex where Object type is shareable.
+**
+** Source Code: main.c and mutex.c
+** main.c creates a mutex, creates processes and waits for all processes to get over
+** mutex.c create threads which will contend for the mutex
+**
+** This test is for Object Management Test case for Mutex where Object type is not shareable.
+** Algorithm
+** o Main Process Creates OBJECT_TYPE Object
+** o Create PROCESS_COUNT processes aware of the Shared Object
+**
+** Author: ShamitP
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resulttime.h"
+
+/* Test Input Variables */
+unsigned int PROCESS_COUNT = 2;
+unsigned int THREAD_COUNT = 2;
+unsigned int REPEAT_COUNT = 40000;
+unsigned int RELATION_ID = 1001;
+
+
+char objectSuffix[MAX_PATH];
+
+struct TestStats{
+ DWORD operationTime;
+ unsigned int relationId;
+ unsigned int processCount;
+ unsigned int threadCount;
+ unsigned int repeatCount;
+ char* buildNumber;
+
+};
+
+int GetParameters( int argc, char **argv)
+{
+ if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management event Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[PROCESS_COUNT (greater than 1)] \n");
+ printf("\t[THREAD_COUNT (greater than 1)] \n");
+ printf("\t[REPEAT_COUNT (greater than 1)]\n");
+ printf("\t[RELATION_ID [greater than 1]\n");
+ printf("\t[Object Name Suffix]\n");
+ return -1;
+ }
+
+ PROCESS_COUNT = atoi(argv[1]);
+ if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+ if(argc == 6)
+ {
+ strncpy(objectSuffix, argv[5], MAX_PATH-1);
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hProcess[MAXIMUM_WAIT_OBJECTS];
+ HANDLE hMutexHandle;
+
+ STARTUPINFO si[MAXIMUM_WAIT_OBJECTS];
+ PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS];
+
+ char ObjName[MAX_PATH] = "SHARED_MUTEX";
+ char lpCommandLine[MAX_PATH] = "";
+
+ int returnCode = 0;
+ DWORD processReturnCode = 0;
+ int testReturnCode = PASS;
+
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ DWORD dwStartTime;
+ struct TestStats testStats;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ZeroMemory( objectSuffix, MAX_PATH );
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+
+ if(argc == 5)
+ {
+ strncat(ObjName, objectSuffix, MAX_PATH - (sizeof(ObjName) + 1) );
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ testStats.relationId = RELATION_ID;
+ testStats.processCount = PROCESS_COUNT;
+ testStats.threadCount = THREAD_COUNT;
+ testStats.repeatCount = REPEAT_COUNT;
+ testStats.buildNumber = getBuildNumber();
+
+
+ _snprintf(fileName, MAX_PATH, "main_mutex_%d_.txt", RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening main file for write\n");
+ }
+
+ hMutexHandle = CreateMutex(
+ NULL,
+ FALSE, /* bInitialOwner, owns initially */
+ ObjName
+ );
+
+ if( hMutexHandle == NULL)
+ {
+ Fail("Unable to create Mutex handle for Main thread returned error [%d]\n", GetLastError());
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ ZeroMemory( lpCommandLine, MAX_PATH );
+ if ( _snprintf( lpCommandLine, MAX_PATH-1, "mutex %d %d %d %d %s", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID, objectSuffix) < 0 )
+ {
+ Fail ("Error Insufficient mutex name string length for %s for iteration [%d]\n", ObjName, i);
+ }
+
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi[i], sizeof(pi[i]) );
+ ZeroMemory ( &si[i], sizeof(si[i]) );
+
+ /* Set the process flags and standard io handles */
+ si[i].cb = sizeof(si[i]);
+
+ //Create Process
+ if(!CreateProcess( NULL, /* lpApplicationName*/
+ lpCommandLine, /* lpCommandLine */
+ NULL, /* lpProcessAttributes */
+ NULL, /* lpThreadAttributes */
+ TRUE, /* bInheritHandles */
+ 0, /* dwCreationFlags, */
+ NULL, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ &si[i], /* lpStartupInfo */
+ &pi[i] /* lpProcessInformation */
+ ))
+ {
+ Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError());
+ }
+ else
+ {
+ hProcess[i] = pi[i].hProcess;
+// Trace("Process created for [%d]\n", i);
+
+ }
+ }
+
+ returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) )
+ {
+ Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n",
+ i, GetLastError() );
+
+ testReturnCode = FAIL;
+ }
+
+ if(processReturnCode == FAIL)
+ {
+ Trace( "Process [%d] failed and returned FAIL\n", i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hThread))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hProcess) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+ }
+
+ testStats.operationTime = GetTimeDiff(dwStartTime);
+ fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber );
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile\n");
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(hMutexHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for hMutexHandle\n", GetLastError());
+ testReturnCode = FAIL;
+
+ }
+
+ if( testReturnCode == PASS)
+ {
+ Trace("Test Passed\n");
+ }
+ else
+ {
+ Trace("Test Failed\n");
+ }
+
+ PAL_Terminate();
+ return testReturnCode;
+}
+
diff --git a/src/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.c b/src/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.c
new file mode 100644
index 0000000000..ec5d9b37ac
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.c
@@ -0,0 +1,354 @@
+// 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.
+
+/*============================================================
+**
+** This test is for Object Management Test case for Mutex where Object type is shareable.
+**
+** Source Code: main.c and mutex.c
+** main.c creates a mutex, creates processes and waits for all processes to get over
+** mutex.c create threads which will contend for the mutex
+**
+** This test is for Object Management Test case for Mutex where Object type is not shareable.
+** Algorithm
+** o Main Process Creates OBJECT_TYPE Object
+** o Create PROCESS_COUNT processes aware of the Shared Object
+**
+** Author: ShamitP
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resultbuffer.h"
+#include "resulttime.h"
+
+#define TIMEOUT 5000
+/* Test Input Variables */
+unsigned int USE_PROCESS_COUNT = 0;
+unsigned int THREAD_COUNT = 0;
+unsigned int REPEAT_COUNT = 0;
+unsigned int RELATION_ID = 0;
+
+
+/* Capture statistics at per thread basis */
+struct statistics{
+ unsigned int processId;
+ unsigned int operationsFailed;
+ unsigned int operationsPassed;
+ unsigned int operationsTotal;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+struct ProcessStats{
+ unsigned int processId;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+HANDLE StartTestsEvHandle = NULL;
+HANDLE hMutexHandle = NULL;
+
+/* Results Buffer */
+ResultBuffer *resultBuffer = NULL;
+
+int testStatus;
+
+const char sTmpEventName[MAX_PATH] = "StartTestEvent";
+char objectSuffix[MAX_PATH];
+
+void PALAPI Run_Thread(LPVOID lpParam);
+
+int GetParameters( int argc, char **argv)
+{
+ if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management event Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[USE_PROCESS_COUNT (greater than 1)] \n");
+ printf("\t[THREAD_COUNT (greater than 1)] \n");
+ printf("\t[REPEAT_COUNT (greater than 1)]\n");
+ printf("\t[RELATION_ID [greater than 1]\n");
+ printf("\t[Object Name Suffix]\n");
+
+ return -1;
+ }
+
+ // Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]);
+
+ USE_PROCESS_COUNT = atoi(argv[1]);
+ if( USE_PROCESS_COUNT < 0)
+ {
+ printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+ if(argc == 6)
+ {
+ strncpy(objectSuffix, argv[5], MAX_PATH-1);
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hThread[MAXIMUM_WAIT_OBJECTS];
+ DWORD threadId[MAXIMUM_WAIT_OBJECTS];
+
+ char ObjName[MAX_PATH] = "SHARED_MUTEX";
+ DWORD dwParam = 0;
+
+ int returnCode = 0;
+
+ /* Variables to capture the file name and the file pointer*/
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ struct statistics* buffer = NULL;
+ int statisticsSize = 0;
+
+ /* Variables to capture the file name and the file pointer at process level*/
+ char processFileName[MAX_PATH];
+ FILE *pProcessFile = NULL;
+ struct ProcessStats processStats;
+ DWORD dwStartTime;
+
+ testStatus = PASS;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ ZeroMemory( objectSuffix, MAX_PATH );
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+// Trace("Process created, value of process count is [%d]\n", USE_PROCESS_COUNT);
+
+ if(argc == 5)
+ {
+ strncat(ObjName, objectSuffix, MAX_PATH - (sizeof(ObjName) + 1) );
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ processStats.relationId = RELATION_ID;
+ processStats.processId = USE_PROCESS_COUNT;
+
+ _snprintf(processFileName, MAX_PATH, "%d_process_mutex_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pProcessFile = fopen(processFileName, "w+");
+ if(pProcessFile == NULL)
+ {
+ Fail("Error in opening process File file for write for process [%d]\n", USE_PROCESS_COUNT);
+ } statisticsSize = sizeof(struct statistics);
+
+ _snprintf(fileName, MAX_PATH, "%d_thread_mutex_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+ // For each thread we will log operations failed (int), passed (int), total (int)
+ // and number of ticks (DWORD) for the operations
+ resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize);
+
+ /* Create StartTest Event */
+ StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/
+ TRUE, /* bManualReset */
+ FALSE, /* bInitialState */
+ NULL); /* name of Event */
+
+ if( StartTestsEvHandle == NULL )
+ {
+ Fail("Error:%d: Unexpected failure "
+ "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT );
+
+ }
+
+ hMutexHandle = CreateMutex(
+ NULL,
+ FALSE, /* bInitialOwner, owns initially */
+ ObjName
+ );
+
+ if( (hMutexHandle == NULL)|| (GetLastError() != ERROR_ALREADY_EXISTS))
+ {
+ Fail("Unable to create Mutex handle for process id [%d], returned error [%d], expected ERROR_ALREADY_EXISTS\n", i, GetLastError());
+ }
+ /* We already assume that the mutex was created previously*/
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ }
+
+ if (!SetEvent(StartTestsEvHandle))
+ {
+ Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ /* Test running */
+ returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE);
+
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError());
+ testStatus = FAIL;
+ }
+
+ processStats.operationTime = GetTimeDiff(dwStartTime);
+
+ /* Write to a file*/
+ if(pFile!= NULL)
+ {
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ buffer = (struct statistics *)resultBuffer->getResultBuffer(i);
+ returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId );
+// Trace("Iteration %d over\n", i);
+
+ }
+ }
+ fclose(pFile);
+
+ fprintf(pProcessFile, "%d,%d,%d\n", USE_PROCESS_COUNT, processStats.operationTime, processStats.relationId );
+ fclose(pProcessFile);
+
+ /* Logging for the test case over, clean up the handles */
+
+// Trace("Process Count %d over\n",USE_PROCESS_COUNT);
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ if(!CloseHandle(hThread[i]) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i);
+ testStatus = FAIL;
+ }
+ }
+
+ if(!CloseHandle(StartTestsEvHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ if(!CloseHandle(hMutexHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hMutexHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ PAL_Terminate();
+ return testStatus;
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ unsigned int i = 0;
+ DWORD dwWaitResult;
+
+ struct statistics stats;
+ DWORD dwStartTime;
+
+ stats.relationId = RELATION_ID;
+ stats.processId = USE_PROCESS_COUNT;
+ stats.operationsFailed = 0;
+ stats.operationsPassed = 0;
+ stats.operationsTotal = 0;
+ stats.operationTime = 0;
+
+ int Id=(int)lpParam;
+
+ dwWaitResult = WaitForSingleObject(
+ StartTestsEvHandle, // handle to mutex
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+ Trace("Error while waiting for StartTest Event@ thread %d\n", Id);
+ testStatus = FAIL;
+ }
+
+ dwStartTime = GetTickCount();
+
+ for( i = 0; i < REPEAT_COUNT; i++ )
+ {
+ dwWaitResult = WaitForSingleObject(
+ hMutexHandle, // handle to mutex
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+// Trace("Error while waiting for onject @ thread %d, # iter %d, Error Returned [%d]\n", Id, i, GetLastError());
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ testStatus = FAIL;
+ continue;
+ }
+ if (! ReleaseMutex(hMutexHandle))
+ {
+ // Deal with error.
+// Trace("Error while releasing mutex @ thread %d # iter %d\n", Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ // Probably need to have while true loop to attempt to release mutex...
+ testStatus = FAIL;
+ continue;
+ }
+
+ stats.operationsTotal += 1;
+ stats.operationsPassed += 1;
+// Trace("Successs while releasing mutex @ iteration %d -> thread %d -> Process count %d\n", i, Id, USE_PROCESS_COUNT);
+
+ }
+ stats.operationTime = GetTimeDiff(dwStartTime);
+ if(resultBuffer->LogResult(Id, (char *)&stats))
+ {
+ Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT);
+ }
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/readme.txt b/src/pal/tests/palsuite/composite/object_management/readme.txt
new file mode 100644
index 0000000000..6bae5f105d
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/readme.txt
@@ -0,0 +1,29 @@
+To compile:
+
+1) create a dat file (say object_management.dat) with contents:
+
+PAL,Composite,palsuite\composite\object_management\mutex\nonshared,mutex=main.c mutex.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
+PAL,Composite,palsuite\composite\object_management\mutex\shared,mutex=main.c mutex.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
+PAL,Composite,palsuite\composite\object_management\semaphore\nonshared,semaphore=main.c semaphore.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
+PAL,Composite,palsuite\composite\object_management\semaphore\shared,semaphore=main.c semaphore.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
+PAL,Composite,palsuite\composite\object_management\event\nonshared,event=main.c event.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
+PAL,Composite,palsuite\composite\object_management\event\shared,event=main.c event.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
+
+
+2) perl rrunmod.pl -r object_management.dat
+
+
+To execute:
+For each of the test cases,
+main [PROCESS_COUNT] [THREAD_COUNT] [REPEAT_COUNT]
+
+
+Output:
+The performance numbers will be in <process_logical_id>_[event|semaphore|mutex].txt
+(will be at palsuite\composite\object_management\[mutex|event|semaphore]\[shared|nonshared]\obj[r|c|d] directory if u use rrunmod.pl)
+
+So if process_count is 3, you will have files 0_mutex.txt, 1_mutex.txt and so on…
+
+For each process txt file created,
+each row represents a thread data (process id, number of failures, number of pass, total number of repeated operations and an integer that will be used to identify a run
+(currently zero)).
diff --git a/src/pal/tests/palsuite/composite/object_management/semaphore/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/semaphore/CMakeLists.txt
new file mode 100644
index 0000000000..2534564f95
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/semaphore/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+add_subdirectory(nonshared)
+add_subdirectory(shared)
+
diff --git a/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/CMakeLists.txt
new file mode 100644
index 0000000000..6efa228fbb
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ main.c
+ semaphore.c
+)
+
+add_executable(paltest_semaphore_nonshared
+ ${SOURCES}
+)
+
+add_dependencies(paltest_semaphore_nonshared coreclrpal)
+
+target_link_libraries(paltest_semaphore_nonshared
+ pthread
+ rt
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/main.c b/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/main.c
new file mode 100644
index 0000000000..854809c8f8
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/main.c
@@ -0,0 +1,228 @@
+// 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 Code: main.c and semaphore.c
+** main.c creates process and waits for all processes to get over
+** semaphore.c creates a semaphore and then calls threads which will contend for the semaphore
+**
+** This test is for Object Management Test case for semaphore where Object type is not shareable.
+** Algorithm
+** o Create PROCESS_COUNT processes.
+** o Main Thread of each process creates OBJECT_TYPE Object
+**
+** Author: ShamitP
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resulttime.h"
+
+/* Test Input Variables */
+unsigned int PROCESS_COUNT = 2;
+unsigned int THREAD_COUNT = 15;
+unsigned int REPEAT_COUNT = 40000;
+unsigned int RELATION_ID = 1001;
+
+
+
+struct TestStats{
+ DWORD operationTime;
+ unsigned int relationId;
+ unsigned int processCount;
+ unsigned int threadCount;
+ unsigned int repeatCount;
+ char* buildNumber;
+};
+
+int GetParameters( int argc, char **argv)
+{
+ if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management Semaphore Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[PROCESS_COUNT [greater than 1] \n");
+ printf("\t[THREAD_COUNT [greater than 1] \n");
+ printf("\t[REPEAT_COUNT [greater than 1]\n");
+ printf("\t[RELATION_ID [greater than 1]\n");
+ return -1;
+ }
+
+ PROCESS_COUNT = atoi(argv[1]);
+ if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than or Equal to 1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hProcess[MAXIMUM_WAIT_OBJECTS];
+ HANDLE hSemaphoreHandle[MAXIMUM_WAIT_OBJECTS];
+
+ STARTUPINFO si[MAXIMUM_WAIT_OBJECTS];
+ PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS];
+
+ const char *ObjName = "Semaphore";
+ char lpCommandLine[MAX_PATH] = "";
+
+ int returnCode = 0;
+ DWORD processReturnCode = 0;
+ int testReturnCode = PASS;
+
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ DWORD dwStartTime;
+ struct TestStats testStats;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ testStats.relationId = RELATION_ID;
+ testStats.processCount = PROCESS_COUNT;
+ testStats.threadCount = THREAD_COUNT;
+ testStats.repeatCount = REPEAT_COUNT;
+ testStats.buildNumber = getBuildNumber();
+
+
+ _snprintf(fileName, MAX_PATH, "main_semaphore_%d_.txt", RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening main file for write\n");
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ ZeroMemory( lpCommandLine, MAX_PATH );
+ if ( _snprintf( lpCommandLine, MAX_PATH-1, "semaphore %d %d %d %d", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID) < 0 )
+ {
+ Fail("Error Insufficient semaphore name string length for %s for iteration [%d]\n", ObjName, i);
+ }
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi[i], sizeof(pi[i]) );
+ ZeroMemory ( &si[i], sizeof(si[i]) );
+
+ /* Set the process flags and standard io handles */
+ si[i].cb = sizeof(si[i]);
+
+ //Create Process
+ if(!CreateProcess( NULL, /* lpApplicationName*/
+ lpCommandLine, /* lpCommandLine */
+ NULL, /* lpProcessAttributes */
+ NULL, /* lpThreadAttributes */
+ TRUE, /* bInheritHandles */
+ 0, /* dwCreationFlags, */
+ NULL, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ &si[i], /* lpStartupInfo */
+ &pi[i] /* lpProcessInformation */
+ ))
+ {
+ Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError());
+ }
+ else
+ {
+ hProcess[i] = pi[i].hProcess;
+// Trace("Process created for [%d]\n", i);
+
+ }
+ }
+
+ returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) )
+ {
+ Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n",
+ i, GetLastError() );
+
+ testReturnCode = FAIL;
+ }
+
+ if(processReturnCode == FAIL)
+ {
+ Trace( "Process [%d] failed and returned FAIL\n", i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hThread))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hProcess) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+ }
+
+ testStats.operationTime = GetTimeDiff(dwStartTime);
+ fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId,testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber );
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile\n");
+ testReturnCode = FAIL;
+ }
+
+ if( testReturnCode == PASS)
+ {
+ Trace("Test Passed\n");
+ }
+ else
+ {
+ Trace("Test Failed\n");
+ }
+
+ PAL_Terminate();
+ return testReturnCode;
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/semaphore.c b/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/semaphore.c
new file mode 100644
index 0000000000..0e487f2c17
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/semaphore/nonshared/semaphore.c
@@ -0,0 +1,342 @@
+// 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 Code: main.c and semaphore.c
+** main.c creates process and waits for all processes to get over
+** semaphore.c creates a semaphore and then calls threads which will contend for the semaphore
+**
+** This test is for Object Management Test case for semaphore where Object type is not shareable.
+** Algorithm
+** o Create PROCESS_COUNT processes.
+** o Main Thread of each process creates OBJECT_TYPE Object
+**
+** Author: ShamitP
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resultbuffer.h"
+#include "resulttime.h"
+
+#define TIMEOUT 5000
+/* Test Input Variables */
+unsigned int USE_PROCESS_COUNT = 0;
+unsigned int THREAD_COUNT = 0;
+unsigned int REPEAT_COUNT = 0;
+unsigned int RELATION_ID = 0;
+
+/* Capture statistics at per thread basis */
+struct statistics{
+ unsigned int processId;
+ unsigned int operationsFailed;
+ unsigned int operationsPassed;
+ unsigned int operationsTotal;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+struct ProcessStats{
+ unsigned int processId;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+/* Semaphore variables */
+unsigned long lInitialCount = 1; /* Signaled */
+unsigned long lMaximumCount = 1; /* Maximum value of 1 */
+
+HANDLE StartTestsEvHandle = NULL;
+HANDLE hSemaphoreHandle = NULL;
+
+/* Results Buffer */
+ResultBuffer *resultBuffer = NULL;
+
+int testStatus;
+
+const char sTmpEventName[MAX_PATH] = "StartTestEvent";
+
+void PALAPI Run_Thread(LPVOID lpParam);
+
+int GetParameters( int argc, char **argv)
+{
+ if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management Semaphore Test\n");
+ printf("Usage:\n");
+ printf("semaphore\n\t[USE_PROCESS_COUNT ( greater than 1] \n");
+ printf("\t[THREAD_COUNT ( greater than 1] \n");
+ printf("\t[REPEAT_COUNT ( greater than 1]\n");
+ printf("\t[RELATION_ID [greater than 1]\n");
+ return -1;
+ }
+
+ // Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]);
+
+ USE_PROCESS_COUNT = atoi(argv[1]);
+ if( USE_PROCESS_COUNT < 0)
+ {
+ printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than or Equal to 1\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hThread[MAXIMUM_WAIT_OBJECTS];
+ DWORD threadId[MAXIMUM_WAIT_OBJECTS];
+
+ const char *ObjName = "Semaphore";
+
+ DWORD dwParam = 0;
+
+ int returnCode = 0;
+
+ /* Variables to capture the file name and the file pointer at thread level*/
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ struct statistics* buffer = NULL;
+ int statisticsSize = 0;
+
+ /* Variables to capture the file name and the file pointer at process level*/
+ char processFileName[MAX_PATH];
+ FILE *pProcessFile = NULL;
+ struct ProcessStats processStats;
+ DWORD dwStartTime;
+
+ testStatus = PASS;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+ // Trace("Process created, value of process count is [%d]\n", USE_PROCESS_COUNT);
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ processStats.relationId = RELATION_ID;
+ processStats.processId = USE_PROCESS_COUNT;
+
+ _snprintf(processFileName, MAX_PATH, "%d_process_semaphore_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pProcessFile = fopen(processFileName, "w+");
+ if(pProcessFile == NULL)
+ {
+ Fail("Error in opening process File file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+
+ statisticsSize = sizeof(struct statistics);
+
+ _snprintf(fileName, MAX_PATH, "%d_thread_semaphore_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+ // For each thread we will log operations failed (int), passed (int), total (int)
+ // and number of ticks (DWORD) for the operations
+ resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize);
+
+ StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/
+ TRUE, /* bManualReset */
+ FALSE, /* bInitialState */
+ NULL); /* name of Event */
+
+ if( StartTestsEvHandle == NULL )
+ {
+ Fail("Error:%d: Unexpected failure "
+ "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT );
+
+ }
+
+ /* Create StartTest Event */
+ hSemaphoreHandle = CreateSemaphore(
+ NULL, /* lpSemaphoreAttributes */
+ lInitialCount, /*lInitialCount*/
+ lMaximumCount, /*lMaximumCount */
+ NULL
+ );
+
+ if( hSemaphoreHandle == NULL)
+ {
+ Fail("Unable to create Semaphore handle for process id [%d], returned error [%d]\n", i, GetLastError());
+ }
+ /* We already assume that the Semaphore was created previously*/
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ }
+
+ if (!SetEvent(StartTestsEvHandle))
+ {
+ Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ /* Test running */
+ returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE);
+
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError());
+ testStatus = FAIL;
+ }
+
+ processStats.operationTime = GetTimeDiff(dwStartTime);
+
+ /* Write to a file*/
+ if(pFile!= NULL)
+ {
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ buffer = (struct statistics *)resultBuffer->getResultBuffer(i);
+ returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId );
+ //Trace("Iteration %d over\n", i);
+
+ }
+ }
+ fclose(pFile);
+ /* Logging for the test case over, clean up the handles */
+
+// Trace("Test Thread %d done\n", USE_PROCESS_COUNT);
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ if(!CloseHandle(hThread[i]) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i);
+ testStatus = FAIL;
+ }
+ }
+
+ if(!CloseHandle(StartTestsEvHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ if(!CloseHandle(hSemaphoreHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hSemaphoreHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ unsigned int i = 0;
+ DWORD dwWaitResult;
+
+ int Id=(int)lpParam;
+
+ struct statistics stats;
+ DWORD dwStartTime;
+
+ stats.relationId = RELATION_ID;
+ stats.processId = USE_PROCESS_COUNT;
+ stats.operationsFailed = 0;
+ stats.operationsPassed = 0;
+ stats.operationsTotal = 0;
+ stats.operationTime = 0;
+
+ dwWaitResult = WaitForSingleObject(
+ StartTestsEvHandle, // handle to start test handle
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+ Fail("Error while waiting for StartTest Event@ thread %d, RC is %d, Error is %d\n", Id, dwWaitResult, GetLastError());
+ }
+
+ dwStartTime = GetTickCount();
+
+ for( i = 0; i < REPEAT_COUNT; i++ )
+ {
+ dwWaitResult = WaitForSingleObject(
+ hSemaphoreHandle, // handle to Semaphore
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+// Trace("Error while waiting for onject @ thread %d, # iter %d, RC is %d, Error is %d\n", Id, i, dwWaitResult, GetLastError());
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ testStatus = FAIL;
+ continue;
+ }
+ if (! ReleaseSemaphore(hSemaphoreHandle, 1, NULL))
+ {
+ // Deal with error.
+ // Trace("Error while releasing Semaphore @ thread %d # iter %d\n", Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ // Probably need to have while true loop to attempt to release semaphore...
+ testStatus = FAIL;
+ continue;
+ }
+
+ stats.operationsTotal += 1;
+ stats.operationsPassed += 1;
+// Trace("Successs while releasing Semaphore @ iteration %d -> thread %d -> Process %d\n", i, Id, USE_PROCESS_COUNT);
+
+ }
+
+ stats.operationTime = GetTimeDiff(dwStartTime);
+ if(resultBuffer->LogResult(Id, (char *)&stats))
+ {
+ Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT);
+ }
+ // Trace("Thread %d over for process %d\n", Id, USE_PROCESS_COUNT);
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/semaphore/shared/CMakeLists.txt b/src/pal/tests/palsuite/composite/object_management/semaphore/shared/CMakeLists.txt
new file mode 100644
index 0000000000..12d3ca867e
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/semaphore/shared/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(SOURCES
+ main.c
+ semaphore.c
+)
+
+add_executable(paltest_semaphore_shared
+ ${SOURCES}
+)
+
+add_dependencies(paltest_semaphore_shared coreclrpal)
+
+target_link_libraries(paltest_semaphore_shared
+ pthread
+ rt
+ m
+ coreclrpal
+)
diff --git a/src/pal/tests/palsuite/composite/object_management/semaphore/shared/main.c b/src/pal/tests/palsuite/composite/object_management/semaphore/shared/main.c
new file mode 100644
index 0000000000..deb8252b70
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/semaphore/shared/main.c
@@ -0,0 +1,278 @@
+// 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 Code: main.c and semaphore.c
+** main.c creates process and waits for all processes to get over
+** semaphore.c creates a semaphore and then calls threads which will contend for the semaphore
+**
+** This test is for Object Management Test case for semaphore where Object type is shareable.
+** Algorithm
+** o Main Process Creates OBJECT_TYPE Object
+** o Create PROCESS_COUNT processes aware of the Shared Object
+**
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resulttime.h"
+
+/* Test Input Variables */
+unsigned int PROCESS_COUNT = 1;
+unsigned int THREAD_COUNT = 1;
+unsigned int REPEAT_COUNT = 4;
+unsigned int RELATION_ID = 1001;
+
+
+unsigned long lInitialCount = 1; /* Signaled */
+unsigned long lMaximumCount = 1; /* Maximum value of 1 */
+
+char objectSuffix[MAX_PATH];
+
+struct TestStats{
+ DWORD operationTime;
+ unsigned int relationId;
+ unsigned int processCount;
+ unsigned int threadCount;
+ unsigned int repeatCount;
+ char* buildNumber;
+
+};
+
+int GetParameters( int argc, char **argv)
+{
+ if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management event Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[PROCESS_COUNT (greater than 1)] \n");
+ printf("\t[THREAD_COUNT (greater than 1)] \n");
+ printf("\t[REPEAT_COUNT (greater than 1)]\n");
+ printf("\t[RELATION_ID [greater than or equal to 1]\n");
+ printf("\t[Object Name Suffix]\n");
+ return -1;
+ }
+
+ PROCESS_COUNT = atoi(argv[1]);
+ if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+
+ if(argc == 6)
+ {
+ strncpy(objectSuffix, argv[5], MAX_PATH-1);
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hProcess[MAXIMUM_WAIT_OBJECTS];
+ HANDLE hSemaphoreHandle;
+
+ STARTUPINFO si[MAXIMUM_WAIT_OBJECTS];
+ PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS];
+
+ char lpCommandLine[MAX_PATH] = "";
+ char ObjName[MAX_PATH] = "SHARED_SEMAPHORE";
+
+ int returnCode = 0;
+ DWORD processReturnCode = 0;
+ int testReturnCode = PASS;
+
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ DWORD dwStartTime;
+ struct TestStats testStats;
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+/*
+"While the new PAL does support named semaphore it's unclear
+if we should change the Windows PAL, since we share that w/ Rotor
+and they are still using the old PAL. For the time being it may
+make the most sense to just skip the named semaphore test on Windows
+- from an object management perspective it doesn't really gain
+us anything over what we already have."
+*/
+#ifdef PLATFORM_UNIX
+
+ ZeroMemory( objectSuffix, MAX_PATH );
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+
+ if(argc == 6)
+ {
+ strncat(ObjName, objectSuffix, MAX_PATH - (sizeof(ObjName) + 1) );
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ testStats.relationId = RELATION_ID;
+ testStats.processCount = PROCESS_COUNT;
+ testStats.threadCount = THREAD_COUNT;
+ testStats.repeatCount = REPEAT_COUNT;
+ testStats.buildNumber = getBuildNumber();
+
+ _snprintf(fileName, MAX_PATH, "main_semaphore_%d_.txt", RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening main file for write\n");
+ }
+
+ hSemaphoreHandle = CreateSemaphore(
+ NULL, /* lpSemaphoreAttributes */
+ lInitialCount, /*lInitialCount*/
+ lMaximumCount, /*lMaximumCount */
+ ObjName
+ );
+
+ if( hSemaphoreHandle == NULL)
+ {
+ Fail("Unable to create shared Semaphore handle @ Main returned error [%d]\n", GetLastError());
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+
+
+ ZeroMemory( lpCommandLine, MAX_PATH );
+ if ( _snprintf( lpCommandLine, MAX_PATH-1, "semaphore %d %d %d %d %s", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID, objectSuffix) < 0 )
+ {
+ Fail("Error: Insufficient semaphore name string length for %s for iteration [%d]\n", ObjName, i);
+ }
+
+
+ /* Zero the data structure space */
+ ZeroMemory ( &pi[i], sizeof(pi[i]) );
+ ZeroMemory ( &si[i], sizeof(si[i]) );
+
+ /* Set the process flags and standard io handles */
+ si[i].cb = sizeof(si[i]);
+
+ if(!CreateProcess( NULL, /* lpApplicationName*/
+ lpCommandLine, /* lpCommandLine */
+ NULL, /* lpProcessAttributes */
+ NULL, /* lpThreadAttributes */
+ TRUE, /* bInheritHandles */
+ 0, /* dwCreationFlags, */
+ NULL, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ &si[i], /* lpStartupInfo */
+ &pi[i] /* lpProcessInformation */
+ ))
+ {
+ Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError());
+ }
+ else
+ {
+ hProcess[i] = pi[i].hProcess;
+// Trace("Process created for [%d]\n", i);
+
+ }
+
+ }
+
+ returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE);
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError());
+ testReturnCode = FAIL;
+ }
+
+ for( i = 0; i < PROCESS_COUNT; i++ )
+ {
+ /* check the exit code from the process */
+ if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) )
+ {
+ Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n",
+ i, GetLastError() );
+
+ testReturnCode = FAIL;
+ }
+
+ if(processReturnCode == FAIL)
+ {
+ Trace( "Process [%d] failed and returned FAIL\n", i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hThread))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+
+ if(!CloseHandle(pi[i].hProcess) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i);
+ testReturnCode = FAIL;
+ }
+ }
+
+ testStats.operationTime = GetTimeDiff(dwStartTime);
+ fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber);
+ if(fclose(pFile))
+ {
+ Trace("Error: fclose failed for pFile\n");
+ testReturnCode = FAIL;
+ };
+
+ if(!CloseHandle(hSemaphoreHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for hSemaphoreHandle\n", GetLastError());
+ testReturnCode = FAIL;
+
+ }
+
+ if( testReturnCode == PASS)
+ {
+ Trace("Test Passed\n");
+ }
+ else
+ {
+ Trace("Test Failed\n");
+ }
+
+#endif //PLATFORM_UNIX
+ PAL_Terminate();
+ return testReturnCode;
+}
diff --git a/src/pal/tests/palsuite/composite/object_management/semaphore/shared/semaphore.c b/src/pal/tests/palsuite/composite/object_management/semaphore/shared/semaphore.c
new file mode 100644
index 0000000000..5143c55143
--- /dev/null
+++ b/src/pal/tests/palsuite/composite/object_management/semaphore/shared/semaphore.c
@@ -0,0 +1,351 @@
+// 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 Code: main.c and semaphore.c
+** main.c creates process and waits for all processes to get over
+** semaphore.c creates a semaphore and then calls threads which will contend for the semaphore
+**
+** This test is for Object Management Test case for semaphore where Object type is shareable.
+** Algorithm
+** o Main Process Creates OBJECT_TYPE Object
+** o Create PROCESS_COUNT processes aware of the Shared Object
+**
+**
+**
+**============================================================
+*/
+
+#include <palsuite.h>
+#include "resultbuffer.h"
+#include "resulttime.h"
+
+#define TIMEOUT 5000
+/* Test Input Variables */
+unsigned int USE_PROCESS_COUNT = 0;
+unsigned int THREAD_COUNT = 0;
+unsigned int REPEAT_COUNT = 0;
+unsigned int RELATION_ID= 0;
+
+/* Capture statistics at per thread basis */
+struct statistics{
+ unsigned int processId;
+ unsigned int operationsFailed;
+ unsigned int operationsPassed;
+ unsigned int operationsTotal;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+struct ProcessStats{
+ unsigned int processId;
+ DWORD operationTime;
+ unsigned int relationId;
+};
+
+/* Semaphore variables */
+unsigned long lInitialCount = 1; /* Signaled */
+unsigned long lMaximumCount = 1; /* Maximum value of 1 */
+
+HANDLE StartTestsEvHandle = NULL;
+HANDLE hSemaphoreHandle = NULL;
+
+/* Results Buffer */
+ResultBuffer *resultBuffer = NULL;
+
+int testStatus;
+
+const char sTmpEventName[MAX_PATH] = "StartTestEvent";
+char objectSuffix[MAX_PATH];
+
+void PALAPI Run_Thread(LPVOID lpParam);
+
+int GetParameters( int argc, char **argv)
+{
+ if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?"))
+ || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H"))
+ {
+ printf("PAL -Composite Object Management event Test\n");
+ printf("Usage:\n");
+ printf("main\n\t[PROCESS_COUNT (greater than 1)] \n");
+ printf("\t[THREAD_COUNT (greater than 1)] \n");
+ printf("\t[REPEAT_COUNT (greater than 1)]\n");
+ printf("\t[RELATION_ID [greater than or equal to 1]\n");
+ printf("\t[Object Name Suffix]\n");
+ return -1;
+ }
+
+ USE_PROCESS_COUNT = atoi(argv[1]);
+ if(USE_PROCESS_COUNT < 0)
+ {
+ printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 0 \n");
+ return -1;
+ }
+
+ THREAD_COUNT = atoi(argv[2]);
+ if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) )
+ {
+ printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS);
+ return -1;
+ }
+
+ REPEAT_COUNT = atoi(argv[3]);
+ if( REPEAT_COUNT < 1)
+ {
+ printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n");
+ return -1;
+ }
+
+ RELATION_ID = atoi(argv[4]);
+ if( RELATION_ID < 1)
+ {
+ printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n");
+ return -1;
+ }
+
+ if(argc == 6)
+ {
+ strncpy(objectSuffix, argv[5], MAX_PATH-1);
+ }
+
+ return 0;
+}
+
+ int __cdecl main(INT argc, CHAR **argv)
+{
+ unsigned int i = 0;
+ HANDLE hThread[MAXIMUM_WAIT_OBJECTS];
+ DWORD threadId[MAXIMUM_WAIT_OBJECTS];
+
+ char ObjName[MAX_PATH] = "SHARED_SEMAPHORE";
+ DWORD dwParam = 0;
+
+ int returnCode = 0;
+
+ /* Variables to capture the file name and the file pointer at thread level*/
+ char fileName[MAX_PATH];
+ FILE *pFile = NULL;
+ struct statistics* buffer = NULL;
+ int statisticsSize = 0;
+
+ /* Variables to capture the file name and the file pointer at process level*/
+ char processFileName[MAX_PATH];
+ FILE *pProcessFile = NULL;
+ struct ProcessStats processStats;
+ DWORD dwStartTime;
+
+ testStatus = PASS;
+
+ ZeroMemory( objectSuffix, MAX_PATH );
+
+ if(0 != (PAL_Initialize(argc, argv)))
+ {
+ return ( FAIL );
+ }
+
+ if(GetParameters(argc, argv))
+ {
+ Fail("Error in obtaining the parameters\n");
+ }
+// Trace("Process created, value of process count is [%d]\n", USE_PROCESS_COUNT);
+
+ if(argc == 6)
+ {
+ strncat(ObjName , objectSuffix, MAX_PATH - (sizeof(ObjName) + 1) );
+ }
+
+ /* Register the start time */
+ dwStartTime = GetTickCount();
+ processStats.relationId = RELATION_ID;
+ processStats.processId = USE_PROCESS_COUNT;
+
+ _snprintf(processFileName, MAX_PATH, "%d_process_semaphore_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pProcessFile = fopen(processFileName, "w+");
+ if(pProcessFile == NULL)
+ {
+ Fail("Error in opening process File file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+
+ statisticsSize = sizeof(struct statistics);
+
+ _snprintf(fileName, MAX_PATH, "%d_thread_semaphore_%d_.txt", USE_PROCESS_COUNT, RELATION_ID);
+ pFile = fopen(fileName, "w+");
+ if(pFile == NULL)
+ {
+ Fail("Error in opening file for write for process [%d]\n", USE_PROCESS_COUNT);
+ }
+ // For each thread we will log operations failed (int), passed (int), total (int)
+ // and number of ticks (DWORD) for the operations
+ resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize);
+
+ /* Create Start Tests event */
+ StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/
+ TRUE, /* bManualReset */
+ FALSE, /* bInitialState */
+ NULL); /* name of Event */
+
+ if( StartTestsEvHandle == NULL )
+ {
+ Fail("Error:%d: Unexpected failure "
+ "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT );
+
+ }
+
+ hSemaphoreHandle = CreateSemaphore(
+ NULL, /* lpSemaphoreAttributes */
+ lInitialCount, /*lInitialCount*/
+ lMaximumCount, /*lMaximumCount */
+ ObjName
+ );
+
+
+ if( (hSemaphoreHandle == NULL) || (GetLastError() != ERROR_ALREADY_EXISTS) )
+ {
+ Fail("Unable to create Semaphore handle for process id [%d], returned error [%d], expected ERROR_ALREADY_EXISTS\n", i, GetLastError());
+ }
+
+ /* We already assume that the Semaphore was created previously*/
+
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ dwParam = (int) i;
+ //Create thread
+ hThread[i] = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE)Run_Thread,/* thread function */
+ (LPVOID)dwParam, /* argument to thread function */
+ 0, /* use default creation flags */
+ &threadId[i] /* returns the thread identifier*/
+ );
+
+ if(hThread[i] == NULL)
+ {
+ Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+ }
+
+ if (!SetEvent(StartTestsEvHandle))
+ {
+ Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError());
+ }
+
+ /* Test running */
+ returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE);
+
+ if( WAIT_OBJECT_0 != returnCode )
+ {
+ Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError());
+ testStatus = FAIL;
+ }
+
+ processStats.operationTime = GetTimeDiff(dwStartTime);
+
+ /* Write to a file*/
+ if(pFile!= NULL)
+ {
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ buffer = (struct statistics *)resultBuffer->getResultBuffer(i);
+ returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId );
+// Trace("Iteration %d over\n", i);
+
+ }
+ }
+ fclose(pFile);
+ /* Logging for the test case over, clean up the handles */
+
+// Trace("Test Thread %d done\n", USE_PROCESS_COUNT);
+ for( i = 0; i < THREAD_COUNT; i++ )
+ {
+ if(!CloseHandle(hThread[i]) )
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i);
+ testStatus = FAIL;
+ }
+ }
+
+ if(!CloseHandle(StartTestsEvHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ if(!CloseHandle(hSemaphoreHandle))
+ {
+ Trace("Error:%d: CloseHandle failed for Process [%d] hSemaphoreHandle\n", GetLastError(), USE_PROCESS_COUNT);
+ testStatus = FAIL;
+ }
+
+ PAL_Terminate();
+ return PASS;
+}
+
+void PALAPI Run_Thread (LPVOID lpParam)
+{
+ unsigned int i = 0;
+ DWORD dwWaitResult;
+
+ int Id=(int)lpParam;
+
+ struct statistics stats;
+ DWORD dwStartTime;
+
+ stats.relationId = RELATION_ID;
+ stats.processId = USE_PROCESS_COUNT;
+ stats.operationsFailed = 0;
+ stats.operationsPassed = 0;
+ stats.operationsTotal = 0;
+ stats.operationTime = 0;
+
+ dwWaitResult = WaitForSingleObject(
+ StartTestsEvHandle, // handle to start test handle
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+ Fail("Error while waiting for StartTest Event@ thread %d, RC is %d, Error is %d\n", Id, dwWaitResult, GetLastError());
+ }
+
+ dwStartTime = GetTickCount();
+
+ for( i = 0; i < REPEAT_COUNT; i++ )
+ {
+ dwWaitResult = WaitForSingleObject(
+ hSemaphoreHandle, // handle to Semaphore
+ TIMEOUT);
+
+ if(dwWaitResult != WAIT_OBJECT_0)
+ {
+// Trace("Error while waiting for onject @ thread %d, # iter %d, RC is %d, Error is %d\n", Id, i, dwWaitResult, GetLastError());
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ testStatus = FAIL;
+ continue;
+ }
+ if (! ReleaseSemaphore(hSemaphoreHandle, 1, NULL))
+ {
+ // Deal with error.
+ // Trace("Error while releasing Semaphore @ thread %d # iter %d\n", Id, i);
+ stats.operationsFailed += 1;
+ stats.operationsTotal += 1;
+ // Probably need to have while true loop to attempt to release semaphore..
+ testStatus = FAIL;
+ continue;
+ }
+
+ stats.operationsTotal += 1;
+ stats.operationsPassed += 1;
+// Trace("Successs while releasing Semaphore @ iteration %d -> thread %d -> Process %d\n", i, Id, USE_PROCESS_COUNT);
+
+ }
+
+ stats.operationTime = GetTimeDiff(dwStartTime);
+ if(resultBuffer->LogResult(Id, (char *)&stats))
+ {
+ Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT);
+ }
+ // Trace("Thread %d over for process %d\n", Id, USE_PROCESS_COUNT);
+}