Skip to content

🐞 fix(components/drivers): fix cpu timer in multithreading #7222

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

Merged
merged 3 commits into from
Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions components/drivers/cputime/cputime.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ static const struct rt_clock_cputime_ops *_cputime_ops = RT_NULL;
* The clock_cpu_getres() function shall return the resolution of CPU time, the
* number of nanosecond per tick.
*
* @return the number of nanosecond per tick
* @return the number of nanosecond per tick(x (1000UL * 1000))
*/
double clock_cpu_getres(void)
uint64_t clock_cpu_getres(void)
{
if (_cputime_ops)
return _cputime_ops->cputime_getres();
Expand Down Expand Up @@ -78,9 +78,9 @@ int clock_cpu_issettimeout(void)
*/
uint64_t clock_cpu_microsecond(uint64_t cpu_tick)
{
double unit = clock_cpu_getres();
uint64_t unit = clock_cpu_getres();

return (uint64_t)((cpu_tick * unit) / 1000);
return (uint64_t)(((cpu_tick * unit) / (1000UL * 1000)) / 1000);
}

/**
Expand All @@ -93,9 +93,9 @@ uint64_t clock_cpu_microsecond(uint64_t cpu_tick)
*/
uint64_t clock_cpu_millisecond(uint64_t cpu_tick)
{
double unit = clock_cpu_getres();
uint64_t unit = clock_cpu_getres();

return (uint64_t)((cpu_tick * unit) / (1000 * 1000));
return (uint64_t)(((cpu_tick * unit) / (1000UL * 1000)) / (1000UL * 1000));
}

/**
Expand Down
6 changes: 3 additions & 3 deletions components/drivers/cputime/cputime_cortexm.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
#endif

/* Use Cycle counter of Data Watchpoint and Trace Register for CPU time */
static double cortexm_cputime_getres(void)
static uint64_t cortexm_cputime_getres(void)
{
double ret = 1000UL * 1000 * 1000;
uint64_t ret = 1000UL * 1000 * 1000;

ret = ret / SystemCoreClock;
ret = (ret * (1000UL * 1000)) / SystemCoreClock;
return ret;
}

Expand Down
6 changes: 3 additions & 3 deletions components/drivers/cputime/cputime_riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

/* Use Cycle counter of Data Watchpoint and Trace Register for CPU time */

static double riscv_cputime_getres(void)
static uint64_t riscv_cputime_getres(void)
{
double ret = 1000UL * 1000 * 1000;
uint64_t ret = 1000UL * 1000 * 1000;

ret = ret / CPUTIME_TIMER_FREQ;
ret = (ret * (1000UL * 1000)) / CPUTIME_TIMER_FREQ;
return ret;
}

Expand Down
181 changes: 59 additions & 122 deletions components/drivers/cputime/cputimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,78 +5,87 @@
*
* Change Logs:
* Date Author Notes
* 2023-02-13 zhkag first version
* 2023-02-13 zhkag first version
* 2023-04-03 xqyjlj fix cputimer in multithreading
*/

#include <rtthread.h>
#include <rthw.h>
#include <rtdevice.h>
#include <rthw.h>
#include <rtthread.h>

static rt_list_t _cputimer_list = RT_LIST_OBJECT_INIT(_cputimer_list);
static rt_list_t _cputimer_list = RT_LIST_OBJECT_INIT(_cputimer_list);
static struct rt_cputimer *_cputimer_nowtimer = RT_NULL;

static void _cputime_timeout(void *parameter)
static void _cputime_sleep_timeout(void *parameter)
{
struct rt_semaphore *sem;
sem = (struct rt_semaphore *)parameter;
rt_sem_release(sem);
}

static void _cputime_timeout_callback(void *parameter)
{
struct rt_cputimer *timer;
timer = (struct rt_cputimer *)parameter;
timer->timeout_func(timer->parameter);
rt_list_remove(&timer->row);
rt_base_t level;
level = rt_hw_interrupt_disable();
_cputimer_nowtimer = RT_NULL;
rt_list_remove(&(timer->row));
rt_hw_interrupt_enable(level);
timer->timeout_func(&(timer->sem));
}

static void _set_next_timeout()
{
struct rt_cputimer *t;

if (&_cputimer_list != _cputimer_list.prev)
{
struct rt_cputimer *t;
t = rt_list_entry(_cputimer_list.next, struct rt_cputimer, row);
clock_cpu_settimeout(t->timeout_tick, _cputime_timeout, t);
t = rt_list_entry((&_cputimer_list)->next, struct rt_cputimer, row);
if (_cputimer_nowtimer != RT_NULL)
{
if (t != _cputimer_nowtimer && t->timeout_tick < _cputimer_nowtimer->timeout_tick)
{
_cputimer_nowtimer = t;
clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
}
}
else
{
_cputimer_nowtimer = t;
clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
}
}
else
clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
if ((timer->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_cputimer_start(timer);
_cputimer_nowtimer = NULL;
}
}

void rt_cputimer_init(rt_cputimer_t timer,
const char *name,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
void *parameter,
rt_uint64_t tick,
rt_uint8_t flag)
rt_uint8_t flag)
{
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(timeout != RT_NULL);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

/* timer object initialization */
rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);

/* set flag */
timer->parent.flag = flag;

/* set deactivated */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

timer->timeout_func = timeout;
timer->parameter = parameter;
timer->parameter = parameter;
timer->timeout_tick = tick + clock_cpu_gettime();
timer->init_tick = tick;

timer->timeout_tick = 0;
timer->init_tick = tick;
rt_list_init(&(timer->row));
}

