Revert D2926896 WebWorkers: Move web worker impl to JSCExecutor

Reviewed By: lexs

Differential Revision: D2982150

fb-gh-sync-id: c75d05988df50b9788608e7c1bf00c4952ccfce1
shipit-source-id: c75d05988df50b9788608e7c1bf00c4952ccfce1
This commit is contained in:
Andy Street
2016-02-26 05:56:36 -08:00
committed by Facebook Github Bot 0
parent 50141f9d3f
commit c32e5fd84f
9 changed files with 312 additions and 229 deletions
+36 -148
View File
@@ -3,8 +3,7 @@
#include "JSCExecutor.h"
#include <algorithm>
#include <condition_variable>
#include <mutex>
#include <atomic>
#include <sstream>
#include <string>
#include <glog/logging.h>
@@ -91,52 +90,6 @@ JSCExecutor::JSCExecutor(Bridge *bridge, const std::string& cacheDir, const foll
m_deviceCacheDir(cacheDir),
m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()),
m_jscConfig(jscConfig) {
initOnJSVMThread();
}
JSCExecutor::JSCExecutor(
Bridge *bridge,
int workerId,
JSCExecutor *owner,
const std::string& script,
const std::unordered_map<std::string, std::string>& globalObjAsJSON,
const folly::dynamic& jscConfig) :
m_bridge(bridge),
m_workerId(workerId),
m_owner(owner),
m_deviceCacheDir(owner->m_deviceCacheDir),
m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()),
m_jscConfig(jscConfig) {
// We post initOnJSVMThread here so that the owner doesn't have to wait for
// initialization on its own thread
m_messageQueueThread->runOnQueue([this, script, globalObjAsJSON] () {
initOnJSVMThread();
installGlobalFunction(m_context, "postMessage", nativePostMessage);
for (auto& it : globalObjAsJSON) {
setGlobalVariable(it.first, it.second);
}
// TODO(9604438): Protect against script does not exist
std::string scriptSrc = WebWorkerUtil::loadScriptFromAssets(script);
// TODO(9994180): Throw on error
loadApplicationScript(scriptSrc, script);
});
}
JSCExecutor::~JSCExecutor() {
*m_isDestroyed = true;
if (m_messageQueueThread->isOnThread()) {
terminateOnJSVMThread();
} else {
m_messageQueueThread->runOnQueueSync([this] () {
terminateOnJSVMThread();
});
}
}
void JSCExecutor::initOnJSVMThread() {
m_context = JSGlobalContextCreateInGroup(nullptr, nullptr);
s_globalContextRefToJSCExecutor[m_context] = this;
installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate);
@@ -165,20 +118,18 @@ void JSCExecutor::initOnJSVMThread() {
#endif
}
void JSCExecutor::terminateOnJSVMThread() {
// terminateOwnedWebWorker mutates m_ownedWorkers so collect all the workers
// to terminate first
JSCExecutor::~JSCExecutor() {
// terminateWebWorker mutates m_webWorkers so collect all the workers to terminate first
std::vector<int> workerIds;
for (auto& it : m_ownedWorkers) {
workerIds.push_back(it.first);
for (auto it = m_webWorkers.begin(); it != m_webWorkers.end(); it++) {
workerIds.push_back(it->first);
}
for (int workerId : workerIds) {
terminateOwnedWebWorker(workerId);
terminateWebWorker(workerId);
}
s_globalContextRefToJSCExecutor.erase(m_context);
JSGlobalContextRelease(m_context);
m_context = nullptr;
}
void JSCExecutor::loadApplicationScript(
@@ -215,10 +166,6 @@ void JSCExecutor::loadApplicationUnbundle(
}
void JSCExecutor::flush() {
if (m_owner != nullptr) {
// Web workers don't support native modules yet
return;
}
// TODO: Make this a first class function instead of evaling. #9317773
std::string calls = executeJSCallWithJSC(m_context, "flushedQueue", std::vector<folly::dynamic>());
m_bridge->callNativeModules(calls, true);
@@ -310,114 +257,56 @@ void JSCExecutor::loadModule(uint32_t moduleId) {
evaluateScript(m_context, source, sourceUrl);
}
int JSCExecutor::addWebWorker(
const std::string& script,
JSValueRef workerRef,
JSValueRef globalObjRef) {
static std::atomic_int nextWorkerId(1);
int workerId = nextWorkerId++;
// WebWorker impl
Object globalObj = Value(m_context, globalObjRef).asObject();
auto workerMQT = WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get());
std::unique_ptr<JSCExecutor> worker;
workerMQT->runOnQueueSync([this, &worker, &script, &globalObj, workerId] () {
worker.reset(new JSCExecutor(m_bridge, workerId, this, script, globalObj.toJSONMap(), m_jscConfig));
});
Object workerObj = Value(m_context, workerRef).asObject();
workerObj.makeProtected();
m_ownedWorkers.emplace(std::piecewise_construct, std::forward_as_tuple(workerId), std::forward_as_tuple(std::move(worker), std::move(workerObj)));
return workerId;
JSGlobalContextRef JSCExecutor::getContext() {
return m_context;
}
void JSCExecutor::postMessageToOwnedWebWorker(int workerId, JSValueRef message, JSValueRef *exn) {
auto worker = m_ownedWorkers.at(workerId).getExecutor();
std::string msgString = Value(m_context, message).toJSONString();
std::shared_ptr<bool> isWorkerDestroyed = worker->m_isDestroyed;
worker->m_messageQueueThread->runOnQueue([isWorkerDestroyed, worker, msgString] () {
if (*isWorkerDestroyed) {
return;
}
worker->receiveMessageFromOwner(msgString);
});
std::shared_ptr<MessageQueueThread> JSCExecutor::getMessageQueueThread() {
return m_messageQueueThread;
}
void JSCExecutor::postMessageToOwner(JSValueRef msg) {
std::string msgString = Value(m_context, msg).toJSONString();
std::shared_ptr<bool> ownerIsDestroyed = m_owner->m_isDestroyed;
m_owner->m_messageQueueThread->runOnQueue([workerId=m_workerId, owner=m_owner, ownerIsDestroyed, msgString] () {
if (*ownerIsDestroyed) {
return;
}
owner->receiveMessageFromOwnedWebWorker(workerId, msgString);
});
}
void JSCExecutor::onMessageReceived(int workerId, const std::string& json) {
Object& worker = m_webWorkerJSObjs.at(workerId);
void JSCExecutor::receiveMessageFromOwnedWebWorker(int workerId, const std::string& json) {
Object* workerObj;
try {
workerObj = &m_ownedWorkers.at(workerId).jsObj;
} catch (std::out_of_range& e) {
// Worker was already terminated
return;
}
Value onmessageValue = workerObj->getProperty("onmessage");
Value onmessageValue = worker.getProperty("onmessage");
if (onmessageValue.isUndefined()) {
return;
}
JSValueRef args[] = { createMessageObject(json) };
JSValueRef args[] = { JSCWebWorker::createMessageObject(m_context, json) };
onmessageValue.asObject().callAsFunction(1, args);
flush();
}
void JSCExecutor::receiveMessageFromOwner(const std::string& msgString) {
CHECK(m_owner) << "Received message in a Executor that doesn't have an owner!";
int JSCExecutor::addWebWorker(const std::string& script, JSValueRef workerRef) {
static std::atomic_int nextWorkerId(0);
int workerId = nextWorkerId++;
JSValueRef args[] = { createMessageObject(msgString) };
Value onmessageValue = Object::getGlobalObject(m_context).getProperty("onmessage");
onmessageValue.asObject().callAsFunction(1, args);
m_webWorkers.emplace(std::piecewise_construct, std::forward_as_tuple(workerId), std::forward_as_tuple(workerId, this, script));
Object workerObj = Value(m_context, workerRef).asObject();
workerObj.makeProtected();
m_webWorkerJSObjs.emplace(workerId, std::move(workerObj));
return workerId;
}
void JSCExecutor::terminateOwnedWebWorker(int workerId) {
auto worker = m_ownedWorkers.at(workerId).getExecutor();
std::shared_ptr<MessageQueueThread> workerMQT = worker->m_messageQueueThread;
m_ownedWorkers.erase(workerId);
workerMQT->quitSynchronous();
void JSCExecutor::postMessageToWebWorker(int workerId, JSValueRef message, JSValueRef *exn) {
JSCWebWorker& worker = m_webWorkers.at(workerId);
worker.postMessage(message);
}
Object JSCExecutor::createMessageObject(const std::string& msgJson) {
Value rebornJSMsg = Value::fromJSON(m_context, String(msgJson.c_str()));
Object messageObject = Object::create(m_context);
messageObject.setProperty("data", rebornJSMsg);
return messageObject;
void JSCExecutor::terminateWebWorker(int workerId) {
JSCWebWorker& worker = m_webWorkers.at(workerId);
worker.terminate();
m_webWorkers.erase(workerId);
m_webWorkerJSObjs.erase(workerId);
}
// Native JS hooks
JSValueRef JSCExecutor::nativePostMessage(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount != 1) {
*exception = makeJSCException(ctx, "postMessage got wrong number of arguments");
return JSValueMakeUndefined(ctx);
}
JSValueRef msg = arguments[0];
JSCExecutor *webWorker = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
webWorker->postMessageToOwner(msg);
return JSValueMakeUndefined(ctx);
}
static JSValueRef makeInvalidModuleIdJSCException(
JSContextRef ctx,
@@ -508,7 +397,6 @@ JSValueRef JSCExecutor::nativeStartWorker(
std::string scriptFile = Value(ctx, arguments[0]).toString().str();
JSValueRef worker = arguments[1];
JSValueRef globalObj = arguments[2];
JSCExecutor *executor;
try {
@@ -518,7 +406,7 @@ JSValueRef JSCExecutor::nativeStartWorker(
return JSValueMakeUndefined(ctx);
}
int workerId = executor->addWebWorker(scriptFile, worker, globalObj);
int workerId = executor->addWebWorker(scriptFile, worker);
return JSValueMakeNumber(ctx, workerId);
}
@@ -549,7 +437,7 @@ JSValueRef JSCExecutor::nativePostMessageToWorker(
return JSValueMakeUndefined(ctx);
}
executor->postMessageToOwnedWebWorker((int) workerDouble, arguments[1], exception);
executor->postMessageToWebWorker((int) workerDouble, arguments[1], exception);
return JSValueMakeUndefined(ctx);
}
@@ -580,7 +468,7 @@ JSValueRef JSCExecutor::nativeTerminateWorker(
return JSValueMakeUndefined(ctx);
}
executor->terminateOwnedWebWorker((int) workerDouble);
executor->terminateWebWorker((int) workerDouble);
return JSValueMakeUndefined(ctx);
}