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

add timer api for timer bindings in zephyr #41

Closed

Conversation

inthehack
Copy link

No description provided.

@inthehack inthehack marked this pull request as draft December 10, 2024 16:58
Copy link
Collaborator

@d3zd3z d3zd3z left a 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.
Copy link
Collaborator

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.

Copy link
Collaborator

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.

Copy link
Author

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 {}
Copy link
Collaborator

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.

Copy link
Author

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)
Copy link
Collaborator

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.

Copy link
Author

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);
Copy link
Collaborator

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.

Copy link
Collaborator

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.

Copy link
Author

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 ())>,
Copy link
Collaborator

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 {
Copy link
Collaborator

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 {
Copy link
Collaborator

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 {
Copy link
Collaborator

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.

Copy link
Author

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,
}
}
Copy link
Collaborator

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.

@d3zd3z
Copy link
Collaborator

d3zd3z commented Dec 11, 2024

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.

@d3zd3z
Copy link
Collaborator

d3zd3z commented Dec 11, 2024

Also, are you on the #rust discord channel?

@d3zd3z
Copy link
Collaborator

d3zd3z commented Dec 12, 2024

@Jpeeters Can you have a look at #42. I think I might want to split this into a SimpleTimer type that doesn't have the callback, and a Timer type like what I have here. The SimpleTimer could be static, or the Timer could just have a 'new' method that constructs a dynamic one.

But, I think I've made the types on the callback mechanism sound.

@inthehack
Copy link
Author

Also, are you on the #rust discord channel?

I am now ;-)

@inthehack
Copy link
Author

@Jpeeters Can you have a look at #42. I think I might want to split this into a SimpleTimer type that doesn't have the callback, and a Timer type like what I have here. The SimpleTimer could be static, or the Timer could just have a 'new' method that constructs a dynamic one.

But, I think I've made the types on the callback mechanism sound.

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.

@inthehack
Copy link
Author

inthehack commented Dec 20, 2024

@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?

@d3zd3z
Copy link
Collaborator

d3zd3z commented Jan 9, 2025

@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?

Have a look at my timers PR. I haven't quite supported directly using timers with async, but gluing them to a callback that signals a wakeup shouldn't be too difficult. My async executor has a Signal type and there is an wait_async method to wait for the signal.

I also have a convenience macro to create an event loop that waits on messages from a channel, and has a periodic timer that it uses to call one block of code. I use absolute timer values, and it detects missed ticks.

@inthehack
Copy link
Author

I also have a convenience macro to create an event loop that waits on messages from a channel, and has a periodic timer that it uses to call one block of code. I use absolute timer values, and it detects missed ticks.

Oh this looks promising and I would love to have a look at your executor. I am using embassy executor at the moment, thinking of writing my own. So, if you agree to share what you've did, I look forward to reading.

@d3zd3z
Copy link
Collaborator

d3zd3z commented Mar 14, 2025

Some other progress that has happened since this change.

I have added some timer types to zephyr::timer. These are not as much for async, but allow for timer callbacks or blocking timer use.

In addition, I have contributed a feature time-driver that provides a timer driver for embassy-time, which allows the embassy Timer and Ticker drivers to work in async context. These should work with any executor, including the zephyr-executor provided by that same series. I will be removing the work-queue-based executor, as it is incompatible with other code that uses async.

Will this meet your needs?

@d3zd3z
Copy link
Collaborator

d3zd3z commented Mar 26, 2025

@inthehack The executor and timer support should be merged now. Have a look at the zephyr::embassy module, which includes an Executor as well as a embassy-time-driver, that when enabled, should just allow embassy-time to be used. There are some performance concerns if the tick rate differs between embassy and Zephyr, but the behavior should be correct. The embassy tick rate is defined by the application crate as a feature on the embassy-time crate.

I also have zephyr::timer which is basically non-async support. If you want timers in async, just use embassy-time.

Let me know if these things meet your needs, and if any other functionality is needed.

@inthehack
Copy link
Author

Thank you very much @d3zd3z. I am quite busy right now. I'll check your work out as soon as I get more bandwidth. For now, I would suggest to close this PR because I think it is outdated and not relevant anymore.

@inthehack inthehack closed this Mar 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants