| 119 | } |
| 120 | |
| 121 | Task EnqueueTask(Task t, bool is_blocking) { |
| 122 | mutex* mu = nullptr; |
| 123 | Queue* task_queue = nullptr; |
| 124 | thread_local int64 closure_counter = 0; |
| 125 | |
| 126 | if (!is_blocking) { |
| 127 | int queue_index = ++closure_counter % non_blocking_work_sharding_factor_; |
| 128 | task_queue = &(non_blocking_work_queues_[queue_index]->queue); |
| 129 | mu = &non_blocking_work_queues_[queue_index]->queue_op_mu; |
| 130 | } else { |
| 131 | task_queue = &blocking_work_queue_; |
| 132 | mu = &blocking_queue_op_mu_; |
| 133 | } |
| 134 | |
| 135 | { |
| 136 | mutex_lock l(*mu); |
| 137 | // For a given queue, only one thread can call PushFront. |
| 138 | t = task_queue->PushFront(std::move(t)); |
| 139 | } |
| 140 | |
| 141 | // Only wake up the thread that can take tasks from both blocking and |
| 142 | // non-blocking queues. The rational is that we don't want to wake up more |
| 143 | // threads than the available physical cores for them to compete for |
| 144 | // resource. The non-blocking threads are used only to compensate for |
| 145 | // threads that may be blocked on some tasks. There is less need to |
| 146 | // proactively wake up those threads. |
| 147 | static int max_rank_to_wakeup = static_cast<int>(ParamFromEnvWithDefault( |
| 148 | "TF_RUN_HANDLER_MAX_RANK_TO_WAKE_UP", kMaxConcurrentHandlers)); |
| 149 | if (max_rank_to_wakeup > 0 && |
| 150 | rank_.load(std::memory_order_relaxed) <= max_rank_to_wakeup) { |
| 151 | mutex_lock l(waiters_mu_); |
| 152 | queue_waiters_.next->cv.notify_one(); |
| 153 | } |
| 154 | VLOG(3) << "Added " << (is_blocking ? "inter" : "intra") << " work from " |
| 155 | << traceme_id_.load(std::memory_order_relaxed); |
| 156 | return t; |
| 157 | } |
| 158 | |
| 159 | Task PopBlockingTask() { return blocking_work_queue_.PopBack(); } |
| 160 |
no test coverage detected