| 256 | #ifdef ARROW_ENABLE_THREADING |
| 257 | |
| 258 | void SerialExecutor::RunLoop() { |
| 259 | // This is called from the SerialExecutor's main thread, so the |
| 260 | // state is guaranteed to be kept alive. |
| 261 | std::unique_lock<std::mutex> lk(state_->mutex); |
| 262 | state_->current_thread = std::this_thread::get_id(); |
| 263 | // If paused we break out immediately. If finished we only break out |
| 264 | // when all work is done. |
| 265 | while (!state_->paused && !(state_->finished && state_->task_queue.empty())) { |
| 266 | // The inner loop is to check if we need to sleep (e.g. while waiting on some |
| 267 | // async task to finish from another thread pool). We still need to check paused |
| 268 | // because sometimes we will pause even with work leftover when processing |
| 269 | // an async generator |
| 270 | while (!state_->paused && !state_->task_queue.empty()) { |
| 271 | Task task = std::move(const_cast<Task&>(state_->task_queue.top().task)); |
| 272 | state_->task_queue.pop(); |
| 273 | lk.unlock(); |
| 274 | if (!task.stop_token.IsStopRequested()) { |
| 275 | std::move(task.callable)(); |
| 276 | } else { |
| 277 | if (task.stop_callback) { |
| 278 | std::move(task.stop_callback)(task.stop_token.Poll()); |
| 279 | } |
| 280 | // Can't break here because there may be cleanup tasks down the chain we still |
| 281 | // need to run. |
| 282 | } |
| 283 | lk.lock(); |
| 284 | } |
| 285 | // In this case we must be waiting on work from external (e.g. I/O) executors. Wait |
| 286 | // for tasks to arrive (typically via transferred futures). |
| 287 | state_->wait_for_tasks.wait(lk, [&] { |
| 288 | return state_->paused || state_->finished || !state_->task_queue.empty(); |
| 289 | }); |
| 290 | } |
| 291 | state_->current_thread = {}; |
| 292 | } |
| 293 | #else // ARROW_ENABLE_THREADING |
| 294 | bool SerialExecutor::RunTasksOnAllExecutors() { |
| 295 | auto globalState = GetSerialExecutorGlobalState(); |
no test coverage detected