-
Notifications
You must be signed in to change notification settings - Fork 9
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
add timer api for timer bindings in zephyr #41
base: main
Are you sure you want to change the base?
Conversation
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. Overall, I think this is a good implementation of sys timers. I think it will fit well with the channels I have, where crossbeam supports a timer that gets either a periodic message, or a single message. I'll have to pub some though though into how to do that. But for that, I'll definitely want dynamically allocate timers (Timer::new.()
).
The future stuff is interesting, and I look forward to seeing how we can make use of Async. It is interesting to me, but I don't think I'd be able to get to it for a while.
}; | ||
use crate::time::{Duration, Timeout}; | ||
|
||
/// Zephyr `k_timer` usable from safe Rust code. |
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 might be a bit of a "stretch" to state that this is usable from safe Rust code. Some of the entries are done with *mut ()
and are technically safe, but not particularly useful without having to do conversions on the pointers.
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.
Actually, thinking about this more, the *mut ()
, are never possible to use soundly in Rust code. Rust forbids creating a &mut _
when there are any other possible references to that object.
To make this work, the pointer needs to be *const ()
, and the item should be protected by something like a SpinMutex
, which was specifically designed for IRQ handlers.
I'm going to take a stab at seeing what it takes to make this code sound.
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 the comment. This is very useful.
|
||
unsafe impl Sync for StaticKernelObject<k_timer> {} | ||
|
||
unsafe impl Send for Timer {} |
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.
We'll have to think a bit (and have a SAFETY comment here explaining it) as to why Timer would implement Send
. Unfortunately, kernel.h doesn't document this very well.
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 definitely need to read more about the kernel.h internal to be able to say something here.
fn get_wrapped(&self, _: Self::I) -> Self::T { | ||
let ptr = self.value.get(); | ||
unsafe { k_timer_init(ptr, Some(expired), None) }; | ||
Timer::new_from_ptr(ptr) |
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.
Technically, there is a race here, as expired
could theoretically be called before the timer is constructed. But, it shouldn't be getting called until after it is started.
If anything, a SAFETY comment on the k_timer_init
line, especially explaining why it is safe to do that is in order.
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.
You're right. I lack of safety comment on this. You pointed out what I had in mind.
|
||
unsafe extern "C" fn expired(ktimer: *mut k_timer) { | ||
let user: *mut _ = k_timer_user_data_get(ktimer); | ||
let timer: &mut Timer = &mut *(user as *mut Timer); |
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 isn't safe to do, even in unsafe Rust, as it violates the rule about there, at any time, only ever being a single mutable reference to something. As the timer can be called at any time (and, in fact is called from IRQ context), this reference is almost certainly invalid.
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, I notice that your use case below doesn't use the self reference. I suggest, at least initially, we just leave it out, and only rely in the user data passed in.
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 the feedback on unsafe usage here. I looked at you implementation in #42 and it does more make sense to me now.
inner: Fixed<k_timer>, | ||
|
||
/// The expired callback if any. | ||
expired: Option<(fn(&mut Self, *mut ()), *mut ())>, |
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.
Let's call this expiry
to match the similar field in k_timer
in Zephyr. It also avoids some confusion, as expired suggests it is some kind of state or such.
} | ||
} | ||
|
||
// impl Drop for Timer { |
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.
Let's think a bit about whether Drop makes sense here. Right now, these timers can only be declared statically. The real question is, with Zephyr, is it legal to reuse memory for a timer after the timer has been stopped. It'd actually be pretty nice if we could support dynamic timers. In this case, we'd want to be able to have either the static pointer like this, or actually hold the timer itself (inside something pinned).
I think this is safe, as Drop can't be called if there is some thread waiting on the timer, because that thread would still have a reference to the timer. I've asked on #kernel to see if anyone has any more insight.
Part of why this is a decent question is that the use of Pin requires allocation anyway, so it'd be nice to just allocate the timer in the first place, and not have to worry about statics at all.
} | ||
|
||
impl<'a> TimerFuture<'a> { | ||
pub fn new<'b: 'a>(mut timer: Pin<&'b mut Timer>, delay: Duration) -> 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.
This is where it'd be really nice if the k_timer
itself could just be inside of the Timer
.
// } | ||
// } | ||
|
||
mod futures { |
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 I'm experienced enough with Futures to know beyond basic safety checks and the likes. Do you have an executor you are using on Zephyr to make use of this.
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 at the moment I made my tests with embassy-executor
with work well. But we could definitely discuss about creating one in zephyr, using the zephyr primitives for certain features like priority queues on separated threads.
expired: None, | ||
_marker: PhantomPinned, | ||
} | ||
} |
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 do think we can provide a new
here that uses an owned pointer. In fact, that is probably a bit of overkill with nested pinning, but that is needed to handle the case of wanting static timers.
Oh, another thing, we'll want to come up with some kind of test for this, I'm probably going to be working on enhancing channels next, and it might make sense to have timer tests there, which might count as testing this. |
Also, are you on the #rust discord channel? |
@jpeeters Can you have a look at #42. I think I might want to split this into a But, I think I've made the types on the callback mechanism sound. |
I am now ;-) |
Sorry for the late response. I looked at it and I like it. I need to be more at ease with Zephyr and the Rust integration so as to provide a better feedback right now. |
@d3zd3z I don't know what you think of this : may be I should close this PR and start again from your current implementation of timers. My contribution could be then more focused on futures for instance. What do you think? |
No description provided.