diff --git a/components/utilities/Kconfig b/components/utilities/Kconfig index a1597db9b5..1ddbdfc3ea 100644 --- a/components/utilities/Kconfig +++ b/components/utilities/Kconfig @@ -15,7 +15,7 @@ config RT_USING_RYM default y endif -config RT_USING_ULOG +menuconfig RT_USING_ULOG bool "Enable ulog" default n diff --git a/components/utilities/ulog/backend/console_be.c b/components/utilities/ulog/backend/console_be.c index 20c87cb2eb..f4c2670485 100644 --- a/components/utilities/ulog/backend/console_be.c +++ b/components/utilities/ulog/backend/console_be.c @@ -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); } diff --git a/components/utilities/ulog/ulog.c b/components/utilities/ulog/ulog.c index 18302a3380..4e091f9a2b 100644 --- a/components/utilities/ulog/ulog.c +++ b/components/utilities/ulog/ulog.c @@ -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); } diff --git a/examples/utest/testcases/kernel/Kconfig b/examples/utest/testcases/kernel/Kconfig index fc92292f0b..988f13f43f 100644 --- a/examples/utest/testcases/kernel/Kconfig +++ b/examples/utest/testcases/kernel/Kconfig @@ -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 diff --git a/examples/utest/testcases/kernel/SConscript b/examples/utest/testcases/kernel/SConscript index ea2780e4d7..598b92251a 100644 --- a/examples/utest/testcases/kernel/SConscript +++ b/examples/utest/testcases/kernel/SConscript @@ -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') diff --git a/examples/utest/testcases/kernel/mtsafe_kprint_tc.c b/examples/utest/testcases/kernel/mtsafe_kprint_tc.c new file mode 100644 index 0000000000..f6934e6b21 --- /dev/null +++ b/examples/utest/testcases/kernel/mtsafe_kprint_tc.c @@ -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 +#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); diff --git a/include/rtthread.h b/include/rtthread.h index 7ccf3b56c6..6f476a0008 100644 --- a/include/rtthread.h +++ b/include/rtthread.h @@ -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); diff --git a/src/Kconfig b/src/Kconfig index 75e19744e2..81b71be4a1 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -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 diff --git a/src/kservice.c b/src/kservice.c index 3a42b65410..8323ea6196 100644 --- a/src/kservice.c +++ b/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;