-
Notifications
You must be signed in to change notification settings - Fork 959
Add FIFO and mutexQueue with bio.c refactored #2895
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
base: unstable
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## unstable #2895 +/- ##
============================================
- Coverage 72.45% 72.43% -0.03%
============================================
Files 129 131 +2
Lines 70526 70664 +138
============================================
+ Hits 51102 51182 +80
- Misses 19424 19482 +58
🚀 New features to boost your workflow:
|
770b442 to
cdf2818
Compare
Signed-off-by: Alina Liu <[email protected]>
cdf2818 to
fce4bd8
Compare
JimB123
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor comments on the unit tests. Looks good overall.
| fifo *q = fifoCreate(); | ||
| TEST_EXPECT(fifoLength(q) == 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These statements should be moved out of the DETECT_CRASH block.
You're trying to detect a crash inside of fifoPop. However, as is, the test will pass if there's a crash inside of fifoCreate.
| fifo *q = fifoCreate(); | ||
| TEST_EXPECT(fifoLength(q) == 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move these 2 statements outside of the DETECT_CRASH block.
| for (int i = 0; i < iterations; i++) exerciseFifo(); | ||
| long fifoMs = elapsedMs(timer); | ||
|
|
||
| TEST_EXPECT(listMs == fifoMs); /* This will fail, printing result */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line isn't needed, right?
|
Note that both of these are also prerequisites to #2878. |
|
Re: the code coverage. I see that there are unit tests which cover the code. From my read of this, it looks like the Codecov process is measuring code coverage based only on the TCL (integration) tests. In this case, I do see that the areas reported as uncovered by Codecov are all covered by the supplied unit tests. This looks ok to me. |
| * POSSIBILITY OF SUCH DAMAGE. | ||
| */ | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't remove unrelated line.
madolson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have seen this code before, and functionality is still consistent. Mostly some high level structural and naming things.
| void *fifoPeek(fifo *q); | ||
|
|
||
| /* Return and remove the first item from the queue. | ||
| * NOTE: asserts if the queue is empty. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had previous comments in the past that I would rather return some fixed defined value instead of crashing. This behavior is unintuitive and might lead to bugs if there aren't explicit checks here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This alternate would work, but makes the API a little less pretty:
// Return true if an item exists. If false, `item` is undefined.
bool fifoPeek(fifo *q, void **item);
bool fifoPop(fifo *q, void **item);Either way, the calling code is forced to perform checking. In either case, if the calling code doesn't check, that's an error.
Given that the calling code has to check anyway, I'm not seeing an advantage to making the API a little more awkward.
|
|
||
| typedef struct fifo fifo; | ||
|
|
||
| /* Create a new FIFO queue. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention for Valkey is that functions are documented primarily in the C function with a high level comment in the .h file. I know there is some contention here, but I think it's worse that we are documenting them in both places. I would rather them just be in the C file, there is no need to be inconsistent here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should be moving towards documenting an API separate from the code. Providing strong focus on what's contained (and not contained) in the API improves modularity. When users wants to understand the API, they shouldn't have to sift through the entire C file to find the documentation for the API.
Unless the core team feels strongly that the header files should be left undocumented, I'd like to leave the documentation in the header.
| /* Blindly overwrites target from source. */ | ||
| static void blindlyMoveFifoContents(fifo *target, fifo *source) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /* Blindly overwrites target from source. */ | |
| static void blindlyMoveFifoContents(fifo *target, fifo *source) { | |
| /* Overwrites target from source. */ | |
| static void overwriteFifoContents(fifo *target, fifo *source) { |
Blindly is a little confusing. Would rather use more inclusive language.
| * SPDX-License-Identifier: BSD-3-Clause | ||
| */ | ||
|
|
||
| /* FIFO - A high-performance FIFO queue implementation */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it mutex queue and fifo? Shouldn't this just be a queue? You use both queue and fifo interchangeably throughput the code without a clear distinction, let's just pick one term so all the code is clearer
Like:
/* Look at the first item in the queue (without removing it).
- NOTE: asserts if the queue is empty. */
void *fifoPeek(fifo *q) {
The code uses both fifo and queue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mutexQueue and fifo are 2 separate things, each with their own API and definition. We really don't need consistency between them. The mutex queue provides the concept of priority queuing, so it's not strictly FIFO. Under the hood, it's implemented using fifo, but that's not required - and could change without altering the API/functionality.
Regarding consistency within fifo.c, you're right, both terms are used. However since FIFO is a specific type of queue, it's not wrong or even (IMO) confusing to refer to a fifo as a queue, in the general sense.
Note that even the parameter is simply q in most cases. This should be clear to most.
| pthread_cond_wait(&bwd->bio_newjob_cond, &bwd->bio_mutex); | ||
| void bioDrainWorker(int type) { | ||
| while (bioPendingJobsOfType(type) > 0) { | ||
| usleep(1000); /* Sleep for 1ms and check again*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why so long of a sleep? I feel like we could sleep much less here.
| * | ||
| * ---------------------------------------------------------------------------- | ||
| * | ||
| * Copyright (c) 2009-2012, Redis Ltd. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you can't remove this. The vast majority of the code is the original implementation. We need to keep the reference for their copyright.
|
@ranshid Answering a question raised in the core team meeting 12/8/2025. The
Alternatively,
Note that for the IO threading use case of #2909, the general purpose queuing structures described here would likely have very different performance characteristics. |
Overview:
This PR introduces two data structures, FIFO and mutexQueue, as mentioned in #2878 , along with a significant refactoring of the existing bio utility that demonstrates their practical value.
New Creative Components:
Refactored Components:
bio.c- refactored using mutexQueue, with 56 lines of code eliminated while achieving more maintainable code.These prepare the prerequisite data structures for bgIteration.
Details:
FIFO -
fifo.h,fifo.c,test_fifo.cmutexQueue -
mutexqueue.h,mutexqueue.c,test_mutexqueue.cbio.cRefactoring:Before: Manual management of 3 synchronization primitives per queue
After: Single mutexQueue abstraction handles everything
Key improvements:
Simplified synchronization - mutexQueue encapsulates mutex locking and condition variable signaling, eliminating manual coordination in:
bioInit()bioSubmitJob()bioProcessBackgroundJobs()bioPendingJobsOfType()Thread-safe counters - Replaced manual locking of
bio_jobs_counterwith atomic operationsbioDrainWorker()now uses polling on the queue abstraction instead of managing low-level synchronizationTesting:
test_fifo.c: all pass,COMPARE_PERFORMANCE_TO_ADLISTshows the improvement = 78.69% (List: 122 ms, FIFO: 26 ms)test_mutexqueue.c:all passbio.cfunctionality preserved with refactored implementation