-
Notifications
You must be signed in to change notification settings - Fork 16
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
iox-23 refine and update publisher and subscriber api #24
iox-23 refine and update publisher and subscriber api #24
Conversation
let (subscriber, sample_receive_token) = | ||
SubscriberBuilder::<Counter>::new("Radar", "FrontLeft", "Counter") | ||
.queue_capacity(5) | ||
.create_mt()?; |
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.
What does the suffix _mt
mean and why does the publisher does not have such a suffix? When it has something todo with multithreaded wouldn't be create_threadsafe
somehow more fitting?
Or does the subscriber come with multiple threads?
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.
_mt
means that a subscribed Subscriber
will be created which can receive samples in a different thread than the one which created the subscriber. Internally the pointer to the C++ subscriber is stored in a smart pointer similar to shared_ptr
. In Rust there are two types Rc
and Arc
. The latter has an atomic ref counting and the former not. This means the latter can be shared across threads and the latter not. Since one only pays for what one uses, there are two differrnt types for a subscriber, st::Subscriber
and mt::Subscriber
.
use std::thread; | ||
use std::time::Duration; | ||
|
||
#[repr(C)] | ||
struct CounterTopic { | ||
struct Counter { |
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.
Would it be possible to outsource this into a separate file so that every publisher and subscriber share the same topic definition.
Otherwise it can become easily inconsistent.
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.
Will do this in a follow up PR. I need to refactor the example a bit in order to share files between the examples. While it is dead easy to add simple examples with cargo, by default it expects one file per example. It is possible to add more complex example but it is ... well ... also a bit more complex
.queue_capacity(1) | ||
.history_request(1) | ||
.build() | ||
.create_without_subscribe() |
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.
Just out of curiosity, we have create
, create_mt
(for multithreaded), create_without_subscriber
, create_without_subscribe_mt
?
The point I wanna make is, I would put everything into the builder and just have a single create so it is nice and consistent and I remember a special someone nagging me about consistency with m_
so I want to return the favor 😄
So the handling I imagine is something like this:
pub fn new() -> InactiveSubscriber<Self> {
SubscriberBuilder::<Self>::new("Introspection", "RouDi_ID", "MemPool")
.queue_capacity(1)
.history_request(1)
.threadsafe(false)
.subscribe_on_create(false)
.build()
.create_without_subscribe()
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.
But then you don't need a builder or you need a second builder for InactiveSubscriber
since new
does not have any parameter. I explained the rationale further down.
self.publisher.ffi_pub.stop_offer(); | ||
self.publisher | ||
pub fn stop(self) -> InactivePublisher<T> { | ||
self.ffi_pub.stop_offer(); |
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.
what does ffi
mean?
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.
Foreign Function Interface
self.publisher | ||
pub fn stop(self) -> InactivePublisher<T> { | ||
self.ffi_pub.stop_offer(); | ||
InactivePublisher::new_from_publisher(self) |
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.
UUhh this is nice, actually we could also implement such a thing for c++.
struct Publisher {
static InactivePublisher stop(Publisher&& p);
};
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 is the type state pattern. Unfortunately not fully possible with C++ since it is not fully possible to control the ownership.
The way this works in Rust is that the compiler will complain if you use the publisher after calling stop_offer
use std::marker::PhantomData; | ||
|
||
pub(super) struct PublisherOptions { | ||
pub history_capacity: u64, | ||
pub node_name: String, | ||
pub offer_on_create: bool, | ||
pub subscriber_too_slow_policy: ConsumerTooSlowPolicy, | ||
_phantom: PhantomData<()>, |
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.
For what is PhantomData
required?
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 is required for the generic parameter T
. Since no other member is dependent on the, some phantom data is required. This does not affect the size of the struct. It is just a marker. See also https://doc.rust-lang.org/std/marker/struct.PhantomData.html
Ok((subscriber, SampleReceiverToken {})) | ||
} | ||
|
||
pub fn create_without_subscribe(mut self) -> Result<InactiveSubscriber<T>, IceoryxError> { |
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 would also go back here and just use one create and make subscribe_on_create
an option. If not you forgot create_without_subscriber_mt
I think.
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.
But then I cannot use the typestate pattern anymore
create
-> returns single threaded subscriberst::Subscriber
create_mt
-> returns multi threaded subscribermt::Subscriber
create_without_subscribe
-> returns inactive subscriberInactiveSubscriber
The InactiveSubscriber
has
subscribe
-> returns single threaded subscriberst::Subscriber
subscribe_mt
-> returns multi threaded subscribermt::Subscriber
The st::Subscriber
has unsubscribe
which returns a InactiveSubscriber
, similar to mt::Subscriber
.
This means one cannot call subscribe
when already subscribed or try to take samples when unsubscribed.
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.
Okay, what about turning the create into a generic where you state the compile time properties, threadsafe and subscribe on create, and the generic finds the most fitting type and returns it?
Something like:
pub fn new() -> InactiveSubscriber<Self> {
SubscriberBuilder::<Self>::new("Introspection", "RouDi_ID", "MemPool")
.queue_capacity(1)
.history_request(1)
.build()
.create<threadSafe::Nope, subscription::OnCreate>()
But if this does not make sense I think your approach is the most reasonable one.
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 depends on the specialization feature which is not yet stabilized.
ab98813
to
670612c
Compare
Codecov Report
@@ Coverage Diff @@
## master #24 +/- ##
==========================================
+ Coverage 35.18% 35.32% +0.14%
==========================================
Files 17 15 -2
Lines 4332 4252 -80
==========================================
- Hits 1524 1502 -22
+ Misses 2808 2750 -58
Continue to review full report at Codecov.
|
661d735
to
9134b42
Compare
90705c1
to
670612c
Compare
670612c
to
94b5f52
Compare
@elfenpiff I would keep the API as is for the 0.1 release and refine it further in follow up releases. Let's discuss API improvements in a discussion page ... @budrus are you able to enable the discussion feature in the general settings? |
Pre-Review Checklist for the PR Author
rustfmt
iox-#123-this-is-a-branch
)iox-#42 commit text
)task-list-completed
)Notes for Reviewer
This PR refines and updates the API to match the iceoryx v2 functionality.
The PR should be merged after #22
Checklist for the PR Reviewer
Post-review Checklist for the PR Author
References