Hey folks,
I've recently
adopted the PyMiniRacer project; a minimalistic Python/V8 bridge originally created by
Sqreen. I revamped a lot of things, including the threading model. I was curious if any experts out there want to take a look at what I've done (it's all on the main branch, with the C++ code
here.)
Some questions of varying levels of specificity:
Overall threading design:
I want to run the message loop indefinitely, processing potentially delayed background work, and receiving new work from multiple (Python) threads. It's particularly confusing how to reconcile the fact that I want an always-running message pump loop (to, e.g., run time-delayed async work like setTimeout callbacks) which AFAICT needs the lock, and run new arbitrary tasks coming in from other threads (from Python).
So the question is, uh, is that a reasonable model?
Thread safety of posting and terminating tasks:
The above brings up a question: is it actually thread-safe to call v8::Platform::GetForegroundTaskRunner(v8::Isolate *) and v8::TaskRunner::PostTask(std::unique_ptr<v8::Task>) without the isolate lock, as I'm currently assuming it is? It's not documented as safe! But it seems to be safe upon cursory inspection of the implementation?
Does v8::platform::PumpMessageLoop need, get, or release the Isolate lock?
Another question from the above: does v8::platform::PumpMessageLoop need the Isolate lock? Does it ever unlock the Isolate lock? I.e., if you grab the Isolate lock and then call it with MessageLoopBehavior::kWaitForWork, it seems to sit around with the lock forever.
I think the answers are: yes the message pump needs the lock, and no it never unlocks it... thus my tentative conclusion is that if you're running the message pump in an indefinite loop in blocking mode, you can't interact with your Isolate on another thread. (Thus the task-posting solution: if you want to interact with your Isolate which has its own infinite message loop, just post a task to the task runner and it will run on the message loop. Makes sense! But it's not written down anywhere AFAICT?)
Is it safe to delete a v8::Persistent without the Isolate lock?
Is it safe to decref a std::shared_ptr<v8::BackingStore> to 0, thus deleting the BackingStore, without the Isolate lock?
Same as above (but it's even weirder if dropping a std::shared_ptr reference might be thread-unsafe!).
(End of questions.)
Any insight on any of these things would be useful for me, and users of the PyMiniRacer project! It seems to hang together fine under tests, but thread safety is a curious art!
And, while we're here, any other commentary on
PyMiniRacer would be very welcome. :)
Thanks,
Ben Creech