-
Notifications
You must be signed in to change notification settings - Fork 6
Conversation
dukconfig/build_dist.sh
Outdated
@@ -0,0 +1,9 @@ | |||
#!/bin/bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use #!/bin/sh
instead of GNU/Linux-specific paths like /bin/bash
. You're not even using any bash-isms in this tiny script :)
(but anyway, I suggested not using a shell script below, just running the commands from Setup.hs
)
dukconfig/build_dist.sh
Outdated
--output-directory dukconfig/dist \ | ||
--source-directory duktape/src-input \ | ||
--config-metadata duktape/config \ | ||
--option-file dukconfig/config.yaml \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing backslash
README.md
Outdated
@@ -64,7 +64,15 @@ The functions must be of type `IO ()`, `IO Value`, `Value -> IO Value`, `Value - | |||
|
|||
## Development | |||
|
|||
Use [stack] to build. | |||
Building from the repository requires initialization with the Duktape configuration script the first time (these files are included in the cabal distribution on Hackage). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it might be possible to use Setup.hs
with hooks to run the configure script?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that. It takes a non-trivial time to run the script so we don't want to do it as part of every build, and it isn't needed for installation from the sdist package. Maybe a preConf
hook that checks to see if the files are already there, and runs the script if not, and a postClean
that removes them if it can find configure.py
. You'd have to remember to clean after updating the Duktape version though.
Alternatively configure.py
also produces a json
metadata file; I could look for that file and compare its version to what I find in the duktape
repo, and always rebuild if they differ.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd check if the modification time of the duktape source files is after the modification time of the files produced by configure.
I just had another thought. It seems unlikely that someone is already using |
@myfreeweb I've made the described changes. A few things to note: stack doesn't invoke Cabal's clean hooks, so I've left that out. I don't think its really an issue. When building from source the first time there will always be cabal warnings that it cannot find required extra files. I had to include a dummy
|
Looks good, thanks! |
-- TimeoutCheck is wrapped to pass as void* udata in `createHeap` and will be provided (by duktape) | ||
-- back to `execTimeoutCheck` when the interpreter invokes that callback. | ||
wrapTimeoutCheckUData ∷ TimeoutCheck → IO (InternalUData, IO ()) | ||
wrapTimeoutCheckUData check = do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is not safe from async exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not trying to dismiss this as I'm sure masking is a good idea in general, but for motivation can you give an example of an async exception we could expect and recover from? Or would this be the user of the library throwing one at us while they are building a heap for some reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user of the lib uses System.Timeout.timeout
or Control.Concurrent.Async.race
, for example, the gap between any of your IO actions are potential exceptions.
A system under heavy load will timeout more easily. I've seen this with e.g. sandboxing tryhaskell.org.
In the race case, if you do e.g. race evalThis readFromCache
- i.e. return whichever completes first, that might throw in your initialisation process.
On a long running service this will at best slowly accumulate allocated bytes. At worst you're looking at increased fragmentation because these are pinned mallocs that can't be moved around by the GC.
I'm doing research for running PureScript's JS output for server-side validation/server-side rendering so I'm evaluating whether to use hs-duktape, write my own binding that will address all my concerns from the ground up, or call out to Nodejs in a pipe and avoid the concerns altogether (and the extra work). 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My two-cents new projects should consider using Fabrice Bellard's quickjs instead though I'm not aware of an existing Haskell binding. I doubt duk will ever have es-6 compatibility. I ended up using quickjs via a Rust binding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at quickjs, but I don't think it has (1) the maturity required, or (2) long-term viability until someone takes over maintenance. Bellard is great and all but he tends to make things and move onto something else, leaving someone else to maintain. Whereas duktape has a proper community, documentation, a list of projects using it, an official GitHub project with an issue tracker where I can report bugs, open PRs, etc. and the expectation that it'll be maintained in five years. Small JS implementations are already niche.
YMMV.
rE `shouldBe` Left "RangeError: execution timeout" | ||
|
||
|
||
allowQuarterSecond ∷ IO (IO Bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will add a pretty hard performance penalty over having a separate thread just write a bool and this callback just reading an IORef atomically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be a better example to provide to people. Duktape doesn't invoke this callback very frequently so actual cost is maybe not as bad as you're thinking; I did some benchmarking but don't have the data handy now unfortunately.
createGovernedHeap ∷ FunPtr DukAllocFunction → FunPtr DukReallocFunction → FunPtr DukFreeFunction → TimeoutCheck → FunPtr DukFatalFunction → IO (Maybe DuktapeCtx) | ||
createGovernedHeap allocf reallocf freef timeoutCheck fatalf = do | ||
(udata, release) ← wrapTimeoutCheckUData timeoutCheck | ||
mctx ← createHeap allocf reallocf freef udata fatalf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the thread is killed after line 193, then what you just allocated won't ever be released.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is an issue in createHeap
as well for the actual Duktape heap.
resolves #6
As described in #6, this provides the a basic mechanism to implement eval timeouts using Duktape's
DUK_USE_EXEC_TIMEOUT_CHECK
feature. That feature is very basic, we pass a macro with the name of an externed function that the Duktape runtime will call periodically.This is a static binding that must be in the
duk_config.h
file at the timeduktape.c
is built and this imposes an unfortunate (distribution) build requirement, which is to invoke Duktape's configure script (requires Python and PyYAML). Invokingdukconfig/build_dist.sh
will take it from there. The configuration script tells Duktape to invoke a function we extern.That function always gets called but just returns False (don't timeout) unless the heap was created with a udata parameter. If there is a udata, it expects that udata to contain a function pointer it can unwrap and evaluate and then respond accordingly.
createGovernedHeap
takes a anIO Bool
and properly wraps it (and sets up finalizers) so it can be conveyed as udata. There is a test that should make basic usage clear.