promise
promise copied to clipboard
Race condition when waiting on Promise resolved in other thread
I'm using with LibUsb to achieve asynchronous transfers. Without going into too much details I'm using two threads:
- Worker: creates promise (P) and starts async USB transfer with supplied callback (C) that resolves promise. After starting transfer, thread wait for promise result.
- LibUSB event loop: triggers event processing in LibUSB, which will call callback C which will resolve promise and resume Worker thread.
All calls to LibUSB are native calls (ctypes
).
Occasionally (one in hundreds) I'm getting AssertionFailed
exception on this line: https://github.com/syrusakbary/promise/blob/master/promise/promise.py#L414 with traceback:
Traceback (most recent call last):
File "C:\Python38\lib\threading.py", line 932, in _bootstrap_inner
self.run()
File "C:\Python38\lib\threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "<snip>", line 66, in _run
packet = self._segmentation.read_packet()
File "<snip>", line 60, in read_packet
payload = self._low.low_read(part_size)
File "<snip>.py", line 56, in low_read
return bytes(ret.get())
File "C:\Python38\lib\site-packages\promise\promise.py", line 511, in get
self._wait(timeout or DEFAULT_TIMEOUT)
File "C:\Python38\lib\site-packages\promise\promise.py", line 506, in _wait
self.wait(self, timeout)
File "C:\Python38\lib\site-packages\promise\promise.py", line 502, in wait
async_instance.wait(promise, timeout)
File "C:\Python38\lib\site-packages\promise\async_.py", line 117, in wait
target.scheduler.wait(target, timeout)
File "C:\Python38\lib\site-packages\promise\schedulers\immediate.py", line 24, in wait
promise._then(on_resolve_or_reject, on_resolve_or_reject)
File "C:\Python38\lib\site-packages\promise\promise.py", line 577, in _then
target._add_callbacks(did_fulfill, did_reject, promise)
File "C:\Python38\lib\site-packages\promise\promise.py", line 414, in _add_callbacks
assert not self._rejection_handler0"
AssertionError
Traceback indicates that assertion failed in context on Worker thread.
I was able to create (sort of...) reproduction of this issue: https://github.com/Novakov/promise/tree/race-condition Unfortunately it's not great. Running test https://github.com/Novakov/promise/blob/race-condition/tests/test_thread_safety.py#L122 shows the issue in most cases.
Investigation of Promise
state suggests that in line https://github.com/syrusakbary/promise/blob/master/promise/promise.py#L577 promise is still pending, than context switch happens, promise gets resolved in loop thread, context switch happends again and _add_callbacks
continues on now resolved promise.
Machine details OS: Windows 10 x64 CPU: 4 cores, 8 logical threads Python: 3.8.1 (tags/v3.8.1:1b293b6, Dec 18 2019, 23:11:46) [MSC v.1916 64 bit (AMD64)]
I was not able to reproduce the issue on Linux machine, however it maybe matter of probability
What version of Promise are you using? It seems what you commented should be solved in 2.3.0
I forked this repository and added test to it. Tag matching release 2.3.0 is there. Latest commit: https://github.com/Novakov/promise/commit/9ba6fbda4472d1448663e5937428596fd30ad22c (2020-01-08)
Piggybacking on this issue - I am also getting this error and made sure that I had Promise>=2.3.0 installed prior. Sadly, it is not easily reproducible. The general setting however is the following:
I am calling functions using a ThreadPoolExecutor, and each of them returns a Promise. Now, I use Promise.all().then(lambda res: pool.submit(somefunction, res)).
This means I am using Promise to handle my threadpool-based Task executing and scheduling.
Also getting these errors (14 times per 2.5 million requests)
File "/var/task/graphql/execution/executor.py", line 218, in complete_value_catching_error
completed = complete_value(exe_context, return_type, field_asts, info, result)
,
File "/var/task/graphql/execution/executor.py", line 262, in complete_value
lambda error: Promise.rejected(GraphQLLocatedError(field_asts, original_error=error))
,
File "/var/task/promise/promise.py", line 630, in then
return self._then(did_fulfill, did_reject)
,
File "/var/task/promise/promise.py", line 577, in _then
target._add_callbacks(did_fulfill, did_reject, promise)
,
File "/var/task/promise/promise.py", line 414, in _add_callbacks
assert not self._rejection_handler0
We're executing Thread
s with callbacks which resolve promise (similar to @Novakov case)
I think I fixed the problem. Review is welcome https://github.com/syrusakbary/promise/pull/89