forked from Distributive-Network/PythonMonkey
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJobQueue.cc
More file actions
103 lines (80 loc) · 3.59 KB
/
JobQueue.cc
File metadata and controls
103 lines (80 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include "include/JobQueue.hh"
#include "include/PyEventLoop.hh"
#include "include/pyTypeFactory.hh"
#include <Python.h>
#include <jsfriendapi.h>
#include <stdexcept>
JSObject *JobQueue::getIncumbentGlobal(JSContext *cx) {
return JS::CurrentGlobalOrNull(cx);
}
bool JobQueue::enqueuePromiseJob(JSContext *cx,
[[maybe_unused]] JS::HandleObject promise,
JS::HandleObject job,
[[maybe_unused]] JS::HandleObject allocationSite,
JS::HandleObject incumbentGlobal) {
// Convert the `job` JS function to a Python function for event-loop callback
MOZ_RELEASE_ASSERT(js::IsFunctionObject(job));
// FIXME (Tom Tang): memory leak, objects not free-ed
// FIXME (Tom Tang): `job` function is going to be GC-ed ???
auto global = new JS::RootedObject(cx, incumbentGlobal);
auto jobv = new JS::RootedValue(cx, JS::ObjectValue(*job));
auto callback = pyTypeFactory(cx, global, jobv)->getPyObject();
// Inform the JS runtime that the job queue is no longer empty
JS::JobQueueMayNotBeEmpty(cx);
// Send job to the running Python event-loop
PyEventLoop loop = PyEventLoop::getRunningLoop();
if (!loop.initialized()) return false;
loop.enqueue(callback);
return true;
}
void JobQueue::runJobs(JSContext *cx) {
// TODO (Tom Tang):
throw std::logic_error("JobQueue::runJobs is not implemented.");
}
// is empty
bool JobQueue::empty() const {
// TODO (Tom Tang): implement using `get_running_loop` and getting job count on loop???
throw std::logic_error("JobQueue::empty is not implemented\n");
}
js::UniquePtr<JS::JobQueue::SavedJobQueue> JobQueue::saveJobQueue(JSContext *cx) {
// TODO (Tom Tang): implement this method way later
throw std::logic_error("JobQueue::saveJobQueue is not implemented\n");
}
bool JobQueue::init(JSContext *cx) {
JS::SetJobQueue(cx, this);
JS::InitDispatchToEventLoop(cx, /* callback */ dispatchToEventLoop, /* closure */ cx);
return true;
}
static PyObject *callDispatchFunc(PyObject *dispatchFuncTuple, PyObject *Py_UNUSED(unused)) {
JSContext *cx = (JSContext *)PyLong_AsVoidPtr(PyTuple_GetItem(dispatchFuncTuple, 0));
JS::Dispatchable *dispatchable = (JS::Dispatchable *)PyLong_AsVoidPtr(PyTuple_GetItem(dispatchFuncTuple, 1));
dispatchable->run(cx, JS::Dispatchable::NotShuttingDown);
Py_RETURN_NONE;
}
static PyMethodDef callDispatchFuncDef = {"JsDispatchCallable", callDispatchFunc, METH_NOARGS, NULL};
bool sendJobToMainLoop(PyObject *pyFunc) {
PyGILState_STATE gstate = PyGILState_Ensure();
// Send job to the running Python event-loop on `cx`'s thread (the main thread)
PyEventLoop loop = PyEventLoop::getMainLoop();
if (!loop.initialized()) {
PyGILState_Release(gstate);
return false;
}
loop.enqueue(pyFunc);
PyGILState_Release(gstate);
return true;
}
/* static */
bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable) {
JSContext *cx = (JSContext *)closure; // `closure` is provided in `JS::InitDispatchToEventLoop` call
// The `dispatchToEventLoop` function is running in a helper thread, so
// we must acquire the Python GIL (global interpreter lock)
// see https://docs.python.org/3/c-api/init.html#non-python-created-threads
PyGILState_STATE gstate = PyGILState_Ensure();
PyObject *dispatchFuncTuple = PyTuple_Pack(2, PyLong_FromVoidPtr(cx), PyLong_FromVoidPtr(dispatchable));
PyObject *pyFunc = PyCFunction_New(&callDispatchFuncDef, dispatchFuncTuple);
// Avoid using the JS helper thread to send jobs to event-loop as it may cause deadlock
PyThread_start_new_thread((void (*)(void *)) &sendJobToMainLoop, pyFunc);
PyGILState_Release(gstate);
return true; // dispatchable must eventually run
}