summaryrefslogtreecommitdiff
path: root/rts/RtsAPI.c
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2016-08-30 20:55:10 +0100
committerSimon Marlow <marlowsd@gmail.com>2016-09-12 08:33:24 +0100
commit454033b54e2f7eef2354cc9d7ae7e7cba4dff09a (patch)
tree3577ed7b0b42e2acff1502673e1ee474fba31319 /rts/RtsAPI.c
parent0e7ccf6d233c66b23a60de4e35e039f78ea3e162 (diff)
downloadhaskell-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.c75
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
+}