-
Notifications
You must be signed in to change notification settings - Fork 6
Add Concurrency option and MPSC support using farbot. #18
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
Conversation
f0511b0 to
06f3f56
Compare
06f3f56 to
13f13fe
Compare
cjappl
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.
Thank you for taking the time for this work!! Let's chat more about some of my comments and see what we can come up with. I like the direction it is going, but we should work out some of the bigger picture.
test/test_rtlog.cpp
Outdated
|
|
||
| SUBCASE("Enqueue more than capacity and get an error") { | ||
| const auto maxNumMessages = 10; | ||
| const auto maxNumMessages = 16; |
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.
Is there a reason this changed? I'm surprised
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'm going to bed soon-ish, so let me put quick comments only for now.)
Yes - farbot FIFO does not accept the size of buffers that is not 2^n. Probably I should put some caution on the README or the header or somewhere.
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.
Maybe we can enforce it via a static assert/restriction on the template param
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 don't think we use this class for now, but I have added static_assert in the MPSC wrapper constructor.
| FetchContent_MakeAvailable(ReaderWriterQueue) | ||
| endif() | ||
|
|
||
| if (NOT TARGET farbot) |
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.
THANK YOU for this contribution and taking the time to write it up.
I have a couple of concerns, which maybe you can help me figure out.
First, I never liked how I downloaded concurrentqueue "secretly" for the users. I think a (maybe better?) approach would be the users providing their own queue, perhaps wrapped in some External Polymorphism package like you've done here.
Now that we have two options (farbot AND concurrentqueue) we are downloading even more dependencies "secretly" for the user. It feels a little dirty, and I don't know what we should do about it.
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.
Perhaps one solution to this is to pass in the queue that the suer wants, but just provide two back ends that can be hidden behind some optional cmake
RTSAN_BUILD_FARBOT_BACKEND
RTSAN_BUILD_CONCURRENTQUEUE_BACKEND
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'm completely spitballing here. I just want to make sure before we extend to 2 backends that we have a plan for when we inevitably need to add a 3rd
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 understand your concern. Once thing I would point out is that we would probably like to have those options alongside and not exclusive - one would like to have both kinds of loggers (also to avoid possible build config conflicts between sub-projects).
(BTW I didn't replace readerwriterqueue with concurrentqueue, so only farbot version is added. I can add that too if you want, but it's not going to be RT log anymore ;-) )
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'm digging into this just a little bit and I remember one of the sticking points that prevented me from doing this in the past.
If you change the args to have a QueueType:
template <typename LogData, size_t MaxNumMessages, size_t MaxMessageLength,
std::atomic<std::size_t>& SequenceNumber, typename QueueType>
This becomes tricky because QueueType needs to depend on InternalLogData.
(I'm not sure immediately how to re-arrange to get around this problem)
|
|
||
| InternalLogData value; | ||
| while (mQueue.try_dequeue(value)) { | ||
| while (mQueue->tryDequeue(value)) { |
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.
My other slight concern is this use of dynamic memory here. While it is perfectly real-time safe, it is also slower (probably) than the one that holds the queue on the stack.
Is there any way to have this be template based instead of inheritance based?
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.
Alright. It is probably possible. We would still need those wrapper classes to make code consistent though (yet removing inheritance).
include/rtlog/rtlog.h
Outdated
| }; | ||
| class InternalQueueMPSC : public InternalQueue { | ||
| farbot::fifo<InternalLogData, farbot::fifo_options::concurrency::single, | ||
| farbot::fifo_options::concurrency::multiple> |
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.
Smaller comment that we should address after the bigger comments above - let's make the behavior on push/pop explicit:
fifo_options::full_empty_failure_mode::return_false_on_full_or_empty,
fifo_options::full_empty_failure_mode::overwrite_or_return_default
Are the options. I think both are wait free on write, I guess I would opt for return_false_on_full_or_empty so the user can decide what they want to do if the queue fails and data loss happens
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.
done.
| Concurrency == QueueConcurrency::Single_Producer_Single_Consumer | ||
| ? (std::unique_ptr<InternalQueue>) | ||
| std::make_unique<InternalQueueSPSC>() | ||
| : std::make_unique<InternalQueueMPSC>()}; |
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.
nit:
perhaps a case statement would be more clear and more extensible for the future
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.
agreed!
|
|
||
| constexpr auto MAX_LOG_MESSAGE_LENGTH = 256; | ||
| constexpr auto MAX_NUM_LOG_MESSAGES = 100; | ||
| constexpr auto MAX_NUM_LOG_MESSAGES = 128; |
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.
Is there a reason this changed?
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.
Yes, same as maxNumMessages.
|
@atsushieno - would you check out #19 when you get a chance? Possibly even pull it down and see if you can easily use it with farbot? This is just a draft of how this COULD work, not necessarily the final version |
|
I'm trying plain #19 without my changes, but I couldn't get this compiled on my macOS environment: I'm not sure how portable the use of those template template parameters are. (I guess it should be fairly standard?) |
|
According to my experiment at Compiler Explorer, clang earlier than 19 had this issue. clang-19 successfully compiles it. https://godbolt.org/z/o755bnvPh I guess this new template template parameters code is going to be blocking until clang-19 becomes broadly available. My xcode toolchain is not the latest 16.2 (it is 16.0) and I kinda have reason to stick to it (safe for language bindings such as kotlin-native and tooling). Maybe some other people too. edit: in xcode 16.0 the clang version is 16.0.0 (clang-1600.0.26.3) |
4ab0c94 to
1d2d9bb
Compare
1d2d9bb to
d1839e5
Compare
Yeah, we should for sure do something that works in compilers older than 19. Thanks for looking. Let me see if there is a different way to do the same thing! |
context: #6 #14
It would not be a fun task to make up a customization setup for queue backends, so I made a minimum-ish one. The behavior should be backward compatible.