first commit for chrg
This commit is contained in:
@@ -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
|
||||
@@ -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')
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
@@ -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 */
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
/**@}*/
|
||||
|
||||
@@ -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
|
||||
@@ -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')
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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')
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
@@ -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) */
|
||||
|
||||
/**@}*/
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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, ¶m);
|
||||
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
|
||||
|
||||
/**@}*/
|
||||
|
||||
@@ -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
@@ -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*/
|
||||
@@ -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)¶meter, 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 */
|
||||
@@ -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
@@ -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 */
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
Reference in New Issue
Block a user