-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Wait API updated to remove deepsleep lock #8189
Conversation
bce7da1
to
9b8d038
Compare
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.
Looks good to me 👍
platform/mbed_wait_api.h
Outdated
* You can avoid it by using Thread::wait(). | ||
* If the RTOS is present, this function spins to get the exact number of microseconds, | ||
* (for 1/100th of sec) which potentially affects power (such as preventing deep sleep) and | ||
* multithread performance. You can avoid it by using ThisThread::sleep_for() / wait_ms() |
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.
minor nit: This slash made me think math was involved. Maybe say "ThisThread::sleep_for() or wait_ms()" ?
platform/mbed_wait_api.h
Outdated
* which potentially affects power (such as preventing deep sleep) and multithread performance. | ||
* If the RTOS is present, this function spins to get the exact number of microseconds, | ||
* (for 1/100th of sec) which potentially affects power (such as preventing deep sleep) and | ||
* multithread performance. You can avoid it by using ThisThread::sleep_for() / wait_ms() |
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.
Isn't this true even if the RTOS is not present?
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.
If the RTOS is present, this function spins to get the exact number of microseconds - This is true if RTOS not present
9b8d038
to
aa906ce
Compare
platform/mbed_wait_api_rtos.cpp
Outdated
ThisThread::sleep_for(ms); | ||
} | ||
|
||
if (sub_ms > 0) { |
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.
Could this be the else
case of the above code? That way delays above the threshold never busy wait.
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.
They still don;t in current logic since we set sub_ms = 0;
But i see your reduced instruction code with above comment ms = ((us + 500) / 1000);
will update as per that
platform/mbed_wait_api_rtos.cpp
Outdated
sleep_manager_lock_deep_sleep(); | ||
Thread::wait((uint32_t)us / 1000); | ||
sleep_manager_unlock_deep_sleep(); | ||
ms = (us / 1000); |
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 could do the rounding as part of this statement:
ms = ((us + 500) / 1000);
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 not really seeing the motivation for changing wait_us
. If someone is calling it specifically, they want the precision, or else they'd be calling wait_ms
, right? The obvious exception is current wait(float)
, but that is a long way from the normal user.
I already have wait_ms(20)
to get 19-20ms. I don't need wait_us(20000)
to give me 19-20ms as well.
Personally, I'm really not a fan of size-of-argument-dependent behaviour here. Ideally something should be a sleeping thread-only function or a non-sleeping any-context function.
I would rather wait_us actually lost the sleep functionality altogether and became a non-sleeping function, and we maybe audited people using it with excessive values (warning/error on debug builds). I don't actually see the utility of wait_us(20000)
with microsecond precision.
I think we may be forced to have value-dependent behaviour on wait(float)
, but I can live with that as long as there are alternative wait_ms
and wait_us
calls with solid behaviour.
platform/mbed_wait_api_rtos.cpp
Outdated
wait_us(ms * 1000); | ||
// Wait equivalent to 1 ms near the tick might result into zero wait. | ||
// Hence ms precision can be used for > 1 ms wait only. | ||
if ((ms >= 1) && core_util_are_interrupts_enabled()) { |
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.
wait_us
already chooses between thread wait and busy wait. Why do you need the checking here?
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.
ThisThread::sleep_for(ms); in the if condition will assert if inside the interrupt.
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.
ms >= 1
? If it's not, it's negative or zero.
And I would say "wait_ms()" if interrupts are disabled is breaking the system. Interrupts should not be disabled for as long as a millisecond, so I don't like coding to permit this. Although I guess the existing code does do it, so maybe backwards compatibility. But if changing anyway...
People willing to do a big wait in interrupt could do it as wait_us(5000)
where the big number kind of exposes the enormity of how bad they're being.
platform/mbed_wait_api.h
Outdated
* If the RTOS is present, this function always spins to get the exact number of microseconds, | ||
* which potentially affects power (such as preventing deep sleep) and multithread performance. | ||
* You can avoid it by using Thread::wait(). | ||
* If the RTOS is present, this function spins to get the exact number of microseconds, |
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.
Microsecond precision is no longer maintained when a delay of greater than 10ms is used. In this case the tolerance would be wait_time - 500us
to wait_time +500us
platform/mbed_wait_api_rtos.cpp
Outdated
wait_us(ms * 1000); | ||
// Wait equivalent to 1 ms near the tick might result into zero wait. | ||
// Hence ms precision can be used for > 1 ms wait only. | ||
if ((ms >= 1) && core_util_are_interrupts_enabled()) { |
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.
ms >= 1
? If it's not, it's negative or zero.
And I would say "wait_ms()" if interrupts are disabled is breaking the system. Interrupts should not be disabled for as long as a millisecond, so I don't like coding to permit this. Although I guess the existing code does do it, so maybe backwards compatibility. But if changing anyway...
People willing to do a big wait in interrupt could do it as wait_us(5000)
where the big number kind of exposes the enormity of how bad they're being.
platform/mbed_wait_api.h
Outdated
* The millisec value specifies the number of timer ticks and is therefore an upper bound. | ||
* The exact time delay depends on the actual time elapsed since the last timer tick. | ||
* The actual time delay may be up to one timer tick less. | ||
* For a value of 1, the function spins to get the exact 1 millisecond. |
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.
Oh, right. That's not what the code says - you've miscoded it.
This special case would be horrible, and a bizarre discontinuity. Why should wait_ms(1)
not wait 1 millisecond less than wait_ms(2)
?
And what happens if you call wait_ms(2)
50 times (with the RTOS)? You get a 100ms delay. Without this special case, wait_ms(1)
100 times would give you a 100ms delay. But with the special case added, you'll get a drift effect, due to the failure to lock onto the ticks.
platform/mbed_wait_api_rtos.cpp
Outdated
sleep_manager_lock_deep_sleep(); | ||
Thread::wait((uint32_t)us / 1000); | ||
sleep_manager_unlock_deep_sleep(); | ||
ms = (us / 1000); |
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 not really seeing the motivation for changing wait_us
. If someone is calling it specifically, they want the precision, or else they'd be calling wait_ms
, right? The obvious exception is current wait(float)
, but that is a long way from the normal user.
I already have wait_ms(20)
to get 19-20ms. I don't need wait_us(20000)
to give me 19-20ms as well.
Personally, I'm really not a fan of size-of-argument-dependent behaviour here. Ideally something should be a sleeping thread-only function or a non-sleeping any-context function.
I would rather wait_us actually lost the sleep functionality altogether and became a non-sleeping function, and we maybe audited people using it with excessive values (warning/error on debug builds). I don't actually see the utility of wait_us(20000)
with microsecond precision.
I think we may be forced to have value-dependent behaviour on wait(float)
, but I can live with that as long as there are alternative wait_ms
and wait_us
calls with solid behaviour.
I think if we're changing it, we should be changing it to something more solid. Specific counter-proposal for the RTOS version:
Whether a call is potentially-thread-sleeping or not is pretty fundamental, and should not depend on values. If it does, someone naively changing what looks like a tuning number past some threshold could radically change the software's behaviour. And at the minute we have no definitely non-thread-sleeping wait. Possible variants to that:
|
21c35eb
to
25dafb7
Compare
@kjbracey-arm - I have update the code as per your suggestion, but with few changes
|
} | ||
|
||
void wait_us(int us) | ||
{ | ||
if (us > 10000) { |
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.
@kjbracey-arm - 0.1s / 0.01s? I would like to keep it same for https://github.com/ARMmbed/mbed-os/pull/8189/files#diff-5a106edf0944ae7ba2d1bf95153fb0a0R30
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, 0.01s makes more sense. I think I probably meant to type that.
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.
0.01s seems fine
platform/mbed_wait_api_rtos.cpp
Outdated
} | ||
|
||
/* The millisec value specifies the number of timer ticks and is therefore an upper bound. |
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 statement is also true for the microsecond wait, just with the finer granularity. In both cases, you'd need to add 1 to make it a lower bound. (Assuming hardware resolution exactly equals the unit, which we assert for the RTOS, and is probably true for the microseconds. If it doesn't match, my head starts to hurt).
So probably shouldn't over-emphasise it on this one - restate it on both calls. This kinds of reads as wait_ms working differently from wait_us in this regard, which it doesn't.
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, hence I added just as comment and not as part of doxygen. Can remove it as well
platform/mbed_wait_api_rtos.cpp
Outdated
if (core_util_are_interrupts_enabled()) { | ||
ThisThread::sleep_for(ms); | ||
} else { | ||
MBED_WARNING(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_INVALID_OPERATION), |
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.
Does MBED_WARNING actually output a visible string at the moment? I see MBED_WARNING
calling mbed_warning
with a string, which it then doesn't use?
My preference for this is to be as harsh as an error, but only on debug builds. I think that's consistent with other nasties we check like RTX errors.
Doing a warning here would likely result in a flood of output or buffer filling, if it wasn't a "once-only" - it could cause extra subtle problems on a normal build, when we're trying to provide a mechanism to catch them.
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.
If you don;t have strong opinion on error
, I can fix mbed_warning
to add debug print and make this warning once-only.
Adding error
will result into mbed_die
and next instruction will not be executed anyways. So I don;t see a point of calling wait_us
after error
unless we add some json config to disable the error.
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 was suggesting flagging the error for the interrupt case to be something like
if (in interrupt)
{
#ifdef MBED_DEBUG // or MBED_TRAP_ERRORS_ENABLED ?
error(blah)
#else
wait_us(ms * 1000);
#endif
}
At some point in the future, once we think that debug error might have persuaded enough people, we get rid of the interrupt check, just call ThisThread::sleep_for
, which would itself go pop-bang if called from interrupt.
If it is a warning, once-only is kind of good when the condition is something like this, which is a design error, likely to be repeated throughout every run, rather than an environmental event. But flagging it does mean static data to track the once-only (or rate limiting).
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.
Fair enough!! Can use MBED_TRAP_ERRORS_ENABLED
since it is set for debug profile and mostly used for similar purpose in RTOS.
} | ||
|
||
void wait_us(int us) | ||
{ | ||
if (us > 10000) { |
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.
0.01s seems fine
if (us > 10000) { | ||
MBED_WARNING(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_UNKNOWN), | ||
"wait_us blocks deep sleep, wait_ms recommended for long delays\n"); | ||
} |
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 was suggesting dropping the sleep functionality from this entirely - restoring it to be the same as the non-RTOS version, hence the message above. What's your thought on that?
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 can do that. Thinking of this again, do you mean remove Thread::wait
and just spin?
Instead of polling continuously like non-RTOS version https://github.com/ARMmbed/mbed-os/blob/master/platform/mbed_wait_api_no_rtos.c#L36. I would prefer yielding to other threads for RTOS
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.
Well, that's kind of my design point. I also prefer yielding to other threads, but I want to define whether a call yields or not - and we would already have the yielding call, which is wait_ms
.
This is a call which already spins for up to a millisecond to do its business, so is already inherently thread-unfriendly - I want people to be using wait_ms
to avoid any spin.
Uses of this should be rare. (Can we do a quick audit - how many actual direct wait_us users are there in tree? Most examples I can think of are GPIO "reset" pulses on init.)
And there are some occasions where it is important not to yield, so I'd like to have an API explicitly defined as that, not dependent on parameter.
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.
Lets get more opinions over here
@TacoGrandeTX @c1728p9 @geky @bulislaw @stevew817 @NeilMacMullen - Please share your thoughts for wait_us
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.
Oh, and this behaviour was my original reason to have my value warning above - we're saying "you do realise you're spinning for 10ms, right?". Not just the deep sleep issue.
I also think you've got a better chance of getting the time you were hoping each time with the spin than if you yield, where you might suddenly be hit with a full 10ms timeslice from another running thread.
Basically anyone wanting sub-millisecond resolution is having special requirements that can't really be catered for if you start dragging the RTOS into it. My feeling is that the current wait_us
behaviour is purely to handle the wait_ms
and wait
built on top of it, rather than something I'd want an explicit wait_us
to do.
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 the intent here to have wait_us
as spinning only, but to be true I am not sure of all use cases it is used for currently, hence will like to know from others as well.
Also agree that current wait_us
behavior is to handle wait
built on top of 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.
FWIW, just searched through the in-tree source, and I can see around a 100 calls to wait_us
, but I can't immediately identify any calls that might ask for >=1000us, except for wait_ms
and wait
themselves.
Actually, no, I see a couple in one of the "onboard_modem_api.c" files, in "wiggle the reset line" code. They're a full second, so would hit the error/warning.
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.
Its wait_ms
- Should not have issue, unless called from interrupt
https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_NXP/TARGET_LPC176X/TARGET_UBLOX_C027/onboard_modem_api.c#L32
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.
One more thought, we can change wait_us
to be always spinning, and update wait
to current behavior of wait_us
25dafb7
to
22114e7
Compare
CC @SenRamakri |
|
||
void wait(float s) | ||
{ | ||
wait_us(s * 1000000.0f); | ||
if (s >= 0.01f) { | ||
wait_ms(s * 1000.0f); |
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.
If wait is used from an interrupt handler it will only trap if the delay is >= 10ms
. Is this acceptable?
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.
Fixed.. wait will not trap
22114e7
to
3c8b8c6
Compare
Hi All, I have updated the code as :
Please review |
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.
Thanks @deepikabhavnani that's very useful to cleanup that area of our code. I do have few observations:
- The documentation of these functions should be precise regards to what happen in interrupt/non interrupt mode, time guarantee and power consumption.
- We can improve consistency:
wait
works in interrupt butwait_ms
doesn't ifMBED_TRAP_ERRORS_ENABLED
is enabled. That is confusing from a user perspective. - us precision can only be achieved from inside a critical section as a context switch or an interrupt of higher priority will disrupt time measured. If a critical section is not used then using a non low power timer makes little sense as precision can be random due to interrupt or context switches ...
- Wouldn't it be possible to have a best-effort event driven wait_us implementation:
- Save current thread priority
- Set current thread priority to real time.
- Register an interrupt handler in the us_ticker that sends a signal to the current thread (or release a semaphore).
- Wait for the signal (or acquire the semaphore).
- Restore thread priority
- Wouldn't it be useful to have more categories of wait function with different and clear guarantees ?
platform/mbed_wait_api.h
Outdated
* You can avoid it by using Thread::wait(). | ||
* If the RTOS is present, this function spins to get the exact number of microseconds for | ||
* usec precision upto 10msec. If delay is larger then 10msec and not in ISR, it is same as | ||
* `wait_ms`. `wait_us` and `wait_ms` are recommended over `wait` |
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.
According to Kevin's comment that function should be deprecated.
Question: should we have a wait_s
function that accepts an integral type ?
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.
@kjbracey-arm - Still work for wait
will be needed in future and I would like to see sleep_for
and sleep_until
replacing wait
. Shall we keep deprecation for next level of improvement on wait?
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_INVALID_OPERATION), | ||
"Deprecated behavior: milli-sec delay should not be used in interrupt.\n"); | ||
#else | ||
wait_us(ms * 1000); |
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.
As stated by Kevin, we could use the low power ticker in that case.
It would be useful to at least print a warning here as behaviour with and without MBED_TRAP_ERRORS_ENABLED
sets are too very different (trap or execute the intended action).
Note that this is a breaking change and it should be documented as 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.
I really don't want to use additional ticker for backwards compatibility, we should be discouraging users from using wait_ms
in interrupt mode.
Note that this is a breaking change and it should be documented as one.
I am not sure how we do this, I have updated the API description and will make sure code does not get in patch release. Do let me know how we can communicate this in better way?
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.
Ok we either trap or not, having it error out in one mode, but happily work without even a warning, in other doesn't make much sense to me.
I don't get this part and I really hope every use of MBED_ERROR
doesn't forces users to add this if
:
#if defined(MBED_TRAP_ERRORS_ENABLED) && MBED_TRAP_ERRORS_ENABLED
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_INVALID_OPERATION),
"Deprecated behavior: milli-sec delay should not be used in interrupt.\n");
#else
I'm happy to show a warning and change the behaviour for next major.
@pan- that was the initial design, but Do you have any other suggestion for |
I agree
Even then, probably can't achieve close to microsecond precision using timers - see #7397 . We would likely need a CPU software loop implementation to achieve that.
Yes, but I don't think raising the priority inside the call would achieve anything - if it mattered, the moment you lowered priority on return, you'd get pre-empted and delayed anyway, right? But you could have a "sleep thread with high-res ticker interrupt wake", leaving it up to the user to make sure it was used from a high-priority thread. That would probably get you typical 100us precision, working on my general assumption of interrupt latency. Potentially useful - better than the regular 1ms tick, without the "spin" horror.
Fully agree, and that's what I was reaching for - have each "wait_xxx" be a clear class. Having looked at C++11 Also, many people needing "precision" from a "wait for" call would be much better served by a "wait until", which is missing here. Without that they're subject to significant drift. |
I think that's still better that have a function that works or doesn't work depending on the debug level, that doesn't make any sense. It also brakes compatibility (sometimes). |
We've got no existing precedent for "run-time console warning for deprecated API use detected at run-time". Having the debug build trap out for this doesn't seem that bad - it's what we did for, say, mutexes. Somewhere around 5.7 debug builds started trapping mutex lock/unlock errors (via Adding extra warning output in develop builds could itself be a compatibility problem anyway. Personally, I'd be fine with just letting it always error now, rather than trying to drop hints about a later clampdown. Make it
Assert isn't even really necessary as you'd hit an assert inside |
@c1728p9 @bulislaw @pan- Thoughts on new proposal to always error if |
It makes more sense. We should avoid this little behaviour changes, but I agree it's a corner case. In any case if someone is using ms wait in ISR somethings not right. |
@bulislaw And do you have similar concerns with |
Yeah our wait story is messy and requires cleanup involving breaking changes, but that should wait till next major version. |
You'll need to go through the OS to sleep even with the low power ticker. If you call sleep directly then other ready to run threads won't get a chance and low priority threads may never get a chance to run. Because of this I don't think there is much of an advantage to using the low power ticker. |
Not quite following the suggestion - in normal thread use, If called to wait milliseconds from ISR/critical section, and you let them do it, you're spinning the CPU anyway, so lptimer versus ustimer doesn't matter. You're not doing either type of sleep. |
@c1728p9 @kjbracey-arm Is conversation done? Sounds like there's still some misunderstandings to sort through. |
@deepikabhavnani Please keep us updated. We might want to remove 5.11 if this is not ready soon |
As discussed off-line, it may be lesser evil. We'll think about redesigning our wait(delay) APIs. |
CI job started. |
Test run: SUCCESSSummary: 4 of 4 test jobs passed |
Description
FOR MINOR RELEASE
ThisThread::sleep_for() allows thread to enter deep sleep mode, and hence saves power. But allowed only tick resolution for wait.
wait
API's earlier applied deepsleep lock to avoid entering deep sleep mode and use busy wait loop for usec precision.Aim here is to get precision as per the API name, and enable devices to enter sleep mode.
Updates:
wait
andwait_us
- Give usec precision till 10msec and perform busy wait loop, no power saving here. But if value passed to these API's is more than 10msec, then behavior will be same aswait_ms
with sub-millisec value rounded.wait_ms
- Will have msec precision and saves power. However it callsThisThread::sleep_for() -> osDelay()
API's which are tick based, i.e actual time delay may be up to one timer tick less.In order to avoid zero delay forwait_ms
we do busy wait for 1ms.Pull request type
Related : #6969 #7154