0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 08:12:37 +01:00

ircd::js: Moz people recommend thread_local JSRuntime/JSContext;

There is no reason to clutter most arguments with contexts. Additionally
these will be merged in the future SpiderMonkey. There is only one
JSRuntime/JSContext per thread and it must be locked when in use.
This commit is contained in:
Jason Volk 2016-10-15 16:53:44 -07:00
parent 6db6b2a975
commit 0e2b8600e5
11 changed files with 218 additions and 178 deletions

View file

@ -27,19 +27,16 @@ namespace js {
class compartment_guard
{
JSContext *cx;
JSCompartment *cp;
public:
compartment_guard(JSContext &, JSObject &);
compartment_guard(JSObject &);
~compartment_guard() noexcept;
};
inline
compartment_guard::compartment_guard(JSContext &cx,
JSObject &obj)
:cx{&cx}
,cp{JS_EnterCompartment(&cx, &obj)}
compartment_guard::compartment_guard(JSObject &obj)
:cp{JS_EnterCompartment(*cx, &obj)}
{
}
@ -47,7 +44,7 @@ inline
compartment_guard::~compartment_guard()
noexcept
{
JS_LeaveCompartment(cx, cp);
JS_LeaveCompartment(*cx, cp);
}
} // namespace js

View file

@ -28,6 +28,16 @@ namespace js {
struct context
:private custom_ptr<JSContext>
{
class lock
{
context *c;
public:
lock(context &c); // BeginRequest on cx of your choice
lock(); // BeginRequest on the thread_local cx
~lock() noexcept; // EndRequest
};
struct opts
{
size_t stack_chunk_size = 8_KiB;
@ -49,11 +59,17 @@ struct context
context(const context &) = delete;
context &operator=(context &&) noexcept;
context &operator=(const context &) = delete;
~context() noexcept;
};
// Current thread_local context. Runtimes/Contexts (soon to be merged in future SpiderMonkey)
// are singled-threaded and this points to the context appropos your thread.
// Do not construct more than one context on the same thread- this is overwritten.
extern __thread context *cx;
// A default JSContext instance is provided residing near the main runtime as a convenience
// for misc/utility/system purposes if necessary.
extern context mc;
// for misc/utility/system purposes if necessary. You should use *cx instead.
extern context main_cx;
// Get to our `struct context` from any upstream JSContext
const context &our(const JSContext *const &);
@ -72,18 +88,36 @@ auto interrupted(const context &c) { return JS_CheckForInterrupt(c
void out_of_memory(context &c) { JS_ReportOutOfMemory(c); }
void allocation_overflow(context &c) { JS_ReportAllocationOverflow(c); }
void run_gc(context &c) { JS_MaybeGC(c); }
JSObject *current_global_p(context &c);
JS::RootedObject current_global(context &c);
JSObject *current_global(context &c);
JS::RootedObject current_global(context &c, rooted_t);
// thread_local
JSObject *current_global();
JS::RootedObject current_global(rooted_t);
inline JS::RootedObject
current_global(context &c)
current_global(rooted_t)
{
return { c, current_global_p(c) };
return { *cx, current_global(*cx, rooted) };
}
inline JSObject *
current_global_p(context &c)
current_global()
{
return current_global(*cx);
}
inline JS::RootedObject
current_global(context &c,
rooted_t)
{
return { c, current_global(c, rooted) };
}
inline JSObject *
current_global(context &c)
{
return JS::CurrentGlobalOrNull(c);
}

View file

@ -29,46 +29,43 @@ using closure_id = std::function<void (const JS::HandleId &)>;
using closure_key_val = std::function<void (JS::HandleValue, JS::HandleValue)>;
using closure_mutable_key_val = std::function<void (JS::HandleValue, JS::MutableHandleValue)>;
void for_each(context &, const JS::HandleObject &, const closure_id &);
void for_each(context &, const JS::HandleObject &, const closure_key_val &);
void for_each(context &, const JS::MutableHandleObject &, const closure_mutable_key_val &);
void for_each(const JS::HandleObject &, const closure_id &);
void for_each(const JS::HandleObject &, const closure_key_val &);
void for_each(const JS::MutableHandleObject &, const closure_mutable_key_val &);
inline void
for_each(context &c,
const JS::MutableHandleObject &obj,
for_each(const JS::MutableHandleObject &obj,
const closure_mutable_key_val &closure)
{
for_each(c, obj, [&c, &obj, &closure]
for_each(obj, [&obj, &closure]
(const JS::HandleId &hid)
{
JS::RootedValue key(c, id(c, hid)), val(c);
JS_GetPropertyById(c, obj, hid, &val);
JS::RootedValue key(*cx, id(hid)), val(*cx);
JS_GetPropertyById(*cx, obj, hid, &val);
closure(key, &val);
});
}
inline void
for_each(context &c,
const JS::HandleObject &obj,
for_each(const JS::HandleObject &obj,
const closure_key_val &closure)
{
for_each(c, obj, [&c, &obj, &closure]
for_each(obj, [&obj, &closure]
(const JS::HandleId &hid)
{
JS::RootedValue key(c, id(c, hid)), val(c);
JS_GetPropertyById(c, obj, hid, &val);
JS::RootedValue key(*cx, id(hid)), val(*cx);
JS_GetPropertyById(*cx, obj, hid, &val);
closure(key, val);
});
}
inline void
for_each(context &c,
const JS::HandleObject &obj,
for_each(const JS::HandleObject &obj,
const closure_id &closure)
{
JS::Rooted<JS::IdVector> props(c, JS::IdVector(c.ptr()));
if(JS_Enumerate(c, obj, &props))
JS::Rooted<JS::IdVector> props(*cx, JS::IdVector(cx->ptr()));
if(JS_Enumerate(*cx, obj, &props))
for(size_t i(0); i < props.length(); ++i)
closure(props[i]);
}

View file

@ -25,64 +25,59 @@
namespace ircd {
namespace js {
JS::RootedValue id(context &, const jsid &);
const jsid &id(context &, const JSProtoKey &);
const jsid &id(context &, const uint32_t &);
const jsid &id(context &, const JS::HandleString &);
const jsid &id(context &, const JS::HandleValue &);
JS::RootedValue id(const jsid &);
const jsid &id(const JSProtoKey &);
const jsid &id(const uint32_t &);
const jsid &id(const JS::HandleString &);
const jsid &id(const JS::HandleValue &);
inline const jsid &
id(context &c,
const JS::HandleValue &h)
id(const JS::HandleValue &h)
{
JS::RootedId ret(c);
if(!JS_ValueToId(c, h, &ret))
JS::RootedId ret(*cx);
if(!JS_ValueToId(*cx, h, &ret))
std::terminate(); //TODO: exception
return ret;
}
inline const jsid &
id(context &c,
const JS::HandleString &h)
id(const JS::HandleString &h)
{
JS::RootedId ret(c);
if(!JS_StringToId(c, h, &ret))
JS::RootedId ret(*cx);
if(!JS_StringToId(*cx, h, &ret))
std::terminate(); //TODO: exception
return ret;
}
inline const jsid &
id(context &c,
const uint32_t &index)
id(const uint32_t &index)
{
JS::RootedId ret(c);
if(!JS_IndexToId(c, index, &ret))
JS::RootedId ret(*cx);
if(!JS_IndexToId(*cx, index, &ret))
std::terminate(); //TODO: exception
return ret;
}
inline const jsid &
id(context &c,
const JSProtoKey &key)
id(const JSProtoKey &key)
{
JS::RootedId ret(c);
JS::ProtoKeyToId(c, key, &ret);
JS::RootedId ret(*cx);
JS::ProtoKeyToId(*cx, key, &ret);
return ret;
}
inline JS::RootedValue
id(context &c,
const jsid &h)
id(const jsid &h)
{
JS::RootedValue ret(c);
if(!JS_IdToValue(c, h, &ret))
JS::RootedValue ret(*cx);
if(!JS_IdToValue(*cx, h, &ret))
std::terminate(); //TODO: exception
return { c, ret };
return { *cx, ret };
}
} // namespace js

View file

@ -61,7 +61,6 @@ JSVersion version(const char *const &v) { return JS_StringToVersion(v);
#include "runtime.h"
#include "context.h"
#include "request_guard.h"
#include "compartment_guard.h"
#include "id.h"
#include "string.h"

View file

@ -1,52 +0,0 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#define HAVE_IRCD_JS_REQUEST_GUARD_H
namespace ircd {
namespace js {
class request_guard
{
JSContext *c;
public:
request_guard(JSContext &);
~request_guard() noexcept;
};
inline
request_guard::request_guard(JSContext &c)
:c(&c)
{
JS_BeginRequest(&c);
}
inline
request_guard::~request_guard()
noexcept
{
JS_EndRequest(c);
}
} // namespace js
} // namespace ircd

View file

@ -61,12 +61,18 @@ class runtime
runtime(const runtime &) = delete;
runtime &operator=(runtime &&) noexcept;
runtime &operator=(const runtime &) = delete;
~runtime() noexcept;
friend void interrupt(runtime &);
};
// Main JSRuntime instance. This should be passable in any argument requiring a
// JSRuntime pointer. It is only valid while the js::init object is held by ircd::main().
// Current thread_local runtime. Runtimes/Contexts are single-threaded and we maintain
// one per thread (or only one will be relevant on the given thread you are on).
// Do not construct more than one runtime on the same thread- this is overwritten.
extern __thread runtime *rt;
// Main JSRuntime instance. It is only valid while the js::init object is held by ircd::main().
// This is available to always find main for whatever reason, but use *rt instead.
extern runtime main;
// Get to our `struct runtime` from any upstream JSRuntime

View file

@ -27,12 +27,12 @@ namespace js {
struct script
{
script(JSContext &);
script();
~script() noexcept;
};
inline
script::script(JSContext &c)
script::script()
{
}

View file

@ -26,60 +26,54 @@ namespace ircd {
namespace js {
// C++ --> JS
JSString &string(context &, const std::string &);
JS::RootedString string(context &, const std::string &, rooted_t);
JSString &string(const std::string &);
JS::RootedString string(const std::string &, rooted_t);
// JS --> C++
std::string string(context &, const JSString &);
std::string string(context &, const JSString *const &);
std::string string(const JSString &);
std::string string(const JSString *const &);
std::string string(context &, const JS::Value &);
std::string string(context &, const jsid &);
std::string string(const JS::Value &);
std::string string(const jsid &);
inline std::string
string(context &c,
const jsid &hid)
string(const jsid &hid)
{
return string(c, id(c, hid));
return string(id(hid));
}
inline std::string
string(context &c,
const JS::Value &s)
string(const JS::Value &s)
{
return s.isString()? string(c, s.toString()) : std::string{};
return s.isString()? string(s.toString()) : std::string{};
}
inline std::string
string(context &c,
const JSString *const &s)
string(const JSString *const &s)
{
return s? string(c, *s) : std::string{};
return s? string(*s) : std::string{};
}
inline std::string
string(context &c,
const JSString &s)
string(const JSString &s)
{
std::string ret(JS_GetStringEncodingLength(c, const_cast<JSString *>(&s)), char());
JS_EncodeStringToBuffer(c, const_cast<JSString *>(&s), &ret.front(), ret.size());
std::string ret(JS_GetStringEncodingLength(*cx, const_cast<JSString *>(&s)), char());
JS_EncodeStringToBuffer(*cx, const_cast<JSString *>(&s), &ret.front(), ret.size());
return ret;
}
inline JS::RootedString
string(context &c,
const std::string &s,
string(const std::string &s,
rooted_t)
{
return { c, &string(c, s) };
return { *cx, &string(s) };
}
inline JSString &
string(context &c,
const std::string &s)
string(const std::string &s)
{
const auto ret(JS_NewStringCopyN(c, s.data(), s.size()));
const auto ret(JS_NewStringCopyN(*cx, s.data(), s.size()));
if(unlikely(!ret))
std::terminate(); //TODO: exception

View file

@ -31,14 +31,14 @@ class trap
const JSClass _class;
// Override these to define JS objects in C
virtual bool on_add(context &, const JSObject &, const jsid &, const JS::Value &);
virtual bool on_set(context &, const JSObject &, const jsid &, JS::MutableHandleValue);
virtual bool on_get(context &, const JSObject &, const jsid &, JS::MutableHandleValue);
virtual bool on_del(context &, const JSObject &, const jsid &);
virtual bool on_res(context &, const JSObject &, const jsid &, bool &resolved);
virtual bool on_enu(context &, const JSObject &);
virtual bool on_call(context &, const unsigned &argc, JS::Value &argv);
virtual bool on_ctor(context &, const unsigned &argc, JS::Value &argv);
virtual bool on_add(const JSObject &, const jsid &, const JS::Value &);
virtual bool on_set(const JSObject &, const jsid &, JS::MutableHandleValue);
virtual bool on_get(const JSObject &, const jsid &, JS::MutableHandleValue);
virtual bool on_del(const JSObject &, const jsid &);
virtual bool on_res(const JSObject &, const jsid &, bool &resolved);
virtual bool on_enu(const JSObject &);
virtual bool on_call(const unsigned &argc, JS::Value &argv);
virtual bool on_ctor(const unsigned &argc, JS::Value &argv);
private:
static trap &from(const JSObject &);
@ -61,8 +61,8 @@ class trap
auto &name() const { return _name; }
auto &jsclass() const { return _class; }
JSObject *operator()(context &, JS::HandleObject proto);
JSObject *operator()(context &);
JSObject *operator()(JS::HandleObject proto);
JSObject *operator()();
trap(std::string name, const uint32_t &flags = 0);
trap(trap &&) = delete;

View file

@ -27,6 +27,11 @@
namespace ircd {
namespace js {
// Location of the thread_local runtext. externs exist in js/runtime.h and js/context.h.
// If these are null, js is not available on your thread.
__thread runtime *rt;
__thread context *cx;
// Location of the main JSRuntime instance. An extern reference to this exists in js/runtime.h.
// It is null until js::init manually constructs (and later destructs) it.
runtime main;
@ -135,16 +140,15 @@ noexcept
}
JSObject *
ircd::js::trap::operator()(context &c)
ircd::js::trap::operator()()
{
return JS_NewObject(c, &_class);
return JS_NewObject(*cx, &_class);
}
JSObject *
ircd::js::trap::operator()(context &c,
JS::HandleObject proto)
ircd::js::trap::operator()(JS::HandleObject proto)
{
return JS_NewObjectWithGivenProto(c, &_class, proto);
return JS_NewObjectWithGivenProto(*cx, &_class, proto);
}
void
@ -158,6 +162,8 @@ ircd::js::trap::handle_ctor(JSContext *const c,
unsigned argc,
JS::Value *const argv)
{
assert(&our(c) == cx);
return false;
}
@ -166,6 +172,8 @@ ircd::js::trap::handle_call(JSContext *const c,
unsigned argc,
JS::Value *const argv)
{
assert(&our(c) == cx);
return false;
}
@ -173,8 +181,10 @@ bool
ircd::js::trap::handle_enu(JSContext *const c,
JS::HandleObject obj)
{
assert(&our(c) == cx);
auto &trap(from(obj));
return trap.on_enu(our(c), *obj.get());
return trap.on_enu(*obj.get());
}
bool
@ -183,8 +193,10 @@ ircd::js::trap::handle_res(JSContext *const c,
JS::HandleId id,
bool *const resolved)
{
assert(&our(c) == cx);
auto &trap(from(obj));
return trap.on_res(our(c), *obj.get(), id.get(), *resolved);
return trap.on_res(*obj.get(), id.get(), *resolved);
}
bool
@ -193,8 +205,10 @@ ircd::js::trap::handle_del(JSContext *const c,
JS::HandleId id,
JS::ObjectOpResult &res)
{
assert(&our(c) == cx);
auto &trap(from(obj));
if(!trap.on_del(our(c), *obj.get(), id.get()))
if(!trap.on_del(*obj.get(), id.get()))
return false;
res.succeed();
@ -207,8 +221,10 @@ ircd::js::trap::handle_get(JSContext *const c,
JS::HandleId id,
JS::MutableHandleValue val)
{
assert(&our(c) == cx);
auto &trap(from(obj));
return trap.on_get(our(c), *obj.get(), id.get(), val);
return trap.on_get(*obj.get(), id.get(), val);
}
bool
@ -218,8 +234,10 @@ ircd::js::trap::handle_set(JSContext *const c,
JS::MutableHandleValue val,
JS::ObjectOpResult &res)
{
assert(&our(c) == cx);
auto &trap(from(obj));
return trap.on_get(our(c), *obj.get(), id.get(), val);
return trap.on_get(*obj.get(), id.get(), val);
}
bool
@ -228,8 +246,10 @@ ircd::js::trap::handle_add(JSContext *const c,
JS::HandleId id,
JS::HandleValue val)
{
assert(&our(c) == cx);
auto &trap(from(obj));
return trap.on_add(our(c), *obj.get(), id.get(), val.get());
return trap.on_add(*obj.get(), id.get(), val.get());
}
bool
@ -238,6 +258,8 @@ ircd::js::trap::handle_inst(JSContext *const c,
JS::MutableHandleValue val,
bool *const has_instance)
{
assert(&our(c) == cx);
return false;
}
@ -267,31 +289,27 @@ ircd::js::trap::from(const JSObject &o)
}
bool
ircd::js::trap::on_ctor(context &c,
const unsigned &argc,
ircd::js::trap::on_ctor(const unsigned &argc,
JS::Value &argv)
{
return false;
}
bool
ircd::js::trap::on_call(context &c,
const unsigned &argc,
ircd::js::trap::on_call(const unsigned &argc,
JS::Value &argv)
{
return false;
}
bool
ircd::js::trap::on_enu(context &c,
const JSObject &obj)
ircd::js::trap::on_enu(const JSObject &obj)
{
return false;
}
bool
ircd::js::trap::on_res(context &c,
const JSObject &obj,
ircd::js::trap::on_res(const JSObject &obj,
const jsid &id,
bool &resolved)
{
@ -299,16 +317,14 @@ ircd::js::trap::on_res(context &c,
}
bool
ircd::js::trap::on_del(context &c,
const JSObject &obj,
ircd::js::trap::on_del(const JSObject &obj,
const jsid &id)
{
return false;
}
bool
ircd::js::trap::on_get(context &c,
const JSObject &obj,
ircd::js::trap::on_get(const JSObject &obj,
const jsid &id,
JS::MutableHandleValue val)
{
@ -316,8 +332,7 @@ ircd::js::trap::on_get(context &c,
}
bool
ircd::js::trap::on_set(context &c,
const JSObject &obj,
ircd::js::trap::on_set(const JSObject &obj,
const jsid &id,
JS::MutableHandleValue val)
{
@ -325,8 +340,7 @@ ircd::js::trap::on_set(context &c,
}
bool
ircd::js::trap::on_add(context &c,
const JSObject &obj,
ircd::js::trap::on_add(const JSObject &obj,
const jsid &id,
const JS::Value &val)
{
@ -499,7 +513,15 @@ ircd::js::context::context(JSRuntime *const &runtime,
}
,opts{opts}
{
assert(&our(runtime) == rt); // Trying to construct on thread without runtime thread_local
// Use their privdata pointer to point to our instance. We can then use our(JSContext*)
// to get back to `this` instance. Remember the pointer must change when this class is
// std::move()'ed etc via the move constructor/assignment.
JS_SetContextPrivate(get(), this);
// Assign the thread_local here.
cx = this;
}
ircd::js::context::context(context &&other)
@ -510,6 +532,9 @@ noexcept
// Branch not taken for null/defaulted instance of JSContext smart ptr
if(!!*this)
JS_SetContextPrivate(get(), this);
// Ensure the thread_local points here now.
cx = this;
}
ircd::js::context &
@ -517,16 +542,43 @@ ircd::js::context::operator=(context &&other)
noexcept
{
static_cast<custom_ptr<JSContext> &>(*this) = std::move(other);
opts = std::move(other.opts);
// Branch not taken for null/defaulted instance of JSContext smart ptr
if(!!*this)
JS_SetContextPrivate(get(), this);
// Ensure the thread_local points here now.
cx = this;
return *this;
}
ircd::js::context::~context()
noexcept
{
// Branch not taken on std::move()
if(!!*this)
cx = nullptr;
}
ircd::js::context::lock::lock()
:lock{*cx}
{
}
ircd::js::context::lock::lock(context &c)
:c{&c}
{
JS_BeginRequest(c);
}
ircd::js::context::lock::~lock()
noexcept
{
JS_EndRequest(*c);
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd/js/runtime.h
@ -540,8 +592,13 @@ ircd::js::runtime::runtime(const struct opts &opts)
}
,opts(opts)
{
// We use their privdata to find `this` via our(JSRuntime*) function.
// Any additional user privdata will have to ride a member in this class itself.
JS_SetRuntimePrivate(get(), this);
// Assign the thread_local runtime pointer here.
rt = this;
JS_SetErrorReporter(get(), handle_error);
JS::SetOutOfMemoryCallback(get(), handle_out_of_memory, nullptr);
JS::SetLargeAllocationFailureCallback(get(), handle_large_allocation_failure, nullptr);
@ -562,6 +619,9 @@ noexcept
// Branch not taken for null/defaulted instance of JSRuntime smart ptr
if(!!*this)
JS_SetRuntimePrivate(get(), this);
// Ensure the thread_local runtime always points here
rt = this;
}
ircd::js::runtime &
@ -569,16 +629,26 @@ ircd::js::runtime::operator=(runtime &&other)
noexcept
{
static_cast<custom_ptr<JSRuntime> &>(*this) = std::move(other);
opts = std::move(other.opts);
// Branch not taken for null/defaulted instance of JSRuntime smart ptr
if(!!*this)
JS_SetRuntimePrivate(get(), this);
// Ensure the thread_local runtime always points here
rt = this;
return *this;
}
ircd::js::runtime::~runtime()
noexcept
{
// Branch not taken on std::move()
if(!!*this)
rt = nullptr;
}
bool
ircd::js::runtime::handle_interrupt(JSContext *const ctx)
{