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.
This commit is contained in:
Kevin Peterson
2026-03-24 14:19:24 +01:00
committed by mathis_s
parent 039b060714
commit c1303c6396
+69 -1
View File
@@ -56,6 +56,10 @@ USAGE... This file contains driver functions that are common
#include <callback.h>
#include <epicsThread.h>
#include <epicsExport.h>
#include <epicsAtomic.h>
#include <epicsExit.h>
#include <epicsMutex.h>
#include <ellLib.h>
#include <stdarg.h>
#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)