-
Notifications
You must be signed in to change notification settings - Fork 9
Description
There is an infinite loop possible when using a record with always_update=True and blocking=True. Run the following IOC:
# Import the basic framework components.
from softioc import softioc, builder, asyncio_dispatcher
# Create an asyncio dispatcher, the event loop is now running
dispatcher = asyncio_dispatcher.AsyncioDispatcher()
# Set the record prefix
builder.SetDeviceName("MY-DEVICE-PREFIX")
# Create some records
async def wrapped_method(value):
print("In callback")
record.set(0, process=False)
record = builder.Action(
"TEST",
on_update=wrapped_method,
blocking=True,
initial_value=0,
ZNAM="Idle",
ONAM="Active",
)
# Boilerplate get the IOC started
builder.LoadDatabase()
softioc.iocInit(dispatcher)
# Finally leave the IOC running with an interactive shell.
softioc.interactive_ioc(globals())
and do caput MY-DEVICE-PREFIX:TEST 1. In the IOC output console you will see the print statement repeating forever, despite process=False.
The reason for this is that when blocking=True is specified, it changes how EPICS executes the db_put_field command present in the record.set method. It returns immediately, rather than running synchronously. This causes our logic that tracks the process=False request to break, and effectively put it back to the default value of process=True.
It is not immediately obvious what the fix is for this; we need to somehow associate the async callback with the initial synchronous record.set, but there is no way to pass context into the EPICS processing chain.
This issue originally found in DiamondLightSource/FastCS#291.