From c1303c639683aa4fce8d423c0d25a0bdb353cca7 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Fri, 13 Feb 2026 14:05:02 -0600 Subject: [PATCH] Fix for infinite loop in motor_task preventing graceful shutdown of the IOC: * Add a global shutdown flag + epicsAtExit() handler * Keep a separate ELLLIST of wakeup events * Exit the infinite loop in motor_task if shutdown flag is set Note: Argo (AI) suggested a broken version of this fix that needed to be manually corrected. --- motorApp/MotorSrc/motordrvCom.cc | 70 +++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/motorApp/MotorSrc/motordrvCom.cc b/motorApp/MotorSrc/motordrvCom.cc index a5c3eb85..f37ee500 100644 --- a/motorApp/MotorSrc/motordrvCom.cc +++ b/motorApp/MotorSrc/motordrvCom.cc @@ -56,6 +56,10 @@ USAGE... This file contains driver functions that are common #include #include #include +#include +#include +#include +#include #include #include "motor.h" @@ -85,6 +89,48 @@ static void process_messages(struct driver_table *, epicsTime, double); static struct mess_node *get_head_node(struct driver_table *); static struct mess_node *motor_malloc(struct circ_queue *, epicsEvent *); +static epicsInt32 motorShutdown = 0; +static epicsThreadOnceId motorShutdownOnce = EPICS_THREAD_ONCE_INIT; +static epicsMutexId motorShutdownLock = 0; +static ELLLIST motorShutdownWakeList; + +typedef struct motorWakeNode { + ELLNODE node; + epicsEvent *ev; +} motorWakeNode; + +static void motorAtExit(void *arg) +{ + epicsAtomicSetIntT(&motorShutdown, 1u); + + /* Wake all motor_task() instances so they can see motorShutdown and exit. */ + if (!motorShutdownLock) + { + return; + } + epicsMutexLock(motorShutdownLock); + for (ELLNODE *n = ellFirst(&motorShutdownWakeList); n; n = ellNext(n)) + { + motorWakeNode *wn = (motorWakeNode*)n; + if (wn->ev) + { + wn->ev->signal(); + } + } + epicsMutexUnlock(motorShutdownLock); +} + +static void motorShutdownInitOnce(void *arg) +{ + ellInit(&motorShutdownWakeList); + motorShutdownLock = epicsMutexCreate(); + epicsAtExit(motorAtExit, NULL); +} + +static void motorShutdownEnsureInit(void) +{ + epicsThreadOnce(&motorShutdownOnce, motorShutdownInitOnce, NULL); +} /* * FUNCION... motor_task() @@ -141,7 +187,23 @@ epicsShareFunc int motor_task(struct thread_args *args) tabptr = args->table; previous_time = epicsTime::getCurrent(); scan_sec = 1 / (double) args->motor_scan_rate; /* Convert HZ to seconds. */ - + + /* One-time registration of IOC shutdown hook + list init (reentrant-safe). */ + motorShutdownEnsureInit(); + + /* Register this task's wake event so IOC shutdown can wake the wait(). */ + if (motorShutdownLock && tabptr && tabptr->semptr) + { + motorWakeNode *wn = (motorWakeNode*)calloc(1, sizeof(*wn)); + if (wn) + { + wn->ev = tabptr->semptr; + epicsMutexLock(motorShutdownLock); + ellAdd(&motorShutdownWakeList, &wn->node); + epicsMutexUnlock(motorShutdownLock); + } + } + if (args->update_delay == 0.0) stale_data_max_delay = 0.0; else if (args->update_delay < quantum * 2.0) @@ -180,6 +242,12 @@ epicsShareFunc int motor_task(struct thread_args *args) sem_ret = tabptr->semptr->wait(wait_time); previous_time = epicsTime::getCurrent(); + /* IOC shutdown: motorAtExit() will signal semptr to wake us. */ + if (epicsAtomicGetIntT(&motorShutdown)) + { + break; + } + if (*tabptr->any_inmotion_ptr) { if (tabptr->strtstat != NULL)