- static ElementWrapper& get(int id) {
- size_t prevSize = threadEntry_.elementsCapacity;
- if (prevSize <= id) {
- size_t newSize = static_cast<size_t>((id + 5) * 1.7);
- auto & meta = instance();
- ElementWrapper* ptr = NULL;
- // Rely on jemalloc to zero the memory if possible -- maybe it knows
- // it's already zeroed and saves us some work.
- if (!usingJEMalloc() ||
- prevSize < jemallocMinInPlaceExpandable ||
- (rallocm(
- static_cast<void**>(static_cast<void*>(&threadEntry_.elements)),
- NULL, newSize * sizeof(ElementWrapper), 0,
- ALLOCM_NO_MOVE | ALLOCM_ZERO) != ALLOCM_SUCCESS)) {
- // Sigh, must realloc, but we can't call realloc here, as elements is
- // still linked in meta, so another thread might access invalid memory
- // after realloc succeeds. We'll copy by hand and update threadEntry_
- // under the lock.
- //
- // Note that we're using calloc instead of malloc in order to zero
- // the entire region. rallocm (ALLOCM_ZERO) will only zero newly
- // allocated memory, so if a previous allocation allocated more than
- // we requested, it's our responsibility to guarantee that the tail
- // is zeroed. calloc() is simpler than malloc() followed by memset(),
- // and potentially faster when dealing with a lot of memory, as
- // it can get already-zeroed pages from the kernel.
- if ((ptr = static_cast<ElementWrapper*>(
- calloc(newSize, sizeof(ElementWrapper)))) != NULL) {
- memcpy(ptr, threadEntry_.elements,
- sizeof(ElementWrapper) * prevSize);
- } else {
- throw std::bad_alloc();
- }
+ /**
+ * Reserve enough space in the ThreadEntry::elements for the item
+ * @id to fit in.
+ */
+ static void reserve(uint32_t id) {
+ auto& meta = instance();
+ ThreadEntry* threadEntry = getThreadEntry();
+ size_t prevCapacity = threadEntry->elementsCapacity;
+ // Growth factor < 2, see folly/docs/FBVector.md; + 5 to prevent
+ // very slow start.
+ size_t newCapacity = static_cast<size_t>((id + 5) * 1.7);
+ assert(newCapacity > prevCapacity);
+ ElementWrapper* reallocated = nullptr;
+
+ // Need to grow. Note that we can't call realloc, as elements is
+ // still linked in meta, so another thread might access invalid memory
+ // after realloc succeeds. We'll copy by hand and update our ThreadEntry
+ // under the lock.
+ if (usingJEMalloc()) {
+ bool success = false;
+ size_t newByteSize = nallocx(newCapacity * sizeof(ElementWrapper), 0);
+
+ // Try to grow in place.
+ //
+ // Note that xallocx(MALLOCX_ZERO) will only zero newly allocated memory,
+ // even if a previous allocation allocated more than we requested.
+ // This is fine; we always use MALLOCX_ZERO with jemalloc and we
+ // always expand our allocation to the real size.
+ if (prevCapacity * sizeof(ElementWrapper) >=
+ jemallocMinInPlaceExpandable) {
+ success = (xallocx(threadEntry->elements, newByteSize, 0, MALLOCX_ZERO)
+ == newByteSize);