[kservice] MT-safe output support (kprintf/kputs)
Signed-off-by: Shell <smokewood@qq.com>
This commit is contained in:
parent
447a22a21d
commit
f6fce5f8d7
|
@ -15,7 +15,7 @@ config RT_USING_RYM
|
||||||
default y
|
default y
|
||||||
endif
|
endif
|
||||||
|
|
||||||
config RT_USING_ULOG
|
menuconfig RT_USING_ULOG
|
||||||
bool "Enable ulog"
|
bool "Enable ulog"
|
||||||
default n
|
default n
|
||||||
|
|
||||||
|
|
|
@ -22,20 +22,7 @@ static struct ulog_backend console = { 0 };
|
||||||
void ulog_console_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw,
|
void ulog_console_backend_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw,
|
||||||
const char *log, rt_size_t len)
|
const char *log, rt_size_t len)
|
||||||
{
|
{
|
||||||
#ifdef RT_USING_DEVICE
|
rt_kputs(log);
|
||||||
rt_device_t dev = rt_console_get_device();
|
|
||||||
|
|
||||||
if (dev == RT_NULL)
|
|
||||||
{
|
|
||||||
rt_hw_console_output(log);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rt_device_write(dev, 0, log, len);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
rt_hw_console_output(log);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ static void output_unlock(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the scheduler is started and in thread context */
|
/* If the scheduler is started and in thread context */
|
||||||
if (rt_interrupt_get_nest() == 0 && rt_thread_self() != RT_NULL)
|
if (rt_scheduler_is_available())
|
||||||
{
|
{
|
||||||
rt_mutex_release(&ulog.output_locker);
|
rt_mutex_release(&ulog.output_locker);
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ static void output_lock(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the scheduler is started and in thread context */
|
/* If the scheduler is started and in thread context */
|
||||||
if (rt_interrupt_get_nest() == 0 && rt_thread_self() != RT_NULL)
|
if (rt_scheduler_is_available())
|
||||||
{
|
{
|
||||||
rt_mutex_take(&ulog.output_locker, RT_WAITING_FOREVER);
|
rt_mutex_take(&ulog.output_locker, RT_WAITING_FOREVER);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,4 +65,8 @@ config UTEST_HOOKLIST_TC
|
||||||
select RT_USING_HOOKLIST
|
select RT_USING_HOOKLIST
|
||||||
default n
|
default n
|
||||||
|
|
||||||
|
config UTEST_MTSAFE_KPRINT_TC
|
||||||
|
bool "mtsafe kprint test"
|
||||||
|
default n
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -47,6 +47,9 @@ if GetDepend(['UTEST_ATOMIC_TC']):
|
||||||
if GetDepend(['UTEST_HOOKLIST_TC']):
|
if GetDepend(['UTEST_HOOKLIST_TC']):
|
||||||
src += ['hooklist_tc.c']
|
src += ['hooklist_tc.c']
|
||||||
|
|
||||||
|
if GetDepend(['UTEST_MTSAFE_KPRINT_TC']):
|
||||||
|
src += ['mtsafe_kprint_tc.c']
|
||||||
|
|
||||||
group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH)
|
group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH)
|
||||||
|
|
||||||
Return('group')
|
Return('group')
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2023-12-25 Shell the first version
|
||||||
|
*/
|
||||||
|
#include <rtthread.h>
|
||||||
|
#include "utest.h"
|
||||||
|
|
||||||
|
#define TEST_LOOP_TIMES 20
|
||||||
|
|
||||||
|
static struct rt_semaphore _thr_exit_sem;
|
||||||
|
|
||||||
|
static void _thread_entry(void *param)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < TEST_LOOP_TIMES; i++)
|
||||||
|
{
|
||||||
|
rt_kprintf("This is thread %p\n", rt_thread_self());
|
||||||
|
rt_thread_mdelay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
rt_sem_release(&_thr_exit_sem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TEST_THREAD_COUNT 16
|
||||||
|
|
||||||
|
static void mtsafe_kprint_tc(void)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < TEST_THREAD_COUNT; i++)
|
||||||
|
{
|
||||||
|
rt_thread_t new_thread =
|
||||||
|
rt_thread_create(
|
||||||
|
"test",
|
||||||
|
_thread_entry,
|
||||||
|
NULL,
|
||||||
|
UTEST_THR_STACK_SIZE,
|
||||||
|
UTEST_THR_PRIORITY,
|
||||||
|
100);
|
||||||
|
rt_thread_startup(new_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < TEST_THREAD_COUNT; i++)
|
||||||
|
{
|
||||||
|
rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t utest_tc_init(void)
|
||||||
|
{
|
||||||
|
rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rt_err_t utest_tc_cleanup(void)
|
||||||
|
{
|
||||||
|
rt_sem_detach(&_thr_exit_sem);
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testcase(void)
|
||||||
|
{
|
||||||
|
UTEST_UNIT_RUN(mtsafe_kprint_tc);
|
||||||
|
}
|
||||||
|
UTEST_TC_EXPORT(testcase, "testcases.kernel.mtsafe_kprint", utest_tc_init, utest_tc_cleanup, 10);
|
|
@ -707,6 +707,11 @@ int rt_snprintf(char *buf, rt_size_t size, const char *format, ...);
|
||||||
#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
|
#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
|
||||||
rt_device_t rt_console_set_device(const char *name);
|
rt_device_t rt_console_set_device(const char *name);
|
||||||
rt_device_t rt_console_get_device(void);
|
rt_device_t rt_console_get_device(void);
|
||||||
|
#ifdef RT_USING_THREDSAFE_PRINTF
|
||||||
|
rt_thread_t rt_console_current_user(void);
|
||||||
|
#else
|
||||||
|
rt_inline void *rt_console_current_user(void) { return RT_NULL; }
|
||||||
|
#endif /* RT_USING_THREDSAFE_PRINTF */
|
||||||
#endif /* defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE) */
|
#endif /* defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE) */
|
||||||
|
|
||||||
rt_err_t rt_get_errno(void);
|
rt_err_t rt_get_errno(void);
|
||||||
|
|
|
@ -417,6 +417,10 @@ config RT_USING_INTERRUPT_INFO
|
||||||
help
|
help
|
||||||
Add name and counter information for interrupt trace.
|
Add name and counter information for interrupt trace.
|
||||||
|
|
||||||
|
config RT_USING_THREDSAFE_PRINTF
|
||||||
|
bool "Enable thread safe kernel print service"
|
||||||
|
default y if RT_USING_SMP && RT_USING_SMART
|
||||||
|
|
||||||
config RT_USING_CONSOLE
|
config RT_USING_CONSOLE
|
||||||
bool "Using console for rt_kprintf"
|
bool "Using console for rt_kprintf"
|
||||||
default y
|
default y
|
||||||
|
@ -429,6 +433,7 @@ if RT_USING_CONSOLE
|
||||||
config RT_CONSOLE_DEVICE_NAME
|
config RT_CONSOLE_DEVICE_NAME
|
||||||
string "the device name for console"
|
string "the device name for console"
|
||||||
default "uart1"
|
default "uart1"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
config RT_VER_NUM
|
config RT_VER_NUM
|
||||||
|
|
124
src/kservice.c
124
src/kservice.c
|
@ -1485,6 +1485,100 @@ rt_weak void rt_hw_console_output(const char *str)
|
||||||
}
|
}
|
||||||
RTM_EXPORT(rt_hw_console_output);
|
RTM_EXPORT(rt_hw_console_output);
|
||||||
|
|
||||||
|
#ifdef RT_USING_THREDSAFE_PRINTF
|
||||||
|
|
||||||
|
static struct rt_spinlock _pr_lock = RT_SPINLOCK_INIT;
|
||||||
|
static struct rt_spinlock _prf_lock = RT_SPINLOCK_INIT;
|
||||||
|
/* current user of system console */
|
||||||
|
static rt_thread_t _pr_curr_user;
|
||||||
|
/* nested level of current user */
|
||||||
|
static int _pr_curr_user_nested;
|
||||||
|
|
||||||
|
rt_thread_t rt_console_current_user(void)
|
||||||
|
{
|
||||||
|
return _pr_curr_user;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _acquire_console(void)
|
||||||
|
{
|
||||||
|
rt_ubase_t level = rt_spin_lock_irqsave(&_pr_lock);
|
||||||
|
rt_thread_t self_thread = rt_thread_self();
|
||||||
|
|
||||||
|
while (_pr_curr_user != self_thread)
|
||||||
|
{
|
||||||
|
if (_pr_curr_user == RT_NULL)
|
||||||
|
{
|
||||||
|
/* no preemption is allowed to avoid dead lock */
|
||||||
|
rt_enter_critical();
|
||||||
|
_pr_curr_user = self_thread;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_spin_unlock_irqrestore(&_pr_lock, level);
|
||||||
|
rt_thread_yield();
|
||||||
|
level = rt_spin_lock_irqsave(&_pr_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pr_curr_user_nested++;
|
||||||
|
|
||||||
|
rt_spin_unlock_irqrestore(&_pr_lock, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _release_console(void)
|
||||||
|
{
|
||||||
|
rt_ubase_t level = rt_spin_lock_irqsave(&_pr_lock);
|
||||||
|
rt_thread_t self_thread = rt_thread_self();
|
||||||
|
|
||||||
|
RT_ASSERT(_pr_curr_user == self_thread);
|
||||||
|
|
||||||
|
_pr_curr_user_nested--;
|
||||||
|
if (!_pr_curr_user_nested)
|
||||||
|
{
|
||||||
|
_pr_curr_user = RT_NULL;
|
||||||
|
rt_exit_critical();
|
||||||
|
}
|
||||||
|
rt_spin_unlock_irqrestore(&_pr_lock, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ACQUIRE_CONSOLE _acquire_console()
|
||||||
|
#define RELEASE_CONSOLE _release_console()
|
||||||
|
#define ACQUIRE_PRINTF_BUFFER rt_ubase_t level = rt_spin_lock_irqsave(&_prf_lock)
|
||||||
|
#define RELEASE_PRINTF_BUFFER rt_spin_unlock_irqrestore(&_prf_lock, level)
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define ACQUIRE_CONSOLE
|
||||||
|
#define RELEASE_CONSOLE
|
||||||
|
#define ACQUIRE_PRINTF_BUFFER
|
||||||
|
#define RELEASE_PRINTF_BUFFER
|
||||||
|
#endif /* RT_USING_THREDSAFE_PRINTF */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function will put string to the console.
|
||||||
|
*
|
||||||
|
* @param str is the string output to the console.
|
||||||
|
*/
|
||||||
|
static void _kputs(const char *str, long len)
|
||||||
|
{
|
||||||
|
ACQUIRE_CONSOLE;
|
||||||
|
|
||||||
|
#ifdef RT_USING_DEVICE
|
||||||
|
if (_console_device == RT_NULL)
|
||||||
|
{
|
||||||
|
rt_hw_console_output(str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rt_device_write(_console_device, 0, str, len);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
rt_hw_console_output(str);
|
||||||
|
#endif /* RT_USING_DEVICE */
|
||||||
|
|
||||||
|
RELEASE_CONSOLE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function will put string to the console.
|
* @brief This function will put string to the console.
|
||||||
*
|
*
|
||||||
|
@ -1497,18 +1591,7 @@ void rt_kputs(const char *str)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef RT_USING_DEVICE
|
_kputs(str, rt_strlen(str));
|
||||||
if (_console_device == RT_NULL)
|
|
||||||
{
|
|
||||||
rt_hw_console_output(str);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rt_device_write(_console_device, 0, str, rt_strlen(str));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
rt_hw_console_output(str);
|
|
||||||
#endif /* RT_USING_DEVICE */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1525,6 +1608,8 @@ rt_weak int rt_kprintf(const char *fmt, ...)
|
||||||
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
|
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
ACQUIRE_PRINTF_BUFFER;
|
||||||
|
|
||||||
/* the return value of vsnprintf is the number of bytes that would be
|
/* the return value of vsnprintf is the number of bytes that would be
|
||||||
* written to buffer had if the size of the buffer been sufficiently
|
* written to buffer had if the size of the buffer been sufficiently
|
||||||
* large excluding the terminating null byte. If the output string
|
* large excluding the terminating null byte. If the output string
|
||||||
|
@ -1536,18 +1621,9 @@ rt_weak int rt_kprintf(const char *fmt, ...)
|
||||||
length = RT_CONSOLEBUF_SIZE - 1;
|
length = RT_CONSOLEBUF_SIZE - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef RT_USING_DEVICE
|
_kputs(rt_log_buf, length);
|
||||||
if (_console_device == RT_NULL)
|
|
||||||
{
|
RELEASE_PRINTF_BUFFER;
|
||||||
rt_hw_console_output(rt_log_buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rt_device_write(_console_device, 0, rt_log_buf, length);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
rt_hw_console_output(rt_log_buf);
|
|
||||||
#endif /* RT_USING_DEVICE */
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
|
|
Loading…
Reference in New Issue