mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 875661 - Part 1: Refactor a thread-safe context out of ForkJoinSlice and JSContext. (r=billm)
This commit is contained in:
parent
852273e373
commit
7cce974dbd
@ -20,18 +20,6 @@
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
void *
|
||||
js::Allocator::onOutOfMemory(void *p, size_t nbytes)
|
||||
{
|
||||
return zone->rt->onOutOfMemory(p, nbytes);
|
||||
}
|
||||
|
||||
void
|
||||
js::Allocator::reportAllocationOverflow()
|
||||
{
|
||||
js_ReportAllocationOverflow(NULL);
|
||||
}
|
||||
|
||||
JS::Zone::Zone(JSRuntime *rt)
|
||||
: rt(rt),
|
||||
allocator(this),
|
||||
|
@ -34,9 +34,9 @@ namespace js {
|
||||
* parallel or sequential mode, you should make it take an
|
||||
* |Allocator*| rather than a |JSContext*|.
|
||||
*/
|
||||
class Allocator : public MallocProvider<Allocator>
|
||||
class Allocator
|
||||
{
|
||||
JS::Zone *zone;
|
||||
JS::Zone *zone_;
|
||||
|
||||
public:
|
||||
explicit Allocator(JS::Zone *zone);
|
||||
@ -44,10 +44,6 @@ class Allocator : public MallocProvider<Allocator>
|
||||
js::gc::ArenaLists arenas;
|
||||
|
||||
inline void *parallelNewGCThing(gc::AllocKind thingKind, size_t thingSize);
|
||||
|
||||
void *onOutOfMemory(void *p, size_t nbytes);
|
||||
inline void updateMallocCounter(size_t nbytes);
|
||||
void reportAllocationOverflow();
|
||||
};
|
||||
|
||||
typedef Vector<JSCompartment *, 1, SystemAllocPolicy> CompartmentVector;
|
||||
|
@ -179,8 +179,7 @@ ion::ParPush(ParPushArgs *args)
|
||||
// slow path anyhow as it reallocates the elements vector.
|
||||
ForkJoinSlice *slice = js::ForkJoinSlice::Current();
|
||||
JSObject::EnsureDenseResult res =
|
||||
args->object->parExtendDenseElements(slice->allocator,
|
||||
&args->value, 1);
|
||||
args->object->parExtendDenseElements(slice, &args->value, 1);
|
||||
if (res != JSObject::ED_OK)
|
||||
return NULL;
|
||||
return args->object;
|
||||
@ -190,7 +189,7 @@ JSObject *
|
||||
ion::ParExtendArray(ForkJoinSlice *slice, JSObject *array, uint32_t length)
|
||||
{
|
||||
JSObject::EnsureDenseResult res =
|
||||
array->parExtendDenseElements(slice->allocator, NULL, length);
|
||||
array->parExtendDenseElements(slice, NULL, length);
|
||||
if (res != JSObject::ED_OK)
|
||||
return NULL;
|
||||
return array;
|
||||
@ -475,8 +474,7 @@ ion::InitRestParameter(ForkJoinSlice *slice, uint32_t length, Value *rest,
|
||||
JS_ASSERT(res->type()->unknownProperties());
|
||||
|
||||
if (length) {
|
||||
JSObject::EnsureDenseResult edr =
|
||||
res->parExtendDenseElements(slice->allocator, rest, length);
|
||||
JSObject::EnsureDenseResult edr = res->parExtendDenseElements(slice, rest, length);
|
||||
if (edr != JSObject::ED_OK)
|
||||
return TP_FATAL;
|
||||
}
|
||||
|
@ -1134,8 +1134,40 @@ js_HandleExecutionInterrupt(JSContext *cx)
|
||||
return result;
|
||||
}
|
||||
|
||||
JSContext::JSContext(JSRuntime *rt)
|
||||
js::ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
|
||||
: ContextFriendFields(rt),
|
||||
contextKind_(kind),
|
||||
perThreadData(pt)
|
||||
{ }
|
||||
|
||||
bool
|
||||
ThreadSafeContext::isJSContext() const
|
||||
{
|
||||
return contextKind_ == Context_JS;
|
||||
}
|
||||
|
||||
JSContext *
|
||||
ThreadSafeContext::asJSContext()
|
||||
{
|
||||
JS_ASSERT(isJSContext());
|
||||
return reinterpret_cast<JSContext *>(this);
|
||||
}
|
||||
|
||||
bool
|
||||
ThreadSafeContext::isForkJoinSlice() const
|
||||
{
|
||||
return contextKind_ == Context_ForkJoin;
|
||||
}
|
||||
|
||||
ForkJoinSlice *
|
||||
ThreadSafeContext::asForkJoinSlice()
|
||||
{
|
||||
JS_ASSERT(isForkJoinSlice());
|
||||
return reinterpret_cast<ForkJoinSlice *>(this);
|
||||
}
|
||||
|
||||
JSContext::JSContext(JSRuntime *rt)
|
||||
: ThreadSafeContext(rt, &rt->mainThread, Context_JS),
|
||||
defaultVersion(JSVERSION_DEFAULT),
|
||||
hasVersionOverride(false),
|
||||
throwing(false),
|
||||
|
@ -440,16 +440,12 @@ namespace js {
|
||||
* there is only one instance of this struct, embedded in the
|
||||
* JSRuntime as the field |mainThread|. During Parallel JS sections,
|
||||
* however, there will be one instance per worker thread.
|
||||
*
|
||||
* The eventual plan is to designate thread-safe portions of the
|
||||
* interpreter and runtime by having them take |PerThreadData*|
|
||||
* arguments instead of |JSContext*| or |JSRuntime*|.
|
||||
*/
|
||||
class PerThreadData : public js::PerThreadDataFriendFields
|
||||
{
|
||||
/*
|
||||
* Backpointer to the full shared JSRuntime* with which this
|
||||
* thread is associaed. This is private because accessing the
|
||||
* thread is associated. This is private because accessing the
|
||||
* fields of this runtime can provoke race conditions, so the
|
||||
* intention is that access will be mediated through safe
|
||||
* functions like |associatedWith()| below.
|
||||
@ -1502,11 +1498,63 @@ FreeOp::free_(void *p)
|
||||
js_free(p);
|
||||
}
|
||||
|
||||
class ForkJoinSlice;
|
||||
|
||||
/*
|
||||
* ThreadSafeContext is the base class for both JSContext, the "normal"
|
||||
* sequential context, and ForkJoinSlice, the per-thread parallel context used
|
||||
* in PJS.
|
||||
*
|
||||
* When cast to a ThreadSafeContext, the only usable operations are casting
|
||||
* back to the context from which it came, and generic allocation
|
||||
* operations. These generic versions branch internally based on whether the
|
||||
* underneath context is really a JSContext or a ForkJoinSlice, and are in
|
||||
* general more expensive than using the context directly.
|
||||
*
|
||||
* Thus, ThreadSafeContext should only be used for VM functions that may be
|
||||
* called in both sequential and parallel execution. The most likely class of
|
||||
* VM functions that do these are those that allocate commonly used data
|
||||
* structures, such as concatenating strings and extending elements.
|
||||
*/
|
||||
struct ThreadSafeContext : js::ContextFriendFields,
|
||||
public MallocProvider<ThreadSafeContext>
|
||||
{
|
||||
public:
|
||||
enum ContextKind {
|
||||
Context_JS,
|
||||
Context_ForkJoin
|
||||
};
|
||||
|
||||
private:
|
||||
ContextKind contextKind_;
|
||||
|
||||
public:
|
||||
PerThreadData *perThreadData;
|
||||
|
||||
explicit ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind);
|
||||
|
||||
bool isJSContext() const;
|
||||
JSContext *asJSContext();
|
||||
|
||||
bool isForkJoinSlice() const;
|
||||
ForkJoinSlice *asForkJoinSlice();
|
||||
|
||||
void *onOutOfMemory(void *p, size_t nbytes) {
|
||||
return runtime_->onOutOfMemory(p, nbytes, isJSContext() ? asJSContext() : NULL);
|
||||
}
|
||||
inline void updateMallocCounter(size_t nbytes) {
|
||||
/* Note: this is racy. */
|
||||
runtime_->updateMallocCounter(zone_, nbytes);
|
||||
}
|
||||
void reportAllocationOverflow() {
|
||||
js_ReportAllocationOverflow(isJSContext() ? asJSContext() : NULL);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
struct JSContext : js::ContextFriendFields,
|
||||
public mozilla::LinkedListElement<JSContext>,
|
||||
public js::MallocProvider<JSContext>
|
||||
struct JSContext : js::ThreadSafeContext,
|
||||
public mozilla::LinkedListElement<JSContext>
|
||||
{
|
||||
explicit JSContext(JSRuntime *rt);
|
||||
JSContext *thisDuringConstruction() { return this; }
|
||||
|
@ -38,16 +38,10 @@ js::AutoCompartment::~AutoCompartment()
|
||||
cx_->leaveCompartment(origin_);
|
||||
}
|
||||
|
||||
void
|
||||
js::Allocator::updateMallocCounter(size_t nbytes)
|
||||
{
|
||||
zone->rt->updateMallocCounter(zone, nbytes);
|
||||
}
|
||||
|
||||
inline void *
|
||||
js::Allocator::parallelNewGCThing(gc::AllocKind thingKind, size_t thingSize)
|
||||
{
|
||||
return arenas.parallelAllocate(zone, thingKind, thingSize);
|
||||
return arenas.parallelAllocate(zone_, thingKind, thingSize);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
@ -1140,7 +1140,7 @@ Zone::reduceGCTriggerBytes(size_t amount)
|
||||
}
|
||||
|
||||
Allocator::Allocator(Zone *zone)
|
||||
: zone(zone)
|
||||
: zone_(zone)
|
||||
{}
|
||||
|
||||
inline void
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TemplateLib.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -2763,41 +2763,35 @@ JSObject::maybeDensifySparseElements(JSContext *cx, HandleObject obj)
|
||||
}
|
||||
|
||||
ObjectElements *
|
||||
AllocateElements(JSObject::MaybeContext maybecx, JSObject *obj, uint32_t nelems)
|
||||
AllocateElements(ThreadSafeContext *tcx, JSObject *obj, uint32_t nelems)
|
||||
{
|
||||
if (JSContext *cx = maybecx.context) {
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
if (tcx->isJSContext()) {
|
||||
JSContext *cx = tcx->asJSContext();
|
||||
return cx->runtime()->gcNursery.allocateElements(cx, obj, nelems);
|
||||
#else
|
||||
return static_cast<js::ObjectElements *>(cx->malloc_(nelems * sizeof(HeapValue)));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
Allocator *alloc = maybecx.allocator;
|
||||
return static_cast<js::ObjectElements *>(alloc->malloc_(nelems * sizeof(HeapValue)));
|
||||
return static_cast<js::ObjectElements *>(tcx->malloc_(nelems * sizeof(HeapValue)));
|
||||
}
|
||||
|
||||
ObjectElements *
|
||||
ReallocateElements(JSObject::MaybeContext maybecx, JSObject *obj, ObjectElements *oldHeader,
|
||||
ReallocateElements(ThreadSafeContext *tcx, JSObject *obj, ObjectElements *oldHeader,
|
||||
uint32_t oldCount, uint32_t newCount)
|
||||
{
|
||||
if (JSContext *cx = maybecx.context) {
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
if (tcx->isJSContext()) {
|
||||
JSContext *cx = tcx->asJSContext();
|
||||
return cx->runtime()->gcNursery.reallocateElements(cx, obj, oldHeader, oldCount, newCount);
|
||||
#else
|
||||
return static_cast<js::ObjectElements *>(cx->realloc_(oldHeader,
|
||||
oldCount * sizeof(HeapValue),
|
||||
newCount * sizeof(HeapSlot)));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
Allocator *alloc = maybecx.allocator;
|
||||
return static_cast<js::ObjectElements *>(alloc->realloc_(oldHeader, oldCount * sizeof(HeapSlot),
|
||||
newCount * sizeof(HeapSlot)));
|
||||
return static_cast<js::ObjectElements *>(tcx->realloc_(oldHeader, oldCount * sizeof(HeapSlot),
|
||||
newCount * sizeof(HeapSlot)));
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::growElements(MaybeContext cx, uint32_t newcap)
|
||||
JSObject::growElements(ThreadSafeContext *tcx, uint32_t newcap)
|
||||
{
|
||||
JS_ASSERT(isExtensible());
|
||||
JS_ASSERT_IF(isArray() && !arrayLengthIsWritable(),
|
||||
@ -2844,11 +2838,11 @@ JSObject::growElements(MaybeContext cx, uint32_t newcap)
|
||||
|
||||
ObjectElements *newheader;
|
||||
if (hasDynamicElements()) {
|
||||
newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated);
|
||||
newheader = ReallocateElements(tcx, this, getElementsHeader(), oldAllocated, newAllocated);
|
||||
if (!newheader)
|
||||
return false; /* Leave elements as its old size. */
|
||||
} else {
|
||||
newheader = AllocateElements(cx, this, newAllocated);
|
||||
newheader = AllocateElements(tcx, this, newAllocated);
|
||||
if (!newheader)
|
||||
return false; /* Leave elements as its old size. */
|
||||
js_memcpy(newheader, getElementsHeader(),
|
||||
|
@ -540,17 +540,8 @@ class JSObject : public js::ObjectImpl
|
||||
static const char *className(JSContext *cx, js::HandleObject obj);
|
||||
|
||||
/* Accessors for elements. */
|
||||
|
||||
struct MaybeContext {
|
||||
js::Allocator *allocator;
|
||||
JSContext *context;
|
||||
|
||||
MaybeContext(JSContext *cx) : allocator(NULL), context(cx) {}
|
||||
MaybeContext(js::Allocator *alloc) : allocator(alloc), context(NULL) {}
|
||||
};
|
||||
|
||||
inline bool ensureElements(JSContext *cx, uint32_t cap);
|
||||
bool growElements(MaybeContext cx, uint32_t newcap);
|
||||
bool growElements(js::ThreadSafeContext *tcx, uint32_t newcap);
|
||||
void shrinkElements(JSContext *cx, uint32_t cap);
|
||||
inline void setDynamicElements(js::ObjectElements *header);
|
||||
|
||||
@ -586,10 +577,10 @@ class JSObject : public js::ObjectImpl
|
||||
*/
|
||||
enum EnsureDenseResult { ED_OK, ED_FAILED, ED_SPARSE };
|
||||
inline EnsureDenseResult ensureDenseElements(JSContext *cx, uint32_t index, uint32_t extra);
|
||||
inline EnsureDenseResult parExtendDenseElements(js::Allocator *alloc, js::Value *v,
|
||||
inline EnsureDenseResult parExtendDenseElements(js::ThreadSafeContext *tcx, js::Value *v,
|
||||
uint32_t extra);
|
||||
template<typename MallocProviderType>
|
||||
inline EnsureDenseResult extendDenseElements(MallocProviderType *cx,
|
||||
|
||||
inline EnsureDenseResult extendDenseElements(js::ThreadSafeContext *tcx,
|
||||
uint32_t requiredCapacity, uint32_t extra);
|
||||
|
||||
/* Convert a single dense element to a sparse property. */
|
||||
|
@ -557,9 +557,8 @@ JSObject::ensureDenseInitializedLength(JSContext *cx, uint32_t index, uint32_t e
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MallocProviderType>
|
||||
JSObject::EnsureDenseResult
|
||||
JSObject::extendDenseElements(MallocProviderType *cx,
|
||||
JSObject::extendDenseElements(js::ThreadSafeContext *tcx,
|
||||
uint32_t requiredCapacity, uint32_t extra)
|
||||
{
|
||||
/*
|
||||
@ -589,14 +588,14 @@ JSObject::extendDenseElements(MallocProviderType *cx,
|
||||
return ED_SPARSE;
|
||||
}
|
||||
|
||||
if (!growElements(cx, requiredCapacity))
|
||||
if (!growElements(tcx, requiredCapacity))
|
||||
return ED_FAILED;
|
||||
|
||||
return ED_OK;
|
||||
}
|
||||
|
||||
inline JSObject::EnsureDenseResult
|
||||
JSObject::parExtendDenseElements(js::Allocator *alloc, js::Value *v, uint32_t extra)
|
||||
JSObject::parExtendDenseElements(js::ThreadSafeContext *tcx, js::Value *v, uint32_t extra)
|
||||
{
|
||||
JS_ASSERT(isNative());
|
||||
JS_ASSERT_IF(isArray(), arrayLengthIsWritable());
|
||||
@ -608,7 +607,7 @@ JSObject::parExtendDenseElements(js::Allocator *alloc, js::Value *v, uint32_t ex
|
||||
return ED_SPARSE; /* Overflow. */
|
||||
|
||||
if (requiredCapacity > header->capacity) {
|
||||
EnsureDenseResult edr = extendDenseElements(alloc, requiredCapacity, extra);
|
||||
EnsureDenseResult edr = extendDenseElements(tcx, requiredCapacity, extra);
|
||||
if (edr != ED_OK)
|
||||
return edr;
|
||||
}
|
||||
|
@ -226,6 +226,8 @@ struct Runtime
|
||||
|
||||
namespace js {
|
||||
|
||||
struct ThreadSafeContext;
|
||||
|
||||
class Allocator;
|
||||
|
||||
class SkipRoot;
|
||||
@ -273,7 +275,7 @@ template <> struct RootKind<JS::Value> : SpecificRootKind<JS::Value, THING_ROOT_
|
||||
struct ContextFriendFields
|
||||
{
|
||||
protected:
|
||||
JSRuntime *const runtime_;
|
||||
JSRuntime *const runtime_;
|
||||
|
||||
/* The current compartment. */
|
||||
JSCompartment *compartment_;
|
||||
|
@ -68,7 +68,7 @@ ForkJoinSlice::releaseContext()
|
||||
}
|
||||
|
||||
bool
|
||||
ForkJoinSlice::isMainThread()
|
||||
ForkJoinSlice::isMainThread() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -387,6 +387,7 @@ class ForkJoinShared : public TaskExecutor, public Monitor
|
||||
void setAbortFlag(bool fatal);
|
||||
|
||||
JSRuntime *runtime() { return cx_->runtime(); }
|
||||
JS::Zone *zone() { return cx_->zone(); }
|
||||
|
||||
JSContext *acquireContext() { PR_Lock(cxLock_); return cx_; }
|
||||
void releaseContext() { PR_Unlock(cxLock_); }
|
||||
@ -1326,7 +1327,7 @@ ForkJoinShared::init()
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < numSlices_; i++) {
|
||||
Allocator *allocator = cx_->runtime()->new_<Allocator>(cx_->zone());
|
||||
Allocator *allocator = cx_->new_<Allocator>(cx_->zone());
|
||||
if (!allocator)
|
||||
return false;
|
||||
|
||||
@ -1411,6 +1412,7 @@ ForkJoinShared::executeFromWorker(uint32_t workerId, uintptr_t stackLimit)
|
||||
|
||||
PerThreadData thisThread(cx_->runtime());
|
||||
TlsPerThreadData.set(&thisThread);
|
||||
|
||||
// Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
|
||||
// lock has not been initialized in these cases.
|
||||
thisThread.ionStackLimit = stackLimit;
|
||||
@ -1645,16 +1647,22 @@ ForkJoinSlice::ForkJoinSlice(PerThreadData *perThreadData,
|
||||
uint32_t sliceId, uint32_t numSlices,
|
||||
Allocator *allocator, ForkJoinShared *shared,
|
||||
ParallelBailoutRecord *bailoutRecord)
|
||||
: perThreadData(perThreadData),
|
||||
sliceId(sliceId),
|
||||
numSlices(numSlices),
|
||||
allocator(allocator),
|
||||
bailoutRecord(bailoutRecord),
|
||||
shared(shared)
|
||||
{ }
|
||||
: ThreadSafeContext(shared->runtime(), perThreadData, Context_ForkJoin),
|
||||
sliceId(sliceId),
|
||||
numSlices(numSlices),
|
||||
allocator(allocator),
|
||||
bailoutRecord(bailoutRecord),
|
||||
shared(shared)
|
||||
{
|
||||
/*
|
||||
* Unsafely set the zone. This is used to track malloc counters and to
|
||||
* trigger GCs and is otherwise not thread-safe to access.
|
||||
*/
|
||||
zone_ = shared->zone();
|
||||
}
|
||||
|
||||
bool
|
||||
ForkJoinSlice::isMainThread()
|
||||
ForkJoinSlice::isMainThread() const
|
||||
{
|
||||
return perThreadData == &shared->runtime()->mainThread;
|
||||
}
|
||||
|
@ -290,12 +290,9 @@ struct ParallelBailoutRecord {
|
||||
|
||||
struct ForkJoinShared;
|
||||
|
||||
struct ForkJoinSlice
|
||||
struct ForkJoinSlice : ThreadSafeContext
|
||||
{
|
||||
public:
|
||||
// PerThreadData corresponding to the current worker thread.
|
||||
PerThreadData *perThreadData;
|
||||
|
||||
// Which slice should you process? Ranges from 0 to |numSlices|.
|
||||
const uint32_t sliceId;
|
||||
|
||||
@ -320,7 +317,7 @@ struct ForkJoinSlice
|
||||
ParallelBailoutRecord *bailoutRecord);
|
||||
|
||||
// True if this is the main thread, false if it is one of the parallel workers.
|
||||
bool isMainThread();
|
||||
bool isMainThread() const;
|
||||
|
||||
// When the code would normally trigger a GC, we don't trigger it
|
||||
// immediately but instead record that request here. This will
|
||||
|
Loading…
Reference in New Issue
Block a user