mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-04 05:51:27 +12:00
Logging system rework
* use one central unified log with channels/priorities ad-hoc listener registration and de-registration * disable buffering by default * add multi-threaded ringbuffer implementation * use buffered listener for the gui (using the ringbuffer)
This commit is contained in:
parent
394b698e92
commit
21da317453
165 changed files with 1731 additions and 1519 deletions
159
Utilities/MTRingbuffer.h
Normal file
159
Utilities/MTRingbuffer.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
#pragma once
|
||||
#include <thread>
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
|
||||
//Simple non-resizable FIFO Ringbuffer that can be simultaneously be read from and written to
|
||||
//if we ever get to use boost please replace this with boost::circular_buffer, there's no reason
|
||||
//why we would have to keep this amateur attempt at such a fundamental data-structure around
|
||||
template< typename T, unsigned int MAX_MTRINGBUFFER_BUFFER_SIZE>
|
||||
class MTRingbuffer{
|
||||
std::array<T, MAX_MTRINGBUFFER_BUFFER_SIZE> mBuffer;
|
||||
//this is a recursive mutex because the get methods lock it but the only
|
||||
//way to be sure that they do not block is to check the size and the only
|
||||
//way to check the size and use get atomically is to lock this mutex,
|
||||
//so it goes:
|
||||
//lock get mutex-->check size-->call get-->lock get mutex-->unlock get mutex-->return from get-->unlock get mutex
|
||||
std::recursive_mutex mMutGet;
|
||||
std::mutex mMutPut;
|
||||
|
||||
size_t mGet;
|
||||
size_t mPut;
|
||||
size_t moveGet(size_t by = 1){ return (mGet + by) % MAX_MTRINGBUFFER_BUFFER_SIZE; }
|
||||
size_t movePut(size_t by = 1){ return (mPut + by) % MAX_MTRINGBUFFER_BUFFER_SIZE; }
|
||||
public:
|
||||
MTRingbuffer() : mGet(0), mPut(0){}
|
||||
|
||||
//blocks until there's something to get, so check "free()" if you want to avoid blocking
|
||||
//also lock the get mutex around the free() check and the pop if you want to avoid racing
|
||||
T pop()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mMutGet);
|
||||
while (mGet == mPut)
|
||||
{
|
||||
//wait until there's actually something to get
|
||||
//throwing an exception might be better, blocking here is a little awkward
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
size_t ret = mGet;
|
||||
mGet = moveGet();
|
||||
return mBuffer[ret];
|
||||
}
|
||||
|
||||
//blocks if the buffer is full until there's enough room
|
||||
void push(T &putEle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutPut);
|
||||
while (movePut() == mGet)
|
||||
{
|
||||
//if this is reached a lot it's time to increase the buffer size
|
||||
//or implement dynamic re-sizing
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
mBuffer[mPut] = std::forward(putEle);
|
||||
mPut = movePut();
|
||||
}
|
||||
|
||||
bool empty()
|
||||
{
|
||||
return mGet == mPut;
|
||||
}
|
||||
|
||||
//returns the amount of free places, this is the amount of actual free spaces-1
|
||||
//since mGet==mPut signals an empty buffer we can't actually use the last free
|
||||
//space, so we shouldn't report it as free.
|
||||
size_t free()
|
||||
{
|
||||
if (mGet < mPut)
|
||||
{
|
||||
return mBuffer.size() - (mPut - mGet) - 1;
|
||||
}
|
||||
else if (mGet > mPut)
|
||||
{
|
||||
return mGet - mPut - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mBuffer.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
//the magic -1 is the same magic 1 that is explained in the free() function
|
||||
return mBuffer.size() - free() - 1;
|
||||
}
|
||||
|
||||
//takes random access iterator to T
|
||||
template<typename IteratorType>
|
||||
void pushRange(IteratorType from, IteratorType until)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutPut);
|
||||
size_t length = until - from;
|
||||
|
||||
//if whatever we're trying to store is greater than the entire buffer the following loop will be infinite
|
||||
assert(mBuffer.size() > length);
|
||||
while (free() < length)
|
||||
{
|
||||
//if this is reached a lot it's time to increase the buffer size
|
||||
//or implement dynamic re-sizing
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
if (mPut + length <= mBuffer.size())
|
||||
{
|
||||
std::copy(from, until, mBuffer.begin() + mPut);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t tillEnd = mBuffer.size() - mPut;
|
||||
std::copy(from, from + tillEnd, mBuffer.begin() + mPut);
|
||||
std::copy(from + tillEnd, until, mBuffer.begin());
|
||||
}
|
||||
mPut = movePut(length);
|
||||
|
||||
}
|
||||
|
||||
//takes output iterator to T
|
||||
template<typename IteratorType>
|
||||
void popN(IteratorType output, size_t n)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mMutGet);
|
||||
//make sure we're not trying to retrieve more than is in
|
||||
assert(n <= size());
|
||||
peekN<IteratorType>(output, n);
|
||||
mGet = moveGet(n);
|
||||
}
|
||||
|
||||
//takes output iterator to T
|
||||
template<typename IteratorType>
|
||||
void peekN(IteratorType output, size_t n)
|
||||
{
|
||||
size_t lGet = mGet;
|
||||
if (lGet + n <= mBuffer.size())
|
||||
{
|
||||
std::copy_n(mBuffer.begin() + lGet, n, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto next = std::copy(mBuffer.begin() + lGet, mBuffer.end(), output);
|
||||
std::copy_n(mBuffer.begin(), n - (mBuffer.size() - lGet), next);
|
||||
}
|
||||
}
|
||||
|
||||
//well this is just asking for trouble
|
||||
//but the comment above the declaration of mMutGet explains why it's there
|
||||
//if there's a better way please remove this
|
||||
void lockGet()
|
||||
{
|
||||
mMutGet.lock();
|
||||
}
|
||||
|
||||
//well this is just asking for trouble
|
||||
//but the comment above the declaration of mMutGet explains why it's there
|
||||
//if there's a better way please remove this
|
||||
void unlockGet()
|
||||
{
|
||||
mMutGet.unlock();
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue