Currently, each Provisioner uses very different synchronization approaches:
- TestingFarmProvisioner has
daemon=True threads that silently die in the background even after .stop()
- SharedVirtProvisioner has one non-daemon thread with proper
threading.Event waiting, but has very over-complicated creation/joining of the one thread (via a threading.Semaphore) depending on when it is / is not needed, to prevent (well, limit) race conditions with its use
- PodmanProvisioner just uses a
threading.Condition and creates containers inside .get_remote(), not in any background thread
- TempVirtProvisioner combines
threading.Event with threading.Condition and a collections.deque for the thread returns
It would be great to at least converge on some common fool-proof solution for all/most of these.
For example:
- Standard
daemon=False threads with threading.Event propagation to lower-level functions, to be done ie. in testingfarm/api.py
threading.Condition to be always used for synchronization (as a generic lock and also .wait_for()), to allow .stop() waking up a blocking .get_remote() across all implementations
- (also
.stop() on .get_remote() should always be an exception, not a random None from a blocking call)
- A simple FIFO
collections.deque for passing returns/exceptions from threads, in combination with the threading.Condition
Not all provisioners need to use all these, but it also shouldn't be some random threading.Semaphore coupled to the logic.
Currently, each Provisioner uses very different synchronization approaches:
daemon=Truethreads that silently die in the background even after.stop()threading.Eventwaiting, but has very over-complicated creation/joining of the one thread (via athreading.Semaphore) depending on when it is / is not needed, to prevent (well, limit) race conditions with its usethreading.Conditionand creates containers inside.get_remote(), not in any background threadthreading.Eventwiththreading.Conditionand acollections.dequefor the thread returnsIt would be great to at least converge on some common fool-proof solution for all/most of these.
For example:
daemon=Falsethreads withthreading.Eventpropagation to lower-level functions, to be done ie. intestingfarm/api.pythreading.Conditionto be always used for synchronization (as a generic lock and also.wait_for()), to allow.stop()waking up a blocking.get_remote()across all implementations.stop()on.get_remote()should always be an exception, not a randomNonefrom a blocking call)collections.dequefor passing returns/exceptions from threads, in combination with thethreading.ConditionNot all provisioners need to use all these, but it also shouldn't be some random
threading.Semaphorecoupled to the logic.