You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
runInBoundThread forks off another thread. However, suppose you're writing an application that manages a bunch of threads. Due to POSIX standards [1] one has to code the main function to catch all exceptions, track child threads, cancel the child threads when you get an exception (which may trigger cleanups in each thread) and then wait for these child threads to exit.
runInBoundThread interferes with this process, since it (a) forks off a worker-bound-thread that nobody else knows about, (b) is uninterruptible while this worker-bound-thread runs, and (c) and if it receives an asynchronous exception in the meantime, it will not rethrow this to the worker-bound-thread. (Arguably the combination of (b) and (c) are design bugs and I'll file a GHC bug shortly.)
In the case of simple-lmdb this is problematic because the worker-bound-thread is often trying to take a lock e.g. in mdb_txn_begin where it calls _lockEnv. If the database has already been closed, then the lock is already taken. Then, the thread will hang forever because it is running inside of runInBoundThread which is uninterruptible.
The simple fix is to use withAsyncBound and wait from Control.Concurrent.Async - define runInBoundThread' action = withAsyncBound action wait and use this instead of runInBoundThread everywhere.
[1] these standards dictate that when the main-thread exits, this also causes all child threads to exit immediately without giving the Haskell runtime a chance to run cleanups
The text was updated successfully, but these errors were encountered:
BTW, whether it's legitimate to close the database and then try to perform actions on it, is a separate discussion. My point is that, trying to perform subsequent actions should not result in an indefinite uninterruptible hang, as is the case with the current code. Generally close functions are idempotent or at the very least throw an immediate exception.
runInBoundThread
forks off another thread. However, suppose you're writing an application that manages a bunch of threads. Due to POSIX standards [1] one has to code the main function to catch all exceptions, track child threads, cancel the child threads when you get an exception (which may trigger cleanups in each thread) and then wait for these child threads to exit.runInBoundThread
interferes with this process, since it (a) forks off a worker-bound-thread that nobody else knows about, (b) is uninterruptible while this worker-bound-thread runs, and (c) and if it receives an asynchronous exception in the meantime, it will not rethrow this to the worker-bound-thread. (Arguably the combination of (b) and (c) are design bugs and I'll file a GHC bug shortly.)In the case of simple-lmdb this is problematic because the worker-bound-thread is often trying to take a lock e.g. in mdb_txn_begin where it calls
_lockEnv
. If the database has already been closed, then the lock is already taken. Then, the thread will hang forever because it is running inside ofrunInBoundThread
which is uninterruptible.The simple fix is to use
withAsyncBound
andwait
fromControl.Concurrent.Async
- definerunInBoundThread' action = withAsyncBound action wait
and use this instead ofrunInBoundThread
everywhere.[1] these standards dictate that when the main-thread exits, this also causes all child threads to exit immediately without giving the Haskell runtime a chance to run cleanups
The text was updated successfully, but these errors were encountered: