epics-base / p4p

Python bindings for the PVAccess network client and server.
BSD 3-Clause "New" or "Revised" License
27 stars 38 forks source link

Server.forever doesn't work with async variables #132

Open Synthetica9 opened 10 months ago

Synthetica9 commented 10 months ago

Description

Server.forever doesn't work with async SharedPVs, probably due to this sleep:

https://github.com/mdavidsaver/p4p/blob/f5c96bad937fff57f5243f72b7fdb89a408d0f4a/src/p4p/server/__init__.py#L175

To reproduce:

Run the following server:

import time
from p4p.nt import NTScalar
from p4p.server import Server
from p4p.server.asyncio import SharedPV

pv = SharedPV(nt=NTScalar('d'), initial=0.0)

@pv.put
async def handle(pv, op):
    pv.post(op.value())
    op.done()

Server.forever(providers=[{'foo': pv}])

Then run pvput foo 12

Observed behaviour

pvput times out:

$ pvput foo 12
Old : 2024-01-15 04:27:05.880  0 
Put timeout

Expected behaviour

pvput succeeds and updates the value

mdavidsaver commented 10 months ago

Indeed Server.forever() is not async, and is in p4p.server instead of p4p.server.asyncio.

In the short term you could instead do something like:

done = asyncio.Event()
# install signal handler to call done.set()
with Server(...):
    await done.wait()

In the longer term, I see the convenience of an async equivalent of Server.forever(). I am not immediately sure where to add it. async/await can't appear in src/p4p/server/__init__.py as long as py2.7 is supported. I am not sure it would be worthwhile to add p4p.server.asyncio.Server just to add one classmethod. Maybe a helper method p4p.server.asyncio.serve_forever()?