Skip to content

sys/ztimer: add convenience functions for sleeping #17330

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

Closed
wants to merge 3 commits into from

Conversation

maribu
Copy link
Member

@maribu maribu commented Dec 3, 2021

Contribution description

This adds three additional wrapper-convenience functions for when drivers have relaxed requirements and don't want to pull in additional clocks, but rather use whatever clock is available.

The xtimer compat code and the STM32 ADC drivers are added as the first users of the new convenience API.

Testing procedure

Sleeping for minimum durations specified in usecs, msecs, and secs should work now regardless of which ztimer clocks are available, as long at least one is present. If multiple options are present, the most suitable clock should be used.

Issues/PRs references

None

@maribu maribu added Type: new feature The issue requests / The PR implemements a new feature for RIOT Area: timers Area: timer subsystems Area: sys Area: System labels Dec 3, 2021
@maribu maribu requested review from aabadie and kaspar030 December 3, 2021 11:05
@github-actions github-actions bot added Area: cpu Area: CPU/MCU ports Platform: ARM Platform: This PR/issue effects ARM-based platforms labels Dec 3, 2021
@kaspar030
Copy link
Contributor

kaspar030 commented Dec 3, 2021

This adds three additional wrapper-convenience functions for when drivers have relaxed requirements and don't want to pull in additional clocks, but rather use whatever clock is available.

I think this is a footgun. If ztimer_sleep_usecs(100} happens to sleep 1-2ms, it is IMO a bug (unless higher priority thread causes this). But it will happen if ZTIMER_MSEC is used, transparently. Also, this changes driver behavior depending on selected modules. As in, ztimer_sleep_usecs() has different timing behavior depending on whether an unrelated module depends on ZTIMER_USEC or not.

Like, start developing driver using ztimer_sleep_usecs(), (default is ZTIMER_MSEC), driver works. Integrate in application, something else depends on ZTIMER_USEC. driver changes behavior.

IMO, the different hardware is difficult to abstract, and guarantees or bounds on accuracy / power consumption are hard to make. If clocks are freely switched depending on the whole dependency tree, it is even harder to make guarantees.

Using a more accurate clock is fine accuracy wise (more accuracy is acceptable), e.g., use ZTIMER_USEC if MSEC is not available. That breaks ztimer_sleep_msec() is low power if the hardware supports it, though.

So in short, I'm OK with mapping ztimer_sleep_msec() -> ztimer_sleep(ZTIMER_MSEC, ..), but I don't like the handover to other timers.

@kaspar030
Copy link
Contributor

Like, start developing driver using ztimer_sleep_usecs(), (default is ZTIMER_MSEC), driver works. Integrate in application, something else depends on ZTIMER_USEC. driver changes behavior.

Although it is a contrieved example, the current implementation would allow a ztimer_sleep_usecs(100) "degrade" to ztimer_sleep(ZTIMER_SEC, 0), which would sleep up to just below 2 seconds. That can't be right. :) And it will cause very visible bootup delays if a driver has a couple of those in it's auto-init.

#if IS_USED(MODULE_ZTIMER_USEC)
ztimer_sleep(ZTIMER_USEC, ADC_T_ADCVREG_STUP_US);
#else
/* to avoid using ZTIMER_USEC unless already included round up the
internal voltage regulator start up to 1ms */
ztimer_sleep(ZTIMER_MSEC, 1);
#endif
ztimer_sleep_usecs(ADC_T_ADCVREG_STUP_US);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that with the exception of the possibility to degrade to second resolution, this is already doing what the convenience functions do. So the question on whether automatic clock selection is acceptable needs to be answered independent of this PR.

There are a lot of drivers that call into ztimer exactly once for a sleep after a reset, and the drivers don't care at all if the delay gets rounded up to milliseconds, or even seconds. (But I agree that for user experience, rounding up to seconds might indeed be a footgun.) For low power use cases it could be beneficial to allow applications to run without pulling in ztimer_usec at all. And on the other hand, a user with a low-on-memory device might care not about the power consumption benefit of adding ztimer_msec, but care a lot about the additional memory this would use.

So I think there are use cases for this. So why not provide a convenience function for this to make it easier to cater these use cases.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped the degrading to seconds. It is now identical to the code before, just easier to read. I also added some more visible warnings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the use case. I still think that we cannot have a "ztimer_sleep_usecs()" that behaves as specified. (the name alone would make users use it, and probably be surprised.)

Can we boil this down to just ztimer_sleep_msec() that just falls back to use ZTIMER_USEC if MSEC is not available?
If users don't care about the accuracy, that should be fine.
And, multiplication is faster than division. :)

Having an API that is called "sleep for usecs" but sometimes it actually sleeps quantized to an msec timer is weird.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And, multiplication is faster than division.

For the use case it has this shouldn't matter, there should be a DRIVER_FOO_RESET_SLEEP defined somewhere, so that the compiler does constant folding anyway.

the name alone would make users use it, and probably be surprised

Maybe we should just find a better name. ztimer_sleep_usec_with_footgun() or ztimer_sleep_usec_unprecise()?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ztimer_sleep_at_least() because when initializing drivers, I don't care how long the sleep takes as long as it sleeps for at least the required amount of time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that also ztimer_sleep() has at-least-semantics, as there is little ztimer could do for low prio tasks in case higher priority tasks would kick in when the sleep is over. The difference is only that this sleep can take longer even in absence of load.

I fear if the is a special at-least function, users start assuming the regular version has no at-least-semantics.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ztimer_sleep() has at-least semantic but also best effort (if there isn't a higher prio task and the sleep isn't to short to manage it should be within 0-2 units)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a _relaxed postfix to all functions, as for each of them at least one assumption has to be relaxed regarding the behavior.

@kfessel
Copy link
Contributor

kfessel commented Dec 6, 2021

Why have this a ztimer function, what it provides can be implemented in different ways ( eg checking ARMs syscounter) as you said it is often used just once ->
Why should ADC depend on ztimer.

i think we could use some convinece sleep functions that use whatever source is available. (making them independent of ztimer (even if there isn't a second implementation now)

sleep_usec
sleep_msec
sleep_sec
and
matching "least" functions that may choose timers of worse resolution

@maribu
Copy link
Member Author

maribu commented Dec 9, 2021

Why have this a ztimer function

I'm not sure what the benefit of a meta-timer API would be. ztimer has proven to be flexible enough to hook in strange beasts like PTP timer and RTC (with C = clock, not C = counter) equally well as regular timers and low power timers (periph_timer and periph_rtt). There is a PR for adding SysTick as backend as well (which really should get some attention).

matching "least" functions that may choose timers of worse resolution

I don't think it makes sense to add on-least functions, as "least" (+/- rounding error and clock drift) is the only sane guarantee one can make. Providing an "exact" or "at most" guarantee will not work if there is load on the system, unless one adds an immensely complex task admission and timer admission system that will only allow to add threads and timer, if the system can be guaranteed to remain real-time capable.

@kaspar030
Copy link
Contributor

I'm fine with the _relaxed() suffix!

@kfessel
Copy link
Contributor

kfessel commented Dec 9, 2021

Profit of a "meta" api you will have to rewrite less if you want to switch the timer below it: a API (similar) to what you are proposing is exiting in these late days of xtimer, in the form of xtimer_msleep the only reason this has to be touched for ztimer is the xtimer_

just had a look into the diff an saw you a reimplemting the very API that shows how this is independent o ztimer

the at_least is about relaxing the resoulution and best effort to keep within - constrains

@kaspar030 just wrote: _relaxed and i think this is nice (better then at_least)

i am still for the meta part since this api needs not to be bound to ztimer as its implemetation

@benpicco benpicco requested a review from jue89 February 4, 2022 15:28
Copy link
Contributor

@jue89 jue89 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea!

But I'm afraid a first-time user might be overwhelmed with all those ztimer methods described in the Doxygen API. Maybe it's a good idea to move these relaxed methods into their own header file resp. module?

Copy link
Contributor

@benpicco benpicco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the API, this will simplify things a lot.

Since I envision those functions being used most of the time, I would have preferred the shorter names (ztimer_usleep(), ztimer_msleep(), ztimer_sleep()).

Bike-shedding aside, anything needed to get this unblocked?

maribu added 2 commits May 26, 2022 23:31
This adds three additional wrapper-convenience functions for when
drivers have relaxed requirements and don't want to pull in
additional clocks, but rather use whatever clock is available.

Also refactored the xtimer_compat code to make use of the new functions.
@maribu
Copy link
Member Author

maribu commented May 26, 2022

Since I envision those functions being used most of the time, I would have preferred the shorter names (ztimer_usleep(), ztimer_msleep(), ztimer_sleep()).

The issue is that there already is a ztimer_sleep(), and also the _relaxed postfix is insisted upon. I also like to have an indication of the unit in the name even for seconds.

Maybe ztimer_usleep_relaxed(), ztimer_msleep_relaxed(), and ztimer_ssleep_relaxed() would be a somewhat shorter compromise?

@maribu
Copy link
Member Author

maribu commented May 26, 2022

Maybe it's a good idea to move these relaxed methods into their own header file resp. module?

How about grouping the functions instead in the documentation (see fixup commit)? That structures the doc and group those sleep functions to easy grasping the documentation.

As @benpicco says, these helpers may become popular ;) Having to use and extra module or #include would be additional effort.

@kfessel
Copy link
Contributor

kfessel commented May 27, 2022

how about removing the ztimer_ prefix since the user of such a relaxed function probaly doesn't care which subsystem or timer implementation is used and this relaxing of costrains could very well adapt to other timer implementations. (there are xtimer_*sleep calls that we are only removing because they reference an old subsystem not because they generate any overhead)
or create an new (not connected to ztimer) prefix idktimer_*sleep easy_*sleep or even relax_*(sleep)

while writing i think relax_sec(10) looks nice

@benpicco
Copy link
Contributor

tbh I find it not immediately clear what 'relaxed' means in the context of timers.
I would imagine a relaxed timer API to look like ztimer_msleep_relaxed(min, max).

How about anytimer_msleep or just xtimer_msleep?

@kfessel
Copy link
Contributor

kfessel commented May 27, 2022

@benpicco: just "relax some seconds" :)

