no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
/****************************************************************************
Copyright (c) 2020-2022 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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.
****************************************************************************/
#include "AutoReleasePool.h"
#import <Foundation/NSAutoreleasePool.h>
namespace cc {
AutoReleasePool::AutoReleasePool() {
_nsReleasePool = [[NSAutoreleasePool alloc] init];
}
AutoReleasePool::~AutoReleasePool() {
drain();
}
void AutoReleasePool::drain() {
if (_nsReleasePool) {
[static_cast<NSAutoreleasePool *>(_nsReleasePool) drain];
_nsReleasePool = nullptr;
}
}
} // namespace cc

View File

@@ -0,0 +1,35 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#include "AutoReleasePool.h"
namespace cc {
AutoReleasePool::AutoReleasePool() = default;
AutoReleasePool::~AutoReleasePool() = default;
void AutoReleasePool::drain() {
}
} // namespace cc

View File

@@ -0,0 +1,44 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
namespace cc {
class AutoReleasePool final {
public:
AutoReleasePool();
AutoReleasePool(const AutoReleasePool &) = delete;
AutoReleasePool(AutoReleasePool &&) = delete;
~AutoReleasePool();
AutoReleasePool &operator=(const AutoReleasePool &) = delete;
AutoReleasePool &operator=(AutoReleasePool &&) = delete;
void drain();
private:
void *_nsReleasePool{nullptr};
};
} // namespace cc

View File

@@ -0,0 +1,44 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#include "ConditionVariable.h"
namespace cc {
void ConditionVariable::wait() noexcept {
std::unique_lock<std::mutex> lock(_mutex);
_condVar.wait(lock);
}
void ConditionVariable::signal() noexcept {
std::lock_guard<std::mutex> lock(_mutex);
_condVar.notify_one();
}
void ConditionVariable::signalAll() noexcept {
std::lock_guard<std::mutex> lock(_mutex);
_condVar.notify_all();
}
} // namespace cc

View File

@@ -0,0 +1,54 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
namespace cc {
class ConditionVariable final {
public:
void wait() noexcept;
template <typename Function, typename... Args>
void wait(Function &&func, Args &&...args) noexcept;
void signal() noexcept;
void signalAll() noexcept;
private:
std::mutex _mutex;
std::condition_variable _condVar;
};
// DO NOT MANIPULATE ANY SYCHRONIZATION PRIMITIVES INSIDE THE CALLBACK
template <typename Function, typename... Args>
void ConditionVariable::wait(Function &&func, Args &&...args) noexcept {
std::unique_lock<std::mutex> lock(_mutex);
_condVar.wait(lock, std::bind(std::forward<Function>(func), std::forward<Args>(args)...));
}
} // namespace cc

View File

@@ -0,0 +1,46 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "ConditionVariable.h"
#include "Semaphore.h"
namespace cc {
template <typename T>
class Event final {
public:
inline void wait() noexcept { _syncObject.wait(); }
inline void signal() noexcept { _syncObject.signal(); };
inline void signalAll() noexcept { _syncObject.signalAll(); }
private:
T _syncObject{};
};
using EventCV = Event<ConditionVariable>;
using EventSem = Event<Semaphore>;
} // namespace cc

View File

