Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  MallocedBlockCache.h   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sw=2 et tw=80:
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef gc_MallocedBlockCache_h
#define gc_MallocedBlockCache_h

#include "ds/PointerAndUint7.h"
#include "js/AllocPolicy.h"
#include "js/Vector.h"
#include "util/Poison.h"

namespace js {
namespace gc {

// MallocedBlockCache implements a lightweight wrapper around js_malloc/js_free.
//
// Blocks are requested by ::alloc and must be returned with ::free, at which
// point the cache may decide to hold on to the block rather than hand it back
// to js_free.  Subsequent ::alloc calls may be satisfied from the cached
// blocks rather than calling js_malloc.  The mechanism is designed to be much
// cheaper than calling js_malloc/js_free directly.  One consequence is that
// there is no locking; it is essential therefore to use each cache only from
// a single thread.
//
// The intended use is for lightweight management of OOL (malloc'd) storage
// associated with WasmStructObject and WasmArrayObject.  The mechanism is
// general and potentially has other uses.  Blocks of size STEP * NUM_LISTS
// and larger are never cached, though.
//
// Request sizes are rounded up to a multiple of STEP.  There are NUM_LISTS-1
// free lists, with a "List ID" indicating the multiple of STEP stored on the
// list.  So for example, blocks of size 3 * STEP (after rounding up) are
// stored on the list with ID 3.  List ID 0 indicates blocks which are too
// large to live on any freelist.  With the default settings, this gives
// separate freelists for blocks of size 16, 32, 48, .. 496.  Blocks of size
// zero are not supported, and `lists[0]` will always be empty.
//
// Allocation of a block produces not only the block's address but also its
// List ID.  When freeing, both values must be presented, because there is
// otherwise no way for ::free to know the size of the original allocation,
// and hence which freelist it should go on.  For this reason, the ::alloc and
// ::free methods produce and take a `PointerAndUint7`, not a `void*`.
//
// Resizing of blocks is not supported.

class MallocedBlockCache {
 public:
  static const size_t STEP = 16;

  static const size_t NUM_LISTS = 32;
  // This limitation exists because allocation returns a PointerAndUint7, and
  // a List-ID value (viz, 0 .. NUM_LISTS-1) is stored in the Uint7 part.
  static_assert(NUM_LISTS <= (1 << 7));

  // list[0] must always remain empty.  List ID 0 indicates a block which
  // cannot participate in the freelist machinery because it is too large.
  //
  // list[i], for 1 <= i < NUM_LISTS, holds blocks of size i * STEP only.
  // All requests are rounded up to multiple of SIZE.
  //
  // We do not expect to be required to issue or accept blocks of size zero.
  static const size_t OVERSIZE_BLOCK_LIST_ID = 0;
  using MallocedBlockVector = Vector<void*, 0, SystemAllocPolicy>;

  MallocedBlockVector lists[NUM_LISTS];

  ~MallocedBlockCache();

  static inline size_t listIDForSize(size_t size);

  // Allocation and freeing.  Use `alloc` to allocate.  `allocSlow` is
  // `alloc`s fallback path.  Do not call it directly, since it doesn't handle
  // all cases by itself.
  [[nodiscard]] inline PointerAndUint7 alloc(size_t size);
  [[nodiscard]] MOZ_NEVER_INLINE PointerAndUint7 allocSlow(size_t size);

  inline void free(PointerAndUint7 blockAndListID);

  // Allows users to gradually hand blocks back to js_free, so as to avoid
  // space leaks in long-running scenarios.  The specified percentage of
  // blocks in each list is discarded.
  void preen(double percentOfBlocksToDiscard);

  // Return all blocks in the cache to js_free.
  void clear();

  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
};

/* static */
inline size_t MallocedBlockCache::listIDForSize(size_t size) {
  // Figure out which free list can give us a block of size `size`, after it
  // has been rounded up to a multiple of `step`.
  //
  // Example mapping for STEP = 16 and NUM_LISTS = 8, after rounding up:
  //   0 never holds any blocks (denotes "too large")
  //   1 holds blocks of size  16
  //   2 holds blocks of size  32
  //   3 holds blocks of size  48
  //   4 holds blocks of size  64
  //   5 holds blocks of size  80
  //   6 holds blocks of size  96
  //   7 holds blocks of size 112
  //
  // For a request of size n:
  // * if n == 0, fail
  // * else
  //      round n up to a multiple of STEP
  //      let i = n / STEP
  //      if i >= NUM_LISTS
  //         alloc direct from js_malloc, and return listID = 0
  //      if lists[i] is nonempty, use lists[i] and return listID = i.
  //      else
  //         let p = js_malloc(n)
  //         return p and listID = i.

  // We're never expected to handle zero-sized blocks.
  MOZ_ASSERT(size > 0);

  size = js::RoundUp(size, STEP);
  size_t i = size / STEP;
  MOZ_ASSERT(i > 0);

  if (i >= NUM_LISTS) {
    return OVERSIZE_BLOCK_LIST_ID;
  }

  return i;
}

inline PointerAndUint7 MallocedBlockCache::alloc(size_t size) {
  size_t i = listIDForSize(size);
  MOZ_ASSERT(i < NUM_LISTS);

  // Fast path: try to pull a block from the relevant list.
  if (MOZ_LIKELY(
          i != OVERSIZE_BLOCK_LIST_ID &&  // "block is small enough to cache"
          !lists[i].empty())) {           // "a cached block is available"
    // Check that i is the right list
    MOZ_ASSERT(i * STEP == js::RoundUp(size, STEP));
    void* block = lists[i].popCopy();
    return PointerAndUint7(block, i);
  }

  // Fallback path for all other cases.
  return allocSlow(size);
}

inline void MallocedBlockCache::free(PointerAndUint7 blockAndListID) {
  // This is a whole lot simpler than the ::alloc case, since we are given the
  // listId and don't have to compute it (not that we have any way to).
  void* block = blockAndListID.pointer();
  uint32_t listID = blockAndListID.uint7();
  MOZ_ASSERT(block);
  MOZ_ASSERT(listID < NUM_LISTS);
  if (MOZ_UNLIKELY(listID == OVERSIZE_BLOCK_LIST_ID)) {
    // It was too large for recycling; go straight to js_free.
    js_free(block);
    return;
  }

  // Put it back on list `listId`, first poisoning it for safety.
  memset(block, JS_NOTINUSE_TRAILER_PATTERN, listID * STEP);
  MOZ_MAKE_MEM_UNDEFINED(block, listID * STEP);
  if (MOZ_UNLIKELY(!lists[listID].append(block))) {
    // OOM'd while doing admin.  Hand it off to js_free and forget about the
    // OOM.
    js_free(block);
  }
}

}  // namespace gc
}  // namespace js

#endif  // gc_MallocedBlockCache_h

Messung V0.5
C=90 H=93 G=91

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge