We run the user supplied code through an instrumentation pass (using Google Closure Compiler) where we modify it at the AST level to call a resource check/interrupt check after every instruction. It also modifies any loop constructs. The compiler will save a sourcemap so you can pretend you’re not modifying user code.
We disable all extensions. We do some instrumentation on string ops as well. We use the mbean API to get the memory and CPU usage of the current thread. We have a monitor thread that checks that for all worker threads. I’ve probably forgotten a bunch of things but it was a huge pain. I’m going to tell our guys about your solution and see if that’s something we can look into.