366 lines
11 KiB
C
366 lines
11 KiB
C
/*
|
|
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
|
*
|
|
* This program and the accompanying materials are made available under the
|
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
|
* v. 1.0 which is available at
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
*
|
|
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
|
*/
|
|
#include "CUnit/Test.h"
|
|
#include "os/os.h"
|
|
|
|
#ifdef __VXWORKS__
|
|
# ifdef _WRS_KERNEL
|
|
# define FORCE_SCHEDULING() taskDelay(1)
|
|
# else
|
|
# define FORCE_SCHEDULING() sched_yield()
|
|
# endif
|
|
#else
|
|
# define FORCE_SCHEDULING()
|
|
#endif
|
|
|
|
#define BUSYLOOP (100000)
|
|
#define MAX_LOOPS (20)
|
|
|
|
typedef struct {
|
|
os_mutex global_mutex;
|
|
os_threadId global_data;
|
|
int nolock_corrupt_count;
|
|
int nolock_loop_count;
|
|
int lock_corrupt_count;
|
|
int lock_loop_count;
|
|
int trylock_corrupt_count;
|
|
int trylock_loop_count;
|
|
int trylock_busy_count;
|
|
int stop;
|
|
} shared_data;
|
|
|
|
os_threadAttr mutex_os_threadAttr;
|
|
os_threadId mutex_os_threadId[4];
|
|
os_time delay1 = { 5, 0 };
|
|
os_time pdelay = { 1, 0 };
|
|
char buffer[512];
|
|
int supported_resultBusy;
|
|
int loop;
|
|
static shared_data *sd;
|
|
char filePath[255];
|
|
|
|
uint32_t concurrent_lock_thread (_In_opt_ void *arg)
|
|
{
|
|
int j;
|
|
int loopc = 0;
|
|
int printed = 0;
|
|
|
|
while (!sd->stop)
|
|
{
|
|
if (arg) os_mutexLock (&sd->global_mutex);
|
|
sd->global_data = os_threadIdSelf();
|
|
|
|
FORCE_SCHEDULING();
|
|
|
|
for (j = 0; j < BUSYLOOP; j++);
|
|
if (os_threadIdToInteger(sd->global_data) !=
|
|
os_threadIdToInteger(os_threadIdSelf()))
|
|
{
|
|
if (arg)
|
|
{
|
|
sd->lock_corrupt_count++;
|
|
}
|
|
else
|
|
{
|
|
sd->nolock_corrupt_count++;
|
|
}
|
|
if (!printed) {
|
|
printed++;
|
|
}
|
|
}
|
|
if (arg)
|
|
{
|
|
sd->lock_loop_count++;
|
|
os_mutexUnlock (&sd->global_mutex);
|
|
}
|
|
else
|
|
{
|
|
sd->nolock_loop_count++;
|
|
}
|
|
|
|
FORCE_SCHEDULING();
|
|
|
|
for (j = 0; j < BUSYLOOP; j++);
|
|
loopc++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32_t concurrent_trylock_thread (_In_opt_ void *arg)
|
|
{
|
|
int j;
|
|
int loopc = 0;
|
|
int printed = 0;
|
|
os_result result;
|
|
|
|
while (!sd->stop)
|
|
{
|
|
if (arg)
|
|
{
|
|
while ((result = os_mutexTryLock (&sd->global_mutex))
|
|
!= os_resultSuccess)
|
|
{
|
|
if (result == os_resultBusy)
|
|
{
|
|
sd->trylock_busy_count++;
|
|
}
|
|
FORCE_SCHEDULING();
|
|
}
|
|
}
|
|
sd->global_data = os_threadIdSelf();
|
|
|
|
FORCE_SCHEDULING();
|
|
|
|
for (j = 0; j < BUSYLOOP; j++);
|
|
if (os_threadIdToInteger(sd->global_data) !=
|
|
os_threadIdToInteger(os_threadIdSelf()))
|
|
{
|
|
if (arg)
|
|
{
|
|
sd->trylock_corrupt_count++;
|
|
}
|
|
else
|
|
{
|
|
sd->nolock_corrupt_count++;
|
|
}
|
|
if (!printed) {
|
|
printed++;
|
|
}
|
|
}
|
|
if (arg)
|
|
{
|
|
sd->trylock_loop_count++;
|
|
os_mutexUnlock (&sd->global_mutex);
|
|
}
|
|
else
|
|
{
|
|
sd->nolock_loop_count++;
|
|
}
|
|
|
|
FORCE_SCHEDULING();
|
|
|
|
for (j = 0; j < BUSYLOOP; j++);
|
|
loopc++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
CU_Init(os_mutex)
|
|
{
|
|
printf ( "Run os_mutex_Initialize\n" );
|
|
|
|
os_osInit();
|
|
|
|
return 0;
|
|
}
|
|
|
|
CU_Clean(os_mutex)
|
|
{
|
|
printf("Run os_mutex_Cleanup\n");
|
|
|
|
os_osExit();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This test only checks a single-threaded use-case; just API availability.*/
|
|
CU_Test(os_mutex, basic)
|
|
{
|
|
os_mutex m;
|
|
os_result r;
|
|
|
|
printf("Starting os_mutex_basic\n");
|
|
|
|
os_mutexInit(&m);
|
|
os_mutexLock(&m);
|
|
os_mutexUnlock(&m);
|
|
r = os_mutexLock_s(&m);
|
|
CU_ASSERT_EQUAL(r, os_resultSuccess); /* Failure can't be forced */
|
|
os_mutexUnlock(&m);
|
|
os_mutexDestroy(&m);
|
|
printf("Ending os_mutex_basic\n");
|
|
}
|
|
|
|
#define RUNTIME_SEC (4)
|
|
#define NUM_THREADS (8)
|
|
#define OS_STRESS_STOP (0)
|
|
#define OS_STRESS_GO (1)
|
|
#define THREAD_NAME_LEN (8)
|
|
|
|
struct os_mutex_stress {
|
|
os_threadId tid;
|
|
os_mutex m;
|
|
os_atomic_uint32_t * flag;
|
|
char name[THREAD_NAME_LEN];
|
|
};
|
|
|
|
static uint32_t
|
|
os_mutex_init_thr(
|
|
void *args)
|
|
{
|
|
struct os_mutex_stress *state = (struct os_mutex_stress *)args;
|
|
os_result r;
|
|
uint32_t iterations = 0;
|
|
|
|
do {
|
|
os_mutexInit(&state->m);
|
|
r = os_mutexLock_s(&state->m); /* Use the mutex to check that all is OK. */
|
|
CU_ASSERT_EQUAL(r, os_resultSuccess); /* Failure can't be forced. */
|
|
os_mutexUnlock(&state->m);
|
|
os_mutexDestroy(&state->m);
|
|
iterations++;
|
|
} while ( os_atomic_ld32(state->flag) != OS_STRESS_STOP && r == os_resultSuccess);
|
|
|
|
printf("%s <%"PRIxMAX">: Performed %u iterations. Stopping now.\n", state->name, os_threadIdToInteger(os_threadIdSelf()), iterations);
|
|
return r != os_resultSuccess; /* Return true on faulure */
|
|
}
|
|
|
|
CU_Test(os_mutex, init_stress)
|
|
{
|
|
struct os_mutex_stress threads[NUM_THREADS];
|
|
os_threadAttr tattr;
|
|
unsigned i;
|
|
os_atomic_uint32_t flag = OS_ATOMIC_UINT32_INIT(OS_STRESS_GO);
|
|
os_time runtime = { .tv_sec = RUNTIME_SEC, .tv_nsec = 0 };
|
|
|
|
printf("Starting os_mutex_init_stress\n");
|
|
|
|
os_threadAttrInit(&tattr);
|
|
for ( i = 0; i < NUM_THREADS; i++ ) {
|
|
(void) snprintf(&threads[i].name[0], THREAD_NAME_LEN, "thr%u", i);
|
|
threads[i].flag = &flag;
|
|
os_threadCreate(&threads[i].tid, threads[i].name, &tattr, &os_mutex_init_thr, &threads[i]);
|
|
printf("main <%"PRIxMAX">: Started thread '%s' with thread-id %" PRIxMAX "\n", os_threadIdToInteger(os_threadIdSelf()), threads[i].name, os_threadIdToInteger(threads[i].tid));
|
|
}
|
|
|
|
printf("main <%"PRIxMAX">: Test will run for ~%ds with %d threads\n", os_threadIdToInteger(os_threadIdSelf()), RUNTIME_SEC, NUM_THREADS);
|
|
os_nanoSleep(runtime);
|
|
os_atomic_st32(&flag, OS_STRESS_STOP);
|
|
|
|
for ( ; i != 0; i-- ) {
|
|
uint32_t thread_failed;
|
|
os_threadWaitExit(threads[i - 1].tid, &thread_failed);
|
|
printf("main <%"PRIxMAX">: Thread %s <%" PRIxMAX "> stopped with result %s.\n", os_threadIdToInteger(os_threadIdSelf()), threads[i - 1].name, os_threadIdToInteger(threads[i - 1].tid), thread_failed ? "FAILED" : "PASS");
|
|
|
|
CU_ASSERT_FALSE(thread_failed);
|
|
}
|
|
printf("Ending os_mutex_init_stress\n");
|
|
}
|
|
|
|
CU_Test(os_mutex, lock, false)
|
|
{
|
|
/* Test critical section access with locking and PRIVATE scope */
|
|
printf ("Starting tc_os_mutex_lock_001\n");
|
|
os_threadAttrInit (&mutex_os_threadAttr);
|
|
|
|
FORCE_SCHEDULING();
|
|
|
|
delay1.tv_sec = 3;
|
|
printf ("Testing for %d.%9.9d seconds without lock\n", delay1.tv_sec, delay1.tv_nsec);
|
|
sd->stop = 0;
|
|
sd->nolock_corrupt_count = 0;
|
|
sd->nolock_loop_count = 0;
|
|
sd->lock_corrupt_count = 0;
|
|
sd->lock_loop_count = 0;
|
|
sd->trylock_corrupt_count = 0;
|
|
sd->trylock_loop_count = 0;
|
|
sd->trylock_busy_count = 0;
|
|
os_threadCreate (&mutex_os_threadId[0], "thr0", &mutex_os_threadAttr, &concurrent_lock_thread, NULL);
|
|
os_threadCreate (&mutex_os_threadId[1], "thr1", &mutex_os_threadAttr, &concurrent_lock_thread, NULL);
|
|
os_threadCreate (&mutex_os_threadId[2], "thr2", &mutex_os_threadAttr, &concurrent_trylock_thread, NULL);
|
|
os_threadCreate (&mutex_os_threadId[3], "thr3", &mutex_os_threadAttr, &concurrent_trylock_thread, NULL);
|
|
os_nanoSleep (delay1);
|
|
sd->stop = 1;
|
|
os_threadWaitExit (mutex_os_threadId[0], NULL);
|
|
os_threadWaitExit (mutex_os_threadId[1], NULL);
|
|
os_threadWaitExit (mutex_os_threadId[2], NULL);
|
|
os_threadWaitExit (mutex_os_threadId[3], NULL);
|
|
printf ("All threads stopped\n");
|
|
|
|
delay1.tv_sec = 3;
|
|
printf ("Testing for %d.%9.9d seconds with lock\n", delay1.tv_sec, delay1.tv_nsec);
|
|
sd->stop = 0;
|
|
sd->nolock_corrupt_count = 0;
|
|
sd->nolock_loop_count = 0;
|
|
sd->lock_corrupt_count = 0;
|
|
sd->lock_loop_count = 0;
|
|
sd->trylock_corrupt_count = 0;
|
|
sd->trylock_loop_count = 0;
|
|
sd->trylock_busy_count = 0;
|
|
os_threadCreate (&mutex_os_threadId[0], "thr0", &mutex_os_threadAttr, &concurrent_lock_thread, (void *)1);
|
|
os_threadCreate (&mutex_os_threadId[1], "thr1", &mutex_os_threadAttr, &concurrent_lock_thread, (void *)1);
|
|
os_threadCreate (&mutex_os_threadId[2], "thr2", &mutex_os_threadAttr, &concurrent_trylock_thread, (void *)1);
|
|
os_threadCreate (&mutex_os_threadId[3], "thr3", &mutex_os_threadAttr, &concurrent_trylock_thread, (void *)1);
|
|
os_nanoSleep (delay1);
|
|
sd->stop = 1;
|
|
os_threadWaitExit (mutex_os_threadId[0], NULL);
|
|
os_threadWaitExit (mutex_os_threadId[1], NULL);
|
|
os_threadWaitExit (mutex_os_threadId[2], NULL);
|
|
os_threadWaitExit (mutex_os_threadId[3], NULL);
|
|
printf ("All threads stopped\n");
|
|
|
|
CU_ASSERT (sd->lock_corrupt_count == 0 || sd->lock_loop_count > 0);
|
|
|
|
/* Lock mutex with PRIVATE scope and Success result */
|
|
printf ("Starting tc_os_mutex_lock_002\n");
|
|
os_mutexLock (&sd->global_mutex); //Cannot be checked
|
|
os_mutexUnlock (&sd->global_mutex);
|
|
|
|
/* Lock mutex with PRIVATE scope and Fail result */
|
|
printf ("Starting tc_os_mutex_lock_003\n");
|
|
printf ("N.A - Failure cannot be forced\n");
|
|
|
|
/* mutexLock_s with PRIVATE scope and Success result */
|
|
printf ("Starting tc_os_mutex_lock_004\n");
|
|
CU_ASSERT (os_mutexLock_s (&sd->global_mutex) == os_resultSuccess);
|
|
os_mutexUnlock (&sd->global_mutex);
|
|
|
|
printf ("Ending os_mutex_lock\n");
|
|
}
|
|
|
|
CU_Test(os_mutex, trylock, false)
|
|
{
|
|
os_result result;
|
|
|
|
/* Test critical section access with trylocking and PRIVATE scope */
|
|
printf ("Starting os_mutex_trylock_001\n");
|
|
CU_ASSERT (sd->trylock_corrupt_count == 0 || sd->trylock_loop_count > 0);
|
|
|
|
/* TryLock mutex with PRIVATE scope and Success result */
|
|
printf ("Starting os_mutex_trylock_002\n");
|
|
result = os_mutexTryLock (&sd->global_mutex);
|
|
CU_ASSERT (result == os_resultSuccess);
|
|
|
|
/* TryLock mutex with PRIVATE scope and Busy result */
|
|
printf ("Starting os_mutex_trylock_003\n");
|
|
#if defined(__VXWORKS__) && !defined(_WRS_KERNEL)
|
|
printf ("N.A - Mutexes are recursive on VxWorks RTP so this test is disabled\n");
|
|
#endif
|
|
|
|
result = os_mutexTryLock (&sd->global_mutex);
|
|
CU_ASSERT (result == os_resultBusy);
|
|
|
|
printf ("Ending os_mutex_trylock\n");
|
|
}
|
|
|
|
CU_Test(os_mutex, destroy, false)
|
|
{
|
|
/* Deinitialize mutex with PRIVATE scope and Success result */
|
|
printf ("Starting os_mutex_destroy_001\n");
|
|
os_mutexDestroy(&sd->global_mutex); // Cannot be checked directly - Success is assumed
|
|
|
|
/* Deinitialize mutex with PRIVATE scope and Fail result */
|
|
printf ("Starting os_mutex_destroy_002\n");
|
|
printf ("N.A - Failure cannot be forced\n");
|
|
|
|
printf ("Ending os_mutex_destroy\n");
|
|
}
|