Skip to content

Commit fa3d616

Browse files
added quickjsMultithreaded back (was quickjsTest before)
1 parent be698c0 commit fa3d616

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

quickjs-hs.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ library
4242
, text
4343
, time
4444
, transformers
45+
, unliftio-core
4546
, unordered-containers
4647
, vector
4748
default-language: Haskell2010

src/Quickjs.hs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This is a very basic wrapper for the [QuickJS](https://bellard.org/quickjs/) .
1212
The current functionality includes evaluating JS code, calling a JS function in the global scope
1313
and marshalling 'Value's to and from 'JSValue's.
1414
-}
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
1616

1717
import Foreign
1818
import Foreign.C (CString, CInt, CDouble, CSize)
@@ -24,6 +24,7 @@ import Control.Monad (when, forM_)
2424
import Control.Monad.Reader (MonadReader, runReaderT, ask)
2525
import Control.Monad.Trans.Reader (ReaderT)
2626
import Control.Monad.IO.Class (MonadIO, liftIO)
27+
import Control.Monad.IO.Unlift (MonadUnliftIO(..), UnliftIO(..), askUnliftIO)
2728
import Data.Aeson (Value(..), encode, toJSON)
2829
import qualified Data.Aeson as Aeson
2930
import Data.Scientific (fromFloatDigits, toRealFloat, toBoundedInteger, isInteger)
@@ -583,4 +584,35 @@ quickjs f = do
583584
where
584585
cleanup ctx rt = liftIO $ do
585586
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

Comments
 (0)