Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix mpsc DynamicBoundedQueue try_enqueue could fail even if the queue is empty #2368

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

wsu13
Copy link

@wsu13 wsu13 commented Jan 16, 2025

Problem:

We used folly::DMPSCQueue<> q(1024) and encountered try_enqueue() return false frequently even if the queue is empty.

Debug:

I logged the three atomic value debit, credit, transfer before and after try_enqueue() operation, and saw the issue.

01-14   20:45:07.562839 INFO    17569   ***.cc:111 trace_id:***548   Submit task to worker1_12 Ok debit=1124, credit=2, transfer=1122, qsize=0, after push debit=1125, credit=2, transfer=1122
01-14   20:45:07.562888 INFO    17576   ***.cc:111 trace_id:***055   Submit task to worker1_12 Ok debit=1125, credit=3, transfer=1122, qsize=0, after push debit=1126, credit=3, transfer=1122
01-14   20:45:07.568038 INFO    17572   ***.cc:111 trace_id:***618   Submit task to worker1_12 Ok debit=1126, credit=4, transfer=1122, qsize=0, after push debit=7, credit=5, transfer=0
01-14   20:45:07.568161 INFO    17570   ***.cc:111 trace_id:***736   Submit task to worker1_12 Fail debit=1126, credit=4, transfer=1122, qsize=0, after push debit=7, credit=5, transfer=0

We use version v2018.08.20.00 the threshold_ is not rounded up, so the capacity is 1024+102 =1126.

  bool tryReduceDebit() noexcept {
    Weight w = takeTransfer();
    if (w > 0) {
      subDebit(w);
    }
    return w > 0;
  }

  Weight takeTransfer() noexcept {
    Weight w = getTransfer();
    if (w > 0) {
      w = transfer_.exchange(0, std::memory_order_acq_rel);
    }
    return w;
  }

At this case, capacity=1126, debit=1126, credit=4, transfer=1122, so the queue is actually empty.

Now there is 2 concurrent producers both do takeTransfer().
Producer_1 succeeded take transfer value, but haven't subDebit(w).
producer_2 saw transfer value is 0, and return, and check debit + weight > capacity, then try_enqueue() failed.

The unit_test in the pr can reproduce the case.

Fix:

Let prodcuer do tryReduceDebit() earlier, when debit reaches half capacity.

@facebook-github-bot
Copy link
Contributor

Hi @wsu13!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@facebook-github-bot
Copy link
Contributor

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@wsu13 wsu13 changed the title fix mpsc DynamicBoundedQueue try_enqueue could fail even if queue is empty fix mpsc DynamicBoundedQueue try_enqueue could fail even if the queue is empty Jan 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants