js/src/jsgcinlines.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #ifndef jsgcinlines_h
     8 #define jsgcinlines_h
    10 #include "jsgc.h"
    12 #include "gc/Zone.h"
    14 namespace js {
    16 class Shape;
    18 /*
    19  * This auto class should be used around any code that might cause a mark bit to
    20  * be set on an object in a dead zone. See AutoMaybeTouchDeadZones
    21  * for more details.
    22  */
    23 struct AutoMarkInDeadZone
    24 {
    25     AutoMarkInDeadZone(JS::Zone *zone)
    26       : zone(zone),
    27         scheduled(zone->scheduledForDestruction)
    28     {
    29         JSRuntime *rt = zone->runtimeFromMainThread();
    30         if (rt->gcManipulatingDeadZones && zone->scheduledForDestruction) {
    31             rt->gcObjectsMarkedInDeadZones++;
    32             zone->scheduledForDestruction = false;
    33         }
    34     }
    36     ~AutoMarkInDeadZone() {
    37         zone->scheduledForDestruction = scheduled;
    38     }
    40   private:
    41     JS::Zone *zone;
    42     bool scheduled;
    43 };
    45 inline Allocator *const
    46 ThreadSafeContext::allocator()
    47 {
    48     JS_ASSERT_IF(isJSContext(), &asJSContext()->zone()->allocator == allocator_);
    49     return allocator_;
    50 }
    52 template <typename T>
    53 inline bool
    54 ThreadSafeContext::isThreadLocal(T thing) const
    55 {
    56     if (!isForkJoinContext())
    57         return true;
    59     if (!IsInsideNursery(runtime_, thing) &&
    60         allocator_->arenas.containsArena(runtime_, thing->arenaHeader()))
    61     {
    62         // GC should be suppressed in preparation for mutating thread local
    63         // objects, as we don't want to trip any barriers.
    64         JS_ASSERT(!thing->zoneFromAnyThread()->needsBarrier());
    65         JS_ASSERT(!thing->runtimeFromAnyThread()->needsBarrier());
    67         return true;
    68     }
    70     return false;
    71 }
    73 namespace gc {
    75 static inline AllocKind
    76 GetGCObjectKind(const Class *clasp)
    77 {
    78     if (clasp == FunctionClassPtr)
    79         return JSFunction::FinalizeKind;
    80     uint32_t nslots = JSCLASS_RESERVED_SLOTS(clasp);
    81     if (clasp->flags & JSCLASS_HAS_PRIVATE)
    82         nslots++;
    83     return GetGCObjectKind(nslots);
    84 }
    86 #ifdef JSGC_GENERATIONAL
    87 inline bool
    88 ShouldNurseryAllocate(const Nursery &nursery, AllocKind kind, InitialHeap heap)
    89 {
    90     return nursery.isEnabled() && IsNurseryAllocable(kind) && heap != TenuredHeap;
    91 }
    92 #endif
    94 inline JSGCTraceKind
    95 GetGCThingTraceKind(const void *thing)
    96 {
    97     JS_ASSERT(thing);
    98     const Cell *cell = static_cast<const Cell *>(thing);
    99 #ifdef JSGC_GENERATIONAL
   100     if (IsInsideNursery(cell->runtimeFromAnyThread(), cell))
   101         return JSTRACE_OBJECT;
   102 #endif
   103     return MapAllocToTraceKind(cell->tenuredGetAllocKind());
   104 }
   106 static inline void
   107 GCPoke(JSRuntime *rt)
   108 {
   109     rt->gcPoke = true;
   111 #ifdef JS_GC_ZEAL
   112     /* Schedule a GC to happen "soon" after a GC poke. */
   113     if (rt->gcZeal() == js::gc::ZealPokeValue)
   114         rt->gcNextScheduled = 1;
   115 #endif
   116 }
   118 class ArenaIter
   119 {
   120     ArenaHeader *aheader;
   121     ArenaHeader *remainingHeader;
   123   public:
   124     ArenaIter() {
   125         init();
   126     }
   128     ArenaIter(JS::Zone *zone, AllocKind kind) {
   129         init(zone, kind);
   130     }
   132     void init() {
   133         aheader = nullptr;
   134         remainingHeader = nullptr;
   135     }
   137     void init(ArenaHeader *aheaderArg) {
   138         aheader = aheaderArg;
   139         remainingHeader = nullptr;
   140     }
   142     void init(JS::Zone *zone, AllocKind kind) {
   143         aheader = zone->allocator.arenas.getFirstArena(kind);
   144         remainingHeader = zone->allocator.arenas.getFirstArenaToSweep(kind);
   145         if (!aheader) {
   146             aheader = remainingHeader;
   147             remainingHeader = nullptr;
   148         }
   149     }
   151     bool done() {
   152         return !aheader;
   153     }
   155     ArenaHeader *get() {
   156         return aheader;
   157     }
   159     void next() {
   160         aheader = aheader->next;
   161         if (!aheader) {
   162             aheader = remainingHeader;
   163             remainingHeader = nullptr;
   164         }
   165     }
   166 };
   168 class CellIterImpl
   169 {
   170     size_t firstThingOffset;
   171     size_t thingSize;
   172     ArenaIter aiter;
   173     FreeSpan firstSpan;
   174     const FreeSpan *span;
   175     uintptr_t thing;
   176     Cell *cell;
   178   protected:
   179     CellIterImpl() {
   180     }
   182     void initSpan(JS::Zone *zone, AllocKind kind) {
   183         JS_ASSERT(zone->allocator.arenas.isSynchronizedFreeList(kind));
   184         firstThingOffset = Arena::firstThingOffset(kind);
   185         thingSize = Arena::thingSize(kind);
   186         firstSpan.initAsEmpty();
   187         span = &firstSpan;
   188         thing = span->first;
   189     }
   191     void init(ArenaHeader *singleAheader) {
   192         initSpan(singleAheader->zone, singleAheader->getAllocKind());
   193         aiter.init(singleAheader);
   194         next();
   195         aiter.init();
   196     }
   198     void init(JS::Zone *zone, AllocKind kind) {
   199         initSpan(zone, kind);
   200         aiter.init(zone, kind);
   201         next();
   202     }
   204   public:
   205     bool done() const {
   206         return !cell;
   207     }
   209     template<typename T> T *get() const {
   210         JS_ASSERT(!done());
   211         return static_cast<T *>(cell);
   212     }
   214     Cell *getCell() const {
   215         JS_ASSERT(!done());
   216         return cell;
   217     }
   219     void next() {
   220         for (;;) {
   221             if (thing != span->first)
   222                 break;
   223             if (MOZ_LIKELY(span->hasNext())) {
   224                 thing = span->last + thingSize;
   225                 span = span->nextSpan();
   226                 break;
   227             }
   228             if (aiter.done()) {
   229                 cell = nullptr;
   230                 return;
   231             }
   232             ArenaHeader *aheader = aiter.get();
   233             firstSpan = aheader->getFirstFreeSpan();
   234             span = &firstSpan;
   235             thing = aheader->arenaAddress() | firstThingOffset;
   236             aiter.next();
   237         }
   238         cell = reinterpret_cast<Cell *>(thing);
   239         thing += thingSize;
   240     }
   241 };
   243 class CellIterUnderGC : public CellIterImpl
   244 {
   245   public:
   246     CellIterUnderGC(JS::Zone *zone, AllocKind kind) {
   247 #ifdef JSGC_GENERATIONAL
   248         JS_ASSERT(zone->runtimeFromAnyThread()->gcNursery.isEmpty());
   249 #endif
   250         JS_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
   251         init(zone, kind);
   252     }
   254     CellIterUnderGC(ArenaHeader *aheader) {
   255         JS_ASSERT(aheader->zone->runtimeFromAnyThread()->isHeapBusy());
   256         init(aheader);
   257     }
   258 };
   260 class CellIter : public CellIterImpl
   261 {
   262     ArenaLists *lists;
   263     AllocKind kind;
   264 #ifdef DEBUG
   265     size_t *counter;
   266 #endif
   267   public:
   268     CellIter(JS::Zone *zone, AllocKind kind)
   269       : lists(&zone->allocator.arenas),
   270         kind(kind)
   271     {
   272         /*
   273          * We have a single-threaded runtime, so there's no need to protect
   274          * against other threads iterating or allocating. However, we do have
   275          * background finalization; we have to wait for this to finish if it's
   276          * currently active.
   277          */
   278         if (IsBackgroundFinalized(kind) &&
   279             zone->allocator.arenas.needBackgroundFinalizeWait(kind))
   280         {
   281             gc::FinishBackgroundFinalize(zone->runtimeFromMainThread());
   282         }
   284 #ifdef JSGC_GENERATIONAL
   285         /* Evict the nursery before iterating so we can see all things. */
   286         JSRuntime *rt = zone->runtimeFromMainThread();
   287         if (!rt->gcNursery.isEmpty())
   288             MinorGC(rt, JS::gcreason::EVICT_NURSERY);
   289 #endif
   291         if (lists->isSynchronizedFreeList(kind)) {
   292             lists = nullptr;
   293         } else {
   294             JS_ASSERT(!zone->runtimeFromMainThread()->isHeapBusy());
   295             lists->copyFreeListToArena(kind);
   296         }
   298 #ifdef DEBUG
   299         /* Assert that no GCs can occur while a CellIter is live. */
   300         counter = &zone->runtimeFromAnyThread()->noGCOrAllocationCheck;
   301         ++*counter;
   302 #endif
   304         init(zone, kind);
   305     }
   307     ~CellIter() {
   308 #ifdef DEBUG
   309         JS_ASSERT(*counter > 0);
   310         --*counter;
   311 #endif
   312         if (lists)
   313             lists->clearFreeListInArena(kind);
   314     }
   315 };
   317 class GCZonesIter
   318 {
   319   private:
   320     ZonesIter zone;
   322   public:
   323     GCZonesIter(JSRuntime *rt) : zone(rt, WithAtoms) {
   324         if (!zone->isCollecting())
   325             next();
   326     }
   328     bool done() const { return zone.done(); }
   330     void next() {
   331         JS_ASSERT(!done());
   332         do {
   333             zone.next();
   334         } while (!zone.done() && !zone->isCollecting());
   335     }
   337     JS::Zone *get() const {
   338         JS_ASSERT(!done());
   339         return zone;
   340     }
   342     operator JS::Zone *() const { return get(); }
   343     JS::Zone *operator->() const { return get(); }
   344 };
   346 typedef CompartmentsIterT<GCZonesIter> GCCompartmentsIter;
   348 /* Iterates over all zones in the current zone group. */
   349 class GCZoneGroupIter {
   350   private:
   351     JS::Zone *current;
   353   public:
   354     GCZoneGroupIter(JSRuntime *rt) {
   355         JS_ASSERT(rt->isHeapBusy());
   356         current = rt->gcCurrentZoneGroup;
   357     }
   359     bool done() const { return !current; }
   361     void next() {
   362         JS_ASSERT(!done());
   363         current = current->nextNodeInGroup();
   364     }
   366     JS::Zone *get() const {
   367         JS_ASSERT(!done());
   368         return current;
   369     }
   371     operator JS::Zone *() const { return get(); }
   372     JS::Zone *operator->() const { return get(); }
   373 };
   375 typedef CompartmentsIterT<GCZoneGroupIter> GCCompartmentGroupIter;
   377 #ifdef JSGC_GENERATIONAL
   378 /*
   379  * Attempt to allocate a new GC thing out of the nursery. If there is not enough
   380  * room in the nursery or there is an OOM, this method will return nullptr.
   381  */
   382 template <AllowGC allowGC>
   383 inline JSObject *
   384 TryNewNurseryObject(ThreadSafeContext *cxArg, size_t thingSize, size_t nDynamicSlots)
   385 {
   386     JSContext *cx = cxArg->asJSContext();
   388     JS_ASSERT(!IsAtomsCompartment(cx->compartment()));
   389     JSRuntime *rt = cx->runtime();
   390     Nursery &nursery = rt->gcNursery;
   391     JSObject *obj = nursery.allocateObject(cx, thingSize, nDynamicSlots);
   392     if (obj)
   393         return obj;
   394     if (allowGC && !rt->mainThread.suppressGC) {
   395         MinorGC(cx, JS::gcreason::OUT_OF_NURSERY);
   397         /* Exceeding gcMaxBytes while tenuring can disable the Nursery. */
   398         if (nursery.isEnabled()) {
   399             JSObject *obj = nursery.allocateObject(cx, thingSize, nDynamicSlots);
   400             JS_ASSERT(obj);
   401             return obj;
   402         }
   403     }
   404     return nullptr;
   405 }
   406 #endif /* JSGC_GENERATIONAL */
   408 static inline bool
   409 PossiblyFail()
   410 {
   411     JS_OOM_POSSIBLY_FAIL();
   412     return true;
   413 }
   415 template <AllowGC allowGC>
   416 static inline bool
   417 CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
   418 {
   419     if (!cx->isJSContext())
   420         return true;
   422     JSContext *ncx = cx->asJSContext();
   423     JSRuntime *rt = ncx->runtime();
   424 #if defined(JS_GC_ZEAL) || defined(DEBUG)
   425     JS_ASSERT_IF(rt->isAtomsCompartment(ncx->compartment()),
   426                  kind == FINALIZE_STRING ||
   427                  kind == FINALIZE_FAT_INLINE_STRING ||
   428                  kind == FINALIZE_JITCODE);
   429     JS_ASSERT(!rt->isHeapBusy());
   430     JS_ASSERT(!rt->noGCOrAllocationCheck);
   431 #endif
   433     // For testing out of memory conditions
   434     if (!PossiblyFail()) {
   435         js_ReportOutOfMemory(cx);
   436         return false;
   437     }
   439     if (allowGC) {
   440 #ifdef JS_GC_ZEAL
   441         if (rt->needZealousGC())
   442             js::gc::RunDebugGC(ncx);
   443 #endif
   445         if (rt->interrupt) {
   446             // Invoking the interrupt callback can fail and we can't usefully
   447             // handle that here. Just check in case we need to collect instead.
   448             js::gc::GCIfNeeded(ncx);
   449         }
   450     }
   452     return true;
   453 }
   455 template <typename T>
   456 static inline void
   457 CheckIncrementalZoneState(ThreadSafeContext *cx, T *t)
   458 {
   459 #ifdef DEBUG
   460     if (!cx->isJSContext())
   461         return;
   463     Zone *zone = cx->asJSContext()->zone();
   464     JS_ASSERT_IF(t && zone->wasGCStarted() && (zone->isGCMarking() || zone->isGCSweeping()),
   465                  t->arenaHeader()->allocatedDuringIncremental);
   466 #endif
   467 }
   469 /*
   470  * Allocate a new GC thing. After a successful allocation the caller must
   471  * fully initialize the thing before calling any function that can potentially
   472  * trigger GC. This will ensure that GC tracing never sees junk values stored
   473  * in the partially initialized thing.
   474  */
   476 template <AllowGC allowGC>
   477 inline JSObject *
   478 AllocateObject(ThreadSafeContext *cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap)
   479 {
   480     size_t thingSize = Arena::thingSize(kind);
   482     JS_ASSERT(thingSize == Arena::thingSize(kind));
   483     if (!CheckAllocatorState<allowGC>(cx, kind))
   484         return nullptr;
   486 #ifdef JSGC_GENERATIONAL
   487     if (cx->hasNursery() && ShouldNurseryAllocate(cx->nursery(), kind, heap)) {
   488         JSObject *obj = TryNewNurseryObject<allowGC>(cx, thingSize, nDynamicSlots);
   489         if (obj)
   490             return obj;
   491     }
   492 #endif
   494     HeapSlot *slots = nullptr;
   495     if (nDynamicSlots) {
   496         slots = cx->pod_malloc<HeapSlot>(nDynamicSlots);
   497         if (MOZ_UNLIKELY(!slots))
   498             return nullptr;
   499         js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
   500     }
   502     JSObject *obj = static_cast<JSObject *>(cx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
   503     if (!obj)
   504         obj = static_cast<JSObject *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
   506     if (obj)
   507         obj->setInitialSlots(slots);
   508     else
   509         js_free(slots);
   511     CheckIncrementalZoneState(cx, obj);
   512     return obj;
   513 }
   515 template <typename T, AllowGC allowGC>
   516 inline T *
   517 AllocateNonObject(ThreadSafeContext *cx)
   518 {
   519     AllocKind kind = MapTypeToFinalizeKind<T>::kind;
   520     size_t thingSize = sizeof(T);
   522     JS_ASSERT(thingSize == Arena::thingSize(kind));
   523     if (!CheckAllocatorState<allowGC>(cx, kind))
   524         return nullptr;
   526     T *t = static_cast<T *>(cx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
   527     if (!t)
   528         t = static_cast<T *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
   530     CheckIncrementalZoneState(cx, t);
   531     return t;
   532 }
   534 /*
   535  * When allocating for initialization from a cached object copy, we will
   536  * potentially destroy the cache entry we want to copy if we allow GC. On the
   537  * other hand, since these allocations are extremely common, we don't want to
   538  * delay GC from these allocation sites. Instead we allow the GC, but still
   539  * fail the allocation, forcing the non-cached path.
   540  */
   541 template <AllowGC allowGC>
   542 inline JSObject *
   543 AllocateObjectForCacheHit(JSContext *cx, AllocKind kind, InitialHeap heap)
   544 {
   545 #ifdef JSGC_GENERATIONAL
   546     if (ShouldNurseryAllocate(cx->nursery(), kind, heap)) {
   547         size_t thingSize = Arena::thingSize(kind);
   549         JS_ASSERT(thingSize == Arena::thingSize(kind));
   550         if (!CheckAllocatorState<NoGC>(cx, kind))
   551             return nullptr;
   553         JSObject *obj = TryNewNurseryObject<NoGC>(cx, thingSize, 0);
   554         if (!obj && allowGC) {
   555             MinorGC(cx, JS::gcreason::OUT_OF_NURSERY);
   556             return nullptr;
   557         }
   558         return obj;
   559     }
   560 #endif
   562     JSObject *obj = AllocateObject<NoGC>(cx, kind, 0, heap);
   563     if (!obj && allowGC) {
   564         MaybeGC(cx);
   565         return nullptr;
   566     }
   568     return obj;
   569 }
   571 } /* namespace gc */
   573 template <js::AllowGC allowGC>
   574 inline JSObject *
   575 NewGCObject(js::ThreadSafeContext *cx, js::gc::AllocKind kind, size_t nDynamicSlots, js::gc::InitialHeap heap)
   576 {
   577     JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
   578     return js::gc::AllocateObject<allowGC>(cx, kind, nDynamicSlots, heap);
   579 }
   581 template <js::AllowGC allowGC>
   582 inline jit::JitCode *
   583 NewJitCode(js::ThreadSafeContext *cx)
   584 {
   585     return gc::AllocateNonObject<jit::JitCode, allowGC>(cx);
   586 }
   588 inline
   589 types::TypeObject *
   590 NewTypeObject(js::ThreadSafeContext *cx)
   591 {
   592     return gc::AllocateNonObject<types::TypeObject, js::CanGC>(cx);
   593 }
   595 } /* namespace js */
   597 template <js::AllowGC allowGC>
   598 inline JSString *
   599 js_NewGCString(js::ThreadSafeContext *cx)
   600 {
   601     return js::gc::AllocateNonObject<JSString, allowGC>(cx);
   602 }
   604 template <js::AllowGC allowGC>
   605 inline JSFatInlineString *
   606 js_NewGCFatInlineString(js::ThreadSafeContext *cx)
   607 {
   608     return js::gc::AllocateNonObject<JSFatInlineString, allowGC>(cx);
   609 }
   611 inline JSExternalString *
   612 js_NewGCExternalString(js::ThreadSafeContext *cx)
   613 {
   614     return js::gc::AllocateNonObject<JSExternalString, js::CanGC>(cx);
   615 }
   617 inline JSScript *
   618 js_NewGCScript(js::ThreadSafeContext *cx)
   619 {
   620     return js::gc::AllocateNonObject<JSScript, js::CanGC>(cx);
   621 }
   623 inline js::LazyScript *
   624 js_NewGCLazyScript(js::ThreadSafeContext *cx)
   625 {
   626     return js::gc::AllocateNonObject<js::LazyScript, js::CanGC>(cx);
   627 }
   629 inline js::Shape *
   630 js_NewGCShape(js::ThreadSafeContext *cx)
   631 {
   632     return js::gc::AllocateNonObject<js::Shape, js::CanGC>(cx);
   633 }
   635 template <js::AllowGC allowGC>
   636 inline js::BaseShape *
   637 js_NewGCBaseShape(js::ThreadSafeContext *cx)
   638 {
   639     return js::gc::AllocateNonObject<js::BaseShape, allowGC>(cx);
   640 }
   642 #endif /* jsgcinlines_h */

mercurial