[kservice] MT-safe output support (kprintf/kputs)

Signed-off-by: Shell <smokewood@qq.com>
This commit is contained in:
Shell 2024-01-09 20:37:47 +08:00 committed by Meco Man
parent 447a22a21d
commit f6fce5f8d7
9 changed files with 189 additions and 41 deletions

View File

@ -15,7 +15,7 @@ config RT_USING_RYM
default y
endif
config RT_USING_ULOG
menuconfig RT_USING_ULOG
bool "Enable ulog"
default n

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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')

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;