Implement System.timeoutWithUnmask as a way to document the deficiency of ordinary System.timeout
The goal is to document the unobvious problem that expressions like timeout 1000 takeMVar
, when executed in a loop, lead to data loss. Namely, the IO action (consisting of takeMVar
) may be interrupted just after takeMVar
finishes and so consumes the value put into the MVar
, giving Nothing
as the result of the whole timeout expression. That's despite takeMVar
being atomic and timeout
being subtle and expensive. In the attached program around 15% of the data is lost with timeout 1 and -threaded
for me.
This ticket is about implementing System.timeoutWithUnmask (sibling of forkIOwithUnmask), proposed by @duog, that prevents data loss in calls such as timeoutWithUnmask $ \_restore -> takeMVar mv
. The documentation would point out the difference and suggest to switch to STM for a more extensible solution using registerDelay
(see https://stackoverflow.com/questions/22171895/using-tchan-with-timeout/22182074). As suggested by @duog, the general usage pattern of timeoutWithUnmask
would be timeoutWithUnmask $ \restore -> do { r <- restore $ expensive; cheap r; }
.
Another angle is to implement tryTakeMVarWithTimeout
and cross-reference explaining why flip timeout takeMVar
is not equivalent by far.
Here's the file showing the data loss (and an occassional lock-up for an analogous reason): timeoutTest.hs