@@ -12,7 +12,7 @@ This is a very basic wrapper for the [QuickJS](https://bellard.org/quickjs/) .
12
12
The current functionality includes evaluating JS code, calling a JS function in the global scope
13
13
and marshalling 'Value's to and from 'JSValue's.
14
14
-}
15
- module Quickjs (JSValue , JSContextPtr , quickjs , call , eval , eval_ , withJSValue , fromJSValue_ ) where
15
+ module Quickjs (JSValue , JSContextPtr , quickjs , quickjsMultithreaded , call , eval , eval_ , withJSValue , fromJSValue_ ) where
16
16
17
17
import Foreign
18
18
import Foreign.C (CString , CInt , CDouble , CSize )
@@ -24,6 +24,7 @@ import Control.Monad (when, forM_)
24
24
import Control.Monad.Reader (MonadReader , runReaderT , ask )
25
25
import Control.Monad.Trans.Reader (ReaderT )
26
26
import Control.Monad.IO.Class (MonadIO , liftIO )
27
+ import Control.Monad.IO.Unlift (MonadUnliftIO (.. ), UnliftIO (.. ), askUnliftIO )
27
28
import Data.Aeson (Value (.. ), encode , toJSON )
28
29
import qualified Data.Aeson as Aeson
29
30
import Data.Scientific (fromFloatDigits , toRealFloat , toBoundedInteger , isInteger )
@@ -583,4 +584,35 @@ quickjs f = do
583
584
where
584
585
cleanup ctx rt = liftIO $ do
585
586
jsFreeContext ctx
586
- jsFreeRuntime rt
587
+ jsFreeRuntime rt
588
+
589
+ {-|
590
+ This env differs from regular 'quickjs', in that it wraps the computation in the 'runInBoundThread' function.
591
+ This is needed when running the Haskell program mutithreaded (e.g. when using the testing framework Tasty),
592
+ since quickjs does not like being called from an OS thread other than the one it was started in.
593
+ Because Haskell uses lightweight threads, this might happen if threaded mode is enabled, as is the case in Tasty.
594
+ This problem does not occur when running via Main.hs, if compiled as single threaded...
595
+ For more info see the paper [Extending the Haskell Foreign Function Interface with Concurrency](https://simonmar.github.io/bib/papers/conc-ffi.pdf)
596
+ -}
597
+ quickjsMultithreaded :: MonadUnliftIO m => ReaderT (Ptr JSContext ) m b -> m b
598
+ quickjsMultithreaded f
599
+ | rtsSupportsBoundThreads = do
600
+ (u :: UnliftIO m ) <- askUnliftIO
601
+
602
+ liftIO $ runInBoundThread $ do
603
+ rt <- jsNewRuntime
604
+ ctx <- jsNewContext rt
605
+
606
+ [C. block | void {
607
+ js_std_add_helpers($(JSContext *ctx), -1, NULL);
608
+ } |]
609
+
610
+ res <- unliftIO u $ runReaderT f ctx
611
+ cleanup ctx rt
612
+ return res
613
+ | otherwise = quickjs f
614
+ where
615
+ cleanup ctx rt = do
616
+ jsFreeContext ctx
617
+ jsFreeRuntime rt
618
+
0 commit comments