@@ -0,0 +1,321 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#include "MessageQueue.h"
#include "AutoReleasePool.h"
#include "base/Utils.h"
namespace cc {
namespace {
uint32_t constexpr MEMORY_CHUNK_POOL_CAPACITY = 64;
uint32_t constexpr SWITCH_CHUNK_MEMORY_REQUIREMENT = sizeof(MemoryChunkSwitchMessage) + utils::ALIGN_TO<sizeof(DummyMessage), 16>;
} // namespace
MessageQueue::MemoryAllocator &MessageQueue::MemoryAllocator::getInstance() noexcept {
static MessageQueue::MemoryAllocator instance;
return instance;
}
uint8_t *MessageQueue::MemoryAllocator::request() noexcept {
uint8_t *newChunk = nullptr;
if (_chunkPool.try_dequeue(newChunk)) {
_chunkCount.fetch_sub(1, std::memory_order_acq_rel);
} else {
newChunk = memoryAllocateForMultiThread<uint8_t>(MEMORY_CHUNK_SIZE);
}
return newChunk;
}
void MessageQueue::MemoryAllocator::recycle(uint8_t *const chunk, bool const freeByUser) noexcept {
if (freeByUser) {
_chunkFreeQueue.enqueue(chunk);
} else {
free(chunk);
}
}
void MessageQueue::MemoryAllocator::freeByUser(MessageQueue *const mainMessageQueue) noexcept {
auto *queue = &_chunkFreeQueue;
ENQUEUE_MESSAGE_1(
mainMessageQueue, FreeChunksInFreeQueue,
queue, queue,
{
uint8_t *chunk = nullptr;
while (queue->try_dequeue(chunk)) {
MessageQueue::MemoryAllocator::getInstance().free(chunk);
}
});
mainMessageQueue->kick();
}
MessageQueue::MemoryAllocator::~MemoryAllocator() noexcept {
destroy();
}
void MessageQueue::MemoryAllocator::destroy() noexcept {
uint8_t *chunk = nullptr;
if (_chunkPool.try_dequeue(chunk)) {
::free(chunk);
_chunkCount.fetch_sub(1, std::memory_order_acq_rel);
}
}
void MessageQueue::MemoryAllocator::free(uint8_t *const chunk) noexcept {
if (_chunkCount.load(std::memory_order_acquire) >= MEMORY_CHUNK_POOL_CAPACITY) {
memoryFreeForMultiThread(chunk);
} else {
_chunkPool.enqueue(chunk);
_chunkCount.fetch_add(1, std::memory_order_acq_rel);
}
}
MessageQueue::MessageQueue() {
uint8_t *const chunk = MemoryAllocator::getInstance().request();
_writer.currentMemoryChunk = chunk;
_reader.currentMemoryChunk = chunk;
// sentinel node will not be executed
Message *const msg = allocate<DummyMessage>(1);
pushMessages();
pullMessages();
_reader.lastMessage = msg;
--_reader.newMessageCount;
}
void MessageQueue::kick() noexcept {
pushMessages();
std::lock_guard<std::mutex> lock(_mutex);
_condVar.notify_all();
}
void MessageQueue::kickAndWait() noexcept {
EventSem event;
EventSem *const pEvent = &event;
ENQUEUE_MESSAGE_1(this, WaitUntilFinish,
pEvent, pEvent,
{
pEvent->signal();
});
kick();
event.wait();
}
void MessageQueue::runConsumerThread() noexcept {
if (_immediateMode || _workerAttached) return;
_reader.terminateConsumerThread = false;
_reader.flushingFinished = false;
_consumerThread = ccnew std::thread(&MessageQueue::consumerThreadLoop, this);
_workerAttached = true;
}
void MessageQueue::terminateConsumerThread() noexcept {
if (_immediateMode || !_workerAttached) return;
EventSem event;
EventSem *const pEvent = &event;
ReaderContext *const pR = &_reader;
ccnew_placement(allocate<TerminateConsumerThreadMessage>(1)) TerminateConsumerThreadMessage(pEvent, pR);
kick();
event.wait();
if (_consumerThread != nullptr) {
if (_consumerThread->joinable()) {
_consumerThread->join();
}
}
CC_SAFE_DELETE(_consumerThread);
}
void MessageQueue::finishWriting() noexcept {
if (!_immediateMode) {
bool *const flushingFinished = &_reader.flushingFinished;
ENQUEUE_MESSAGE_1(this, finishWriting,
flushingFinished, flushingFinished,
{
*flushingFinished = true;
});
kick();
}
}
void MessageQueue::recycleMemoryChunk(uint8_t *const chunk) const noexcept {
MessageQueue::MemoryAllocator::getInstance().recycle(chunk, _freeChunksByUser);
}
void MessageQueue::freeChunksInFreeQueue(MessageQueue *const mainMessageQueue) noexcept {
MessageQueue::MemoryAllocator::getInstance().freeByUser(mainMessageQueue);
}
// NOLINTNEXTLINE(misc-no-recursion)
uint8_t *MessageQueue::allocateImpl(uint32_t allocatedSize, uint32_t const requestSize) noexcept {
uint32_t const alignedSize = align(requestSize, 16);
CC_ASSERT(alignedSize + SWITCH_CHUNK_MEMORY_REQUIREMENT <= MEMORY_CHUNK_SIZE);
uint32_t const newOffset = _writer.offset + alignedSize;
// newOffset contains the DummyMessage
if (newOffset + sizeof(MemoryChunkSwitchMessage) <= MEMORY_CHUNK_SIZE) {
uint8_t *const allocatedMemory = _writer.currentMemoryChunk + _writer.offset;
_writer.offset = newOffset;
return allocatedMemory;
}
uint8_t *const newChunk = MessageQueue::MemoryAllocator::getInstance().request();
auto *const switchMessage = reinterpret_cast<MemoryChunkSwitchMessage *>(_writer.currentMemoryChunk + _writer.offset);
ccnew_placement(switchMessage) MemoryChunkSwitchMessage(this, newChunk, _writer.currentMemoryChunk);
switchMessage->_next = reinterpret_cast<Message *>(newChunk); // point to start position
_writer.lastMessage = switchMessage;
++_writer.pendingMessageCount;
_writer.currentMemoryChunk = newChunk;
_writer.offset = 0;
DummyMessage *const head = allocate<DummyMessage>(1);
ccnew_placement(head) DummyMessage;
if (_immediateMode) {
pushMessages();
pullMessages();
CC_ASSERT_EQ(_reader.newMessageCount, 2);
executeMessages();
executeMessages();
}
return allocateImpl(allocatedSize, requestSize);
}
void MessageQueue::pushMessages() noexcept {
_writer.writtenMessageCount.fetch_add(_writer.pendingMessageCount, std::memory_order_acq_rel);
_writer.pendingMessageCount = 0;
}
void MessageQueue::pullMessages() noexcept {
uint32_t const writtenMessageCountNew = _writer.writtenMessageCount.load(std::memory_order_acquire);
_reader.newMessageCount += writtenMessageCountNew - _reader.writtenMessageCountSnap;
_reader.writtenMessageCountSnap = writtenMessageCountNew;
}
void MessageQueue::flushMessages() noexcept {
while (!_reader.flushingFinished) {
executeMessages();
}
_reader.flushingFinished = false;
}
void MessageQueue::executeMessages() noexcept {
Message *const msg = readMessage();
if (!msg) {
return;
}
msg->execute();
msg->~Message();
}
Message *MessageQueue::readMessage() noexcept {
while (!hasNewMessage()) { // if empty
std::unique_lock<std::mutex> lock(_mutex);
pullMessages(); // try pulling data from consumer
if (!hasNewMessage()) { // still empty
_condVar.wait(lock); // wait for the producer to wake me up
pullMessages(); // pulling again
}
}
Message *const msg = _reader.lastMessage->getNext();
_reader.lastMessage = msg;
--_reader.newMessageCount;
CC_ASSERT(msg);
return msg;
}
MessageQueue::~MessageQueue() {
recycleMemoryChunk(_writer.currentMemoryChunk);
}
void MessageQueue::consumerThreadLoop() noexcept {
while (!_reader.terminateConsumerThread) {
AutoReleasePool autoReleasePool;
flushMessages();
}
_workerAttached = false;
}
char const *DummyMessage::getName() const noexcept {
return "Dummy";
}
MemoryChunkSwitchMessage::MemoryChunkSwitchMessage(MessageQueue *const queue, uint8_t *const newChunk, uint8_t *const oldChunk) noexcept
: _messageQueue(queue),
_newChunk(newChunk),
_oldChunk(oldChunk) {
}
MemoryChunkSwitchMessage::~MemoryChunkSwitchMessage() {
_messageQueue->recycleMemoryChunk(_oldChunk);
}
void MemoryChunkSwitchMessage::execute() noexcept {
_messageQueue->_reader.currentMemoryChunk = _newChunk;
_messageQueue->pullMessages();
}
char const *MemoryChunkSwitchMessage::getName() const noexcept {
return "MemoryChunkSwitch";
}
TerminateConsumerThreadMessage::TerminateConsumerThreadMessage(EventSem *const pEvent, ReaderContext *const pR) noexcept
: _event(pEvent),
_reader(pR) {
}
void TerminateConsumerThreadMessage::execute() noexcept {
_reader->terminateConsumerThread = true;
_reader->flushingFinished = true;
_event->signal();
}
char const *TerminateConsumerThreadMessage::getName() const noexcept {
return "TerminateConsumerThread";
}
} // namespace cc

