[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
|
||||
endif
|
||||
|
||||
config RT_USING_ULOG
|
||||
menuconfig RT_USING_ULOG
|
||||
bool "Enable ulog"
|
||||
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,
|
||||
const char *log, rt_size_t len)
|
||||
{
|
||||
#ifdef RT_USING_DEVICE
|
||||
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
|
||||
rt_kputs(log);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ static void output_unlock(void)
|
|||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ static void output_lock(void)
|
|||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
|
@ -65,4 +65,8 @@ config UTEST_HOOKLIST_TC
|
|||
select RT_USING_HOOKLIST
|
||||
default n
|
||||
|
||||
config UTEST_MTSAFE_KPRINT_TC
|
||||
bool "mtsafe kprint test"
|
||||
default n
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -47,6 +47,9 @@ if GetDepend(['UTEST_ATOMIC_TC']):
|
|||
if GetDepend(['UTEST_HOOKLIST_TC']):
|
||||
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)
|
||||
|
||||
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)
|
||||
rt_device_t rt_console_set_device(const char *name);
|
||||
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) */
|
||||
|
||||
rt_err_t rt_get_errno(void);
|
||||
|
|
|
@ -417,6 +417,10 @@ config RT_USING_INTERRUPT_INFO
|
|||
help
|
||||
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
|
||||
bool "Using console for rt_kprintf"
|
||||
default y
|
||||
|
@ -429,6 +433,7 @@ if RT_USING_CONSOLE
|
|||
config RT_CONSOLE_DEVICE_NAME
|
||||
string "the device name for console"
|
||||
default "uart1"
|
||||
|
||||
endif
|
||||
|
||||
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);
|
||||
|
||||
#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.
|
||||
*
|
||||
|
@ -1497,18 +1591,7 @@ void rt_kputs(const char *str)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_DEVICE
|
||||
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 */
|
||||
_kputs(str, rt_strlen(str));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1525,6 +1608,8 @@ rt_weak int rt_kprintf(const char *fmt, ...)
|
|||
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
|
||||
|
||||
va_start(args, fmt);
|
||||
ACQUIRE_PRINTF_BUFFER;
|
||||
|
||||
/* 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
|
||||
* 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;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_DEVICE
|
||||
if (_console_device == RT_NULL)
|
||||
{
|
||||
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 */
|
||||
_kputs(rt_log_buf, length);
|
||||
|
||||
RELEASE_PRINTF_BUFFER;
|
||||
va_end(args);
|
||||
|
||||
return length;
|
||||
|
|
Loading…
Reference in New Issue