no message
This commit is contained in:
46
cocos/base/threading/AutoReleasePool-apple.mm
Normal file
46
cocos/base/threading/AutoReleasePool-apple.mm
Normal 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
|
||||
35
cocos/base/threading/AutoReleasePool.cpp
Normal file
35
cocos/base/threading/AutoReleasePool.cpp
Normal 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
|
||||
44
cocos/base/threading/AutoReleasePool.h
Normal file
44
cocos/base/threading/AutoReleasePool.h
Normal 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
|
||||
44
cocos/base/threading/ConditionVariable.cpp
Normal file
44
cocos/base/threading/ConditionVariable.cpp
Normal 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
|
||||
54
cocos/base/threading/ConditionVariable.h
Normal file
54
cocos/base/threading/ConditionVariable.h
Normal 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
|
||||
46
cocos/base/threading/Event.h
Normal file
46
cocos/base/threading/Event.h
Normal 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
|
||||
321
cocos/base/threading/MessageQueue.cpp
Normal file
321
cocos/base/threading/MessageQueue.cpp
Normal 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
|
||||
725
cocos/base/threading/MessageQueue.h
Normal file
725
cocos/base/threading/MessageQueue.h
Normal 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
|
||||
57
cocos/base/threading/ReadWriteLock.h
Normal file
57
cocos/base/threading/ReadWriteLock.h
Normal 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
|
||||
45
cocos/base/threading/Semaphore.cpp
Normal file
45
cocos/base/threading/Semaphore.cpp
Normal 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
|
||||
45
cocos/base/threading/Semaphore.h
Normal file
45
cocos/base/threading/Semaphore.h
Normal 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
|
||||
82
cocos/base/threading/ThreadPool.cpp
Normal file
82
cocos/base/threading/ThreadPool.cpp
Normal 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
|
||||
85
cocos/base/threading/ThreadPool.h
Normal file
85
cocos/base/threading/ThreadPool.h
Normal 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
|
||||
47
cocos/base/threading/ThreadSafeCounter.h
Normal file
47
cocos/base/threading/ThreadSafeCounter.h
Normal 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
|
||||
71
cocos/base/threading/ThreadSafeLinearAllocator.cpp
Normal file
71
cocos/base/threading/ThreadSafeLinearAllocator.cpp
Normal 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
|
||||
78
cocos/base/threading/ThreadSafeLinearAllocator.h
Normal file
78
cocos/base/threading/ThreadSafeLinearAllocator.h
Normal 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
|
||||
Reference in New Issue
Block a user