-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update lock-free queue for multi-threading, add remove task process in the wait method of Task. #436
Update lock-free queue for multi-threading, add remove task process in the wait method of Task. #436
Changes from 5 commits
d9be4c5
f042c88
f00d2dd
27ae210
3d39078
41c5136
bf1ea98
9fd14e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,21 +33,27 @@ | |
|
||
namespace tgfx { | ||
static constexpr size_t CACHELINE_SIZE = 64; | ||
// QUEUE_SIZE needs to be a power of 2, otherwise the implementation of GetIndex needs to be changed. | ||
static constexpr uint32_t QUEUE_SIZE = 1024; | ||
|
||
inline uint32_t GetIndex(uint32_t position) { | ||
return position & (QUEUE_SIZE - 1); | ||
} | ||
|
||
template <typename T> | ||
class LockFreeQueue { | ||
public: | ||
LockFreeQueue() { | ||
queuePool = reinterpret_cast<T*>(std::calloc(QUEUE_SIZE, sizeof(T))); | ||
/** | ||
* The capacity needs to be a power of 2, otherwise, it will be automatically set to the nearest | ||
* power of 2 larger than the capacity. | ||
* @param capacity | ||
*/ | ||
explicit LockFreeQueue(uint32_t capacity) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个Capacity还有一种写法,可以声明到template的参数里, template <typename T, size_t N>, 这样就可以直接声明了。 |
||
if ((capacity & (capacity - 1)) != 0) { | ||
_capacity = 1; | ||
while (_capacity < capacity) { | ||
_capacity <<= 1; | ||
} | ||
} else { | ||
_capacity = capacity; | ||
} | ||
queuePool = reinterpret_cast<T*>(std::calloc(_capacity, sizeof(T))); | ||
if (queuePool == nullptr) { | ||
LOGE("LockFreeQueue init Failed!\n"); | ||
return; | ||
ABORT("LockFreeQueue init Failed!\n"); | ||
} | ||
} | ||
|
||
|
@@ -59,9 +65,6 @@ class LockFreeQueue { | |
} | ||
|
||
T dequeue() { | ||
if (queuePool == nullptr) { | ||
return nullptr; | ||
} | ||
uint32_t newHead = 0; | ||
uint32_t oldHead = head.load(std::memory_order_relaxed); | ||
T element = nullptr; | ||
|
@@ -71,11 +74,11 @@ class LockFreeQueue { | |
if (newHead == tailPosition.load(std::memory_order_acquire)) { | ||
return nullptr; | ||
} | ||
element = queuePool[GetIndex(newHead)]; | ||
element = queuePool[getIndex(newHead)]; | ||
} while (!head.compare_exchange_weak(oldHead, newHead, std::memory_order_acq_rel, | ||
std::memory_order_relaxed)); | ||
|
||
queuePool[GetIndex(newHead)] = nullptr; | ||
queuePool[getIndex(newHead)] = nullptr; | ||
|
||
uint32_t newHeadPosition = 0; | ||
uint32_t oldHeadPosition = headPosition.load(std::memory_order_relaxed); | ||
|
@@ -87,22 +90,19 @@ class LockFreeQueue { | |
} | ||
|
||
bool enqueue(const T& element) { | ||
if (queuePool == nullptr) { | ||
return false; | ||
} | ||
uint32_t newTail = 0; | ||
uint32_t oldTail = tail.load(std::memory_order_relaxed); | ||
|
||
do { | ||
newTail = oldTail + 1; | ||
if (GetIndex(newTail) == GetIndex(headPosition.load(std::memory_order_acquire))) { | ||
LOGI("The queue has reached its maximum capacity, capacity: %u!\n", QUEUE_SIZE); | ||
if (getIndex(oldTail) == getIndex(headPosition.load(std::memory_order_acquire))) { | ||
LOGI("The queue has reached its maximum capacity, capacity: %u!\n", _capacity); | ||
return false; | ||
} | ||
newTail = oldTail + 1; | ||
} while (!tail.compare_exchange_weak(oldTail, newTail, std::memory_order_acq_rel, | ||
std::memory_order_relaxed)); | ||
|
||
queuePool[GetIndex(oldTail)] = std::move(element); | ||
queuePool[getIndex(oldTail)] = std::move(element); | ||
|
||
uint32_t newTailPosition = 0; | ||
uint32_t oldTailPosition = tailPosition.load(std::memory_order_relaxed); | ||
|
@@ -115,6 +115,7 @@ class LockFreeQueue { | |
|
||
private: | ||
T* queuePool = nullptr; | ||
uint32_t _capacity = 0; | ||
#ifdef DISABLE_ALIGNAS | ||
// head indicates the position after requesting space. | ||
std::atomic<uint32_t> head = {0}; | ||
|
@@ -134,6 +135,10 @@ class LockFreeQueue { | |
// tailPosition indicates the position after filling data. | ||
alignas(CACHELINE_SIZE) std::atomic<uint32_t> tailPosition = {1}; | ||
#endif | ||
|
||
uint32_t getIndex(uint32_t position) { | ||
return position & (_capacity - 1); | ||
} | ||
}; | ||
|
||
} // namespace tgfx |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,42 +34,51 @@ std::shared_ptr<Task> Task::Run(std::function<void()> block) { | |
Task::Task(std::function<void()> block) : block(std::move(block)) { | ||
} | ||
|
||
bool Task::executing() { | ||
std::lock_guard<std::mutex> autoLock(locker); | ||
return _executing; | ||
} | ||
|
||
bool Task::cancelled() { | ||
std::lock_guard<std::mutex> autoLock(locker); | ||
return _cancelled; | ||
} | ||
|
||
bool Task::finished() { | ||
std::lock_guard<std::mutex> autoLock(locker); | ||
return !_executing && !_cancelled; | ||
} | ||
|
||
void Task::wait() { | ||
std::unique_lock<std::mutex> autoLock(locker); | ||
if (!_executing) { | ||
auto oldStatus = taskStatus.load(std::memory_order_relaxed); | ||
if (oldStatus == TaskStatus::cancelled || oldStatus == TaskStatus::finished) { | ||
return; | ||
} | ||
condition.wait(autoLock); | ||
// If wait() is called from the thread pool, all threads might block, leaving no thread to execute | ||
// this task. To avoid deadlock, execute the task directly on the current thread if it's queued. | ||
if (oldStatus == TaskStatus::queuing) { | ||
if (taskStatus.compare_exchange_weak(oldStatus, TaskStatus::executing, | ||
std::memory_order_acq_rel, std::memory_order_relaxed)) { | ||
block(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这句之前加一行注释:If wait() is called from the thread pool, all threads might block, leaving no thread to execute this task. To avoid deadlock, execute the task directly on the current thread if it's queued. |
||
oldStatus = TaskStatus::executing; | ||
while (!taskStatus.compare_exchange_weak( | ||
oldStatus, TaskStatus::finished, std::memory_order_acq_rel, std::memory_order_relaxed)) | ||
; | ||
return; | ||
} | ||
} | ||
std::unique_lock<std::mutex> autoLock(locker); | ||
if (taskStatus.load(std::memory_order_acquire) == TaskStatus::executing) { | ||
condition.wait(autoLock); | ||
} | ||
} | ||
|
||
void Task::cancel() { | ||
std::unique_lock<std::mutex> autoLock(locker); | ||
if (!_executing) { | ||
return; | ||
auto currentStatus = taskStatus.load(std::memory_order_relaxed); | ||
if (currentStatus == TaskStatus::queuing) { | ||
while (!taskStatus.compare_exchange_weak(currentStatus, TaskStatus::cancelled, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里不能while,如果不是queuing状态,就是不能cancel了。 |
||
std::memory_order_acq_rel, std::memory_order_relaxed)) | ||
; | ||
} | ||
_executing = false; | ||
_cancelled = true; | ||
} | ||
|
||
void Task::execute() { | ||
block(); | ||
std::lock_guard<std::mutex> auoLock(locker); | ||
_executing = false; | ||
condition.notify_all(); | ||
auto oldStatus = taskStatus.load(std::memory_order_relaxed); | ||
if (oldStatus == TaskStatus::queuing && | ||
taskStatus.compare_exchange_weak(oldStatus, TaskStatus::executing, std::memory_order_acq_rel, | ||
std::memory_order_relaxed)) { | ||
block(); | ||
oldStatus = TaskStatus::executing; | ||
while (!taskStatus.compare_exchange_weak(oldStatus, TaskStatus::finished, | ||
std::memory_order_acq_rel, std::memory_order_relaxed)) | ||
; | ||
std::unique_lock<std::mutex> autoLock(locker); | ||
condition.notify_all(); | ||
} | ||
} | ||
} // namespace tgfx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
加个只读方法,不能直接这么暴露出来。