From b02a252b5b890827faafafde06534cc261da2eb8 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Sun, 14 Jun 2026 09:34:30 +0000 Subject: [PATCH] 0525 core: import NSS cache work scheduling runtime --- include/core/include/schedule.h | 33 +- src/core/CMakeLists.txt | 1 + src/core/library/os/delay.c | 38 ++ src/core/nss/cache/work.c | 760 +++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/core/CMakeLists.txt | 1 + tests/core/work/CMakeLists.txt | 3 + tests/core/work/test_nwcore_work.c | 64 +++ 8 files changed, 895 insertions(+), 6 deletions(-) create mode 100644 src/core/nss/cache/work.c create mode 100644 tests/core/work/CMakeLists.txt create mode 100644 tests/core/work/test_nwcore_work.c diff --git a/include/core/include/schedule.h b/include/core/include/schedule.h index 35d7716..4638b0b 100644 --- a/include/core/include/schedule.h +++ b/include/core/include/schedule.h @@ -104,6 +104,27 @@ extern void fillInWork( voidfunc_t procedureToCall, void *userParameter); +extern STATUS ZOS_ScheduleWorkToDo(struct WorkToDoStructure *work); +extern STATUS ZOS_ScheduleFastWorkToDo(struct WorkToDoStructure *work, int priority); +extern STATUS ZOS_CancelWorkToDo(struct WorkToDoStructure *work); + +#define ScheduleWork(_work) \ + ZOS_ScheduleWorkToDo((struct WorkToDoStructure *)(_work)) +#define ScheduleFastWork(_work) \ + ZOS_ScheduleFastWorkToDo((struct WorkToDoStructure *)(_work), 1) +#define CancelWork(_work) \ + ZOS_CancelWorkToDo((struct WorkToDoStructure *)(_work)) + +#ifndef PERIODIC_YIELD_COUNT +#define PERIODIC_YIELD_COUNT 50 +#endif +#ifndef PERIODIC_YIELD +#define PERIODIC_YIELD() Yield() +#endif +#ifndef CHECK_INTERRUPTS +#define CHECK_INTERRUPTS() ((void)0) +#endif + #define Wait() ZOS_Sleep() #define Continue(pid) ZOS_WakeUp((THREAD)(pid)) #define Yield() ZOS_YieldThread() @@ -228,14 +249,14 @@ extern STATUS DestroyThread( LONG threadID); /* MP APIs*/ -extern ERROR kDestroyThread(THREAD ThreadHandle); +extern STATUS kDestroyThread(THREAD ThreadHandle); extern THREAD kCurrentThread(void); -extern ERROR kDelayThread(unsigned int); +extern STATUS kDelayThread(unsigned int); #define ThreadId() (ADDR)kCurrentThread() -extern ERROR kWakeUp(THREAD); +extern STATUS kWakeUp(THREAD); extern void kSleep(); extern void kYieldThread(); @@ -408,9 +429,9 @@ typedef struct zWorkProc2_s - extern ERROR kScheduleWorkToDo(struct WorkToDoStructure *); - extern ERROR kScheduleFastWorkTo(struct WorkToDoStructure *); - extern ERROR kCancelWorkToDo(struct WorkToDoStructure *); + extern STATUS kScheduleWorkToDo(struct WorkToDoStructure *); + extern STATUS kScheduleFastWorkTo(struct WorkToDoStructure *); + extern STATUS kCancelWorkToDo(struct WorkToDoStructure *); #define ScheduleWork(_work) \ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 914f2b9..9d3da49 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -145,6 +145,7 @@ add_library(nwcore SHARED nss/cache/control.c nss/cache/asyncio.c nss/cache/bond.c + nss/cache/work.c library/fsm/fsmnw.c library/latch/intlatch.c library/latch/latch.c diff --git a/src/core/library/os/delay.c b/src/core/library/os/delay.c index 6f97051..b839c3b 100644 --- a/src/core/library/os/delay.c +++ b/src/core/library/os/delay.c @@ -43,6 +43,7 @@ #include #include +#include /************************************************************************** * Delay the given number of milliseconeds @@ -107,3 +108,40 @@ LONG GetSuperHighResolutionTimer(void) clock_gettime(CLOCK_MONOTONIC, &now); return (LONG)(now.tv_sec * 1000000L + now.tv_nsec / 1000L); } + +STATUS ZOS_ScheduleWorkToDo(struct WorkToDoStructure *work) +{ + zWork_s *zwork = (zWork_s *)work; + BOOL hadLock = MPKNSS_I_OWN_SPINLOCK(); + + if (zwork == NULL || zwork->ProcedureToCall == NULL) + { + return zERR_BAD_PARAMETER_VALUE; + } + + if (hadLock) + { + MPKNSS_UNLOCK(); + } + + ((void (*)(void *))zwork->ProcedureToCall)(work); + + if (hadLock) + { + MPKNSS_LOCK(); + } + + return zOK; +} + +STATUS ZOS_ScheduleFastWorkToDo(struct WorkToDoStructure *work, int priority) +{ + (void)priority; + return ZOS_ScheduleWorkToDo(work); +} + +STATUS ZOS_CancelWorkToDo(struct WorkToDoStructure *work) +{ + (void)work; + return zOK; +} diff --git a/src/core/nss/cache/work.c b/src/core/nss/cache/work.c new file mode 100644 index 0000000..7f96e61 --- /dev/null +++ b/src/core/nss/cache/work.c @@ -0,0 +1,760 @@ +/**************************************************************************** + | + | (C) Copyright 1985, 1991, 1993, 1996-1998 Novell, Inc. + | All Rights Reserved. + | + | This program is free software; you can redistribute it and/or + | modify it under the terms of version 2 of the GNU General Public + | License as published by the Free Software Foundation. + | + | This program is distributed in the hope that it will be useful, + | but WITHOUT ANY WARRANTY; without even the implied warranty of + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + | GNU General Public License for more details. + | + | You should have received a copy of the GNU General Public License + | along with this program; if not, contact Novell, Inc. + | + | To contact Novell about this file by physical or electronic mail, + | you may find current contact information at www.novell.com + | + |*************************************************************************** + | + | NetWare Advance File Services (NSS) Initialization module + | + |--------------------------------------------------------------------------- + | + | $Author: vandana $ + | $Date: 2007-01-03 04:50:48 +0530 (Wed, 03 Jan 2007) $ + | + | $RCSfile$ + | $Revision: 1802 $ + | + |--------------------------------------------------------------------------- + | This module is used to: + | Routines for allocating and managing structures for + | spinning off worktodos. + +-------------------------------------------------------------------------*/ +#include /* NetWare Includes*/ + +#include /* NSS Library includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +NINT WorkWaitingCount = 0; +NINT WorkHighWaitingCount = 0; +NINT WorkLowWaitingCount = 0; + +#define ACTIVE_WORK_WAIT_LIST 0 +#define ACTIVE_WORK_HIGH_WAIT_LIST 1 + +#define WORK_COUNT_HIGH 10 +#define WORK_COUNT_LOW 8 + +CIRhead_t QueuedThreads; +CIRhead_t QueuedThreadsHigh; +CIRhead_t WorkWaitingListHead; +CIRhead_t WorkHighWaitingListHead; +CIRhead_t WorkLowWaitingListHead; +NINT ActiveWorkWaitingList = ACTIVE_WORK_WAIT_LIST; + +#if zLINUX +extern STATUS kSchedulePrioWorkToDo(struct WorkToDoStructure *work); +#endif + +/************************************************************************** + * Because work control needs to use a different dispatch algorithm + * then other resouce allocation routines, we use the CONTROL initialization + * but use our own allocation and free routines. + ***************************************************************************/ +typedef struct Work_s +{ + zWork_s worktodo; + FsmLite_s *fsm; +#if NSS_DEBUG IS_ENABLED + LONG magic; + char *name; + NINT instance; +#endif +} Work_s; + +ControlStore_s WorkControl; +ControlStore_s WorkControlHigh; +ControlStore_s WorkControlLow; +NINT PendingWork = 0; + +extern void WORK_Run(Work_s *work); +extern void WORK_RunHigh(Work_s *work); +extern void WORK_RunLow(Work_s *work); + +/************************************************************************** + * + ***************************************************************************/ +void WORK_Init (Work_s *work) +{ + work->worktodo.ProcedureToCall = WORK_Run; + work->worktodo.WorkResourceTag = COMN_Resource.workToDoRTag; + +#if NSS_DEBUG IS_ENABLED + { + static NINT instance = 0; + + work->magic = 0xbaddad; + work->name = MSGNot("Work"); + work->instance = instance++; + } +#endif +} + +void WORK_InitHigh (Work_s *work) +{ + work->worktodo.ProcedureToCall = WORK_RunHigh; + work->worktodo.WorkResourceTag = COMN_Resource.workToDoRTag; + +#if NSS_DEBUG IS_ENABLED + { + static NINT instance = 0; + + work->magic = 0xbaddad; + work->name = MSGNot("WorkHigh"); + work->instance = instance++; + } +#endif +} + +void WORK_InitLow (Work_s *work) +{ + work->worktodo.ProcedureToCall = WORK_RunLow; + work->worktodo.WorkResourceTag = COMN_Resource.workToDoRTag; + +#if NSS_DEBUG IS_ENABLED + { + static NINT instance = 0; + + work->magic = 0xbaddad; + work->name = MSGNot("WorkLow"); + work->instance = instance++; + } +#endif +} + +#if NSS_DEBUG IS_ENABLED + +/* + * This code is used to verify that WORK_Schedule() is running threads + * within an acceptable time frame. This was written because under + * MOAB we saw some threads not ruuning for numerous seconds. + */ + +#define TT_NOT_RUNNING 0 +#define TT_RUNNING 1 +#define TT_ABORT_REQUEST 2 + +int gNSSThreadTestState = TT_NOT_RUNNING; +QUAD gNSSThreadTestCount = 0; +Time_t gNSSThreadTestTimeLast; +Time_t gNSSThreadTestTimeAssert = 20; /* Number of seconds before asserting */ +Time_t gNSSThreadTestTimeMax; /* Max seconds */ +Time_t gNSSThreadTestTimeMaxTime; /* When max occurred */ +FsmLite_s gNSSThreadTestWorkToDoFsm; + + +void WORK_ThreadTestWorkToDoRoutine( FsmLite_s *workToDoFsm ) + +{ + Time_t cTime; + Time_t diffTime; + char buffer[40]; + + + WORK_PROCESS_INIT(); + + ++gNSSThreadTestCount; + if ( gNSSThreadTestCount < 2 ) + { + DBG_DebugPrintf(CYAN,MSGNot("Thread Test is running ...\n")); + } + cTime = GetUTCTime(); + if ( cTime < gNSSThreadTestTimeLast ) + { /* NetWare sometimes sets the clock back a little */ + diffTime = 0; + } + else + { + diffTime = cTime - gNSSThreadTestTimeLast; + } + + if ( diffTime >= gNSSThreadTestTimeAssert ) + { + DBG_DebugPrintf( LRED, + MSGNot("NSS Thread Test schedule delayed for %lu seconds ending at %s.\n"), + (unsigned long)diffTime, UTCTime2Str(cTime,&buffer[0]) ); +// +// Because we can queue up a lot of pending work, this is not that unusual +// +// /* We print to screen so that testers can see delay time */ +// aprintf(LRED, + MSGNot("NSS Thread Test schedule delayed for %lu seconds ending at %s.\n"), +// (unsigned long)diffTime, UTCTime2Str(cTime,&buffer[0]) ); +// zASSERT( "It appears that scheduling is really slow" == NULL ); +// + } + /* Is this the maximum delay for a schedule? */ + if ( diffTime > gNSSThreadTestTimeMax ) + { /* Yes - update max time and when max time occurred. */ + gNSSThreadTestTimeMax = diffTime; + gNSSThreadTestTimeMaxTime = cTime; + /* Display in debugger buffer */ + DBG_DebugPrintf( LRED, + MSGNot("NSS Thread Test new MAX delay of %lu seconds ending at %s.\n"), + (unsigned long)gNSSThreadTestTimeMax, + UTCTime2Str(gNSSThreadTestTimeMaxTime,&buffer[0]) ); + } + LB_delay( 1 * 1000 ); /* 1 second delay - can not do a yield because + * GreenRiver was complaining that low priority + * threads were not being allowed to run. + */ + /* Should we continue running the test? */ + if ( gNSSThreadTestState == TT_RUNNING ) + { /* Yes - reset time and re-schedule */ + gNSSThreadTestTimeLast = GetUTCTime(); + WORK_Schedule( &gNSSThreadTestWorkToDoFsm, + WORK_ThreadTestWorkToDoRoutine, 0); + + + return; + } + /* Mark that we are not running the test anymore */ + DBG_DebugPrintf(CYAN,MSGNot("... Thread Test has stopped\n")); + gNSSThreadTestState = TT_NOT_RUNNING; + + return; + +} /* End of WORK_ThreadTestWorkToDoRoutine() */ +#endif + +/************************************************************************** + * + ***************************************************************************/ +STATUS WORK_Startup (void) +{ + STATUS status; + ASSERT_MPKNSS_LOCK(); + + CIR_INIT(QueuedThreads); + CIR_INIT(QueuedThreadsHigh); + CIR_INIT(WorkWaitingListHead); + CIR_INIT(WorkHighWaitingListHead); + CIR_INIT(WorkLowWaitingListHead); + + status = CONTROL_Startup( &WorkControl, Config.os.workLimit, + sizeof(Work_s), (voidfunc_t)WORK_Init); + status = CONTROL_Startup( &WorkControlHigh, WORK_COUNT_HIGH, + sizeof(Work_s), (voidfunc_t)WORK_InitHigh); + status = CONTROL_Startup( &WorkControlLow, WORK_COUNT_LOW, + sizeof(Work_s), (voidfunc_t)WORK_InitLow); + +#if NSS_DEBUG IS_ENABLED + FSMLITE_INIT( &gNSSThreadTestWorkToDoFsm /* Lite FSM */, + MSGNot("Thread test Work-To-Do"), 0 /* Instance */ ); +#if zLINUX + gNSSThreadTestState = TT_NOT_RUNNING; +#else + gNSSThreadTestState = TT_RUNNING; + gNSSThreadTestTimeLast = GetUTCTime(); + WORK_Schedule( &gNSSThreadTestWorkToDoFsm, + WORK_ThreadTestWorkToDoRoutine, 0); +#endif // zLINUX + +#endif // NSS_DEBUG IS_ENABLED + +#if MEM_KEEP_LIST IS_ENABLED + /* We must wait until at least the timer AND work code is inited because + * free will schedule a one shot (that will call work) (it is not doing immediate + * frees). + */ + MKL_AllocStart(); +#endif // MEM_KEEP_LIST IS_ENABLED + + return status; +} + +/************************************************************************** + * This will wait for pending work to drain. This should wait on the order + * of 10 seconds. + ***************************************************************************/ +void WORK_WaitForPending() +{ + NINT delayCount; + NINT okCount = 0; + + /* FixFixFix6 - zfsPool.c and beastStartup.c call this routine + * to let threads finish up when a 'volume' is being shutdown. + * This is a little bogus because this routine waits for all + * threads to complete, not just the threads working on the + * 'volume' being shutdown. I believe the rational is that + * on shutdown all volumes will be shutdown, although I am + * not sure this is true because I assume you can unload a single + * LSS that would only cause its volumes to shutdown. + */ +#if NSS_DEBUG IS_ENABLED + /* In debug mode we have one thread that runs all the time. + * Do special code so we do not wait '10 seconds' on + * each volumes shutdown. + */ + /* Is special thread still running? */ + if ( gNSSThreadTestState == TT_RUNNING ) + { /* YES - then delay until only one thread running */ + okCount = 1; + } +#endif + for (delayCount = Config.os.workDelayCnt; + (PendingWork != okCount) && (delayCount > 0); + --delayCount) + { + LB_delay(Config.msec_s.workWait); + } +#ifdef __linux__ + if (PendingWork != okCount) + { + LB_aprintf(0, ".........PendingWork=%d okCount=%d, delayCount=%d\n", PendingWork, okCount, delayCount); + } +#else + zASSERT(PendingWork == okCount); +#endif +} + +/************************************************************************** + * This will wait up to 10 seconds for the work to finish processing + ***************************************************************************/ +void WORK_Shutdown (void) +{ + +#if MEM_KEEP_LIST IS_ENABLED + MKL_AllocStop(); +#endif +#if NSS_DEBUG IS_ENABLED + /* Wait for our thread test to stop - this works even if the test + * was not started. + */ + while ( gNSSThreadTestState != TT_NOT_RUNNING ) + { + gNSSThreadTestState = TT_ABORT_REQUEST; /* Request thread test to stop */ + Yield(); + } +#endif + WORK_WaitForPending(); + CONTROL_Shutdown( &WorkControl); + CONTROL_Shutdown( &WorkControlHigh); + CONTROL_Shutdown( &WorkControlLow); +} + + +/************************************************************************** + * This is the MAIN routine you should call when you want to create + * WorkToDo's. + ***************************************************************************/ +void WORK_Schedule (FsmLite_s *fsm, voidfunc_t action, ADDR parameter) +{ + FakeControl_s *fake; + Work_s *work; + + ENTER(TFSM, WORK_Schedule); + ASSERT_MPKNSS_LOCK(); + + INC_HISTOGRAM(WorkControl.histogram); + + fsm->action = (voidfunc_t)action; + fsm->param.user = parameter; + + STK_POP(WorkControl.head.freeList, fake, FakeControl_s, link); + + if (fake == NULL) + { + CIR_ENQ(WorkWaitingListHead, fsm, link); + WorkWaitingCount++; + /* This can not be here because this routine is called from + * a FastWorkToDo which can't yield + */ + //Yield(); // I hate this. Paul T. + } + else + { + PendingWork++; + work = (Work_s *)&fake->link; + work->fsm = fsm; + ScheduleWork(work); + } + RTN_VOID(); +} + + +/************************************************************************** + * This is the MAIN routine you should call when you want to create + * WorkToDo's, and scheule them with a higher priority if put on the + * waiting list. + ***************************************************************************/ +void WORK_Schedule_HIGH (FsmLite_s *fsm, voidfunc_t action, ADDR parameter) +{ + FakeControl_s *fake; + Work_s *work; + + ENTER(TFSM, WORK_Schedule); + INC_HISTOGRAM(WorkControlHigh.histogram); + + fsm->action = (voidfunc_t)action; + fsm->param.user = parameter; + + STK_POP(WorkControlHigh.head.freeList, fake, FakeControl_s, link); + + if (fake == NULL) + { + CIR_ENQ(WorkHighWaitingListHead, fsm, link); + WorkHighWaitingCount++; + /* This can not be here because this routine is called from + * a FastWorkToDo which can't yield + */ + //Yield(); // I hate this. Paul T. + } + else + { + PendingWork++; + work = (Work_s *)&fake->link; + work->fsm = fsm; +#if zLINUX + MPKNSS_UNLOCK(); + kSchedulePrioWorkToDo((struct WorkToDoStructure *)work); + MPKNSS_LOCK(); +#else + ScheduleWork(work); +#endif + } + RTN_VOID(); +} + + +/************************************************************************** + * This routine queues up work for a WorkToDo but does not start it. The + * queued items will be handled by an already running WorkToDo. If no + * WorkToDo is started then it will start one. + ***************************************************************************/ +void WORK_Queue (FsmLite_s *fsm, voidfunc_t action, ADDR parameter) +{ + fsm->action = (voidfunc_t)action; + fsm->param.user = parameter; + + if (PendingWork > 0) + { + CIR_ENQ(WorkLowWaitingListHead, fsm, link); + WorkLowWaitingCount++; + } + else + { + WORK_Schedule(fsm, action, parameter); + } +} + + +/************************************************************************** + * This is the MAIN routine you should call when you want to create + * WorkToDo's, and scheule them with a lower priority if put on the + * waiting list. + ***************************************************************************/ +void WORK_Schedule_LOW (FsmLite_s *fsm, voidfunc_t action, ADDR parameter) +{ + FakeControl_s *fake; + Work_s *work; + + ENTER(TFSM, WORK_Schedule); + INC_HISTOGRAM(WorkControlLow.histogram); + + fsm->action = (voidfunc_t)action; + fsm->param.user = parameter; + + STK_POP(WorkControlLow.head.freeList, fake, FakeControl_s, link); + + if (fake == NULL) + { + CIR_ENQ(WorkLowWaitingListHead, fsm, link); + WorkLowWaitingCount++; + } + else + { + PendingWork++; + work = (Work_s *)&fake->link; + work->fsm = fsm; + ScheduleWork(work); + } + RTN_VOID(); +} +WorkInst_s WorkInst = { 0 }; + +/************************************************************************** + * This is the routine that is called to execute workToDo code. + ***************************************************************************/ +void WORK_Run (Work_s *work) +{ + FakeControl_s *fake; + FsmLite_s *fsm = work->fsm; +#if NSS_DEBUG IS_ENABLED + NINT displayedDebug = FALSE; +#endif + + Enable(); + MPKNSS_LOCK(); + + //ENTER(TFSM, WORK_Run); +++WorkInst.enter; + for (;;) + { +++WorkInst.loop; +#if NSS_DEBUG IS_ENABLED + if (!displayedDebug) + { + displayedDebug = TRUE; + DEBUG_PRINTF(TYIELDS,DBG_BOTH_NOINDENT,(LRED, + MSGNot("Scheduling WORK (Thread=%08x)\n"),ThreadId())); + } +#endif + ((void (*)(FsmLite_s *, ADDR))fsm->action)(fsm, fsm->param.user); + CHECK_INTERRUPTS(); + + DEC_HISTOGRAM(WorkControl.histogram); + + if (ActiveWorkWaitingList == ACTIVE_WORK_WAIT_LIST) + { + if (CIR_NOT_EMPTY(WorkWaitingListHead)) + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkWaitingListHead, fsm, FsmLite_s, link); + ActiveWorkWaitingList = ACTIVE_WORK_HIGH_WAIT_LIST; + WorkWaitingCount--; +// SIGNAL_NEXT_THREAD(); + PERIODIC_YIELD(); +++WorkInst.yield; + } + else if (CIR_NOT_EMPTY(WorkHighWaitingListHead)) + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkHighWaitingListHead, fsm, FsmLite_s, link); + ActiveWorkWaitingList = ACTIVE_WORK_WAIT_LIST; + WorkHighWaitingCount--; +// SIGNAL_NEXT_THREAD_HIGH(); + PERIODIC_YIELD(); +++WorkInst.yield; + } + else if (CIR_NOT_EMPTY(WorkLowWaitingListHead)) + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkLowWaitingListHead, fsm, FsmLite_s, link); + WorkLowWaitingCount--; + PERIODIC_YIELD(); +++WorkInst.yield; + } + else + { + fake = STRUCT(work, FakeControl_s, link); + NULLIFY( &fake->link); + STK_PUSH(WorkControl.head.freeList, fake, link); + zASSERT(PendingWork > 0); + PendingWork--; +++WorkInst.exit; + +#if NSS_DEBUG IS_ENABLED + if (displayedDebug) + { + DEBUG_PRINTF(TYIELDS,DBG_BOTH_NOINDENT,(LRED, + MSGNot("Stopped Scheduling WORK (Thread=%08x)\n"),ThreadId())); + } +#endif + //RTN_VOID(); + MPKNSS_UNLOCK(); + return; + } + } + else + { + if (CIR_NOT_EMPTY(WorkHighWaitingListHead)) + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkHighWaitingListHead, fsm, FsmLite_s, link); + ActiveWorkWaitingList = ACTIVE_WORK_WAIT_LIST; + WorkHighWaitingCount--; +// SIGNAL_NEXT_THREAD_HIGH(); + PERIODIC_YIELD(); +++WorkInst.yield; + } + else if (CIR_NOT_EMPTY(WorkWaitingListHead)) + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkWaitingListHead, fsm, FsmLite_s, link); + ActiveWorkWaitingList = ACTIVE_WORK_HIGH_WAIT_LIST; + WorkWaitingCount--; +// SIGNAL_NEXT_THREAD(); + PERIODIC_YIELD(); +++WorkInst.yield; + } + else if (CIR_NOT_EMPTY(WorkLowWaitingListHead)) + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkLowWaitingListHead, fsm, FsmLite_s, link); + WorkLowWaitingCount--; + PERIODIC_YIELD(); +++WorkInst.yield; + } + else + { + fake = STRUCT(work, FakeControl_s, link); + NULLIFY( &fake->link); + STK_PUSH(WorkControl.head.freeList, fake, link); + zASSERT(PendingWork > 0); + PendingWork--; +++WorkInst.exit; + +#if NSS_DEBUG IS_ENABLED + if (displayedDebug) + { + DEBUG_PRINTF(TYIELDS,DBG_BOTH_NOINDENT,(LRED, + MSGNot("Stopped Scheduling WORK (Thread=%08x)\n"),ThreadId())); + } +#endif + //RTN_VOID(); + MPKNSS_UNLOCK(); + return; + } + } + } +} + + +void WORK_RunHigh (Work_s *work) +{ + FakeControl_s *fake; + FsmLite_s *fsm = work->fsm; +#if NSS_DEBUG IS_ENABLED + NINT displayedDebug = FALSE; +#endif + + Enable(); + MPKNSS_LOCK(); + + //ENTER(TFSM, WORK_Run); +++WorkInst.enter; + for (;;) + { +++WorkInst.loop; +#if NSS_DEBUG IS_ENABLED + if (!displayedDebug) + { + displayedDebug = TRUE; + DEBUG_PRINTF(TYIELDS,DBG_BOTH_NOINDENT,(LRED, + MSGNot("Scheduling WORK (Thread=%08x)\n"),ThreadId())); + } +#endif + ((void (*)(FsmLite_s *, ADDR))fsm->action)(fsm, fsm->param.user); + CHECK_INTERRUPTS(); + + DEC_HISTOGRAM(WorkControlHigh.histogram); + + if (CIR_NOT_EMPTY(WorkHighWaitingListHead)) + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkHighWaitingListHead, fsm, FsmLite_s, link); + WorkHighWaitingCount--; +// SIGNAL_NEXT_THREAD_HIGH(); + PERIODIC_YIELD(); +++WorkInst.yield; + } + else + { + fake = STRUCT(work, FakeControl_s, link); + NULLIFY( &fake->link); + STK_PUSH(WorkControlHigh.head.freeList, fake, link); + zASSERT(PendingWork > 0); + PendingWork--; +++WorkInst.exit; + +#if NSS_DEBUG IS_ENABLED + if (displayedDebug) + { + DEBUG_PRINTF(TYIELDS,DBG_BOTH_NOINDENT,(LRED, + MSGNot("Stopped Scheduling WORK (Thread=%08x)\n"),ThreadId())); + } +#endif + //RTN_VOID(); + MPKNSS_UNLOCK(); + return; + } + } +} + +void WORK_RunLow (Work_s *work) +{ + FakeControl_s *fake; + FsmLite_s *fsm = work->fsm; +#if NSS_DEBUG IS_ENABLED + NINT displayedDebug = FALSE; +#endif + + Enable(); + MPKNSS_LOCK(); + + //ENTER(TFSM, WORK_Run); +++WorkInst.enter; + for (;;) + { +++WorkInst.loop; +#if NSS_DEBUG IS_ENABLED + if (!displayedDebug) + { + displayedDebug = TRUE; + DEBUG_PRINTF(TYIELDS,DBG_BOTH_NOINDENT,(LRED, + MSGNot("Scheduling WORK (Thread=%08x)\n"),ThreadId())); + } +#endif + ((void (*)(FsmLite_s *, ADDR))fsm->action)(fsm, fsm->param.user); + CHECK_INTERRUPTS(); + + DEC_HISTOGRAM(WorkControlLow.histogram); + + if (CIR_NOT_EMPTY(WorkLowWaitingListHead)) + + { +++WorkInst.reuse; + CIR_DEQ_NO_CHECK(WorkLowWaitingListHead, fsm, FsmLite_s, link); + WorkLowWaitingCount--; +// SIGNAL_NEXT_THREAD_HIGH(); + PERIODIC_YIELD(); +++WorkInst.yield; + } + else + { + fake = STRUCT(work, FakeControl_s, link); + NULLIFY( &fake->link); + STK_PUSH(WorkControlLow.head.freeList, fake, link); + zASSERT(PendingWork > 0); + PendingWork--; +++WorkInst.exit; + +#if NSS_DEBUG IS_ENABLED + if (displayedDebug) + { + DEBUG_PRINTF(TYIELDS,DBG_BOTH_NOINDENT,(LRED, + MSGNot("Stopped Scheduling WORK (Thread=%08x)\n"),ThreadId())); + } +#endif + //RTN_VOID(); + MPKNSS_UNLOCK(); + return; + } + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index be142e7..19b040f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,6 +25,7 @@ if(MARS_NWE_BUILD_NWFS_TESTS) add_subdirectory(core/register) add_subdirectory(core/schedule) add_subdirectory(core/worktodo) + add_subdirectory(core/work) add_subdirectory(core/alarm) add_subdirectory(core/control) add_subdirectory(core/asyncio) diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt index 202b936..ffc4aa4 100644 --- a/tests/core/CMakeLists.txt +++ b/tests/core/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(pssdebug) add_subdirectory(register) add_subdirectory(schedule) add_subdirectory(worktodo) +add_subdirectory(work) add_subdirectory(alarm) add_subdirectory(control) add_subdirectory(asyncio) diff --git a/tests/core/work/CMakeLists.txt b/tests/core/work/CMakeLists.txt new file mode 100644 index 0000000..8c25849 --- /dev/null +++ b/tests/core/work/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(test_nwcore_work test_nwcore_work.c) +target_link_libraries(test_nwcore_work PRIVATE mars_nwe::core) +add_test(NAME nwcore.work COMMAND test_nwcore_work) diff --git a/tests/core/work/test_nwcore_work.c b/tests/core/work/test_nwcore_work.c new file mode 100644 index 0000000..8ea1772 --- /dev/null +++ b/tests/core/work/test_nwcore_work.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#define CHECK(expr) do { \ + if (!(expr)) { \ + fprintf(stderr, "CHECK failed: %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + return EXIT_FAILURE; \ + } \ +} while (0) + +extern STATUS WORK_Startup(void); +extern void WORK_Shutdown(void); +extern void WORK_Schedule(FsmLite_s *fsm, voidfunc_t action, ADDR parameter); +extern void WORK_Schedule_HIGH(FsmLite_s *fsm, voidfunc_t action, ADDR parameter); +extern void WORK_Schedule_LOW(FsmLite_s *fsm, voidfunc_t action, ADDR parameter); +extern NINT PendingWork; + +typedef struct WorkTest_s { + int value; +} WorkTest_s; + +static void work_action(FsmLite_s *fsm, ADDR parameter) +{ + WorkTest_s *state = (WorkTest_s *)parameter; + if (fsm != NULL) + { + state->value++; + } +} + +int main(void) +{ + WorkTest_s state = {0}; + FsmLite_s fsm; + FsmLite_s high; + FsmLite_s low; + + configStartup(); + nssInitializeSpinLockCode(); + CHECK(COMN_RegisterLibraryResourceTags("nwcore.work") == zOK); + + MPKNSS_LOCK(); + CHECK(WORK_Startup() == zOK); + FSMLITE_INIT(&fsm, "work", 0); + FSMLITE_INIT(&high, "work-high", 1); + FSMLITE_INIT(&low, "work-low", 2); + + WORK_Schedule(&fsm, (voidfunc_t)work_action, (ADDR)&state); + WORK_Schedule_HIGH(&high, (voidfunc_t)work_action, (ADDR)&state); + WORK_Schedule_LOW(&low, (voidfunc_t)work_action, (ADDR)&state); + + CHECK(state.value == 3); + CHECK(PendingWork == 0); + WORK_Shutdown(); + MPKNSS_UNLOCK(); + return EXIT_SUCCESS; +}