| 527 | } |
| 528 | |
| 529 | inline int ExecutionQueueBase::dereference() { |
| 530 | const uint64_t vref = _versioned_ref.fetch_sub( |
| 531 | 1, butil::memory_order_release); |
| 532 | const int32_t nref = _ref_of_vref(vref); |
| 533 | // We need make the fast path as fast as possible, don't put any extra |
| 534 | // code before this point |
| 535 | if (nref > 1) { |
| 536 | return 0; |
| 537 | } |
| 538 | const uint64_t id = _this_id; |
| 539 | if (__builtin_expect(nref == 1, 1)) { |
| 540 | const uint32_t ver = _version_of_vref(vref); |
| 541 | const uint32_t id_ver = _version_of_id(id); |
| 542 | // Besides first successful stop() adds 1 to version, one of |
| 543 | // those dereferencing nref from 1->0 adds another 1 to version. |
| 544 | // Notice "one of those": The wait-free address() may make ref of a |
| 545 | // version-unmatched slot change from 1 to 0 for mutiple times, we |
| 546 | // have to use version as a guard variable to prevent returning the |
| 547 | // executor to pool more than once. |
| 548 | if (__builtin_expect(ver == id_ver || ver == id_ver + 1, 1)) { |
| 549 | // sees nref:1->0, try to set version=id_ver+2,--nref. |
| 550 | // No retry: if version changes, the slot is already returned by |
| 551 | // another one who sees nref:1->0 concurrently; if nref changes, |
| 552 | // which must be non-zero, the slot will be returned when |
| 553 | // nref changes from 1->0 again. |
| 554 | // Example: |
| 555 | // stop(): --nref, sees nref:1->0 (1) |
| 556 | // try to set version=id_ver+2 (2) |
| 557 | // address(): ++nref, unmatched version (3) |
| 558 | // --nref, sees nref:1->0 (4) |
| 559 | // try to set version=id_ver+2 (5) |
| 560 | // 1,2,3,4,5 or 1,3,4,2,5: |
| 561 | // stop() succeeds, address() fails at (5). |
| 562 | // 1,3,2,4,5: stop() fails with (2), the slot will be |
| 563 | // returned by (5) of address() |
| 564 | // 1,3,4,5,2: stop() fails with (2), the slot is already |
| 565 | // returned by (5) of address(). |
| 566 | uint64_t expected_vref = vref - 1; |
| 567 | if (_versioned_ref.compare_exchange_strong( |
| 568 | expected_vref, _make_vref(id_ver + 2, 0), |
| 569 | butil::memory_order_acquire, |
| 570 | butil::memory_order_relaxed)) { |
| 571 | _on_recycle(); |
| 572 | // We don't return m immediately when the reference count |
| 573 | // reaches 0 as there might be in processing tasks. Instead |
| 574 | // _on_recycle would push a `stop_task' after which is executed |
| 575 | // m would be finally returned and reset |
| 576 | return 1; |
| 577 | } |
| 578 | return 0; |
| 579 | } |
| 580 | LOG(FATAL) << "Invalid id=" << id; |
| 581 | return -1; |
| 582 | } |
| 583 | LOG(FATAL) << "Over dereferenced id=" << id; |
| 584 | return -1; |
| 585 | } |
| 586 |
no test coverage detected