Hi Phil,

Thanks for the reply, however that just gives me a forced deadlock removal as before.

new bound thread (1)
cap 0: schedule()
cap 0: running thread 1 (ThreadRunGHC)
cap 0: thread 1 stopped (blocked on an MVar)
        thread    1 @ 0000000003205388 is blocked on an MVar @ 00000000032040c8 (TSO_DIRTY)
deadlocked, forcing major GC...
all threads:
threads on capability 0:
other threads:
        thread    1 @ 0000000003205388 is blocked on an MVar @ 00000000032040c8 (TSO_DIRTY)
cap 0: starting GC

I don't believe any solution involving MVars will work for the non-threaded RTS. Though I'd love to be wrong here...

Regards,
Tamar

On Sun, Jan 6, 2019 at 9:38 PM Phil Ruffwind <rf@rufflewind.com> wrote:
What if you wrap the MVar in a foreign closure?

    import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
    import Control.Exception (bracket)
    import Foreign.Ptr (FunPtr, freeHaskellFunPtr)

    foreign import ccall "wrapper" wrapAwaken :: IO () -> IO (FunPtr (IO ()))

    main = do
      mvar <- newEmptyMVar
      bracket (wrapAwaken (putMVar mvar ())) freeHaskellFunPtr $ \ awaken -> do
        -- giveToExternalCode awaken
        takeMVar mvar

On Sun, Jan 6, 2019, at 10:37, Phyx wrote:
> Hi All,
>
> I'm looking for a way to block a task indefinitely until it is woken up by
> an external event in both the threaded and non-threaded RTS and returns a
> value that was stored/passed. MVar works great for the threaded RTS, but
> for the non-threaded there's a bunch of deadlock detection in the scheduler
> that would forcibly free the lock and resume the task with an opaque
> exception. This means that MVar and anything derived from it is not usable.
>
> STMs are more expensive but also have the same deadlock code. So again no
> go. The reason it looks like a deadlock to the RTS is that the "Wake-up"
> call in the non-threaded rts will come from C code running inside the RTS.
> The RTS essentially just sees all tasks blocked on it's main capability and
> (usually rightly so) assumes a deadlock occurred.
>
> You have other states like BlockedOnForeign etc but those are not usable as
> a primitive. Previous iterations of I/O managers have each invented
> primitives for this such as asyncRead#, but they are not general and can't
> be re-used, and requires a different solution for threaded and non-threaded.
>
> I have started making a new primitive IOPort for this, based on the MVar
> code, but this is not trivial... (currently I'm getting a segfault
> *somewhere* in the primitive's cmm code). The reason is that the semantics
> are decidedly different from what MVars guarantee. I should also mention
> that this is meant to be internal to base (i.e no exported).
>
> So before I continue down this path and some painful debugging..., does
> anyone know of a way to block a task, unblock it later and pass a value
> back? It does not need to support anything complicated such as multiple
> take/put requests etc.
>
> Cheers,
> Tamar
> _______________________________________________
> ghc-devs mailing list
> ghc-devs@haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs