blob: 21e433ad352358a9dd92e876587120d2cc0dc614 [file] [log] [blame]
// Copyright 2017 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#pragma once
#include "android/base/system/System.h"
#include <array>
#include <atomic>
#include <cassert>
namespace android {
namespace snapshot {
//
// FastReleasePool<> - a resource pool that optimizes for fast object releasing.
//
// The primary usecase is when there's a big number of fast producers and a
// single / slow consumer, that needs to release the resource after it was
// consumed as fast as possible.
//
// Both allocate() and release() calls are blocking, and allocate() may force
// the calling thread to go into sleeping mode. release() won't ever sleep, but
// may instead spin forever if one has tried to release more resources than the
// pool is supposed to contain.
//
template <class T, int N>
class FastReleasePool {
public:
using value_type = T*;
static constexpr int capacity = N;
FastReleasePool() = default;
template <class Iter>
FastReleasePool(Iter begin, Iter end)
: mAvailable(std::distance(begin, end)) {
assert(mAvailable <= N);
for (auto out = mResources.begin(); begin != end; ++begin, ++out) {
out->store(&*begin, std::memory_order_relaxed);
}
}
T* allocate();
void release(T* t);
private:
std::atomic<int> mAvailable{0};
std::array<std::atomic<T*>, N> mResources = {};
base::System* mSystem = base::System::get();
};
template <class T, int N>
T* FastReleasePool<T, N>::allocate() {
// Spin for a while before starting to go into real sleep.
int spinLimit = 2 * mResources.size();
T* resource = nullptr;
for (;;) {
for (auto& current : mResources) {
while (!mAvailable.load(std::memory_order_relaxed)) {
if (spinLimit-- > 0) {
mSystem->yield();
} else {
mSystem->sleepMs(1);
}
}
resource = current.exchange(nullptr, std::memory_order_relaxed);
if (resource) {
mAvailable.fetch_sub(1, std::memory_order_relaxed);
return resource;
}
}
}
}
template <class T, int N>
void FastReleasePool<T, N>::release(T* resource) {
assert(mAvailable < N);
auto current = mResources.begin();
for (;;) {
T* needed = nullptr;
if (current->compare_exchange_strong(needed, resource,
std::memory_order_relaxed,
std::memory_order_relaxed)) {
mAvailable.fetch_add(1, std::memory_order_relaxed);
return;
}
if (++current == mResources.end()) {
current = mResources.begin();
}
}
}
} // namespace snapshot
} // namespace android