Browse Source

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

Signed-off-by: Shell <smokewood@qq.com>
Shell 1 year ago
parent
commit
f6fce5f8d7

+ 1 - 1
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
 

+ 1 - 14
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);
 
 }
 

+ 2 - 2
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);
     }

+ 4 - 0
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

+ 3 - 0
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')

+ 68 - 0
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 <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);

+ 5 - 0
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);

+ 5 - 0
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

+ 94 - 18
src/kservice.c

@@ -1485,17 +1485,83 @@ 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.
  */
-void rt_kputs(const char *str)
+static void _kputs(const char *str, long len)
 {
-    if (!str)
-    {
-        return;
-    }
+    ACQUIRE_CONSOLE;
 
 #ifdef RT_USING_DEVICE
     if (_console_device == RT_NULL)
@@ -1504,11 +1570,28 @@ void rt_kputs(const char *str)
     }
     else
     {
-        rt_device_write(_console_device, 0, str, rt_strlen(str));
+        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.
+ *
+ * @param str is the string output to the console.
+ */
+void rt_kputs(const char *str)
+{
+    if (!str)
+    {
+        return;
+    }
+
+    _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;