Skip to content

Commit 2715ecc

Browse files
authored
Merge pull request #240 from epics-modules/issue199
Fix for infinite loop in motor_task preventing graceful shutdown of the IOC Fixes #199
2 parents 7378022 + 4ebe748 commit 2715ecc

File tree

1 file changed

+69
-1
lines changed

1 file changed

+69
-1
lines changed

motorApp/MotorSrc/motordrvCom.cc

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ USAGE... This file contains driver functions that are common
5656
#include <callback.h>
5757
#include <epicsThread.h>
5858
#include <epicsExport.h>
59+
#include <epicsAtomic.h>
60+
#include <epicsExit.h>
61+
#include <epicsMutex.h>
62+
#include <ellLib.h>
5963
#include <stdarg.h>
6064

6165
#include "motor.h"
@@ -85,6 +89,48 @@ static void process_messages(struct driver_table *, epicsTime, double);
8589
static struct mess_node *get_head_node(struct driver_table *);
8690
static struct mess_node *motor_malloc(struct circ_queue *, epicsEvent *);
8791

92+
static epicsInt32 motorShutdown = 0;
93+
static epicsThreadOnceId motorShutdownOnce = EPICS_THREAD_ONCE_INIT;
94+
static epicsMutexId motorShutdownLock = 0;
95+
static ELLLIST motorShutdownWakeList;
96+
97+
typedef struct motorWakeNode {
98+
ELLNODE node;
99+
epicsEvent *ev;
100+
} motorWakeNode;
101+
102+
static void motorAtExit(void *arg)
103+
{
104+
epicsAtomicSetIntT(&motorShutdown, 1u);
105+
106+
/* Wake all motor_task() instances so they can see motorShutdown and exit. */
107+
if (!motorShutdownLock)
108+
{
109+
return;
110+
}
111+
epicsMutexLock(motorShutdownLock);
112+
for (ELLNODE *n = ellFirst(&motorShutdownWakeList); n; n = ellNext(n))
113+
{
114+
motorWakeNode *wn = (motorWakeNode*)n;
115+
if (wn->ev)
116+
{
117+
wn->ev->signal();
118+
}
119+
}
120+
epicsMutexUnlock(motorShutdownLock);
121+
}
122+
123+
static void motorShutdownInitOnce(void *arg)
124+
{
125+
ellInit(&motorShutdownWakeList);
126+
motorShutdownLock = epicsMutexCreate();
127+
epicsAtExit(motorAtExit, NULL);
128+
}
129+
130+
static void motorShutdownEnsureInit(void)
131+
{
132+
epicsThreadOnce(&motorShutdownOnce, motorShutdownInitOnce, NULL);
133+
}
88134

89135
/*
90136
* FUNCION... motor_task()
@@ -141,7 +187,23 @@ epicsShareFunc int motor_task(struct thread_args *args)
141187
tabptr = args->table;
142188
previous_time = epicsTime::getCurrent();
143189
scan_sec = 1 / (double) args->motor_scan_rate; /* Convert HZ to seconds. */
144-
190+
191+
/* One-time registration of IOC shutdown hook + list init (reentrant-safe). */
192+
motorShutdownEnsureInit();
193+
194+
/* Register this task's wake event so IOC shutdown can wake the wait(). */
195+
if (motorShutdownLock && tabptr && tabptr->semptr)
196+
{
197+
motorWakeNode *wn = (motorWakeNode*)calloc(1, sizeof(*wn));
198+
if (wn)
199+
{
200+
wn->ev = tabptr->semptr;
201+
epicsMutexLock(motorShutdownLock);
202+
ellAdd(&motorShutdownWakeList, &wn->node);
203+
epicsMutexUnlock(motorShutdownLock);
204+
}
205+
}
206+
145207
if (args->update_delay == 0.0)
146208
stale_data_max_delay = 0.0;
147209
else if (args->update_delay < quantum * 2.0)
@@ -180,6 +242,12 @@ epicsShareFunc int motor_task(struct thread_args *args)
180242
sem_ret = tabptr->semptr->wait(wait_time);
181243
previous_time = epicsTime::getCurrent();
182244

245+
/* IOC shutdown: motorAtExit() will signal semptr to wake us. */
246+
if (epicsAtomicGetIntT(&motorShutdown))
247+
{
248+
break;
249+
}
250+
183251
if (*tabptr->any_inmotion_ptr)
184252
{
185253
if (tabptr->strtstat != NULL)

0 commit comments

Comments
 (0)