static void _set_next_timeout()
{
struct rt_cputimer *t;
if (&_cputimer_list != _cputimer_list.prev)
{
t = rt_list_entry((&_cputimer_list)->next, struct rt_cputimer, row);
clock_cpu_settimeout(t->timeout_tick, _cputime_timeout, t);
}
else
clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
rt_sem_init(&(timer->sem), "cputime", 0, RT_IPC_FLAG_PRIO);
}

rt_err_t rt_cputimer_delete(rt_cputimer_t timer)
Expand All @@ -85,8 +94,6 @@ rt_err_t rt_cputimer_delete(rt_cputimer_t timer)

/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

/* disable interrupt */
Expand All @@ -99,7 +106,6 @@ rt_err_t rt_cputimer_delete(rt_cputimer_t timer)
/* enable interrupt */
rt_hw_interrupt_enable(level);

rt_object_delete(&(timer->parent));
_set_next_timeout();

return RT_EOK;
Expand All @@ -108,11 +114,10 @@ rt_err_t rt_cputimer_delete(rt_cputimer_t timer)
rt_err_t rt_cputimer_start(rt_cputimer_t timer)
{
rt_list_t *timer_list;
rt_base_t level;
rt_base_t level;

/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

/* stop timer firstly */
Expand All @@ -123,8 +128,6 @@ rt_err_t rt_cputimer_start(rt_cputimer_t timer)
/* change status of timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

timer->timeout_tick = clock_cpu_gettime() + timer->init_tick;

timer_list = &_cputimer_list;

for (; timer_list != _cputimer_list.prev;
Expand Down Expand Up @@ -165,7 +168,6 @@ rt_err_t rt_cputimer_stop(rt_cputimer_t timer)

/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
Expand All @@ -191,7 +193,6 @@ rt_err_t rt_cputimer_control(rt_cputimer_t timer, int cmd, void *arg)

/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

level = rt_hw_interrupt_disable();
Expand Down Expand Up @@ -260,8 +261,6 @@ rt_err_t rt_cputimer_detach(rt_cputimer_t timer)

/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
Copy link
Member

Choose a reason for hiding this comment

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

为啥把类型判断都删了啊

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cputimer实际场景是用在us乃至ns级别的场景,把object注册进内核一来用处不大,因为这个object的生命周期就是定时器的时长。二来是因为注册内核和卸载需要频繁拿大小锁,比较耗时,干脆就直接拿掉了

Copy link
Member

Choose a reason for hiding this comment

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

频繁调用的API,这条意见有效。 init/detach这些应该没有影响。

Copy link
Contributor Author

Choose a reason for hiding this comment

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

rt_object_init, rt_object_detach这些都是用的rt_hw_interrupt_disable作为关锁,在多核上,这个rt_hw_interrupt_disable就是一把大锁来的,很耗时

RT_ASSERT(rt_object_is_systemobject(&timer->parent));
RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

/* disable interrupt */
Expand All @@ -275,52 +274,19 @@ rt_err_t rt_cputimer_detach(rt_cputimer_t timer)
/* enable interrupt */
rt_hw_interrupt_enable(level);

rt_object_detach(&(timer->parent));
rt_sem_detach(&(timer->sem));

return RT_EOK;
}

static void _cputime_sleep_timeout(void *parameter)
{
struct rt_thread *thread;
rt_base_t level;

thread = (struct rt_thread *)parameter;

/* parameter check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

/* disable interrupt */
level = rt_hw_interrupt_disable();

/* set error number */
thread->error = -RT_ETIMEOUT;

/* remove from suspend list */
rt_list_remove(&(thread->tlist));

/* insert to schedule ready list */
rt_schedule_insert_thread(thread);

/* enable interrupt */
rt_hw_interrupt_enable(level);

/* do schedule */
rt_schedule();
}

rt_err_t rt_cputime_sleep(rt_uint64_t tick)
{
rt_base_t level;
struct rt_thread *thread;
rt_base_t level;
struct rt_cputimer cputimer;
int err;

if (!clock_cpu_issettimeout())
{
rt_int32_t ms = tick * clock_cpu_getres() / 1000000;
rt_int32_t ms = clock_cpu_millisecond(tick);
return rt_thread_delay(rt_tick_from_millisecond(ms));
}

Expand All @@ -329,53 +295,24 @@ rt_err_t rt_cputime_sleep(rt_uint64_t tick)
return -RT_EINVAL;
}

/* set to current thread */
thread = rt_thread_self();
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

/* current context checking */
RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);

rt_cputimer_init(&cputimer, "cputime_sleep", _cputime_sleep_timeout, thread, 0, RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);
rt_cputimer_init(&cputimer, "cputime_sleep", _cputime_sleep_timeout, &(cputimer.sem), tick,
RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);

/* disable interrupt */
level = rt_hw_interrupt_disable();

/* reset thread error */
thread->error = RT_EOK;

/* suspend thread */
err = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE);

/* reset the timeout of thread timer and start it */
if (err == RT_EOK)
{
rt_cputimer_control(&cputimer, RT_TIMER_CTRL_SET_TIME, &tick);
rt_cputimer_start(&cputimer);

/* enable interrupt */
rt_hw_interrupt_enable(level);

thread->error = -RT_EINTR;

rt_schedule();
if (thread->error == -RT_ETIMEOUT)
thread->error = RT_EOK;
}
else
{
rt_hw_interrupt_enable(level);
}
rt_cputimer_start(&cputimer); /* reset the timeout of thread timer and start it */
rt_hw_interrupt_enable(level);
rt_sem_take_interruptible(&(cputimer.sem), RT_WAITING_FOREVER);

rt_cputimer_detach(&cputimer);
return err;
return RT_EOK;
}

rt_err_t rt_cputime_ndelay(rt_uint64_t ns)
{
double unit = clock_cpu_getres();
return rt_cputime_sleep(ns / unit);
uint64_t unit = clock_cpu_getres();
return rt_cputime_sleep(ns * (1000UL * 1000) / unit);
}

rt_err_t rt_cputime_udelay(rt_uint64_t us)
Expand Down
4 changes: 2 additions & 2 deletions components/drivers/include/drivers/cputime.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@

struct rt_clock_cputime_ops
{
double (*cputime_getres)(void);
uint64_t (*cputime_getres)(void);
uint64_t (*cputime_gettime)(void);
int (*cputime_settimeout)(uint64_t tick, void (*timeout)(void *param), void *param);
};

double clock_cpu_getres(void);
uint64_t clock_cpu_getres(void);
uint64_t clock_cpu_gettime(void);
int clock_cpu_settimeout(uint64_t tick, void (*timeout)(void *param), void *param);
int clock_cpu_issettimeout(void);
Expand Down
1 change: 1 addition & 0 deletions components/drivers/include/drivers/cputimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct rt_cputimer
void *parameter;
rt_uint64_t init_tick;
rt_uint64_t timeout_tick;
struct rt_semaphore sem;
};
typedef struct rt_cputimer *rt_cputimer_t;

Expand Down
Loading