blob: bbaed157671fca5dff72e66948c8eac5fe62755c [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IPCZ_SRC_IPCZ_BLOCK_ALLOCATOR_POOL_H_
#define IPCZ_SRC_IPCZ_BLOCK_ALLOCATOR_POOL_H_
#include <atomic>
#include <cstdint>
#include <list>
#include "ipcz/block_allocator.h"
#include "ipcz/buffer_id.h"
#include "ipcz/fragment.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/abseil-cpp/absl/synchronization/mutex.h"
#include "third_party/abseil-cpp/absl/types/span.h"
namespace ipcz {
// Manages a pool of BlockAllocators for a specific block size. This maintains
// the allocators in a list along with a cached reference to the allocator most
// recently used for a successful allocation.
//
// The pool cycles through its allocators as needed to satisfy new requests,
// failing only once a complete pass over all allocators fails to yield a
// successful allocation.
//
// BlockAllocatorPool is is thread-safe.
class BlockAllocatorPool {
public:
BlockAllocatorPool();
~BlockAllocatorPool();
// Returns the total allocable capacity (in bytes) within this pool. Note that
// this counts all blocks, including ones which are currently allocated.
size_t GetCapacity();
// Registers a new allocator. `buffer_memory` is the entire mapped region
// associated with `buffer_id`, not just the subspan managed by `allocator`.
//
// Note that each BlockAllocatorPool allows only one allocator per unique
// `buffer_id`. This returns false if an allocator was already registered for
// `buffer_id`, and true otherwise to indicate success.
bool Add(BufferId buffer_id,
absl::Span<uint8_t> buffer_memory,
const BlockAllocator& allocator);
// Allocates a block from the pool and returns a reference to it as a
// Fragment. Returns a null Fragment if a block could not be allocated.
Fragment Allocate();
// Frees a fragment previously allocated from one of this pool's allocators.
// Returns true if and only if `fragment` was a valid fragment to free.
bool Free(const Fragment& fragment);
private:
struct Entry {
Entry(BufferId buffer_id,
absl::Span<uint8_t> buffer_memory,
const BlockAllocator& allocator);
~Entry();
const BufferId buffer_id;
const absl::Span<uint8_t> buffer_memory;
const BlockAllocator allocator;
// Cached pointer to the next entry.
Entry* next = nullptr;
};
absl::Mutex mutex_;
// List of all allocators added to this pool. Once added, elements are never
// removed from this list. Note that std::list is chosen so that Entry
// references are stable over time.
std::list<Entry> entries_ ABSL_GUARDED_BY(mutex_);
// Maps BufferId to a specific Entry in the pool, so individual Fragments can
// be freed efficiently by the pool.
absl::flat_hash_map<BufferId, Entry*> entry_map_ ABSL_GUARDED_BY(mutex_);
// The total capacity of all BlockAllocators added to the pool so far.
size_t capacity_ ABSL_GUARDED_BY(mutex_) = 0;
// An atomic cache of the most recently used Entry, for fast unsynchronized
// access in the common case.
std::atomic<Entry*> active_entry_{nullptr};
};
} // namespace ipcz
#endif // IPCZ_SRC_IPCZ_BLOCK_ALLOCATOR_POOL_H_