/** * Copyright 2013-2021 Software Radio Systems Limited * * This file is part of srsLTE. * * srsLTE is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * srsLTE 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 Affero General Public License for more details. * * A copy of the GNU Affero General Public License can be found in * the LICENSE file in the top-level directory of this distribution * and at http://www.gnu.org/licenses/. * */ #ifndef SRSLTE_BUFFER_POOL_H #define SRSLTE_BUFFER_POOL_H #include "byte_buffer.h" #include #include #include #include #include #include /******************************************************************************* INCLUDES *******************************************************************************/ #include "srslte/common/common.h" #include "srslte/common/log.h" #include "srslte/srslog/srslog.h" namespace srslte { /****************************************************************************** * Buffer pool * * Preallocates a large number of buffer_t and provides allocate and * deallocate functions. Provides quick object creation and deletion as well * as object reuse. * Singleton class of byte_buffer_t (but other pools of different type can be created) *****************************************************************************/ template class buffer_pool { public: // non-static methods buffer_pool(int capacity_ = -1) { uint32_t nof_buffers = POOL_SIZE; if (capacity_ > 0) { nof_buffers = (uint32_t)capacity_; } pthread_mutex_init(&mutex, nullptr); pthread_cond_init(&cv_not_empty, nullptr); for (uint32_t i = 0; i < nof_buffers; i++) { buffer_t* b = new (std::nothrow) buffer_t; if (!b) { perror("Error allocating memory. Exiting...\n"); exit(-1); } available.push(b); } capacity = nof_buffers; } ~buffer_pool() { // this destructor assumes all buffers have been properly deallocated while (available.size()) { delete available.top(); available.pop(); } for (uint32_t i = 0; i < used.size(); i++) { delete used[i]; } pthread_cond_destroy(&cv_not_empty); pthread_mutex_destroy(&mutex); } void print_all_buffers() { printf("%d buffers in queue\n", (int)used.size()); #ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED std::map buffer_cnt; for (uint32_t i = 0; i < used.size(); i++) { buffer_cnt[strlen(used[i]->debug_name) ? used[i]->debug_name : "Undefined"]++; } std::map::iterator it; for (it = buffer_cnt.begin(); it != buffer_cnt.end(); it++) { printf(" - %dx %s\n", it->second, it->first.c_str()); } #endif } uint32_t nof_available_pdus() { return available.size(); } bool is_almost_empty() { return available.size() < capacity / 20; } buffer_t* allocate(const char* debug_name = nullptr, bool blocking = false) { pthread_mutex_lock(&mutex); buffer_t* b = nullptr; if (available.size() > 0) { b = available.top(); used.push_back(b); available.pop(); if (is_almost_empty()) { printf("Warning buffer pool capacity is %f %%\n", (float)100 * available.size() / capacity); } #ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED if (debug_name) { strncpy(b->debug_name, debug_name, SRSLTE_BUFFER_POOL_LOG_NAME_LEN); b->debug_name[SRSLTE_BUFFER_POOL_LOG_NAME_LEN - 1] = 0; } #endif } else if (blocking) { // blocking allocation while (available.size() == 0) { pthread_cond_wait(&cv_not_empty, &mutex); } // retrieve the new buffer b = available.top(); used.push_back(b); available.pop(); // do not print any warning } else { printf("Error - buffer pool is empty\n"); #ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED print_all_buffers(); #endif } pthread_mutex_unlock(&mutex); return b; } bool deallocate(buffer_t* b) { bool ret = false; pthread_mutex_lock(&mutex); typename std::vector::iterator elem = std::find(used.begin(), used.end(), b); if (elem != used.end()) { used.erase(elem); available.push(b); ret = true; } pthread_cond_signal(&cv_not_empty); pthread_mutex_unlock(&mutex); return ret; } private: static const int POOL_SIZE = 4096; std::stack available; std::vector used; pthread_mutex_t mutex; pthread_cond_t cv_not_empty; uint32_t capacity; }; class byte_buffer_pool { using mem_chunk = typename std::aligned_storage::type; public: // Singleton static methods static byte_buffer_pool* get_instance(int capacity = -1) { static std::unique_ptr instance(new byte_buffer_pool(capacity)); return instance.get(); } byte_buffer_pool(int capacity = -1) : pool(capacity) {} byte_buffer_pool(const byte_buffer_pool& other) = delete; byte_buffer_pool(byte_buffer_pool&& other) = delete; byte_buffer_pool& operator=(const byte_buffer_pool& other) = delete; byte_buffer_pool& operator=(byte_buffer_pool&& other) = delete; void* allocate(const char* debug_name = nullptr, bool blocking = false) { return pool.allocate(debug_name, blocking); } void enable_logger(bool enabled) { print_to_log = enabled; } void deallocate(void* b) { if (!b) { return; } if (!pool.deallocate(static_cast(b))) { #ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED print_error("Error deallocating PDU: Addr=0x%p, name=%s not found in pool", (void*)b, b->debug_name); #else print_error("Error deallocating PDU: Addr=0x%p", (void*)b); #endif } } void print_all_buffers() { pool.print_all_buffers(); } private: /// Formats and prints the input string and arguments into the configured output stream. template void print_error(const char* str, Args&&... args) { if (print_to_log) { srslog::fetch_basic_logger("POOL", false).error(str, std::forward(args)...); } else { fmt::printf(std::string(str) + "\n", std::forward(args)...); } } private: bool print_to_log = false; buffer_pool pool; }; inline unique_byte_buffer_t make_byte_buffer() noexcept { return std::unique_ptr(new (std::nothrow) byte_buffer_t()); } inline unique_byte_buffer_t make_byte_buffer(uint32_t size, uint8_t value) noexcept { return std::unique_ptr(new (std::nothrow) byte_buffer_t(size, value)); } inline unique_byte_buffer_t make_byte_buffer(const char* debug_ctxt) noexcept { std::unique_ptr buffer(new (std::nothrow) byte_buffer_t()); if (buffer == nullptr) { srslog::fetch_basic_logger("POOL").error("Failed to allocate byte buffer in %s", debug_ctxt); } return buffer; } } // namespace srslte #endif // SRSLTE_BUFFER_POOL_H