mirror of
https://github.com/apache/nuttx.git
synced 2025-12-06 17:23:49 +08:00
sleep: optimize sleep logic, to reduce the disable IRQ time
Signed-off-by: ligd <liguiding1@xiaomi.com>
This commit is contained in:
@@ -439,6 +439,55 @@ int nxsig_kill(pid_t pid, int signo);
|
||||
|
||||
#define nxsig_waitinfo(s,i) nxsig_timedwait(s,i,NULL)
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxsig_clockwait
|
||||
*
|
||||
* Description:
|
||||
* This function selects the pending signal set specified by the argument
|
||||
* set. If multiple signals are pending in set, it will remove and return
|
||||
* the lowest numbered one. If no signals in set are pending at the time
|
||||
* of the call, the calling process will be suspended until one of the
|
||||
* signals in set becomes pending, OR until the process is interrupted by
|
||||
* an unblocked signal, OR until the time interval specified by timeout
|
||||
* (if any), has expired. If timeout is NULL, then the timeout interval
|
||||
* is forever.
|
||||
*
|
||||
* If the info argument is non-NULL, the selected signal number is stored
|
||||
* in the si_signo member and the cause of the signal is stored in the
|
||||
* si_code member. The content of si_value is only meaningful if the
|
||||
* signal was generated by sigqueue() (or nxsig_queue).
|
||||
*
|
||||
* This is an internal OS interface. It is functionally equivalent to
|
||||
* sigtimedwait() except that:
|
||||
*
|
||||
* - It is not a cancellation point, and
|
||||
* - It does not modify the errno value.
|
||||
*
|
||||
* Input Parameters:
|
||||
* clockid - The ID of the clock to be used to measure the timeout.
|
||||
* flags - Open flags. TIMER_ABSTIME is the only supported flag.
|
||||
* rqtp - The amount of time to be suspended from execution.
|
||||
* rmtp - If the rmtp argument is non-NULL, the timespec structure
|
||||
* referenced by it is updated to contain the amount of time
|
||||
* remaining in the interval (the requested time minus the time
|
||||
* actually slept)
|
||||
*
|
||||
* Returned Value:
|
||||
* This is an internal OS interface and should not be used by applications.
|
||||
* A negated errno value is returned on failure.
|
||||
*
|
||||
* EAGAIN - wait time is zero.
|
||||
* EINTR - The wait was interrupted by an unblocked, caught signal.
|
||||
*
|
||||
* Notes:
|
||||
* This function should be called with critical section set.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int nxsig_clockwait(int clockid, int flags,
|
||||
FAR const struct timespec *rqtp,
|
||||
FAR struct timespec *rmtp);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxsig_timedwait
|
||||
*
|
||||
|
||||
@@ -93,112 +93,11 @@
|
||||
int nxsig_nanosleep(FAR const struct timespec *rqtp,
|
||||
FAR struct timespec *rmtp)
|
||||
{
|
||||
irqstate_t flags;
|
||||
clock_t starttick;
|
||||
sigset_t set;
|
||||
int ret;
|
||||
|
||||
/* Sanity check */
|
||||
ret = nxsig_clockwait(CLOCK_REALTIME, 0, rqtp, rmtp);
|
||||
|
||||
if (rqtp == NULL || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If rqtp is zero, yield CPU and return
|
||||
* Notice: The behavior of sleep(0) is not defined in POSIX, so there are
|
||||
* different implementations:
|
||||
* 1. In Linux, nanosleep(0) will call schedule() to yield CPU:
|
||||
* https://elixir.bootlin.com/linux/latest/source/kernel/time/
|
||||
* hrtimer.c#L2038
|
||||
* 2. In BSD, nanosleep(0) will return immediately:
|
||||
* https://github.com/freebsd/freebsd-src/blob/
|
||||
* 475fa89800086718bd9249fd4dc3f862549f1f78/crypto/openssh/
|
||||
* openbsd-compat/bsd-misc.c#L243
|
||||
*/
|
||||
|
||||
if (rqtp->tv_sec == 0 && rqtp->tv_nsec == 0)
|
||||
{
|
||||
sched_yield();
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Get the start time of the wait. Interrupts are disabled to prevent
|
||||
* timer interrupts while we do tick-related calculations before and
|
||||
* after the wait.
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
starttick = clock_systime_ticks();
|
||||
|
||||
/* Set up for the sleep. Using the empty set means that we are not
|
||||
* waiting for any particular signal. However, any unmasked signal can
|
||||
* still awaken nxsig_timedwait().
|
||||
*/
|
||||
|
||||
sigemptyset(&set);
|
||||
|
||||
/* nxsig_nanosleep is a simple application of nxsig_timedwait. */
|
||||
|
||||
ret = nxsig_timedwait(&set, NULL, rqtp);
|
||||
|
||||
/* nxsig_timedwait() cannot succeed. It should always return error with
|
||||
* either (1) EAGAIN meaning that the timeout occurred, or (2) EINTR
|
||||
* meaning that some other unblocked signal was caught.
|
||||
*/
|
||||
|
||||
if (ret == -EAGAIN)
|
||||
{
|
||||
/* The timeout "error" is the normal, successful result */
|
||||
|
||||
leave_critical_section(flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* If we get there, the wait has failed because we were awakened by a
|
||||
* signal. Return the amount of "unwaited" time if rmtp is non-NULL.
|
||||
*/
|
||||
|
||||
if (rmtp)
|
||||
{
|
||||
clock_t elapsed;
|
||||
clock_t remaining;
|
||||
sclock_t ticks;
|
||||
|
||||
/* REVISIT: The conversion from time to ticks and back could
|
||||
* be avoided. clock_timespec_subtract() would be used instead
|
||||
* to get the time difference.
|
||||
*/
|
||||
|
||||
/* First get the number of clock ticks that we were requested to
|
||||
* wait.
|
||||
*/
|
||||
|
||||
ticks = clock_time2ticks(rqtp);
|
||||
|
||||
/* Get the number of ticks that we actually waited */
|
||||
|
||||
elapsed = clock_systime_ticks() - starttick;
|
||||
|
||||
/* The difference between the number of ticks that we were requested
|
||||
* to wait and the number of ticks that we actually waited is that
|
||||
* amount of time that we failed to wait.
|
||||
*/
|
||||
|
||||
if (elapsed >= (clock_t)ticks)
|
||||
{
|
||||
remaining = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
remaining = (clock_t)ticks - elapsed;
|
||||
}
|
||||
|
||||
clock_ticks2time(rmtp, remaining);
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
return ret;
|
||||
return ret == -EAGAIN ? 0 : ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@@ -292,52 +191,17 @@ int clock_nanosleep(clockid_t clockid, int flags,
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Check if absolute time is selected */
|
||||
/* Just a wrapper around nxsig_clockwait() */
|
||||
|
||||
if ((flags & TIMER_ABSTIME) != 0)
|
||||
ret = nxsig_clockwait(clockid, flags, rqtp, rmtp);
|
||||
|
||||
/* Check if nxsig_clockwait() succeeded */
|
||||
|
||||
if (ret == -EAGAIN)
|
||||
{
|
||||
struct timespec reltime;
|
||||
struct timespec now;
|
||||
irqstate_t irqstate;
|
||||
|
||||
/* Calculate the relative time delay. We need to enter a critical
|
||||
* section early to assure the relative time is valid from this
|
||||
* point in time.
|
||||
*/
|
||||
|
||||
irqstate = enter_critical_section();
|
||||
ret = clock_gettime(clockid, &now);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* clock_gettime() sets the errno variable */
|
||||
|
||||
leave_critical_section(irqstate);
|
||||
leave_cancellation_point();
|
||||
return -ret;
|
||||
}
|
||||
|
||||
clock_timespec_subtract(rqtp, &now, &reltime);
|
||||
|
||||
/* Now that we have the relative time, the remaining operations
|
||||
* are equivalent to nxsig_nanosleep().
|
||||
*/
|
||||
|
||||
ret = nxsig_nanosleep(&reltime, rmtp);
|
||||
leave_critical_section(irqstate);
|
||||
ret = OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In the relative time case, clock_nanosleep() is equivalent to
|
||||
* nanosleep. In this case, it is a paper thin wrapper around
|
||||
* nxsig_nanosleep().
|
||||
*/
|
||||
|
||||
ret = nxsig_nanosleep(rqtp, rmtp);
|
||||
}
|
||||
|
||||
/* Check if nxsig_nanosleep() succeeded */
|
||||
|
||||
if (ret < 0)
|
||||
else if (ret < 0)
|
||||
{
|
||||
/* If not return the errno */
|
||||
|
||||
|
||||
@@ -203,6 +203,141 @@ void nxsig_wait_irq(FAR struct tcb_s *wtcb, int errcode)
|
||||
}
|
||||
#endif /* CONFIG_CANCELLATION_POINTS */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxsig_clockwait
|
||||
*
|
||||
* Description:
|
||||
* This function selects the pending signal set specified by the argument
|
||||
* set. If multiple signals are pending in set, it will remove and return
|
||||
* the lowest numbered one. If no signals in set are pending at the time
|
||||
* of the call, the calling process will be suspended until one of the
|
||||
* signals in set becomes pending, OR until the process is interrupted by
|
||||
* an unblocked signal, OR until the time interval specified by timeout
|
||||
* (if any), has expired. If timeout is NULL, then the timeout interval
|
||||
* is forever.
|
||||
*
|
||||
* If the info argument is non-NULL, the selected signal number is stored
|
||||
* in the si_signo member and the cause of the signal is stored in the
|
||||
* si_code member. The content of si_value is only meaningful if the
|
||||
* signal was generated by sigqueue() (or nxsig_queue).
|
||||
*
|
||||
* This is an internal OS interface. It is functionally equivalent to
|
||||
* sigtimedwait() except that:
|
||||
*
|
||||
* - It is not a cancellation point, and
|
||||
* - It does not modify the errno value.
|
||||
*
|
||||
* Input Parameters:
|
||||
* clockid - The ID of the clock to be used to measure the timeout.
|
||||
* flags - Open flags. TIMER_ABSTIME is the only supported flag.
|
||||
* rqtp - The amount of time to be suspended from execution.
|
||||
* rmtp - If the rmtp argument is non-NULL, the timespec structure
|
||||
* referenced by it is updated to contain the amount of time
|
||||
* remaining in the interval (the requested time minus the time
|
||||
* actually slept)
|
||||
*
|
||||
* Returned Value:
|
||||
* This is an internal OS interface and should not be used by applications.
|
||||
* A negated errno value is returned on failure.
|
||||
*
|
||||
* EAGAIN - wait time is zero.
|
||||
* EINTR - The wait was interrupted by an unblocked, caught signal.
|
||||
*
|
||||
* Notes:
|
||||
* This function should be called with critical section set.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int nxsig_clockwait(int clockid, int flags,
|
||||
FAR const struct timespec *rqtp,
|
||||
FAR struct timespec *rmtp)
|
||||
{
|
||||
FAR struct tcb_s *rtcb = this_task();
|
||||
irqstate_t iflags;
|
||||
clock_t expect;
|
||||
clock_t stop;
|
||||
|
||||
/* If rqtp is zero, yield CPU and return
|
||||
* Notice: The behavior of sleep(0) is not defined in POSIX, so there are
|
||||
* different implementations:
|
||||
* 1. In Linux, nanosleep(0) will call schedule() to yield CPU:
|
||||
* https://elixir.bootlin.com/linux/latest/source/kernel/time/
|
||||
* hrtimer.c#L2038
|
||||
* 2. In BSD, nanosleep(0) will return immediately:
|
||||
* https://github.com/freebsd/freebsd-src/blob/
|
||||
* 475fa89800086718bd9249fd4dc3f862549f1f78/crypto/openssh/
|
||||
* openbsd-compat/bsd-misc.c#L243
|
||||
*/
|
||||
|
||||
if (rqtp && rqtp->tv_sec == 0 && rqtp->tv_nsec == 0)
|
||||
{
|
||||
sched_yield();
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* nxsig_clockwait() is not a cancellation point, but it may be called
|
||||
* from a cancellation point. So if a cancellation is pending, we
|
||||
* must exit immediately without waiting.
|
||||
*/
|
||||
|
||||
if (check_cancellation_point())
|
||||
{
|
||||
/* If there is a pending cancellation, then do not perform
|
||||
* the wait. Exit now with ECANCELED.
|
||||
*/
|
||||
|
||||
return -ECANCELED;
|
||||
}
|
||||
#endif
|
||||
|
||||
iflags = enter_critical_section();
|
||||
|
||||
if (rqtp)
|
||||
{
|
||||
/* Start the watchdog timer */
|
||||
|
||||
expect = clock_time2ticks(rqtp);
|
||||
|
||||
if ((flags & TIMER_ABSTIME) == 0)
|
||||
{
|
||||
expect += clock_systime_ticks();
|
||||
}
|
||||
|
||||
wd_start_absolute(&rtcb->waitdog, expect, nxsig_timeout, (wdparm_t)rtcb);
|
||||
}
|
||||
|
||||
/* Remove the tcb task from the ready-to-run list. */
|
||||
|
||||
nxsched_remove_self(rtcb);
|
||||
|
||||
/* Add the task to the specified blocked task list */
|
||||
|
||||
rtcb->task_state = TSTATE_WAIT_SIG;
|
||||
dq_addlast((FAR dq_entry_t *)rtcb, &g_waitingforsignal);
|
||||
|
||||
/* Now, perform the context switch if one is needed */
|
||||
|
||||
up_switch_context(this_task(), rtcb);
|
||||
|
||||
/* We no longer need the watchdog */
|
||||
|
||||
if (rqtp)
|
||||
{
|
||||
wd_cancel(&rtcb->waitdog);
|
||||
stop = clock_systime_ticks();
|
||||
}
|
||||
|
||||
leave_critical_section(iflags);
|
||||
|
||||
if (rqtp && rmtp)
|
||||
{
|
||||
clock_ticks2time(rmtp, expect > stop ? expect - stop : 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nxsig_timedwait
|
||||
*
|
||||
@@ -250,7 +385,6 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct siginfo *info,
|
||||
sigset_t intersection;
|
||||
FAR sigpendq_t *sigpend;
|
||||
irqstate_t flags;
|
||||
sclock_t waitticks;
|
||||
siginfo_t unbinfo;
|
||||
int ret;
|
||||
|
||||
@@ -295,126 +429,28 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct siginfo *info,
|
||||
/* Then dispose of the pending signal structure properly */
|
||||
|
||||
nxsig_release_pendingsignal(sigpend);
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
|
||||
/* We will have to wait for a signal to be posted to this task. */
|
||||
|
||||
else
|
||||
{
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* nxsig_timedwait() is not a cancellation point, but it may be called
|
||||
* from a cancellation point. So if a cancellation is pending, we
|
||||
* must exit immediately without waiting.
|
||||
*/
|
||||
|
||||
if (check_cancellation_point())
|
||||
{
|
||||
/* If there is a pending cancellation, then do not perform
|
||||
* the wait. Exit now with ECANCELED.
|
||||
*/
|
||||
|
||||
leave_critical_section(flags);
|
||||
return -ECANCELED;
|
||||
}
|
||||
#endif
|
||||
|
||||
rtcb->sigunbinfo = (info == NULL) ? &unbinfo : info;
|
||||
|
||||
/* Check if we should wait for the timeout */
|
||||
/* Save the set of pending signals to wait for */
|
||||
|
||||
if (timeout != NULL)
|
||||
rtcb->sigwaitmask = *set;
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
ret = nxsig_clockwait(CLOCK_REALTIME, 0, timeout, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Convert the timespec to system clock ticks, making sure that
|
||||
* the resulting delay is greater than or equal to the requested
|
||||
* time in nanoseconds.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SYSTEM_TIME64
|
||||
waitticks = ((uint64_t)timeout->tv_sec * NSEC_PER_SEC +
|
||||
(uint64_t)timeout->tv_nsec + NSEC_PER_TICK - 1) /
|
||||
NSEC_PER_TICK;
|
||||
#else
|
||||
uint32_t waitmsec;
|
||||
|
||||
DEBUGASSERT(timeout->tv_sec < UINT32_MAX / MSEC_PER_SEC);
|
||||
waitmsec = timeout->tv_sec * MSEC_PER_SEC +
|
||||
(timeout->tv_nsec + NSEC_PER_MSEC - 1) / NSEC_PER_MSEC;
|
||||
waitticks = MSEC2TICK(waitmsec);
|
||||
#endif
|
||||
|
||||
if (waitticks > 0)
|
||||
{
|
||||
/* Save the set of pending signals to wait for */
|
||||
|
||||
rtcb->sigwaitmask = *set;
|
||||
|
||||
/* Start the watchdog */
|
||||
|
||||
wd_start(&rtcb->waitdog, waitticks,
|
||||
nxsig_timeout, (uintptr_t)rtcb);
|
||||
|
||||
/* Now wait for either the signal or the watchdog, but
|
||||
* first, make sure this is not the idle task,
|
||||
* descheduling that isn't going to end well.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(!is_idle_task(rtcb));
|
||||
|
||||
/* Remove the tcb task from the ready-to-run list. */
|
||||
|
||||
nxsched_remove_self(rtcb);
|
||||
|
||||
/* Add the task to the specified blocked task list */
|
||||
|
||||
rtcb->task_state = TSTATE_WAIT_SIG;
|
||||
dq_addlast((FAR dq_entry_t *)rtcb, list_waitingforsignal());
|
||||
|
||||
/* Now, perform the context switch if one is needed */
|
||||
|
||||
up_switch_context(this_task(), rtcb);
|
||||
|
||||
/* We no longer need the watchdog */
|
||||
|
||||
wd_cancel(&rtcb->waitdog);
|
||||
}
|
||||
else
|
||||
{
|
||||
rtcb->sigunbinfo = NULL;
|
||||
|
||||
leave_critical_section(flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
rtcb->sigunbinfo = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* No timeout, just wait */
|
||||
|
||||
else
|
||||
{
|
||||
/* Save the set of pending signals to wait for */
|
||||
|
||||
rtcb->sigwaitmask = *set;
|
||||
|
||||
/* And wait until one of the unblocked signals is posted,
|
||||
* but first make sure this is not the idle task,
|
||||
* descheduling that isn't going to end well.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(!is_idle_task(rtcb));
|
||||
|
||||
/* Remove the tcb task from the running list. */
|
||||
|
||||
nxsched_remove_self(rtcb);
|
||||
|
||||
/* Add the task to the specified blocked task list */
|
||||
|
||||
rtcb->task_state = TSTATE_WAIT_SIG;
|
||||
dq_addlast((FAR dq_entry_t *)rtcb, list_waitingforsignal());
|
||||
|
||||
/* Now, perform the context switch */
|
||||
|
||||
up_switch_context(this_task(), rtcb);
|
||||
}
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* We are running again, clear the sigwaitmask */
|
||||
|
||||
@@ -472,10 +508,10 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct siginfo *info,
|
||||
}
|
||||
|
||||
rtcb->sigunbinfo = NULL;
|
||||
|
||||
leave_critical_section(flags);
|
||||
}
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user