Skip to content

Commit 95d6d83

Browse files
Improve synchronisation for parent-child messages
This new model will have the parent process wait until IocInit() has successfully completed before continuing with the test The child process had need to do a queue.put() followed immediately by a queue.get(). On most platforms the main process would interuupt by pulling from the queue, but this was not guaranteed. To solve this using queues would have required a second queue for signalling. We work around this by using Pipes instead. These provide the read-write semantics we want - the ability to wait for an explicit message from the parent process - without needing a side-channel for more signalling. Hopefully this will fix the MacOS issues where it seemed caget was returning values from a previous iteration of the tests, possibly because the test process was running much faster than the child process could keep up with.
1 parent 2e9f37c commit 95d6d83

File tree

1 file changed

+31
-15
lines changed

1 file changed

+31
-15
lines changed

tests/test_records.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ class GetValueEnum(Enum):
293293
CAGET = 2
294294

295295

296-
def run_ioc(creation_func, initial_value, queue, set_enum):
296+
def run_ioc(creation_func, initial_value, conn, set_enum, get_enum):
297297
"""Creates a record and starts the IOC. `initial_value` will be set on
298298
the record at different times based on the `set_enum` parameter."""
299299
kwarg = {}
@@ -316,15 +316,21 @@ def run_ioc(creation_func, initial_value, queue, set_enum):
316316
builder.LoadDatabase()
317317
softioc.iocInit(dispatcher)
318318

319+
conn.send("IOC started")
320+
319321
if set_enum == SetValueEnum.SET_AFTER_INIT:
320322
out_rec.set(initial_value)
321323

322-
if queue is not None:
323-
queue.put(out_rec.get())
324+
if get_enum == GetValueEnum.GET:
325+
conn.send(out_rec.get())
324326
# Some tests need to do a caput and then cause another .get() to happen
325-
val = queue.get(timeout=TIMEOUT)
326-
if val:
327-
queue.put(out_rec.get())
327+
if set_enum == SetValueEnum.CAPUT:
328+
if conn.poll(TIMEOUT):
329+
val = conn.recv()
330+
if val is not None:
331+
conn.send(out_rec.get())
332+
333+
conn.close()
328334

329335
# Keep process alive while main thread works.
330336
asyncio.run_coroutine_threadsafe(
@@ -343,17 +349,21 @@ def run_test_function(
343349

344350
creation_func, initial_value, expected_value, expected_type = record_values
345351

346-
queue = None
347-
if get_enum == GetValueEnum.GET:
348-
queue = multiprocessing.Queue()
352+
parent_conn, child_conn = multiprocessing.Pipe()
349353

350354
ioc_process = multiprocessing.Process(
351355
target=run_ioc,
352-
args=(creation_func, initial_value, queue, set_enum)
356+
args=(creation_func, initial_value, child_conn, set_enum, get_enum)
353357
)
354358

355359
ioc_process.start()
356360

361+
# Wait for message that IOC has started
362+
if parent_conn.poll(TIMEOUT):
363+
parent_conn.recv()
364+
else:
365+
pytest.fail("IOC process did not start before TIMEOUT expired")
366+
357367
# Infer some required keywords from parameters
358368
put_kwargs = {}
359369
get_kwargs = {}
@@ -375,24 +385,30 @@ def run_test_function(
375385
_channel_cache.purge()
376386

377387
if set_enum == SetValueEnum.CAPUT:
378-
if queue:
379-
queue.get(timeout=TIMEOUT)
388+
if get_enum == GetValueEnum.GET:
389+
if parent_conn.poll(TIMEOUT):
390+
parent_conn.recv()
391+
else:
392+
pytest.fail("IOC did not provide initial record value")
380393
caput(
381394
DEVICE_NAME + ":" + RECORD_NAME,
382395
initial_value,
383396
wait=True,
384397
**put_kwargs)
385398

386-
if queue:
387-
queue.put("Do another get!")
399+
if get_enum == GetValueEnum.GET:
400+
parent_conn.send("Do another get!")
388401
# Ensure IOC process has time to execute.
389402
# I saw failures on MacOS where it appeared the IOC had not
390403
# processed the put'ted value as the caget returned the same value
391404
# as was originally passed in.
392405
Yield(timeout=TIMEOUT)
393406

394407
if get_enum == GetValueEnum.GET:
395-
rec_val = queue.get(timeout=TIMEOUT)
408+
if parent_conn.poll(TIMEOUT):
409+
rec_val = parent_conn.recv()
410+
else:
411+
pytest.fail("IOC did not provide record value in queue")
396412
else:
397413
rec_val = caget(
398414
DEVICE_NAME + ":" + RECORD_NAME,

0 commit comments

Comments
 (0)