Skip to content

Infinite loop when setting value in record's on_update callback #201

@AlexanderWells-diamond

Description

@AlexanderWells-diamond

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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions