winio: xchg semantics that got merged not the same as winio expected
when !3293 (closed) was split off from the main winio
MR the semantics were slightly changed and I hadn't caught it at the time.
Essentially the semantics were changed to do what the xchg
operation semantically does but not what winio operationally needed,
that is xchg
swaps the values in both src
and dst
.
For winio
this is problematic as the instruction is used as
oldDataPtr <- exchangePtr ptr_lpol nullReq
where nullReq
is a CAF created by
-- | Terminator symbol for IOCP request
nullReq :: Ptr (Ptr a)
nullReq = castPtr $ unsafePerformIO $ new $ (nullPtr :: Ptr ())
the intention is to be able to test oldDataPtr
after the swap to see if it's nullReq
, if it is then something else already handled the request. This is why the original primop was a RMM
instead of RM
and forced a copy.
however now that nullReq
also changes this check will never be true. This shows itself as a race condition, if the request is completed synchronously
you never see it, if one of them is completed asynchronously
then you will deference an invalid pointer and segfault.
Now given that the semantics of interlockedExchangeAddr#
matches the expected semantics of the instruction now I think it should probably stay like that, but I am unsure how to fix this issue.
I don't know Cmm well enough, but can I enforce a copy of the value in
{-# INLINE exchangePtr #-}
exchangePtr :: Ptr (Ptr a) -> Ptr b -> IO (Ptr c)
exchangePtr (Ptr dst) (Ptr val) =
IO $ \s ->
case (interlockedExchangeAddr# dst val s) of
(# s2, old_val #) -> (# s2, Ptr old_val #)
? As in, is there something I can use that always generates a copy (with no additional overhead)?