If the work_queue is absent, then we don't wait. While already applied or executed targets are normally not locked, if options contain any bits that are not already in cur_options, then the target is locked even in these states.
| 239 | // target is locked even in these states. |
| 240 | // |
| 241 | target_lock |
| 242 | lock_impl (action a, const target& ct, |
| 243 | optional<scheduler::work_queue> wq, |
| 244 | uint64_t options) |
| 245 | { |
| 246 | context& ctx (ct.ctx); |
| 247 | |
| 248 | assert (ctx.phase == run_phase::match); |
| 249 | |
| 250 | // Most likely the target's state is (count_touched - 1), that is, 0 or |
| 251 | // previously executed, so let's start with that. |
| 252 | // |
| 253 | size_t b (ctx.count_base ()); |
| 254 | size_t e (b + target::offset_touched - 1); |
| 255 | |
| 256 | size_t appl (b + target::offset_applied); |
| 257 | size_t busy (b + target::offset_busy); |
| 258 | |
| 259 | const target::opstate& cs (ct[a]); |
| 260 | atomic_count& task_count (cs.task_count); |
| 261 | |
| 262 | while (!task_count.compare_exchange_strong ( |
| 263 | e, |
| 264 | busy, |
| 265 | memory_order_acq_rel, // Synchronize on success. |
| 266 | memory_order_acquire)) // Synchronize on failure. |
| 267 | { |
| 268 | // Wait for the count to drop below busy if someone is already working |
| 269 | // on this target. |
| 270 | // |
| 271 | if (e >= busy) |
| 272 | { |
| 273 | // Check for dependency cycles. The cycle members should be evident |
| 274 | // from the "while ..." info lines that will follow. |
| 275 | // |
| 276 | if (dependency_cycle (a, ct)) |
| 277 | fail << "dependency cycle detected involving target " << ct; |
| 278 | |
| 279 | if (!wq) |
| 280 | return target_lock {a, nullptr, e - b, false}; |
| 281 | |
| 282 | // @@ TMP: remove if the queue_mark approach pans out. |
| 283 | // |
| 284 | #if 0 |
| 285 | // Don't work the queue if in the update during load during |
| 286 | // interrupting load. Failed that we may deadlock via dir_search(). |
| 287 | // |
| 288 | if (ctx.update_during_load > 1) |
| 289 | *wq = scheduler::work_none; |
| 290 | #endif |
| 291 | |
| 292 | // We also unlock the phase for the duration of the wait. Why? |
| 293 | // Consider this scenario: we are trying to match a dir{} target whose |
| 294 | // buildfile still needs to be loaded. Let's say someone else started |
| 295 | // the match before us. So we wait for their completion and they wait |
| 296 | // to switch the phase to load. Which would result in a deadlock |
| 297 | // unless we release the phase. |
| 298 | // |
no outgoing calls
no test coverage detected