TeaSpeakLibrary/src/protocol/buffers_allocator_b.cpp

179 lines
3.9 KiB
C++

#include "buffers.h"
#include <unistd.h>
using namespace std;
using namespace ts;
using namespace ts::protocol;
using namespace ts::buffer;
#pragma GCC optimize ("O3")
class spin_lock {
std::atomic_flag locked = ATOMIC_FLAG_INIT;
public:
void lock() {
uint8_t round = 0;
while (locked.test_and_set(std::memory_order_acquire)) {
//Yield when we're using this lock for a longer time, which we usually not doing
if(round++ % 8 == 0)
std::this_thread::yield();
}
}
inline bool try_lock() {
return !locked.test_and_set(std::memory_order_acquire);
}
void unlock() {
locked.clear(std::memory_order_release);
}
};
typedef spin_lock fast_lock_t;
struct BufferAllocator {
bool operator()(size_t& length, void*& result) {
__throw_logic_error("Cant reallocate a fixed buffer");
}
};
struct Freelist {
struct Buffer {
Buffer* next_buffer;
char data_ptr[0];
};
std::function<bool(void*)> deallocator;
BufferAllocator allocator{};
fast_lock_t lock{};
Buffer* head = nullptr;
Buffer* tail = nullptr;
ssize_t length = 0;
size_t buffer_size;
ssize_t max_length;
#ifdef DEBUG_FREELIST
std::atomic<size_t> extra_alloc = 0;
std::atomic<size_t> extra_free = 0;
std::atomic<size_t> total_alloc = 0;
std::atomic<size_t> total_free = 0;
#endif
pipes::buffer next_buffer() {
#ifdef DEBUG_FREELIST
this->total_alloc++;
#endif
Buffer* buffer = nullptr;
{
lock_guard lock(this->lock);
if(this->head) {
buffer = this->head;
if(buffer == this->tail) {
this->tail = nullptr;
this->head = nullptr;
assert(this->length == 0);
} else {
this->head = buffer->next_buffer;
assert(this->length > 0);
}
this->length--;
}
}
if(!buffer) {
#ifdef DEBUG_FREELIST
this->extra_alloc++;
#endif
buffer = (Buffer*) malloc(sizeof(Buffer) + this->buffer_size);
}
return pipes::buffer{(void*) buffer, this->buffer_size + sizeof(Buffer), false, allocator, deallocator}.range(sizeof(Buffer));
}
bool release_buffer(void* ptr) {
#ifdef DEBUG_FREELIST
this->total_free++;
#endif
auto buffer = (Buffer*) ptr;
if(this->max_length > 0 && this->length > this->max_length) {
/* dont push anymore stuff into the freelist! */
#ifdef DEBUG_FREELIST
this->extra_free++;
#endif
free(ptr);
return true;
}
{
lock_guard lock(this->lock);
if(this->tail) {
this->tail->next_buffer = buffer;
} else {
this->head = buffer;
}
this->tail = buffer;
buffer->next_buffer = nullptr;
this->length++;
}
return true;
}
cleaninfo cleanup() {
size_t buffers_deallocated = 0;
{
lock_guard lock(this->lock);
while(this->head) {
auto buffer = this->head;
this->head = buffer->next_buffer;
free(buffer);
buffers_deallocated++;
}
this->tail = nullptr;
this->head = nullptr;
this->length = 0;
}
return {buffers_deallocated * 8, buffers_deallocated * this->buffer_size};
}
explicit Freelist(size_t size) {
this->buffer_size = size;
this->max_length = 1024 * 1024 * 1024; /* 1GB */
this->deallocator = bind(&Freelist::release_buffer, this, placeholders::_1);
}
};
Freelist freelists[size::max - size::min] = {
Freelist(size::byte_length(size::Bytes_512)),
Freelist(size::byte_length(size::Bytes_1024)),
Freelist(size::byte_length(size::Bytes_1536))
};
buffer_t buffer::allocate_buffer(size::value size) {
assert((size - size::min) > 0 && (size - size::min) < (size::max - size::min));
return freelists[size - size::min].next_buffer();
}
cleaninfo& operator+=(cleaninfo& self, const cleaninfo& other) {
self.bytes_freed_internal += other.bytes_freed_internal;
self.bytes_freed_buffer += other.bytes_freed_buffer;
return self;
}
cleaninfo buffer::cleanup_buffers(cleanmode::value mode) {
cleaninfo info{0, 0};
info += freelists[0].cleanup();
info += freelists[1].cleanup();
info += freelists[2].cleanup();
return info;
}
//cleanup_buffers
//$4 = {bytes_freed_internal = 8389144, bytes_freed_buffer = 536948736}