/*************************************************************************\ * Copyright (c) 2012 ITER Organization * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* epicsThreadHooksTest.c */ #include #include "epicsThread.h" #include "epicsExit.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "errlog.h" #include "epicsUnitTest.h" #include "testMain.h" #define MAX_THREADS 16 #define TEST_THREADS 8 #define NUM_HOOKS 4 static int order[MAX_THREADS][NUM_HOOKS]; static int cnt[MAX_THREADS]; static int mine[MAX_THREADS]; static int mapped[MAX_THREADS]; static epicsThreadId tid[MAX_THREADS]; static epicsMutexId tidLock; static epicsEventId shutdown[TEST_THREADS]; static int newThreadIndex(epicsThreadId id) { int i = 0; if (epicsMutexLock(tidLock)) testAbort("newThreadIndex: Locking problem"); while (i < MAX_THREADS && tid[i] != 0) i++; if (i < MAX_THREADS) tid[i] = id; else testAbort("newThreadIndex: Too many threads!"); epicsMutexUnlock(tidLock); return i; } static int findThreadIndex(epicsThreadId id) { int i = 0; while (i < MAX_THREADS && tid[i] != id) i++; return i; } static void atExitHook1 (void *arg) { int no = findThreadIndex(epicsThreadGetIdSelf()); if (no < MAX_THREADS) order[no][3] = cnt[no]++; } static void atExitHook2 (void *arg) { int no = findThreadIndex(epicsThreadGetIdSelf()); if (no < MAX_THREADS) order[no][2] = cnt[no]++; } static void startHook1 (epicsThreadId id) { int no = newThreadIndex(id); if (no < MAX_THREADS) order[no][0] = cnt[no]++; epicsAtThreadExit(atExitHook1, NULL); } static void startHook2 (epicsThreadId id) { int no = findThreadIndex(id); if (no < MAX_THREADS) order[no][1] = cnt[no]++; epicsAtThreadExit(atExitHook2, NULL); } static void my_thread (void *arg) { int no = findThreadIndex(epicsThreadGetIdSelf()); if (no < MAX_THREADS) mine[no] = 1; epicsEventMustWait((epicsEventId) arg); } static void mapper (epicsThreadId id) { int no = findThreadIndex(id); if (no < MAX_THREADS) mapped[no]++; } MAIN(epicsThreadHooksTest) { int i; int ok; testPlan(TEST_THREADS + 1); tidLock = epicsMutexMustCreate(); if (epicsThreadHookAdd(startHook1)) testAbort("startHook1 registration failed"); if (epicsThreadHookAdd(startHook2)) testAbort("startHook2 registration failed"); for (i = 0; i < TEST_THREADS; i++) { char name[10]; shutdown[i] = epicsEventCreate(epicsEventEmpty); sprintf(name, "t%d", (int) i); epicsThreadCreate(name, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), my_thread, shutdown[i]); } epicsThreadSleep(1.0); epicsThreadMap(mapper); ok = 1; for (i = 0; i < MAX_THREADS; i++) { if (!mine[i]) continue; if (mapped[i] != 1) { ok = 0; testDiag("mapped[%d] = %d", i, mapped[i]); } } testOk(ok, "All my tasks covered once by epicsThreadMap"); for (i = 0; i < TEST_THREADS; i++) { epicsEventSignal(shutdown[i]); } epicsThreadSleep(1.0); for (i = 0; i < MAX_THREADS; i++) { int j; if (!mine[i]) continue; ok = 1; for (j = 0; j < NUM_HOOKS; j++) { if (order[i][j] != j) { ok = 0; testDiag("order[%d][%d] = %d", i, j, order[i][j]); } } testOk(ok, "All hooks for task %d called in correct order", i); } epicsThreadHookDelete(startHook1); epicsThreadHookDelete(startHook2); return testDone(); }