I also don't think of this in term of a timer, but in more in the sleeping (or relaxing) (realworld example: more like the laying down onto bed/couch and rest, than the setting an alarm clock)

I don't see a reason to tie this API to ztimer. but anytimer_*sleep is also nice.

sadly xtimer_msleep (x as in any) is also taken (by x as in this is using the xtimer backend (or an api emuation) (there is some old API we are in the process of replacing)

@benpicco
Copy link
Contributor

sadly xtimer_msleep (x as in any) is also taken (by x as in this is using the xtimer backend (or an api emuation) (there is some old API we are in the process of replacing)

but wouldn't it be super convenient if the old API just so happens to be compatible with the new API and will automatically do the right thing?

@kfessel
Copy link
Contributor

kfessel commented May 27, 2022

sadly xtimer_msleep (x as in any) is also taken (by x as in this is using the xtimer backend (or an api emuation) (there is some old API we are in the process of replacing)

but wouldn't it be super convenient if the old API just so happens to be compatible with the new API and will automatically do the right thing?

but we can't -since the old API is defined and expected to do the best thing it can do to hit that timing (may need some extra cylces for the list magement or if the CPU is occupied otherwise some extra cylcles of that), but with this API the very common case of requesting something and checkback later should be addressed.

e.g. Hardware Initialisation Datasheet: power this thing up and it will be ready in max 1sec typical 400ms)

e.g.: Onewire Temperature Messurement while the protocol needs µsec timing the meassurment periode can have a sleeping cpu (no exact timing need just sleep longer than 750ms (of my head this is for 10Bit??)) (wakeup takes less than a msec (stm32) so 749ms of just a LPTIMER do the waiting and this can easily be relaxed to to 1-2 seconds which would be RTC realm (no need for a ztimer_msec accuracy so just relax_msec(750))

@maribu
Copy link
Member Author

maribu commented May 27, 2022

So, the verdict is to move this to sys/include/sleep.h and name the functions sleep_s, sleep_ms, and sleep_us?

@kaspar030
Copy link
Contributor

So, the verdict is to move this to sys/include/sleep.h and name the functions sleep_s, sleep_ms, and sleep_us?

maybe delay_*()? "sleeping" carries some low-power promise which might not be met (e.g., for sleep_us().

@kaspar030
Copy link
Contributor

... but I still think having those functions switch their accuracy to "lower" by an order of magnitude, depending on the dependency tree, is a footgun ...

@kfessel
Copy link
Contributor

kfessel commented May 29, 2022

relax_s does not carry an implicit assumption about accuracy or power consumption.
I never heard of an lib or lang that has a relax keyword, so anyone seeing that would know there is a reason it is neither delay (typical for simple delay-funtions that count cycles) nor sleep (either a higher level of delay or something that deals with threads and maybe power) -> they will read the doc

@benpicco
Copy link
Contributor

I think this is still very much useful.

@maribu
Copy link
Member Author

maribu commented Jun 8, 2023

I picked up the idea here again in #19719 and this time hopefully picked a API names that are resistant to bike-shedding

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: cpu Area: CPU/MCU ports Area: sys Area: System Area: timers Area: timer subsystems Platform: ARM Platform: This PR/issue effects ARM-based platforms Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants