first commit for chrg

This commit is contained in:
wmano
2025-08-16 22:58:22 +08:00
commit 52a3ed5862
2306 changed files with 1021208 additions and 0 deletions
+448
View File
@@ -0,0 +1,448 @@
menu "RT-Thread Kernel"
rsource "klibc/Kconfig"
config RT_NAME_MAX
int "The maximal size of kernel object name"
range 2 64
default 8
help
Each kernel object, such as thread, timer, semaphore etc, has a name,
the RT_NAME_MAX is the maximal size of this object name.
config RT_USING_ARCH_DATA_TYPE
bool "Use the data types defined in ARCH_CPU"
default n
help
For the data type like, `rt_uint8/int8_t, rt_uint16/int16_t, rt_uint32/int32_t`,
BSP can define these basic data types in ARCH_CPU level.
Please re-define these data types in rtconfig_project.h file.
config RT_USING_NANO
bool "Enable RT-Thread Nano"
default n
help
RT-Thread Nano is a very small size and refined hard real-time kernel,
which is suited for the extremely resource-constrained MCU system.
config RT_USING_SMART
bool "Enable RT-Thread Smart (microkernel on kernel/userland)"
default n
select RT_USING_LWP
select RT_USING_DFS
select RT_USING_POSIX_CLOCKTIME
select RT_USING_DEVICE
select RT_USING_NULL
select RT_USING_ZERO
select RT_USING_RANDOM
select RT_USING_RTC
select RT_USING_POSIX_TIMER
select RT_USING_POSIX_CLOCK
select RT_USING_POSIX_FS
select RT_USING_POSIX_TERMIOS
select RT_USING_KTIME
select RT_USING_STDC_ATOMIC
select RT_USING_SYSTEM_WORKQUEUE
select RT_USING_CPU_USAGE_TRACER
select RT_USING_SCHED_THREAD_CTX
depends on ARCH_ARM_CORTEX_M || ARCH_ARM_ARM9 || ARCH_ARM_CORTEX_A || ARCH_ARMV8 || ARCH_RISCV64
depends on !RT_USING_NANO
help
RT-Thread Smart is a microkernel based operating system on RT-Thread.
config RT_USING_AMP
bool "Enable AMP (Asymmetric Multi-Processing)"
default n
if RT_USING_AMP
choice
prompt "Select the AMP role"
default RT_AMP_SLAVE
config RT_AMP_MASTER
bool "amp role MASTER"
config RT_AMP_SLAVE
bool "amp role SLAVE"
endchoice
endif
config RT_USING_SMP
bool "Enable SMP (Symmetric multiprocessing)"
default n
help
This option should be selected by machines which have an SMP-
capable CPU.
The only effect of this option is to make the SMP-related
options available to the user for configuration.
config RT_CPUS_NR
int "Number of CPUs"
default 1
range 1 1 if !RT_USING_SMP && !RT_USING_AMP
help
Number of CPUs in the system
config RT_ALIGN_SIZE
int "Alignment size for CPU architecture data access"
default 8
help
Alignment size for CPU architecture data access
choice
prompt "The maximal level value of priority of thread"
default RT_THREAD_PRIORITY_32
config RT_THREAD_PRIORITY_8
bool "8"
config RT_THREAD_PRIORITY_32
bool "32"
config RT_THREAD_PRIORITY_256
bool "256"
endchoice
config RT_THREAD_PRIORITY_MAX
int
default 8 if RT_THREAD_PRIORITY_8
default 32 if RT_THREAD_PRIORITY_32
default 256 if RT_THREAD_PRIORITY_256
config RT_TICK_PER_SECOND
int "Tick frequency, Hz"
range 10 1000
default 1000
help
System's tick frequency, Hz.
config RT_USING_OVERFLOW_CHECK
bool "Using stack overflow checking"
default y if RT_USING_DEBUG
help
Enable thread stack overflow checking. The stack overflow is checking when
each thread switch.
config RT_USING_HOOK
bool "Enable system hook"
default y
select RT_USING_IDLE_HOOK
help
Enable the hook function when system running, such as idle thread hook,
thread context switch etc.
if RT_USING_HOOK
config RT_HOOK_USING_FUNC_PTR
bool "Using function pointers as system hook"
default y
endif
config RT_USING_HOOKLIST
bool "Enable hook list"
default n
help
Enable the hook list feature for rt-thread packages. With this, they can
plug in to the system on run-time.
config RT_USING_IDLE_HOOK
bool "Enable IDLE Task hook"
default y if RT_USING_HOOK
if RT_USING_IDLE_HOOK
config RT_IDLE_HOOK_LIST_SIZE
int "The max size of idle hook list"
default 4
range 1 16
help
The system has a hook list. This is the hook list size.
endif
config IDLE_THREAD_STACK_SIZE
int "The stack size of idle thread"
default 1024 if ARCH_CPU_64BIT
default 256
config SYSTEM_THREAD_STACK_SIZE
int "The stack size of system thread (for defunct etc.)"
depends on RT_USING_SMP
default IDLE_THREAD_STACK_SIZE
config RT_USING_TIMER_SOFT
bool "Enable software timer with a timer thread"
default y
help
the timeout function context of soft-timer is under a high priority timer
thread.
if RT_USING_TIMER_SOFT
config RT_TIMER_THREAD_PRIO
int "The priority level value of timer thread"
default 4
config RT_TIMER_THREAD_STACK_SIZE
int "The stack size of timer thread"
default 2048 if ARCH_CPU_64BIT
default 512
config RT_USING_TIMER_ALL_SOFT
bool "Set all timer as soft timer"
default n
endif
config RT_USING_CPU_USAGE_TRACER
select RT_USING_HOOK
bool "Enable cpu usage tracing"
help
Enable cpu usage tracer for application like top.
default y if RT_USING_SMART
default n
menu "kservice options"
config RT_USING_TINY_FFS
bool "Enable kservice to use tiny finding first bit set method"
default n
endmenu
menuconfig RT_USING_DEBUG
bool "Enable debugging features"
default y
if RT_USING_DEBUG
config RT_DEBUGING_ASSERT
bool "Enable assertion debugging"
default y
config RT_DEBUGING_COLOR
bool "Enable color debugging log"
default y
config RT_DEBUGING_CONTEXT
bool "Enable debugging of environment and context check"
default y
config RT_DEBUGING_AUTO_INIT
bool "Enable debugging of components automatic initialization"
default n
config RT_DEBUGING_SPINLOCK
bool "Enable spinlock debugging"
depends on RT_USING_SMP
default n
config RT_DEBUGING_CRITICAL
bool "Enable critical level tracing"
depends on RT_USING_SMP
default y if RT_USING_SMART
default n
endif
config RT_USING_CI_ACTION
bool "Enable CI Action build mode"
select RT_USING_UTEST
select RT_UTEST_USING_AUTO_RUN
select RT_UTEST_USING_ALL_CASES
default n
help
Identify that the environment is CI Action.
menu "Inter-Thread communication"
config RT_USING_SEMAPHORE
bool "Enable semaphore"
default y
config RT_USING_MUTEX
bool "Enable mutex"
default y
config RT_USING_EVENT
bool "Enable event flag"
default y
config RT_USING_MAILBOX
bool "Enable mailbox"
default y
config RT_USING_MESSAGEQUEUE
bool "Enable message queue"
default y
config RT_USING_MESSAGEQUEUE_PRIORITY
bool "Enable message queue priority"
depends on RT_USING_MESSAGEQUEUE
default n
config RT_USING_SIGNALS
bool "Enable signals"
select RT_USING_MEMPOOL
default n
help
A signal is an asynchronous notification sent to a specific thread
in order to notify it of an event that occurred.
endmenu
menu "Memory Management"
config RT_USING_MEMPOOL
bool "Using memory pool"
default y
help
Using static memory fixed partition
config RT_USING_SMALL_MEM
bool "Using Small Memory Algorithm"
default n
help
Using Small Memory Algorithm
config RT_USING_SLAB
bool "Using SLAB Memory Algorithm"
default n
help
The slab allocator of RT-Thread is a memory allocation algorithm
optimizedfor embedded systems based on the slab allocator
implemented by Matthew Dillon, founder of dragonfly BSD.
The original slab algorithm is an efficient kernel memory
allocation algorithm introduced by Jeff bonwick for
Solaris Operating System.
menuconfig RT_USING_MEMHEAP
bool "Using memheap Memory Algorithm"
default n
if RT_USING_MEMHEAP
choice
prompt "Memheap memory allocation mode"
default RT_MEMHEAP_FAST_MODE
config RT_MEMHEAP_FAST_MODE
bool "fast mode"
help
Speed priority mode.
As long as the memory block size meets the requirements, the search ends immediately.
config RT_MEMHEAP_BEST_MODE
bool "best mode"
help
Best size first.
The search does not end until the memory block of the most appropriate size is found
endchoice
endif
choice
prompt "System Heap Memory Management"
default RT_USING_SMALL_MEM_AS_HEAP
config RT_USING_SMALL_MEM_AS_HEAP
bool "Small Memory Algorithm"
select RT_USING_SMALL_MEM
config RT_USING_MEMHEAP_AS_HEAP
bool "Use memheap objects as heap"
select RT_USING_MEMHEAP
if RT_USING_MEMHEAP_AS_HEAP
config RT_USING_MEMHEAP_AUTO_BINDING
bool "Use all of memheap objects as heap"
default y
endif
config RT_USING_SLAB_AS_HEAP
bool "SLAB Algorithm for large memory"
select RT_USING_SLAB
config RT_USING_USERHEAP
bool "Use user heap"
help
If this option is selected, please implement these functions:
rt_malloc(), rt_malloc_sethook()
rt_free(), rt_free_sethook()
rt_calloc(), rt_realloc()
rt_memory_info()
rt_system_heap_init()
config RT_USING_NOHEAP
bool "Disable Heap"
endchoice
config RT_USING_MEMTRACE
bool "Enable memory trace"
default n
help
When enable RT_USING_MEMTRACE with shell, developer can call cmd:
1. memtrace
to dump memory block information.
2. memcheck
to check memory block to avoid memory overwritten.
And developer also can call memcheck() in each of scheduling
to check memory block to find which thread has wrongly modified
memory.
config RT_USING_HEAP_ISR
bool "Using heap in ISR"
default n
help
When this option is enabled, the critical zone will be protected with disable interrupt.
config RT_USING_HEAP
bool
default n if RT_USING_NOHEAP
default y if RT_USING_SMALL_MEM
default y if RT_USING_SLAB
default y if RT_USING_MEMHEAP_AS_HEAP
default y if RT_USING_USERHEAP
endmenu
config RT_USING_DEVICE
bool "Using device object"
depends on !RT_USING_NANO
default y
config RT_USING_DEVICE_OPS
bool "Using ops for each device object"
depends on RT_USING_DEVICE
default n
config RT_USING_INTERRUPT_INFO
bool "Enable additional interrupt trace information"
default n
help
Add name and counter information for interrupt trace.
config RT_USING_THREADSAFE_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
if RT_USING_CONSOLE
config RT_CONSOLEBUF_SIZE
int "the buffer size for console log printf"
default 256 if RT_USING_UTEST
default 128
config RT_CONSOLE_DEVICE_NAME
string "the device name for console"
default "uart1"
endif
config RT_VER_NUM
hex
default 0x50201
help
RT-Thread version number
config RT_USING_STDC_ATOMIC
bool "Use atomic implemented in stdatomic.h"
default n
config RT_BACKTRACE_LEVEL_MAX_NR
int "Max number of backtrace level"
default 32
endmenu
+67
View File
@@ -0,0 +1,67 @@
from building import *
import os
src = Glob('*.c')
cwd = GetCurrentDir()
inc = [os.path.join(cwd, '..', 'include')]
if GetDepend('RT_USING_SMALL_MEM') == False:
SrcRemove(src, ['mem.c'])
if GetDepend('RT_USING_SLAB') == False:
SrcRemove(src, ['slab.c'])
if GetDepend('RT_USING_MEMPOOL') == False:
SrcRemove(src, ['mempool.c'])
if GetDepend('RT_USING_MEMHEAP') == False:
SrcRemove(src, ['memheap.c'])
if GetDepend('RT_USING_SIGNALS') == False:
SrcRemove(src, ['signal.c'])
if GetDepend('RT_USING_DEVICE') == False:
SrcRemove(src, ['device.c'])
if GetDepend('RT_USING_SMP') == False:
SrcRemove(src, ['cpu_mp.c', 'scheduler_mp.c'])
else:
SrcRemove(src, ['cpu_up.c', 'scheduler_up.c'])
LOCAL_CFLAGS = ''
LINKFLAGS = ''
if rtconfig.PLATFORM in ['gcc']: # only for GCC
LOCAL_CFLAGS += ' -Wunused' # unused warning
LOCAL_CFLAGS += ' -Wformat -Wformat-security' # printf/scanf format warning
LOCAL_CFLAGS += ' -Warray-bounds -Wuninitialized' # memory access warning
LOCAL_CFLAGS += ' -Wreturn-type -Wcomment -Wswitch' # code style warning
LOCAL_CFLAGS += ' -Wparentheses -Wlogical-op ' # operation warning
LOCAL_CFLAGS += ' -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes' # function declaration warning
if 'mips' not in rtconfig.PREFIX: # mips toolchain does not support
LOCAL_CFLAGS += ' -Wimplicit-fallthrough' # implicit fallthrough warning
LOCAL_CFLAGS += ' -Wduplicated-cond -Wduplicated-branches' # duplicated condition warning
if rtconfig.ARCH not in ['sim']:
LINKFLAGS += ' -Wl,--gc-sections,--print-memory-usage' # remove unused sections and print memory usage
if GetDepend('RT_USING_HOOKLIST') == True:
if rtconfig.PLATFORM in ['gcc', 'armclang']:
LOCAL_CFLAGS += ' -std=gnu99'
elif rtconfig.PLATFORM in ['armcc']:
LOCAL_CFLAGS += ' --c99 --gnu'
if rtconfig.CROSS_TOOL == 'msvc':
group = DefineGroup('Kernel', src, depend=[''], CPPPATH=inc,
LINKFLAGS=LINKFLAGS, LOCAL_CFLAGS=LOCAL_CFLAGS,
CPPDEFINES=['__RTTHREAD__', '__RT_KERNEL_SOURCE__'])
else:
group = DefineGroup('Kernel', src, depend=[''], CPPPATH=inc,
LINKFLAGS=LINKFLAGS, LOCAL_CFLAGS=LOCAL_CFLAGS,
CPPDEFINES=['__RTTHREAD__'], LOCAL_CPPDEFINES=['__RT_KERNEL_SOURCE__'])
list = os.listdir(cwd)
for item in list:
if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
group = group + SConscript(os.path.join(item, 'SConscript'))
Return('group')
+261
View File
@@ -0,0 +1,261 @@
/*
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-12 Bernard first version
* 2006-05-27 Bernard add support for same priority thread schedule
* 2006-08-10 Bernard remove the last rt_schedule in rt_tick_increase
* 2010-03-08 Bernard remove rt_passed_second
* 2010-05-20 Bernard fix the tick exceeds the maximum limits
* 2010-07-13 Bernard fix rt_tick_from_millisecond issue found by kuronca
* 2011-06-26 Bernard add rt_tick_set function.
* 2018-11-22 Jesven add per cpu tick
* 2020-12-29 Meco Man implement rt_tick_get_millisecond()
* 2021-06-01 Meco Man add critical section projection for rt_tick_increase()
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-10-16 RiceChen fix: only the main core detection rt_timer_check(), in SMP mode
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtatomic.h>
#if defined(RT_USING_SMART) && defined(RT_USING_VDSO)
#include <vdso.h>
#endif
#ifdef RT_USING_SMP
#define rt_tick rt_cpu_index(0)->tick
#else
static volatile rt_atomic_t rt_tick = 0;
#endif /* RT_USING_SMP */
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_tick_hook)(void);
/**
* @addtogroup group_hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when tick increase
*
*
* @param hook the hook function
*/
void rt_tick_sethook(void (*hook)(void))
{
rt_tick_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
/**
* @addtogroup group_clock_management
*/
/**@{*/
/**
* @brief This function will return current tick from operating system startup.
*
* @return Return current tick.
*/
rt_tick_t rt_tick_get(void)
{
/* return the global tick */
return (rt_tick_t)rt_atomic_load(&(rt_tick));
}
RTM_EXPORT(rt_tick_get);
/**
* @brief This function will return delta tick from base.
*
* @param base to consider
*
* @return Return delta tick.
*/
rt_tick_t rt_tick_get_delta(rt_tick_t base)
{
rt_tick_t tnow = rt_tick_get();
if (tnow >= base)
return tnow - base;
return RT_TICK_MAX - base + tnow + 1;
}
RTM_EXPORT(rt_tick_get_delta);
/**
* @brief This function will set current tick.
*
* @param tick is the value that you will set.
*/
void rt_tick_set(rt_tick_t tick)
{
rt_atomic_store(&(rt_tick), tick);
}
#ifdef RT_USING_CPU_USAGE_TRACER
static void _update_process_times(rt_tick_t tick)
{
struct rt_thread *thread = rt_thread_self();
struct rt_cpu *pcpu = rt_cpu_self();
if (!LWP_IS_USER_MODE(thread))
{
thread->user_time += tick;
pcpu->cpu_stat.user += tick;
}
else
{
thread->system_time += tick;
if (thread == pcpu->idle_thread)
{
pcpu->cpu_stat.idle += tick;
}
else
{
pcpu->cpu_stat.system += tick;
}
}
}
#else
#define _update_process_times(tick)
#endif /* RT_USING_CPU_USAGE_TRACER */
/**
* @brief This function will notify kernel there is one tick passed.
* Normally, this function is invoked by clock ISR.
*/
void rt_tick_increase(void)
{
RT_ASSERT(rt_interrupt_get_nest() > 0);
RT_OBJECT_HOOK_CALL(rt_tick_hook, ());
/* tracing cpu usage */
_update_process_times(1);
/* increase the global tick */
#ifdef RT_USING_SMP
/* get percpu and increase the tick */
rt_atomic_add(&(rt_cpu_self()->tick), 1);
#else
rt_atomic_add(&(rt_tick), 1);
#endif /* RT_USING_SMP */
/* check time slice */
rt_sched_tick_increase(1);
/* check timer */
#ifdef RT_USING_SMP
if (rt_cpu_get_id() != 0)
{
return;
}
#endif
rt_timer_check();
}
/**
* @brief This function will notify kernel there is n tick passed.
* Normally, this function is invoked by clock ISR.
*/
void rt_tick_increase_tick(rt_tick_t tick)
{
RT_ASSERT(rt_interrupt_get_nest() > 0);
RT_OBJECT_HOOK_CALL(rt_tick_hook, ());
/* tracing cpu usage */
_update_process_times(tick);
/* increase the global tick */
#ifdef RT_USING_SMP
/* get percpu and increase the tick */
rt_atomic_add(&(rt_cpu_self()->tick), tick);
#else
rt_atomic_add(&(rt_tick), tick);
#endif /* RT_USING_SMP */
/* check time slice */
rt_sched_tick_increase(tick);
/* check timer */
#ifdef RT_USING_SMP
if (rt_cpu_get_id() != 0)
{
return;
}
#endif
rt_timer_check();
#ifdef RT_USING_VDSO
rt_vdso_update_glob_time();
#endif
}
/**
* @brief This function will calculate the tick from millisecond.
*
* @param ms is the specified millisecond.
* - Negative Number wait forever
* - Zero not wait
* - Max 0x7fffffff
*
* @return Return the calculated tick.
*/
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
{
rt_tick_t tick;
if (ms < 0)
{
tick = (rt_tick_t)RT_WAITING_FOREVER;
}
else
{
#if RT_TICK_PER_SECOND == 1000u
tick = ms;
#else
tick = RT_TICK_PER_SECOND * (ms / 1000);
tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
#endif /* RT_TICK_PER_SECOND == 1000u */
}
/* return the calculated tick */
return tick;
}
RTM_EXPORT(rt_tick_from_millisecond);
/**
* @brief This function will return the passed millisecond from boot.
*
* @note if the value of RT_TICK_PER_SECOND is lower than 1000 or
* is not an integral multiple of 1000, this function will not
* provide the correct 1ms-based tick.
*
* @return Return passed millisecond from boot.
*/
rt_weak rt_tick_t rt_tick_get_millisecond(void)
{
#if RT_TICK_PER_SECOND == 0 /* make cppcheck happy*/
#error "RT_TICK_PER_SECOND must be greater than zero"
#endif
#if 1000 % RT_TICK_PER_SECOND == 0u
return rt_tick_get() * (1000u / RT_TICK_PER_SECOND);
#else
#warning "rt-thread cannot provide a correct 1ms-based tick any longer,\
please redefine this function in another file by using a high-precision hard-timer."
return 0;
#endif /* 1000 % RT_TICK_PER_SECOND == 0u */
}
/**@}*/
+286
View File
@@ -0,0 +1,286 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-09-20 Bernard Change the name to components.c
* And all components related header files.
* 2012-12-23 Bernard fix the pthread initialization issue.
* 2013-06-23 Bernard Add the init_call for components initialization.
* 2013-07-05 Bernard Remove initialization feature for MS VC++ compiler
* 2015-02-06 Bernard Remove the MS VC++ support and move to the kernel
* 2015-05-04 Bernard Rename it to components.c because compiling issue
* in some IDEs.
* 2015-07-29 Arda.Fu Add support to use RT_USING_USER_MAIN with IAR
* 2018-11-22 Jesven Add secondary cpu boot up
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_USER_MAIN
#ifndef RT_MAIN_THREAD_STACK_SIZE
#define RT_MAIN_THREAD_STACK_SIZE 2048
#endif /* RT_MAIN_THREAD_STACK_SIZE */
#ifndef RT_MAIN_THREAD_PRIORITY
#define RT_MAIN_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 3)
#endif /* RT_MAIN_THREAD_PRIORITY */
#endif /* RT_USING_USER_MAIN */
#ifdef RT_USING_COMPONENTS_INIT
/*
* Components Initialization will initialize some driver and components as following
* order:
* rti_start --> 0
* BOARD_EXPORT --> 1
* rti_board_end --> 1.end
*
* DEVICE_EXPORT --> 2
* COMPONENT_EXPORT --> 3
* FS_EXPORT --> 4
* ENV_EXPORT --> 5
* APP_EXPORT --> 6
*
* rti_end --> 6.end
*
* These automatically initialization, the driver or component initial function must
* be defined with:
* INIT_BOARD_EXPORT(fn);
* INIT_DEVICE_EXPORT(fn);
* ...
* INIT_APP_EXPORT(fn);
* etc.
*/
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_board_start(void)
{
return 0;
}
INIT_EXPORT(rti_board_start, "0.end");
static int rti_board_end(void)
{
return 0;
}
INIT_EXPORT(rti_board_end, "1.end");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_end, "6.end");
/**
* @brief Onboard components initialization. In this function, the board-level
* initialization function will be called to complete the initialization
* of the on-board peripherals.
*/
void rt_components_board_init(void)
{
#ifdef RT_DEBUGING_AUTO_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s\n", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
#endif /* RT_DEBUGING_AUTO_INIT */
}
/**
* @brief RT-Thread Components Initialization.
*/
void rt_components_init(void)
{
#ifdef RT_DEBUGING_AUTO_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s\n", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif /* RT_DEBUGING_AUTO_INIT */
}
#endif /* RT_USING_COMPONENTS_INIT */
#ifdef RT_USING_USER_MAIN
void rt_application_init(void);
void rt_hw_board_init(void);
int rtthread_startup(void);
#ifdef __ARMCC_VERSION
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rtthread_startup();
return 0;
}
#elif defined(__ICCARM__)
/* __low_level_init will auto called by IAR cstartup */
extern void __iar_data_init3(void);
int __low_level_init(void)
{
// call IAR table copy function.
__iar_data_init3();
rtthread_startup();
return 0;
}
#elif defined(__GNUC__)
/* Add -eentry to arm-none-eabi-gcc argument */
int entry(void)
{
rtthread_startup();
return 0;
}
#endif
#ifndef RT_USING_HEAP
/* if there is not enable heap, we should use static thread and stack. */
rt_align(RT_ALIGN_SIZE)
static rt_uint8_t main_thread_stack[RT_MAIN_THREAD_STACK_SIZE];
struct rt_thread main_thread;
#endif /* RT_USING_HEAP */
/**
* @brief The system main thread. In this thread will call the rt_components_init()
* for initialization of RT-Thread Components and call the user's programming
* entry main().
*
* @param parameter is the arg of the thread.
*/
static void main_thread_entry(void *parameter)
{
extern int main(void);
RT_UNUSED(parameter);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif /* RT_USING_COMPONENTS_INIT */
#ifdef RT_USING_SMP
rt_hw_secondary_cpu_up();
#endif /* RT_USING_SMP */
/* invoke system main function */
#ifdef __ARMCC_VERSION
{
extern int $Super$$main(void);
$Super$$main(); /* for ARMCC. */
}
#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__) || defined(__TI_COMPILER_VERSION__)
main();
#endif /* __ARMCC_VERSION */
}
/**
* @brief This function will create and start the main thread, but this thread
* will not run until the scheduler starts.
*/
void rt_application_init(void)
{
rt_thread_t tid;
#ifdef RT_USING_HEAP
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(tid != RT_NULL);
#else
rt_err_t result;
tid = &main_thread;
result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
main_thread_stack, sizeof(main_thread_stack), RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(result == RT_EOK);
/* if not define RT_USING_HEAP, using to eliminate the warning */
(void)result;
#endif /* RT_USING_HEAP */
rt_thread_startup(tid);
}
/**
* @brief This function will call all levels of initialization functions to complete
* the initialization of the system, and finally start the scheduler.
*
* @return Normally never returns. If 0 is returned, the scheduler failed.
*/
int rtthread_startup(void)
{
#ifdef RT_USING_SMP
rt_hw_spin_lock_init(&_cpus_lock);
#endif
rt_hw_local_irq_disable();
/* board level initialization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif /* RT_USING_SIGNALS */
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
/* defunct thread initialization */
rt_thread_defunct_init();
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif /* RT_USING_SMP */
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
#endif /* RT_USING_USER_MAIN */
+236
View File
@@ -0,0 +1,236 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-10-30 Bernard The first version
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-12-10 xqyjlj spinlock should lock sched
* 2024-01-25 Shell Using rt_exit_critical_safe
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_SMART
#include <lwp.h>
#endif
#ifdef RT_USING_DEBUG
rt_base_t _cpus_critical_level;
#endif /* RT_USING_DEBUG */
static struct rt_cpu _cpus[RT_CPUS_NR];
rt_hw_spinlock_t _cpus_lock;
#if defined(RT_DEBUGING_SPINLOCK)
void *_cpus_lock_owner = 0;
void *_cpus_lock_pc = 0;
#endif /* RT_DEBUGING_SPINLOCK */
/**
* @brief Initialize a static spinlock object.
*
* @param lock is a pointer to the spinlock to initialize.
*/
void rt_spin_lock_init(struct rt_spinlock *lock)
{
rt_hw_spin_lock_init(&lock->lock);
}
RTM_EXPORT(rt_spin_lock_init)
/**
* @brief This function will lock the spinlock, will lock the thread scheduler.
*
* @note If the spinlock is locked, the current CPU will keep polling the spinlock state
* until the spinlock is unlocked.
*
* @param lock is a pointer to the spinlock.
*/
void rt_spin_lock(struct rt_spinlock *lock)
{
rt_enter_critical();
rt_hw_spin_lock(&lock->lock);
RT_SPIN_LOCK_DEBUG(lock);
}
RTM_EXPORT(rt_spin_lock)
/**
* @brief This function will unlock the spinlock, will unlock the thread scheduler.
*
* @param lock is a pointer to the spinlock.
*/
void rt_spin_unlock(struct rt_spinlock *lock)
{
rt_base_t critical_level;
RT_SPIN_UNLOCK_DEBUG(lock, critical_level);
rt_hw_spin_unlock(&lock->lock);
rt_exit_critical_safe(critical_level);
}
RTM_EXPORT(rt_spin_unlock)
/**
* @brief This function will disable the local interrupt and then lock the spinlock, will lock the thread scheduler.
*
* @note If the spinlock is locked, the current CPU will keep polling the spinlock state
* until the spinlock is unlocked.
*
* @param lock is a pointer to the spinlock.
*
* @return Return current cpu interrupt status.
*/
rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock)
{
rt_base_t level;
level = rt_hw_local_irq_disable();
rt_enter_critical();
rt_hw_spin_lock(&lock->lock);
RT_SPIN_LOCK_DEBUG(lock);
return level;
}
RTM_EXPORT(rt_spin_lock_irqsave)
/**
* @brief This function will unlock the spinlock and then restore current cpu interrupt status, will unlock the thread scheduler.
*
* @param lock is a pointer to the spinlock.
*
* @param level is interrupt status returned by rt_spin_lock_irqsave().
*/
void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level)
{
rt_base_t critical_level;
RT_SPIN_UNLOCK_DEBUG(lock, critical_level);
rt_hw_spin_unlock(&lock->lock);
rt_exit_critical_safe(critical_level);
rt_hw_local_irq_enable(level);
}
RTM_EXPORT(rt_spin_unlock_irqrestore)
/**
* @brief This fucntion will return current cpu object.
*
* @return Return a pointer to the current cpu object.
*/
struct rt_cpu *rt_cpu_self(void)
{
return &_cpus[rt_hw_cpu_id()];
}
/**
* @brief This fucntion will return the cpu object corresponding to index.
*
* @param index is the index of target cpu object.
*
* @return Return a pointer to the cpu object corresponding to index.
*/
struct rt_cpu *rt_cpu_index(int index)
{
return &_cpus[index];
}
/**
* @brief This function will lock all cpus's scheduler and disable local irq.
*
* @return Return current cpu interrupt status.
*/
rt_base_t rt_cpus_lock(void)
{
rt_base_t level;
struct rt_cpu* pcpu;
level = rt_hw_local_irq_disable();
pcpu = rt_cpu_self();
if (pcpu->current_thread != RT_NULL)
{
rt_ubase_t lock_nest = rt_atomic_load(&(pcpu->current_thread->cpus_lock_nest));
rt_atomic_add(&(pcpu->current_thread->cpus_lock_nest), 1);
if (lock_nest == 0)
{
rt_enter_critical();
rt_hw_spin_lock(&_cpus_lock);
#ifdef RT_USING_DEBUG
_cpus_critical_level = rt_critical_level();
#endif /* RT_USING_DEBUG */
#ifdef RT_DEBUGING_SPINLOCK
_cpus_lock_owner = pcpu->current_thread;
_cpus_lock_pc = __GET_RETURN_ADDRESS;
#endif /* RT_DEBUGING_SPINLOCK */
}
}
return level;
}
RTM_EXPORT(rt_cpus_lock);
/**
* @brief This function will restore all cpus's scheduler and restore local irq.
*
* @param level is interrupt status returned by rt_cpus_lock().
*/
void rt_cpus_unlock(rt_base_t level)
{
struct rt_cpu* pcpu = rt_cpu_self();
if (pcpu->current_thread != RT_NULL)
{
rt_base_t critical_level = 0;
RT_ASSERT(rt_atomic_load(&(pcpu->current_thread->cpus_lock_nest)) > 0);
rt_atomic_sub(&(pcpu->current_thread->cpus_lock_nest), 1);
if (pcpu->current_thread->cpus_lock_nest == 0)
{
#if defined(RT_DEBUGING_SPINLOCK)
_cpus_lock_owner = __OWNER_MAGIC;
_cpus_lock_pc = RT_NULL;
#endif /* RT_DEBUGING_SPINLOCK */
#ifdef RT_USING_DEBUG
critical_level = _cpus_critical_level;
_cpus_critical_level = 0;
#endif /* RT_USING_DEBUG */
rt_hw_spin_unlock(&_cpus_lock);
rt_exit_critical_safe(critical_level);
}
}
rt_hw_local_irq_enable(level);
}
RTM_EXPORT(rt_cpus_unlock);
/**
* This function is invoked by scheduler.
* It will restore the lock state to whatever the thread's counter expects.
* If target thread not locked the cpus then unlock the cpus lock.
*
* @param thread is a pointer to the target thread.
*/
void rt_cpus_lock_status_restore(struct rt_thread *thread)
{
#if defined(ARCH_MM_MMU) && defined(RT_USING_SMART)
lwp_aspace_switch(thread);
#endif
rt_sched_post_ctx_switch(thread);
}
RTM_EXPORT(rt_cpus_lock_status_restore);
/* A safe API with debugging feature to be called in most codes */
#undef rt_cpu_get_id
/**
* @brief Get logical CPU ID
*
* @return logical CPU ID
*/
rt_base_t rt_cpu_get_id(void)
{
RT_ASSERT(rt_sched_thread_is_binding(RT_NULL) ||
rt_hw_interrupt_is_disabled() ||
!rt_scheduler_is_available());
return rt_hw_cpu_id();
}
+110
View File
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-04-19 Shell Fixup UP irq spinlock
* 2024-05-22 Shell Add UP cpu object and
* maintain the rt_current_thread inside it
*/
#include <rthw.h>
#include <rtthread.h>
static struct rt_cpu _cpu;
/**
* @brief Initialize a static spinlock object.
*
* @param lock is a pointer to the spinlock to initialize.
*/
void rt_spin_lock_init(struct rt_spinlock *lock)
{
RT_UNUSED(lock);
}
/**
* @brief This function will lock the spinlock, will lock the thread scheduler.
*
* @note If the spinlock is locked, the current CPU will keep polling the spinlock state
* until the spinlock is unlocked.
*
* @param lock is a pointer to the spinlock.
*/
void rt_spin_lock(struct rt_spinlock *lock)
{
rt_enter_critical();
RT_SPIN_LOCK_DEBUG(lock);
}
/**
* @brief This function will unlock the spinlock, will unlock the thread scheduler.
* If the scheduling function is called before unlocking, it will be scheduled in this function.
*
* @param lock is a pointer to the spinlock.
*/
void rt_spin_unlock(struct rt_spinlock *lock)
{
rt_base_t critical_level;
RT_SPIN_UNLOCK_DEBUG(lock, critical_level);
rt_exit_critical_safe(critical_level);
}
/**
* @brief This function will disable the local interrupt and then lock the spinlock, will lock the thread scheduler.
*
* @note If the spinlock is locked, the current CPU will keep polling the spinlock state
* until the spinlock is unlocked.
*
* @param lock is a pointer to the spinlock.
*
* @return Return current cpu interrupt status.
*/
rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock)
{
rt_base_t level;
RT_UNUSED(lock);
level = rt_hw_interrupt_disable();
rt_enter_critical();
RT_SPIN_LOCK_DEBUG(lock);
return level;
}
/**
* @brief This function will unlock the spinlock and then restore current cpu interrupt status, will unlock the thread scheduler.
* If the scheduling function is called before unlocking, it will be scheduled in this function.
*
* @param lock is a pointer to the spinlock.
*
* @param level is interrupt status returned by rt_spin_lock_irqsave().
*/
void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level)
{
rt_base_t critical_level;
RT_SPIN_UNLOCK_DEBUG(lock, critical_level);
rt_exit_critical_safe(critical_level);
rt_hw_interrupt_enable(level);
}
/**
* @brief This fucntion will return current cpu object.
*
* @return Return a pointer to the current cpu object.
*/
struct rt_cpu *rt_cpu_self(void)
{
return &_cpu;
}
/**
* @brief This fucntion will return the cpu object corresponding to index.
*
* @param index is the index of target cpu object.
*
* @return Return a pointer to the cpu object corresponding to index.
*/
struct rt_cpu *rt_cpu_index(int index)
{
return index == 0 ? &_cpu : RT_NULL;
}
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-08-30 heyuanjie87 the first version
*
*/
#include <rthw.h>
#include <rtthread.h>
#ifndef SYSTEM_THREAD_STACK_SIZE
#define SYSTEM_THREAD_STACK_SIZE IDLE_THREAD_STACK_SIZE
#endif
static rt_list_t _rt_thread_defunct = RT_LIST_OBJECT_INIT(_rt_thread_defunct);
static struct rt_spinlock _defunct_spinlock;
#if defined(RT_USING_SMP) || defined(RT_USING_SMART)
static struct rt_thread rt_system_thread;
rt_align(RT_ALIGN_SIZE) static rt_uint8_t rt_system_stack[SYSTEM_THREAD_STACK_SIZE];
static struct rt_semaphore system_sem;
#endif
/**
* @brief Enqueue a thread to defunct queue.
*
* @param thread the thread to be enqueued.
*
* @note It must be called between rt_hw_interrupt_disable and rt_hw_interrupt_enable
*/
void rt_thread_defunct_enqueue(rt_thread_t thread)
{
rt_base_t level;
level = rt_spin_lock_irqsave(&_defunct_spinlock);
rt_list_insert_after(&_rt_thread_defunct, &RT_THREAD_LIST_NODE(thread));
rt_spin_unlock_irqrestore(&_defunct_spinlock, level);
#if defined(RT_USING_SMP) || defined(RT_USING_SMART)
rt_sem_release(&system_sem);
#endif
}
/**
* @brief Dequeue a thread from defunct queue.
*/
rt_thread_t rt_thread_defunct_dequeue(void)
{
rt_base_t level;
rt_thread_t thread = RT_NULL;
rt_list_t *l = &_rt_thread_defunct;
level = rt_spin_lock_irqsave(&_defunct_spinlock);
if (!rt_list_isempty(l))
{
thread = RT_THREAD_LIST_NODE_ENTRY(l->next);
rt_list_remove(&RT_THREAD_LIST_NODE(thread));
}
rt_spin_unlock_irqrestore(&_defunct_spinlock, level);
return thread;
}
/**
* @brief This function will perform system background job when system idle.
*/
void rt_defunct_execute(void)
{
/* Loop until there is no dead thread. So one call to rt_defunct_execute
* will do all the cleanups. */
while (1)
{
rt_thread_t thread;
rt_bool_t object_is_systemobject;
void (*cleanup)(struct rt_thread *tid);
#ifdef RT_USING_MODULE
struct rt_dlmodule *module = RT_NULL;
#endif
/* get defunct thread */
thread = rt_thread_defunct_dequeue();
if (thread == RT_NULL)
{
break;
}
#ifdef RT_USING_MODULE
module = (struct rt_dlmodule *)thread->parent.module_id;
if (module)
{
dlmodule_destroy(module);
}
#endif
#ifdef RT_USING_SIGNALS
rt_thread_free_sig(thread);
#endif
/* store the point of "thread->cleanup" avoid to lose */
cleanup = thread->cleanup;
/* if it's a system object, detach it */
object_is_systemobject = rt_object_is_systemobject((rt_object_t)thread);
if (object_is_systemobject == RT_TRUE)
{
/* detach this object */
rt_object_detach((rt_object_t)thread);
}
/* invoke thread cleanup */
if (cleanup != RT_NULL)
{
cleanup(thread);
}
#ifdef RT_USING_HEAP
#ifdef RT_USING_MEM_PROTECTION
if (thread->mem_regions != RT_NULL)
{
RT_KERNEL_FREE(thread->mem_regions);
}
#endif
/* if need free, delete it */
if (object_is_systemobject == RT_FALSE)
{
/* release thread's stack */
#ifdef RT_USING_HW_STACK_GUARD
RT_KERNEL_FREE(thread->stack_buf);
#else
RT_KERNEL_FREE(thread->stack_addr);
#endif
/* delete thread object */
rt_object_delete((rt_object_t)thread);
}
#endif
}
}
#if defined(RT_USING_SMP) || defined(RT_USING_SMART)
static void rt_thread_system_entry(void *parameter)
{
RT_UNUSED(parameter);
while (1)
{
int ret = rt_sem_take(&system_sem, RT_WAITING_FOREVER);
if (ret != RT_EOK)
{
rt_kprintf("failed to sem_take() error %d\n", ret);
RT_ASSERT(0);
}
rt_defunct_execute();
}
}
#endif
void rt_thread_defunct_init(void)
{
RT_ASSERT(RT_THREAD_PRIORITY_MAX > 2);
rt_spin_lock_init(&_defunct_spinlock);
#if defined(RT_USING_SMP) || defined(RT_USING_SMART)
rt_sem_init(&system_sem, "defunct", 0, RT_IPC_FLAG_FIFO);
/* create defunct thread */
rt_thread_init(&rt_system_thread,
"tsystem",
rt_thread_system_entry,
RT_NULL,
rt_system_stack,
sizeof(rt_system_stack),
RT_THREAD_PRIORITY_MAX - 2,
32);
/* startup */
rt_thread_startup(&rt_system_thread);
#endif
}
+219
View File
@@ -0,0 +1,219 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-23 Bernard the first version
* 2010-11-10 Bernard add cleanup callback function in thread exit.
* 2012-12-29 Bernard fix compiling warning.
* 2013-12-21 Grissiom let rt_thread_idle_excute loop until there is no
* dead thread.
* 2016-08-09 ArdaFu add method to get the handler of the idle thread.
* 2018-02-07 Bernard lock scheduler to protect tid->cleanup.
* 2018-07-14 armink add idle hook list
* 2018-11-22 Jesven add per cpu idle task
* combine the code of primary and secondary cpu
* 2021-11-15 THEWON Remove duplicate work between idle and _thread_exit
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-11-07 xqyjlj fix thread exit
* 2023-12-10 xqyjlj add _hook_spinlock
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MODULE
#include <dlmodule.h>
#endif /* RT_USING_MODULE */
#ifdef RT_USING_HOOK
#ifndef RT_USING_IDLE_HOOK
#define RT_USING_IDLE_HOOK
#endif /* RT_USING_IDLE_HOOK */
#endif /* RT_USING_HOOK */
#ifndef IDLE_THREAD_STACK_SIZE
#if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP)
#define IDLE_THREAD_STACK_SIZE 256
#else
#define IDLE_THREAD_STACK_SIZE 128
#endif /* (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) */
#endif /* IDLE_THREAD_STACK_SIZE */
#define _CPUS_NR RT_CPUS_NR
static struct rt_thread idle_thread[_CPUS_NR];
rt_align(RT_ALIGN_SIZE)
static rt_uint8_t idle_thread_stack[_CPUS_NR][IDLE_THREAD_STACK_SIZE];
#ifdef RT_USING_IDLE_HOOK
#ifndef RT_IDLE_HOOK_LIST_SIZE
#define RT_IDLE_HOOK_LIST_SIZE 4
#endif /* RT_IDLE_HOOK_LIST_SIZE */
static void (*idle_hook_list[RT_IDLE_HOOK_LIST_SIZE])(void);
static struct rt_spinlock _hook_spinlock;
/**
* @brief This function sets a hook function to idle thread loop. When the system performs
* idle loop, this hook function should be invoked.
*
* @param hook the specified hook function.
*
* @return RT_EOK: set OK.
* -RT_EFULL: hook list is full.
*
* @note the hook function must be simple and never be blocked or suspend.
*/
rt_err_t rt_thread_idle_sethook(void (*hook)(void))
{
rt_size_t i;
rt_err_t ret = -RT_EFULL;
rt_base_t level;
level = rt_spin_lock_irqsave(&_hook_spinlock);
for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
{
if (idle_hook_list[i] == RT_NULL)
{
idle_hook_list[i] = hook;
ret = RT_EOK;
break;
}
}
rt_spin_unlock_irqrestore(&_hook_spinlock, level);
return ret;
}
/**
* @brief delete the idle hook on hook list.
*
* @param hook the specified hook function.
*
* @return RT_EOK: delete OK.
* -RT_ENOSYS: hook was not found.
*/
rt_err_t rt_thread_idle_delhook(void (*hook)(void))
{
rt_size_t i;
rt_err_t ret = -RT_ENOSYS;
rt_base_t level;
level = rt_spin_lock_irqsave(&_hook_spinlock);
for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
{
if (idle_hook_list[i] == hook)
{
idle_hook_list[i] = RT_NULL;
ret = RT_EOK;
break;
}
}
rt_spin_unlock_irqrestore(&_hook_spinlock, level);
return ret;
}
#endif /* RT_USING_IDLE_HOOK */
static void idle_thread_entry(void *parameter)
{
RT_UNUSED(parameter);
#ifdef RT_USING_SMP
if (rt_cpu_get_id() != 0)
{
while (1)
{
rt_hw_secondary_cpu_idle_exec();
}
}
#endif /* RT_USING_SMP */
while (1)
{
#ifdef RT_USING_IDLE_HOOK
rt_size_t i;
void (*idle_hook)(void);
for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
{
idle_hook = idle_hook_list[i];
if (idle_hook != RT_NULL)
{
idle_hook();
}
}
#endif /* RT_USING_IDLE_HOOK */
#if !defined(RT_USING_SMP) && !defined(RT_USING_SMART)
rt_defunct_execute();
#endif
#ifdef RT_USING_PM
void rt_system_power_manager(void);
rt_system_power_manager();
#endif /* RT_USING_PM */
}
}
/**
* @brief This function will initialize idle thread, then start it.
*
* @note this function must be invoked when system init.
*/
void rt_thread_idle_init(void)
{
rt_ubase_t i;
#if RT_NAME_MAX > 0
char idle_thread_name[RT_NAME_MAX];
#endif /* RT_NAME_MAX > 0 */
#ifdef RT_USING_IDLE_HOOK
rt_spin_lock_init(&_hook_spinlock);
#endif
for (i = 0; i < _CPUS_NR; i++)
{
#if RT_NAME_MAX > 0
rt_snprintf(idle_thread_name, RT_NAME_MAX, "tidle%d", i);
#endif /* RT_NAME_MAX > 0 */
rt_thread_init(&idle_thread[i],
#if RT_NAME_MAX > 0
idle_thread_name,
#else
"tidle",
#endif /* RT_NAME_MAX > 0 */
idle_thread_entry,
RT_NULL,
&idle_thread_stack[i][0],
sizeof(idle_thread_stack[i]),
RT_THREAD_PRIORITY_MAX - 1,
32);
#ifdef RT_USING_SMP
rt_thread_control(&idle_thread[i], RT_THREAD_CTRL_BIND_CPU, (void*)i);
#endif /* RT_USING_SMP */
/* update */
rt_cpu_index(i)->idle_thread = &idle_thread[i];
/* startup */
rt_thread_startup(&idle_thread[i]);
}
}
/**
* @brief This function will get the handler of the idle thread.
*/
rt_thread_t rt_thread_idle_gethandler(void)
{
int id = rt_cpu_get_id();
return (rt_thread_t)(&idle_thread[id]);
}
+4033
View File
File diff suppressed because it is too large Load Diff
+157
View File
@@ -0,0 +1,157 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-02-24 Bernard first version
* 2006-05-03 Bernard add IRQ_DEBUG
* 2016-08-09 ArdaFu add interrupt enter and leave hook.
* 2018-11-22 Jesven rt_interrupt_get_nest function add disable irq
* 2021-08-15 Supperthomas fix the comment
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to irq.c
* 2022-07-04 Yunjie fix RT_DEBUG_LOG
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2024-01-05 Shell Fixup of data racing in rt_interrupt_get_nest
* 2024-01-03 Shell Support for interrupt context
*/
#include <rthw.h>
#include <rtthread.h>
#define DBG_TAG "kernel.irq"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_interrupt_enter_hook)(void);
static void (*rt_interrupt_leave_hook)(void);
/**
* @ingroup group_hook
*
* @brief This function set a hook function when the system enter a interrupt
*
* @note The hook function must be simple and never be blocked or suspend.
*
* @param hook the function point to be called
*/
void rt_interrupt_enter_sethook(void (*hook)(void))
{
rt_interrupt_enter_hook = hook;
}
/**
* @ingroup group_hook
*
* @brief This function set a hook function when the system exit a interrupt.
*
* @note The hook function must be simple and never be blocked or suspend.
*
* @param hook the function point to be called
*/
void rt_interrupt_leave_sethook(void (*hook)(void))
{
rt_interrupt_leave_hook = hook;
}
#endif /* RT_USING_HOOK */
/**
* @addtogroup group_kernel_core
*/
/**@{*/
#ifdef RT_USING_SMP
#define rt_interrupt_nest rt_cpu_self()->irq_nest
#else
volatile rt_atomic_t rt_interrupt_nest = 0;
#endif /* RT_USING_SMP */
#ifdef ARCH_USING_IRQ_CTX_LIST
void rt_interrupt_context_push(rt_interrupt_context_t this_ctx)
{
struct rt_cpu *this_cpu = rt_cpu_self();
rt_slist_insert(&this_cpu->irq_ctx_head, &this_ctx->node);
}
void rt_interrupt_context_pop(void)
{
struct rt_cpu *this_cpu = rt_cpu_self();
rt_slist_pop(&this_cpu->irq_ctx_head);
}
void *rt_interrupt_context_get(void)
{
struct rt_cpu *this_cpu = rt_cpu_self();
return rt_slist_first_entry(&this_cpu->irq_ctx_head, struct rt_interrupt_context, node)->context;
}
#endif /* ARCH_USING_IRQ_CTX_LIST */
/**
* @brief This function will be invoked by BSP, when enter interrupt service routine
*
* @note Please don't invoke this routine in application
*
* @see rt_interrupt_leave
*/
rt_weak void rt_interrupt_enter(void)
{
rt_atomic_add(&(rt_interrupt_nest), 1);
RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,());
LOG_D("irq has come..., irq current nest:%d",
(rt_int32_t)rt_atomic_load(&(rt_interrupt_nest)));
}
RTM_EXPORT(rt_interrupt_enter);
/**
* @brief This function will be invoked by BSP, when leave interrupt service routine
*
* @note Please don't invoke this routine in application
*
* @see rt_interrupt_enter
*/
rt_weak void rt_interrupt_leave(void)
{
LOG_D("irq is going to leave, irq current nest:%d",
(rt_int32_t)rt_atomic_load(&(rt_interrupt_nest)));
RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook,());
rt_atomic_sub(&(rt_interrupt_nest), 1);
}
RTM_EXPORT(rt_interrupt_leave);
/**
* @brief This function will return the nest of interrupt.
*
* User application can invoke this function to get whether current
* context is interrupt context.
*
* @return the number of nested interrupts.
*/
rt_weak rt_uint8_t rt_interrupt_get_nest(void)
{
rt_uint8_t ret;
rt_base_t level;
level = rt_hw_local_irq_disable();
ret = rt_atomic_load(&rt_interrupt_nest);
rt_hw_local_irq_enable(level);
return ret;
}
RTM_EXPORT(rt_interrupt_get_nest);
RTM_EXPORT(rt_hw_interrupt_disable);
RTM_EXPORT(rt_hw_interrupt_enable);
rt_weak rt_bool_t rt_hw_interrupt_is_disabled(void)
{
return RT_FALSE;
}
RTM_EXPORT(rt_hw_interrupt_is_disabled);
/**@}*/
+258
View File
@@ -0,0 +1,258 @@
menu "klibc options"
menu "rt_vsnprintf options"
config RT_KLIBC_USING_LIBC_VSNPRINTF
bool "Enable rt_vsnprintf to use libc vsnprintf"
default n
config RT_KLIBC_USING_VSNPRINTF_LONGLONG
bool "Enable rt_vsnprintf function to support long-long format"
depends on !RT_KLIBC_USING_LIBC_VSNPRINTF
default n
help
Support for the long long integral types (with the ll, z and t length modifiers for specifiers
%d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported.
menuconfig RT_KLIBC_USING_VSNPRINTF_STANDARD
bool "Enable standard rt_vsnprintf version"
default y if ARCH_CPU_64BIT
default n
select RT_KLIBC_USING_VSNPRINTF_LONGLONG
depends on !RT_KLIBC_USING_LIBC_VSNPRINTF
help
Standard version of rt_vsnprintf, which is full function but higher stack usage.
if RT_KLIBC_USING_VSNPRINTF_STANDARD
config RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
bool "Support decimal notation floating point conversion specifiers (%f, %F)"
default y
help
Support for the decimal notation floating point conversion specifiers (%f, %F)
config RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
bool "Support exponential notation floating point conversion specifiers (%e, %g, %E, %G)"
default y
help
Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G)
config RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
bool "Support length write-back specifier (%n)"
default y
help
Support for the length write-back specifier (%n)
config RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER
bool "safety check: no NULL end string"
default y if RT_USING_DEBUG
default n
help
Be extra-safe, and don't assume format specifiers are completed correctly
before the format string end.
config RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
bool "Support MSVC style integer specifiers"
default n
help
the integer format specifiers used in Microsoft's Visual C++ (MSVC) compiler.
These specifiers, like %I64d for 64-bit integers, deviate slightly from the standard
C format specifiers and are specific to MSVC. They allow for controlled formatting of
integers in printf()-like functions, accommodating different integer sizes and ensuring
compatibility with MSVC's environment. It's important to note that these specifiers might
not be recognized or function in other compilers due to their MSVC-specific nature.
config RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE
int "'ntoa' conversion buffer size"
default 32
help
'ntoa' conversion buffer size, this must be big enough to hold one converted
numeric number including padded zeros (dynamically created on stack)
config RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE
int "printing individual decimal numbers buffer size"
default 32
help
size of the fixed (on-stack) buffer for printing individual decimal numbers.
this must be big enough to hold one converted floating-point value including
padded zeros.
config RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION
int "floating point conversion specifiers"
default 6
help
Default precision for the floating point conversion specifiers (the C standard sets this at 6)
config RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL
int "integral nums printed as float in rt_vsnprint"
default 9
help
According to the C languages standard, printf() and related functions must be able to print any
integral number in floating-point notation, regardless of length, when using the %f specifier -
possibly hundreds of characters, potentially overflowing your buffers. In this implementation,
all values beyond this threshold are switched to exponential notation.
config RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS
int "the number of terms in a Taylor series expansion of log_10(x)"
default 4
range 2 99
help
The number of terms in a Taylor series expansion of log_10(x) to
use for approximation - including the power-zero term (i.e. the
value at the point of expansion).
endif
endmenu # rt_vsnprintf options
menu "rt_vsscanf options"
config RT_KLIBC_USING_LIBC_VSSCANF
bool "Enable rt_vsscanf to use libc vsscanf"
default n
endmenu # rt_vsscanf options
menu "rt_memset options"
config RT_KLIBC_USING_USER_MEMSET
bool "Enable rt_memset to use user-defined version"
default n
if !RT_KLIBC_USING_USER_MEMSET
config RT_KLIBC_USING_LIBC_MEMSET
bool "Enable rt_memset to use libc memset"
default n
config RT_KLIBC_USING_TINY_MEMSET
bool "Enable rt_memset to use tiny version"
depends on !RT_KLIBC_USING_LIBC_MEMSET
default n
endif
endmenu # rt_memset options
menu "rt_memcpy options"
config RT_KLIBC_USING_USER_MEMCPY
bool "Enable rt_memcpy to use user-defined version"
default n
if !RT_KLIBC_USING_USER_MEMCPY
config RT_KLIBC_USING_LIBC_MEMCPY
bool "Enable rt_memcpy to use libc memcpy"
default n
config RT_KLIBC_USING_TINY_MEMCPY
bool "Enable rt_memcpy to use tiny version"
depends on !RT_KLIBC_USING_LIBC_MEMCPY
default n
endif
endmenu # rt_memcpy options
menu "rt_memmove options"
config RT_KLIBC_USING_USER_MEMMOVE
bool "Enable rt_memmove to use user-defined version"
default n
if !RT_KLIBC_USING_USER_MEMMOVE
config RT_KLIBC_USING_LIBC_MEMMOVE
bool "Enable rt_memmove to use libc memmove"
default n
endif
endmenu # rt_memmove options
menu "rt_memcmp options"
config RT_KLIBC_USING_USER_MEMCMP
bool "Enable rt_memcmp to use user-defined version"
default n
if !RT_KLIBC_USING_USER_MEMCMP
config RT_KLIBC_USING_LIBC_MEMCMP
bool "Enable rt_memcmp to use libc memcmp"
default n
endif
endmenu # rt_memcmp options
menu "rt_strstr options"
config RT_KLIBC_USING_USER_STRSTR
bool "Enable rt_strstr to use user-defined version"
default n
if !RT_KLIBC_USING_USER_STRSTR
config RT_KLIBC_USING_LIBC_STRSTR
bool "Enable rt_strstr to use libc strstr"
default n
endif
endmenu # rt_strstr options
menu "rt_strcasecmp options"
config RT_KLIBC_USING_USER_STRCASECMP
bool "Enable rt_strcasecmp to use user-defined version"
default n
endmenu # rt_strcasecmp options
menu "rt_strncpy options"
config RT_KLIBC_USING_USER_STRNCPY
bool "Enable rt_strncpy to use user-defined version"
default n
if !RT_KLIBC_USING_USER_STRNCPY
config RT_KLIBC_USING_LIBC_STRNCPY
bool "Enable rt_strncpy to use libc strncpy"
default n
endif
endmenu # rt_strncpy options
menu "rt_strcpy options"
config RT_KLIBC_USING_USER_STRCPY
bool "Enable rt_strcpy to use user-defined version"
default n
if !RT_KLIBC_USING_USER_STRCPY
config RT_KLIBC_USING_LIBC_STRCPY
bool "Enable rt_strcpy to use libc strcpy"
default n
endif
endmenu # rt_strcpy options
menu "rt_strncmp options"
config RT_KLIBC_USING_USER_STRNCMP
bool "Enable rt_strncmp to use user-defined version"
default n
if !RT_KLIBC_USING_USER_STRNCMP
config RT_KLIBC_USING_LIBC_STRNCMP
bool "Enable rt_strncmp to use libc strncmp"
default n
endif
endmenu # rt_strncmp options
menu "rt_strcmp options"
config RT_KLIBC_USING_USER_STRCMP
bool "Enable rt_strcmp to use user-defined version"
default n
if !RT_KLIBC_USING_USER_STRCMP
config RT_KLIBC_USING_LIBC_STRCMP
bool "Enable rt_strcmp to use libc strcmp"
default n
endif
endmenu # rt_strcmp options
menu "rt_strlen options"
config RT_KLIBC_USING_USER_STRLEN
bool "Enable rt_strlen to use user-defined version"
default n
if !RT_KLIBC_USING_USER_STRLEN
config RT_KLIBC_USING_LIBC_STRLEN
bool "Enable rt_strlen to use libc strlen"
default n
endif
endmenu # rt_strlen options
menu "rt_strnlen options"
config RT_KLIBC_USING_USER_STRNLEN
bool "Enable rt_strnlen to use user-defined version"
default n
endmenu # rt_strnlen options
config RT_UTEST_TC_USING_KLIBC
bool "Enable klibc utest cases"
select RT_USING_UTEST
default n
endmenu
+23
View File
@@ -0,0 +1,23 @@
from building import *
import os
cwd = GetCurrentDir()
src = ['kerrno.c', 'kstdio.c', 'kstring.c']
if not GetDepend(['RT_KLIBC_USING_LIBC_VSNPRINTF']):
if GetDepend(['RT_KLIBC_USING_VSNPRINTF_STANDARD']):
src += ['rt_vsnprintf_std.c']
else:
src += ['rt_vsnprintf_tiny.c']
if not GetDepend(['RT_KLIBC_USING_LIBC_VSSCANF']):
src += ['rt_vsscanf.c']
group = DefineGroup('klibc', src, depend = [''])
list = os.listdir(cwd)
for item in list:
if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
group = group + SConscript(os.path.join(item, 'SConscript'))
Return('group')
+161
View File
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-09-22 Meco Man the first version
*/
#include <rtthread.h>
/**
* @brief A global variable used to store the error code.
*
* This volatile static integer is used to store the most recent error code globally.
* Its volatile nature ensures that every read operation fetches the most current value,
* providing real-time error status across different parts of the program.
*/
static volatile int __rt_errno;
/**
* @struct _errno_str_t
* @brief Structure for mapping error codes to corresponding error strings.
*
* This structure is used to create a mapping that associates an rt_err_t type error code
* with a corresponding error description string.
*/
struct _errno_str_t
{
rt_err_t error; /**< Error code of type rt_err_t, representing different kinds of errors. */
const char *str; /**< Pointer to the error description string. */
};
/**
* @brief An array containing mappings of error codes to their corresponding error strings.
*
* This array uses the _errno_str_t structure to define several error codes and their
* corresponding error description strings. These mappings can be used at runtime
* to provide more readable error information.
*/
static struct _errno_str_t rt_errno_strs[] =
{
{RT_EOK , "OK "}, /**< Operation successful. */
{RT_ERROR , "ERROR "}, /**< General error. */
{RT_ETIMEOUT, "ETIMOUT"}, /**< Operation timed out. */
{RT_EFULL , "ERSFULL"}, /**< Resource is full. */
{RT_EEMPTY , "ERSEPTY"}, /**< Resource is empty. */
{RT_ENOMEM , "ENOMEM "}, /**< Not enough memory. */
{RT_ENOSYS , "ENOSYS "}, /**< Function not implemented. */
{RT_EBUSY , "EBUSY "}, /**< Resource is busy. */
{RT_EIO , "EIO "}, /**< Input/output error. */
{RT_EINTR , "EINTRPT"}, /**< Interrupted system call. */
{RT_EINVAL , "EINVAL "}, /**< Invalid argument. */
{RT_ENOENT , "ENOENT "}, /**< No such file or directory. */
{RT_ENOSPC , "ENOSPC "}, /**< No space left on device. */
{RT_EPERM , "EPERM "}, /**< Operation not permitted. */
{RT_ETRAP , "ETRAP "}, /**< Trap error. */
};
/**
* @brief This function return a pointer to a string that contains the
* message of error.
*
* @param error the errorno code
* @return a point to error message string
*/
const char *rt_strerror(rt_err_t error)
{
int i = 0;
if (error < 0)
error = -error;
for (i = 0; i < sizeof(rt_errno_strs) / sizeof(rt_errno_strs[0]); i++)
{
if (rt_errno_strs[i].error == error)
return rt_errno_strs[i].str;
}
return "EUNKNOW";
}
RTM_EXPORT(rt_strerror);
/**
* @brief This function gets the global errno for the current thread.
*
* @return errno
*/
rt_err_t rt_get_errno(void)
{
rt_thread_t tid = RT_NULL;
if (rt_interrupt_get_nest() != 0)
{
/* it's in interrupt context */
return __rt_errno;
}
tid = rt_thread_self();
if (tid == RT_NULL)
{
return __rt_errno;
}
return tid->error;
}
RTM_EXPORT(rt_get_errno);
/**
* @brief This function sets the global errno for the current thread.
*
* @param error is the errno shall be set.
*/
void rt_set_errno(rt_err_t error)
{
rt_thread_t tid = RT_NULL;
if (rt_interrupt_get_nest() != 0)
{
/* it's in interrupt context */
__rt_errno = error;
return;
}
tid = rt_thread_self();
if (tid == RT_NULL)
{
__rt_errno = error;
return;
}
tid->error = error;
}
RTM_EXPORT(rt_set_errno);
/**
* @brief This function returns the address of the current thread errno.
*
* @return The errno address.
*/
int *_rt_errno(void)
{
rt_thread_t tid = RT_NULL;
if (rt_interrupt_get_nest() != 0)
{
return (int *)&__rt_errno;
}
tid = rt_thread_self();
if (tid != RT_NULL)
{
return (int *) & (tid->error);
}
return (int *)&__rt_errno;
}
RTM_EXPORT(_rt_errno);
+116
View File
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-03-10 Meco Man the first version
*/
#include <rtthread.h>
#if defined(RT_KLIBC_USING_LIBC_VSSCANF) || \
defined(RT_KLIBC_USING_LIBC_VSNPRINTF)
#include <stdio.h>
#endif
/**
* @brief This function will fill a formatted string to buffer.
*
* @param buf is the buffer to save formatted string.
*
* @param size is the size of buffer.
*
* @param fmt is the format parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...)
{
rt_int32_t n = 0;
va_list args;
va_start(args, fmt);
n = rt_vsnprintf(buf, size, fmt, args);
va_end(args);
return n;
}
RTM_EXPORT(rt_snprintf);
/**
* @brief This function will fill a formatted string to buffer.
*
* @param buf is the buffer to save formatted string.
*
* @param format is the format parameters.
*
* @param arg_ptr is a list of variable parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_vsprintf(char *buf, const char *format, va_list arg_ptr)
{
return rt_vsnprintf(buf, (rt_size_t) - 1, format, arg_ptr);
}
RTM_EXPORT(rt_vsprintf);
/**
* @brief This function will fill a formatted string to buffer
*
* @param buf the buffer to save formatted string.
*
* @param format is the format parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_sprintf(char *buf, const char *format, ...)
{
rt_int32_t n = 0;
va_list arg_ptr;
va_start(arg_ptr, format);
n = rt_vsprintf(buf, format, arg_ptr);
va_end(arg_ptr);
return n;
}
RTM_EXPORT(rt_sprintf);
#ifdef RT_KLIBC_USING_LIBC_VSNPRINTF
int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
{
return vsnprintf(buf, size, fmt, args);
}
#endif /* RT_KLIBC_USING_LIBC_VSNPRINTF */
RTM_EXPORT(rt_vsnprintf);
#ifdef RT_KLIBC_USING_LIBC_VSSCANF
int rt_vsscanf(const char *buffer, const char *format, va_list ap)
{
return vsscanf(buffer, format, ap);
}
#endif /* RT_KLIBC_USING_LIBC_VSSCANF */
RTM_EXPORT(rt_vsscanf);
/**
* @brief This function parses a formatted string from the input string.
*
* @param str the input string to be parsed.
*
* @param format the format string that specifies how to interpret the input.
*
* @return The number of input items successfully matched and assigned.
*/
int rt_sscanf(const char *str, const char *format, ...)
{
va_list ap;
int rv;
va_start(ap, format);
rv = rt_vsscanf(str, format, ap);
va_end(ap);
return rv;
}
RTM_EXPORT(rt_sscanf);
+559
View File
@@ -0,0 +1,559 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-03-10 Meco Man the first version
*/
#include <rtthread.h>
#if defined(RT_KLIBC_USING_LIBC_MEMSET) || \
defined(RT_KLIBC_USING_LIBC_MEMCPY) || \
defined(RT_KLIBC_USING_LIBC_MEMMOVE) || \
defined(RT_KLIBC_USING_LIBC_MEMCMP) || \
defined(RT_KLIBC_USING_LIBC_STRSTR) || \
defined(RT_KLIBC_USING_LIBC_STRNCPY) || \
defined(RT_KLIBC_USING_LIBC_STRCPY) || \
defined(RT_KLIBC_USING_LIBC_STRNCMP) || \
defined(RT_KLIBC_USING_LIBC_STRCMP) || \
defined(RT_KLIBC_USING_LIBC_STRLEN)
#include <string.h>
#endif
/**
* @brief This function will set the content of memory to specified value.
*
* @param s is the address of source memory, point to the memory block to be filled.
*
* @param c is the value to be set. The value is passed in int form, but the function
* uses the unsigned character form of the value when filling the memory block.
*
* @param count number of bytes to be set.
*
* @return The address of source memory.
*/
#ifndef RT_KLIBC_USING_USER_MEMSET
void *rt_memset(void *s, int c, rt_ubase_t count)
{
#if defined(RT_KLIBC_USING_LIBC_MEMSET)
return memset(s, c, count);
#elif defined(RT_KLIBC_USING_TINY_MEMSET)
char *xs = (char *)s;
while (count--)
*xs++ = c;
return s;
#else
#define LBLOCKSIZE (sizeof(rt_ubase_t))
#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1))
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
unsigned int i = 0;
char *m = (char *)s;
unsigned long buffer = 0;
unsigned long *aligned_addr = RT_NULL;
unsigned char d = (unsigned int)c & (unsigned char)(-1); /* To avoid sign extension, copy C to an
unsigned variable. (unsigned)((char)(-1))=0xFF for 8bit and =0xFFFF for 16bit: word independent */
RT_ASSERT(LBLOCKSIZE == 2 || LBLOCKSIZE == 4 || LBLOCKSIZE == 8);
if (!TOO_SMALL(count) && !UNALIGNED(s))
{
/* If we get this far, we know that count is large and s is word-aligned. */
aligned_addr = (unsigned long *)s;
/* Store d into each char sized location in buffer so that
* we can set large blocks quickly.
*/
for (i = 0; i < LBLOCKSIZE; i++)
{
*(((unsigned char *)&buffer)+i) = d;
}
while (count >= LBLOCKSIZE * 4)
{
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
count -= 4 * LBLOCKSIZE;
}
while (count >= LBLOCKSIZE)
{
*aligned_addr++ = buffer;
count -= LBLOCKSIZE;
}
/* Pick up the remainder with a bytewise loop. */
m = (char *)aligned_addr;
}
while (count--)
{
*m++ = (char)d;
}
return s;
#undef LBLOCKSIZE
#undef UNALIGNED
#undef TOO_SMALL
#endif /* RT_KLIBC_USING_LIBC_MEMSET */
}
#endif /* RT_KLIBC_USING_USER_MEMSET */
RTM_EXPORT(rt_memset);
/**
* @brief This function will copy memory content from source address to destination address.
*
* @param dst is the address of destination memory, points to the copied content.
*
* @param src is the address of source memory, pointing to the data source to be copied.
*
* @param count is the copied length.
*
* @return The address of destination memory
*/
#ifndef RT_KLIBC_USING_USER_MEMCPY
void *rt_memcpy(void *dst, const void *src, rt_ubase_t count)
{
#if defined(RT_KLIBC_USING_LIBC_MEMCPY)
return memcpy(dst, src, count);
#elif defined(RT_KLIBC_USING_TINY_MEMCPY)
char *tmp = (char *)dst, *s = (char *)src;
rt_ubase_t len = 0;
if (tmp <= s || tmp > (s + count))
{
while (count--)
*tmp ++ = *s ++;
}
else
{
for (len = count; len > 0; len --)
tmp[len - 1] = s[len - 1];
}
return dst;
#else
#define UNALIGNED(X, Y) \
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
#define BIGBLOCKSIZE (sizeof (long) << 2)
#define LITTLEBLOCKSIZE (sizeof (long))
#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE)
char *dst_ptr = (char *)dst;
char *src_ptr = (char *)src;
long *aligned_dst = RT_NULL;
long *aligned_src = RT_NULL;
rt_ubase_t len = count;
/* If the size is small, or either SRC or DST is unaligned,
then punt into the byte copy loop. This should be rare. */
if (!TOO_SMALL(len) && !UNALIGNED(src_ptr, dst_ptr))
{
aligned_dst = (long *)dst_ptr;
aligned_src = (long *)src_ptr;
/* Copy 4X long words at a time if possible. */
while (len >= BIGBLOCKSIZE)
{
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
len -= BIGBLOCKSIZE;
}
/* Copy one long word at a time if possible. */
while (len >= LITTLEBLOCKSIZE)
{
*aligned_dst++ = *aligned_src++;
len -= LITTLEBLOCKSIZE;
}
/* Pick up any residual with a byte copier. */
dst_ptr = (char *)aligned_dst;
src_ptr = (char *)aligned_src;
}
while (len--)
*dst_ptr++ = *src_ptr++;
return dst;
#undef UNALIGNED
#undef BIGBLOCKSIZE
#undef LITTLEBLOCKSIZE
#undef TOO_SMALL
#endif /* RT_KLIBC_USING_LIBC_MEMCPY */
}
#endif /* RT_KLIBC_USING_USER_MEMCPY */
RTM_EXPORT(rt_memcpy);
/**
* @brief This function will move memory content from source address to destination
* address. If the destination memory does not overlap with the source memory,
* the function is the same as memcpy().
*
* @param dest is the address of destination memory, points to the copied content.
*
* @param src is the address of source memory, point to the data source to be copied.
*
* @param n is the copied length.
*
* @return The address of destination memory.
*/
#ifndef RT_KLIBC_USING_USER_MEMMOVE
void *rt_memmove(void *dest, const void *src, rt_size_t n)
{
#ifdef RT_KLIBC_USING_LIBC_MEMMOVE
return memmove(dest, src, n);
#else
char *tmp = (char *)dest, *s = (char *)src;
if (s < tmp && tmp < s + n)
{
tmp += n;
s += n;
while (n--)
*(--tmp) = *(--s);
}
else
{
while (n--)
*tmp++ = *s++;
}
return dest;
#endif /* RT_KLIBC_USING_LIBC_MEMMOVE */
}
#endif /* RT_KLIBC_USING_USER_MEMMOVE */
RTM_EXPORT(rt_memmove);
/**
* @brief This function will compare two areas of memory.
*
* @param cs is a block of memory.
*
* @param ct is another block of memory.
*
* @param count is the size of the area.
*
* @return Compare the results:
* If the result < 0, cs is smaller than ct.
* If the result > 0, cs is greater than ct.
* If the result = 0, cs is equal to ct.
*/
#ifndef RT_KLIBC_USING_USER_MEMCMP
rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_size_t count)
{
#ifdef RT_KLIBC_USING_LIBC_MEMCMP
return memcmp(cs, ct, count);
#else
const unsigned char *su1 = RT_NULL, *su2 = RT_NULL;
int res = 0;
for (su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0)
break;
return res;
#endif /* RT_KLIBC_USING_LIBC_MEMCMP */
}
#endif /* RT_KLIBC_USING_USER_MEMCMP */
RTM_EXPORT(rt_memcmp);
/**
* @brief This function will return the first occurrence of a string, without the
* terminator '\0'.
*
* @param s1 is the source string.
*
* @param s2 is the find string.
*
* @return The first occurrence of a s2 in s1, or RT_NULL if no found.
*/
#ifndef RT_KLIBC_USING_USER_STRSTR
char *rt_strstr(const char *s1, const char *s2)
{
#ifdef RT_KLIBC_USING_LIBC_STRSTR
return strstr(s1, s2);
#else
int l1 = 0, l2 = 0;
l2 = rt_strlen(s2);
if (!l2)
{
return (char *)s1;
}
l1 = rt_strlen(s1);
while (l1 >= l2)
{
l1 --;
if (!rt_memcmp(s1, s2, l2))
{
return (char *)s1;
}
s1 ++;
}
return RT_NULL;
#endif /* RT_KLIBC_USING_LIBC_STRSTR */
}
#endif /* RT_KLIBC_USING_USER_STRSTR */
RTM_EXPORT(rt_strstr);
/**
* @brief This function will compare two strings while ignoring differences in case
*
* @param a is the string to be compared.
*
* @param b is the string to be compared.
*
* @return Compare the results:
* If the result < 0, a is smaller than a.
* If the result > 0, a is greater than a.
* If the result = 0, a is equal to a.
*/
#ifndef RT_KLIBC_USING_USER_STRCASECMP
rt_int32_t rt_strcasecmp(const char *a, const char *b)
{
int ca = 0, cb = 0;
do
{
ca = *a++ & 0xff;
cb = *b++ & 0xff;
if (ca >= 'A' && ca <= 'Z')
ca += 'a' - 'A';
if (cb >= 'A' && cb <= 'Z')
cb += 'a' - 'A';
}
while (ca == cb && ca != '\0');
return ca - cb;
}
#endif /* RT_KLIBC_USING_USER_STRCASECMP */
RTM_EXPORT(rt_strcasecmp);
/**
* @brief This function will copy string no more than n bytes.
*
* @param dst points to the address used to store the copied content.
*
* @param src is the string to be copied.
*
* @param n is the maximum copied length.
*
* @return The address where the copied content is stored.
*/
#ifndef RT_KLIBC_USING_USER_STRNCPY
char *rt_strncpy(char *dst, const char *src, rt_size_t n)
{
#ifdef RT_KLIBC_USING_LIBC_STRNCPY
return strncpy(dst, src, n);
#else
if (n != 0)
{
char *d = dst;
const char *s = src;
do
{
if ((*d++ = *s++) == 0)
{
/* NUL pad the remaining n-1 bytes */
while (--n != 0)
{
*d++ = 0;
}
break;
}
} while (--n != 0);
}
return (dst);
#endif /* RT_KLIBC_USING_LIBC_STRNCPY */
}
#endif /* RT_KLIBC_USING_USER_STRNCPY */
RTM_EXPORT(rt_strncpy);
/**
* @brief This function will copy string.
*
* @param dst points to the address used to store the copied content.
*
* @param src is the string to be copied.
*
* @return The address where the copied content is stored.
*/
#ifndef RT_KLIBC_USING_USER_STRCPY
char *rt_strcpy(char *dst, const char *src)
{
#ifdef RT_KLIBC_USING_LIBC_STRCPY
return strcpy(dst, src);
#else
char *dest = dst;
while (*src != '\0')
{
*dst = *src;
dst++;
src++;
}
*dst = '\0';
return dest;
#endif /* RT_KLIBC_USING_LIBC_STRCPY */
}
#endif /* RT_KLIBC_USING_USER_STRCPY */
RTM_EXPORT(rt_strcpy);
/**
* @brief This function will compare two strings with specified maximum length.
*
* @param cs is the string to be compared.
*
* @param ct is the string to be compared.
*
* @param count is the maximum compare length.
*
* @return Compare the results:
* If the result < 0, cs is smaller than ct.
* If the result > 0, cs is greater than ct.
* If the result = 0, cs is equal to ct.
*/
#ifndef RT_KLIBC_USING_USER_STRNCMP
rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_size_t count)
{
#ifdef RT_KLIBC_USING_LIBC_STRNCMP
return strncmp(cs, ct, count);
#else
signed char res = 0;
while (count)
{
if ((res = *cs - *ct++) != 0 || !*cs++)
{
break;
}
count --;
}
return res;
#endif /* RT_KLIBC_USING_LIBC_STRNCMP */
}
#endif /* RT_KLIBC_USING_USER_STRNCMP */
RTM_EXPORT(rt_strncmp);
/**
* @brief This function will compare two strings without specified length.
*
* @param cs is the string to be compared.
*
* @param ct is the string to be compared.
*
* @return Compare the results:
* If the result < 0, cs is smaller than ct.
* If the result > 0, cs is greater than ct.
* If the result = 0, cs is equal to ct.
*/
#ifndef RT_KLIBC_USING_USER_STRCMP
rt_int32_t rt_strcmp(const char *cs, const char *ct)
{
#ifdef RT_KLIBC_USING_LIBC_STRCMP
return strcmp(cs, ct);
#else
while (*cs && *cs == *ct)
{
cs++;
ct++;
}
return (*cs - *ct);
#endif /* RT_KLIBC_USING_LIBC_STRCMP */
}
#endif /* RT_KLIBC_USING_USER_STRCMP */
RTM_EXPORT(rt_strcmp);
/**
* @brief This function will return the length of a string, which terminate will
* null character.
*
* @param s is the string
*
* @return The length of string.
*/
#ifndef RT_KLIBC_USING_USER_STRLEN
rt_size_t rt_strlen(const char *s)
{
#ifdef RT_KLIBC_USING_LIBC_STRLEN
return strlen(s);
#else
const char *sc = RT_NULL;
for (sc = s; *sc != '\0'; ++sc);
return sc - s;
#endif /* RT_KLIBC_USING_LIBC_STRLEN */
}
#endif /* RT_KLIBC_USING_USER_STRLEN */
RTM_EXPORT(rt_strlen);
/**
* @brief The strnlen() function returns the number of characters in the
* string pointed to by s, excluding the terminating null byte ('\0'),
* but at most maxlen. In doing this, strnlen() looks only at the
* first maxlen characters in the string pointed to by s and never
* beyond s+maxlen.
*
* @param s is the string.
*
* @param maxlen is the max size.
*
* @return The length of string.
*/
#ifndef RT_KLIBC_USING_USER_STRNLEN
rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen)
{
const char *sc;
for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc);
return sc - s;
}
#endif /* RT_KLIBC_USING_USER_STRNLEN */
RTM_EXPORT(rt_strnlen);
#ifdef RT_USING_HEAP
/**
* @brief This function will duplicate a string.
*
* @param s is the string to be duplicated.
*
* @return The string address of the copy.
*/
char *rt_strdup(const char *s)
{
rt_size_t len = rt_strlen(s) + 1;
char *tmp = (char *)rt_malloc(len);
if (!tmp)
{
return RT_NULL;
}
rt_memcpy(tmp, s, len);
return tmp;
}
RTM_EXPORT(rt_strdup);
#endif /* RT_USING_HEAP */
File diff suppressed because it is too large Load Diff
+611
View File
@@ -0,0 +1,611 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-11-19 Meco Man the first version
*/
#include <rtthread.h>
#define _ISDIGIT(c) ((unsigned)((c) - '0') < 10)
/**
* @brief This function will duplicate a string.
*
* @param n is the string to be duplicated.
*
* @param base is support divide instructions value.
*
* @return the duplicated string pointer.
*/
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
rt_inline int divide(unsigned long long *n, int base)
#else
rt_inline int divide(unsigned long *n, int base)
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
{
int res;
/* optimized for processor which does not support divide instructions. */
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
res = (int)((*n) % base);
*n = (long long)((*n) / base);
#else
res = (int)((*n) % base);
*n = (long)((*n) / base);
#endif
return res;
}
rt_inline int skip_atoi(const char **s)
{
int i = 0;
while (_ISDIGIT(**s))
i = i * 10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD (1 << 0) /* pad with zero */
#define SIGN (1 << 1) /* unsigned/signed long */
#define PLUS (1 << 2) /* show plus */
#define SPACE (1 << 3) /* space if plus */
#define LEFT (1 << 4) /* left justified */
#define SPECIAL (1 << 5) /* 0x */
#define LARGE (1 << 6) /* use 'ABCDEF' instead of 'abcdef' */
static char *print_number(char *buf,
char *end,
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
unsigned long long num,
#else
unsigned long num,
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
int base,
int qualifier,
int s,
int precision,
int type)
{
char c = 0, sign = 0;
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
char tmp[64] = {0};
#else
char tmp[32] = {0};
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
int precision_bak = precision;
const char *digits = RT_NULL;
static const char small_digits[] = "0123456789abcdef";
static const char large_digits[] = "0123456789ABCDEF";
int i = 0;
int size = 0;
size = s;
digits = (type & LARGE) ? large_digits : small_digits;
if (type & LEFT)
{
type &= ~ZEROPAD;
}
c = (type & ZEROPAD) ? '0' : ' ';
/* get sign */
sign = 0;
if (type & SIGN)
{
switch (qualifier)
{
case 'h':
if ((rt_int16_t)num < 0)
{
sign = '-';
num = (rt_uint16_t)-num;
}
break;
case 'L':
case 'l':
if ((long)num < 0)
{
sign = '-';
num = (unsigned long)-num;
}
break;
case 0:
default:
if ((rt_int32_t)num < 0)
{
sign = '-';
num = (rt_uint32_t)-num;
}
break;
}
if (sign != '-')
{
if (type & PLUS)
{
sign = '+';
}
else if (type & SPACE)
{
sign = ' ';
}
}
}
if (type & SPECIAL)
{
if (base == 2 || base == 16)
{
size -= 2;
}
else if (base == 8)
{
size--;
}
}
i = 0;
if (num == 0)
{
tmp[i++] = '0';
}
else
{
while (num != 0)
tmp[i++] = digits[divide(&num, base)];
}
if (i > precision)
{
precision = i;
}
size -= precision;
if (!(type & (ZEROPAD | LEFT)))
{
if ((sign) && (size > 0))
{
size--;
}
while (size-- > 0)
{
if (buf < end)
{
*buf = ' ';
}
++ buf;
}
}
if (sign)
{
if (buf < end)
{
*buf = sign;
}
-- size;
++ buf;
}
if (type & SPECIAL)
{
if (base == 2)
{
if (buf < end)
*buf = '0';
++ buf;
if (buf < end)
*buf = 'b';
++ buf;
}
else if (base == 8)
{
if (buf < end)
*buf = '0';
++ buf;
}
else if (base == 16)
{
if (buf < end)
{
*buf = '0';
}
++ buf;
if (buf < end)
{
*buf = type & LARGE ? 'X' : 'x';
}
++ buf;
}
}
/* no align to the left */
if (!(type & LEFT))
{
while (size-- > 0)
{
if (buf < end)
{
*buf = c;
}
++ buf;
}
}
while (i < precision--)
{
if (buf < end)
{
*buf = '0';
}
++ buf;
}
/* put number in the temporary buffer */
while (i-- > 0 && (precision_bak != 0))
{
if (buf < end)
{
*buf = tmp[i];
}
++ buf;
}
while (size-- > 0)
{
if (buf < end)
{
*buf = ' ';
}
++ buf;
}
return buf;
}
#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
/* Disable "-Wimplicit-fallthrough" below GNUC V7 */
#pragma GCC diagnostic push
/* ignore warning: this statement may fall through */
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
/**
* @brief This function will fill a formatted string to buffer.
*
* @param buf is the buffer to save formatted string.
*
* @param size is the size of buffer.
*
* @param fmt is the format parameters.
*
* @param args is a list of variable parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
{
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
unsigned long long num = 0;
#else
unsigned long num = 0;
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
int i = 0, len = 0;
char *str = RT_NULL, *end = RT_NULL, c = 0;
const char *s = RT_NULL;
rt_uint8_t base = 0; /* the base of number */
rt_uint8_t flags = 0; /* flags to print number */
rt_uint8_t qualifier = 0; /* 'h', 'l', or 'L' for integer fields */
rt_int32_t field_width = 0; /* width of output field */
int precision = 0; /* min. # of digits for integers and max for a string */
str = buf;
end = buf + size;
/* Make sure end is always >= buf */
if (end < buf)
{
end = ((char *) - 1);
size = end - buf;
}
for (; *fmt ; ++fmt)
{
if (*fmt != '%')
{
if (str < end)
{
*str = *fmt;
}
++ str;
continue;
}
/* process flags */
flags = 0;
while (1)
{
/* skips the first '%' also */
++fmt;
if (*fmt == '-') flags |= LEFT;
else if (*fmt == '+') flags |= PLUS;
else if (*fmt == ' ') flags |= SPACE;
else if (*fmt == '#') flags |= SPECIAL;
else if (*fmt == '0') flags |= ZEROPAD;
else break;
}
/* get field width */
field_width = -1;
if (_ISDIGIT(*fmt))
{
field_width = skip_atoi(&fmt);
}
else if (*fmt == '*')
{
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0)
{
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.')
{
++fmt;
if (_ISDIGIT(*fmt))
{
precision = skip_atoi(&fmt);
}
else if (*fmt == '*')
{
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
{
precision = 0;
}
}
qualifier = 0; /* get the conversion qualifier */
if (*fmt == 'h' || *fmt == 'l' ||
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
*fmt == 'L' ||
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
*fmt == 'z')
{
qualifier = *fmt;
++fmt;
#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
if (qualifier == 'l' && *fmt == 'l')
{
qualifier = 'L';
++fmt;
}
#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
if (qualifier == 'h' && *fmt == 'h')
{
qualifier = 'H';
++fmt;
}
}
/* the default base */
base = 10;
switch (*fmt)
{
case 'c':
if (!(flags & LEFT))
{
while (--field_width > 0)
{
if (str < end) *str = ' ';
++ str;
}
}
/* get character */
c = (rt_uint8_t)va_arg(args, int);
if (str < end)
{
*str = c;
}
++ str;
/* put width */
while (--field_width > 0)
{
if (str < end) *str = ' ';
++ str;
}
continue;
case 's':
s = va_arg(args, char *);
if (!s)
{
s = "(null)";
}
for (len = 0; (len != field_width) && (s[len] != '\0'); len++);
if (precision > 0 && len > precision)
{
len = precision;
}
if (!(flags & LEFT))
{
while (len < field_width--)
{
if (str < end) *str = ' ';
++ str;
}
}
for (i = 0; i < len; ++i)
{
if (str < end) *str = *s;
++ str;
++ s;
}
while (len < field_width--)
{
if (str < end) *str = ' ';
++ str;
}
continue;
case 'p':
if (field_width == -1)
{
field_width = sizeof(void *) << 1;
field_width += 2; /* `0x` prefix */
flags |= SPECIAL;
flags |= ZEROPAD;
}
str = print_number(str, end, (unsigned long)va_arg(args, void *),
16, qualifier, field_width, precision, flags);
continue;
case '%':
if (str < end)
{
*str = '%';
}
++ str;
continue;
/* integer number formats - set up the flags and "break" */
case 'b':
base = 2;
break;
case 'o':
base = 8;
break;
case 'X':
flags |= LARGE;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
case 'e':
case 'E':
case 'G':
case 'g':
case 'f':
case 'F':
va_arg(args, double);
default:
if (str < end)
{
*str = '%';
}
++ str;
if (*fmt)
{
if (str < end)
{
*str = *fmt;
}
++ str;
}
else
{
-- fmt;
}
continue;
}
if (qualifier == 'L')
{
num = va_arg(args, unsigned long long);
}
else if (qualifier == 'l')
{
num = va_arg(args, unsigned long);
}
else if (qualifier == 'H')
{
num = (rt_int8_t)va_arg(args, rt_int32_t);
if (flags & SIGN)
{
num = (rt_int8_t)num;
}
}
else if (qualifier == 'h')
{
num = (rt_uint16_t)va_arg(args, rt_int32_t);
if (flags & SIGN)
{
num = (rt_int16_t)num;
}
}
else if (qualifier == 'z')
{
num = va_arg(args, rt_size_t);
if (flags & SIGN)
{
num = (rt_ssize_t)num;
}
}
else
{
num = (rt_uint32_t)va_arg(args, unsigned long);
}
str = print_number(str, end, num, base, qualifier, field_width, precision, flags);
}
if (size > 0)
{
if (str < end)
{
*str = '\0';
}
else
{
end[-1] = '\0';
}
}
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str - buf;
}
#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
#pragma GCC diagnostic pop /* ignored "-Wimplicit-fallthrough" */
#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
+700
View File
@@ -0,0 +1,700 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-11-24 Meco Man port to klibc
* 2025-01-04 Meco Man using Phoenix version
*/
/*
* Copyright 2017, 2022-2023 Phoenix Systems
* Author: Adrian Kepka, Gerard Swiderski
*/
#include <rtthread.h>
#include <stdlib.h> /* for strtod */
#include <ctype.h> /* for isspace */
#include <stdarg.h> /* for va_list */
#define FORMAT_NIL_STR "(nil)"
#define FORMAT_NIL_STR_LEN (sizeof(FORMAT_NIL_STR) - 1)
#define LONG 0x01 /* l: long or double */
#define LONGDOUBLE 0x02 /* L: long double */
#define SHORT 0x04 /* h: short */
#define SUPPRESS 0x08 /* *: suppress assignment */
#define POINTER 0x10 /* p: void * (as hex) */
#define NOSKIP 0x20 /* [ or c: do not skip blanks */
#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
#define PTRDIFF 0x800 /* t: ptrdiff_t */
#define SHORTSHORT 0x4000 /* hh: char */
#define UNSIGNED 0x8000 /* %[oupxX] conversions */
#define SIGNOK 0x40 /* +/- is (still) legal */
#define NDIGITS 0x80 /* no digits detected */
#define PFXOK 0x100 /* 0x prefix is (still) legal */
#define NZDIGITS 0x200 /* no zero digits detected */
#define CT_CHAR 0 /* %c conversion */
#define CT_CCL 1 /* %[...] conversion */
#define CT_STRING 2 /* %s conversion */
#define CT_INT 3 /* %[dioupxX] conversion */
#define CT_FLOAT 4 /* %[aefgAEFG] conversion */
#define CT_NONE 5 /* No conversion (ex. %n) */
static const unsigned char *__sccl(char *tab, const unsigned char *fmt)
{
int c, n, v;
c = *fmt++;
if (c == '^') {
v = 1;
c = *fmt++;
}
else {
v = 0;
}
rt_memset(tab, (uint8_t)v, 256);
if (c == 0) {
return (fmt - 1);
}
v = 1 - v;
tab[c] = v;
for (;;) {
n = *fmt++;
switch (n) {
case 0:
return (fmt - 1);
case '-':
n = *fmt;
if ((n == ']') || (n < c)) {
c = '-';
tab[c] = v;
break;
}
fmt++;
do {
tab[++c] = v;
} while (c < n);
c = n;
break;
case ']':
return (fmt);
default:
c = n;
tab[c] = v;
break;
}
}
}
static int scanf_parse(char *ccltab, const char *inp, int *inr, char const *fmt0, va_list ap)
{
const unsigned char *fmt = (const unsigned char *)fmt0;
int c, n, flags, nassigned, nconversions, nread, base;
rt_size_t width;
char *p, *p0;
char buf[32];
static const short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
*inr = rt_strlen(inp);
nassigned = 0;
nconversions = 0;
nread = 0;
base = 0;
for (;;) {
int convType = CT_NONE;
c = *fmt++;
if (c == '\0') {
return (nassigned);
}
if (isspace(c) != 0) {
while ((*inr > 0) && (isspace((int)*inp) != 0)) {
nread++;
(*inr)--;
inp++;
}
continue;
}
if (c != '%') {
if (*inr <= 0) {
return (nconversions != 0 ? nassigned : -1);
}
if (*inp != c) {
return nassigned;
}
nread++;
(*inr)--;
inp++;
continue;
}
width = 0;
flags = 0;
for (;;) {
c = *fmt++;
if (c == '\0') {
return nassigned;
}
if (c == '%') {
if (*inr <= 0) {
return (nconversions != 0 ? nassigned : -1);
}
if (*inp != c) {
return nassigned;
}
nread++;
(*inr)--;
inp++;
break;
}
switch (c) {
case '*':
flags |= SUPPRESS;
continue;
case 'l':
if ((flags & LONG) != 0) {
flags &= ~LONG;
flags |= LONGLONG;
}
else {
flags |= LONG;
}
continue;
case 'L':
flags |= LONGDOUBLE;
continue;
case 'q':
case 'j':
flags |= LONGLONG;
continue;
case 't':
flags |= PTRDIFF;
continue;
case 'z':
if (sizeof(rt_size_t) == sizeof(uint64_t)) {
flags |= LONGLONG;
}
continue;
case 'h':
if ((flags & SHORT) != 0) {
flags &= ~SHORT;
flags |= SHORTSHORT;
}
else {
flags |= SHORT;
}
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
width = width * 10 + c - '0';
continue;
default:
break;
}
/* conversions */
switch (c) {
case 'd':
convType = CT_INT;
base = 10;
break;
case 'i':
convType = CT_INT;
base = 0;
break;
case 'o':
convType = CT_INT;
flags |= UNSIGNED;
base = 8;
break;
case 'u':
convType = CT_INT;
flags |= UNSIGNED;
base = 10;
break;
case 'X':
case 'x':
flags |= PFXOK; /* enable 0x prefixing */
convType = CT_INT;
flags |= UNSIGNED;
base = 16;
break;
case 'A':
case 'E':
case 'F':
case 'G':
case 'a':
case 'e':
case 'f':
case 'g':
convType = CT_FLOAT;
break;
case 's':
convType = CT_STRING;
break;
case '[':
fmt = __sccl(ccltab, fmt);
flags |= NOSKIP;
convType = CT_CCL;
break;
case 'c':
flags |= NOSKIP;
convType = CT_CHAR;
break;
case 'p':
flags |= POINTER | PFXOK | UNSIGNED;
convType = CT_INT;
base = 16;
break;
case 'n':
nconversions++;
if ((flags & SUPPRESS) != 0) {
break;
}
if ((flags & SHORTSHORT) != 0) {
*va_arg(ap, char *) = nread;
}
else if ((flags & SHORT) != 0) {
*va_arg(ap, short *) = nread;
}
else if ((flags & LONG) != 0) {
*va_arg(ap, long *) = nread;
}
else if ((flags & LONGLONG) != 0) {
*va_arg(ap, long long *) = nread;
}
else if ((flags & PTRDIFF) != 0) {
*va_arg(ap, ptrdiff_t *) = nread;
}
else {
*va_arg(ap, int *) = nread;
}
break;
default:
/* Character not a conversion specifier; end parsing */
return nassigned;
}
break;
}
if (convType == CT_NONE) {
continue;
}
if (*inr <= 0) {
return (nconversions != 0 ? nassigned : -1);
}
if ((flags & NOSKIP) == 0) {
while (isspace((int)*inp) != 0) {
nread++;
if (--(*inr) > 0) {
inp++;
}
else {
return (nconversions != 0 ? nassigned : -1);
}
}
}
/* do the conversion */
switch (convType) {
case CT_CHAR:
if (width == 0) {
width = 1;
}
if (*inr <= 0) {
return (nconversions != 0 ? nassigned : -1);
}
if (width > *inr) {
width = *inr;
}
if ((flags & SUPPRESS) == 0) {
rt_memcpy(va_arg(ap, char *), inp, width);
nassigned++;
}
*inr -= width;
inp += width;
nread += width;
nconversions++;
break;
case CT_CCL:
if (width == 0) {
width = (rt_size_t)~0;
}
if ((flags & SUPPRESS) != 0) {
n = 0;
while (ccltab[(unsigned char)*inp] != 0) {
n++;
(*inr)--;
inp++;
if (--width == 0) {
break;
}
if (*inr <= 0) {
if (n == 0) {
return (nconversions != 0 ? nassigned : -1);
}
break;
}
}
if (n == 0) {
return nassigned;
}
}
else {
p0 = p = va_arg(ap, char *);
while (ccltab[(unsigned char)*inp] != 0) {
(*inr)--;
*p++ = *inp++;
if (--width == 0) {
break;
}
if (*inr <= 0) {
if (p == p0) {
return (nconversions != 0 ? nassigned : -1);
}
break;
}
}
n = p - p0;
if (n == 0) {
return nassigned;
}
*p = 0;
nassigned++;
}
nread += n;
nconversions++;
break;
case CT_STRING:
if (width == 0) {
width = (rt_size_t)~0;
}
if ((flags & SUPPRESS) != 0) {
while (isspace((int)*inp) == 0) {
nread++;
(*inr)--;
inp++;
if (--width == 0) {
break;
}
if (*inr <= 0) {
break;
}
}
}
else {
p0 = p = va_arg(ap, char *);
while (isspace((int)*inp) == 0) {
(*inr)--;
*p++ = *inp++;
if (--width == 0) {
break;
}
if (*inr <= 0) {
break;
}
}
*p = 0;
nread += p - p0;
nassigned++;
}
nconversions++;
continue;
case CT_INT:
if (((flags & POINTER) != 0) && ((*inr) >= FORMAT_NIL_STR_LEN) && (rt_strncmp(FORMAT_NIL_STR, inp, FORMAT_NIL_STR_LEN) == 0)) {
*va_arg(ap, void **) = RT_NULL;
nassigned++;
nconversions++;
nread += FORMAT_NIL_STR_LEN;
inp += FORMAT_NIL_STR_LEN;
(*inr) -= FORMAT_NIL_STR_LEN;
break;
}
if (--width > (sizeof(buf) - 2)) {
width = sizeof(buf) - 2;
}
width++;
if ((flags & SUPPRESS) != 0) {
width = ~0;
}
flags |= SIGNOK | NDIGITS | NZDIGITS;
for (p = buf; width; width--) {
int ok = 0;
c = *inp;
switch (c) {
case '0':
if (base == 0) {
base = 8;
flags |= PFXOK;
}
if ((flags & NZDIGITS) != 0) {
flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
}
else {
flags &= ~(SIGNOK | PFXOK | NDIGITS);
}
ok = 1;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
base = basefix[base];
flags &= ~(SIGNOK | PFXOK | NDIGITS);
ok = 1;
break;
case '8':
case '9':
base = basefix[base];
if (base <= 8) {
break; /* not legal here */
}
flags &= ~(SIGNOK | PFXOK | NDIGITS);
ok = 1;
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
if (base <= 10) {
break;
}
flags &= ~(SIGNOK | PFXOK | NDIGITS);
ok = 1;
break;
case '+':
case '-':
if ((flags & SIGNOK) != 0) {
flags &= ~SIGNOK;
ok = 1;
}
break;
case 'x':
case 'X':
if (((flags & PFXOK) != 0) && (p == buf + 1)) {
base = 16; /* if %i */
flags &= ~PFXOK;
ok = 1;
}
break;
}
if (!ok)
break;
if ((flags & SUPPRESS) == 0) {
*p++ = c;
}
if (--(*inr) > 0) {
inp++;
}
else {
break;
}
}
if ((flags & NDIGITS) != 0) {
return (nconversions != 0 ? nassigned : -1);
}
c = ((unsigned char *)p)[-1];
if ((c == 'x') || (c == 'X')) {
--p;
inp--;
(*inr)++;
}
if ((flags & SUPPRESS) == 0) {
uint64_t res;
*p = 0;
if ((flags & UNSIGNED) == 0) {
res = strtoll(buf, (char **)RT_NULL, base);
}
else {
res = strtoull(buf, (char **)RT_NULL, base);
}
if ((flags & POINTER) != 0) {
*va_arg(ap, void **) = (void *)(unsigned long)res;
}
else if ((flags & SHORTSHORT) != 0) {
*va_arg(ap, char *) = res;
}
else if ((flags & SHORT) != 0) {
*va_arg(ap, short *) = res;
}
else if ((flags & LONG) != 0) {
*va_arg(ap, long *) = res;
}
else if ((flags & LONGLONG) != 0) {
*va_arg(ap, long long *) = res;
}
else if ((flags & PTRDIFF) != 0) {
*va_arg(ap, ptrdiff_t *) = res;
}
else {
*va_arg(ap, int *) = res;
}
nassigned++;
}
nread += p - buf;
nconversions++;
break;
case CT_FLOAT: {
union {
float f;
double d;
long double ld;
} res;
const char *srcbuf = inp;
if ((width != 0) && (width < *inr)) {
/* TODO: handle larger widths */
if (width > (sizeof(buf) - 1)) {
return (nconversions != 0 ? nassigned : -1);
}
rt_memcpy(buf, inp, width);
buf[width] = '\0';
srcbuf = buf;
}
int is_zero;
if ((flags & LONGDOUBLE) != 0) {
res.ld = strtold(srcbuf, &p);
is_zero = res.ld == 0;
}
else if ((flags & LONG) != 0) {
res.d = strtod(srcbuf, &p);
is_zero = res.d == 0;
}
else {
res.f = strtof(srcbuf, &p);
is_zero = res.f == 0;
}
if (is_zero && (srcbuf == p)) {
return (nconversions != 0 ? nassigned : -1);
}
int consumed = p - srcbuf;
*inr -= consumed;
inp += consumed;
nread += consumed;
nconversions++;
if ((flags & SUPPRESS) == 0) {
if ((flags & LONGDOUBLE) != 0) {
*va_arg(ap, long double *) = res.ld;
}
else if ((flags & LONG) != 0) {
*va_arg(ap, double *) = res.d;
}
else {
*va_arg(ap, float *) = res.f;
}
nassigned++;
}
break;
}
default:
break;
}
}
/* never reached */
}
int rt_vsscanf(const char *str, const char *format, va_list ap)
{
int ret, nremain;
char *ccltab = rt_malloc(256);
if (ccltab == RT_NULL) {
return -1;
}
ret = scanf_parse(ccltab, str, &nremain, format, ap);
rt_free(ccltab);
return ret;
}
+10
View File
@@ -0,0 +1,10 @@
from building import *
src = []
if GetDepend('RT_UTEST_USING_ALL_CASES') or GetDepend('RT_UTEST_TC_USING_KLIBC'):
src += Glob('TC_*.c')
group = DefineGroup('utestcases', src, depend = [''])
Return('group')
+161
View File
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-05-06 Phillip Johnston the first version
* 2024-12-24 Meco Man port to utest
*/
#include <rtklibc.h>
#include <utest.h>
static void TC_rt_memcmp_str(void)
{
const char* s = "abc 123";
uassert_int_equal(rt_memcmp("abc", "abc", 4), 0);
uassert_int_equal(rt_memcmp(s, "abc", 3), 0);
uassert_int_equal(rt_memcmp("abc", s, 3), 0);
/* The following tests intentionally use a length > 3 */
/* To test what rt_memcmp does in such a situation */
uassert_value_greater(rt_memcmp(s, "abc", 6), 0);
uassert_value_less(rt_memcmp("abc", s, 6), 0);
}
static void TC_rt_memcmp_int_array(void)
{
int arr1[] = {1, 2, 3, 4, 5};
int arr2[] = {1, 2, 3, 4, 5};
int arr3[] = {1, 2, 3, 4, 6};
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
}
static void TC_rt_memcmp_float_array(void)
{
float arr1[] = {1.0f, 2.0f, 3.0f};
float arr2[] = {1.0f, 2.0f, 3.0f};
float arr3[] = {1.0f, 2.0f, 3.1f};
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
}
typedef struct {
int id;
float value;
} Item;
static void TC_rt_memcmp_struct_array(void)
{
Item arr1[] = {{1, 1.0f}, {2, 2.0f}};
Item arr2[] = {{1, 1.0f}, {2, 2.0f}};
Item arr3[] = {{1, 1.0f}, {2, 2.1f}};
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
}
typedef struct {
int id;
float value;
char name[10];
} MixedItem;
static void TC_rt_memcmp_mixed_array(void)
{
MixedItem arr1[] = {{1, 1.0f, "item1"}, {2, 2.0f, "item2"}};
MixedItem arr2[] = {{1, 1.0f, "item1"}, {2, 2.0f, "item2"}};
MixedItem arr3[] = {{1, 1.0f, "item1"}, {2, 2.1f, "item2"}};
uassert_int_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
uassert_value_less(rt_memcmp(arr1, arr3, sizeof(arr1)), 0);
uassert_value_greater(rt_memcmp(arr3, arr1, sizeof(arr1)), 0);
}
typedef struct {
int id;
float score;
} Student;
typedef struct {
Student students[3];
char className[10];
} Class;
static void TC_rt_memcmp_nested_struct_array(void)
{
Class class1 = {
.students = {{1, 90.5}, {2, 85.0}, {3, 92.0}},
.className = "ClassA"
};
Class class2 = {
.students = {{1, 90.5}, {2, 85.0}, {3, 92.0}},
.className = "ClassA"
};
Class class3 = {
.students = {{1, 90.5}, {2, 85.1}, {3, 92.0}},
.className = "ClassA"
};
uassert_int_equal(rt_memcmp(&class1, &class2, sizeof(Class)), 0);
uassert_int_not_equal(rt_memcmp(&class1, &class3, sizeof(Class)), 0);
}
static void TC_rt_memcmp_partial_match(void)
{
char arr1[] = "abcdefghijklmnopqrstuvwxyz";
char arr2[] = "abcdefghijklmxyznopqrstuvw";
uassert_int_equal(rt_memcmp(arr1, arr2, 13), 0);
uassert_int_not_equal(rt_memcmp(arr1, arr2, sizeof(arr1)), 0);
}
#define LARGE_ARRAY_SIZE 500
static void TC_rt_memcmp_large_array(void)
{
int *arr1 = rt_calloc(LARGE_ARRAY_SIZE, sizeof(int));
int *arr2 = rt_calloc(LARGE_ARRAY_SIZE, sizeof(int));
uassert_not_null(arr1);
uassert_not_null(arr2);
for (int i = 0; i < LARGE_ARRAY_SIZE; i++) {
arr1[i] = i;
arr2[i] = i;
}
uassert_int_equal(rt_memcmp(arr1, arr2, LARGE_ARRAY_SIZE * sizeof(int)), 0);
arr2[LARGE_ARRAY_SIZE - 1] = LARGE_ARRAY_SIZE;
uassert_value_less(rt_memcmp(arr1, arr2, LARGE_ARRAY_SIZE * sizeof(int)), 0);
uassert_value_greater(rt_memcmp(arr2, arr1, LARGE_ARRAY_SIZE * sizeof(int)), 0);
rt_free(arr1);
rt_free(arr2);
}
static void utest_do_tc(void)
{
UTEST_UNIT_RUN(TC_rt_memcmp_str);
UTEST_UNIT_RUN(TC_rt_memcmp_int_array);
UTEST_UNIT_RUN(TC_rt_memcmp_float_array);
UTEST_UNIT_RUN(TC_rt_memcmp_struct_array);
UTEST_UNIT_RUN(TC_rt_memcmp_mixed_array);
UTEST_UNIT_RUN(TC_rt_memcmp_nested_struct_array);
UTEST_UNIT_RUN(TC_rt_memcmp_partial_match);
UTEST_UNIT_RUN(TC_rt_memcmp_large_array);
}
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memcmp", RT_NULL, RT_NULL, 1000);
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-05-06 Phillip Johnston the first version
* 2024-12-24 Meco Man port to utest
*/
#include <rtthread.h>
#include <utest.h>
#define N 80 /**< Define the constant N for buffer size as 80 */
#define TEST_BUF_SIZE 512 /**< Define the constant TEST_BUF_SIZE as 512 */
static char *buf; /**< Define a static buffer of 512 bytes, initialized to 0 */
static rt_err_t utest_tc_init(void)
{
buf = rt_malloc(TEST_BUF_SIZE * sizeof(char)); /**< Allocate memory for the buffer */
uassert_not_null(buf);
return RT_EOK;
}
static rt_err_t utest_tc_cleanup(void)
{
rt_free(buf);
return RT_EOK;
}
/**
* Test memory copy with alignment.
* @param dalign The alignment offset for the destination buffer.
* @param salign The alignment offset for the source buffer.
* @param len The length of data to copy.
*/
static void test_align(unsigned dalign, unsigned salign, size_t len)
{
char *src = (char *)RT_ALIGN((rt_ubase_t)buf, 64); /**< Source buffer starting address, 64-byte aligned */
char *dst = (char *)RT_ALIGN(((rt_ubase_t)buf + 128), 64); /**< Destination buffer starting address, 64-byte aligned from buf+128 */
char *want = (char *)RT_ALIGN(((rt_ubase_t)buf + 256), 64); /**< Expected result buffer starting address, 64-byte aligned from buf+256 */
char *p; /**< Pointer to receive the return value of rt_memcpy */
unsigned i;
/** Assert that the source alignment offset plus length does not exceed N */
uassert_false(salign + len > N);
/** Assert that the destination alignment offset plus length does not exceed N */
uassert_false(dalign + len > N);
/** Initialize all buffers with '#' or ' ' */
for(i = 0; i < N; i++)
{
src[i] = '#';
dst[i] = want[i] = ' ';
}
/** Set data in the specified alignment offsets of the source and expected result buffers */
for(i = 0; i < len; i++)
{
src[salign + i] = want[dalign + i] = (char)('0' + i);
}
/** Call rt_memcpy to copy data */
p = rt_memcpy(dst + dalign, src + salign, len);
/** Assert that the return value of rt_memcpy is the pointer to the start of the copied data in the destination buffer */
uassert_ptr_equal(p, dst + dalign);
/** Assert that the content of the destination buffer matches the expected result buffer */
for(i = 0; i < N; i++)
{
uassert_int_equal(dst[i], want[i]);
}
}
/**
* Test case to iterate over all possible alignment offsets and length combinations.
*/
static void TC_rt_memcpy_align(void)
{
for(unsigned i = 0; i < 16; i++) /**< Iterate over source alignment offsets from 0 to 15 */
{
for(unsigned j = 0; j < 16; j++) /**< Iterate over destination alignment offsets from 0 to 15 */
{
for(size_t k = 0; k < 64; k++) /**< Iterate over data lengths from 0 to 63 */
{
test_align(i, j, k); /**< Call the test_align function */
}
}
}
}
static void TC_rt_memcpy_str(void)
{
const char src[] = "Hello, memcpy!";
char dest[20] = {0};
rt_memcpy(dest, src, sizeof(src));
uassert_true(rt_strcmp(src, dest) == 0);
}
static void utest_do_tc(void)
{
UTEST_UNIT_RUN(TC_rt_memcpy_str);
UTEST_UNIT_RUN(TC_rt_memcpy_align);
}
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memcpy", utest_tc_init, utest_tc_cleanup, 1000);
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-12-25 Meco Man first version
*/
#include <rtthread.h>
#include <utest.h>
/* Basic move with no overlap */
static void TC_rt_memmove_basic(void)
{
char src[] = "Hello";
char dest[10] = {0};
rt_memmove(dest, src, rt_strlen(src) + 1);
uassert_str_equal(dest, "Hello");
}
/* Move with overlap (src before dest) */
static void TC_rt_memmove_overlap_src_before(void)
{
char buffer[] = "1234567890";
rt_memmove(&buffer[3], buffer, 5);
uassert_str_equal(buffer, "1231234590");
}
/* Move with overlap (src after dest) */
static void TC_rt_memmove_overlap_src_after(void)
{
char buffer[] = "1234567890";
rt_memmove(&buffer[2], &buffer[5], 5);
uassert_str_equal(buffer, "1267890890");
}
/* Move with zero length */
static void TC_rt_memmove_zero_length(void)
{
char src[] = "Hello";
char dest[10] = "World";
rt_memmove(dest, src, 0);
uassert_str_equal(dest, "World");
}
/* Move to the same location */
static void TC_rt_memmove_same_location(void)
{
char buffer[] = "Hello";
rt_memmove(buffer, buffer, rt_strlen(buffer) + 1);
uassert_str_equal(buffer, "Hello");
}
/* Move from NULL */
static void TC_rt_memmove_null_src(void)
{
char dest[10];
rt_memset(dest, 'A', sizeof(dest));
rt_memmove(dest, RT_NULL, 0); /* Should not crash and do nothing */
uassert_buf_equal(dest, "AAAAAAAAAA", 10);
}
/* Move to NULL */
static void TC_rt_memmove_null_dest(void)
{
char src[] = "Hello";
rt_memmove(RT_NULL, src, 0); /* Should not crash and do nothing */
}
/* Move more than source size */
static void TC_rt_memmove_too_long(void)
{
char src[] = "Short";
char dest[10] = {0};
rt_memmove(dest, src, sizeof(src) + 5); /* Should only copy up to src length */
uassert_str_equal(dest, "Short");
uassert_int_equal(dest[5], 0); /* Ensure no buffer overflow */
}
/* Move empty string */
static void TC_rt_memmove_empty_string(void)
{
char src[] = "";
char dest[10] = "Unchanged";
rt_memmove(dest, src, rt_strlen(src) + 1);
/* Expect dest to only contain '\0' at the start */
uassert_str_equal(dest, ""); /* Destination should now be an empty string */
uassert_int_equal(dest[0], '\0'); /* First character should be '\0' */
}
/* Utest function to run all test cases */
static void utest_do_tc(void)
{
UTEST_UNIT_RUN(TC_rt_memmove_basic);
UTEST_UNIT_RUN(TC_rt_memmove_overlap_src_before);
UTEST_UNIT_RUN(TC_rt_memmove_overlap_src_after);
UTEST_UNIT_RUN(TC_rt_memmove_zero_length);
UTEST_UNIT_RUN(TC_rt_memmove_same_location);
UTEST_UNIT_RUN(TC_rt_memmove_null_src);
UTEST_UNIT_RUN(TC_rt_memmove_null_dest);
UTEST_UNIT_RUN(TC_rt_memmove_too_long);
UTEST_UNIT_RUN(TC_rt_memmove_empty_string);
}
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memmove", RT_NULL, RT_NULL, 1000);
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-05-06 Phillip Johnston the first version
* 2024-12-24 Meco Man port to utest
*/
#include <rtthread.h>
#include <utest.h>
#define TEST_BUF_SIZE 400
static char *buf;
static char *buf2;
static rt_err_t utest_tc_init(void)
{
buf = rt_malloc(TEST_BUF_SIZE * sizeof(char));
uassert_not_null(buf);
buf2 = rt_malloc(TEST_BUF_SIZE * sizeof(char));
uassert_not_null(buf2);
return RT_EOK;
}
static rt_err_t utest_tc_cleanup(void)
{
rt_free(buf);
rt_free(buf2);
return RT_EOK;
}
static void test_align(int align, size_t len)
{
char *s = (char *)RT_ALIGN(((rt_ubase_t)buf + 64), 64) + align;
char *want = (char *)RT_ALIGN(((rt_ubase_t)buf2 + 64), 64) + align;
char *p;
int i;
uassert_false(len + 64 > (size_t)(buf + TEST_BUF_SIZE - s));
uassert_false(len + 64 > (size_t)(buf2 + TEST_BUF_SIZE - want));
for(i = 0; i < TEST_BUF_SIZE; i++)
{
buf[i] = buf2[i] = ' ';
}
for(i = 0; i < (int)len; i++)
{
want[i] = '#';
}
p = rt_memset(s, '#', len);
uassert_ptr_equal(p, s);
for(i = -64; i < (int)len + 64; i++)
{
uassert_int_equal(s[i], want[i]);
}
}
static void TC_rt_memcpy_align(void)
{
for(int i = 0; i < 16; i++)
{
for(size_t j = 0; j < 200; j++)
{
test_align(i, j);
}
}
}
static void test_input(char c)
{
rt_memset(buf, c, 10);
for(int i = 0; i < 10; i++)
{
uassert_int_equal(buf[i], c);
}
}
static void TC_rt_memcpy_input(void)
{
test_input('c');
test_input(0);
test_input(-1);
test_input(0xab);
test_input((char)RT_UINT32_MAX);
test_input((char)-RT_UINT32_MAX);
}
static void utest_do_tc(void)
{
UTEST_UNIT_RUN(TC_rt_memcpy_align);
UTEST_UNIT_RUN(TC_rt_memcpy_input);
}
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_memset", utest_tc_init, utest_tc_cleanup, 1000);
File diff suppressed because it is too large Load Diff
+250
View File
@@ -0,0 +1,250 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-01-04 Meco Man the first version
*/
#include <rtklibc.h>
#include "utest.h"
static void TC_rt_sscanf_char(void)
{
const char str[] = "A B";
char a, b;
rt_sscanf(str, "%c %c", &a, &b);
uassert_true(a == 'A' && b == 'B');
/* Move to the next character after space for the second %c */
rt_sscanf(str + 2, "%c", &b);
uassert_true(b == 'B');
}
static void TC_rt_sscanf_basic_int(void)
{
const char str[] = "12345";
int value;
int result = rt_sscanf(str, "%d", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 12345);
}
static void TC_rt_sscanf_basic_float(void)
{
const char str[] = "123.45";
float value;
int result = rt_sscanf(str, "%f", &value);
uassert_int_equal(result, 1);
uassert_float_equal(value, 123.45);
}
static void TC_rt_sscanf_basic_string(void)
{
const char str[] = "Hello, World!";
char buffer[20];
int result = rt_sscanf(str, "%s", buffer);
uassert_int_equal(result, 1);
uassert_str_equal(buffer, "Hello,");
}
static void TC_rt_sscanf_string_with_space(void)
{
const char str[] = "Hello World";
char a[20];
rt_sscanf(str, "%*s %s", a);
uassert_str_equal(a, "World");
}
static void TC_rt_sscanf_basic_char(void)
{
const char str[] = "A";
char value;
int result = rt_sscanf(str, "%c", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 'A');
}
static void TC_rt_sscanf_hex_1(void)
{
const char str[] = "0x1A3F";
int value;
int result = rt_sscanf(str, "%x", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 0x1A3F);
}
static void TC_rt_sscanf_hex_2(void)
{
const char str[] = "0x1A 0XFF";
int a, b;
rt_sscanf(str, "%x %x", &a, &b);
uassert_true(a == 0x1A && b == 0XFF);
}
static void TC_rt_sscanf_oct_1(void)
{
const char str[] = "0755";
int value;
int result = rt_sscanf(str, "%o", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 0755);
}
static void TC_rt_sscanf_oct_2(void)
{
const char str[] = "012 077";
int a, b;
rt_sscanf(str, "%o %o", &a, &b);
uassert_true(a == 012 && b == 077);
}
static void TC_rt_sscanf_multiple_args(void)
{
const char str[] = "123 Hello";
int int_value;
char str_value[20];
int result = rt_sscanf(str, "%d %s", &int_value, str_value);
uassert_int_equal(result, 2);
uassert_int_equal(int_value, 123);
uassert_str_equal(str_value, "Hello");
}
static void TC_rt_sscanf_pointer(void)
{
const char str[] = "0x12345678";
void *ptr;
int result = rt_sscanf(str, "%p", &ptr);
uassert_int_equal(result, 1);
uassert_ptr_equal(ptr, (void *)0x12345678);
}
static void TC_rt_sscanf_width_specifier(void)
{
const char str[] = "123456789";
int value;
int result = rt_sscanf(str, "%4d", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 1234);
}
static void TC_rt_sscanf_suppression(void)
{
const char str[] = "123 456";
int second_value;
int result = rt_sscanf(str, "%*d %d", &second_value);
uassert_int_equal(result, 1);
uassert_int_equal(second_value, 456);
}
static void TC_rt_sscanf_match_set(void)
{
const char str[] = "abc123";
char buffer[10] = {0};
int result = rt_sscanf(str, "%[a-z]", buffer);
uassert_int_equal(result, 1);
uassert_str_equal(buffer, "abc");
}
static void TC_rt_sscanf_match_set_negated(void)
{
const char str[] = "abc123";
char buffer[10];
int result = rt_sscanf(str, "%[^0-9]", buffer);
uassert_int_equal(result, 1);
uassert_str_equal(buffer, "abc");
}
static void TC_rt_sscanf_match_set_range(void)
{
const char str[] = "a-zA-Z";
char buffer[10];
int result = rt_sscanf(str, "%[a-z-A-Z]", buffer);
uassert_int_equal(result, 1);
uassert_str_equal(buffer, "a-zA-Z");
}
static void TC_rt_sscanf_whitespace_skip(void)
{
const char str[] = " 12345";
int value;
int result = rt_sscanf(str, "%d", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 12345);
}
static void TC_rt_sscanf_unsigned_int(void)
{
const char str[] = "4294967295";
unsigned int value;
int result = rt_sscanf(str, "%u", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 4294967295U);
}
static void TC_rt_sscanf_long_long_int(void)
{
const char str[] = "9223372036854775807";
long long value;
int result = rt_sscanf(str, "%lld", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 9223372036854775807LL);
}
static void TC_rt_sscanf_short_int(void)
{
const char str[] = "32767";
short value;
int result = rt_sscanf(str, "%hd", &value);
uassert_int_equal(result, 1);
uassert_int_equal(value, 32767);
}
static void TC_rt_sscanf_null_string(void)
{
const char str[] = "";
int value;
int result = rt_sscanf(str, "%d", &value);
uassert_int_equal(result, -1);
}
/* https://github.com/RT-Thread/rt-thread/issues/9853 */
static void TC_rt_sscanf_issue_9853(void)
{
int device_socket = 255;
int bfsz = 255;
const char str[] = "+MIPURC: \"rtcp\",0,240,HTTP/1.1 200 OK";
rt_sscanf(str, "+MIPURC:%*[^,],%d,%d", &device_socket, (int *)&bfsz);
uassert_int_equal(device_socket, 0);
uassert_int_equal(bfsz, 240);
}
static void utest_do_tc(void)
{
UTEST_UNIT_RUN(TC_rt_sscanf_char);
UTEST_UNIT_RUN(TC_rt_sscanf_basic_int);
UTEST_UNIT_RUN(TC_rt_sscanf_basic_float);
UTEST_UNIT_RUN(TC_rt_sscanf_basic_string);
UTEST_UNIT_RUN(TC_rt_sscanf_string_with_space);
UTEST_UNIT_RUN(TC_rt_sscanf_basic_char);
UTEST_UNIT_RUN(TC_rt_sscanf_hex_1);
UTEST_UNIT_RUN(TC_rt_sscanf_hex_2);
UTEST_UNIT_RUN(TC_rt_sscanf_oct_1);
UTEST_UNIT_RUN(TC_rt_sscanf_oct_2);
UTEST_UNIT_RUN(TC_rt_sscanf_multiple_args);
UTEST_UNIT_RUN(TC_rt_sscanf_pointer);
UTEST_UNIT_RUN(TC_rt_sscanf_width_specifier);
UTEST_UNIT_RUN(TC_rt_sscanf_suppression);
UTEST_UNIT_RUN(TC_rt_sscanf_match_set);
UTEST_UNIT_RUN(TC_rt_sscanf_match_set_negated);
UTEST_UNIT_RUN(TC_rt_sscanf_match_set_range);
UTEST_UNIT_RUN(TC_rt_sscanf_whitespace_skip);
UTEST_UNIT_RUN(TC_rt_sscanf_unsigned_int);
UTEST_UNIT_RUN(TC_rt_sscanf_long_long_int);
UTEST_UNIT_RUN(TC_rt_sscanf_short_int);
UTEST_UNIT_RUN(TC_rt_sscanf_null_string);
UTEST_UNIT_RUN(TC_rt_sscanf_issue_9853);
}
UTEST_TC_EXPORT(utest_do_tc, "klibc.rt_sscanf", RT_NULL, RT_NULL, 1000);
File diff suppressed because it is too large Load Diff
+667
View File
@@ -0,0 +1,667 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2008-7-12 Bernard the first version
* 2010-06-09 Bernard fix the end stub of heap
* fix memory check in rt_realloc function
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-14 Bernard fix rt_realloc issue when realloc a NULL pointer.
* 2017-07-14 armink fix rt_realloc issue when new size is 0
* 2018-10-02 Bernard Add 64bit support
*/
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
* Simon Goldschmidt
*
*/
#include <rthw.h>
#include <rtthread.h>
#if defined (RT_USING_SMALL_MEM)
#define DBG_TAG "kernel.mem"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
struct rt_small_mem_item
{
rt_uintptr_t pool_ptr; /**< small memory object addr */
rt_size_t next; /**< next free item */
rt_size_t prev; /**< prev free item */
#ifdef RT_USING_MEMTRACE
#ifdef ARCH_CPU_64BIT
rt_uint8_t thread[8]; /**< thread name */
#else
rt_uint8_t thread[4]; /**< thread name */
#endif /* ARCH_CPU_64BIT */
#endif /* RT_USING_MEMTRACE */
};
/**
* Base structure of small memory object
*/
struct rt_small_mem
{
struct rt_memory parent; /**< inherit from rt_memory */
rt_uint8_t *heap_ptr; /**< pointer to the heap */
struct rt_small_mem_item *heap_end;
struct rt_small_mem_item *lfree;
rt_size_t mem_size_aligned; /**< aligned memory size */
};
#define MIN_SIZE (sizeof(rt_uintptr_t) + sizeof(rt_size_t) + sizeof(rt_size_t))
#define MEM_MASK ((~(rt_size_t)0) - 1)
#define MEM_USED(_mem) ((((rt_uintptr_t)(_mem)) & MEM_MASK) | 0x1)
#define MEM_FREED(_mem) ((((rt_uintptr_t)(_mem)) & MEM_MASK) | 0x0)
#define MEM_ISUSED(_mem) \
(((rt_uintptr_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (~MEM_MASK))
#define MEM_POOL(_mem) \
((struct rt_small_mem *)(((rt_uintptr_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (MEM_MASK)))
#define MEM_SIZE(_heap, _mem) \
(((struct rt_small_mem_item *)(_mem))->next - ((rt_uintptr_t)(_mem) - \
(rt_uintptr_t)((_heap)->heap_ptr)) - RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE))
#define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE)
#define SIZEOF_STRUCT_MEM RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE)
#ifdef RT_USING_MEMTRACE
rt_inline void rt_smem_setname(struct rt_small_mem_item *mem, const char *name)
{
int index;
for (index = 0; index < sizeof(mem->thread); index ++)
{
if (name[index] == '\0') break;
mem->thread[index] = name[index];
}
for (; index < sizeof(mem->thread); index ++)
{
mem->thread[index] = ' ';
}
}
#endif /* RT_USING_MEMTRACE */
static void plug_holes(struct rt_small_mem *m, struct rt_small_mem_item *mem)
{
struct rt_small_mem_item *nmem;
struct rt_small_mem_item *pmem;
RT_ASSERT((rt_uint8_t *)mem >= m->heap_ptr);
RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)m->heap_end);
/* plug hole forward */
nmem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next];
if (mem != nmem && !MEM_ISUSED(nmem) &&
(rt_uint8_t *)nmem != (rt_uint8_t *)m->heap_end)
{
/* if mem->next is unused and not end of m->heap_ptr,
* combine mem and mem->next
*/
if (m->lfree == nmem)
{
m->lfree = mem;
}
nmem->pool_ptr = 0;
mem->next = nmem->next;
((struct rt_small_mem_item *)&m->heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - m->heap_ptr;
}
/* plug hole backward */
pmem = (struct rt_small_mem_item *)&m->heap_ptr[mem->prev];
if (pmem != mem && !MEM_ISUSED(pmem))
{
/* if mem->prev is unused, combine mem and mem->prev */
if (m->lfree == mem)
{
m->lfree = pmem;
}
mem->pool_ptr = 0;
pmem->next = mem->next;
((struct rt_small_mem_item *)&m->heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - m->heap_ptr;
}
}
/**
* @brief This function will initialize small memory management algorithm.
*
* @param name is the name of the small memory management object.
*
* @param begin_addr the beginning address of memory.
*
* @param size is the size of the memory.
*
* @return Return a pointer to the memory object. When the return value is RT_NULL, it means the init failed.
*/
rt_smem_t rt_smem_init(const char *name,
void *begin_addr,
rt_size_t size)
{
struct rt_small_mem_item *mem;
struct rt_small_mem *small_mem;
rt_uintptr_t start_addr, begin_align, end_align, mem_size;
small_mem = (struct rt_small_mem *)RT_ALIGN((rt_uintptr_t)begin_addr, RT_ALIGN_SIZE);
start_addr = (rt_uintptr_t)small_mem + sizeof(*small_mem);
begin_align = RT_ALIGN((rt_uintptr_t)start_addr, RT_ALIGN_SIZE);
end_align = RT_ALIGN_DOWN((rt_uintptr_t)begin_addr + size, RT_ALIGN_SIZE);
/* alignment addr */
if ((end_align > (2 * SIZEOF_STRUCT_MEM)) &&
((end_align - 2 * SIZEOF_STRUCT_MEM) >= start_addr))
{
/* calculate the aligned memory size */
mem_size = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM;
}
else
{
rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n",
(rt_uintptr_t)begin_addr, (rt_uintptr_t)begin_addr + size);
return RT_NULL;
}
rt_memset(small_mem, 0, sizeof(*small_mem));
/* initialize small memory object */
rt_object_init(&(small_mem->parent.parent), RT_Object_Class_Memory, name);
small_mem->parent.algorithm = "small";
small_mem->parent.address = begin_align;
small_mem->parent.total = mem_size;
small_mem->mem_size_aligned = mem_size;
/* point to begin address of heap */
small_mem->heap_ptr = (rt_uint8_t *)begin_align;
LOG_D("mem init, heap begin address 0x%x, size %d",
(rt_uintptr_t)small_mem->heap_ptr, small_mem->mem_size_aligned);
/* initialize the start of the heap */
mem = (struct rt_small_mem_item *)small_mem->heap_ptr;
mem->pool_ptr = MEM_FREED(small_mem);
mem->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM;
mem->prev = 0;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem, "INIT");
#endif /* RT_USING_MEMTRACE */
/* initialize the end of the heap */
small_mem->heap_end = (struct rt_small_mem_item *)&small_mem->heap_ptr[mem->next];
small_mem->heap_end->pool_ptr = MEM_USED(small_mem);
small_mem->heap_end->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM;
small_mem->heap_end->prev = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(small_mem->heap_end, "INIT");
#endif /* RT_USING_MEMTRACE */
/* initialize the lowest-free pointer to the start of the heap */
small_mem->lfree = (struct rt_small_mem_item *)small_mem->heap_ptr;
return &small_mem->parent;
}
RTM_EXPORT(rt_smem_init);
/**
* @brief This function will remove a small mem from the system.
*
* @param m the small memory management object.
*
* @return RT_EOK
*/
rt_err_t rt_smem_detach(rt_smem_t m)
{
RT_ASSERT(m != RT_NULL);
RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&m->parent));
rt_object_detach(&(m->parent));
return RT_EOK;
}
RTM_EXPORT(rt_smem_detach);
/**
* @addtogroup group_memory_management
*/
/**@{*/
/**
* @brief Allocate a block of memory with a minimum of 'size' bytes.
*
* @param m the small memory management object.
*
* @param size is the minimum size of the requested block in bytes.
*
* @return the pointer to allocated memory or NULL if no free memory was found.
*/
void *rt_smem_alloc(rt_smem_t m, rt_size_t size)
{
rt_size_t ptr, ptr2;
struct rt_small_mem_item *mem, *mem2;
struct rt_small_mem *small_mem;
if (size == 0)
return RT_NULL;
RT_ASSERT(m != RT_NULL);
RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&m->parent));
small_mem = (struct rt_small_mem *)m;
/* alignment size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
/* every data block must be at least MIN_SIZE_ALIGNED long */
if (size < MIN_SIZE_ALIGNED)
size = MIN_SIZE_ALIGNED;
if (size > small_mem->mem_size_aligned)
{
LOG_D("no memory");
return RT_NULL;
}
for (ptr = (rt_uint8_t *)small_mem->lfree - small_mem->heap_ptr;
ptr <= small_mem->mem_size_aligned - size;
ptr = ((struct rt_small_mem_item *)&small_mem->heap_ptr[ptr])->next)
{
mem = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr];
if ((!MEM_ISUSED(mem)) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size)
{
/* mem is not used and at least perfect fit is possible:
* mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >=
(size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED))
{
/* (in addition to the above, we test if another struct rt_small_mem_item (SIZEOF_STRUCT_MEM) containing
* at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
* -> split large block, create empty remainder,
* remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
* mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
* struct rt_small_mem_item would fit in but no data between mem2 and mem2->next
* @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
* region that couldn't hold data, but when mem->next gets freed,
* the 2 regions would be combined, resulting in more free memory
*/
ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
/* create mem2 struct */
mem2 = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr2];
mem2->pool_ptr = MEM_FREED(small_mem);
mem2->next = mem->next;
mem2->prev = ptr;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem2, " ");
#endif /* RT_USING_MEMTRACE */
/* and insert it between mem and mem->next */
mem->next = ptr2;
if (mem2->next != small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM)
{
((struct rt_small_mem_item *)&small_mem->heap_ptr[mem2->next])->prev = ptr2;
}
small_mem->parent.used += (size + SIZEOF_STRUCT_MEM);
if (small_mem->parent.max < small_mem->parent.used)
small_mem->parent.max = small_mem->parent.used;
}
else
{
/* (a mem2 struct does no fit into the user data space of mem and mem->next will always
* be used at this point: if not we have 2 unused structs in a row, plug_holes should have
* take care of this).
* -> near fit or excact fit: do not split, no mem2 creation
* also can't move mem->next directly behind mem, since mem->next
* will always be used at this point!
*/
small_mem->parent.used += mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr);
if (small_mem->parent.max < small_mem->parent.used)
small_mem->parent.max = small_mem->parent.used;
}
/* set small memory object */
mem->pool_ptr = MEM_USED(small_mem);
#ifdef RT_USING_MEMTRACE
if (rt_thread_self())
rt_smem_setname(mem, rt_thread_self()->parent.name);
else
rt_smem_setname(mem, "NONE");
#endif /* RT_USING_MEMTRACE */
if (mem == small_mem->lfree)
{
/* Find next free block after mem and update lowest free pointer */
while (MEM_ISUSED(small_mem->lfree) && small_mem->lfree != small_mem->heap_end)
small_mem->lfree = (struct rt_small_mem_item *)&small_mem->heap_ptr[small_mem->lfree->next];
RT_ASSERT(((small_mem->lfree == small_mem->heap_end) || (!MEM_ISUSED(small_mem->lfree))));
}
RT_ASSERT((rt_uintptr_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_uintptr_t)small_mem->heap_end);
RT_ASSERT((rt_uintptr_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0);
RT_ASSERT((((rt_uintptr_t)mem) & (RT_ALIGN_SIZE - 1)) == 0);
LOG_D("allocate memory at 0x%x, size: %d",
(rt_uintptr_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM),
(rt_uintptr_t)(mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr)));
/* return the memory data except mem struct */
return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM;
}
}
return RT_NULL;
}
RTM_EXPORT(rt_smem_alloc);
/**
* @brief This function will change the size of previously allocated memory block.
*
* @param m the small memory management object.
*
* @param rmem is the pointer to memory allocated by rt_mem_alloc.
*
* @param newsize is the required new size.
*
* @return the changed memory block address.
*/
void *rt_smem_realloc(rt_smem_t m, void *rmem, rt_size_t newsize)
{
rt_size_t size;
rt_size_t ptr, ptr2;
struct rt_small_mem_item *mem, *mem2;
struct rt_small_mem *small_mem;
void *nmem;
RT_ASSERT(m != RT_NULL);
RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&m->parent));
small_mem = (struct rt_small_mem *)m;
/* alignment size */
newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
if (newsize > small_mem->mem_size_aligned)
{
LOG_D("realloc: out of memory");
return RT_NULL;
}
else if (newsize == 0)
{
rt_smem_free(rmem);
return RT_NULL;
}
/* allocate a new memory block */
if (rmem == RT_NULL)
return rt_smem_alloc(&small_mem->parent, newsize);
RT_ASSERT((((rt_uintptr_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0);
RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)small_mem->heap_ptr);
RT_ASSERT((rt_uint8_t *)rmem < (rt_uint8_t *)small_mem->heap_end);
mem = (struct rt_small_mem_item *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);
/* current memory block size */
ptr = (rt_uint8_t *)mem - small_mem->heap_ptr;
size = mem->next - ptr - SIZEOF_STRUCT_MEM;
if (size == newsize)
{
/* the size is the same as */
return rmem;
}
if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size)
{
/* split memory block */
small_mem->parent.used -= (size - newsize);
ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
mem2 = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr2];
mem2->pool_ptr = MEM_FREED(small_mem);
mem2->next = mem->next;
mem2->prev = ptr;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem2, " ");
#endif /* RT_USING_MEMTRACE */
mem->next = ptr2;
if (mem2->next != small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM)
{
((struct rt_small_mem_item *)&small_mem->heap_ptr[mem2->next])->prev = ptr2;
}
if (mem2 < small_mem->lfree)
{
/* the splited struct is now the lowest */
small_mem->lfree = mem2;
}
plug_holes(small_mem, mem2);
return rmem;
}
/* expand memory */
nmem = rt_smem_alloc(&small_mem->parent, newsize);
if (nmem != RT_NULL) /* check memory */
{
rt_memcpy(nmem, rmem, size < newsize ? size : newsize);
rt_smem_free(rmem);
}
return nmem;
}
RTM_EXPORT(rt_smem_realloc);
/**
* @brief This function will release the previously allocated memory block by
* rt_mem_alloc. The released memory block is taken back to system heap.
*
* @param rmem the address of memory which will be released.
*/
void rt_smem_free(void *rmem)
{
struct rt_small_mem_item *mem;
struct rt_small_mem *small_mem;
if (rmem == RT_NULL)
return;
RT_ASSERT((((rt_uintptr_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0);
/* Get the corresponding struct rt_small_mem_item ... */
mem = (struct rt_small_mem_item *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);
/* ... which has to be in a used state ... */
small_mem = MEM_POOL(mem);
RT_ASSERT(small_mem != RT_NULL);
RT_ASSERT(MEM_ISUSED(mem));
RT_ASSERT(rt_object_get_type(&small_mem->parent.parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&small_mem->parent.parent));
RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)small_mem->heap_ptr &&
(rt_uint8_t *)rmem < (rt_uint8_t *)small_mem->heap_end);
RT_ASSERT(MEM_POOL(&small_mem->heap_ptr[mem->next]) == small_mem);
LOG_D("release memory 0x%x, size: %d",
(rt_uintptr_t)rmem,
(rt_uintptr_t)(mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr)));
/* ... and is now unused. */
mem->pool_ptr = MEM_FREED(small_mem);
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem, " ");
#endif /* RT_USING_MEMTRACE */
if (mem < small_mem->lfree)
{
/* the newly freed struct is now the lowest */
small_mem->lfree = mem;
}
small_mem->parent.used -= (mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr));
/* finally, see if prev or next are free also */
plug_holes(small_mem, mem);
}
RTM_EXPORT(rt_smem_free);
#ifdef RT_USING_FINSH
#include <finsh.h>
#ifdef RT_USING_MEMTRACE
static int memcheck(int argc, char *argv[])
{
int position;
rt_base_t level;
struct rt_small_mem_item *mem;
struct rt_small_mem *m;
struct rt_object_information *information;
struct rt_list_node *node;
struct rt_object *object;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
level = rt_hw_interrupt_disable();
/* get mem object */
information = rt_object_get_information(RT_Object_Class_Memory);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, object->name, RT_NAME_MAX) != 0)
{
continue;
}
/* mem object */
m = (struct rt_small_mem *)object;
if(rt_strncmp(m->parent.algorithm, "small", RT_NAME_MAX) != 0)
{
continue;
}
/* check mem */
for (mem = (struct rt_small_mem_item *)m->heap_ptr; mem != m->heap_end; mem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next])
{
position = (rt_uintptr_t)mem - (rt_uintptr_t)m->heap_ptr;
if (position < 0) goto __exit;
if (position > (int)m->mem_size_aligned) goto __exit;
if (MEM_POOL(mem) != m) goto __exit;
}
}
rt_hw_interrupt_enable(level);
return 0;
__exit:
rt_kprintf("Memory block wrong:\n");
rt_kprintf(" name: %s\n", m->parent.parent.name);
rt_kprintf("address: 0x%08x\n", mem);
rt_kprintf(" pool: 0x%04x\n", mem->pool_ptr);
rt_kprintf(" size: %d\n", mem->next - position - SIZEOF_STRUCT_MEM);
rt_hw_interrupt_enable(level);
return 0;
}
MSH_CMD_EXPORT(memcheck, check memory data);
static int memtrace(int argc, char **argv)
{
struct rt_small_mem_item *mem;
struct rt_small_mem *m;
struct rt_object_information *information;
struct rt_list_node *node;
struct rt_object *object;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
/* get mem object */
information = rt_object_get_information(RT_Object_Class_Memory);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, object->name, RT_NAME_MAX) != 0)
{
continue;
}
/* mem object */
m = (struct rt_small_mem *)object;
if(rt_strncmp(m->parent.algorithm, "small", RT_NAME_MAX) != 0)
{
continue;
}
/* show memory information */
rt_kprintf("\nmemory heap address:\n");
rt_kprintf("name : %s\n", m->parent.parent.name);
rt_kprintf("total : %d\n", m->parent.total);
rt_kprintf("used : %d\n", m->parent.used);
rt_kprintf("max_used: %d\n", m->parent.max);
rt_kprintf("heap_ptr: 0x%08x\n", m->heap_ptr);
rt_kprintf("lfree : 0x%08x\n", m->lfree);
rt_kprintf("heap_end: 0x%08x\n", m->heap_end);
rt_kprintf("\n--memory item information --\n");
for (mem = (struct rt_small_mem_item *)m->heap_ptr; mem != m->heap_end; mem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next])
{
int size = MEM_SIZE(m, mem);
rt_kprintf("[0x%08x - ", mem);
if (size < 1024)
rt_kprintf("%5d", size);
else if (size < 1024 * 1024)
rt_kprintf("%4dK", size / 1024);
else
rt_kprintf("%4dM", size / (1024 * 1024));
rt_kprintf("] %c%c%c%c", mem->thread[0], mem->thread[1], mem->thread[2], mem->thread[3]);
if (MEM_POOL(mem) != m)
rt_kprintf(": ***\n");
else
rt_kprintf("\n");
}
}
return 0;
}
MSH_CMD_EXPORT(memtrace, dump memory trace information);
#endif /* RT_USING_MEMTRACE */
#endif /* RT_USING_FINSH */
#endif /* defined (RT_USING_SMALL_MEM) */
/**@}*/
+998
View File
@@ -0,0 +1,998 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* File : memheap.c
*
* Change Logs:
* Date Author Notes
* 2012-04-10 Bernard first implementation
* 2012-10-16 Bernard add the mutex lock for heap object.
* 2012-12-29 Bernard memheap can be used as system heap.
* change mutex lock to semaphore lock.
* 2013-04-10 Bernard add rt_memheap_realloc function.
* 2013-05-24 Bernard fix the rt_memheap_realloc issue.
* 2013-07-11 Grissiom fix the memory block splitting issue.
* 2013-07-15 Grissiom optimize rt_memheap_realloc
* 2021-06-03 Flybreak Fix the crash problem after opening Oz optimization on ac6.
* 2023-03-01 Bernard Fix the alignment issue for minimal size
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MEMHEAP
#define DBG_TAG "kernel.memheap"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/* dynamic pool magic and mask */
#define RT_MEMHEAP_MAGIC 0x1ea01ea0
#define RT_MEMHEAP_MASK 0xFFFFFFFE
#define RT_MEMHEAP_USED 0x01
#define RT_MEMHEAP_FREED 0x00
#define RT_MEMHEAP_IS_USED(i) ((i)->magic & RT_MEMHEAP_USED)
#define RT_MEMHEAP_MINIALLOC RT_ALIGN(12, RT_ALIGN_SIZE)
#define RT_MEMHEAP_SIZE RT_ALIGN(sizeof(struct rt_memheap_item), RT_ALIGN_SIZE)
#define MEMITEM_SIZE(item) ((rt_uintptr_t)item->next - (rt_uintptr_t)item - RT_MEMHEAP_SIZE)
#define MEMITEM(ptr) (struct rt_memheap_item*)((rt_uint8_t*)ptr - RT_MEMHEAP_SIZE)
static void _remove_next_ptr(volatile struct rt_memheap_item *next_ptr)
{
/* Fix the crash problem after opening Oz optimization on ac6 */
/* Fix IAR compiler warning */
next_ptr->next_free->prev_free = next_ptr->prev_free;
next_ptr->prev_free->next_free = next_ptr->next_free;
next_ptr->next->prev = next_ptr->prev;
next_ptr->prev->next = next_ptr->next;
}
/**
* @brief This function initializes a piece of memory called memheap.
*
* @note The initialized memory pool will be:
* +-----------------------------------+--------------------------+
* | whole freed memory block | Used Memory Block Tailer |
* +-----------------------------------+--------------------------+
*
* block_list --> whole freed memory block
*
* The length of Used Memory Block Tailer is 0,
* which is prevents block merging across list
*
* @param memheap is a pointer of the memheap object.
*
* @param name is the name of the memheap.
*
* @param start_addr is the start address of the memheap.
*
* @param size is the size of the memheap.
*
* @return RT_EOK
*/
rt_err_t rt_memheap_init(struct rt_memheap *memheap,
const char *name,
void *start_addr,
rt_size_t size)
{
struct rt_memheap_item *item;
RT_ASSERT(memheap != RT_NULL);
/* initialize pool object */
rt_object_init(&(memheap->parent), RT_Object_Class_MemHeap, name);
memheap->start_addr = start_addr;
memheap->pool_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE);
memheap->max_used_size = memheap->pool_size - memheap->available_size;
/* initialize the free list header */
item = &(memheap->free_header);
item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
item->pool_ptr = memheap;
item->next = RT_NULL;
item->prev = RT_NULL;
item->next_free = item;
item->prev_free = item;
/* set the free list to free list header */
memheap->free_list = item;
/* initialize the first big memory block */
item = (struct rt_memheap_item *)start_addr;
item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
item->pool_ptr = memheap;
item->next = RT_NULL;
item->prev = RT_NULL;
item->next_free = item;
item->prev_free = item;
#ifdef RT_USING_MEMTRACE
rt_memset(item->owner_thread_name, ' ', sizeof(item->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
item->next = (struct rt_memheap_item *)
((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE);
item->prev = item->next;
/* block list header */
memheap->block_list = item;
/* place the big memory block to free list */
item->next_free = memheap->free_list->next_free;
item->prev_free = memheap->free_list;
memheap->free_list->next_free->prev_free = item;
memheap->free_list->next_free = item;
/* move to the end of memory pool to build a small tailer block,
* which prevents block merging
*/
item = item->next;
/* it's a used memory block */
item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED);
item->pool_ptr = memheap;
item->next = (struct rt_memheap_item *)start_addr;
item->prev = (struct rt_memheap_item *)start_addr;
/* not in free list */
item->next_free = item->prev_free = RT_NULL;
/* initialize semaphore lock */
rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_PRIO);
memheap->locked = RT_FALSE;
LOG_D("memory heap: start addr 0x%08x, size %d, free list header 0x%08x",
start_addr, size, &(memheap->free_header));
return RT_EOK;
}
RTM_EXPORT(rt_memheap_init);
/**
* @brief This function will remove a memheap from the system.
*
* @param heap is a pointer of memheap object.
*
* @return RT_EOK
*/
rt_err_t rt_memheap_detach(struct rt_memheap *heap)
{
RT_ASSERT(heap);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
RT_ASSERT(rt_object_is_systemobject(&heap->parent));
rt_sem_detach(&heap->lock);
rt_object_detach(&(heap->parent));
/* Return a successful completion. */
return RT_EOK;
}
RTM_EXPORT(rt_memheap_detach);
/**
* @brief Allocate a block of memory with a minimum of 'size' bytes on memheap.
*
* @param heap is a pointer for memheap object.
*
* @param size is the minimum size of the requested block in bytes.
*
* @return the pointer to allocated memory or NULL if no free memory was found.
*/
void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size)
{
rt_err_t result;
rt_size_t free_size;
struct rt_memheap_item *header_ptr;
RT_ASSERT(heap != RT_NULL);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
/* align allocated size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
if (size < RT_MEMHEAP_MINIALLOC)
size = RT_MEMHEAP_MINIALLOC;
LOG_D("allocate %d on heap:%8.*s",
size, RT_NAME_MAX, heap->parent.name);
if (size < heap->available_size)
{
/* search on free list */
free_size = 0;
/* lock memheap */
if (heap->locked == RT_FALSE)
{
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
}
/* get the first free memory block */
header_ptr = heap->free_list->next_free;
while (header_ptr != heap->free_list && free_size < size)
{
/* get current freed memory block size */
free_size = MEMITEM_SIZE(header_ptr);
if (free_size < size)
{
/* move to next free memory block */
header_ptr = header_ptr->next_free;
}
}
/* determine if the memory is available. */
if (free_size >= size)
{
/* a block that satisfies the request has been found. */
/* determine if the block needs to be split. */
if (free_size >= (size + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC))
{
struct rt_memheap_item *new_ptr;
/* split the block. */
new_ptr = (struct rt_memheap_item *)
(((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE);
LOG_D("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]",
header_ptr,
header_ptr->next,
header_ptr->prev,
new_ptr);
/* mark the new block as a memory block and freed. */
new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* put the pool pointer into the new block. */
new_ptr->pool_ptr = heap;
#ifdef RT_USING_MEMTRACE
rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
/* break down the block list */
new_ptr->prev = header_ptr;
new_ptr->next = header_ptr->next;
header_ptr->next->prev = new_ptr;
header_ptr->next = new_ptr;
/* remove header ptr from free list */
header_ptr->next_free->prev_free = header_ptr->prev_free;
header_ptr->prev_free->next_free = header_ptr->next_free;
header_ptr->next_free = RT_NULL;
header_ptr->prev_free = RT_NULL;
/* insert new_ptr to free list */
new_ptr->next_free = heap->free_list->next_free;
new_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = new_ptr;
heap->free_list->next_free = new_ptr;
LOG_D("new ptr: next_free 0x%08x, prev_free 0x%08x",
new_ptr->next_free,
new_ptr->prev_free);
/* decrement the available byte count. */
heap->available_size = heap->available_size -
size -
RT_MEMHEAP_SIZE;
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
}
else
{
/* decrement the entire free size from the available bytes count. */
heap->available_size = heap->available_size - free_size;
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
/* remove header_ptr from free list */
LOG_D("one block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x",
header_ptr,
header_ptr->next_free,
header_ptr->prev_free);
header_ptr->next_free->prev_free = header_ptr->prev_free;
header_ptr->prev_free->next_free = header_ptr->next_free;
header_ptr->next_free = RT_NULL;
header_ptr->prev_free = RT_NULL;
}
/* Mark the allocated block as not available. */
header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED);
#ifdef RT_USING_MEMTRACE
if (rt_thread_self())
rt_memcpy(header_ptr->owner_thread_name, rt_thread_self()->parent.name, sizeof(header_ptr->owner_thread_name));
else
rt_memcpy(header_ptr->owner_thread_name, "NONE", sizeof(header_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
/* Return a memory address to the caller. */
LOG_D("alloc mem: memory[0x%08x], heap[0x%08x], size: %d",
(void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE),
header_ptr,
size);
return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE);
}
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
}
LOG_D("allocate memory: failed");
/* Return the completion status. */
return RT_NULL;
}
RTM_EXPORT(rt_memheap_alloc);
/**
* @brief This function will change the size of previously allocated memory block.
*
* @param heap is a pointer to the memheap object, which will reallocate
* memory from the block
*
* @param ptr is a pointer to start address of memory.
*
* @param newsize is the required new size.
*
* @return the changed memory block address.
*/
void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize)
{
rt_err_t result;
rt_size_t oldsize;
struct rt_memheap_item *header_ptr;
struct rt_memheap_item *new_ptr;
RT_ASSERT(heap);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
if (newsize == 0)
{
rt_memheap_free(ptr);
return RT_NULL;
}
/* align allocated size */
newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
if (newsize < RT_MEMHEAP_MINIALLOC)
newsize = RT_MEMHEAP_MINIALLOC;
if (ptr == RT_NULL)
{
return rt_memheap_alloc(heap, newsize);
}
/* get memory block header and get the size of memory block */
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);
oldsize = MEMITEM_SIZE(header_ptr);
/* re-allocate memory */
if (newsize > oldsize)
{
void *new_ptr;
volatile struct rt_memheap_item *next_ptr;
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
}
next_ptr = header_ptr->next;
/* header_ptr should not be the tail */
RT_ASSERT(next_ptr > header_ptr);
/* check whether the following free space is enough to expand */
if (!RT_MEMHEAP_IS_USED(next_ptr))
{
rt_int32_t nextsize;
nextsize = MEMITEM_SIZE(next_ptr);
RT_ASSERT(next_ptr > 0);
/* Here is the ASCII art of the situation that we can make use of
* the next free node without alloc/memcpy, |*| is the control
* block:
*
* oldsize free node
* |*|-----------|*|----------------------|*|
* newsize >= minialloc
* |*|----------------|*|-----------------|*|
*/
if (nextsize + oldsize > newsize + RT_MEMHEAP_MINIALLOC)
{
/* decrement the entire free size from the available bytes count. */
heap->available_size = heap->available_size - (newsize - oldsize);
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
/* remove next_ptr from free list */
LOG_D("remove block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x",
next_ptr,
next_ptr->next_free,
next_ptr->prev_free);
_remove_next_ptr(next_ptr);
/* build a new one on the right place */
next_ptr = (struct rt_memheap_item *)((char *)ptr + newsize);
LOG_D("new free block: block[0x%08x] nextm[0x%08x] prevm[0x%08x]",
next_ptr,
next_ptr->next,
next_ptr->prev);
/* mark the new block as a memory block and freed. */
next_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* put the pool pointer into the new block. */
next_ptr->pool_ptr = heap;
#ifdef RT_USING_MEMTRACE
rt_memset((void *)next_ptr->owner_thread_name, ' ', sizeof(next_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
next_ptr->prev = header_ptr;
next_ptr->next = header_ptr->next;
header_ptr->next->prev = (struct rt_memheap_item *)next_ptr;
header_ptr->next = (struct rt_memheap_item *)next_ptr;
/* insert next_ptr to free list */
next_ptr->next_free = heap->free_list->next_free;
next_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = (struct rt_memheap_item *)next_ptr;
heap->free_list->next_free = (struct rt_memheap_item *)next_ptr;
LOG_D("new ptr: next_free 0x%08x, prev_free 0x%08x",
next_ptr->next_free,
next_ptr->prev_free);
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
return ptr;
}
}
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
/* re-allocate a memory block */
new_ptr = (void *)rt_memheap_alloc(heap, newsize);
if (new_ptr != RT_NULL)
{
rt_memcpy(new_ptr, ptr, oldsize < newsize ? oldsize : newsize);
rt_memheap_free(ptr);
}
return new_ptr;
}
/* don't split when there is less than one node space left */
if (newsize + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC >= oldsize)
return ptr;
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
}
/* split the block. */
new_ptr = (struct rt_memheap_item *)
(((rt_uint8_t *)header_ptr) + newsize + RT_MEMHEAP_SIZE);
LOG_D("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]",
header_ptr,
header_ptr->next,
header_ptr->prev,
new_ptr);
/* mark the new block as a memory block and freed. */
new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* put the pool pointer into the new block. */
new_ptr->pool_ptr = heap;
#ifdef RT_USING_MEMTRACE
rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
/* break down the block list */
new_ptr->prev = header_ptr;
new_ptr->next = header_ptr->next;
header_ptr->next->prev = new_ptr;
header_ptr->next = new_ptr;
/* determine if the block can be merged with the next neighbor. */
if (!RT_MEMHEAP_IS_USED(new_ptr->next))
{
struct rt_memheap_item *free_ptr;
/* merge block with next neighbor. */
free_ptr = new_ptr->next;
heap->available_size = heap->available_size - MEMITEM_SIZE(free_ptr);
LOG_D("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x",
header_ptr, header_ptr->next_free, header_ptr->prev_free);
free_ptr->next->prev = new_ptr;
new_ptr->next = free_ptr->next;
/* remove free ptr from free list */
free_ptr->next_free->prev_free = free_ptr->prev_free;
free_ptr->prev_free->next_free = free_ptr->next_free;
}
/* insert the split block to free list */
new_ptr->next_free = heap->free_list->next_free;
new_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = new_ptr;
heap->free_list->next_free = new_ptr;
LOG_D("new free ptr: next_free 0x%08x, prev_free 0x%08x",
new_ptr->next_free,
new_ptr->prev_free);
/* increment the available byte count. */
heap->available_size = heap->available_size + MEMITEM_SIZE(new_ptr);
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
/* return the old memory block */
return ptr;
}
RTM_EXPORT(rt_memheap_realloc);
/**
* @brief This function will release the allocated memory block by
* rt_malloc. The released memory block is taken back to system heap.
*
* @param ptr the address of memory which will be released.
*/
void rt_memheap_free(void *ptr)
{
rt_err_t result;
struct rt_memheap *heap;
struct rt_memheap_item *header_ptr, *new_ptr;
rt_bool_t insert_header;
/* NULL check */
if (ptr == RT_NULL) return;
/* set initial status as OK */
insert_header = RT_TRUE;
new_ptr = RT_NULL;
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);
LOG_D("free memory: memory[0x%08x], block[0x%08x]",
ptr, header_ptr);
/* check magic */
if (header_ptr->magic != (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED) ||
(header_ptr->next->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC)
{
LOG_D("bad magic:0x%08x @ memheap",
header_ptr->magic);
RT_ASSERT(header_ptr->magic == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED));
/* check whether this block of memory has been over-written. */
RT_ASSERT((header_ptr->next->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC);
}
/* get pool ptr */
heap = header_ptr->pool_ptr;
RT_ASSERT(heap);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return ;
}
}
/* Mark the memory as available. */
header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* Adjust the available number of bytes. */
heap->available_size += MEMITEM_SIZE(header_ptr);
/* Determine if the block can be merged with the previous neighbor. */
if (!RT_MEMHEAP_IS_USED(header_ptr->prev))
{
LOG_D("merge: left node 0x%08x",
header_ptr->prev);
/* adjust the available number of bytes. */
heap->available_size += RT_MEMHEAP_SIZE;
/* yes, merge block with previous neighbor. */
(header_ptr->prev)->next = header_ptr->next;
(header_ptr->next)->prev = header_ptr->prev;
/* move header pointer to previous. */
header_ptr = header_ptr->prev;
/* don't insert header to free list */
insert_header = RT_FALSE;
}
/* determine if the block can be merged with the next neighbor. */
if (!RT_MEMHEAP_IS_USED(header_ptr->next))
{
/* adjust the available number of bytes. */
heap->available_size += RT_MEMHEAP_SIZE;
/* merge block with next neighbor. */
new_ptr = header_ptr->next;
LOG_D("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x",
new_ptr, new_ptr->next_free, new_ptr->prev_free);
new_ptr->next->prev = header_ptr;
header_ptr->next = new_ptr->next;
/* remove new ptr from free list */
new_ptr->next_free->prev_free = new_ptr->prev_free;
new_ptr->prev_free->next_free = new_ptr->next_free;
}
if (insert_header)
{
struct rt_memheap_item *n = heap->free_list->next_free;
#if defined(RT_MEMHEAP_BEST_MODE)
rt_size_t blk_size = MEMITEM_SIZE(header_ptr);
for (;n != heap->free_list; n = n->next_free)
{
rt_size_t m = MEMITEM_SIZE(n);
if (blk_size <= m)
{
break;
}
}
#endif
/* no left merge, insert to free list */
header_ptr->next_free = n;
header_ptr->prev_free = n->prev_free;
n->prev_free->next_free = header_ptr;
n->prev_free = header_ptr;
LOG_D("insert to free list: next_free 0x%08x, prev_free 0x%08x",
header_ptr->next_free, header_ptr->prev_free);
}
#ifdef RT_USING_MEMTRACE
rt_memset(header_ptr->owner_thread_name, ' ', sizeof(header_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
}
RTM_EXPORT(rt_memheap_free);
/**
* @brief This function will caculate the total memory, the used memory, and
* the max used memory.
*
* @param heap is a pointer to the memheap object, which will reallocate
* memory from the block
*
* @param total is a pointer to get the total size of the memory.
*
* @param used is a pointer to get the size of memory used.
*
* @param max_used is a pointer to get the maximum memory used.
*/
void rt_memheap_info(struct rt_memheap *heap,
rt_size_t *total,
rt_size_t *used,
rt_size_t *max_used)
{
rt_err_t result;
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return;
}
}
if (total != RT_NULL)
*total = heap->pool_size;
if (used != RT_NULL)
*used = heap->pool_size - heap->available_size;
if (max_used != RT_NULL)
*max_used = heap->max_used_size;
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
}
#ifdef RT_USING_MEMHEAP_AS_HEAP
/*
* rt_malloc port function
*/
void *_memheap_alloc(struct rt_memheap *heap, rt_size_t size)
{
void *ptr;
/* try to allocate in system heap */
ptr = rt_memheap_alloc(heap, size);
#ifdef RT_USING_MEMHEAP_AUTO_BINDING
if (ptr == RT_NULL)
{
struct rt_object *object;
struct rt_list_node *node;
struct rt_memheap *_heap;
struct rt_object_information *information;
/* try to allocate on other memory heap */
information = rt_object_get_information(RT_Object_Class_MemHeap);
RT_ASSERT(information != RT_NULL);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
_heap = (struct rt_memheap *)object;
/* not allocate in the default system heap */
if (heap == _heap)
continue;
ptr = rt_memheap_alloc(_heap, size);
if (ptr != RT_NULL)
break;
}
}
#endif /* RT_USING_MEMHEAP_AUTO_BINDING */
return ptr;
}
/*
* rt_free port function
*/
void _memheap_free(void *rmem)
{
rt_memheap_free(rmem);
}
/*
* rt_realloc port function
*/
void *_memheap_realloc(struct rt_memheap *heap, void *rmem, rt_size_t newsize)
{
void *new_ptr;
struct rt_memheap_item *header_ptr;
if (rmem == RT_NULL)
return _memheap_alloc(heap, newsize);
if (newsize == 0)
{
_memheap_free(rmem);
return RT_NULL;
}
/* get old memory item */
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)rmem - RT_MEMHEAP_SIZE);
new_ptr = rt_memheap_realloc(header_ptr->pool_ptr, rmem, newsize);
if (new_ptr == RT_NULL && newsize != 0)
{
/* allocate memory block from other memheap */
new_ptr = _memheap_alloc(heap, newsize);
if (new_ptr != RT_NULL && rmem != RT_NULL)
{
rt_size_t oldsize;
/* get the size of old memory block */
oldsize = MEMITEM_SIZE(header_ptr);
if (newsize > oldsize)
rt_memcpy(new_ptr, rmem, oldsize);
else
rt_memcpy(new_ptr, rmem, newsize);
_memheap_free(rmem);
}
}
return new_ptr;
}
#endif
#ifdef RT_USING_MEMTRACE
static int memheapcheck(int argc, char *argv[])
{
struct rt_object_information *info;
struct rt_list_node *list;
struct rt_memheap *heap;
struct rt_list_node *node;
struct rt_memheap_item *item;
rt_bool_t has_bad = RT_FALSE;
rt_base_t level;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
level = rt_hw_interrupt_disable();
info = rt_object_get_information(RT_Object_Class_MemHeap);
list = &info->object_list;
for (node = list->next; node != list; node = node->next)
{
heap = (struct rt_memheap *)rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, heap->parent.name, RT_NAME_MAX) != 0)
continue;
/* check memheap */
for (item = heap->block_list; item->next != heap->block_list; item = item->next)
{
/* check magic */
if (!((item->magic & (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED)) == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED) ||
(item->magic & (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)) == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)))
{
has_bad = RT_TRUE;
break;
}
/* check pool_ptr */
if (heap != item->pool_ptr)
{
has_bad = RT_TRUE;
break;
}
/* check next and prev */
if (!((rt_uintptr_t)item->next <= (rt_uintptr_t)((rt_uintptr_t)heap->start_addr + heap->pool_size) &&
(rt_uintptr_t)item->prev >= (rt_uintptr_t)heap->start_addr) &&
(rt_uintptr_t)item->next == RT_ALIGN((rt_uintptr_t)item->next, RT_ALIGN_SIZE) &&
(rt_uintptr_t)item->prev == RT_ALIGN((rt_uintptr_t)item->prev, RT_ALIGN_SIZE))
{
has_bad = RT_TRUE;
break;
}
/* check item */
if (item->next == item->next->prev)
{
has_bad = RT_TRUE;
break;
}
}
}
rt_hw_interrupt_enable(level);
if (has_bad)
{
rt_kprintf("Memory block wrong:\n");
rt_kprintf("name: %s\n", heap->parent.name);
rt_kprintf("item: 0x%p\n", item);
}
return 0;
}
MSH_CMD_EXPORT(memheapcheck, check memory for memheap);
static int memheaptrace(int argc, char *argv[])
{
struct rt_object_information *info;
struct rt_list_node *list;
struct rt_memheap *mh;
struct rt_list_node *node;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
info = rt_object_get_information(RT_Object_Class_MemHeap);
list = &info->object_list;
for (node = list->next; node != list; node = node->next)
{
struct rt_memheap_item *header_ptr;
long block_size;
mh = (struct rt_memheap *)rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, mh->parent.name, RT_NAME_MAX) != 0)
continue;
/* memheap dump */
rt_kprintf("\nmemory heap address:\n");
rt_kprintf("name : %s\n", mh->parent.name);
rt_kprintf("heap_ptr: 0x%p\n", mh->start_addr);
rt_kprintf("free : 0x%08x\n", mh->available_size);
rt_kprintf("max_used: 0x%08x\n", mh->max_used_size);
rt_kprintf("size : 0x%08x\n", mh->pool_size);
rt_kprintf("\n--memory used information --\n");
/* memheap item */
for (header_ptr = mh->block_list;
header_ptr->next != mh->block_list;
header_ptr = header_ptr->next)
{
if ((header_ptr->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC)
{
rt_kprintf("[0x%p - incorrect magic: 0x%08x\n",
header_ptr, header_ptr->magic);
break;
}
/* get current memory block size */
block_size = MEMITEM_SIZE(header_ptr);
if (block_size < 0)
break;
rt_kprintf("[0x%p - ", header_ptr);
if (block_size < 1024)
rt_kprintf("%5d", block_size);
else if (block_size < 1024 * 1024)
rt_kprintf("%4dK", block_size / 1024);
else if (block_size < 1024 * 1024 * 100)
rt_kprintf("%2d.%dM", block_size / (1024 * 1024), (block_size % (1024 * 1024) * 10) / (1024 * 1024));
else
rt_kprintf("%4dM", block_size / (1024 * 1024));
/* dump thread name */
rt_kprintf("] %c%c%c%c\n",
header_ptr->owner_thread_name[0],
header_ptr->owner_thread_name[1],
header_ptr->owner_thread_name[2],
header_ptr->owner_thread_name[3]);
}
}
return 0;
}
#ifdef RT_USING_FINSH
#include <finsh.h>
MSH_CMD_EXPORT(memheaptrace, dump memory trace for memheap);
#endif /* RT_USING_FINSH */
#endif /* RT_USING_MEMTRACE */
#endif /* RT_USING_MEMHEAP */
+411
View File
@@ -0,0 +1,411 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-05-27 Bernard implement memory pool
* 2006-06-03 Bernard fix the thread timer init bug
* 2006-06-30 Bernard fix the allocate/free block bug
* 2006-08-04 Bernard add hook support
* 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-26 yi.qiu add module support in rt_mp_delete
* 2011-01-24 Bernard add object allocation check.
* 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create.
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to mempool.c
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-12-10 xqyjlj fix spinlock assert
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MEMPOOL
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block);
static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block);
/**
* @addtogroup group_hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when a memory
* block is allocated from the memory pool.
*
* @param hook the hook function
*/
void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block))
{
rt_mp_alloc_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when a memory
* block is released to the memory pool.
*
* @param hook the hook function
*/
void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block))
{
rt_mp_free_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
/**
* @addtogroup group_memory_management
*/
/**@{*/
/**
* @brief This function will initialize a memory pool object, normally which is used
* for static object.
*
* @param mp is the memory pool object.
*
* @param name is the name of the memory pool.
*
* @param start is the start address of the memory pool.
*
* @param size is the total size of the memory pool.
*
* @param block_size is the size for each block..
*
* @return RT_EOK
*/
rt_err_t rt_mp_init(struct rt_mempool *mp,
const char *name,
void *start,
rt_size_t size,
rt_size_t block_size)
{
rt_uint8_t *block_ptr;
rt_size_t offset;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
RT_ASSERT(name != RT_NULL);
RT_ASSERT(start != RT_NULL);
RT_ASSERT(size > 0 && block_size > 0);
/* initialize object */
rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
/* initialize memory pool */
mp->start_address = start;
mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
/* align the block size */
block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
mp->block_size = block_size;
/* align to align size byte */
mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
mp->block_free_count = mp->block_total_count;
/* initialize suspended thread list */
rt_list_init(&(mp->suspend_thread));
/* initialize free block list */
block_ptr = (rt_uint8_t *)mp->start_address;
for (offset = 0; offset < mp->block_total_count; offset ++)
{
*(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) =
(rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)));
}
*(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) =
RT_NULL;
mp->block_list = block_ptr;
rt_spin_lock_init(&(mp->spinlock));
return RT_EOK;
}
RTM_EXPORT(rt_mp_init);
/**
* @brief This function will detach a memory pool from system object management.
*
* @param mp is the memory pool object.
*
* @return RT_EOK
*/
rt_err_t rt_mp_detach(struct rt_mempool *mp)
{
rt_base_t level;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
RT_ASSERT(rt_object_is_systemobject(&mp->parent));
level = rt_spin_lock_irqsave(&(mp->spinlock));
/* wake up all suspended threads */
rt_susp_list_resume_all(&mp->suspend_thread, RT_ERROR);
/* detach object */
rt_object_detach(&(mp->parent));
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
return RT_EOK;
}
RTM_EXPORT(rt_mp_detach);
#ifdef RT_USING_HEAP
/**
* @brief This function will create a mempool object and allocate the memory pool from
* heap.
*
* @param name is the name of memory pool.
*
* @param block_count is the count of blocks in memory pool.
*
* @param block_size is the size for each block.
*
* @return the created mempool object
*/
rt_mp_t rt_mp_create(const char *name,
rt_size_t block_count,
rt_size_t block_size)
{
rt_uint8_t *block_ptr;
struct rt_mempool *mp;
rt_size_t offset;
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(name != RT_NULL);
RT_ASSERT(block_count > 0 && block_size > 0);
/* allocate object */
mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name);
/* allocate object failed */
if (mp == RT_NULL)
return RT_NULL;
/* initialize memory pool */
block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
mp->block_size = block_size;
mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count;
/* allocate memory */
mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
block_count);
if (mp->start_address == RT_NULL)
{
/* no memory, delete memory pool object */
rt_object_delete(&(mp->parent));
return RT_NULL;
}
mp->block_total_count = block_count;
mp->block_free_count = mp->block_total_count;
/* initialize suspended thread list */
rt_list_init(&(mp->suspend_thread));
/* initialize free block list */
block_ptr = (rt_uint8_t *)mp->start_address;
for (offset = 0; offset < mp->block_total_count; offset ++)
{
*(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *)))
= block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *));
}
*(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *)))
= RT_NULL;
mp->block_list = block_ptr;
rt_spin_lock_init(&(mp->spinlock));
return mp;
}
RTM_EXPORT(rt_mp_create);
/**
* @brief This function will delete a memory pool and release the object memory.
*
* @param mp is the memory pool object.
*
* @return RT_EOK
*/
rt_err_t rt_mp_delete(rt_mp_t mp)
{
rt_base_t level;
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE);
level = rt_spin_lock_irqsave(&(mp->spinlock));
/* wake up all suspended threads */
rt_susp_list_resume_all(&mp->suspend_thread, RT_ERROR);
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
/* release allocated room */
rt_free(mp->start_address);
/* detach object */
rt_object_delete(&(mp->parent));
return RT_EOK;
}
RTM_EXPORT(rt_mp_delete);
#endif /* RT_USING_HEAP */
/**
* @brief This function will allocate a block from memory pool.
*
* @param mp is the memory pool object.
*
* @param time is the maximum waiting time for allocating memory.
* - 0 for not waiting, allocating memory immediately.
*
* @return the allocated memory block or RT_NULL on allocated failed.
*/
void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
{
rt_uint8_t *block_ptr;
rt_base_t level;
struct rt_thread *thread;
rt_uint32_t before_sleep = 0;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
/* get current thread */
thread = rt_thread_self();
level = rt_spin_lock_irqsave(&(mp->spinlock));
while (mp->block_free_count == 0)
{
/* memory block is unavailable. */
if (time == 0)
{
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
rt_set_errno(-RT_ETIMEOUT);
return RT_NULL;
}
RT_DEBUG_NOT_IN_INTERRUPT;
thread->error = RT_EOK;
/* need suspend thread */
rt_thread_suspend_to_list(thread, &mp->suspend_thread, RT_IPC_FLAG_FIFO, RT_UNINTERRUPTIBLE);
if (time > 0)
{
/* get the start tick of timer */
before_sleep = rt_tick_get();
/* init thread timer and start it */
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&time);
rt_timer_start(&(thread->thread_timer));
}
/* enable interrupt */
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
/* do a schedule */
rt_schedule();
if (thread->error != RT_EOK)
return RT_NULL;
if (time > 0)
{
time -= rt_tick_get() - before_sleep;
if (time < 0)
time = 0;
}
level = rt_spin_lock_irqsave(&(mp->spinlock));
}
/* memory block is available. decrease the free block counter */
mp->block_free_count--;
/* get block from block list */
block_ptr = mp->block_list;
RT_ASSERT(block_ptr != RT_NULL);
/* Setup the next free node. */
mp->block_list = *(rt_uint8_t **)block_ptr;
/* point to memory pool */
*(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp;
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook,
(mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *))));
return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *));
}
RTM_EXPORT(rt_mp_alloc);
/**
* @brief This function will release a memory block.
*
* @param block the address of memory block to be released.
*/
void rt_mp_free(void *block)
{
rt_uint8_t **block_ptr;
struct rt_mempool *mp;
rt_base_t level;
/* parameter check */
if (block == RT_NULL) return;
/* get the control block of pool which the block belongs to */
block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *));
mp = (struct rt_mempool *)*block_ptr;
RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
level = rt_spin_lock_irqsave(&(mp->spinlock));
/* increase the free block count */
mp->block_free_count ++;
/* link the block into the block list */
*block_ptr = mp->block_list;
mp->block_list = (rt_uint8_t *)block_ptr;
if (rt_susp_list_dequeue(&mp->suspend_thread, RT_EOK))
{
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
/* do a schedule */
rt_schedule();
return;
}
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
}
RTM_EXPORT(rt_mp_free);
/**@}*/
#endif /* RT_USING_MEMPOOL */
+818
View File
@@ -0,0 +1,818 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-14 Bernard the first version
* 2006-04-21 Bernard change the scheduler lock to interrupt lock
* 2006-05-18 Bernard fix the object init bug
* 2006-08-03 Bernard add hook support
* 2007-01-28 Bernard rename RT_OBJECT_Class_Static to RT_Object_Class_Static
* 2010-10-26 yi.qiu add module support in rt_object_allocate and rt_object_free
* 2017-12-10 Bernard Add object_info enum.
* 2018-01-25 Bernard Fix the object find issue when enable MODULE.
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to object.c
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-11-17 xqyjlj add process group and session support
*/
#include <rtthread.h>
#include <rthw.h>
#ifdef RT_USING_MODULE
#include <dlmodule.h>
#endif /* RT_USING_MODULE */
#ifdef RT_USING_SMART
#include <lwp.h>
#endif
#define DBG_TAG "kernel.obj"
#define DBG_LVL DBG_ERROR
#include <rtdbg.h>
struct rt_custom_object
{
struct rt_object parent;
rt_err_t (*destroy)(void *);
void *data;
};
/*
* define object_info for the number of _object_container items.
*/
enum rt_object_info_type
{
RT_Object_Info_Thread = 0, /**< The object is a thread. */
#ifdef RT_USING_SEMAPHORE
RT_Object_Info_Semaphore, /**< The object is a semaphore. */
#endif
#ifdef RT_USING_MUTEX
RT_Object_Info_Mutex, /**< The object is a mutex. */
#endif
#ifdef RT_USING_EVENT
RT_Object_Info_Event, /**< The object is a event. */
#endif
#ifdef RT_USING_MAILBOX
RT_Object_Info_MailBox, /**< The object is a mail box. */
#endif
#ifdef RT_USING_MESSAGEQUEUE
RT_Object_Info_MessageQueue, /**< The object is a message queue. */
#endif
#ifdef RT_USING_MEMHEAP
RT_Object_Info_MemHeap, /**< The object is a memory heap */
#endif
#ifdef RT_USING_MEMPOOL
RT_Object_Info_MemPool, /**< The object is a memory pool. */
#endif
#ifdef RT_USING_DEVICE
RT_Object_Info_Device, /**< The object is a device */
#endif
RT_Object_Info_Timer, /**< The object is a timer. */
#ifdef RT_USING_MODULE
RT_Object_Info_Module, /**< The object is a module. */
#endif
#ifdef RT_USING_HEAP
RT_Object_Info_Memory, /**< The object is a memory. */
#endif
#ifdef RT_USING_SMART
RT_Object_Info_Channel, /**< The object is a IPC channel */
RT_Object_Info_ProcessGroup, /**< The object is a process group */
RT_Object_Info_Session, /**< The object is a session */
#endif
#ifdef RT_USING_HEAP
RT_Object_Info_Custom, /**< The object is a custom object */
#endif
RT_Object_Info_Unknown, /**< The object is unknown. */
};
#define _OBJ_CONTAINER_LIST_INIT(c) \
{&(_object_container[c].object_list), &(_object_container[c].object_list)}
static struct rt_object_information _object_container[RT_Object_Info_Unknown] =
{
/* initialize object container - thread */
{RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread), RT_SPINLOCK_INIT},
#ifdef RT_USING_SEMAPHORE
/* initialize object container - semaphore */
{RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MUTEX
/* initialize object container - mutex */
{RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_EVENT
/* initialize object container - event */
{RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MAILBOX
/* initialize object container - mailbox */
{RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MESSAGEQUEUE
/* initialize object container - message queue */
{RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MEMHEAP
/* initialize object container - memory heap */
{RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MEMPOOL
/* initialize object container - memory pool */
{RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_DEVICE
/* initialize object container - device */
{RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device), RT_SPINLOCK_INIT},
#endif
/* initialize object container - timer */
{RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer), RT_SPINLOCK_INIT},
#ifdef RT_USING_MODULE
/* initialize object container - module */
{RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_HEAP
/* initialize object container - small memory */
{RT_Object_Class_Memory, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Memory), sizeof(struct rt_memory), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_SMART
/* initialize object container - module */
{RT_Object_Class_Channel, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Channel), sizeof(struct rt_channel), RT_SPINLOCK_INIT},
{RT_Object_Class_ProcessGroup, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_ProcessGroup), sizeof(struct rt_processgroup), RT_SPINLOCK_INIT},
{RT_Object_Class_Session, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Session), sizeof(struct rt_session), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_HEAP
{RT_Object_Class_Custom, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Custom), sizeof(struct rt_custom_object), RT_SPINLOCK_INIT},
#endif
};
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_object_attach_hook)(struct rt_object *object);
static void (*rt_object_detach_hook)(struct rt_object *object);
void (*rt_object_trytake_hook)(struct rt_object *object);
void (*rt_object_take_hook)(struct rt_object *object);
void (*rt_object_put_hook)(struct rt_object *object);
/**
* @addtogroup group_hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when object
* attaches to kernel object system.
*
* @param hook is the hook function.
*/
void rt_object_attach_sethook(void (*hook)(struct rt_object *object))
{
rt_object_attach_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* detaches from kernel object system.
*
* @param hook is the hook function
*/
void rt_object_detach_sethook(void (*hook)(struct rt_object *object))
{
rt_object_detach_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* is taken from kernel object system.
*
* The object is taken means:
* semaphore - semaphore is taken by thread
* mutex - mutex is taken by thread
* event - event is received by thread
* mailbox - mail is received by thread
* message queue - message is received by thread
*
* @param hook is the hook function.
*/
void rt_object_trytake_sethook(void (*hook)(struct rt_object *object))
{
rt_object_trytake_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* have been taken from kernel object system.
*
* The object have been taken means:
* semaphore - semaphore have been taken by thread
* mutex - mutex have been taken by thread
* event - event have been received by thread
* mailbox - mail have been received by thread
* message queue - message have been received by thread
* timer - timer is started
*
* @param hook the hook function.
*/
void rt_object_take_sethook(void (*hook)(struct rt_object *object))
{
rt_object_take_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* is put to kernel object system.
*
* @param hook is the hook function
*/
void rt_object_put_sethook(void (*hook)(struct rt_object *object))
{
rt_object_put_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
/**
* @addtogroup group_object_management
*/
/**@{*/
/**
* @brief This function will return the specified type of object information.
*
* @param type is the type of object, which can be
* RT_Object_Class_Thread/Semaphore/Mutex... etc
*
* @return the object type information or RT_NULL
*/
struct rt_object_information *
rt_object_get_information(enum rt_object_class_type type)
{
int index;
type = (enum rt_object_class_type)(type & ~RT_Object_Class_Static);
for (index = 0; index < RT_Object_Info_Unknown; index ++)
if (_object_container[index].type == type) return &_object_container[index];
return RT_NULL;
}
RTM_EXPORT(rt_object_get_information);
/**
* @brief This function will return the length of object list in object container.
*
* @param type is the type of object, which can be
* RT_Object_Class_Thread/Semaphore/Mutex... etc
*
* @return the length of object list
*/
int rt_object_get_length(enum rt_object_class_type type)
{
int count = 0;
rt_base_t level;
struct rt_list_node *node = RT_NULL;
struct rt_object_information *information = RT_NULL;
information = rt_object_get_information((enum rt_object_class_type)type);
if (information == RT_NULL) return 0;
level = rt_spin_lock_irqsave(&(information->spinlock));
rt_list_for_each(node, &(information->object_list))
{
count ++;
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return count;
}
RTM_EXPORT(rt_object_get_length);
/**
* @brief This function will copy the object pointer of the specified type,
* with the maximum size specified by maxlen.
*
* @param type is the type of object, which can be
* RT_Object_Class_Thread/Semaphore/Mutex... etc
*
* @param pointers is the pointer will be saved to.
*
* @param maxlen is the maximum number of pointers can be saved.
*
* @return the copied number of object pointers.
*/
int rt_object_get_pointers(enum rt_object_class_type type, rt_object_t *pointers, int maxlen)
{
int index = 0;
rt_base_t level;
struct rt_object *object;
struct rt_list_node *node = RT_NULL;
struct rt_object_information *information = RT_NULL;
if (maxlen <= 0) return 0;
information = rt_object_get_information(type);
if (information == RT_NULL) return 0;
level = rt_spin_lock_irqsave(&(information->spinlock));
/* retrieve pointer of object */
rt_list_for_each(node, &(information->object_list))
{
object = rt_list_entry(node, struct rt_object, list);
pointers[index] = object;
index ++;
if (index >= maxlen) break;
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return index;
}
RTM_EXPORT(rt_object_get_pointers);
/**
* @brief This function will initialize an object and add it to object system
* management.
*
* @param object The specified object to be initialized.
* The object pointer that needs to be initialized must point to
* a specific object memory block, not a null pointer or a wild pointer.
*
* @param type The object type. The type of the object must be a enumeration
* type listed in rt_object_class_type, RT_Object_Class_Static
* excluded. (For static objects, or objects initialized with the
* rt_object_init interface, the system identifies it as an
* RT_Object_Class_Static type)
*
* @param name Name of the object. In system, the object's name must be unique.
* Each object can be set to a name, and the maximum length for the
* name is specified by RT_NAME_MAX. The system does not care if it
* uses '\0' as a terminal symbol.
*/
void rt_object_init(struct rt_object *object,
enum rt_object_class_type type,
const char *name)
{
rt_base_t level;
rt_size_t obj_name_len;
#ifdef RT_DEBUGING_ASSERT
struct rt_list_node *node = RT_NULL;
#endif /* RT_DEBUGING_ASSERT */
struct rt_object_information *information;
#ifdef RT_USING_MODULE
struct rt_dlmodule *module = dlmodule_self();
#endif /* RT_USING_MODULE */
/* get object information */
information = rt_object_get_information(type);
RT_ASSERT(information != RT_NULL);
#ifdef RT_DEBUGING_ASSERT
/* check object type to avoid re-initialization */
/* enter critical */
level = rt_spin_lock_irqsave(&(information->spinlock));
/* try to find object */
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
struct rt_object *obj;
obj = rt_list_entry(node, struct rt_object, list);
RT_ASSERT(obj != object);
}
/* leave critical */
rt_spin_unlock_irqrestore(&(information->spinlock), level);
#endif /* RT_DEBUGING_ASSERT */
/* initialize object's parameters */
/* set object type to static */
object->type = type | RT_Object_Class_Static;
#if RT_NAME_MAX > 0
if (name)
{
obj_name_len = rt_strlen(name);
if(obj_name_len > RT_NAME_MAX - 1)
{
LOG_E("Object name %s exceeds RT_NAME_MAX=%d, consider increasing RT_NAME_MAX.", name, RT_NAME_MAX);
RT_ASSERT(obj_name_len <= RT_NAME_MAX - 1);
}
rt_memcpy(object->name, name, obj_name_len);
object->name[obj_name_len] = '\0';
}
else
{
object->name[0] = '\0';
}
#else
object->name = name;
#endif
RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));
level = rt_spin_lock_irqsave(&(information->spinlock));
#ifdef RT_USING_MODULE
if (module)
{
rt_list_insert_after(&(module->object_list), &(object->list));
object->module_id = (void *)module;
}
else
#endif /* RT_USING_MODULE */
{
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
}
/**
* @brief This function will detach a static object from object system,
* and the memory of static object is not freed.
*
* @param object the specified object to be detached.
*/
void rt_object_detach(rt_object_t object)
{
rt_base_t level;
struct rt_object_information *information;
/* object check */
RT_ASSERT(object != RT_NULL);
RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));
information = rt_object_get_information((enum rt_object_class_type)object->type);
RT_ASSERT(information != RT_NULL);
level = rt_spin_lock_irqsave(&(information->spinlock));
/* remove from old list */
rt_list_remove(&(object->list));
rt_spin_unlock_irqrestore(&(information->spinlock), level);
object->type = RT_Object_Class_Null;
}
#ifdef RT_USING_HEAP
/**
* @brief This function will allocate an object from object system.
*
* @param type Type of object. The type of the allocated object can only be of
* type rt_object_class_type other than RT_Object_Class_Static.
* In addition, the type of object allocated through this interface
* is dynamic, not static.
*
* @param name Name of the object. In system, the object's name must be unique.
* Each object can be set to a name, and the maximum length for the
* name is specified by RT_NAME_MAX. The system does not care if it
* uses '\0' as a terminal symbol.
*
* @return object handle allocated successfully, or RT_NULL if no memory can be allocated.
*/
rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name)
{
struct rt_object *object;
rt_base_t level;
rt_size_t obj_name_len;
struct rt_object_information *information;
#ifdef RT_USING_MODULE
struct rt_dlmodule *module = dlmodule_self();
#endif /* RT_USING_MODULE */
RT_DEBUG_NOT_IN_INTERRUPT;
/* get object information */
information = rt_object_get_information(type);
RT_ASSERT(information != RT_NULL);
object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size);
if (object == RT_NULL)
{
/* no memory can be allocated */
return RT_NULL;
}
/* clean memory data of object */
rt_memset(object, 0x0, information->object_size);
/* initialize object's parameters */
/* set object type */
object->type = type;
/* set object flag */
object->flag = 0;
#if RT_NAME_MAX > 0
if (name)
{
obj_name_len = rt_strlen(name);
if(obj_name_len > RT_NAME_MAX - 1)
{
LOG_E("Object name '%s' exceeds RT_NAME_MAX=%d, consider increasing RT_NAME_MAX.", name, RT_NAME_MAX);
RT_ASSERT(obj_name_len <= RT_NAME_MAX - 1);
}
rt_memcpy(object->name, name, obj_name_len);
object->name[obj_name_len] = '\0';
}
else
{
object->name[0] = '\0';
}
#else
object->name = name;
#endif
RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));
level = rt_spin_lock_irqsave(&(information->spinlock));
#ifdef RT_USING_MODULE
if (module)
{
rt_list_insert_after(&(module->object_list), &(object->list));
object->module_id = (void *)module;
}
else
#endif /* RT_USING_MODULE */
{
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return object;
}
/**
* @brief This function will delete an object and release object memory.
*
* @param object The specified object to be deleted.
*/
void rt_object_delete(rt_object_t object)
{
rt_base_t level;
struct rt_object_information *information;
/* object check */
RT_ASSERT(object != RT_NULL);
RT_ASSERT(!(object->type & RT_Object_Class_Static));
RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));
information = rt_object_get_information((enum rt_object_class_type)object->type);
RT_ASSERT(information != RT_NULL);
level = rt_spin_lock_irqsave(&(information->spinlock));
/* remove from old list */
rt_list_remove(&(object->list));
rt_spin_unlock_irqrestore(&(information->spinlock), level);
/* reset object type */
object->type = RT_Object_Class_Null;
/* free the memory of object */
RT_KERNEL_FREE(object);
}
#endif /* RT_USING_HEAP */
/**
* @brief This function will judge the object is system object or not.
*
* @note Normally, the system object is a static object and the type
* of object set to RT_Object_Class_Static.
*
* @param object The specified object to be judged.
*
* @return RT_TRUE if a system object, RT_FALSE for others.
*/
rt_bool_t rt_object_is_systemobject(rt_object_t object)
{
/* object check */
RT_ASSERT(object != RT_NULL);
if (object->type & RT_Object_Class_Static)
return RT_TRUE;
return RT_FALSE;
}
/**
* @brief This function will return the type of object without
* RT_Object_Class_Static flag.
*
* @param object is the specified object to be get type.
*
* @return the type of object.
*/
rt_uint8_t rt_object_get_type(rt_object_t object)
{
/* object check */
RT_ASSERT(object != RT_NULL);
return object->type & ~RT_Object_Class_Static;
}
/**
* @brief This function will iterate through each object from object
* container.
*
* @param type is the type of object
* @param iter is the iterator
* @param data is the specified data passed to iterator
*
* @return RT_EOK on succeed, otherwise the error from `iter`
*
* @note this function shall not be invoked in interrupt status.
*/
rt_err_t rt_object_for_each(rt_uint8_t type, rt_object_iter_t iter, void *data)
{
struct rt_object *object = RT_NULL;
struct rt_list_node *node = RT_NULL;
struct rt_object_information *information = RT_NULL;
rt_base_t level;
rt_err_t error;
information = rt_object_get_information((enum rt_object_class_type)type);
/* parameter check */
if (information == RT_NULL)
{
return -RT_EINVAL;
}
/* which is invoke in interrupt status */
RT_DEBUG_NOT_IN_INTERRUPT;
/* enter critical */
level = rt_spin_lock_irqsave(&(information->spinlock));
/* try to find object */
rt_list_for_each(node, &(information->object_list))
{
object = rt_list_entry(node, struct rt_object, list);
if ((error = iter(object, data)) != RT_EOK)
{
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return error >= 0 ? RT_EOK : error;
}
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return RT_EOK;
}
struct _obj_find_param
{
const char *match_name;
rt_object_t matched_obj;
};
static rt_err_t _match_name(struct rt_object *obj, void *data)
{
struct _obj_find_param *param = data;
const char *name = param->match_name;
char truncated_name[RT_NAME_MAX];
/* Truncate input name to RT_NAME_MAX - 1 to match object name storage */
rt_strncpy(truncated_name, name, RT_NAME_MAX - 1);
truncated_name[RT_NAME_MAX - 1] = '\0';
if (rt_strcmp(obj->name, truncated_name) == 0)
{
param->matched_obj = obj;
/* notify an early break of loop, but not on error */
return 1;
}
return RT_EOK;
}
/**
* @brief This function will find specified name object from object
* container.
*
* @param name is the specified name of object.
*
* @param type is the type of object
*
* @return the found object or RT_NULL if there is no this object
* in object container.
*
* @note this function shall not be invoked in interrupt status.
*/
rt_object_t rt_object_find(const char *name, rt_uint8_t type)
{
struct _obj_find_param param =
{
.match_name = name,
.matched_obj = RT_NULL,
};
/* parameter check */
if (name == RT_NULL || rt_object_get_information(type) == RT_NULL)
return RT_NULL;
/* which is invoke in interrupt status */
RT_DEBUG_NOT_IN_INTERRUPT;
rt_object_for_each(type, _match_name, &param);
return param.matched_obj;
}
/**
* @brief This function will return the name of the specified object container
*
* @param object the specified object to be get name
* @param name buffer to store the object name string
* @param name_size maximum size of the buffer to store object name
*
* @return -RT_EINVAL if any parameter is invalid or RT_EOK if the operation is successfully executed
*
* @note this function shall not be invoked in interrupt status
*/
rt_err_t rt_object_get_name(rt_object_t object, char *name, rt_uint8_t name_size)
{
rt_err_t result = -RT_EINVAL;
if ((object != RT_NULL) && (name != RT_NULL) && (name_size != 0U))
{
const char *obj_name = object->name;
rt_strncpy(name, obj_name, (rt_size_t)name_size);
/* Ensure null-termination */
name[name_size - 1] = '\0';
result = RT_EOK;
}
return result;
}
#ifdef RT_USING_HEAP
/**
* This function will create a custom object
* container.
*
* @param name the specified name of object.
* @param data the custom data
* @param data_destroy the custom object destroy callback
*
* @return the found object or RT_NULL if there is no this object
* in object container.
*
* @note this function shall not be invoked in interrupt status.
*/
rt_object_t rt_custom_object_create(const char *name, void *data, rt_err_t (*data_destroy)(void *))
{
struct rt_custom_object *cobj = RT_NULL;
cobj = (struct rt_custom_object *)rt_object_allocate(RT_Object_Class_Custom, name);
if (!cobj)
{
return RT_NULL;
}
cobj->destroy = data_destroy;
cobj->data = data;
return (struct rt_object *)cobj;
}
/**
* This function will destroy a custom object
* container.
*
* @param obj the specified name of object.
*
* @note this function shall not be invoked in interrupt status.
*/
rt_err_t rt_custom_object_destroy(rt_object_t obj)
{
rt_err_t ret = -1;
struct rt_custom_object *cobj = (struct rt_custom_object *)obj;
if (obj && obj->type == RT_Object_Class_Custom)
{
if (cobj->destroy)
{
ret = cobj->destroy(cobj->data);
}
rt_object_delete(obj);
}
return ret;
}
#endif
/**@}*/
+484
View File
@@ -0,0 +1,484 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* (scheduler_comm.c) Common API of scheduling routines.
*
* Change Logs:
* Date Author Notes
* 2024-01-18 Shell Separate scheduling related codes from thread.c, scheduler_.*
*/
#define DBG_TAG "kernel.sched"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <rtthread.h>
/**
* @brief Initialize thread scheduling context
*
* @param thread The thread to be initialized
* @param tick Initial time slice value for the thread
* @param priority Initial priority of the thread
*
* @details This function performs the following initialization:
* - Sets thread status to INIT
* - For SMP systems:
* * Sets bind CPU to none (RT_CPUS_NR)
* * Marks CPU as detached (RT_CPU_DETACHED)
* - Calls rt_sched_thread_init_priv() for private scheduling data initialization
*/
void rt_sched_thread_init_ctx(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority)
{
/* setup thread status */
RT_SCHED_CTX(thread).stat = RT_THREAD_INIT;
#ifdef RT_USING_SMP
/* not bind on any cpu */
RT_SCHED_CTX(thread).bind_cpu = RT_CPUS_NR;
RT_SCHED_CTX(thread).oncpu = RT_CPU_DETACHED;
#endif /* RT_USING_SMP */
rt_sched_thread_init_priv(thread, tick, priority);
}
/**
* @brief Start the thread timer for scheduling
*
* @param thread The thread whose timer needs to be started
*
* @return rt_err_t Always returns RT_EOK on success
*
* @details This function:
* - Requires scheduler lock to be held.
* - Sets the thread's timer flag (sched_flag_ttmr_set) to indicate timer is active
*/
rt_err_t rt_sched_thread_timer_start(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
RT_SCHED_CTX(thread).sched_flag_ttmr_set = 1;
return RT_EOK;
}
/**
* @brief Stop the thread timer for scheduling
*
* @param thread The thread whose timer needs to be stopped
*
* @return rt_err_t
* - RT_EOK if timer was successfully stopped or not active
* - Other error codes from rt_timer_stop() if stop operation failed
*/
rt_err_t rt_sched_thread_timer_stop(struct rt_thread *thread)
{
rt_err_t error;
RT_SCHED_DEBUG_IS_LOCKED;
if (RT_SCHED_CTX(thread).sched_flag_ttmr_set)
{
error = rt_timer_stop(&thread->thread_timer);
/* mask out timer flag no matter stop success or not */
RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
}
else
{
error = RT_EOK;
}
return error;
}
/**
* @brief Get the current status of a thread
*
* @param thread The thread to get status from
*
* @return rt_uint8_t The thread status masked with RT_THREAD_STAT_MASK
*
* @details This function:
* - Requires scheduler lock to be held (RT_SCHED_DEBUG_IS_LOCKED)
* - Returns the thread's status field masked with RT_THREAD_STAT_MASK
*/
rt_uint8_t rt_sched_thread_get_stat(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
return RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK;
}
/**
* @brief Get the current priority of a thread
*
* @param thread The thread to get priority from
*
* @return rt_uint8_t The current priority value of the thread
*
* @details This function:
* - Requires scheduler lock to be held (RT_SCHED_DEBUG_IS_LOCKED)
* - Returns the thread's current priority field from its private scheduling data
*/
rt_uint8_t rt_sched_thread_get_curr_prio(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
return RT_SCHED_PRIV(thread).current_priority;
}
/**
* @brief Get the initial priority of a thread
*
* @param thread The thread to get priority from
*
* @return rt_uint8_t The initial priority value of the thread
*
* @details This function:
* - Returns the thread's initial priority field from its private scheduling data
* - Does not require scheduler lock as it accesses read-only fields
*/
rt_uint8_t rt_sched_thread_get_init_prio(struct rt_thread *thread)
{
/* read only fields, so lock is unnecessary */
return RT_SCHED_PRIV(thread).init_priority;
}
/**
* @brief Check if a thread is in suspended state
*
* @param thread The thread to check
*
* @return rt_uint8_t
* - 1 if thread is suspended (matches RT_THREAD_SUSPEND_MASK)
* - 0 otherwise
*
* @details This function:
* - Requires scheduler lock to be held (RT_SCHED_DEBUG_IS_LOCKED)
* - Checks thread's status field against RT_THREAD_SUSPEND_MASK
*
* @note Caller must hold the scheduler lock before calling this function
*/
rt_uint8_t rt_sched_thread_is_suspended(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
return (RT_SCHED_CTX(thread).stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK;
}
/**
* @brief Close a thread by setting its status to CLOSED
*
* @param thread The thread to be closed
* @return rt_err_t Always returns RT_EOK on success
*
* @details This function:
* - Requires scheduler lock to be held (RT_SCHED_DEBUG_IS_LOCKED)
* - Sets the thread's status to RT_THREAD_CLOSE
*
* @note Must be called with scheduler lock held
*/
rt_err_t rt_sched_thread_close(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
RT_SCHED_CTX(thread).stat = RT_THREAD_CLOSE;
return RT_EOK;
}
/**
* @brief Yield the current thread's remaining time slice
*
* @param thread The thread to yield
* @return rt_err_t Always returns RT_EOK on success
*
* @details This function:
* - Requires scheduler lock to be held (RT_SCHED_DEBUG_IS_LOCKED)
* - Resets the thread's remaining tick count to its initial value
* - Sets the thread's status to YIELD state
*
* @note Must be called with scheduler lock held
*/
rt_err_t rt_sched_thread_yield(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
RT_SCHED_PRIV(thread).remaining_tick = RT_SCHED_PRIV(thread).init_tick;
RT_SCHED_CTX(thread).stat |= RT_THREAD_STAT_YIELD;
return RT_EOK;
}
/**
* @brief Make a suspended thread ready for scheduling
*
* @param thread The thread to be made ready
*
* @return rt_err_t
* - RT_EOK if operation succeeded
* - -RT_EINVAL if thread is not suspended
* - Other error codes from rt_sched_thread_timer_stop() if timer stop failed
*
* @details This function:
* - Requires scheduler lock to be held (RT_SCHED_DEBUG_IS_LOCKED)
* - Checks if thread is suspended (returns -RT_EINVAL if not)
* - Stops thread timer if active
* - Removes thread from suspend list
* - Clears wakeup handler (if RT_USING_SMART is defined)
* - Inserts thread into ready queue
*
* @note Must be called with scheduler lock held
* May fail due to racing conditions with timeout ISR
*/
rt_err_t rt_sched_thread_ready(struct rt_thread *thread)
{
rt_err_t error;
RT_SCHED_DEBUG_IS_LOCKED;
if (!rt_sched_thread_is_suspended(thread))
{
/* failed to proceed, and that's possibly due to a racing condition */
error = -RT_EINVAL;
}
else
{
if (RT_SCHED_CTX(thread).sched_flag_ttmr_set)
{
/**
* Quiet timeout timer first if set. and don't continue if we
* failed, because it probably means that a timeout ISR racing to
* resume thread before us.
*/
error = rt_sched_thread_timer_stop(thread);
}
else
{
error = RT_EOK;
}
if (!error)
{
/* remove from suspend list */
rt_list_remove(&RT_THREAD_LIST_NODE(thread));
#ifdef RT_USING_SMART
thread->wakeup_handle.func = RT_NULL;
#endif
/* insert to schedule ready list and remove from susp list */
rt_sched_insert_thread(thread);
}
}
return error;
}
/**
* @brief Increase the system tick and update thread's remaining time slice
*
* @param tick The number of ticks to increase
* @return rt_err_t Always returns RT_EOK
*
* @details This function:
* - Gets the current thread
* - Locks the scheduler
* - Decreases the thread's remaining tick count by the specified amount
* - If remaining ticks reach zero:
* * Calls rt_sched_thread_yield() to yield the thread
* * Requests a reschedule with rt_sched_unlock_n_resched()
* - Otherwise simply unlocks the scheduler
*
* @note This function is typically called from timer interrupt context
* It handles both SMP and non-SMP cases
*/
rt_err_t rt_sched_tick_increase(rt_tick_t tick)
{
struct rt_thread *thread;
rt_sched_lock_level_t slvl;
thread = rt_thread_self();
rt_sched_lock(&slvl);
if(RT_SCHED_PRIV(thread).remaining_tick > tick)
{
RT_SCHED_PRIV(thread).remaining_tick -= tick;
}
else
{
RT_SCHED_PRIV(thread).remaining_tick = 0;
}
if (RT_SCHED_PRIV(thread).remaining_tick)
{
rt_sched_unlock(slvl);
}
else
{
rt_sched_thread_yield(thread);
/* request a rescheduling even though we are probably in an ISR */
rt_sched_unlock_n_resched(slvl);
}
return RT_EOK;
}
/**
* @brief Update thread priority and adjust scheduling attributes
*
* @param thread The thread to update priority for
* @param priority New priority value to set
* @param update_init_prio Flag to determine if initial priority should also be updated
* @return rt_err_t Always returns RT_EOK on success
*
* @details This function:
* - Requires scheduler lock to be held (RT_SCHED_DEBUG_IS_LOCKED)
* - For ready threads:
* * Removes from ready queue
* * Updates priority values
* * Recalculates priority attributes (number, mask, etc.)
* * Reinserts into ready queue with new priority
* - For non-ready threads:
* * Only updates priority values and attributes
* - Handles both 32-bit and >32-bit priority systems
*
* @note Must be called with scheduler lock held
* Thread status must be valid before calling
*/
static rt_err_t _rt_sched_update_priority(struct rt_thread *thread, rt_uint8_t priority, rt_bool_t update_init_prio)
{
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
RT_SCHED_DEBUG_IS_LOCKED;
/* for ready thread, change queue; otherwise simply update the priority */
if ((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY)
{
/* remove thread from schedule queue first */
rt_sched_remove_thread(thread);
/* change thread priority */
if (update_init_prio)
{
RT_SCHED_PRIV(thread).init_priority = priority;
}
RT_SCHED_PRIV(thread).current_priority = priority;
/* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).number;
RT_SCHED_PRIV(thread).high_mask = 1 << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */
#else
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).current_priority;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
RT_SCHED_CTX(thread).stat = RT_THREAD_INIT;
/* insert thread to schedule queue again */
rt_sched_insert_thread(thread);
}
else
{
if (update_init_prio)
{
RT_SCHED_PRIV(thread).init_priority = priority;
}
RT_SCHED_PRIV(thread).current_priority = priority;
/* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).number;
RT_SCHED_PRIV(thread).high_mask = 1 << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */
#else
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).current_priority;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
}
return RT_EOK;
}
/**
* @brief Update priority of the target thread
*/
rt_err_t rt_sched_thread_change_priority(struct rt_thread *thread, rt_uint8_t priority)
{
return _rt_sched_update_priority(thread, priority, RT_FALSE);
}
/**
* @brief Reset priority of the target thread
*/
rt_err_t rt_sched_thread_reset_priority(struct rt_thread *thread, rt_uint8_t priority)
{
return _rt_sched_update_priority(thread, priority, RT_TRUE);
}
#ifdef RT_USING_OVERFLOW_CHECK
/**
* @brief Check thread stack for overflow or near-overflow conditions
*
* @param thread The thread to check stack for
*
* @details This function performs the following checks:
* - For SMART mode without MMU: skips check if SP is in user data section
* - Without hardware stack guard:
* * For upward-growing stacks: checks magic number at top and SP range
* * For downward-growing stacks: checks magic number at bottom and SP range
* * Triggers error and infinite loop on overflow
* - Additional warnings when stack pointer is near boundaries
*/
void rt_scheduler_stack_check(struct rt_thread *thread)
{
RT_ASSERT(thread != RT_NULL);
#ifdef RT_USING_SMART
#ifndef ARCH_MM_MMU
struct rt_lwp *lwp = thread ? (struct rt_lwp *)thread->lwp : 0;
/* if stack pointer locate in user data section skip stack check. */
if (lwp && ((rt_uint32_t)thread->sp > (rt_uint32_t)lwp->data_entry &&
(rt_uint32_t)thread->sp <= (rt_uint32_t)lwp->data_entry + (rt_uint32_t)lwp->data_size))
{
return;
}
#endif /* not defined ARCH_MM_MMU */
#endif /* RT_USING_SMART */
#ifndef RT_USING_HW_STACK_GUARD
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
if (*((rt_uint8_t *)((rt_uintptr_t)thread->stack_addr + thread->stack_size - 1)) != '#' ||
#else
if (*((rt_uint8_t *)thread->stack_addr) != '#' ||
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
(rt_uintptr_t)thread->sp <= (rt_uintptr_t)thread->stack_addr ||
(rt_uintptr_t)thread->sp >
(rt_uintptr_t)thread->stack_addr + (rt_uintptr_t)thread->stack_size)
{
rt_base_t dummy = 1;
LOG_E("thread:%s stack overflow\n", thread->parent.name);
while (dummy);
}
#endif /* RT_USING_HW_STACK_GUARD */
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
#ifndef RT_USING_HW_STACK_GUARD
else if ((rt_uintptr_t)thread->sp > ((rt_uintptr_t)thread->stack_addr + thread->stack_size))
#else
if ((rt_uintptr_t)thread->sp > ((rt_uintptr_t)thread->stack_addr + thread->stack_size))
#endif
{
LOG_W("warning: %s stack is close to the top of stack address.\n",
thread->parent.name);
}
#else
#ifndef RT_USING_HW_STACK_GUARD
else if ((rt_uintptr_t)thread->sp <= ((rt_uintptr_t)thread->stack_addr + 32))
#else
if ((rt_uintptr_t)thread->sp <= ((rt_uintptr_t)thread->stack_addr + 32))
#endif
{
LOG_W("warning: %s stack is close to end of stack address.\n",
thread->parent.name);
}
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
}
#endif /* RT_USING_OVERFLOW_CHECK */
File diff suppressed because it is too large Load Diff
+735
View File
@@ -0,0 +1,735 @@
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-17 Bernard the first version
* 2006-04-28 Bernard fix the scheduler algorthm
* 2006-04-30 Bernard add SCHEDULER_DEBUG
* 2006-05-27 Bernard fix the scheduler algorthm for same priority
* thread schedule
* 2006-06-04 Bernard rewrite the scheduler algorithm
* 2006-08-03 Bernard add hook support
* 2006-09-05 Bernard add 32 priority level support
* 2006-09-24 Bernard add rt_system_scheduler_start function
* 2009-09-16 Bernard fix _rt_scheduler_stack_check
* 2010-04-11 yi.qiu add module feature
* 2010-07-13 Bernard fix the maximal number of rt_scheduler_lock_nest
* issue found by kuronca
* 2010-12-13 Bernard add defunct list initialization even if not use heap.
* 2011-05-10 Bernard clean scheduler debug log.
* 2013-12-21 Grissiom add rt_critical_level
* 2018-11-22 Jesven remove the current task from ready queue
* add per cpu ready queue
* add _scheduler_get_highest_priority_thread to find highest priority task
* rt_schedule_insert_thread won't insert current task to ready queue
* in smp version, rt_hw_context_switch_interrupt maybe switch to
* new task directly
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to scheduler.c
* 2023-03-27 rose_man Split into scheduler upc and scheduler_mp.c
* 2023-10-17 ChuShicheng Modify the timing of clearing RT_THREAD_STAT_YIELD flag bits
* 2025-08-04 Pillar Add rt_scheduler_critical_switch_flag
*/
#define __RT_IPC_SOURCE__
#include <rtthread.h>
#include <rthw.h>
#define DBG_TAG "kernel.scheduler"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
rt_uint32_t rt_thread_ready_priority_group;
#if RT_THREAD_PRIORITY_MAX > 32
/* Maximum priority level, 256 */
rt_uint8_t rt_thread_ready_table[32];
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
extern volatile rt_atomic_t rt_interrupt_nest;
static rt_int16_t rt_scheduler_lock_nest;
rt_uint8_t rt_current_priority;
static rt_int8_t rt_scheduler_critical_switch_flag;
#define IS_CRITICAL_SWITCH_PEND() (rt_scheduler_critical_switch_flag == 1)
#define SET_CRITICAL_SWITCH_FLAG() (rt_scheduler_critical_switch_flag = 1)
#define CLR_CRITICAL_SWITCH_FLAG() (rt_scheduler_critical_switch_flag = 0)
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_scheduler_hook)(struct rt_thread *from, struct rt_thread *to);
static void (*rt_scheduler_switch_hook)(struct rt_thread *tid);
/**
* @addtogroup group_hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when thread
* switch happens.
*
* @param hook is the hook function.
*/
void rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
{
rt_scheduler_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when context
* switch happens.
*
* @param hook is the hook function.
*/
void rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid))
{
rt_scheduler_switch_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio)
{
struct rt_thread *highest_priority_thread;
rt_ubase_t highest_ready_priority;
#if RT_THREAD_PRIORITY_MAX > 32
rt_ubase_t number;
number = __rt_ffs(rt_thread_ready_priority_group) - 1;
highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#else
highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
/* get highest ready priority thread */
highest_priority_thread = RT_THREAD_LIST_NODE_ENTRY(rt_thread_priority_table[highest_ready_priority].next);
*highest_prio = highest_ready_priority;
return highest_priority_thread;
}
/**
* @brief Lock the scheduler and save the interrupt level
*
* @param plvl Pointer to store the interrupt level before locking
*
* @return rt_err_t
* - RT_EOK on success
* - -RT_EINVAL if plvl is NULL
*
* @details This function:
* - Disables interrupts to prevent preemption
* - Saves the previous interrupt level in plvl
* - Must be paired with rt_sched_unlock() to restore interrupts
*
* @note The lock is implemented by disabling interrupts
* Caller must ensure plvl is valid
*/
rt_err_t rt_sched_lock(rt_sched_lock_level_t *plvl)
{
rt_base_t level;
if (!plvl)
return -RT_EINVAL;
level = rt_hw_interrupt_disable();
*plvl = level;
return RT_EOK;
}
/**
* @brief Unlock the scheduler and restore the interrupt level
*
* @param level The interrupt level to restore (previously saved by rt_sched_lock)
* @return rt_err_t Always returns RT_EOK
*
* @details This function:
* - Restores the interrupt level that was saved when locking the scheduler
* - Must be called to match each rt_sched_lock() call
*
* @note Must be called with the same interrupt level that was saved by rt_sched_lock()
* Should not be called without a corresponding rt_sched_lock() first
*/
rt_err_t rt_sched_unlock(rt_sched_lock_level_t level)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
/**
* @brief Unlock scheduler and trigger a reschedule if needed
*
* @param level The interrupt level to restore (previously saved by rt_sched_lock)
* @return rt_err_t Always returns RT_EOK
*
* @details This function:
* - Restores the interrupt level that was saved when locking the scheduler
* - Triggers a reschedule if the scheduler is available (rt_thread_self() != NULL)
* - Combines the functionality of rt_sched_unlock() and rt_schedule()
*/
rt_err_t rt_sched_unlock_n_resched(rt_sched_lock_level_t level)
{
if (rt_thread_self())
{
/* if scheduler is available */
rt_schedule();
}
rt_hw_interrupt_enable(level);
return RT_EOK;
}
/**
* @brief Initialize the system scheduler for single-core systems
*
* @details This function performs the following initialization tasks:
* - Resets the scheduler lock nest counter to 0
* - Initializes the priority table for all priority levels
* - Clears the ready priority group bitmap
* - For systems with >32 priority levels, initializes the ready table
*
* @note This function must be called before any thread scheduling can occur.
* It prepares the scheduler data structures for single-core operation
*/
void rt_system_scheduler_init(void)
{
rt_base_t offset;
rt_scheduler_lock_nest = 0;
LOG_D("start scheduler: max priority 0x%02x",
RT_THREAD_PRIORITY_MAX);
for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
{
rt_list_init(&rt_thread_priority_table[offset]);
}
/* initialize ready priority group */
rt_thread_ready_priority_group = 0;
#if RT_THREAD_PRIORITY_MAX > 32
/* initialize ready table */
rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table));
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
}
/**
* @brief Start the system scheduler and switch to the highest priority thread
*
* @details This function:
* - Gets the highest priority ready thread using _scheduler_get_highest_priority_thread()
* - Sets it as the current thread for the CPU
* - Removes the thread from ready queue and sets its status to RUNNING
* - Performs a context switch to the selected thread using rt_hw_context_switch_to()
*
* @note This function does not return as it switches to the first thread to run.
* Must be called after rt_system_scheduler_init().
* The selected thread will begin execution immediately
*/
void rt_system_scheduler_start(void)
{
struct rt_thread *to_thread;
rt_ubase_t highest_ready_priority;
to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
rt_cpu_self()->current_thread = to_thread;
/* flush critical switch flag */
CLR_CRITICAL_SWITCH_FLAG();
rt_sched_remove_thread(to_thread);
RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING;
/* switch to new thread */
rt_hw_context_switch_to((rt_uintptr_t)&to_thread->sp);
/* never come back */
}
/**
* @addtogroup group_thread_management
* @cond
*/
/**@{*/
/**
* @brief Perform thread scheduling once. Select the highest priority thread and switch to it.
*
* @details This function:
* - Disables interrupts to prevent preemption during scheduling
* - Checks if scheduler is enabled (lock_nest == 0)
* - Gets the highest priority ready thread
* - Determines if current thread should continue running or be preempted
* - Performs context switch if needed:
* * From current thread to new thread (normal case)
* * Handles special cases like interrupt context switches
* - Manages thread states (READY/RUNNING) and priority queues
* - Handles thread yield flags and signal processing
*/
void rt_schedule(void)
{
rt_base_t level;
struct rt_thread *to_thread;
struct rt_thread *from_thread;
/* using local variable to avoid unecessary function call */
struct rt_thread *curr_thread = rt_thread_self();
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* check the scheduler is enabled or not */
if (rt_scheduler_lock_nest == 0)
{
rt_ubase_t highest_ready_priority;
if (rt_thread_ready_priority_group != 0)
{
/* need_insert_from_thread: need to insert from_thread to ready queue */
int need_insert_from_thread = 0;
to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
if ((RT_SCHED_CTX(curr_thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)
{
if (RT_SCHED_PRIV(curr_thread).current_priority < highest_ready_priority)
{
to_thread = curr_thread;
}
else if (RT_SCHED_PRIV(curr_thread).current_priority == highest_ready_priority
&& (RT_SCHED_CTX(curr_thread).stat & RT_THREAD_STAT_YIELD_MASK) == 0)
{
to_thread = curr_thread;
}
else
{
need_insert_from_thread = 1;
}
}
if (to_thread != curr_thread)
{
/* if the destination thread is not the same as current thread */
rt_current_priority = (rt_uint8_t)highest_ready_priority;
from_thread = curr_thread;
rt_cpu_self()->current_thread = to_thread;
RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));
if (need_insert_from_thread)
{
rt_sched_insert_thread(from_thread);
}
if ((RT_SCHED_CTX(from_thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0)
{
RT_SCHED_CTX(from_thread).stat &= ~RT_THREAD_STAT_YIELD_MASK;
}
rt_sched_remove_thread(to_thread);
RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(to_thread).stat & ~RT_THREAD_STAT_MASK);
/* switch to new thread */
LOG_D("[%d]switch to priority#%d "
"thread:%.*s(sp:0x%08x), "
"from thread:%.*s(sp: 0x%08x)",
rt_interrupt_nest, highest_ready_priority,
RT_NAME_MAX, to_thread->parent.name, to_thread->sp,
RT_NAME_MAX, from_thread->parent.name, from_thread->sp);
RT_SCHEDULER_STACK_CHECK(to_thread);
if (rt_interrupt_nest == 0)
{
extern void rt_thread_handle_sig(rt_bool_t clean_state);
RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (from_thread));
rt_hw_context_switch((rt_uintptr_t)&from_thread->sp,
(rt_uintptr_t)&to_thread->sp);
/* enable interrupt */
rt_hw_interrupt_enable(level);
#ifdef RT_USING_SIGNALS
/* check stat of thread for signal */
level = rt_hw_interrupt_disable();
if (RT_SCHED_CTX(curr_thread).stat & RT_THREAD_STAT_SIGNAL_PENDING)
{
extern void rt_thread_handle_sig(rt_bool_t clean_state);
RT_SCHED_CTX(curr_thread).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
rt_hw_interrupt_enable(level);
/* check signal status */
rt_thread_handle_sig(RT_TRUE);
}
else
{
rt_hw_interrupt_enable(level);
}
#endif /* RT_USING_SIGNALS */
goto __exit;
}
else
{
LOG_D("switch in interrupt");
rt_hw_context_switch_interrupt((rt_uintptr_t)&from_thread->sp,
(rt_uintptr_t)&to_thread->sp, from_thread, to_thread);
}
}
else
{
rt_sched_remove_thread(curr_thread);
RT_SCHED_CTX(curr_thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(curr_thread).stat & ~RT_THREAD_STAT_MASK);
}
}
}
else
{
SET_CRITICAL_SWITCH_FLAG();
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
__exit:
return;
}
/**
* @brief Initialize thread scheduling attributes for startup
*
* @param thread The thread to be initialized
*
* @details This function:
* - For systems with >32 priority levels:
* * Sets the thread's priority group number (5 bits)
* * Creates number mask for the priority group
* * Creates high mask for the specific priority (3 bits)
* - For systems with <=32 priority levels:
* * Creates a simple number mask for the priority
* - Sets thread state to SUSPEND to prepare for later activation
*
* @note This function must be called before a thread can be scheduled.
* It prepares the thread's priority-related data structures.
* Normally, there isn't anyone racing with us so this operation is lockless
*/
void rt_sched_thread_startup(struct rt_thread *thread)
{
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */
RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).number;
RT_SCHED_PRIV(thread).high_mask = 1L << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */
#else
RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).current_priority;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
/* change thread stat, so we can resume it */
RT_SCHED_CTX(thread).stat = RT_THREAD_SUSPEND;
}
/**
* @brief Initialize thread's scheduling private data
*
* @param thread Pointer to the thread control block
* @param tick Initial time slice value for the thread
* @param priority Initial priority of the thread
*
* @details This function:
* - Initializes the thread's list node
* - Sets initial and current priority (must be < RT_THREAD_PRIORITY_MAX)
* - Initializes priority masks (number_mask, number, high_mask for >32 priorities)
* - Sets initial and remaining time slice ticks
*/
void rt_sched_thread_init_priv(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority)
{
rt_list_init(&RT_THREAD_LIST_NODE(thread));
/* priority init */
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
RT_SCHED_PRIV(thread).init_priority = priority;
RT_SCHED_PRIV(thread).current_priority = priority;
/* don't add to scheduler queue as init thread */
RT_SCHED_PRIV(thread).number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = 0;
RT_SCHED_PRIV(thread).high_mask = 0;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
/* tick init */
RT_SCHED_PRIV(thread).init_tick = tick;
RT_SCHED_PRIV(thread).remaining_tick = tick;
}
/**
* @brief This function will insert a thread to the system ready queue. The state of
* thread will be set as READY and the thread will be removed from suspend queue.
*
* @param thread is the thread to be inserted.
*
* @note Please do not invoke this function in user application.
*/
void rt_sched_insert_thread(struct rt_thread *thread)
{
rt_base_t level;
RT_ASSERT(thread != RT_NULL);
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* it's current thread, it should be RUNNING thread */
if (thread == rt_current_thread)
{
RT_SCHED_CTX(thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK);
goto __exit;
}
/* READY thread, insert to ready queue */
RT_SCHED_CTX(thread).stat = RT_THREAD_READY | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK);
/* there is no time slices left(YIELD), inserting thread before ready list*/
if((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0)
{
rt_list_insert_before(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]),
&RT_THREAD_LIST_NODE(thread));
}
/* there are some time slices left, inserting thread after ready list to schedule it firstly at next time*/
else
{
rt_list_insert_after(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]),
&RT_THREAD_LIST_NODE(thread));
}
LOG_D("insert thread[%.*s], the priority: %d",
RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(rt_current_thread).current_priority);
/* set priority mask */
#if RT_THREAD_PRIORITY_MAX > 32
rt_thread_ready_table[RT_SCHED_PRIV(thread).number] |= RT_SCHED_PRIV(thread).high_mask;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
rt_thread_ready_priority_group |= RT_SCHED_PRIV(thread).number_mask;
__exit:
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
/**
* @brief This function will remove a thread from system ready queue.
*
* @param thread is the thread to be removed.
*
* @note Please do not invoke this function in user application.
*/
void rt_sched_remove_thread(struct rt_thread *thread)
{
rt_base_t level;
RT_ASSERT(thread != RT_NULL);
/* disable interrupt */
level = rt_hw_interrupt_disable();
LOG_D("remove thread[%.*s], the priority: %d",
RT_NAME_MAX, thread->parent.name,
RT_SCHED_PRIV(rt_current_thread).current_priority);
/* remove thread from ready list */
rt_list_remove(&RT_THREAD_LIST_NODE(thread));
if (rt_list_isempty(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority])))
{
#if RT_THREAD_PRIORITY_MAX > 32
rt_thread_ready_table[RT_SCHED_PRIV(thread).number] &= ~RT_SCHED_PRIV(thread).high_mask;
if (rt_thread_ready_table[RT_SCHED_PRIV(thread).number] == 0)
{
rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask;
}
#else
rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
#ifdef RT_DEBUGING_CRITICAL
static volatile int _critical_error_occurred = 0;
/**
* @brief Safely exit critical section with level checking
*
* @param critical_level The expected critical level to match current lock nest
*
* @details This function:
* - Disables interrupts to prevent preemption during check
* - Verifies the provided critical_level matches current rt_scheduler_lock_nest
* - If mismatch detected (debug mode only):
* * Sets error flag
* * Prints debug information including backtrace
* * Enters infinite loop to halt system
* - Always calls rt_exit_critical() to perform actual exit
*
* @note This is a debug version that adds safety checks for critical section exit.
*/
void rt_exit_critical_safe(rt_base_t critical_level)
{
rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
if (!_critical_error_occurred)
{
if (critical_level != rt_scheduler_lock_nest)
{
int dummy = 1;
_critical_error_occurred = 1;
rt_kprintf("%s: un-compatible critical level\n" \
"\tCurrent %d\n\tCaller %d\n",
__func__, rt_scheduler_lock_nest,
critical_level);
rt_backtrace();
while (dummy) ;
}
}
rt_hw_interrupt_enable(level);
rt_exit_critical();
}
#else /* !RT_DEBUGING_CRITICAL */
/**
* @brief Safely exit critical section (non-debug version)
* If the scheduling function is called before exiting, it will be scheduled in this function.
*
* @param critical_level The expected critical level (unused in non-debug build)
*
* @details This is the non-debug version that simply calls rt_exit_critical().
* The critical_level parameter is ignored in this implementation.
*/
void rt_exit_critical_safe(rt_base_t critical_level)
{
rt_exit_critical();
}
#endif/* RT_DEBUGING_CRITICAL */
RTM_EXPORT(rt_exit_critical_safe);
/**
* @brief Enter critical section and lock the scheduler
*
* @return rt_base_t The current critical level (nesting count)
*
* @details This function:
* - Disables interrupts to prevent preemption
* - Increments the scheduler lock nesting count
* - Returns the new nesting count as critical level
* - Re-enables interrupts while maintaining the lock
*
* @note The nesting count can go up to RT_UINT16_MAX.
* Must be paired with rt_exit_critical().
* Interrupts are only disabled during the lock operation.
*/
rt_base_t rt_enter_critical(void)
{
rt_base_t level;
rt_base_t critical_level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/*
* the maximal number of nest is RT_UINT16_MAX, which is big
* enough and does not check here
*/
rt_scheduler_lock_nest ++;
critical_level = rt_scheduler_lock_nest;
/* enable interrupt */
rt_hw_interrupt_enable(level);
return critical_level;
}
RTM_EXPORT(rt_enter_critical);
/**
* @brief Exit critical section and unlock scheduler
* If the scheduling function is called before exiting, it will be scheduled in this function.
*
* @details This function:
* - Decrements the scheduler lock nesting count
* - If nesting count reaches zero:
* * Resets the nesting count
* * Re-enables interrupts
* * Triggers a scheduler run if current thread exists
* - If nesting count still positive:
* * Just re-enables interrupts while maintaining lock
*
* @note Must be paired with rt_enter_critical().
* Interrupts are only disabled during the lock operation.
* Scheduling only occurs when fully unlocked (nest=0)
*/
void rt_exit_critical(void)
{
rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
rt_scheduler_lock_nest --;
if (rt_scheduler_lock_nest <= 0)
{
rt_scheduler_lock_nest = 0;
/* enable interrupt */
rt_hw_interrupt_enable(level);
if (IS_CRITICAL_SWITCH_PEND())
{
CLR_CRITICAL_SWITCH_FLAG();
/* if scheduler is started and needs to be scheduled, do a schedule */
rt_schedule();
}
}
else
{
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
}
RTM_EXPORT(rt_exit_critical);
/**
* @brief Get the scheduler lock level.
*
* @return the level of the scheduler lock. 0 means unlocked.
*/
rt_uint16_t rt_critical_level(void)
{
return rt_scheduler_lock_nest;
}
RTM_EXPORT(rt_critical_level);
rt_err_t rt_sched_thread_bind_cpu(struct rt_thread *thread, int cpu)
{
return -RT_EINVAL;
}
/**@}*/
/**@endcond*/
+680
View File
@@ -0,0 +1,680 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017/10/5 Bernard the first version
* 2018/09/17 Jesven fix: in _signal_deliver RT_THREAD_STAT_MASK to RT_THREAD_STAT_SIGNAL_MASK
* 2018/11/22 Jesven in smp version rt_hw_context_switch_to add a param
*/
#include <stdint.h>
#include <string.h>
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_SIGNALS
#ifndef RT_SIG_INFO_MAX
#ifdef ARCH_CPU_64BIT
#define RT_SIG_INFO_MAX 64
#else
#define RT_SIG_INFO_MAX 32
#endif /* ARCH_CPU_64BIT */
#endif /* RT_SIG_INFO_MAX */
#define DBG_TAG "SIGN"
#define DBG_LVL DBG_WARNING
#include <rtdbg.h>
#ifdef RT_USING_MUSLLIBC
#define sig_mask(sig_no) (1u << (sig_no - 1))
#else
#define sig_mask(sig_no) (1u << sig_no)
#endif
#define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX)
static struct rt_spinlock _thread_signal_lock = RT_SPINLOCK_INIT;
struct siginfo_node
{
siginfo_t si;
struct rt_slist_node list;
};
static struct rt_mempool *_siginfo_pool;
static void _signal_deliver(rt_thread_t tid);
void rt_thread_handle_sig(rt_bool_t clean_state);
static void _signal_default_handler(int signo)
{
RT_UNUSED(signo);
LOG_I("handled signo[%d] with default action.", signo);
return ;
}
static void _signal_entry(void *parameter)
{
RT_UNUSED(parameter);
rt_thread_t tid = rt_thread_self();
/* handle signal */
rt_thread_handle_sig(RT_FALSE);
#ifdef RT_USING_SMP
#else
/* return to thread */
tid->sp = tid->sig_ret;
tid->sig_ret = RT_NULL;
#endif /* RT_USING_SMP */
LOG_D("switch back to: 0x%08x\n", tid->sp);
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL;
#ifdef RT_USING_SMP
rt_hw_context_switch_to((rt_uintptr_t)&parameter, tid);
#else
rt_hw_context_switch_to((rt_uintptr_t)&(tid->sp));
#endif /* RT_USING_SMP */
}
/*
* To deliver a signal to thread, there are cases:
* 1. When thread is suspended, function resumes thread and
* set signal stat;
* 2. When thread is ready:
* - If function delivers a signal to self thread, just handle
* it.
* - If function delivers a signal to another ready thread, OS
* should build a slice context to handle it.
*/
static void _signal_deliver(rt_thread_t tid)
{
rt_base_t level;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
/* thread is not interested in pended signals */
if (!(tid->sig_pending & tid->sig_mask))
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return;
}
if ((RT_SCHED_CTX(tid).stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)
{
/* resume thread to handle signal */
#ifdef RT_USING_SMART
rt_thread_wakeup(tid);
#else
rt_thread_resume(tid);
#endif
/* add signal state */
RT_SCHED_CTX(tid).stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* re-schedule */
rt_schedule();
}
else
{
if (tid == rt_thread_self())
{
/* add signal state */
RT_SCHED_CTX(tid).stat |= RT_THREAD_STAT_SIGNAL;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* do signal action in self thread context */
if (rt_interrupt_get_nest() == 0)
{
rt_thread_handle_sig(RT_TRUE);
}
}
else if (!((RT_SCHED_CTX(tid).stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL))
{
/* add signal state */
RT_SCHED_CTX(tid).stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);
#ifdef RT_USING_SMP
{
int cpu_id;
cpu_id = RT_SCHED_CTX(tid).oncpu;
if ((cpu_id != RT_CPU_DETACHED) && (cpu_id != rt_cpu_get_id()))
{
rt_uint32_t cpu_mask;
cpu_mask = RT_CPU_MASK ^ (1 << cpu_id);
rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);
}
}
#else
/* point to the signal handle entry */
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
tid->sig_ret = tid->sp;
tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL,
(void *)((char *)tid->sig_ret - 32), RT_NULL);
#endif /* RT_USING_SMP */
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
LOG_D("signal stack pointer @ 0x%08x", tid->sp);
/* re-schedule */
rt_schedule();
}
else
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
}
}
#ifdef RT_USING_SMP
void *rt_signal_check(void* context)
{
rt_sched_lock_level_t level;
int cpu_id;
struct rt_cpu* pcpu;
struct rt_thread *current_thread;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
cpu_id = rt_cpu_get_id();
pcpu = rt_cpu_index(cpu_id);
current_thread = pcpu->current_thread;
if (pcpu->irq_nest)
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return context;
}
if (current_thread->cpus_lock_nest == 1)
{
if (RT_SCHED_CTX(current_thread).stat & RT_THREAD_STAT_SIGNAL_PENDING)
{
void *sig_context;
RT_SCHED_CTX(current_thread).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
sig_context = rt_hw_stack_init((void *)_signal_entry, context,
(void*)((char*)context - 32), RT_NULL);
return sig_context;
}
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return context;
}
#endif /* RT_USING_SMP */
/**
* @brief This function will install a processing function to a specific
* signal and return the old processing function of this signal.
*
* @note This function needs to be used in conjunction with the
* rt_signal_unmask() function to make the signal effective.
*
* @see rt_signal_unmask()
*
* @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX).
*
* @param handler is sets the processing of signal value.
*
* @return Return the old processing function of this signal. ONLY When the
* return value is SIG_ERR, the operation is failed.
*/
rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler)
{
rt_base_t level;
rt_sighandler_t old = RT_NULL;
rt_thread_t tid = rt_thread_self();
if (!sig_valid(signo)) return SIG_ERR;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_vectors == RT_NULL)
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
rt_thread_alloc_sig(tid);
level = rt_spin_lock_irqsave(&_thread_signal_lock);
}
if (tid->sig_vectors)
{
old = tid->sig_vectors[signo];
if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL;
else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler;
else tid->sig_vectors[signo] = handler;
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return old;
}
/**
* @brief This function will block the specified signal.
*
* @note This function will block the specified signal, even if the
* rt_thread_kill() function is called to send this signal to
* the current thread, it will no longer take effect.
*
* @see rt_thread_kill()
*
* @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX).
*/
void rt_signal_mask(int signo)
{
rt_base_t level;
rt_thread_t tid = rt_thread_self();
level = rt_spin_lock_irqsave(&_thread_signal_lock);
tid->sig_mask &= ~sig_mask(signo);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
/**
* @brief This function will unblock the specified signal.
*
* @note This function will unblock the specified signal. After calling
* the rt_thread_kill() function to send this signal to the current
* thread, it will take effect.
*
* @see rt_thread_kill()
*
* @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX).
*/
void rt_signal_unmask(int signo)
{
rt_base_t level;
rt_thread_t tid = rt_thread_self();
level = rt_spin_lock_irqsave(&_thread_signal_lock);
tid->sig_mask |= sig_mask(signo);
/* let thread handle pended signals */
if (tid->sig_mask & tid->sig_pending)
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
_signal_deliver(tid);
}
else
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
}
/**
* @brief This function will wait for the arrival of the set signal. If it does not wait for this signal, the thread will be
* suspended until it waits for this signal or the waiting time exceeds the specified timeout: timeout.
*
* @param set is the set of signal values to be waited for. Use the function
* sigaddset() to add the signal.
*
* @param si is a pointer to the received signal info. If you don't care about this value, you can use RT_NULL to set.
*
* @param timeout is a timeout period (unit: an OS tick).
*
* @return Return the operation status. When the return value is RT_EOK, the operation is successful.
* If the return value is any other values, it means that the signal wait failed.
*/
int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout)
{
int ret = RT_EOK;
rt_base_t level;
rt_thread_t tid = rt_thread_self();
struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL;
/* current context checking */
RT_DEBUG_IN_THREAD_CONTEXT;
/* parameters check */
if (set == NULL || *set == 0 || si == NULL )
{
ret = -RT_EINVAL;
goto __done_return;
}
/* clear siginfo to avoid unknown value */
memset(si, 0x0, sizeof(rt_siginfo_t));
level = rt_spin_lock_irqsave(&_thread_signal_lock);
/* already pending */
if (tid->sig_pending & *set) goto __done;
if (timeout == 0)
{
ret = -RT_ETIMEOUT;
goto __done_int;
}
/* suspend self thread */
rt_thread_suspend_with_flag(tid, RT_UNINTERRUPTIBLE);
/* set thread stat as waiting for signal */
RT_SCHED_CTX(tid).stat |= RT_THREAD_STAT_SIGNAL_WAIT;
/* start timeout timer */
if (timeout != RT_WAITING_FOREVER)
{
/* reset the timeout of thread timer and start it */
rt_timer_control(&(tid->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&timeout);
rt_timer_start(&(tid->thread_timer));
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* do thread scheduling */
rt_schedule();
level = rt_spin_lock_irqsave(&_thread_signal_lock);
/* remove signal waiting flag */
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL_WAIT;
/* check errno of thread */
if (tid->error == -RT_ETIMEOUT)
{
tid->error = RT_EOK;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* timer timeout */
ret = -RT_ETIMEOUT;
goto __done_return;
}
__done:
/* to get the first matched pending signals */
si_node = (struct siginfo_node *)tid->si_list;
while (si_node)
{
int signo;
signo = si_node->si.si_signo;
if (sig_mask(signo) & *set)
{
*si = si_node->si;
LOG_D("sigwait: %d sig raised!", signo);
if (si_prev) si_prev->list.next = si_node->list.next;
else
{
struct siginfo_node *node_next;
if (si_node->list.next)
{
node_next = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
tid->si_list = node_next;
}
else
{
tid->si_list = RT_NULL;
}
}
/* clear pending */
tid->sig_pending &= ~sig_mask(signo);
rt_mp_free(si_node);
break;
}
si_prev = si_node;
if (si_node->list.next)
{
si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
}
else
{
si_node = RT_NULL;
}
}
__done_int:
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
__done_return:
return ret;
}
void rt_thread_handle_sig(rt_bool_t clean_state)
{
rt_base_t level;
rt_thread_t tid = rt_thread_self();
struct siginfo_node *si_node;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_pending & tid->sig_mask)
{
/* if thread is not waiting for signal */
if (!(RT_SCHED_CTX(tid).stat & RT_THREAD_STAT_SIGNAL_WAIT))
{
while (tid->sig_pending & tid->sig_mask)
{
int signo, error;
rt_sighandler_t handler;
si_node = (struct siginfo_node *)tid->si_list;
if (!si_node) break;
/* remove this sig info node from list */
if (si_node->list.next == RT_NULL)
tid->si_list = RT_NULL;
else
tid->si_list = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
signo = si_node->si.si_signo;
handler = tid->sig_vectors[signo];
tid->sig_pending &= ~sig_mask(signo);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
LOG_D("handle signal: %d, handler 0x%08x", signo, handler);
if (handler) handler(signo);
level = rt_spin_lock_irqsave(&_thread_signal_lock);
error = -RT_EINTR;
rt_mp_free(si_node); /* release this siginfo node */
/* set errno in thread tcb */
tid->error = error;
}
/* whether clean signal status */
if (clean_state == RT_TRUE)
{
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL;
}
else
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return;
}
}
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
void rt_thread_alloc_sig(rt_thread_t tid)
{
int index;
rt_bool_t need_free = RT_FALSE;
rt_base_t level;
rt_sighandler_t *vectors;
vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX);
RT_ASSERT(vectors != RT_NULL);
for (index = 0; index < RT_SIG_MAX; index ++)
{
vectors[index] = _signal_default_handler;
}
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_vectors == RT_NULL)
{
tid->sig_vectors = vectors;
}
else
{
need_free = RT_TRUE;
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
if (need_free)
{
rt_free(vectors);
}
}
void rt_thread_free_sig(rt_thread_t tid)
{
rt_base_t level;
struct siginfo_node *si_node;
rt_sighandler_t *sig_vectors;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
si_node = (struct siginfo_node *)tid->si_list;
tid->si_list = RT_NULL;
sig_vectors = tid->sig_vectors;
tid->sig_vectors = RT_NULL;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
if (si_node)
{
struct rt_slist_node *node;
struct rt_slist_node *node_to_free;
LOG_D("free signal info list");
node = &(si_node->list);
do
{
node_to_free = node;
node = node->next;
si_node = rt_slist_entry(node_to_free, struct siginfo_node, list);
rt_mp_free(si_node);
} while (node);
}
if (sig_vectors)
{
RT_KERNEL_FREE(sig_vectors);
}
}
/**
* @brief This function can be used to send any signal to any thread.
*
* @param tid is a pointer to the thread that receives the signal.
*
* @param sig is a specific signal value (range: 0 ~ RT_SIG_MAX).
*
* @return Return the operation status. When the return value is RT_EOK, the operation is successful.
* If the return value is any other values, it means that the signal send failed.
*/
int rt_thread_kill(rt_thread_t tid, int sig)
{
siginfo_t si;
rt_base_t level;
struct siginfo_node *si_node;
RT_ASSERT(tid != RT_NULL);
if (!sig_valid(sig)) return -RT_EINVAL;
LOG_I("send signal: %d", sig);
si.si_signo = sig;
si.si_code = SI_USER;
si.si_value.sival_ptr = RT_NULL;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_pending & sig_mask(sig))
{
/* whether already emits this signal? */
struct rt_slist_node *node;
struct siginfo_node *entry;
si_node = (struct siginfo_node *)tid->si_list;
if (si_node)
node = (struct rt_slist_node *)&si_node->list;
else
node = RT_NULL;
/* update sig info */
for (; (node) != RT_NULL; node = node->next)
{
entry = rt_slist_entry(node, struct siginfo_node, list);
if (entry->si.si_signo == sig)
{
memcpy(&(entry->si), &si, sizeof(siginfo_t));
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return 0;
}
}
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
si_node = (struct siginfo_node *) rt_mp_alloc(_siginfo_pool, 0);
if (si_node)
{
rt_slist_init(&(si_node->list));
memcpy(&(si_node->si), &si, sizeof(siginfo_t));
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->si_list)
{
struct siginfo_node *si_list;
si_list = (struct siginfo_node *)tid->si_list;
rt_slist_append(&(si_list->list), &(si_node->list));
}
else
{
tid->si_list = si_node;
}
/* a new signal */
tid->sig_pending |= sig_mask(sig);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
else
{
LOG_E("The allocation of signal info node failed.");
return -RT_EEMPTY;
}
/* deliver signal to this thread */
_signal_deliver(tid);
return RT_EOK;
}
int rt_system_signal_init(void)
{
_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node));
if (_siginfo_pool == RT_NULL)
{
LOG_E("create memory pool for signal info failed.");
RT_ASSERT(0);
}
return 0;
}
#endif /* RT_USING_SIGNALS */
+856
View File
@@ -0,0 +1,856 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* File : slab.c
*
* Change Logs:
* Date Author Notes
* 2008-07-12 Bernard the first version
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-23 yi.qiu add module memory allocator
* 2010-12-18 yi.qiu fix zone release bug
*/
/*
* KERN_SLABALLOC.C - Kernel SLAB memory allocator
*
* Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
*
* This code is derived from software contributed to The DragonFly Project
* by Matthew Dillon <dillon@backplane.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of The DragonFly Project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_SLAB
#define DBG_TAG "kernel.slab"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/*
* slab allocator implementation
*
* A slab allocator reserves a ZONE for each chunk size, then lays the
* chunks out in an array within the zone. Allocation and deallocation
* is nearly instantanious, and fragmentation/overhead losses are limited
* to a fixed worst-case amount.
*
* The downside of this slab implementation is in the chunk size
* multiplied by the number of zones. ~80 zones * 128K = 10MB of VM per cpu.
* In a kernel implementation all this memory will be physical so
* the zone size is adjusted downward on machines with less physical
* memory. The upside is that overhead is bounded... this is the *worst*
* case overhead.
*
* Slab management is done on a per-cpu basis and no locking or mutexes
* are required, only a critical section. When one cpu frees memory
* belonging to another cpu's slab manager an asynchronous IPI message
* will be queued to execute the operation. In addition, both the
* high level slab allocator and the low level zone allocator optimize
* M_ZERO requests, and the slab allocator does not have to pre initialize
* the linked list of chunks.
*
* XXX Balancing is needed between cpus. Balance will be handled through
* asynchronous IPIs primarily by reassigning the z_Cpu ownership of chunks.
*
* XXX If we have to allocate a new zone and M_USE_RESERVE is set, use of
* the new zone should be restricted to M_USE_RESERVE requests only.
*
* Alloc Size Chunking Number of zones
* 0-127 8 16
* 128-255 16 8
* 256-511 32 8
* 512-1023 64 8
* 1024-2047 128 8
* 2048-4095 256 8
* 4096-8191 512 8
* 8192-16383 1024 8
* 16384-32767 2048 8
* (if RT_MM_PAGE_SIZE is 4K the maximum zone allocation is 16383)
*
* Allocations >= zone_limit go directly to kmem.
*
* API REQUIREMENTS AND SIDE EFFECTS
*
* To operate as a drop-in replacement to the FreeBSD-4.x malloc() we
* have remained compatible with the following API requirements:
*
* + small power-of-2 sized allocations are power-of-2 aligned (kern_tty)
* + all power-of-2 sized allocations are power-of-2 aligned (twe)
* + malloc(0) is allowed and returns non-RT_NULL (ahc driver)
* + ability to allocate arbitrarily large chunks of memory
*/
#define ZALLOC_SLAB_MAGIC 0x51ab51ab
#define ZALLOC_ZONE_LIMIT (16 * 1024) /* max slab-managed alloc */
#define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */
#define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */
#define ZONE_RELEASE_THRESH 2 /* threshold number of zones */
/*
* Misc constants. Note that allocations that are exact multiples of
* RT_MM_PAGE_SIZE, or exceed the zone limit, fall through to the kmem module.
*/
#define MIN_CHUNK_SIZE 8 /* in bytes */
#define MIN_CHUNK_MASK (MIN_CHUNK_SIZE - 1)
/*
* Array of descriptors that describe the contents of each page
*/
#define PAGE_TYPE_FREE 0x00
#define PAGE_TYPE_SMALL 0x01
#define PAGE_TYPE_LARGE 0x02
#define btokup(addr) \
(&slab->memusage[((rt_uintptr_t)(addr) - slab->heap_start) >> RT_MM_PAGE_BITS])
/**
* Base structure of slab memory object
*/
/*
* The IN-BAND zone header is placed at the beginning of each zone.
*/
struct rt_slab_zone
{
rt_uint32_t z_magic; /**< magic number for sanity check */
rt_uint32_t z_nfree; /**< total free chunks / ualloc space in zone */
rt_uint32_t z_nmax; /**< maximum free chunks */
struct rt_slab_zone *z_next; /**< zoneary[] link if z_nfree non-zero */
rt_uint8_t *z_baseptr; /**< pointer to start of chunk array */
rt_uint32_t z_uindex; /**< current initial allocation index */
rt_uint32_t z_chunksize; /**< chunk size for validation */
rt_uint32_t z_zoneindex; /**< zone index */
struct rt_slab_chunk *z_freechunk; /**< free chunk list */
};
/*
* Chunk structure for free elements
*/
struct rt_slab_chunk
{
struct rt_slab_chunk *c_next;
};
struct rt_slab_memusage
{
rt_uint32_t type: 2 ; /**< page type */
rt_uint32_t size: 30; /**< pages allocated or offset from zone */
};
/*
* slab page allocator
*/
struct rt_slab_page
{
struct rt_slab_page *next; /**< next valid page */
rt_size_t page; /**< number of page */
/* dummy */
char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_slab_page *) + sizeof(rt_size_t))];
};
#define RT_SLAB_NZONES 72 /* number of zones */
/*
* slab object
*/
struct rt_slab
{
struct rt_memory parent; /**< inherit from rt_memory */
rt_uintptr_t heap_start; /**< memory start address */
rt_uintptr_t heap_end; /**< memory end address */
struct rt_slab_memusage *memusage;
struct rt_slab_zone *zone_array[RT_SLAB_NZONES]; /* linked list of zones NFree > 0 */
struct rt_slab_zone *zone_free; /* whole zones that have become free */
rt_uint32_t zone_free_cnt;
rt_uint32_t zone_size;
rt_uint32_t zone_limit;
rt_uint32_t zone_page_cnt;
struct rt_slab_page *page_list;
};
/**
* @brief Alloc memory size by page.
*
* @param m the slab memory management object.
*
* @param npages the number of pages.
*/
void *rt_slab_page_alloc(rt_slab_t m, rt_size_t npages)
{
struct rt_slab_page *b, *n;
struct rt_slab_page **prev;
struct rt_slab *slab = (struct rt_slab *)m;
if (npages == 0)
return RT_NULL;
for (prev = &slab->page_list; (b = *prev) != RT_NULL; prev = &(b->next))
{
if (b->page > npages)
{
/* splite pages */
n = b + npages;
n->next = b->next;
n->page = b->page - npages;
*prev = n;
break;
}
if (b->page == npages)
{
/* this node fit, remove this node */
*prev = b->next;
break;
}
}
return b;
}
/**
* @brief Free memory by page.
*
* @param m the slab memory management object.
*
* @param addr is the head address of first page.
*
* @param npages is the number of pages.
*/
void rt_slab_page_free(rt_slab_t m, void *addr, rt_size_t npages)
{
struct rt_slab_page *b, *n;
struct rt_slab_page **prev;
struct rt_slab *slab = (struct rt_slab *)m;
RT_ASSERT(addr != RT_NULL);
RT_ASSERT((rt_uintptr_t)addr % RT_MM_PAGE_SIZE == 0);
RT_ASSERT(npages != 0);
n = (struct rt_slab_page *)addr;
for (prev = &slab->page_list; (b = *prev) != RT_NULL; prev = &(b->next))
{
RT_ASSERT(b->page > 0);
RT_ASSERT(b > n || b + b->page <= n);
if (b + b->page == n)
{
if (b + (b->page += npages) == b->next)
{
b->page += b->next->page;
b->next = b->next->next;
}
return;
}
if (b == n + npages)
{
n->page = b->page + npages;
n->next = b->next;
*prev = n;
return;
}
if (b > n + npages)
break;
}
n->page = npages;
n->next = b;
*prev = n;
}
/*
* Initialize the page allocator
*/
static void rt_slab_page_init(struct rt_slab *slab, void *addr, rt_size_t npages)
{
RT_ASSERT(addr != RT_NULL);
RT_ASSERT(npages != 0);
slab->page_list = RT_NULL;
rt_slab_page_free((rt_slab_t)(&slab->parent), addr, npages);
}
/**
* @brief This function will init slab memory management algorithm
*
* @param name is the name of the slab memory management object.
*
* @param begin_addr the beginning address of system page.
*
* @param size is the size of the memory.
*
* @return Return a pointer to the slab memory object.
*/
rt_slab_t rt_slab_init(const char *name, void *begin_addr, rt_size_t size)
{
rt_uint32_t limsize, npages;
rt_uintptr_t start_addr, begin_align, end_align;
struct rt_slab *slab;
slab = (struct rt_slab *)RT_ALIGN((rt_uintptr_t)begin_addr, RT_ALIGN_SIZE);
start_addr = (rt_uintptr_t)slab + sizeof(*slab);
/* align begin and end addr to page */
begin_align = RT_ALIGN((rt_uintptr_t)start_addr, RT_MM_PAGE_SIZE);
end_align = RT_ALIGN_DOWN((rt_uintptr_t)begin_addr + size, RT_MM_PAGE_SIZE);
if (begin_align >= end_align)
{
rt_kprintf("slab init errr. wrong address[0x%x - 0x%x]\n",
(rt_uintptr_t)begin_addr, (rt_uintptr_t)begin_addr + size);
return RT_NULL;
}
limsize = end_align - begin_align;
npages = limsize / RT_MM_PAGE_SIZE;
LOG_D("heap[0x%x - 0x%x], size 0x%x, 0x%x pages",
begin_align, end_align, limsize, npages);
rt_memset(slab, 0, sizeof(*slab));
/* initialize slab memory object */
rt_object_init(&(slab->parent.parent), RT_Object_Class_Memory, name);
slab->parent.algorithm = "slab";
slab->parent.address = begin_align;
slab->parent.total = limsize;
slab->parent.used = 0;
slab->parent.max = 0;
slab->heap_start = begin_align;
slab->heap_end = end_align;
/* init pages */
rt_slab_page_init(slab, (void *)slab->heap_start, npages);
/* calculate zone size */
slab->zone_size = ZALLOC_MIN_ZONE_SIZE;
while (slab->zone_size < ZALLOC_MAX_ZONE_SIZE && (slab->zone_size << 1) < (limsize / 1024))
slab->zone_size <<= 1;
slab->zone_limit = slab->zone_size / 4;
if (slab->zone_limit > ZALLOC_ZONE_LIMIT)
slab->zone_limit = ZALLOC_ZONE_LIMIT;
slab->zone_page_cnt = slab->zone_size / RT_MM_PAGE_SIZE;
LOG_D("zone size 0x%x, zone page count 0x%x",
slab->zone_size, slab->zone_page_cnt);
/* allocate slab->memusage array */
limsize = npages * sizeof(struct rt_slab_memusage);
limsize = RT_ALIGN(limsize, RT_MM_PAGE_SIZE);
slab->memusage = rt_slab_page_alloc((rt_slab_t)(&slab->parent), limsize / RT_MM_PAGE_SIZE);
LOG_D("slab->memusage 0x%x, size 0x%x",
(rt_uintptr_t)slab->memusage, limsize);
return &slab->parent;
}
RTM_EXPORT(rt_slab_init);
/**
* @brief This function will remove a slab object from the system.
*
* @param m the slab memory management object.
*
* @return RT_EOK
*/
rt_err_t rt_slab_detach(rt_slab_t m)
{
struct rt_slab *slab = (struct rt_slab *)m;
RT_ASSERT(slab != RT_NULL);
RT_ASSERT(rt_object_get_type(&slab->parent.parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&slab->parent.parent));
rt_object_detach(&(slab->parent.parent));
return RT_EOK;
}
RTM_EXPORT(rt_slab_detach);
/*
* Calculate the zone index for the allocation request size and set the
* allocation request size to that particular zone's chunk size.
*/
rt_inline int zoneindex(rt_size_t *bytes)
{
/* unsigned for shift opt */
rt_uintptr_t n = (rt_uintptr_t)(*bytes);
if (n < 128)
{
*bytes = n = (n + 7) & ~7;
/* 8 byte chunks, 16 zones */
return (n / 8 - 1);
}
if (n < 256)
{
*bytes = n = (n + 15) & ~15;
return (n / 16 + 7);
}
if (n < 8192)
{
if (n < 512)
{
*bytes = n = (n + 31) & ~31;
return (n / 32 + 15);
}
if (n < 1024)
{
*bytes = n = (n + 63) & ~63;
return (n / 64 + 23);
}
if (n < 2048)
{
*bytes = n = (n + 127) & ~127;
return (n / 128 + 31);
}
if (n < 4096)
{
*bytes = n = (n + 255) & ~255;
return (n / 256 + 39);
}
*bytes = n = (n + 511) & ~511;
return (n / 512 + 47);
}
if (n < 16384)
{
*bytes = n = (n + 1023) & ~1023;
return (n / 1024 + 55);
}
rt_kprintf("Unexpected byte count %d", n);
return 0;
}
/**
* @addtogroup group_memory_management
*/
/**@{*/
/**
* @brief This function will allocate a block from slab object.
*
* @note the RT_NULL is returned if
* - the nbytes is less than zero.
* - there is no nbytes sized memory valid in system.
*
* @param m the slab memory management object.
*
* @param size is the size of memory to be allocated.
*
* @return the allocated memory.
*/
void *rt_slab_alloc(rt_slab_t m, rt_size_t size)
{
struct rt_slab_zone *z;
rt_int32_t zi;
struct rt_slab_chunk *chunk;
struct rt_slab_memusage *kup;
struct rt_slab *slab = (struct rt_slab *)m;
/* zero size, return RT_NULL */
if (size == 0)
return RT_NULL;
/*
* Handle large allocations directly. There should not be very many of
* these so performance is not a big issue.
*/
if (size >= slab->zone_limit)
{
size = RT_ALIGN(size, RT_MM_PAGE_SIZE);
chunk = rt_slab_page_alloc(m, size >> RT_MM_PAGE_BITS);
if (chunk == RT_NULL)
return RT_NULL;
/* set kup */
kup = btokup(chunk);
kup->type = PAGE_TYPE_LARGE;
kup->size = size >> RT_MM_PAGE_BITS;
LOG_D("alloc a large memory 0x%x, page cnt %d, kup %d",
size,
size >> RT_MM_PAGE_BITS,
((rt_uintptr_t)chunk - slab->heap_start) >> RT_MM_PAGE_BITS);
/* mem stat */
slab->parent.used += size;
if (slab->parent.used > slab->parent.max)
slab->parent.max = slab->parent.used;
return chunk;
}
/*
* Attempt to allocate out of an existing zone. First try the free list,
* then allocate out of unallocated space. If we find a good zone move
* it to the head of the list so later allocations find it quickly
* (we might have thousands of zones in the list).
*
* Note: zoneindex() will panic of size is too large.
*/
zi = zoneindex(&size);
RT_ASSERT(zi < RT_SLAB_NZONES);
LOG_D("try to alloc 0x%x on zone: %d", size, zi);
if ((z = slab->zone_array[zi]) != RT_NULL)
{
RT_ASSERT(z->z_nfree > 0);
/* Remove us from the zone_array[] when we become full */
if (--z->z_nfree == 0)
{
slab->zone_array[zi] = z->z_next;
z->z_next = RT_NULL;
}
/*
* No chunks are available but nfree said we had some memory, so
* it must be available in the never-before-used-memory area
* governed by uindex. The consequences are very serious if our zone
* got corrupted so we use an explicit rt_kprintf rather then a KASSERT.
*/
if (z->z_uindex + 1 != z->z_nmax)
{
z->z_uindex = z->z_uindex + 1;
chunk = (struct rt_slab_chunk *)(z->z_baseptr + z->z_uindex * size);
}
else
{
/* find on free chunk list */
chunk = z->z_freechunk;
/* remove this chunk from list */
z->z_freechunk = z->z_freechunk->c_next;
}
/* mem stats */
slab->parent.used += z->z_chunksize;
if (slab->parent.used > slab->parent.max)
slab->parent.max = slab->parent.used;
return chunk;
}
/*
* If all zones are exhausted we need to allocate a new zone for this
* index.
*
* At least one subsystem, the tty code (see CROUND) expects power-of-2
* allocations to be power-of-2 aligned. We maintain compatibility by
* adjusting the base offset below.
*/
{
rt_uint32_t off;
if ((z = slab->zone_free) != RT_NULL)
{
/* remove zone from free zone list */
slab->zone_free = z->z_next;
-- slab->zone_free_cnt;
}
else
{
/* allocate a zone from page */
z = rt_slab_page_alloc(m, slab->zone_size / RT_MM_PAGE_SIZE);
if (z == RT_NULL)
{
return RT_NULL;
}
LOG_D("alloc a new zone: 0x%x",
(rt_uintptr_t)z);
/* set message usage */
for (off = 0, kup = btokup(z); off < slab->zone_page_cnt; off ++)
{
kup->type = PAGE_TYPE_SMALL;
kup->size = off;
kup ++;
}
}
/* clear to zero */
rt_memset(z, 0, sizeof(struct rt_slab_zone));
/* offset of slab zone struct in zone */
off = sizeof(struct rt_slab_zone);
/*
* Guarentee power-of-2 alignment for power-of-2-sized chunks.
* Otherwise just 8-byte align the data.
*/
if ((size | (size - 1)) + 1 == (size << 1))
off = (off + size - 1) & ~(size - 1);
else
off = (off + MIN_CHUNK_MASK) & ~MIN_CHUNK_MASK;
z->z_magic = ZALLOC_SLAB_MAGIC;
z->z_zoneindex = zi;
z->z_nmax = (slab->zone_size - off) / size;
z->z_nfree = z->z_nmax - 1;
z->z_baseptr = (rt_uint8_t *)z + off;
z->z_uindex = 0;
z->z_chunksize = size;
chunk = (struct rt_slab_chunk *)(z->z_baseptr + z->z_uindex * size);
/* link to zone array */
z->z_next = slab->zone_array[zi];
slab->zone_array[zi] = z;
/* mem stats */
slab->parent.used += z->z_chunksize;
if (slab->parent.used > slab->parent.max)
slab->parent.max = slab->parent.used;
}
return chunk;
}
RTM_EXPORT(rt_slab_alloc);
/**
* @brief This function will change the size of previously allocated memory block.
*
* @param m the slab memory management object.
*
* @param ptr is the previously allocated memory block.
*
* @param size is the new size of memory block.
*
* @return the allocated memory.
*/
void *rt_slab_realloc(rt_slab_t m, void *ptr, rt_size_t size)
{
void *nptr;
struct rt_slab_zone *z;
struct rt_slab_memusage *kup;
struct rt_slab *slab = (struct rt_slab *)m;
if (ptr == RT_NULL)
return rt_slab_alloc(m, size);
if (size == 0)
{
rt_slab_free(m, ptr);
return RT_NULL;
}
/*
* Get the original allocation's zone. If the new request winds up
* using the same chunk size we do not have to do anything.
*/
kup = btokup((rt_uintptr_t)ptr & ~RT_MM_PAGE_MASK);
if (kup->type == PAGE_TYPE_LARGE)
{
rt_size_t osize;
osize = kup->size << RT_MM_PAGE_BITS;
if ((nptr = rt_slab_alloc(m, size)) == RT_NULL)
return RT_NULL;
rt_memcpy(nptr, ptr, size > osize ? osize : size);
rt_slab_free(m, ptr);
return nptr;
}
else if (kup->type == PAGE_TYPE_SMALL)
{
z = (struct rt_slab_zone *)(((rt_uintptr_t)ptr & ~RT_MM_PAGE_MASK) -
kup->size * RT_MM_PAGE_SIZE);
RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC);
zoneindex(&size);
if (z->z_chunksize == size)
return (ptr); /* same chunk */
/*
* Allocate memory for the new request size. Note that zoneindex has
* already adjusted the request size to the appropriate chunk size, which
* should optimize our bcopy(). Then copy and return the new pointer.
*/
if ((nptr = rt_slab_alloc(m, size)) == RT_NULL)
return RT_NULL;
rt_memcpy(nptr, ptr, size > z->z_chunksize ? z->z_chunksize : size);
rt_slab_free(m, ptr);
return nptr;
}
return RT_NULL;
}
RTM_EXPORT(rt_slab_realloc);
/**
* @brief This function will release the previous allocated memory block by rt_slab_alloc.
*
* @note The released memory block is taken back to system heap.
*
* @param m the slab memory management object.
* @param ptr is the address of memory which will be released
*/
void rt_slab_free(rt_slab_t m, void *ptr)
{
struct rt_slab_zone *z;
struct rt_slab_chunk *chunk;
struct rt_slab_memusage *kup;
struct rt_slab *slab = (struct rt_slab *)m;
/* free a RT_NULL pointer */
if (ptr == RT_NULL)
return ;
/* get memory usage */
#if (DBG_LVL == DBG_LOG)
{
rt_uintptr_t addr = ((rt_uintptr_t)ptr & ~RT_MM_PAGE_MASK);
LOG_D("free a memory 0x%x and align to 0x%x, kup index %d",
(rt_uintptr_t)ptr,
(rt_uintptr_t)addr,
((rt_uintptr_t)(addr) - slab->heap_start) >> RT_MM_PAGE_BITS);
}
#endif /* DBG_LVL == DBG_LOG */
kup = btokup((rt_uintptr_t)ptr & ~RT_MM_PAGE_MASK);
/* release large allocation */
if (kup->type == PAGE_TYPE_LARGE)
{
rt_uintptr_t size;
/* clear page counter */
size = kup->size;
kup->size = 0;
/* mem stats */
slab->parent.used -= size * RT_MM_PAGE_SIZE;
LOG_D("free large memory block 0x%x, page count %d",
(rt_uintptr_t)ptr, size);
/* free this page */
rt_slab_page_free(m, ptr, size);
return;
}
/* zone case. get out zone. */
z = (struct rt_slab_zone *)(((rt_uintptr_t)ptr & ~RT_MM_PAGE_MASK) -
kup->size * RT_MM_PAGE_SIZE);
RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC);
chunk = (struct rt_slab_chunk *)ptr;
chunk->c_next = z->z_freechunk;
z->z_freechunk = chunk;
/* mem stats */
slab->parent.used -= z->z_chunksize;
/*
* Bump the number of free chunks. If it becomes non-zero the zone
* must be added back onto the appropriate list.
*/
if (z->z_nfree++ == 0)
{
z->z_next = slab->zone_array[z->z_zoneindex];
slab->zone_array[z->z_zoneindex] = z;
}
/*
* If the zone becomes totally free, and there are other zones we
* can allocate from, move this zone to the FreeZones list. Since
* this code can be called from an IPI callback, do *NOT* try to mess
* with kernel_map here. Hysteresis will be performed at malloc() time.
*/
if (z->z_nfree == z->z_nmax &&
(z->z_next || slab->zone_array[z->z_zoneindex] != z))
{
struct rt_slab_zone **pz;
LOG_D("free zone %#x, zoneindex %d",
(rt_uintptr_t)z, z->z_zoneindex);
/* remove zone from zone array list */
for (pz = &slab->zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next)
;
*pz = z->z_next;
/* reset zone */
z->z_magic = RT_UINT32_MAX;
/* insert to free zone list */
z->z_next = slab->zone_free;
slab->zone_free = z;
++ slab->zone_free_cnt;
/* release zone to page allocator */
if (slab->zone_free_cnt > ZONE_RELEASE_THRESH)
{
register rt_uint32_t i;
z = slab->zone_free;
slab->zone_free = z->z_next;
-- slab->zone_free_cnt;
/* set message usage */
for (i = 0, kup = btokup(z); i < slab->zone_page_cnt; i ++)
{
kup->type = PAGE_TYPE_FREE;
kup->size = 0;
kup ++;
}
/* release pages */
rt_slab_page_free(m, z, slab->zone_size / RT_MM_PAGE_SIZE);
return;
}
}
}
RTM_EXPORT(rt_slab_free);
#endif /* RT_USING_SLAB */
File diff suppressed because it is too large Load Diff
+871
View File
@@ -0,0 +1,871 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-12 Bernard first version
* 2006-04-29 Bernard implement thread timer
* 2006-06-04 Bernard implement rt_timer_control
* 2006-08-10 Bernard fix the periodic timer bug
* 2006-09-03 Bernard implement rt_timer_detach
* 2009-11-11 LiJin add soft timer
* 2010-05-12 Bernard fix the timer check bug.
* 2010-11-02 Charlie re-implement tick overflow issue
* 2012-12-15 Bernard fix the next timeout issue in soft timer
* 2014-07-12 Bernard does not lock scheduler when invoking soft-timer
* timeout function.
* 2021-08-15 supperthomas add the comment
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to timer.c
* 2022-04-19 Stanley Correct descriptions
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2024-01-25 Shell add RT_TIMER_FLAG_THREAD_TIMER for timer to sync with sched
* 2024-05-01 wdfk-prog The rt_timer_check and _soft_timer_check functions are merged
*/
#include <rtthread.h>
#include <rthw.h>
#define DBG_TAG "kernel.timer"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifndef RT_USING_TIMER_ALL_SOFT
/* hard timer list */
static rt_list_t _timer_list[RT_TIMER_SKIP_LIST_LEVEL];
static struct rt_spinlock _htimer_lock;
#endif
#ifdef RT_USING_TIMER_SOFT
#ifndef RT_TIMER_THREAD_STACK_SIZE
#define RT_TIMER_THREAD_STACK_SIZE 512
#endif /* RT_TIMER_THREAD_STACK_SIZE */
#ifndef RT_TIMER_THREAD_PRIO
#define RT_TIMER_THREAD_PRIO 0
#endif /* RT_TIMER_THREAD_PRIO */
/* soft timer list */
static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
static struct rt_spinlock _stimer_lock;
static struct rt_thread _timer_thread;
static struct rt_semaphore _soft_timer_sem;
rt_align(RT_ALIGN_SIZE)
static rt_uint8_t _timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
#endif /* RT_USING_TIMER_SOFT */
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
extern void (*rt_object_take_hook)(struct rt_object *object);
extern void (*rt_object_put_hook)(struct rt_object *object);
static void (*rt_timer_enter_hook)(struct rt_timer *timer);
static void (*rt_timer_exit_hook)(struct rt_timer *timer);
/**
* @addtogroup group_hook
*/
/**@{*/
/**
* @brief This function will set a hook function on timer,
* which will be invoked when enter timer timeout callback function.
*
* @param hook is the function point of timer
*/
void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer))
{
rt_timer_enter_hook = hook;
}
/**
* @brief This function will set a hook function, which will be
* invoked when exit timer timeout callback function.
*
* @param hook is the function point of timer
*/
void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer))
{
rt_timer_exit_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
rt_inline struct rt_spinlock* _timerlock_idx(struct rt_timer *timer)
{
#ifdef RT_USING_TIMER_ALL_SOFT
return &_stimer_lock;
#else
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
return &_stimer_lock;
}
else
#endif /* RT_USING_TIMER_SOFT */
{
return &_htimer_lock;
}
#endif
}
/**
* @brief [internal] The init funtion of timer
*
* The internal called function of rt_timer_init
*
* @see rt_timer_init
*
* @param timer is timer object
*
* @param timeout is the timeout function
*
* @param parameter is the parameter of timeout function
*
* @param time is the tick of timer
*
* @param flag the flag of timer
*/
static void _timer_init(rt_timer_t timer,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
int i;
#ifdef RT_USING_TIMER_ALL_SOFT
flag |= RT_TIMER_FLAG_SOFT_TIMER;
#endif
/* set flag */
timer->parent.flag = flag;
/* set deactivated */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
timer->timeout_tick = 0;
timer->init_tick = time;
/* initialize timer list */
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_init(&(timer->row[i]));
}
}
/**
* @brief Find the next emtpy timer ticks
*
* @param timer_list is the array of time list
*
* @param timeout_tick is the next timer's ticks
*
* @return Return the operation status. If the return value is RT_EOK, the function is successfully executed.
* If the return value is any other values, it means this operation failed.
*/
static rt_err_t _timer_list_next_timeout(rt_list_t timer_list[], rt_tick_t *timeout_tick)
{
struct rt_timer *timer;
if (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
*timeout_tick = timer->timeout_tick;
return RT_EOK;
}
return -RT_ERROR;
}
/**
* @brief Remove the timer
*
* @param timer the point of the timer
*/
rt_inline void _timer_remove(rt_timer_t timer)
{
int i;
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_remove(&timer->row[i]);
}
}
#if (DBG_LVL == DBG_LOG)
/**
* @brief The number of timer
*
* @param timer the head of timer
*
* @return count of timer
*/
static int _timer_count_height(struct rt_timer *timer)
{
int i, cnt = 0;
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
if (!rt_list_isempty(&timer->row[i]))
cnt++;
}
return cnt;
}
/**
* @brief dump the all timer information
*
* @param timer_heads the head of timer
*/
void rt_timer_dump(rt_list_t timer_heads[])
{
rt_list_t *list;
for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1].next;
list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1];
list = list->next)
{
struct rt_timer *timer = rt_list_entry(list,
struct rt_timer,
row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
rt_kprintf("%d", _timer_count_height(timer));
}
rt_kprintf("\n");
}
#endif /* (DBG_LVL == DBG_LOG) */
/**
* @addtogroup group_clock_management
*/
/**@{*/
/**
* @brief This function will initialize a timer
* normally this function is used to initialize a static timer object.
*
* @param timer is the point of timer
*
* @param name is a pointer to the name of the timer
*
* @param timeout is the callback of timer
*
* @param parameter is the param of the callback
*
* @param time is timeout ticks of timer
*
* NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
*
* @param flag is the flag of timer
*
*/
void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(timeout != RT_NULL);
RT_ASSERT(time < RT_TICK_MAX / 2);
/* timer object initialization */
rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);
_timer_init(timer, timeout, parameter, time, flag);
}
RTM_EXPORT(rt_timer_init);
/**
* @brief This function will detach a timer from timer management.
*
* @param timer is the timer to be detached
*
* @return the status of detach
*/
rt_err_t rt_timer_detach(rt_timer_t timer)
{
rt_base_t level;
struct rt_spinlock *spinlock;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent));
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
_timer_remove(timer);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_spin_unlock_irqrestore(spinlock, level);
rt_object_detach(&(timer->parent));
return RT_EOK;
}
RTM_EXPORT(rt_timer_detach);
#ifdef RT_USING_HEAP
/**
* @brief This function will create a timer
*
* @param name is the name of timer
*
* @param timeout is the timeout function
*
* @param parameter is the parameter of timeout function
*
* @param time is timeout ticks of the timer
*
* NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
*
* @param flag is the flag of timer. Timer will invoke the timeout function according to the selected values of flag, if one or more of the following flags is set.
*
* RT_TIMER_FLAG_ONE_SHOT One shot timing
* RT_TIMER_FLAG_PERIODIC Periodic timing
*
* RT_TIMER_FLAG_HARD_TIMER Hardware timer
* RT_TIMER_FLAG_SOFT_TIMER Software timer
* RT_TIMER_FLAG_THREAD_TIMER Thread timer
*
* NOTE:
* You can use multiple values with "|" logical operator. By default, system will use the RT_TIME_FLAG_HARD_TIMER.
*
* @return the created timer object
*/
rt_timer_t rt_timer_create(const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
struct rt_timer *timer;
/* parameter check */
RT_ASSERT(timeout != RT_NULL);
RT_ASSERT(time < RT_TICK_MAX / 2);
/* allocate a object */
timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
if (timer == RT_NULL)
{
return RT_NULL;
}
_timer_init(timer, timeout, parameter, time, flag);
return timer;
}
RTM_EXPORT(rt_timer_create);
/**
* @brief This function will delete a timer and release timer memory
*
* @param timer the timer to be deleted
*
* @return the operation status, RT_EOK on OK; -RT_ERROR on error
*/
rt_err_t rt_timer_delete(rt_timer_t timer)
{
rt_base_t level;
struct rt_spinlock *spinlock;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
_timer_remove(timer);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_spin_unlock_irqrestore(spinlock, level);
rt_object_delete(&(timer->parent));
return RT_EOK;
}
RTM_EXPORT(rt_timer_delete);
#endif /* RT_USING_HEAP */
/**
* @brief This function will start the timer
*
* @param timer the timer to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
static rt_err_t _timer_start(rt_list_t *timer_list, rt_timer_t timer)
{
unsigned int row_lvl;
rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
unsigned int tst_nr;
static unsigned int random_nr;
/* remove timer from list */
_timer_remove(timer);
/* change status of timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
timer->timeout_tick = rt_tick_get() + timer->init_tick;
row_head[0] = &timer_list[0];
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
for (; row_head[row_lvl] != timer_list[row_lvl].prev;
row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer *t;
rt_list_t *p = row_head[row_lvl]->next;
/* fix up the entry pointer */
t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
/* If we have two timers that timeout at the same time, it's
* preferred that the timer inserted early get called early.
* So insert the new timer to the end the the some-timeout timer
* list.
*/
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
break;
}
}
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
row_head[row_lvl + 1] = row_head[row_lvl] + 1;
}
/* Interestingly, this super simple timer insert counter works very very
* well on distributing the list height uniformly. By means of "very very
* well", I mean it beats the randomness of timer->timeout_tick very easily
* (actually, the timeout_tick is not random and easy to be attacked). */
random_nr++;
tst_nr = random_nr;
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
else
break;
/* Shift over the bits we have tested. Works well with 1 bit and 2
* bits. */
tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
}
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
return RT_EOK;
}
/**
* @brief This function will check timer list, if a timeout event happens,
* the corresponding timeout function will be invoked.
*
* @param timer_list The timer list to check.
* @param lock The lock for the timer list.
*/
static void _timer_check(rt_list_t *timer_list, struct rt_spinlock *lock)
{
struct rt_timer *t;
rt_tick_t current_tick;
rt_base_t level;
rt_list_t list;
level = rt_spin_lock_irqsave(lock);
current_tick = rt_tick_get();
rt_list_init(&list);
while (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
t = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
/* re-get tick */
current_tick = rt_tick_get();
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
/* remove timer from timer list firstly */
_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
/* add timer to temporary list */
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
rt_spin_unlock_irqrestore(lock, level);
/* call timeout function */
t->timeout_func(t->parameter);
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
level = rt_spin_lock_irqsave(lock);
/* Check whether the timer object is detached or started again */
if (rt_list_isempty(&list))
{
continue;
}
rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
_timer_start(timer_list, t);
}
}
else break;
}
rt_spin_unlock_irqrestore(lock, level);
}
/**
* @brief This function will start the timer
*
* @param timer the timer to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_timer_start(rt_timer_t timer)
{
rt_sched_lock_level_t slvl;
int is_thread_timer = 0;
struct rt_spinlock *spinlock;
rt_list_t *timer_list;
rt_base_t level;
rt_err_t err;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
#ifdef RT_USING_TIMER_ALL_SOFT
timer_list = _soft_timer_list;
spinlock = &_stimer_lock;
#else
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
timer_list = _soft_timer_list;
spinlock = &_stimer_lock;
}
else
#endif /* RT_USING_TIMER_SOFT */
{
timer_list = _timer_list;
spinlock = &_htimer_lock;
}
#endif
if (timer->parent.flag & RT_TIMER_FLAG_THREAD_TIMER)
{
rt_thread_t thread;
is_thread_timer = 1;
rt_sched_lock(&slvl);
thread = rt_container_of(timer, struct rt_thread, thread_timer);
RT_ASSERT(rt_object_get_type(&thread->parent) == RT_Object_Class_Thread);
rt_sched_thread_timer_start(thread);
}
level = rt_spin_lock_irqsave(spinlock);
err = _timer_start(timer_list, timer);
rt_spin_unlock_irqrestore(spinlock, level);
if (is_thread_timer)
{
rt_sched_unlock(slvl);
}
return err;
}
RTM_EXPORT(rt_timer_start);
/**
* @brief This function will stop the timer
*
* @param timer the timer to be stopped
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_timer_stop(rt_timer_t timer)
{
rt_base_t level;
struct rt_spinlock *spinlock;
/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
rt_spin_unlock_irqrestore(spinlock, level);
return -RT_ERROR;
}
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
_timer_remove(timer);
/* change status */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_spin_unlock_irqrestore(spinlock, level);
return RT_EOK;
}
RTM_EXPORT(rt_timer_stop);
/**
* @brief This function will get or set some options of the timer
*
* @param timer the timer to be get or set
* @param cmd the control command
* @param arg the argument
*
* @return the statu of control
*/
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
{
struct rt_spinlock *spinlock;
rt_base_t level;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
switch (cmd)
{
case RT_TIMER_CTRL_GET_TIME:
*(rt_tick_t *)arg = timer->init_tick;
break;
case RT_TIMER_CTRL_SET_TIME:
RT_ASSERT((*(rt_tick_t *)arg) < RT_TICK_MAX / 2);
if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
{
_timer_remove(timer);
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
timer->init_tick = *(rt_tick_t *)arg;
break;
case RT_TIMER_CTRL_SET_ONESHOT:
timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_SET_PERIODIC:
timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_GET_STATE:
if(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
{
/*timer is start and run*/
*(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
}
else
{
/*timer is stop*/
*(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
}
break;
case RT_TIMER_CTRL_GET_REMAIN_TIME:
*(rt_tick_t *)arg = timer->timeout_tick;
break;
case RT_TIMER_CTRL_GET_FUNC:
*(void **)arg = (void *)timer->timeout_func;
break;
case RT_TIMER_CTRL_SET_FUNC:
timer->timeout_func = (void (*)(void*))arg;
break;
case RT_TIMER_CTRL_GET_PARM:
*(void **)arg = timer->parameter;
break;
case RT_TIMER_CTRL_SET_PARM:
timer->parameter = arg;
break;
default:
break;
}
rt_spin_unlock_irqrestore(spinlock, level);
return RT_EOK;
}
RTM_EXPORT(rt_timer_control);
/**
* @brief This function will check timer list, if a timeout event happens,
* the corresponding timeout function will be invoked.
*
* @note This function shall be invoked in operating system timer interrupt.
*/
void rt_timer_check(void)
{
RT_ASSERT(rt_interrupt_get_nest() > 0);
#ifdef RT_USING_SMP
/* Running on core 0 only */
if (rt_cpu_get_id() != 0)
{
return;
}
#endif
#ifdef RT_USING_TIMER_SOFT
rt_err_t ret = RT_ERROR;
rt_tick_t next_timeout;
ret = _timer_list_next_timeout(_soft_timer_list, &next_timeout);
if ((ret == RT_EOK) && (next_timeout <= rt_tick_get()))
{
rt_sem_release(&_soft_timer_sem);
}
#endif
#ifndef RT_USING_TIMER_ALL_SOFT
_timer_check(_timer_list, &_htimer_lock);
#endif
}
/**
* @brief This function will return the next timeout tick in the system.
*
* @return the next timeout tick in the system
*/
rt_tick_t rt_timer_next_timeout_tick(void)
{
rt_base_t level;
rt_tick_t htimer_next_timeout = RT_TICK_MAX, stimer_next_timeout = RT_TICK_MAX;
#ifndef RT_USING_TIMER_ALL_SOFT
level = rt_spin_lock_irqsave(&_htimer_lock);
_timer_list_next_timeout(_timer_list, &htimer_next_timeout);
rt_spin_unlock_irqrestore(&_htimer_lock, level);
#endif
#ifdef RT_USING_TIMER_SOFT
level = rt_spin_lock_irqsave(&_stimer_lock);
_timer_list_next_timeout(_soft_timer_list, &stimer_next_timeout);
rt_spin_unlock_irqrestore(&_stimer_lock, level);
#endif
return htimer_next_timeout < stimer_next_timeout ? htimer_next_timeout : stimer_next_timeout;
}
#ifdef RT_USING_TIMER_SOFT
/**
* @brief System timer thread entry
*
* @param parameter is the arg of the thread
*/
static void _timer_thread_entry(void *parameter)
{
RT_UNUSED(parameter);
while (1)
{
_timer_check(_soft_timer_list, &_stimer_lock); /* check software timer */
rt_sem_take(&_soft_timer_sem, RT_WAITING_FOREVER);
}
}
#endif /* RT_USING_TIMER_SOFT */
/**
* @ingroup group_system_init
*
* @brief This function will initialize system timer
*/
void rt_system_timer_init(void)
{
#ifndef RT_USING_TIMER_ALL_SOFT
rt_size_t i;
for (i = 0; i < sizeof(_timer_list) / sizeof(_timer_list[0]); i++)
{
rt_list_init(_timer_list + i);
}
rt_spin_lock_init(&_htimer_lock);
#endif
}
/**
* @ingroup group_system_init
*
* @brief This function will initialize system timer thread
*/
void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT
int i;
for (i = 0;
i < sizeof(_soft_timer_list) / sizeof(_soft_timer_list[0]);
i++)
{
rt_list_init(_soft_timer_list + i);
}
rt_spin_lock_init(&_stimer_lock);
rt_sem_init(&_soft_timer_sem, "stimer", 0, RT_IPC_FLAG_PRIO);
rt_sem_control(&_soft_timer_sem, RT_IPC_CMD_SET_VLIMIT, (void*)1);
/* start software timer thread */
rt_thread_init(&_timer_thread,
"timer",
_timer_thread_entry,
RT_NULL,
&_timer_thread_stack[0],
sizeof(_timer_thread_stack),
RT_TIMER_THREAD_PRIO,
10);
/* startup */
rt_thread_startup(&_timer_thread);
#endif /* RT_USING_TIMER_SOFT */
}
/**@}*/