|
17 | 17 | #include "async-uv.h" |
18 | 18 | #include <nd-logger.h> |
19 | 19 | #include <uv.h> |
20 | | -#include <functional> |
21 | 20 | #include "debug-mem-trace.h" |
22 | 21 |
|
23 | | -std::queue<AsyncUV::Task> AsyncUV::queue_; |
24 | | -std::mutex AsyncUV::queue_mutex_; |
| 22 | +struct AsyncUV::LoopData { |
| 23 | + uv_loop_t* loop{nullptr}; |
| 24 | + uv_async_t* handle{nullptr}; |
| 25 | + std::queue<Task> queue; |
| 26 | + std::mutex queue_mutex; |
| 27 | +}; |
25 | 28 |
|
26 | | -AsyncUV::AsyncUV(uv_loop_t* loop, Task task) : uv_h_(nullptr), task_(task) { |
27 | | - if (loop && task) { |
28 | | - Init(loop, task); |
29 | | - } |
| 29 | +std::map<uv_loop_t*, AsyncUV::LoopData*> AsyncUV::loop_data_; |
| 30 | +std::mutex AsyncUV::loop_data_mutex_; |
| 31 | +std::queue<AsyncUV::Task> AsyncUV::pending_queue_; |
| 32 | +std::mutex AsyncUV::pending_queue_mutex_; |
| 33 | + |
| 34 | +AsyncUV::AsyncUV(uv_loop_t* loop, Task task) : loop_(loop), task_(task) { |
30 | 35 | TRACE_ADD(ASYNC, this); |
31 | 36 | } |
32 | 37 |
|
33 | 38 | AsyncUV::~AsyncUV() { |
34 | | - if (!uv_h_) { |
| 39 | + TRACE(ASYNC, "~AsyncUV"); |
| 40 | + TRACE_REMOVE(ASYNC, this); |
| 41 | +} |
| 42 | + |
| 43 | +void AsyncUV::OnAsyncCalled(uv_async_t* handle) { |
| 44 | + auto* loop_data = static_cast<LoopData*>(handle->data); |
| 45 | + if (loop_data == nullptr) { |
35 | 46 | return; |
36 | 47 | } |
37 | 48 |
|
38 | | - TRACE(ASYNC, "~AsyncUV"); |
39 | | - TRACE_REMOVE(ASYNC, this); |
| 49 | + std::queue<Task> tasks_to_run; |
| 50 | + { |
| 51 | + std::lock_guard<std::mutex> lock(loop_data->queue_mutex); |
| 52 | + loop_data->queue.swap(tasks_to_run); |
| 53 | + } |
40 | 54 |
|
41 | | - uv_close(reinterpret_cast<uv_handle_t*>(uv_h_), [](uv_handle_t* handle) { |
42 | | - TRACE(ASYNC, "~uv_close"); |
43 | | - TRACE_REMOVE(ASYNC_UV, handle); |
44 | | - delete reinterpret_cast<uv_async_t*>(handle); |
45 | | - }); |
| 55 | + while (!tasks_to_run.empty()) { |
| 56 | + Task& task = tasks_to_run.front(); |
| 57 | + if (task) { |
| 58 | + TRACE(MSGPORT, "run task"); |
| 59 | + task(handle); |
| 60 | + TRACE(MSGPORT, "/run task"); |
| 61 | + } |
| 62 | + tasks_to_run.pop(); |
| 63 | + } |
46 | 64 | } |
47 | 65 |
|
48 | | -bool AsyncUV::Send(uv_loop_t* loop, Task task) { |
| 66 | +AsyncUV::LoopData* AsyncUV::GetLoopData(uv_loop_t* loop, |
| 67 | + bool create_if_not_found) { |
| 68 | + std::lock_guard<std::mutex> lock(loop_data_mutex_); |
| 69 | + auto it = loop_data_.find(loop); |
| 70 | + if (it != loop_data_.end()) { |
| 71 | + return it->second; |
| 72 | + } |
| 73 | + |
| 74 | + if (!create_if_not_found || loop == nullptr) { |
| 75 | + return nullptr; |
| 76 | + } |
| 77 | + |
| 78 | + auto* loop_data = new LoopData(); |
| 79 | + loop_data->loop = loop; |
| 80 | + loop_data->handle = new uv_async_t(); |
| 81 | + loop_data->handle->data = loop_data; |
| 82 | + uv_async_init(loop, loop_data->handle, OnAsyncCalled); |
| 83 | + uv_unref(reinterpret_cast<uv_handle_t*>(loop_data->handle)); |
| 84 | + TRACE_ADD(ASYNC_UV, loop_data->handle); |
| 85 | + loop_data_[loop] = loop_data; |
| 86 | + return loop_data; |
| 87 | +} |
| 88 | + |
| 89 | +bool AsyncUV::InitPerThread(uv_loop_t* loop) { |
49 | 90 | if (loop == nullptr) { |
| 91 | + return false; |
| 92 | + } |
| 93 | + |
| 94 | + return GetLoopData(loop, true) != nullptr; |
| 95 | +} |
| 96 | + |
| 97 | +void AsyncUV::CleanupPerThread(uv_loop_t* loop) { |
| 98 | + std::lock_guard<std::mutex> lock(loop_data_mutex_); |
| 99 | + auto it = loop_data_.find(loop); |
| 100 | + if (it == loop_data_.end()) { |
| 101 | + return; |
| 102 | + } |
| 103 | + |
| 104 | + LoopData* loop_data = it->second; |
| 105 | + loop_data_.erase(it); |
| 106 | + |
| 107 | + if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(loop_data->handle))) { |
| 108 | + uv_close(reinterpret_cast<uv_handle_t*>(loop_data->handle), |
| 109 | + [](uv_handle_t* handle) { |
| 110 | + TRACE(ASYNC, "~uv_close"); |
| 111 | + TRACE_REMOVE(ASYNC_UV, handle); |
| 112 | + auto* loop_data = static_cast<LoopData*>(handle->data); |
| 113 | + delete loop_data->handle; |
| 114 | + delete loop_data; |
| 115 | + }); |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +bool AsyncUV::Send(uv_loop_t* loop, Task task) { |
| 120 | + if (loop == nullptr || !task) { |
50 | 121 | TRACE(MSGPORT, "invalid loop"); |
51 | 122 | return false; |
52 | 123 | } |
53 | | - return (new AsyncUV(loop, task))->Send(); |
| 124 | + |
| 125 | + LoopData* loop_data = GetLoopData(loop, true); |
| 126 | + if (loop_data == nullptr) { |
| 127 | + return false; |
| 128 | + } |
| 129 | + |
| 130 | + { |
| 131 | + std::lock_guard<std::mutex> lock(loop_data->queue_mutex); |
| 132 | + loop_data->queue.push(std::move(task)); |
| 133 | + } |
| 134 | + |
| 135 | + uv_async_send(loop_data->handle); |
| 136 | + return true; |
54 | 137 | } |
55 | 138 |
|
56 | 139 | size_t AsyncUV::EnqueueTask(Task task) { |
57 | 140 | TRACE(MSGPORT, "EnqueueTask"); |
58 | | - std::lock_guard<std::mutex> lock(queue_mutex_); |
59 | | - queue_.push(task); |
60 | | - return queue_.size(); |
| 141 | + std::lock_guard<std::mutex> lock(pending_queue_mutex_); |
| 142 | + pending_queue_.push(std::move(task)); |
| 143 | + return pending_queue_.size(); |
61 | 144 | } |
62 | 145 |
|
63 | 146 | bool AsyncUV::DrainPendingTasks(uv_loop_t* loop) { |
64 | | - TRACE(MSGPORT, "DrainPendingTasks"); |
65 | | - std::lock_guard<std::mutex> lock(queue_mutex_); |
66 | | - TRACE(MSGPORT, "drain pending tasks %zu", queue_.size()); |
67 | | - |
68 | 147 | if (loop == nullptr) { |
69 | 148 | TRACE(MSGPORT, "invalid loop"); |
70 | 149 | return false; |
71 | 150 | } |
72 | 151 |
|
73 | | - while (!queue_.empty()) { |
74 | | - AsyncUV::Send(loop, queue_.front()); |
75 | | - queue_.pop(); |
| 152 | + TRACE(MSGPORT, "DrainPendingTasks"); |
| 153 | + std::queue<Task> pending_tasks; |
| 154 | + { |
| 155 | + std::lock_guard<std::mutex> lock(pending_queue_mutex_); |
| 156 | + TRACE(MSGPORT, "drain pending tasks %zu", pending_queue_.size()); |
| 157 | + pending_queue_.swap(pending_tasks); |
| 158 | + } |
| 159 | + |
| 160 | + while (!pending_tasks.empty()) { |
| 161 | + AsyncUV::Send(loop, std::move(pending_tasks.front())); |
| 162 | + pending_tasks.pop(); |
76 | 163 | } |
77 | 164 | TRACE(MSGPORT, "/drain pending tasks"); |
78 | 165 | return true; |
79 | 166 | } |
80 | 167 |
|
81 | 168 | void AsyncUV::DeletePendingTasks() { |
82 | 169 | TRACE(MSGPORT, "DeletePendingTasks"); |
83 | | - std::lock_guard<std::mutex> lock(queue_mutex_); |
84 | | - TRACE(MSGPORT, "delete pending tasks %zu", queue_.size()); |
85 | | - if (!queue_.empty()) { |
| 170 | + std::lock_guard<std::mutex> lock(pending_queue_mutex_); |
| 171 | + TRACE(MSGPORT, "delete pending tasks %zu", pending_queue_.size()); |
| 172 | + if (!pending_queue_.empty()) { |
86 | 173 | std::queue<Task> empty; |
87 | | - std::swap(queue_, empty); |
| 174 | + std::swap(pending_queue_, empty); |
88 | 175 | } |
89 | 176 | } |
90 | 177 |
|
91 | 178 | bool AsyncUV::IsPendingTasksEmpty() { |
92 | 179 | TRACE(MSGPORT, "IsPendingTasksEmpty"); |
93 | | - std::lock_guard<std::mutex> lock(queue_mutex_); |
94 | | - return queue_.empty(); |
| 180 | + std::lock_guard<std::mutex> lock(pending_queue_mutex_); |
| 181 | + return pending_queue_.empty(); |
95 | 182 | } |
96 | 183 |
|
97 | 184 | void AsyncUV::Init(uv_loop_t* loop, Task task) { |
| 185 | + loop_ = loop; |
98 | 186 | task_ = task; |
99 | | - |
100 | | - uv_h_ = new uv_async_t(); |
101 | | - uv_h_->data = this; |
102 | | - uv_async_init(loop, uv_h_, [](uv_async_t* handle) { |
103 | | - auto event = static_cast<AsyncUV*>(handle->data); |
104 | | - if (event->task_) { |
105 | | - TRACE(MSGPORT, "run task"); |
106 | | - event->task_(handle); |
107 | | - TRACE(MSGPORT, "/run task"); |
108 | | - } |
109 | | - delete event; |
110 | | - }); |
111 | | - TRACE_ADD(ASYNC_UV, uv_h_); |
112 | 187 | } |
113 | 188 |
|
114 | 189 | bool AsyncUV::Send() { |
115 | | - if (!uv_h_) { |
| 190 | + if (loop_ == nullptr || !task_) { |
116 | 191 | return false; |
117 | 192 | } |
118 | | - uv_async_send(uv_h_); |
119 | | - return true; |
| 193 | + return Send(loop_, std::move(task_)); |
120 | 194 | } |
0 commit comments