View File

@@ -0,0 +1,725 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <cstdint>
#include "../memory/Memory.h"
#include "Event.h"
#include "concurrentqueue/concurrentqueue.h"
namespace cc {
// TODO(YunHsiao): thread-specific allocators
template <typename T>
inline T *memoryAllocateForMultiThread(uint32_t const count) noexcept {
return static_cast<T *>(malloc(sizeof(T) * count));
}
template <typename T>
inline void memoryFreeForMultiThread(T *const p) noexcept {
free(p);
}
inline uint32_t constexpr align(uint32_t const val, uint32_t const alignment) noexcept {
return (val + alignment - 1) & ~(alignment - 1);
}
class Message {
public:
Message() = default;
virtual ~Message() = default;
Message(Message const &) = delete;
Message(Message &&) = delete;
Message &operator=(Message const &) = delete;
Message &operator=(Message &&) = delete;
virtual void execute() = 0;
virtual char const *getName() const noexcept = 0;
inline Message *getNext() const noexcept { return _next; }
private:
Message *_next; // explicitly assigned beforehand, don't init the member here
friend class MessageQueue;
};
// structs may be padded
#if (CC_COMPILER == CC_COMPILER_MSVC)
#pragma warning(disable : 4324)
#endif
struct ALIGNAS(64) WriterContext final {
uint8_t *currentMemoryChunk{nullptr};
Message *lastMessage{nullptr};
uint32_t offset{0};
uint32_t pendingMessageCount{0};
std::atomic<uint32_t> writtenMessageCount{0};
};
struct ALIGNAS(64) ReaderContext final {
uint8_t *currentMemoryChunk{nullptr};
Message *lastMessage{nullptr};
uint32_t offset{0};
uint32_t writtenMessageCountSnap{0};
uint32_t newMessageCount{0};
bool terminateConsumerThread{false};
bool flushingFinished{false};
};
// A single-producer single-consumer circular buffer queue.
// Both the messages and their submitting data should be allocated from here.
class ALIGNAS(64) MessageQueue final {
public:
static constexpr uint32_t MEMORY_CHUNK_SIZE = 4096 * 16;
MessageQueue();
~MessageQueue();
MessageQueue(MessageQueue const &) = delete;
MessageQueue(MessageQueue &&) = delete;
MessageQueue &operator=(MessageQueue const &) = delete;
MessageQueue &operator=(MessageQueue &&) = delete;
// message allocation
template <typename T>
std::enable_if_t<std::is_base_of<Message, T>::value, T *>
allocate(uint32_t count) noexcept;
// general-purpose allocation
template <typename T>
std::enable_if_t<!std::is_base_of<Message, T>::value, T *>
allocate(uint32_t count) noexcept;
template <typename T>
T *allocateAndCopy(uint32_t count, void const *data) noexcept;
template <typename T>
T *allocateAndZero(uint32_t count) noexcept;
// notify the consumer to start working
void kick() noexcept;
// notify the consumer to start working and block the producer until finished
void kickAndWait() noexcept;
void runConsumerThread() noexcept;
void terminateConsumerThread() noexcept;
void finishWriting() noexcept;
void flushMessages() noexcept;
inline bool isImmediateMode() const noexcept { return _immediateMode; }
void recycleMemoryChunk(uint8_t *chunk) const noexcept;
static void freeChunksInFreeQueue(MessageQueue *mainMessageQueue) noexcept;
inline void setImmediateMode(bool immediateMode) noexcept { _immediateMode = immediateMode; }
private:
class ALIGNAS(64) MemoryAllocator final {
public:
MemoryAllocator() = default;
~MemoryAllocator() noexcept;
MemoryAllocator(MemoryAllocator const &) = delete;
MemoryAllocator(MemoryAllocator &&) = delete;
MemoryAllocator &operator=(MemoryAllocator const &) = delete;
MemoryAllocator &operator=(MemoryAllocator &&) = delete;
static MemoryAllocator &getInstance() noexcept;
uint8_t *request() noexcept;
void recycle(uint8_t *chunk, bool freeByUser) noexcept;
void freeByUser(MessageQueue *mainMessageQueue) noexcept;
void destroy() noexcept;
private:
using ChunkQueue = moodycamel::ConcurrentQueue<uint8_t *>;
void free(uint8_t *chunk) noexcept;
std::atomic<uint32_t> _chunkCount{0};
ChunkQueue _chunkPool{};
ChunkQueue _chunkFreeQueue{};
};
// structs may be padded
#if (CC_COMPILER == CC_COMPILER_MSVC)
#pragma warning(default : 4324)
#endif
uint8_t *allocateImpl(uint32_t allocatedSize, uint32_t requestSize) noexcept;
void pushMessages() noexcept;
// consumer thread specifics
void pullMessages() noexcept;
void executeMessages() noexcept;
Message *readMessage() noexcept;
inline bool hasNewMessage() const noexcept { return _reader.newMessageCount > 0 && !_reader.flushingFinished; }
void consumerThreadLoop() noexcept;
WriterContext _writer;
ReaderContext _reader;
std::mutex _mutex;
std::condition_variable _condVar;
bool _immediateMode{true};
bool _workerAttached{false};
bool _freeChunksByUser{true}; // recycled chunks will be stashed until explicit free instruction
std::thread *_consumerThread{nullptr};
friend class MemoryChunkSwitchMessage;
};
class DummyMessage final : public Message {
public:
void execute() noexcept override {}
char const *getName() const noexcept override;
};
class MemoryChunkSwitchMessage final : public Message {
public:
MemoryChunkSwitchMessage(MessageQueue *queue, uint8_t *newChunk, uint8_t *oldChunk) noexcept;
~MemoryChunkSwitchMessage() override;
void execute() noexcept override;
char const *getName() const noexcept override;
private:
MessageQueue *_messageQueue{nullptr};
uint8_t *_newChunk{nullptr};
uint8_t *_oldChunk{nullptr};
};
class TerminateConsumerThreadMessage final : public Message {
public:
TerminateConsumerThreadMessage(EventSem *pEvent, ReaderContext *pR) noexcept;
void execute() noexcept override;
char const *getName() const noexcept override;
private:
EventSem *_event{nullptr};
ReaderContext *_reader{nullptr};
};
template <typename T>
std::enable_if_t<std::is_base_of<Message, T>::value, T *>
MessageQueue::allocate(uint32_t const /*count*/) noexcept {
uint32_t allocatedSize = 0;
T *const msg = reinterpret_cast<T *>(allocateImpl(allocatedSize, sizeof(T)));
msg->_next = reinterpret_cast<Message *>(_writer.currentMemoryChunk + _writer.offset);
++_writer.pendingMessageCount;
_writer.lastMessage = msg;
return msg;
}
template <typename T>
std::enable_if_t<!std::is_base_of<Message, T>::value, T *>
MessageQueue::allocate(uint32_t const count) noexcept {
uint32_t const requestSize = sizeof(T) * count;
CC_ASSERT(requestSize);
uint32_t allocatedSize = 0;
uint8_t *const allocatedMemory = allocateImpl(allocatedSize, requestSize);
_writer.lastMessage->_next = reinterpret_cast<Message *>(_writer.currentMemoryChunk + _writer.offset);
return reinterpret_cast<T *>(allocatedMemory);
}
template <typename T>
T *MessageQueue::allocateAndCopy(uint32_t const count, void const *data) noexcept {
T *const allocatedMemory = allocate<T>(count);
memcpy(allocatedMemory, data, sizeof(T) * count);
return allocatedMemory;
}
template <typename T>
T *MessageQueue::allocateAndZero(uint32_t const count) noexcept {
T *const allocatedMemory = allocate<T>(count);
memset(allocatedMemory, 0, sizeof(T) * count);
return allocatedMemory;
}
// utility macros for the producer thread to enqueue messages
#define WRITE_MESSAGE(queue, MessageName, Params) \
{ \
if (!queue->isImmediateMode()) { \
ccnew_placement(queue->allocate<MessageName>(1)) MessageName Params; \
} else { \
MessageName msg Params; \
msg.execute(); \
} \
}
#define ENQUEUE_MESSAGE_0(queue, MessageName, Code) \
{ \
class MessageName final : public Message { \
public: \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
}; \
WRITE_MESSAGE(queue, MessageName, ); \
}
#define ENQUEUE_MESSAGE_1(queue, MessageName, \
Param1, Value1, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
\
class MessageName final : public Message { \
public: \
explicit MessageName(Type1 In##Param1) \
: Param1(std::move(In##Param1)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
}; \
WRITE_MESSAGE(queue, MessageName, (Value1)) \
}
#define ENQUEUE_MESSAGE_2(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
\
class MessageName final : public Message { \
public: \
MessageName( \
Type1 In##Param1, Type2 In##Param2) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
}; \
WRITE_MESSAGE(queue, MessageName, (Value1, Value2)) \
}
#define ENQUEUE_MESSAGE_3(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Param3, Value3, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
using Type3 = typename std::decay<decltype(Value3)>::type; \
\
class MessageName final : public Message { \
public: \
MessageName( \
Type1 In##Param1, \
Type2 In##Param2, \
Type3 In##Param3) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)), \
Param3(std::move(In##Param3)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
Type3 Param3; \
}; \
WRITE_MESSAGE(queue, MessageName, \
(Value1, \
Value2, \
Value3)) \
}
#define ENQUEUE_MESSAGE_4(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Param3, Value3, \
Param4, Value4, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
using Type3 = typename std::decay<decltype(Value3)>::type; \
using Type4 = typename std::decay<decltype(Value4)>::type; \
\
class MessageName : public Message { \
public: \
MessageName( \
Type1 In##Param1, \
Type2 In##Param2, \
Type3 In##Param3, \
Type4 In##Param4) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)), \
Param3(std::move(In##Param3)), \
Param4(std::move(In##Param4)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
Type3 Param3; \
Type4 Param4; \
}; \
WRITE_MESSAGE(queue, MessageName, \
(Value1, \
Value2, \
Value3, \
Value4)) \
}
#define ENQUEUE_MESSAGE_5(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Param3, Value3, \
Param4, Value4, \
Param5, Value5, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
using Type3 = typename std::decay<decltype(Value3)>::type; \
using Type4 = typename std::decay<decltype(Value4)>::type; \
using Type5 = typename std::decay<decltype(Value5)>::type; \
\
class MessageName : public Message { \
public: \
MessageName( \
Type1 In##Param1, \
Type2 In##Param2, \
Type3 In##Param3, \
Type4 In##Param4, \
Type5 In##Param5) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)), \
Param3(std::move(In##Param3)), \
Param4(std::move(In##Param4)), \
Param5(std::move(In##Param5)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
Type3 Param3; \
Type4 Param4; \
Type5 Param5; \
}; \
WRITE_MESSAGE(queue, MessageName, \
(Value1, \
Value2, \
Value3, \
Value4, \
Value5)) \
}
#define ENQUEUE_MESSAGE_6(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Param3, Value3, \
Param4, Value4, \
Param5, Value5, \
Param6, Value6, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
using Type3 = typename std::decay<decltype(Value3)>::type; \
using Type4 = typename std::decay<decltype(Value4)>::type; \
using Type5 = typename std::decay<decltype(Value5)>::type; \
using Type6 = typename std::decay<decltype(Value6)>::type; \
\
class MessageName : public Message { \
public: \
MessageName( \
Type1 In##Param1, \
Type2 In##Param2, \
Type3 In##Param3, \
Type4 In##Param4, \
Type5 In##Param5, \
Type6 In##Param6) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)), \
Param3(std::move(In##Param3)), \
Param4(std::move(In##Param4)), \
Param5(std::move(In##Param5)), \
Param6(std::move(In##Param6)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
Type3 Param3; \
Type4 Param4; \
Type5 Param5; \
Type6 Param6; \
}; \
WRITE_MESSAGE(queue, MessageName, \
(Value1, \
Value2, \
Value3, \
Value4, \
Value5, \
Value6)) \
}
#define ENQUEUE_MESSAGE_7(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Param3, Value3, \
Param4, Value4, \
Param5, Value5, \
Param6, Value6, \
Param7, Value7, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
using Type3 = typename std::decay<decltype(Value3)>::type; \
using Type4 = typename std::decay<decltype(Value4)>::type; \
using Type5 = typename std::decay<decltype(Value5)>::type; \
using Type6 = typename std::decay<decltype(Value6)>::type; \
using Type7 = typename std::decay<decltype(Value7)>::type; \
\
class MessageName : public Message { \
public: \
MessageName( \
Type1 In##Param1, \
Type2 In##Param2, \
Type3 In##Param3, \
Type4 In##Param4, \
Type5 In##Param5, \
Type6 In##Param6, \
Type7 In##Param7) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)), \
Param3(std::move(In##Param3)), \
Param4(std::move(In##Param4)), \
Param5(std::move(In##Param5)), \
Param6(std::move(In##Param6)), \
Param7(std::move(In##Param7)) { \
} \
void execute() override { \
Code \
} \
const char *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
Type3 Param3; \
Type4 Param4; \
Type5 Param5; \
Type6 Param6; \
Type7 Param7; \
}; \
WRITE_MESSAGE(queue, MessageName, \
(Value1, \
Value2, \
Value3, \
Value4, \
Value5, \
Value6, \
Value7)) \
}
#define ENQUEUE_MESSAGE_8(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Param3, Value3, \
Param4, Value4, \
Param5, Value5, \
Param6, Value6, \
Param7, Value7, \
Param8, Value8, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
using Type3 = typename std::decay<decltype(Value3)>::type; \
using Type4 = typename std::decay<decltype(Value4)>::type; \
using Type5 = typename std::decay<decltype(Value5)>::type; \
using Type6 = typename std::decay<decltype(Value6)>::type; \
using Type7 = typename std::decay<decltype(Value7)>::type; \
using Type8 = typename std::decay<decltype(Value8)>::type; \
\
class MessageName : public Message { \
public: \
MessageName(Type1 In##Param1, \
Type2 In##Param2, \
Type3 In##Param3, \
Type4 In##Param4, \
Type5 In##Param5, \
Type6 In##Param6, \
Type7 In##Param7, \
Type8 In##Param8) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)), \
Param3(std::move(In##Param3)), \
Param4(std::move(In##Param4)), \
Param5(std::move(In##Param5)), \
Param6(std::move(In##Param6)), \
Param7(std::move(In##Param7)), \
Param8(std::move(In##Param8)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
Type3 Param3; \
Type4 Param4; \
Type5 Param5; \
Type6 Param6; \
Type7 Param7; \
Type8 Param8; \
}; \
WRITE_MESSAGE(queue, MessageName, \
(Value1, \
Value2, \
Value3, \
Value4, \
Value5, \
Value6, \
Value7, \
Value8)) \
}
#define ENQUEUE_MESSAGE_9(queue, MessageName, \
Param1, Value1, \
Param2, Value2, \
Param3, Value3, \
Param4, Value4, \
Param5, Value5, \
Param6, Value6, \
Param7, Value7, \
Param8, Value8, \
Param9, Value9, \
Code) \
{ \
using Type1 = typename std::decay<decltype(Value1)>::type; \
using Type2 = typename std::decay<decltype(Value2)>::type; \
using Type3 = typename std::decay<decltype(Value3)>::type; \
using Type4 = typename std::decay<decltype(Value4)>::type; \
using Type5 = typename std::decay<decltype(Value5)>::type; \
using Type6 = typename std::decay<decltype(Value6)>::type; \
using Type7 = typename std::decay<decltype(Value7)>::type; \
using Type8 = typename std::decay<decltype(Value8)>::type; \
using Type9 = typename std::decay<decltype(Value9)>::type; \
\
class MessageName : public Message { \
public: \
MessageName(Type1 In##Param1, \
Type2 In##Param2, \
Type3 In##Param3, \
Type4 In##Param4, \
Type5 In##Param5, \
Type6 In##Param6, \
Type7 In##Param7, \
Type8 In##Param8, \
Type9 In##Param9) \
: Param1(std::move(In##Param1)), \
Param2(std::move(In##Param2)), \
Param3(std::move(In##Param3)), \
Param4(std::move(In##Param4)), \
Param5(std::move(In##Param5)), \
Param6(std::move(In##Param6)), \
Param7(std::move(In##Param7)), \
Param8(std::move(In##Param8)), \
Param9(std::move(In##Param9)) { \
} \
void execute() override { \
Code \
} \
char const *getName() const noexcept override { \
return (#MessageName); \
} \
\
private: \
Type1 Param1; \
Type2 Param2; \
Type3 Param3; \
Type4 Param4; \
Type5 Param5; \
Type6 Param6; \
Type7 Param7; \
Type8 Param8; \
Type9 Param9; \
}; \
WRITE_MESSAGE(queue, MessageName, \
(Value1, \
Value2, \
Value3, \
Value4, \
Value5, \
Value6, \
Value7, \
Value8, \
Value9)) \
}
} // namespace cc

View File

@@ -0,0 +1,57 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <shared_mutex>
namespace cc {
class ReadWriteLock final {
public:
ReadWriteLock() = default;
template <typename Function, typename... Args>
auto lockRead(Function &&func, Args &&...args) noexcept -> decltype(func(std::forward<Args>(args)...));
template <typename Function, typename... Args>
auto lockWrite(Function &&func, Args &&...args) noexcept -> decltype(func(std::forward<Args>(args)...));
private:
std::shared_mutex _mutex;
};
template <typename Function, typename... Args>
auto ReadWriteLock::lockRead(Function &&func, Args &&...args) noexcept -> decltype(func(std::forward<Args>(args)...)) {
std::shared_lock<std::shared_mutex> lock(_mutex);
return func(std::forward<Args>(args)...);
}
template <typename Function, typename... Args>
auto ReadWriteLock::lockWrite(Function &&func, Args &&...args) noexcept -> decltype(func(std::forward<Args>(args)...)) {
std::lock_guard<std::shared_mutex> lock(_mutex);
return func(std::forward<Args>(args)...);
}
} // namespace cc

View File

@@ -0,0 +1,45 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#include "Semaphore.h"
namespace cc {
Semaphore::Semaphore() noexcept
: _semaphore(0) {
}
Semaphore::Semaphore(int initialCount) noexcept
: _semaphore(initialCount) {
}
void Semaphore::wait() noexcept {
_semaphore.wait();
}
void Semaphore::signal(int count) noexcept {
_semaphore.signal(count);
}
} // namespace cc

View File

@@ -0,0 +1,45 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include "base/Macros.h"
#include "concurrentqueue/concurrentqueue.h"
#include "concurrentqueue/lightweightsemaphore.h"
namespace cc {
class Semaphore final {
public:
Semaphore() noexcept;
explicit Semaphore(int initialCount) noexcept;
void wait() noexcept;
void signal(int count = 1) noexcept;
void signalAll() noexcept { CC_ABORT(); } // NOLINT(readability-convert-member-functions-to-static)
private:
moodycamel::details::Semaphore _semaphore;
};
} // namespace cc

View File

@@ -0,0 +1,82 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#include "ThreadPool.h"
namespace cc {
uint8_t const ThreadPool::CPU_CORE_COUNT = std::thread::hardware_concurrency();
uint8_t const ThreadPool::MAX_THREAD_COUNT = CPU_CORE_COUNT - 1;
void ThreadPool::start() {
if (_running) {
return;
}
_running = true;
for (uint8_t i = 0; i < MAX_THREAD_COUNT; ++i) {
addThread();
}
}
void ThreadPool::stop() {
if (!_running) {
return;
}
_running = false;
_event.signalAll();
for (auto &worker : _workers) {
if (worker.joinable()) {
worker.join();
}
}
_workers.clear();
}
void ThreadPool::addThread() {
CC_ASSERT(_workers.size() < MAX_THREAD_COUNT);
auto workerLoop = [this]() {
while (_running) {
Task task = nullptr;
if (_tasks.try_dequeue(task)) {
task();
} else {
// Double Check
_event.wait([this]() {
return _tasks.size_approx() != 0;
});
}
}
};
_workers.emplace_back(workerLoop);
}
} // namespace cc

View File

@@ -0,0 +1,85 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <atomic>
#include <cstdint>
#include <functional>
#include <future>
#include <thread>
#include "Event.h"
#include "base/Macros.h"
#include "base/std/container/list.h"
#include "concurrentqueue/concurrentqueue.h"
namespace cc {
class ThreadPool final {
public:
using Task = std::function<void()>;
using TaskQueue = moodycamel::ConcurrentQueue<Task>;
static uint8_t const CPU_CORE_COUNT;
static uint8_t const MAX_THREAD_COUNT;
ThreadPool() = default;
~ThreadPool() = default;
ThreadPool(ThreadPool const &) = delete;
ThreadPool(ThreadPool &&) noexcept = delete;
ThreadPool &operator=(ThreadPool const &) = delete;
ThreadPool &operator=(ThreadPool &&) noexcept = delete;
template <typename Function, typename... Args>
auto dispatchTask(Function &&func, Args &&...args) -> std::future<decltype(func(std::forward<Args>(args)...))>;
void start();
void stop();
private:
using Event = ConditionVariable;
void addThread();
TaskQueue _tasks{};
ccstd::list<std::thread> _workers{};
Event _event{};
std::atomic<bool> _running{false};
uint8_t _workerCount{MAX_THREAD_COUNT};
};
template <typename Function, typename... Args>
auto ThreadPool::dispatchTask(Function &&func, Args &&...args) -> std::future<decltype(func(std::forward<Args>(args)...))> {
CC_ASSERT(_running);
using ReturnType = decltype(func(std::forward<Args>(args)...));
auto task = std::make_shared<std::packaged_task<ReturnType()>>(std::bind(std::forward<Function>(func), std::forward<Args>(args)...));
bool const succeed = _tasks.enqueue([task]() {
(*task)();
});
CC_ASSERT(succeed);
_event.signal();
return task->get_future();
}
} // namespace cc

View File

@@ -0,0 +1,47 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <atomic>
#include <cstdint>
namespace cc {
template <typename T, typename = typename std::enable_if_t<std::is_integral<T>::value>>
class ThreadSafeCounter final {
public:
inline T increment() noexcept { return add(1); }
inline T add(T const v) noexcept { return _counter.fetch_add(v, std::memory_order_relaxed); }
inline T decrement() noexcept { return subtract(1); }
inline T subtract(T const v) noexcept { return _counter.fetch_sub(v, std::memory_order_relaxed); }
inline void set(T const v) noexcept { _counter.store(v, std::memory_order_relaxed); }
inline T get() const noexcept { return _counter.load(std::memory_order_relaxed); }
inline void reset() noexcept { set(0); }
private:
std::atomic<T> _counter{0};
};
} // namespace cc

View File

@@ -0,0 +1,71 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#include "ThreadSafeLinearAllocator.h"
#include "acl/core/memory_utils.h"
#include "base/Macros.h"
namespace cc {
ThreadSafeLinearAllocator::ThreadSafeLinearAllocator(size_t size, size_t alignment) noexcept
: _capacity(size), _alignment(alignment) {
if (alignment == 1) {
_buffer = CC_MALLOC(size);
} else {
_buffer = CC_MALLOC_ALIGN(size, alignment);
}
CC_ASSERT(_buffer);
}
ThreadSafeLinearAllocator::~ThreadSafeLinearAllocator() {
if (_alignment == 1) {
CC_FREE(_buffer);
} else {
CC_FREE_ALIGN(_buffer);
}
}
void *ThreadSafeLinearAllocator::doAllocate(size_t size, size_t alignment) noexcept {
if (size == 0) {
return nullptr;
}
void *allocatedMemory = nullptr;
size_t oldUsedSize = 0;
uint64_t newUsedSize = 0; // force 64-bit here to correctly detect overflows
do {
oldUsedSize = getUsedSize();
allocatedMemory = acl::align_to(acl::add_offset_to_ptr<void>(_buffer, oldUsedSize), alignment);
newUsedSize = reinterpret_cast<uintptr_t>(allocatedMemory) - reinterpret_cast<uintptr_t>(_buffer) + size;
if (newUsedSize > _capacity) {
return nullptr; // overflows
}
} while (!_usedSize.compare_exchange_weak(oldUsedSize, static_cast<size_t>(newUsedSize), std::memory_order_relaxed, std::memory_order_relaxed)); // no ABA possible
return allocatedMemory;
}
} // namespace cc

View File

@@ -0,0 +1,78 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
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.
****************************************************************************/
#pragma once
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include "../memory/Memory.h"
namespace cc {
// class may be padded
#if (CC_COMPILER == CC_COMPILER_MSVC)
#pragma warning(disable : 4324)
#endif
class ALIGNAS(16) ThreadSafeLinearAllocator final {
public:
explicit ThreadSafeLinearAllocator(size_t size, size_t alignment = 1) noexcept;
~ThreadSafeLinearAllocator();
ThreadSafeLinearAllocator(ThreadSafeLinearAllocator const &) = delete;
ThreadSafeLinearAllocator(ThreadSafeLinearAllocator &&) = delete;
ThreadSafeLinearAllocator &operator=(ThreadSafeLinearAllocator const &) = delete;
ThreadSafeLinearAllocator &operator=(ThreadSafeLinearAllocator &&) = delete;
template <typename T>
inline T *allocate(size_t count, size_t alignment = 1) noexcept {
return reinterpret_cast<T *>(doAllocate(count * sizeof(T), alignment));
}
inline std::ptrdiff_t allocateToOffset(size_t size, size_t alignment = 1) noexcept {
return reinterpret_cast<intptr_t>(doAllocate(size, alignment)) - reinterpret_cast<intptr_t>(_buffer);
}
inline void *getBuffer() const noexcept { return _buffer; }
inline size_t getCapacity() const noexcept { return _capacity; }
inline size_t getUsedSize() const noexcept { return _usedSize.load(std::memory_order_relaxed); }
inline size_t getBalance() const noexcept { return getCapacity() - getUsedSize(); }
inline void recycle() noexcept { _usedSize.store(0, std::memory_order_relaxed); }
private:
void *doAllocate(size_t size, size_t alignment) noexcept;
void *_buffer{nullptr};
size_t _capacity{0};
size_t _alignment{1};
std::atomic<size_t> _usedSize{0};
};
// class may be padded
#if (CC_COMPILER == CC_COMPILER_MSVC)
#pragma warning(default : 4324)
#endif
} // namespace cc