06a3da7c63
Change-Id: I380d867fa5fb04e68b5b332e9abf33fbeb1e9418
209 строки
6.9 KiB
C++
209 строки
6.9 KiB
C++
/* Copyright (c) 2018-2022 Advanced Micro Devices, Inc.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE. */
|
|
|
|
#ifndef MEMORY_POOL_H_
|
|
#define MEMORY_POOL_H_
|
|
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <atomic>
|
|
#include <mutex>
|
|
|
|
#include "util/exception.h"
|
|
|
|
#define PTHREAD_CALL(call) \
|
|
do { \
|
|
int err = call; \
|
|
if (err != 0) { \
|
|
errno = err; \
|
|
perror(#call); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
|
|
namespace roctracer {
|
|
|
|
class MemoryPool {
|
|
public:
|
|
typedef std::mutex mutex_t;
|
|
|
|
static void allocator_default(char** ptr, size_t size, void* arg) {
|
|
(void)arg;
|
|
if (*ptr == NULL) {
|
|
*ptr = reinterpret_cast<char*>(malloc(size));
|
|
} else if (size != 0) {
|
|
*ptr = reinterpret_cast<char*>(realloc(*ptr, size));
|
|
} else {
|
|
free(*ptr);
|
|
*ptr = NULL;
|
|
}
|
|
}
|
|
|
|
MemoryPool(const roctracer_properties_t& properties) {
|
|
// Assigning pool allocator
|
|
alloc_fun_ = allocator_default;
|
|
alloc_arg_ = NULL;
|
|
if (properties.alloc_fun != NULL) {
|
|
alloc_fun_ = properties.alloc_fun;
|
|
alloc_arg_ = properties.alloc_arg;
|
|
}
|
|
|
|
// Pool definition
|
|
buffer_size_ = properties.buffer_size;
|
|
const size_t pool_size = 2 * buffer_size_;
|
|
pool_begin_ = NULL;
|
|
alloc_fun_(&pool_begin_, pool_size, alloc_arg_);
|
|
if (pool_begin_ == NULL) EXC_ABORT(ROCTRACER_STATUS_ERROR, "pool allocator failed");
|
|
pool_end_ = pool_begin_ + pool_size;
|
|
buffer_begin_ = pool_begin_;
|
|
buffer_end_ = buffer_begin_ + buffer_size_;
|
|
write_ptr_ = buffer_begin_;
|
|
|
|
// Consuming read thread
|
|
read_callback_fun_ = properties.buffer_callback_fun;
|
|
read_callback_arg_ = properties.buffer_callback_arg;
|
|
consumer_arg_.set(this, NULL, NULL, true);
|
|
PTHREAD_CALL(pthread_mutex_init(&read_mutex_, NULL));
|
|
PTHREAD_CALL(pthread_cond_init(&read_cond_, NULL));
|
|
PTHREAD_CALL(pthread_create(&consumer_thread_, NULL, reader_fun, &consumer_arg_));
|
|
}
|
|
|
|
~MemoryPool() {
|
|
Flush();
|
|
PTHREAD_CALL(pthread_cancel(consumer_thread_));
|
|
void *res;
|
|
PTHREAD_CALL(pthread_join(consumer_thread_, &res));
|
|
if (res != PTHREAD_CANCELED) EXC_ABORT(ROCTRACER_STATUS_ERROR, "consumer thread wasn't stopped correctly");
|
|
allocator_default(&pool_begin_, 0, alloc_arg_);
|
|
}
|
|
|
|
template <typename Record>
|
|
void Write(const Record& record) {
|
|
std::lock_guard<mutex_t> lock(write_mutex_);
|
|
getRecord<Record>(record);
|
|
}
|
|
|
|
void Flush() {
|
|
std::lock_guard<mutex_t> lock(write_mutex_);
|
|
if (write_ptr_ > buffer_begin_) {
|
|
spawn_reader(buffer_begin_, write_ptr_);
|
|
sync_reader(&consumer_arg_);
|
|
buffer_begin_ = (buffer_end_ == pool_end_) ? pool_begin_ : buffer_end_;
|
|
buffer_end_ = buffer_begin_ + buffer_size_;
|
|
write_ptr_ = buffer_begin_;
|
|
}
|
|
}
|
|
|
|
private:
|
|
struct consumer_arg_t {
|
|
MemoryPool* obj;
|
|
const char* begin;
|
|
const char* end;
|
|
volatile std::atomic<bool> valid;
|
|
void set(MemoryPool* obj_p, const char* begin_p, const char* end_p, bool valid_p) {
|
|
obj = obj_p;
|
|
begin = begin_p;
|
|
end = end_p;
|
|
valid.store(valid_p);
|
|
}
|
|
};
|
|
|
|
template <typename Record>
|
|
Record* getRecord(const Record& init) {
|
|
char* next = write_ptr_ + sizeof(Record);
|
|
if (next > buffer_end_) {
|
|
if (write_ptr_ == buffer_begin_) EXC_ABORT(ROCTRACER_STATUS_ERROR, "buffer size(" << buffer_size_ << ") is less then the record(" << sizeof(Record) << ")");
|
|
spawn_reader(buffer_begin_, write_ptr_);
|
|
buffer_begin_ = (buffer_end_ == pool_end_) ? pool_begin_ : buffer_end_;
|
|
buffer_end_ = buffer_begin_ + buffer_size_;
|
|
write_ptr_ = buffer_begin_;
|
|
next = write_ptr_ + sizeof(Record);
|
|
}
|
|
|
|
Record* ptr = reinterpret_cast<Record*>(write_ptr_);
|
|
write_ptr_ = next;
|
|
|
|
*ptr = init;
|
|
return ptr;
|
|
}
|
|
|
|
static void reset_reader(consumer_arg_t* arg) {
|
|
arg->valid.store(false);
|
|
}
|
|
|
|
static void sync_reader(const consumer_arg_t* arg) {
|
|
while(arg->valid.load() == true) PTHREAD_CALL(sched_yield());
|
|
}
|
|
|
|
static void* reader_fun(void* consumer_arg) {
|
|
consumer_arg_t* arg = reinterpret_cast<consumer_arg_t*>(consumer_arg);
|
|
roctracer::MemoryPool* obj = arg->obj;
|
|
|
|
reset_reader(arg);
|
|
|
|
while (1) {
|
|
PTHREAD_CALL(pthread_mutex_lock(&(obj->read_mutex_)));
|
|
while (arg->valid.load() == false) {
|
|
PTHREAD_CALL(pthread_cond_wait(&(obj->read_cond_), &(obj->read_mutex_)));
|
|
}
|
|
|
|
obj->read_callback_fun_(arg->begin, arg->end, obj->read_callback_arg_);
|
|
reset_reader(arg);
|
|
PTHREAD_CALL(pthread_mutex_unlock(&(obj->read_mutex_)));
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void spawn_reader(const char* data_begin, const char* data_end) {
|
|
sync_reader(&consumer_arg_);
|
|
PTHREAD_CALL(pthread_mutex_lock(&read_mutex_));
|
|
consumer_arg_.set(this, data_begin, data_end, true);
|
|
PTHREAD_CALL(pthread_cond_signal(&read_cond_));
|
|
PTHREAD_CALL(pthread_mutex_unlock(&read_mutex_));
|
|
}
|
|
|
|
// pool allocator
|
|
roctracer_allocator_t alloc_fun_;
|
|
void* alloc_arg_;
|
|
|
|
// Pool definition
|
|
size_t buffer_size_;
|
|
char* pool_begin_;
|
|
char* pool_end_;
|
|
char* buffer_begin_;
|
|
char* buffer_end_;
|
|
char* write_ptr_;
|
|
mutex_t write_mutex_;
|
|
|
|
// Consuming read thread
|
|
roctracer_buffer_callback_t read_callback_fun_;
|
|
void* read_callback_arg_;
|
|
consumer_arg_t consumer_arg_;
|
|
pthread_t consumer_thread_;
|
|
pthread_mutex_t read_mutex_;
|
|
pthread_cond_t read_cond_;
|
|
};
|
|
|
|
} // namespace roctracer
|
|
|
|
#endif // MEMORY_POOL_H_
|