Happy New Year Rob and everyone else,

On 09/01/2025 22:02, Rob Arthan wrote:
Happy New Year to the Poly/ML User Community!

I am trying to find a fix to a problem where we want to apply a function
that sometimes takes far too long to execute. So I want to abort execution of
the function after a specified time limit. I have a solution that seems to work
by forking a worker thread to execute the function and store the result in a 
ref while
the control thread goes into a loop that waits for 100ms and then either (1) 
returns the
result from the ref if the worker thread is no longer active, (2) repeats if 
the thread
is still active and we haven’t reaced the time limit or otherwise (3) kills the 
worker thread
and returns a dummy value.

I am implementing this using Thread.ConditionVar.waitUntil and have two 
questions
about that:

1) What does the bool returned by Thread.ConditionVar.waitUntil mean? That 
doesn’t seem
to be documented.

This indicates whether the wait has returned because the condition variable was signalled (true) or because it timed out (false).

2) In addition to the timeout, Thread.ConditionVar.waitUntil has a condition 
variable and a mutex
as arguments. I am just creating these using Thread.ConditionVar.conditionVar 
and Thread.Mutex.mutex
because I need some values to pass to Thread.ConditionVar.waitUntil. I am not 
doing any signalling
or locking or unlocking. Is that going to cause any problems? (The condition 
variable and the mutex
are local to the control thread function, so the worker thread function has no 
access to them.)

This is a standard paradigm but you do need to follow the rules if you want it to work properly.
The condition variable and mutex need to be shared between the two threads.
In the worker thread:
When you're ready to return a result:
Disable asynchronous interrupts if they're enabled,
Lock the mutex,
Store the result in the shared reference,
Signal the condition variable,
Unlock the mutex,
Re-enable the asynchronous interrupts if you're continuing to process
Or exit

In the control thread:
Disable asynchronous interrupts if necessary,
Lock the mutex,
Start of loop:
Read the shared reference. If the worker has stored the result, unlock the mutex, restore interrupts and exit the loop,
Wait on the condition variable and mutex,
Go to the start of the loop.

It's important that the worker thread locks the mutex for two reasons. It avoids any race conditions which could occur if the worker thread set the shared reference and signalled the condition variable in the time between the control thread deciding that the result had not been stored yet and blocking on the condition variable. That's not too much of a problem if you're using waitUntil with a short interval. More importantly, locking and unlocking the mutex also puts in memory barriers which are essential on machines such as the ARM with relaxed memory ordering. Without some sort of interlock between the worker thread and the control thread there is no guarantee that the control thread will ever see that the shared reference has been updated.

It's better to use Thread.interrupt rather than Thread.kill since it allows the worker thread to handle the Interrupt exception and free up resources where necessary and also to mask the exception in critical sections. The worker thread needs to be created with InterruptState InterruptAsynch for this to work or to set that with Thread.setAttributes once it is running.

David

_______________________________________________
polyml mailing list
[email protected]
http://lists.inf.ed.ac.uk/mailman/listinfo/polyml

Reply via email to