setup-stack hasn't had a commit in over a year and needs an env var
setting to work around some default github-actions security, so it
seems to be abandonware.
Putting the tasks in separate files would be nice, but that doesn't
play so nicely with the path-restricted git resources. Such a
resource pulls the git version *which changed the path*, not the
latest, so I'd need to pull in a second, unrestricted, input to get
the task files from.
That makes the pipeline look kind of awkward in the UI, so I decided
to just inline the definitions here.
With the ParMonad / "testing exposes a deadlock" / randomly test,
simplification threw an error:
Exception: (dejafu) Got a different result after simplifying:
'True' /= 'Abort'
This is failing even with previously supported versions of GHC, so I
think it's a new library version (probably random, giving an original
trace which exposes the bug).
Disabling "safe IO" for this test fixes it, which means that either
the IO is not safe after all, or that there's another bug elsewhere
which is being hidden now that simplification is more
restricted... however, none of the other tests are failing, so I hope
it's just the IO.
Type inference seems to fall down with the rank-n type of
`forkWithUnmask` + `const`:
Control/Monad/Conc/Class.hs:525:27: error:
• Couldn't match type ‘b5’ with ‘forall a. m a -> m a’
Expected: (forall a. m a -> m a) -> m ()
Actual: b5 -> m ()
Cannot instantiate unification variable ‘b5’
with a type involving polytypes: forall a. m a -> m a
• In the first argument of ‘forkWithUnmask’, namely ‘(const ma)’
In the expression: forkWithUnmask (const ma)
In an equation for ‘fork’: fork ma = forkWithUnmask (const ma)
• Relevant bindings include
ma :: m () (bound at Control/Monad/Conc/Class.hs:525:6)
fork :: m () -> m (ThreadId m)
(bound at Control/Monad/Conc/Class.hs:525:1)
|
525 | fork ma = forkWithUnmask (const ma)
This adds the overhead of constructing the effect to every STM
transaction, which I worried would be a large cost; but it turns out
that the overhead is negligible.
This is for transactions which throw an exception, rather than
stuffing that information into the `STM` action. This makes the
traces a bit clearer (eg, you can now tell without needing to inspect
the STM trace if an exception was thrown by a transaction).
I've called this "ThrownSTM" rather than "ThrowSTM" because it's like
"BlockedSTM" (just another failure case), and that's also past tense.
In this excerpt:
uninterruptibleMask $ \restore -> fork $ do
result <- try (restore (throw ThreadKilled))
...
The `throw` jumps to an exception handler registered outside the
`restore`, which means there is a masking state change. Previously,
dejafu handled this by inserting an `AResetMask` action as the first
action of the handler; but this is incorrect, as it opens a potential
race condition with another thread calling `throwTo`. As `throw` (and
`throwTo`, and an uncaught `throwSTM`) "use up" the exception handler,
this is not a benign race: the thread will be killed!
The solution is to atomically restore the masking state.
This commit implements that, and changes `Throw`, `ThrowTo`, and `STM`
to include the new masking state (if it changed). I think this is a
bit confusing, so I'll make a follow-up commit to split out a new
`ThrownSTM` action.