diff options
author | Simon Marlow <marlowsd@gmail.com> | 2016-08-30 20:55:10 +0100 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2016-09-12 08:33:24 +0100 |
commit | 454033b54e2f7eef2354cc9d7ae7e7cba4dff09a (patch) | |
tree | 3577ed7b0b42e2acff1502673e1ee474fba31319 /rts/RtsAPI.c | |
parent | 0e7ccf6d233c66b23a60de4e35e039f78ea3e162 (diff) | |
download | haskell-454033b54e2f7eef2354cc9d7ae7e7cba4dff09a.tar.gz |
Add hs_try_putmvar()
Summary:
This is a fast, non-blocking, asynchronous, interface to tryPutMVar that
can be called from C/C++.
It's useful for callback-based C/C++ APIs: the idea is that the callback
invokes hs_try_putmvar(), and the Haskell code waits for the callback to
run by blocking in takeMVar.
The callback doesn't block - this is often a requirement of
callback-based APIs. The callback wakes up the Haskell thread with
minimal overhead and no unnecessary context-switches.
There are a couple of benchmarks in
testsuite/tests/concurrent/should_run. Some example results comparing
hs_try_putmvar() with using a standard foreign export:
./hs_try_putmvar003 1 64 16 100 +RTS -s -N4 0.49s
./hs_try_putmvar003 2 64 16 100 +RTS -s -N4 2.30s
hs_try_putmvar() is 4x faster for this workload (see the source for
hs_try_putmvar003.hs for details of the workload).
An alternative solution is to use the IO Manager for this. We've tried
it, but there are problems with that approach:
* Need to create a new file descriptor for each callback
* The IO Manger thread(s) become a bottleneck
* More potential for things to go wrong, e.g. throwing an exception in
an IO Manager callback kills the IO Manager thread.
Test Plan: validate; new unit tests
Reviewers: niteria, erikd, ezyang, bgamari, austin, hvr
Subscribers: thomie
Differential Revision: https://phabricator.haskell.org/D2501
Diffstat (limited to 'rts/RtsAPI.c')
-rw-r--r-- | rts/RtsAPI.c | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/rts/RtsAPI.c b/rts/RtsAPI.c index 34d68c7e3b..e72430743e 100644 --- a/rts/RtsAPI.c +++ b/rts/RtsAPI.c @@ -16,6 +16,7 @@ #include "Schedule.h" #include "Capability.h" #include "Stable.h" +#include "Threads.h" #include "Weak.h" /* ---------------------------------------------------------------------------- @@ -620,3 +621,77 @@ void rts_done (void) freeMyTask(); } +/* ----------------------------------------------------------------------------- + tryPutMVar from outside Haskell + + The C call + + hs_try_putmvar(cap, mvar) + + is equivalent to the Haskell call + + tryPutMVar mvar () + + but it is + + * non-blocking: takes a bounded, short, amount of time + * asynchronous: the actual putMVar may be performed after the + call returns. That's why hs_try_putmvar() doesn't return a + result to say whether the put succeeded. + + NOTE: this call transfers ownership of the StablePtr to the RTS, which will + free it after the tryPutMVar has taken place. The reason is that otherwise, + it would be very difficult for the caller to arrange to free the StablePtr + in all circumstances. + + For more details, see the section "Waking up Haskell threads from C" in the + User's Guide. + -------------------------------------------------------------------------- */ + +void hs_try_putmvar (/* in */ int capability, + /* in */ HsStablePtr mvar) +{ + Task *task = getTask(); + Capability *cap; + + if (capability < 0) { + capability = task->preferred_capability; + if (capability < 0) { + capability = 0; + } + } + cap = capabilities[capability % enabled_capabilities]; + +#if !defined(THREADED_RTS) + + performTryPutMVar(cap, (StgMVar*)deRefStablePtr(mvar), Unit_closure); + freeStablePtr(mvar); + +#else + + ACQUIRE_LOCK(&cap->lock); + // If the capability is free, we can perform the tryPutMVar immediately + if (cap->running_task == NULL) { + cap->running_task = task; + task->cap = cap; + RELEASE_LOCK(&cap->lock); + + performTryPutMVar(cap, (StgMVar*)deRefStablePtr(mvar), Unit_closure); + + freeStablePtr(mvar); + + // Wake up the capability, which will start running the thread that we + // just awoke (if there was one). + releaseCapability(cap); + } else { + PutMVar *p = stgMallocBytes(sizeof(PutMVar),"hs_try_putmvar"); + // We cannot deref the StablePtr if we don't have a capability, + // so we have to store it and deref it later. + p->mvar = mvar; + p->link = cap->putMVars; + cap->putMVars = p; + RELEASE_LOCK(&cap->lock); + } + +#endif +} |