#![allow(unused_variables)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use core::ffi::c_void;
use core::ptr;
use crate::config::*;
use crate::kernel::list::*;
use crate::port::*;
use crate::types::*;
pub const taskSCHEDULER_SUSPENDED: BaseType_t = 0;
pub const taskSCHEDULER_NOT_STARTED: BaseType_t = 1;
pub const taskSCHEDULER_RUNNING: BaseType_t = 2;
const taskNOT_WAITING_NOTIFICATION: u8 = 0;
const taskWAITING_NOTIFICATION: u8 = 1;
const taskNOTIFICATION_RECEIVED: u8 = 2;
const tskSTACK_FILL_BYTE: u8 = 0xa5;
const tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB: u8 = 0;
const tskSTATICALLY_ALLOCATED_STACK_ONLY: u8 = 1;
const tskSTATICALLY_ALLOCATED_STACK_AND_TCB: u8 = 2;
const tskRUNNING_CHAR: char = 'X';
const tskBLOCKED_CHAR: char = 'B';
const tskREADY_CHAR: char = 'R';
const tskDELETED_CHAR: char = 'D';
const tskSUSPENDED_CHAR: char = 'S';
const taskTASK_NOT_RUNNING: BaseType_t = -1;
const taskTASK_SCHEDULED_TO_YIELD: BaseType_t = -2;
const taskBITS_PER_BYTE: usize = 8;
#[cfg(not(feature = "tick-64bit"))]
const taskEVENT_LIST_ITEM_VALUE_IN_USE: TickType_t = 0x8000_0000;
#[cfg(feature = "tick-64bit")]
const taskEVENT_LIST_ITEM_VALUE_IN_USE: TickType_t = 0x8000_0000_0000_0000;
const configIDLE_TASK_NAME: &[u8] = b"IDLE\0";
const taskATTRIBUTE_IS_IDLE: UBaseType_t = 1 << 0;
#[repr(C)]
pub struct TimeOut_t {
pub xOverflowCount: BaseType_t,
pub xTimeOnEntering: TickType_t,
}
impl TimeOut_t {
pub const fn new() -> Self {
TimeOut_t {
xOverflowCount: 0,
xTimeOnEntering: 0,
}
}
}
impl Default for TimeOut_t {
fn default() -> Self {
Self::new()
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum eTaskState {
eRunning = 0,
eReady = 1,
eBlocked = 2,
eSuspended = 3,
eDeleted = 4,
eInvalid = 5,
}
#[repr(C)]
pub struct tskTaskControlBlock {
pub pxTopOfStack: *mut StackType_t,
pub xStateListItem: ListItem_t,
pub xEventListItem: ListItem_t,
pub uxPriority: UBaseType_t,
pub pxStack: *mut StackType_t,
pub uxStackDepth: configSTACK_DEPTH_TYPE,
pub pcTaskName: [u8; configMAX_TASK_NAME_LEN],
#[cfg(any(
not(feature = "arch-32bit"), // portSTACK_GROWTH > 0 check would go here
feature = "record-stack-high-address"
))]
pub pxEndOfStack: *mut StackType_t,
#[cfg(feature = "trace-facility")]
pub uxTCBNumber: UBaseType_t,
#[cfg(feature = "trace-facility")]
pub uxTaskNumber: UBaseType_t,
#[cfg(feature = "use-mutexes")]
pub uxBasePriority: UBaseType_t,
#[cfg(feature = "use-mutexes")]
pub uxMutexesHeld: UBaseType_t,
#[cfg(feature = "application-task-tag")]
pub pxTaskTag: Option<extern "C" fn(*mut c_void) -> BaseType_t>,
pub ulNotifiedValue: [u32; configTASK_NOTIFICATION_ARRAY_ENTRIES],
pub ucNotifyState: [u8; configTASK_NOTIFICATION_ARRAY_ENTRIES],
pub ucStaticallyAllocated: u8,
#[cfg(feature = "abort-delay")]
pub ucDelayAborted: u8,
#[cfg(feature = "thread-local-storage")]
pub pvThreadLocalStoragePointers: [*mut c_void; configNUM_THREAD_LOCAL_STORAGE_POINTERS],
#[cfg(feature = "generate-run-time-stats")]
pub ulRunTimeCounter: configRUN_TIME_COUNTER_TYPE,
}
pub type TCB_t = tskTaskControlBlock;
impl tskTaskControlBlock {
pub const fn new() -> Self {
tskTaskControlBlock {
pxTopOfStack: ptr::null_mut(),
xStateListItem: ListItem_t::new(),
xEventListItem: ListItem_t::new(),
uxPriority: 0,
pxStack: ptr::null_mut(),
uxStackDepth: 0,
pcTaskName: [0u8; configMAX_TASK_NAME_LEN],
#[cfg(any(not(feature = "arch-32bit"), feature = "record-stack-high-address"))]
pxEndOfStack: ptr::null_mut(),
#[cfg(feature = "trace-facility")]
uxTCBNumber: 0,
#[cfg(feature = "trace-facility")]
uxTaskNumber: 0,
#[cfg(feature = "use-mutexes")]
uxBasePriority: 0,
#[cfg(feature = "use-mutexes")]
uxMutexesHeld: 0,
#[cfg(feature = "application-task-tag")]
pxTaskTag: None,
ulNotifiedValue: [0u32; configTASK_NOTIFICATION_ARRAY_ENTRIES],
ucNotifyState: [0u8; configTASK_NOTIFICATION_ARRAY_ENTRIES],
ucStaticallyAllocated: 0,
#[cfg(feature = "abort-delay")]
ucDelayAborted: 0,
#[cfg(feature = "thread-local-storage")]
pvThreadLocalStoragePointers: [ptr::null_mut();
configNUM_THREAD_LOCAL_STORAGE_POINTERS],
#[cfg(feature = "generate-run-time-stats")]
ulRunTimeCounter: 0,
}
}
}
#[repr(transparent)]
pub struct StaticTask_t {
_storage: core::mem::MaybeUninit<TCB_t>,
}
impl StaticTask_t {
pub const fn new() -> Self {
StaticTask_t {
_storage: core::mem::MaybeUninit::uninit(),
}
}
}
#[no_mangle]
pub static mut pxCurrentTCB: *mut TCB_t = ptr::null_mut();
static mut pxReadyTasksLists: [List_t; configMAX_PRIORITIES as usize] =
[const { List_t::new() }; configMAX_PRIORITIES as usize];
static mut xDelayedTaskList1: List_t = List_t::new();
static mut xDelayedTaskList2: List_t = List_t::new();
static mut pxDelayedTaskList: *mut List_t = ptr::null_mut();
static mut pxOverflowDelayedTaskList: *mut List_t = ptr::null_mut();
static mut xPendingReadyList: List_t = List_t::new();
static mut xTasksWaitingTermination: List_t = List_t::new();
static mut uxDeletedTasksWaitingCleanUp: UBaseType_t = 0;
static mut xSuspendedTaskList: List_t = List_t::new();
static mut uxCurrentNumberOfTasks: UBaseType_t = 0;
static mut xTickCount: TickType_t = configINITIAL_TICK_COUNT;
static mut uxTopReadyPriority: UBaseType_t = tskIDLE_PRIORITY;
static mut xSchedulerRunning: BaseType_t = pdFALSE;
static mut xPendedTicks: TickType_t = 0;
static mut xYieldPendings: [BaseType_t; configNUMBER_OF_CORES as usize] =
[pdFALSE; configNUMBER_OF_CORES as usize];
static mut xNumOfOverflows: BaseType_t = 0;
static mut uxTaskNumber: UBaseType_t = 0;
static mut xNextTaskUnblockTime: TickType_t = 0;
static mut xIdleTaskHandles: [TaskHandle_t; configNUMBER_OF_CORES as usize] =
[ptr::null_mut(); configNUMBER_OF_CORES as usize];
static mut pxIdleTaskTCBBuffer: *mut StaticTask_t = ptr::null_mut();
static mut pxIdleTaskStackBuffer: *mut StackType_t = ptr::null_mut();
static mut uxIdleTaskStackSize: configSTACK_DEPTH_TYPE = 0;
#[cfg(feature = "stack-overflow-check")]
pub type StackOverflowHook_t = extern "C" fn(TaskHandle_t, *const u8);
#[cfg(feature = "stack-overflow-check")]
static mut pxApplicationStackOverflowHook: Option<StackOverflowHook_t> = None;
#[cfg(not(any(feature = "alloc", feature = "heap-4", feature = "heap-5")))]
static mut xStaticIdleTaskTCB: StaticTask_t = StaticTask_t::new();
#[cfg(not(any(feature = "alloc", feature = "heap-4", feature = "heap-5")))]
static mut uxStaticIdleTaskStack: [StackType_t; configMINIMAL_STACK_SIZE] =
[0; configMINIMAL_STACK_SIZE];
static mut uxSchedulerSuspended: UBaseType_t = 0;
#[no_mangle]
static uxTopUsedPriority: UBaseType_t = configMAX_PRIORITIES - 1;
#[cfg(feature = "generate-run-time-stats")]
static mut ulTaskSwitchedInTime: configRUN_TIME_COUNTER_TYPE = 0;
#[cfg(feature = "generate-run-time-stats")]
static mut ulRunTimeStart: configRUN_TIME_COUNTER_TYPE = 0;
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
const fn prvRunTimeDelta(
ulNow: configRUN_TIME_COUNTER_TYPE,
ulThen: configRUN_TIME_COUNTER_TYPE,
) -> configRUN_TIME_COUNTER_TYPE {
ulNow.wrapping_sub(ulThen)
}
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
unsafe fn prvGetTotalRunTime() -> configRUN_TIME_COUNTER_TYPE {
crate::port::portGET_RUN_TIME_COUNTER_VALUE().wrapping_sub(ulRunTimeStart)
}
#[inline(always)]
unsafe fn taskRECORD_READY_PRIORITY(uxPriority: UBaseType_t) {
if uxPriority > uxTopReadyPriority {
uxTopReadyPriority = uxPriority;
}
}
#[inline(always)]
unsafe fn taskSELECT_HIGHEST_PRIORITY_TASK() {
let mut uxTopPriority: UBaseType_t = uxTopReadyPriority;
while listLIST_IS_EMPTY(ptr::addr_of!(pxReadyTasksLists[uxTopPriority as usize])) != pdFALSE {
configASSERT(uxTopPriority > 0);
uxTopPriority -= 1;
}
pxCurrentTCB =
listGET_OWNER_OF_NEXT_ENTRY(ptr::addr_of_mut!(pxReadyTasksLists[uxTopPriority as usize]))
as *mut TCB_t;
uxTopReadyPriority = uxTopPriority;
}
#[inline(always)]
unsafe fn taskRESET_READY_PRIORITY(_uxPriority: UBaseType_t) {
}
#[inline(always)]
unsafe fn taskSWITCH_DELAYED_LISTS() {
configASSERT(listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE);
let pxTemp = pxDelayedTaskList;
pxDelayedTaskList = pxOverflowDelayedTaskList;
pxOverflowDelayedTaskList = pxTemp;
xNumOfOverflows += 1;
prvResetNextTaskUnblockTime();
}
#[inline(always)]
unsafe fn prvAddTaskToReadyList(pxTCB: *mut TCB_t) {
crate::trace::traceMOVED_TASK_TO_READY_STATE(pxTCB as *mut c_void);
taskRECORD_READY_PRIORITY((*pxTCB).uxPriority);
vListInsertEnd(
ptr::addr_of_mut!(pxReadyTasksLists[(*pxTCB).uxPriority as usize]),
ptr::addr_of_mut!((*pxTCB).xStateListItem),
);
crate::trace::tracePOST_MOVED_TASK_TO_READY_STATE(pxTCB as *mut c_void);
}
#[inline(always)]
unsafe fn prvGetTCBFromHandle(pxHandle: TaskHandle_t) -> *mut TCB_t {
if pxHandle.is_null() {
pxCurrentTCB
} else {
pxHandle as *mut TCB_t
}
}
#[inline(always)]
unsafe fn prvReadNotifiedValue(pxTCB: *const TCB_t, uxIndex: usize) -> u32 {
let pxValues = core::ptr::addr_of!((*pxTCB).ulNotifiedValue).cast::<u32>();
core::ptr::read_volatile(pxValues.add(uxIndex))
}
#[inline(always)]
unsafe fn prvWriteNotifiedValue(pxTCB: *mut TCB_t, uxIndex: usize, ulValue: u32) {
let pxValues = core::ptr::addr_of_mut!((*pxTCB).ulNotifiedValue).cast::<u32>();
core::ptr::write_volatile(pxValues.add(uxIndex), ulValue);
}
#[inline(always)]
unsafe fn prvReadNotifyState(pxTCB: *const TCB_t, uxIndex: usize) -> u8 {
let pxStates = core::ptr::addr_of!((*pxTCB).ucNotifyState).cast::<u8>();
core::ptr::read_volatile(pxStates.add(uxIndex))
}
#[inline(always)]
unsafe fn prvWriteNotifyState(pxTCB: *mut TCB_t, uxIndex: usize, ucState: u8) {
let pxStates = core::ptr::addr_of_mut!((*pxTCB).ucNotifyState).cast::<u8>();
core::ptr::write_volatile(pxStates.add(uxIndex), ucState);
}
#[inline(always)]
pub unsafe fn portYIELD_WITHIN_API() {
if configUSE_PREEMPTION != 0 {
portYIELD();
}
}
#[inline(always)]
unsafe fn taskYIELD_ANY_CORE_IF_USING_PREEMPTION(pxTCB: *mut TCB_t) {
if configUSE_PREEMPTION != 0 {
if !pxCurrentTCB.is_null() && (*pxCurrentTCB).uxPriority < (*pxTCB).uxPriority {
portYIELD_WITHIN_API();
}
}
}
unsafe fn prvInitialiseTaskLists() {
for uxPriority in 0..configMAX_PRIORITIES as usize {
vListInitialise(ptr::addr_of_mut!(pxReadyTasksLists[uxPriority]));
}
vListInitialise(ptr::addr_of_mut!(xDelayedTaskList1));
vListInitialise(ptr::addr_of_mut!(xDelayedTaskList2));
vListInitialise(ptr::addr_of_mut!(xPendingReadyList));
vListInitialise(ptr::addr_of_mut!(xTasksWaitingTermination));
vListInitialise(ptr::addr_of_mut!(xSuspendedTaskList));
pxDelayedTaskList = ptr::addr_of_mut!(xDelayedTaskList1);
pxOverflowDelayedTaskList = ptr::addr_of_mut!(xDelayedTaskList2);
}
unsafe fn prvResetNextTaskUnblockTime() {
if listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE {
xNextTaskUnblockTime = portMAX_DELAY;
} else {
let pxTCB = listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList) as *mut TCB_t;
xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE(ptr::addr_of!((*pxTCB).xStateListItem));
}
}
unsafe fn prvAddNewTaskToReadyList(pxNewTCB: *mut TCB_t) {
taskENTER_CRITICAL();
{
uxCurrentNumberOfTasks += 1;
if pxCurrentTCB.is_null() {
pxCurrentTCB = pxNewTCB;
if uxCurrentNumberOfTasks == 1 {
prvInitialiseTaskLists();
}
} else {
if xSchedulerRunning == pdFALSE {
if (*pxCurrentTCB).uxPriority <= (*pxNewTCB).uxPriority {
pxCurrentTCB = pxNewTCB;
}
}
}
uxTaskNumber = uxTaskNumber.wrapping_add(1);
#[cfg(feature = "trace-facility")]
{
(*pxNewTCB).uxTCBNumber = uxTaskNumber;
}
crate::trace::traceTASK_CREATE(pxNewTCB as *mut c_void);
prvAddTaskToReadyList(pxNewTCB);
}
taskEXIT_CRITICAL();
if xSchedulerRunning != pdFALSE {
if (*pxCurrentTCB).uxPriority < (*pxNewTCB).uxPriority {
portYIELD_WITHIN_API();
}
}
}
unsafe fn prvInitialiseNewTask(
pxTaskCode: TaskFunction_t,
pcName: *const u8,
uxStackDepth: configSTACK_DEPTH_TYPE,
pvParameters: *mut c_void,
uxPriority: UBaseType_t,
pxCreatedTask: *mut TaskHandle_t,
pxNewTCB: *mut TCB_t,
pxTopOfStack: *mut StackType_t,
) {
if !pcName.is_null() {
let mut i = 0usize;
while i < configMAX_TASK_NAME_LEN - 1 {
let c = *pcName.add(i);
(*pxNewTCB).pcTaskName[i] = c;
if c == 0 {
break;
}
i += 1;
}
(*pxNewTCB).pcTaskName[configMAX_TASK_NAME_LEN - 1] = 0;
} else {
(*pxNewTCB).pcTaskName[0] = 0;
}
configASSERT(uxPriority < configMAX_PRIORITIES);
let mut uxPriority = uxPriority;
if uxPriority >= configMAX_PRIORITIES {
uxPriority = configMAX_PRIORITIES - 1;
}
(*pxNewTCB).uxPriority = uxPriority;
#[cfg(feature = "use-mutexes")]
{
(*pxNewTCB).uxBasePriority = uxPriority;
(*pxNewTCB).uxMutexesHeld = 0;
}
vListInitialiseItem(ptr::addr_of_mut!((*pxNewTCB).xStateListItem));
vListInitialiseItem(ptr::addr_of_mut!((*pxNewTCB).xEventListItem));
listSET_LIST_ITEM_OWNER(
ptr::addr_of_mut!((*pxNewTCB).xStateListItem),
pxNewTCB as *mut c_void,
);
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxNewTCB).xEventListItem),
(configMAX_PRIORITIES - uxPriority) as TickType_t,
);
listSET_LIST_ITEM_OWNER(
ptr::addr_of_mut!((*pxNewTCB).xEventListItem),
pxNewTCB as *mut c_void,
);
for i in 0..configTASK_NOTIFICATION_ARRAY_ENTRIES {
prvWriteNotifiedValue(pxNewTCB, i, 0);
prvWriteNotifyState(pxNewTCB, i, taskNOT_WAITING_NOTIFICATION);
}
#[cfg(feature = "application-task-tag")]
{
(*pxNewTCB).pxTaskTag = None;
}
#[cfg(feature = "abort-delay")]
{
(*pxNewTCB).ucDelayAborted = pdFALSE as u8;
}
(*pxNewTCB).uxStackDepth = uxStackDepth;
let pxInitialisedTopOfStack: *mut StackType_t;
#[cfg(any(
feature = "port-cortex-m0",
feature = "port-cortex-m3",
feature = "port-cortex-m4f",
feature = "port-cortex-m7"
))]
{
pxInitialisedTopOfStack =
unsafe { pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters) };
}
#[cfg(not(any(
feature = "port-cortex-m0",
feature = "port-cortex-m3",
feature = "port-cortex-m4f",
feature = "port-cortex-m7"
)))]
{
pxInitialisedTopOfStack = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters);
}
(*pxNewTCB).pxTopOfStack = pxInitialisedTopOfStack;
let uxStackBytes = uxStackDepth * core::mem::size_of::<StackType_t>();
if portSTACK_GROWTH < 0 {
configASSERT((pxInitialisedTopOfStack as usize) <= (pxTopOfStack as usize));
configASSERT(
(pxTopOfStack as usize).wrapping_sub(pxInitialisedTopOfStack as usize) < uxStackBytes,
);
} else {
configASSERT((pxInitialisedTopOfStack as usize) >= (pxTopOfStack as usize));
configASSERT(
(pxInitialisedTopOfStack as usize).wrapping_sub(pxTopOfStack as usize) < uxStackBytes,
);
}
if !pxCreatedTask.is_null() {
*pxCreatedTask = pxNewTCB as TaskHandle_t;
}
}
unsafe fn prvAddCurrentTaskToDelayedList(
xTicksToWait: TickType_t,
xCanBlockIndefinitely: BaseType_t,
) {
let xConstTickCount = xTickCount;
#[cfg(feature = "abort-delay")]
{
(*pxCurrentTCB).ucDelayAborted = pdFALSE as u8;
}
let _ux = uxListRemove(ptr::addr_of_mut!((*pxCurrentTCB).xStateListItem));
#[cfg(feature = "task-suspend")]
if xTicksToWait == portMAX_DELAY && xCanBlockIndefinitely != pdFALSE {
vListInsertEnd(
ptr::addr_of_mut!(xSuspendedTaskList),
ptr::addr_of_mut!((*pxCurrentTCB).xStateListItem),
);
return;
}
{
let xTimeToWake = xConstTickCount.wrapping_add(xTicksToWait);
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxCurrentTCB).xStateListItem),
xTimeToWake,
);
if xTimeToWake < xConstTickCount {
vListInsert(
pxOverflowDelayedTaskList,
ptr::addr_of_mut!((*pxCurrentTCB).xStateListItem),
);
} else {
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*pxCurrentTCB).xStateListItem),
);
if xTimeToWake < xNextTaskUnblockTime {
xNextTaskUnblockTime = xTimeToWake;
}
}
}
}
unsafe fn prvCheckTasksWaitingTermination() {
#[cfg(feature = "task-delete")]
{
while uxDeletedTasksWaitingCleanUp > 0 {
let pxTCB: *mut TCB_t;
taskENTER_CRITICAL();
{
pxTCB = listGET_OWNER_OF_HEAD_ENTRY(ptr::addr_of!(xTasksWaitingTermination))
as *mut TCB_t;
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
uxCurrentNumberOfTasks -= 1;
uxDeletedTasksWaitingCleanUp -= 1;
}
taskEXIT_CRITICAL();
prvDeleteTCB(pxTCB);
}
}
}
#[cfg(feature = "task-delete")]
unsafe fn prvDeleteTCB(pxTCB: *mut TCB_t) {
if (*pxTCB).ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB {
crate::memory::vPortFree((*pxTCB).pxStack as *mut c_void);
crate::memory::vPortFree(pxTCB as *mut c_void);
} else if (*pxTCB).ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY {
crate::memory::vPortFree(pxTCB as *mut c_void);
}
}
#[cfg(feature = "stack-overflow-check")]
pub fn vTaskSetStackOverflowHook(pxHook: Option<StackOverflowHook_t>) -> BaseType_t {
unsafe {
taskENTER_CRITICAL();
let xReturn = if xSchedulerRunning == pdFALSE {
pxApplicationStackOverflowHook = pxHook;
pdPASS
} else {
pdFAIL
};
taskEXIT_CRITICAL();
xReturn
}
}
#[cfg(feature = "stack-overflow-check")]
#[inline(always)]
unsafe fn prvCallApplicationStackOverflowHook(pxTCB: *mut TCB_t) {
let pxHook = pxApplicationStackOverflowHook;
if let Some(pxHook) = pxHook {
pxHook(pxTCB.cast(), (*pxTCB).pcTaskName.as_ptr());
} else {
configASSERT(false);
}
}
extern "C" fn prvIdleTask(_pvParameters: *mut c_void) {
loop {
unsafe {
prvCheckTasksWaitingTermination();
}
#[cfg(feature = "idle-yield")]
unsafe {
if listCURRENT_LIST_LENGTH(ptr::addr_of!(pxReadyTasksLists[0]))
> configNUMBER_OF_CORES as UBaseType_t
{
portYIELD_WITHIN_API();
}
}
if configUSE_IDLE_HOOK != 0 {
}
#[cfg(feature = "tickless-idle")]
{
let xExpectedIdleTime = prvGetExpectedIdleTime();
if xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP {
unsafe { vTaskSuspendAll() };
{
unsafe {
configASSERT(xNextTaskUnblockTime >= xTickCount);
}
let xExpectedIdleTime = prvGetExpectedIdleTime();
if xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP {
crate::trace::traceLOW_POWER_IDLE_BEGIN();
crate::port::vPortSuppressTicksAndSleep(xExpectedIdleTime);
crate::trace::traceLOW_POWER_IDLE_END();
}
}
unsafe { xTaskResumeAll() };
}
}
}
}
unsafe fn prvCreateIdleTasks() -> BaseType_t {
let xReturn: BaseType_t;
if !pxIdleTaskTCBBuffer.is_null() && !pxIdleTaskStackBuffer.is_null() && uxIdleTaskStackSize > 0
{
xIdleTaskHandles[0] = xTaskCreateStatic(
prvIdleTask,
configIDLE_TASK_NAME.as_ptr(),
uxIdleTaskStackSize,
ptr::null_mut(),
tskIDLE_PRIORITY,
pxIdleTaskStackBuffer,
pxIdleTaskTCBBuffer,
);
return if xIdleTaskHandles[0].is_null() {
pdFAIL
} else {
pdPASS
};
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
{
xReturn = xTaskCreate(
prvIdleTask,
configIDLE_TASK_NAME.as_ptr(),
configMINIMAL_STACK_SIZE,
ptr::null_mut(),
tskIDLE_PRIORITY,
&mut xIdleTaskHandles[0],
);
}
#[cfg(not(any(feature = "alloc", feature = "heap-4", feature = "heap-5")))]
{
xIdleTaskHandles[0] = xTaskCreateStatic(
prvIdleTask,
configIDLE_TASK_NAME.as_ptr(),
configMINIMAL_STACK_SIZE,
ptr::null_mut(),
tskIDLE_PRIORITY,
uxStaticIdleTaskStack.as_mut_ptr(),
&mut xStaticIdleTaskTCB,
);
xReturn = if xIdleTaskHandles[0].is_null() {
pdFAIL
} else {
pdPASS
};
}
xReturn
}
pub unsafe fn vTaskSetIdleTaskMemory(
pxTaskTCBBuffer: *mut StaticTask_t,
pxTaskStackBuffer: *mut StackType_t,
uxStackSize: configSTACK_DEPTH_TYPE,
) -> BaseType_t {
if pxTaskTCBBuffer.is_null() || pxTaskStackBuffer.is_null() || uxStackSize == 0 {
return pdFAIL;
}
taskENTER_CRITICAL();
let xReturn = if xSchedulerRunning == pdFALSE && xIdleTaskHandles[0].is_null() {
pxIdleTaskTCBBuffer = pxTaskTCBBuffer;
pxIdleTaskStackBuffer = pxTaskStackBuffer;
uxIdleTaskStackSize = uxStackSize;
pdPASS
} else {
pdFAIL
};
taskEXIT_CRITICAL();
xReturn
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub unsafe fn xTaskCreate(
pxTaskCode: TaskFunction_t,
pcName: *const u8,
uxStackDepth: configSTACK_DEPTH_TYPE,
pvParameters: *mut c_void,
uxPriority: UBaseType_t,
pxCreatedTask: *mut TaskHandle_t,
) -> BaseType_t {
if uxStackDepth == 0 {
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
let stack_size = match uxStackDepth.checked_mul(core::mem::size_of::<StackType_t>()) {
Some(size) => size,
None => return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY,
};
let (pxNewTCB, pxStack) = if portSTACK_GROWTH > 0 {
let pxNewTCB = crate::memory::pvPortMalloc(core::mem::size_of::<TCB_t>()) as *mut TCB_t;
if pxNewTCB.is_null() {
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
let pxStack = crate::memory::pvPortMalloc(stack_size) as *mut StackType_t;
if pxStack.is_null() {
crate::memory::vPortFree(pxNewTCB.cast());
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
(pxNewTCB, pxStack)
} else {
let pxStack = crate::memory::pvPortMalloc(stack_size) as *mut StackType_t;
if pxStack.is_null() {
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
let pxNewTCB = crate::memory::pvPortMalloc(core::mem::size_of::<TCB_t>()) as *mut TCB_t;
if pxNewTCB.is_null() {
crate::memory::vPortFree(pxStack.cast());
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
(pxNewTCB, pxStack)
};
ptr::write(pxNewTCB, TCB_t::new());
(*pxNewTCB).pxStack = pxStack;
let mut pxTopOfStack: *mut StackType_t;
if portSTACK_GROWTH < 0 {
pxTopOfStack = pxStack.add(uxStackDepth - 1);
pxTopOfStack = ((pxTopOfStack as usize) & !(portBYTE_ALIGNMENT - 1)) as *mut StackType_t;
#[cfg(any(not(feature = "arch-32bit"), feature = "record-stack-high-address"))]
{
(*pxNewTCB).pxEndOfStack = pxTopOfStack;
}
} else {
pxTopOfStack = (((pxStack as usize) + (portBYTE_ALIGNMENT - 1)) & !(portBYTE_ALIGNMENT - 1))
as *mut StackType_t;
#[cfg(any(not(feature = "arch-32bit"), feature = "record-stack-high-address"))]
{
(*pxNewTCB).pxEndOfStack = pxStack.add(uxStackDepth - 1);
}
}
configASSERT((pxTopOfStack as usize & (portBYTE_ALIGNMENT - 1)) == 0);
if tskSET_NEW_STACKS_TO_KNOWN_VALUE != 0 {
ptr::write_bytes(pxStack.cast::<u8>(), tskSTACK_FILL_BYTE, stack_size);
}
(*pxNewTCB).ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
prvInitialiseNewTask(
pxTaskCode,
pcName,
uxStackDepth,
pvParameters,
uxPriority,
pxCreatedTask,
pxNewTCB,
pxTopOfStack,
);
prvAddNewTaskToReadyList(pxNewTCB);
pdPASS
}
pub unsafe fn xTaskCreateStatic(
pxTaskCode: TaskFunction_t,
pcName: *const u8,
uxStackDepth: configSTACK_DEPTH_TYPE,
pvParameters: *mut c_void,
uxPriority: UBaseType_t,
puxStackBuffer: *mut StackType_t,
pxTaskBuffer: *mut StaticTask_t,
) -> TaskHandle_t {
let mut xReturn: TaskHandle_t = ptr::null_mut();
let xStackSizeBytes = uxStackDepth.checked_mul(core::mem::size_of::<StackType_t>());
if uxStackDepth > 0
&& xStackSizeBytes.is_some()
&& !puxStackBuffer.is_null()
&& !pxTaskBuffer.is_null()
{
let pxNewTCB = pxTaskBuffer as *mut TCB_t;
ptr::write(pxNewTCB, TCB_t::new());
(*pxNewTCB).pxStack = puxStackBuffer;
let pxTopOfStack: *mut StackType_t;
if portSTACK_GROWTH < 0 {
let pxTop = puxStackBuffer.add(uxStackDepth - 1);
pxTopOfStack = pxTop.map_addr(|address| address & !(portBYTE_ALIGNMENT - 1));
#[cfg(any(not(feature = "arch-32bit"), feature = "record-stack-high-address"))]
{
(*pxNewTCB).pxEndOfStack = pxTopOfStack;
}
} else {
pxTopOfStack = puxStackBuffer.map_addr(|address| {
address.wrapping_add(portBYTE_ALIGNMENT - 1) & !(portBYTE_ALIGNMENT - 1)
});
#[cfg(any(not(feature = "arch-32bit"), feature = "record-stack-high-address"))]
{
(*pxNewTCB).pxEndOfStack = puxStackBuffer.add(uxStackDepth - 1);
}
}
configASSERT((pxTopOfStack as usize & (portBYTE_ALIGNMENT - 1)) == 0);
if tskSET_NEW_STACKS_TO_KNOWN_VALUE != 0 {
ptr::write_bytes(
puxStackBuffer as *mut u8,
tskSTACK_FILL_BYTE,
xStackSizeBytes.unwrap(),
);
}
(*pxNewTCB).ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
prvInitialiseNewTask(
pxTaskCode,
pcName,
uxStackDepth,
pvParameters,
uxPriority,
&mut xReturn,
pxNewTCB,
pxTopOfStack,
);
prvAddNewTaskToReadyList(pxNewTCB);
}
xReturn
}
pub unsafe fn vTaskStartScheduler() {
unsafe {
let xReturn = prvCreateIdleTasks();
#[cfg(feature = "timers")]
let xReturn = if xReturn == pdPASS {
crate::kernel::timers::xTimerCreateTimerTask()
} else {
xReturn
};
if xReturn == pdPASS {
portDISABLE_INTERRUPTS();
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = 0;
if configINITIAL_TICK_COUNT != 0 {
xTickCount = configINITIAL_TICK_COUNT;
}
#[cfg(feature = "generate-run-time-stats")]
{
crate::port::portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
ulRunTimeStart = crate::port::portGET_RUN_TIME_COUNTER_VALUE();
ulTaskSwitchedInTime = ulRunTimeStart;
}
crate::trace::traceTASK_SWITCHED_IN();
xPortStartScheduler();
} else {
configASSERT(xReturn == pdPASS);
}
}
}
pub unsafe fn vTaskEndScheduler() {
unsafe {
#[cfg(feature = "task-delete")]
{
#[cfg(feature = "timers")]
{
vTaskDelete(crate::kernel::timers::xTimerGetTimerDaemonTaskHandle());
}
vTaskDelete(xIdleTaskHandles[0]);
prvCheckTasksWaitingTermination();
}
portDISABLE_INTERRUPTS();
xSchedulerRunning = pdFALSE;
vPortEndScheduler();
}
}
pub unsafe fn vTaskSuspendAll() {
unsafe {
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
uxSchedulerSuspended = uxSchedulerSuspended.wrapping_add(1);
portMEMORY_BARRIER();
}
}
pub unsafe fn xTaskResumeAll() -> BaseType_t {
let mut xAlreadyYielded = pdFALSE;
unsafe {
configASSERT(uxSchedulerSuspended > 0);
taskENTER_CRITICAL();
{
uxSchedulerSuspended -= 1;
if uxSchedulerSuspended == 0 {
if uxCurrentNumberOfTasks > 0 {
let mut pxMovedTCB: *mut TCB_t = ptr::null_mut();
while listLIST_IS_EMPTY(ptr::addr_of!(xPendingReadyList)) == pdFALSE {
let pxTCB = listGET_OWNER_OF_HEAD_ENTRY(ptr::addr_of!(xPendingReadyList))
as *mut TCB_t;
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xEventListItem));
portMEMORY_BARRIER();
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
prvAddTaskToReadyList(pxTCB);
pxMovedTCB = pxTCB;
if (*pxTCB).uxPriority > (*pxCurrentTCB).uxPriority {
xYieldPendings[0] = pdTRUE;
}
}
if !pxMovedTCB.is_null() {
prvResetNextTaskUnblockTime();
}
if xPendedTicks > 0 {
while xPendedTicks > 0 {
if xTaskIncrementTick() != pdFALSE {
xYieldPendings[0] = pdTRUE;
}
xPendedTicks -= 1;
}
}
if xYieldPendings[0] != pdFALSE {
xAlreadyYielded = pdTRUE;
portYIELD_WITHIN_API();
}
}
}
}
taskEXIT_CRITICAL();
}
xAlreadyYielded
}
pub unsafe fn vTaskDelay(xTicksToDelay: TickType_t) {
let mut xAlreadyYielded = pdFALSE;
if xTicksToDelay > 0 {
unsafe {
configASSERT(uxSchedulerSuspended == 0);
vTaskSuspendAll();
{
crate::trace::traceTASK_DELAY();
prvAddCurrentTaskToDelayedList(xTicksToDelay, pdFALSE);
}
xAlreadyYielded = xTaskResumeAll();
}
}
if xAlreadyYielded == pdFALSE {
portYIELD_WITHIN_API();
}
}
pub unsafe fn xTaskDelayUntil(
pxPreviousWakeTime: *mut TickType_t,
xTimeIncrement: TickType_t,
) -> BaseType_t {
let xAlreadyYielded: BaseType_t;
let mut xShouldDelay = pdFALSE;
unsafe {
configASSERT(!pxPreviousWakeTime.is_null());
configASSERT(xTimeIncrement > 0);
configASSERT(uxSchedulerSuspended == 0);
vTaskSuspendAll();
{
let xConstTickCount = xTickCount;
let xTimeToWake = (*pxPreviousWakeTime).wrapping_add(xTimeIncrement);
if xConstTickCount < *pxPreviousWakeTime {
if xTimeToWake < *pxPreviousWakeTime && xTimeToWake > xConstTickCount {
xShouldDelay = pdTRUE;
}
} else {
if xTimeToWake < *pxPreviousWakeTime || xTimeToWake > xConstTickCount {
xShouldDelay = pdTRUE;
}
}
*pxPreviousWakeTime = xTimeToWake;
if xShouldDelay != pdFALSE {
crate::trace::traceTASK_DELAY_UNTIL(xTimeToWake);
prvAddCurrentTaskToDelayedList(xTimeToWake.wrapping_sub(xConstTickCount), pdFALSE);
}
}
xAlreadyYielded = xTaskResumeAll();
}
if xAlreadyYielded == pdFALSE {
portYIELD_WITHIN_API();
}
xShouldDelay
}
pub fn xTaskGetSchedulerState() -> BaseType_t {
unsafe {
if xSchedulerRunning == pdFALSE {
taskSCHEDULER_NOT_STARTED
} else if uxSchedulerSuspended == 0 {
taskSCHEDULER_RUNNING
} else {
taskSCHEDULER_SUSPENDED
}
}
}
pub fn xTaskGetCurrentTaskHandle() -> TaskHandle_t {
unsafe { pxCurrentTCB as TaskHandle_t }
}
pub fn xTaskGetCurrentTaskHandleForCore(xCoreID: BaseType_t) -> TaskHandle_t {
if xCoreID == 0 {
unsafe { pxCurrentTCB.cast() }
} else {
ptr::null_mut()
}
}
#[cfg(feature = "use-mutexes")]
pub unsafe fn pvTaskIncrementMutexHeldCount() -> TaskHandle_t {
unsafe {
if !pxCurrentTCB.is_null() {
(*pxCurrentTCB).uxMutexesHeld = (*pxCurrentTCB).uxMutexesHeld.wrapping_add(1);
}
pxCurrentTCB as TaskHandle_t
}
}
pub fn uxTaskGetNumberOfTasks() -> UBaseType_t {
unsafe { uxCurrentNumberOfTasks }
}
pub unsafe fn pcTaskGetName(xTaskToQuery: TaskHandle_t) -> *mut u8 {
let pxTCB = prvGetTCBFromHandle(xTaskToQuery);
configASSERT(!pxTCB.is_null());
(*pxTCB).pcTaskName.as_mut_ptr()
}
pub unsafe fn xTaskGetStaticBuffers(
xTask: TaskHandle_t,
ppuxStackBuffer: *mut *mut StackType_t,
ppxTaskBuffer: *mut *mut StaticTask_t,
) -> BaseType_t {
configASSERT(!ppuxStackBuffer.is_null());
configASSERT(!ppxTaskBuffer.is_null());
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
if (*pxTCB).ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB {
*ppuxStackBuffer = (*pxTCB).pxStack;
*ppxTaskBuffer = pxTCB.cast();
pdTRUE
} else if (*pxTCB).ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY {
*ppuxStackBuffer = (*pxTCB).pxStack;
*ppxTaskBuffer = ptr::null_mut();
pdTRUE
} else {
pdFALSE
}
}
pub fn xTaskGetTickCount() -> TickType_t {
let xTicks: TickType_t;
taskENTER_CRITICAL();
unsafe {
xTicks = ptr::read_volatile(ptr::addr_of!(xTickCount));
}
taskEXIT_CRITICAL();
xTicks
}
pub fn xTaskGetTickCountFromISR() -> TickType_t {
let xTicks: TickType_t;
let uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
unsafe {
xTicks = ptr::read_volatile(ptr::addr_of!(xTickCount));
}
taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus);
xTicks
}
pub unsafe fn xTaskRemoveFromEventList(pxEventList: *const List_t) -> BaseType_t {
let pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY(pxEventList) as *mut TCB_t;
configASSERT(!pxUnblockedTCB.is_null());
let _ux = uxListRemove(ptr::addr_of_mut!((*pxUnblockedTCB).xEventListItem));
if uxSchedulerSuspended == 0 {
let _ux = uxListRemove(ptr::addr_of_mut!((*pxUnblockedTCB).xStateListItem));
prvAddTaskToReadyList(pxUnblockedTCB);
#[cfg(feature = "tickless-idle")]
prvResetNextTaskUnblockTime();
} else {
vListInsertEnd(
ptr::addr_of_mut!(xPendingReadyList),
ptr::addr_of_mut!((*pxUnblockedTCB).xEventListItem),
);
}
if (*pxUnblockedTCB).uxPriority > (*pxCurrentTCB).uxPriority {
xYieldPendings[0] = pdTRUE;
pdTRUE
} else {
pdFALSE
}
}
pub unsafe fn vTaskPlaceOnEventList(pxEventList: *mut List_t, xTicksToWait: TickType_t) {
configASSERT(!pxEventList.is_null());
vListInsert(
pxEventList,
ptr::addr_of_mut!((*pxCurrentTCB).xEventListItem),
);
prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
}
pub unsafe fn vTaskPlaceOnEventListRestricted(
pxEventList: *mut List_t,
mut xTicksToWait: TickType_t,
xWaitIndefinitely: BaseType_t,
) {
configASSERT(!pxEventList.is_null());
configASSERT(uxSchedulerSuspended != 0);
vListInsertEnd(
pxEventList,
ptr::addr_of_mut!((*pxCurrentTCB).xEventListItem),
);
if xWaitIndefinitely != pdFALSE {
xTicksToWait = portMAX_DELAY;
}
prvAddCurrentTaskToDelayedList(xTicksToWait, xWaitIndefinitely);
}
pub unsafe fn vTaskPlaceOnUnorderedEventList(
pxEventList: *mut List_t,
xItemValue: TickType_t,
xTicksToWait: TickType_t,
) {
configASSERT(!pxEventList.is_null());
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxCurrentTCB).xEventListItem),
xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE,
);
vListInsertEnd(
pxEventList,
ptr::addr_of_mut!((*pxCurrentTCB).xEventListItem),
);
prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
}
pub unsafe fn xTaskRemoveFromUnorderedEventList(
pxEventListItem: *mut ListItem_t,
xItemValue: TickType_t,
) -> BaseType_t {
configASSERT(uxSchedulerSuspended != 0);
listSET_LIST_ITEM_VALUE(
pxEventListItem,
xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE,
);
let pxUnblockedTCB = listGET_LIST_ITEM_OWNER(pxEventListItem) as *mut TCB_t;
configASSERT(!pxUnblockedTCB.is_null());
let _ux = uxListRemove(pxEventListItem);
#[cfg(feature = "tickless-idle")]
{
prvResetNextTaskUnblockTime();
}
let _ux = uxListRemove(ptr::addr_of_mut!((*pxUnblockedTCB).xStateListItem));
prvAddTaskToReadyList(pxUnblockedTCB);
if (*pxUnblockedTCB).uxPriority > (*pxCurrentTCB).uxPriority {
xYieldPendings[0] = pdTRUE;
pdTRUE
} else {
pdFALSE
}
}
pub unsafe fn uxTaskResetEventItemValue() -> TickType_t {
unsafe {
let pxCurrentTCBLocal = pxCurrentTCB;
let uxReturn = listGET_LIST_ITEM_VALUE(ptr::addr_of!((*pxCurrentTCBLocal).xEventListItem));
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxCurrentTCBLocal).xEventListItem),
(configMAX_PRIORITIES as TickType_t)
.wrapping_sub((*pxCurrentTCBLocal).uxPriority as TickType_t),
);
uxReturn
}
}
pub unsafe fn vTaskSetTimeOutState(pxTimeOut: *mut TimeOut_t) {
unsafe {
configASSERT(!pxTimeOut.is_null());
taskENTER_CRITICAL();
{
(*pxTimeOut).xOverflowCount = xNumOfOverflows;
(*pxTimeOut).xTimeOnEntering = xTickCount;
}
taskEXIT_CRITICAL();
}
}
pub unsafe fn vTaskInternalSetTimeOutState(pxTimeOut: *mut TimeOut_t) {
unsafe {
(*pxTimeOut).xOverflowCount = xNumOfOverflows;
(*pxTimeOut).xTimeOnEntering = xTickCount;
}
}
pub unsafe fn xTaskCheckForTimeOut(
pxTimeOut: *mut TimeOut_t,
pxTicksToWait: *mut TickType_t,
) -> BaseType_t {
let xReturn: BaseType_t;
unsafe {
configASSERT(!pxTimeOut.is_null());
configASSERT(!pxTicksToWait.is_null());
taskENTER_CRITICAL();
{
let xConstTickCount = xTickCount;
let xElapsedTime = xConstTickCount.wrapping_sub((*pxTimeOut).xTimeOnEntering);
#[cfg(feature = "abort-delay")]
{
if (*pxCurrentTCB).ucDelayAborted != (pdFALSE as u8) {
(*pxCurrentTCB).ucDelayAborted = pdFALSE as u8;
xReturn = pdTRUE;
taskEXIT_CRITICAL();
return xReturn;
}
}
if cfg!(feature = "task-suspend") && *pxTicksToWait == portMAX_DELAY {
xReturn = pdFALSE;
} else if xNumOfOverflows != (*pxTimeOut).xOverflowCount
&& xConstTickCount >= (*pxTimeOut).xTimeOnEntering
{
xReturn = pdTRUE;
*pxTicksToWait = 0;
} else if xElapsedTime < *pxTicksToWait {
*pxTicksToWait -= xElapsedTime;
vTaskInternalSetTimeOutState(pxTimeOut);
xReturn = pdFALSE;
} else {
*pxTicksToWait = 0;
xReturn = pdTRUE;
}
}
taskEXIT_CRITICAL();
}
xReturn
}
pub unsafe fn xTaskPriorityInherit(pxMutexHolder: TaskHandle_t) -> BaseType_t {
#[cfg(feature = "use-mutexes")]
unsafe {
let mut xReturn = pdFALSE;
if !pxMutexHolder.is_null() {
let pxMutexHolderTCB = pxMutexHolder as *mut TCB_t;
if (*pxMutexHolderTCB).uxPriority < (*pxCurrentTCB).uxPriority {
if (listGET_LIST_ITEM_VALUE(ptr::addr_of!((*pxMutexHolderTCB).xEventListItem))
& taskEVENT_LIST_ITEM_VALUE_IN_USE)
== 0
{
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxMutexHolderTCB).xEventListItem),
(configMAX_PRIORITIES - (*pxCurrentTCB).uxPriority) as TickType_t,
);
}
if listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[(*pxMutexHolderTCB).uxPriority as usize]),
ptr::addr_of!((*pxMutexHolderTCB).xStateListItem),
) != pdFALSE
{
if uxListRemove(ptr::addr_of_mut!((*pxMutexHolderTCB).xStateListItem)) == 0 {
taskRESET_READY_PRIORITY((*pxMutexHolderTCB).uxPriority);
}
(*pxMutexHolderTCB).uxPriority = (*pxCurrentTCB).uxPriority;
prvAddTaskToReadyList(pxMutexHolderTCB);
} else {
(*pxMutexHolderTCB).uxPriority = (*pxCurrentTCB).uxPriority;
}
crate::trace::traceTASK_PRIORITY_INHERIT(
pxMutexHolderTCB as *mut c_void,
(*pxCurrentTCB).uxPriority,
);
xReturn = pdTRUE;
} else if (*pxMutexHolderTCB).uxBasePriority < (*pxCurrentTCB).uxPriority {
xReturn = pdTRUE;
}
}
xReturn
}
#[cfg(not(feature = "use-mutexes"))]
{
let _ = pxMutexHolder;
pdFALSE
}
}
#[inline(always)]
pub unsafe fn vTaskPriorityInherit(pxMutexHolder: TaskHandle_t) {
let _ = unsafe { xTaskPriorityInherit(pxMutexHolder) };
}
pub unsafe fn xTaskPriorityDisinherit(pxMutexHolder: TaskHandle_t) -> BaseType_t {
#[cfg(feature = "use-mutexes")]
unsafe {
if !pxMutexHolder.is_null() {
let pxTCB = pxMutexHolder as *mut TCB_t;
configASSERT(pxTCB == pxCurrentTCB);
configASSERT((*pxTCB).uxMutexesHeld > 0);
(*pxTCB).uxMutexesHeld -= 1;
if (*pxTCB).uxPriority != (*pxTCB).uxBasePriority {
if (*pxTCB).uxMutexesHeld == 0 {
if uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem)) == 0 {
taskRESET_READY_PRIORITY((*pxTCB).uxPriority);
}
(*pxTCB).uxPriority = (*pxTCB).uxBasePriority;
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxTCB).xEventListItem),
(configMAX_PRIORITIES - (*pxTCB).uxPriority) as TickType_t,
);
prvAddTaskToReadyList(pxTCB);
crate::trace::traceTASK_PRIORITY_DISINHERIT(
pxTCB as *mut c_void,
(*pxTCB).uxBasePriority,
);
return pdTRUE;
}
}
}
pdFALSE
}
#[cfg(not(feature = "use-mutexes"))]
{
let _ = pxMutexHolder;
pdFALSE
}
}
pub unsafe fn vTaskPriorityDisinheritAfterTimeout(
pxMutexHolder: TaskHandle_t,
uxHighestPriorityWaitingTask: UBaseType_t,
) {
#[cfg(feature = "use-mutexes")]
unsafe {
if !pxMutexHolder.is_null() {
let pxTCB = pxMutexHolder as *mut TCB_t;
configASSERT((*pxTCB).uxMutexesHeld > 0);
let uxPriorityToUse = if (*pxTCB).uxBasePriority > uxHighestPriorityWaitingTask {
(*pxTCB).uxBasePriority
} else {
uxHighestPriorityWaitingTask
};
if uxPriorityToUse != (*pxTCB).uxPriority {
if (*pxTCB).uxMutexesHeld == 1 {
configASSERT(pxTCB != pxCurrentTCB);
let uxPriorityUsedOnEntry = (*pxTCB).uxPriority;
(*pxTCB).uxPriority = uxPriorityToUse;
if (listGET_LIST_ITEM_VALUE(ptr::addr_of!((*pxTCB).xEventListItem))
& taskEVENT_LIST_ITEM_VALUE_IN_USE)
== 0
{
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxTCB).xEventListItem),
(configMAX_PRIORITIES - uxPriorityToUse) as TickType_t,
);
}
if listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[uxPriorityUsedOnEntry as usize]),
ptr::addr_of!((*pxTCB).xStateListItem),
) != pdFALSE
{
if uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem)) == 0 {
taskRESET_READY_PRIORITY(uxPriorityUsedOnEntry);
}
prvAddTaskToReadyList(pxTCB);
}
}
}
}
}
#[cfg(not(feature = "use-mutexes"))]
{
let _ = pxMutexHolder;
let _ = uxHighestPriorityWaitingTask;
}
}
pub fn vTaskMissedYield() {
unsafe {
xYieldPendings[0] = pdTRUE;
}
}
#[no_mangle]
pub unsafe extern "C" fn xTaskIncrementTick() -> BaseType_t {
let mut xSwitchRequired = pdFALSE;
unsafe {
if uxSchedulerSuspended == 0 {
let xConstTickCount = xTickCount.wrapping_add(1);
xTickCount = xConstTickCount;
if xConstTickCount == 0 {
taskSWITCH_DELAYED_LISTS();
}
if xConstTickCount >= xNextTaskUnblockTime {
loop {
if listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE {
xNextTaskUnblockTime = portMAX_DELAY;
break;
} else {
let pxTCB = listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList) as *mut TCB_t;
let xItemValue =
listGET_LIST_ITEM_VALUE(ptr::addr_of!((*pxTCB).xStateListItem));
if xConstTickCount < xItemValue {
xNextTaskUnblockTime = xItemValue;
break;
}
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
if listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem))
!= ptr::null_mut()
{
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xEventListItem));
}
prvAddTaskToReadyList(pxTCB);
if configUSE_PREEMPTION != 0 {
if (*pxTCB).uxPriority > (*pxCurrentTCB).uxPriority {
xSwitchRequired = pdTRUE;
}
}
}
}
}
if configUSE_PREEMPTION != 0 {
if listCURRENT_LIST_LENGTH(ptr::addr_of!(
pxReadyTasksLists[(*pxCurrentTCB).uxPriority as usize]
)) > 1
{
xSwitchRequired = pdTRUE;
}
}
if configUSE_TICK_HOOK != 0 {
}
if xYieldPendings[0] != pdFALSE {
xSwitchRequired = pdTRUE;
}
} else {
xPendedTicks = xPendedTicks.wrapping_add(1);
if configUSE_TICK_HOOK != 0 {
}
}
}
xSwitchRequired
}
#[no_mangle]
pub unsafe extern "C" fn vTaskSwitchContext() {
unsafe {
if uxSchedulerSuspended != 0 {
xYieldPendings[0] = pdTRUE;
return;
}
xYieldPendings[0] = pdFALSE;
crate::trace::traceTASK_SWITCHED_OUT();
#[cfg(feature = "generate-run-time-stats")]
{
let ulCurrentCounter = crate::port::portGET_RUN_TIME_COUNTER_VALUE();
if !pxCurrentTCB.is_null() {
let ulElapsed = prvRunTimeDelta(ulCurrentCounter, ulTaskSwitchedInTime);
(*pxCurrentTCB).ulRunTimeCounter =
(*pxCurrentTCB).ulRunTimeCounter.wrapping_add(ulElapsed);
}
ulTaskSwitchedInTime = ulCurrentCounter;
}
#[cfg(feature = "stack-overflow-check")]
taskCHECK_FOR_STACK_OVERFLOW();
taskSELECT_HIGHEST_PRIORITY_TASK();
crate::trace::traceTASK_SWITCHED_IN();
}
}
#[inline(always)]
pub unsafe fn vTaskYieldWithinAPI() {
portYIELD();
}
#[cfg(feature = "task-delete")]
pub unsafe fn vTaskDelete(xTaskToDelete: TaskHandle_t) {
unsafe {
let mut xDeleteTCBInIdleTask: BaseType_t = pdFALSE;
taskENTER_CRITICAL();
{
let pxTCB = prvGetTCBFromHandle(xTaskToDelete);
configASSERT(!pxTCB.is_null());
if uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem)) == 0 {
taskRESET_READY_PRIORITY((*pxTCB).uxPriority);
}
if !listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem)).is_null() {
uxListRemove(ptr::addr_of_mut!((*pxTCB).xEventListItem));
}
uxTaskNumber = uxTaskNumber.wrapping_add(1);
let xTaskIsRunning = pxTCB == pxCurrentTCB;
if xSchedulerRunning != pdFALSE && xTaskIsRunning {
vListInsertEnd(
ptr::addr_of_mut!(xTasksWaitingTermination),
ptr::addr_of_mut!((*pxTCB).xStateListItem),
);
uxDeletedTasksWaitingCleanUp += 1;
crate::trace::traceTASK_DELETE(pxTCB as *mut c_void);
xDeleteTCBInIdleTask = pdTRUE;
xYieldPendings[0] = pdTRUE;
} else {
uxCurrentNumberOfTasks -= 1;
crate::trace::traceTASK_DELETE(pxTCB as *mut c_void);
prvResetNextTaskUnblockTime();
}
}
taskEXIT_CRITICAL();
if xDeleteTCBInIdleTask != pdTRUE {
prvDeleteTCB(prvGetTCBFromHandle(xTaskToDelete));
}
if xSchedulerRunning != pdFALSE {
let pxTCB = prvGetTCBFromHandle(xTaskToDelete);
if pxTCB == pxCurrentTCB {
configASSERT(uxSchedulerSuspended == 0);
portYIELD_WITHIN_API();
}
}
}
}
#[cfg(feature = "task-suspend")]
pub unsafe fn vTaskSuspend(xTaskToSuspend: TaskHandle_t) {
unsafe {
taskENTER_CRITICAL();
{
let pxTCB = prvGetTCBFromHandle(xTaskToSuspend);
configASSERT(!pxTCB.is_null());
crate::trace::traceTASK_SUSPEND(pxTCB as *mut c_void);
if uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem)) == 0 {
taskRESET_READY_PRIORITY((*pxTCB).uxPriority);
}
if listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem)) != ptr::null_mut() {
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xEventListItem));
}
vListInsertEnd(
ptr::addr_of_mut!(xSuspendedTaskList),
ptr::addr_of_mut!((*pxTCB).xStateListItem),
);
for i in 0..configTASK_NOTIFICATION_ARRAY_ENTRIES {
if prvReadNotifyState(pxTCB, i) == taskWAITING_NOTIFICATION {
prvWriteNotifyState(pxTCB, i, taskNOT_WAITING_NOTIFICATION);
}
}
}
taskEXIT_CRITICAL();
if xSchedulerRunning != pdFALSE {
taskENTER_CRITICAL();
{
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL();
}
let pxTCB = prvGetTCBFromHandle(xTaskToSuspend);
if pxTCB == pxCurrentTCB {
if xSchedulerRunning != pdFALSE {
portYIELD_WITHIN_API();
} else {
if listCURRENT_LIST_LENGTH(ptr::addr_of!(xSuspendedTaskList))
== uxCurrentNumberOfTasks
{
pxCurrentTCB = ptr::null_mut();
} else {
vTaskSwitchContext();
}
}
}
}
}
#[cfg(feature = "task-suspend")]
pub unsafe fn vTaskResume(xTaskToResume: TaskHandle_t) {
unsafe {
let pxTCB = xTaskToResume as *mut TCB_t;
configASSERT(!pxTCB.is_null());
if !pxTCB.is_null() && pxTCB != pxCurrentTCB {
taskENTER_CRITICAL();
{
if prvTaskIsTaskSuspended(pxTCB) != pdFALSE {
crate::trace::traceTASK_RESUME(pxTCB as *mut c_void);
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
prvAddTaskToReadyList(pxTCB);
if (*pxTCB).uxPriority > (*pxCurrentTCB).uxPriority {
portYIELD_WITHIN_API();
}
}
}
taskEXIT_CRITICAL();
}
}
}
#[cfg(feature = "task-suspend")]
pub unsafe fn xTaskResumeFromISR(xTaskToResume: TaskHandle_t) -> BaseType_t {
let mut xYieldRequired = pdFALSE;
unsafe {
let pxTCB = xTaskToResume as *mut TCB_t;
configASSERT(!pxTCB.is_null());
let uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
{
if prvTaskIsTaskSuspended(pxTCB) != pdFALSE {
crate::trace::traceTASK_RESUME(pxTCB as *mut c_void);
if uxSchedulerSuspended == 0 {
if (*pxTCB).uxPriority > (*pxCurrentTCB).uxPriority {
xYieldRequired = pdTRUE;
xYieldPendings[0] = pdTRUE;
}
let _ux = uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
prvAddTaskToReadyList(pxTCB);
} else {
vListInsertEnd(
ptr::addr_of_mut!(xPendingReadyList),
ptr::addr_of_mut!((*pxTCB).xEventListItem),
);
}
}
}
taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus);
}
xYieldRequired
}
#[cfg(feature = "task-suspend")]
unsafe fn prvTaskIsTaskSuspended(pxTCB: *const TCB_t) -> BaseType_t {
if listIS_CONTAINED_WITHIN(
ptr::addr_of!(xSuspendedTaskList),
ptr::addr_of!((*pxTCB).xStateListItem),
) != pdFALSE
{
if listIS_CONTAINED_WITHIN(
ptr::addr_of!(xPendingReadyList),
ptr::addr_of!((*pxTCB).xEventListItem),
) == pdFALSE
{
if listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem)) == ptr::null_mut() {
let mut xReturn = pdTRUE;
let mut uxIndex = 0usize;
while uxIndex < configTASK_NOTIFICATION_ARRAY_ENTRIES {
if prvReadNotifyState(pxTCB, uxIndex) == taskWAITING_NOTIFICATION {
xReturn = pdFALSE;
break;
}
uxIndex += 1;
}
return xReturn;
}
}
}
pdFALSE
}
#[inline(always)]
pub fn taskENTER_CRITICAL() {
portENTER_CRITICAL();
}
#[inline(always)]
pub fn taskEXIT_CRITICAL() {
portEXIT_CRITICAL();
}
#[inline(always)]
pub fn taskENTER_CRITICAL_FROM_ISR() -> UBaseType_t {
portSET_INTERRUPT_MASK_FROM_ISR()
}
#[inline(always)]
pub fn taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus: UBaseType_t) {
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
}
#[repr(u8)]
pub enum eNotifyAction {
eNoAction = 0,
eSetBits = 1,
eIncrement = 2,
eSetValueWithOverwrite = 3,
eSetValueWithoutOverwrite = 4,
}
const tskDEFAULT_INDEX_TO_NOTIFY: UBaseType_t = 0;
pub unsafe fn xTaskNotify(xTaskToNotify: TaskHandle_t, ulValue: u32, eAction: i32) -> BaseType_t {
xTaskGenericNotify(
xTaskToNotify,
tskDEFAULT_INDEX_TO_NOTIFY,
ulValue,
eAction,
ptr::null_mut(),
)
}
pub unsafe fn xTaskNotifyIndexed(
xTaskToNotify: TaskHandle_t,
uxIndexToNotify: UBaseType_t,
ulValue: u32,
eAction: i32,
) -> BaseType_t {
xTaskGenericNotify(
xTaskToNotify,
uxIndexToNotify,
ulValue,
eAction,
ptr::null_mut(),
)
}
pub unsafe fn xTaskNotifyWait(
ulBitsToClearOnEntry: u32,
ulBitsToClearOnExit: u32,
pulNotificationValue: *mut u32,
xTicksToWait: TickType_t,
) -> BaseType_t {
xTaskGenericNotifyWait(
tskDEFAULT_INDEX_TO_NOTIFY,
ulBitsToClearOnEntry,
ulBitsToClearOnExit,
pulNotificationValue,
xTicksToWait,
)
}
pub unsafe fn xTaskNotifyWaitIndexed(
uxIndexToWait: UBaseType_t,
ulBitsToClearOnEntry: u32,
ulBitsToClearOnExit: u32,
pulNotificationValue: *mut u32,
xTicksToWait: TickType_t,
) -> BaseType_t {
xTaskGenericNotifyWait(
uxIndexToWait,
ulBitsToClearOnEntry,
ulBitsToClearOnExit,
pulNotificationValue,
xTicksToWait,
)
}
pub unsafe fn xTaskNotifyFromISR(
xTaskToNotify: TaskHandle_t,
ulValue: u32,
eAction: i32,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> BaseType_t {
xTaskGenericNotifyFromISR(
xTaskToNotify,
tskDEFAULT_INDEX_TO_NOTIFY,
ulValue,
eAction,
ptr::null_mut(),
pxHigherPriorityTaskWoken,
)
}
pub unsafe fn xTaskNotifyIndexedFromISR(
xTaskToNotify: TaskHandle_t,
uxIndexToNotify: UBaseType_t,
ulValue: u32,
eAction: i32,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> BaseType_t {
xTaskGenericNotifyFromISR(
xTaskToNotify,
uxIndexToNotify,
ulValue,
eAction,
ptr::null_mut(),
pxHigherPriorityTaskWoken,
)
}
pub unsafe fn xTaskNotifyGiveIndexed(
xTaskToNotify: TaskHandle_t,
uxIndexToNotify: UBaseType_t,
) -> BaseType_t {
xTaskGenericNotify(
xTaskToNotify,
uxIndexToNotify,
0,
eNotifyAction::eIncrement as i32,
ptr::null_mut(),
)
}
pub unsafe fn xTaskNotifyGive(xTaskToNotify: TaskHandle_t) -> BaseType_t {
xTaskNotifyGiveIndexed(xTaskToNotify, tskDEFAULT_INDEX_TO_NOTIFY)
}
pub unsafe fn vTaskNotifyGiveIndexedFromISR(
xTaskToNotify: TaskHandle_t,
uxIndexToNotify: UBaseType_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
let _ = xTaskGenericNotifyFromISR(
xTaskToNotify,
uxIndexToNotify,
0,
eNotifyAction::eIncrement as i32,
ptr::null_mut(),
pxHigherPriorityTaskWoken,
);
}
pub unsafe fn vTaskNotifyGiveFromISR(
xTaskToNotify: TaskHandle_t,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) {
vTaskNotifyGiveIndexedFromISR(
xTaskToNotify,
tskDEFAULT_INDEX_TO_NOTIFY,
pxHigherPriorityTaskWoken,
);
}
pub unsafe fn xTaskGenericNotify(
xTaskToNotify: TaskHandle_t,
uxIndexToNotify: UBaseType_t,
ulValue: u32,
eAction: i32,
pulPreviousNotificationValue: *mut u32,
) -> BaseType_t {
configASSERT(!xTaskToNotify.is_null());
configASSERT((uxIndexToNotify as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES);
configASSERT(
eAction >= eNotifyAction::eNoAction as i32
&& eAction <= eNotifyAction::eSetValueWithoutOverwrite as i32,
);
let pxTCB = xTaskToNotify as *mut TCB_t;
let uxIndex = uxIndexToNotify as usize;
let mut xReturn: BaseType_t = pdPASS;
let ucOriginalNotifyState: u8;
taskENTER_CRITICAL();
{
if !pulPreviousNotificationValue.is_null() {
*pulPreviousNotificationValue = prvReadNotifiedValue(pxTCB, uxIndex);
}
ucOriginalNotifyState = prvReadNotifyState(pxTCB, uxIndex);
prvWriteNotifyState(pxTCB, uxIndex, taskNOTIFICATION_RECEIVED);
match eAction {
x if x == eNotifyAction::eSetBits as i32 => {
prvWriteNotifiedValue(
pxTCB,
uxIndex,
prvReadNotifiedValue(pxTCB, uxIndex) | ulValue,
);
}
x if x == eNotifyAction::eIncrement as i32 => {
prvWriteNotifiedValue(
pxTCB,
uxIndex,
prvReadNotifiedValue(pxTCB, uxIndex).wrapping_add(1),
);
}
x if x == eNotifyAction::eSetValueWithOverwrite as i32 => {
prvWriteNotifiedValue(pxTCB, uxIndex, ulValue);
}
x if x == eNotifyAction::eSetValueWithoutOverwrite as i32 => {
if ucOriginalNotifyState != taskNOTIFICATION_RECEIVED {
prvWriteNotifiedValue(pxTCB, uxIndex, ulValue);
} else {
xReturn = pdFAIL;
}
}
x if x == eNotifyAction::eNoAction as i32 => {
}
_ => configASSERT(false),
}
if ucOriginalNotifyState == taskWAITING_NOTIFICATION {
let _ = uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
prvAddTaskToReadyList(pxTCB);
configASSERT(listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem)).is_null());
#[cfg(feature = "tickless-idle")]
prvResetNextTaskUnblockTime();
if (*pxTCB).uxPriority > (*pxCurrentTCB).uxPriority {
portYIELD_WITHIN_API();
}
}
}
taskEXIT_CRITICAL();
xReturn
}
pub unsafe fn xTaskGenericNotifyWait(
uxIndexToWait: UBaseType_t,
ulBitsToClearOnEntry: u32,
ulBitsToClearOnExit: u32,
pulNotificationValue: *mut u32,
xTicksToWait: TickType_t,
) -> BaseType_t {
configASSERT((uxIndexToWait as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES);
let uxIndex = uxIndexToWait as usize;
let xReturn: BaseType_t;
let mut xShouldBlock: BaseType_t = pdFALSE;
if prvReadNotifyState(pxCurrentTCB, uxIndex) != taskNOTIFICATION_RECEIVED && xTicksToWait > 0 {
vTaskSuspendAll();
{
taskENTER_CRITICAL();
{
if prvReadNotifyState(pxCurrentTCB, uxIndex) != taskNOTIFICATION_RECEIVED {
prvWriteNotifiedValue(
pxCurrentTCB,
uxIndex,
prvReadNotifiedValue(pxCurrentTCB, uxIndex) & !ulBitsToClearOnEntry,
);
prvWriteNotifyState(pxCurrentTCB, uxIndex, taskWAITING_NOTIFICATION);
xShouldBlock = pdTRUE;
}
}
taskEXIT_CRITICAL();
if xShouldBlock == pdTRUE {
prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
}
}
let xAlreadyYielded = xTaskResumeAll();
if xShouldBlock == pdTRUE && xAlreadyYielded == pdFALSE {
portYIELD_WITHIN_API();
}
}
taskENTER_CRITICAL();
{
if !pulNotificationValue.is_null() {
*pulNotificationValue = prvReadNotifiedValue(pxCurrentTCB, uxIndex);
}
if prvReadNotifyState(pxCurrentTCB, uxIndex) != taskNOTIFICATION_RECEIVED {
xReturn = pdFALSE;
} else {
prvWriteNotifiedValue(
pxCurrentTCB,
uxIndex,
prvReadNotifiedValue(pxCurrentTCB, uxIndex) & !ulBitsToClearOnExit,
);
xReturn = pdTRUE;
}
prvWriteNotifyState(pxCurrentTCB, uxIndex, taskNOT_WAITING_NOTIFICATION);
}
taskEXIT_CRITICAL();
xReturn
}
pub unsafe fn xTaskGenericNotifyFromISR(
xTaskToNotify: TaskHandle_t,
uxIndexToNotify: UBaseType_t,
ulValue: u32,
eAction: i32,
pulPreviousNotificationValue: *mut u32,
pxHigherPriorityTaskWoken: *mut BaseType_t,
) -> BaseType_t {
configASSERT(!xTaskToNotify.is_null());
configASSERT((uxIndexToNotify as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES);
configASSERT(
eAction >= eNotifyAction::eNoAction as i32
&& eAction <= eNotifyAction::eSetValueWithoutOverwrite as i32,
);
let pxTCB = xTaskToNotify as *mut TCB_t;
let uxIndex = uxIndexToNotify as usize;
let mut xReturn: BaseType_t = pdPASS;
let ucOriginalNotifyState: u8;
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
if !pulPreviousNotificationValue.is_null() {
*pulPreviousNotificationValue = prvReadNotifiedValue(pxTCB, uxIndex);
}
ucOriginalNotifyState = prvReadNotifyState(pxTCB, uxIndex);
prvWriteNotifyState(pxTCB, uxIndex, taskNOTIFICATION_RECEIVED);
match eAction {
x if x == eNotifyAction::eSetBits as i32 => {
prvWriteNotifiedValue(
pxTCB,
uxIndex,
prvReadNotifiedValue(pxTCB, uxIndex) | ulValue,
);
}
x if x == eNotifyAction::eIncrement as i32 => {
prvWriteNotifiedValue(
pxTCB,
uxIndex,
prvReadNotifiedValue(pxTCB, uxIndex).wrapping_add(1),
);
}
x if x == eNotifyAction::eSetValueWithOverwrite as i32 => {
prvWriteNotifiedValue(pxTCB, uxIndex, ulValue);
}
x if x == eNotifyAction::eSetValueWithoutOverwrite as i32 => {
if ucOriginalNotifyState != taskNOTIFICATION_RECEIVED {
prvWriteNotifiedValue(pxTCB, uxIndex, ulValue);
} else {
xReturn = pdFAIL;
}
}
x if x == eNotifyAction::eNoAction as i32 => {
}
_ => configASSERT(false),
}
if ucOriginalNotifyState == taskWAITING_NOTIFICATION {
configASSERT(listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem)).is_null());
if uxSchedulerSuspended != 0 {
vListInsertEnd(
ptr::addr_of_mut!(xPendingReadyList),
ptr::addr_of_mut!((*pxTCB).xEventListItem),
);
} else {
let _ = uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
prvAddTaskToReadyList(pxTCB);
#[cfg(feature = "tickless-idle")]
prvResetNextTaskUnblockTime();
}
if (*pxTCB).uxPriority > (*pxCurrentTCB).uxPriority {
if !pxHigherPriorityTaskWoken.is_null() {
*pxHigherPriorityTaskWoken = pdTRUE;
}
xYieldPendings[0] = pdTRUE;
}
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
xReturn
}
pub unsafe fn ulTaskNotifyTakeIndexed(
uxIndexToWait: UBaseType_t,
xClearCountOnExit: BaseType_t,
xTicksToWait: TickType_t,
) -> u32 {
configASSERT((uxIndexToWait as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES);
let uxIndex = uxIndexToWait as usize;
let mut xShouldBlock = pdFALSE;
if prvReadNotifiedValue(pxCurrentTCB, uxIndex) == 0 && xTicksToWait > 0 {
vTaskSuspendAll();
{
taskENTER_CRITICAL();
{
if prvReadNotifiedValue(pxCurrentTCB, uxIndex) == 0 {
prvWriteNotifyState(pxCurrentTCB, uxIndex, taskWAITING_NOTIFICATION);
xShouldBlock = pdTRUE;
}
}
taskEXIT_CRITICAL();
if xShouldBlock == pdTRUE {
prvAddCurrentTaskToDelayedList(xTicksToWait, pdTRUE);
}
}
let xAlreadyYielded = xTaskResumeAll();
if xShouldBlock == pdTRUE && xAlreadyYielded == pdFALSE {
portYIELD_WITHIN_API();
}
}
let ulReturn: u32;
taskENTER_CRITICAL();
{
ulReturn = prvReadNotifiedValue(pxCurrentTCB, uxIndex);
if ulReturn != 0 {
if xClearCountOnExit != pdFALSE {
prvWriteNotifiedValue(pxCurrentTCB, uxIndex, 0);
} else {
prvWriteNotifiedValue(pxCurrentTCB, uxIndex, ulReturn - 1);
}
}
prvWriteNotifyState(pxCurrentTCB, uxIndex, taskNOT_WAITING_NOTIFICATION);
}
taskEXIT_CRITICAL();
ulReturn
}
pub unsafe fn ulTaskNotifyTake(xClearCountOnExit: BaseType_t, xTicksToWait: TickType_t) -> u32 {
ulTaskNotifyTakeIndexed(tskDEFAULT_INDEX_TO_NOTIFY, xClearCountOnExit, xTicksToWait)
}
pub unsafe fn xTaskNotifyStateClearIndexed(
xTask: TaskHandle_t,
uxIndexToClear: UBaseType_t,
) -> BaseType_t {
configASSERT((uxIndexToClear as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES);
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
let xReturn: BaseType_t;
taskENTER_CRITICAL();
{
if prvReadNotifyState(pxTCB, uxIndexToClear as usize) == taskNOTIFICATION_RECEIVED {
prvWriteNotifyState(pxTCB, uxIndexToClear as usize, taskNOT_WAITING_NOTIFICATION);
xReturn = pdPASS;
} else {
xReturn = pdFAIL;
}
}
taskEXIT_CRITICAL();
xReturn
}
pub unsafe fn xTaskNotifyStateClear(xTask: TaskHandle_t) -> BaseType_t {
xTaskNotifyStateClearIndexed(xTask, tskDEFAULT_INDEX_TO_NOTIFY)
}
pub unsafe fn ulTaskNotifyValueClearIndexed(
xTask: TaskHandle_t,
uxIndexToClear: UBaseType_t,
ulBitsToClear: u32,
) -> u32 {
configASSERT((uxIndexToClear as usize) < configTASK_NOTIFICATION_ARRAY_ENTRIES);
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
let ulReturn: u32;
taskENTER_CRITICAL();
{
ulReturn = prvReadNotifiedValue(pxTCB, uxIndexToClear as usize);
prvWriteNotifiedValue(pxTCB, uxIndexToClear as usize, ulReturn & !ulBitsToClear);
}
taskEXIT_CRITICAL();
ulReturn
}
pub unsafe fn ulTaskNotifyValueClear(xTask: TaskHandle_t, ulBitsToClear: u32) -> u32 {
ulTaskNotifyValueClearIndexed(xTask, tskDEFAULT_INDEX_TO_NOTIFY, ulBitsToClear)
}
#[cfg(feature = "thread-local-storage")]
pub unsafe fn vTaskSetThreadLocalStoragePointer(
xTaskToSet: TaskHandle_t,
xIndex: BaseType_t,
pvValue: *mut c_void,
) {
if xIndex >= 0 && (xIndex as usize) < configNUM_THREAD_LOCAL_STORAGE_POINTERS {
let pxTCB = prvGetTCBFromHandle(xTaskToSet);
configASSERT(!pxTCB.is_null());
(*pxTCB).pvThreadLocalStoragePointers[xIndex as usize] = pvValue;
}
}
#[cfg(feature = "thread-local-storage")]
pub unsafe fn pvTaskGetThreadLocalStoragePointer(
xTaskToQuery: TaskHandle_t,
xIndex: BaseType_t,
) -> *mut c_void {
let mut pvReturn: *mut c_void = ptr::null_mut();
if xIndex >= 0 && (xIndex as usize) < configNUM_THREAD_LOCAL_STORAGE_POINTERS {
let pxTCB = prvGetTCBFromHandle(xTaskToQuery);
configASSERT(!pxTCB.is_null());
pvReturn = (*pxTCB).pvThreadLocalStoragePointers[xIndex as usize];
}
pvReturn
}
#[cfg(feature = "application-task-tag")]
pub type TaskHookFunction_t = Option<extern "C" fn(*mut c_void) -> BaseType_t>;
#[cfg(feature = "application-task-tag")]
pub unsafe fn vTaskSetApplicationTaskTag(xTask: TaskHandle_t, pxHookFunction: TaskHookFunction_t) {
let pxTCB: *mut TCB_t;
if xTask.is_null() {
pxTCB = pxCurrentTCB;
} else {
pxTCB = xTask as *mut TCB_t;
}
taskENTER_CRITICAL();
(*pxTCB).pxTaskTag = pxHookFunction;
taskEXIT_CRITICAL();
}
#[cfg(feature = "application-task-tag")]
pub unsafe fn xTaskGetApplicationTaskTag(xTask: TaskHandle_t) -> TaskHookFunction_t {
let pxTCB: *mut TCB_t = prvGetTCBFromHandle(xTask);
let xReturn: TaskHookFunction_t;
configASSERT(!pxTCB.is_null());
taskENTER_CRITICAL();
xReturn = (*pxTCB).pxTaskTag;
taskEXIT_CRITICAL();
xReturn
}
#[cfg(feature = "application-task-tag")]
pub unsafe fn xTaskGetApplicationTaskTagFromISR(xTask: TaskHandle_t) -> TaskHookFunction_t {
let pxTCB: *mut TCB_t = prvGetTCBFromHandle(xTask);
let xReturn: TaskHookFunction_t;
configASSERT(!pxTCB.is_null());
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
xReturn = (*pxTCB).pxTaskTag;
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
xReturn
}
#[cfg(feature = "application-task-tag")]
pub unsafe fn xTaskCallApplicationTaskHook(
xTask: TaskHandle_t,
pvParameter: *mut c_void,
) -> BaseType_t {
let pxTCB: *mut TCB_t;
let xReturn: BaseType_t;
if xTask.is_null() {
pxTCB = pxCurrentTCB;
} else {
pxTCB = xTask as *mut TCB_t;
}
if let Some(hook) = (*pxTCB).pxTaskTag {
xReturn = hook(pvParameter);
} else {
xReturn = pdFAIL;
}
xReturn
}
#[cfg(any(feature = "trace-facility", feature = "stack-high-water-mark"))]
unsafe fn prvTaskCheckFreeStackSpace(
pxStack: *const StackType_t,
uxStackDepth: configSTACK_DEPTH_TYPE,
) -> configSTACK_DEPTH_TYPE {
if pxStack.is_null() || uxStackDepth == 0 {
return 0;
}
let Some(xStackBytes) = uxStackDepth.checked_mul(core::mem::size_of::<StackType_t>()) else {
return 0;
};
let pucStackBase = pxStack.cast::<u8>();
let mut uxCount = 0usize;
if portSTACK_GROWTH < 0 {
while uxCount < xStackBytes && *pucStackBase.add(uxCount) == tskSTACK_FILL_BYTE {
uxCount += 1;
}
} else {
while uxCount < xStackBytes
&& *pucStackBase.add(xStackBytes - 1 - uxCount) == tskSTACK_FILL_BYTE
{
uxCount += 1;
}
}
uxCount / core::mem::size_of::<StackType_t>()
}
#[cfg(feature = "stack-overflow-check")]
#[inline(always)]
unsafe fn taskCHECK_FOR_STACK_OVERFLOW() {
let pxTCB = pxCurrentTCB;
if pxTCB.is_null() {
return;
}
let pxStack = (*pxTCB).pxStack;
let uxStackDepth = (*pxTCB).uxStackDepth;
let Some(xStackBytes) = uxStackDepth.checked_mul(core::mem::size_of::<StackType_t>()) else {
prvCallApplicationStackOverflowHook(pxTCB);
return;
};
if pxStack.is_null() || xStackBytes == 0 || (*pxTCB).pxTopOfStack.is_null() {
prvCallApplicationStackOverflowHook(pxTCB);
return;
}
let uxBase = pxStack as usize;
let Some(uxEndExclusive) = uxBase.checked_add(xStackBytes) else {
prvCallApplicationStackOverflowHook(pxTCB);
return;
};
let uxTop = (*pxTCB).pxTopOfStack as usize;
let mut xOverflowed = if portSTACK_GROWTH < 0 {
uxTop <= uxBase
} else {
uxTop >= uxEndExclusive.saturating_sub(core::mem::size_of::<StackType_t>())
};
if configCHECK_FOR_STACK_OVERFLOW > 1 {
let pucStack = pxStack.cast::<u8>();
let uxGuardBytes = if portSTACK_GROWTH < 0 { 16 } else { 20 }.min(xStackBytes);
let mut uxIndex = 0usize;
while uxIndex < uxGuardBytes {
let uxOffset = if portSTACK_GROWTH < 0 {
uxIndex
} else {
xStackBytes - 1 - uxIndex
};
if *pucStack.add(uxOffset) != tskSTACK_FILL_BYTE {
xOverflowed = true;
break;
}
uxIndex += 1;
}
}
if xOverflowed {
prvCallApplicationStackOverflowHook(pxTCB);
}
}
#[cfg(feature = "stack-high-water-mark")]
pub unsafe fn uxTaskGetStackHighWaterMark(xTask: TaskHandle_t) -> UBaseType_t {
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
if pxTCB.is_null() {
return 0;
}
let uxFree = prvTaskCheckFreeStackSpace((*pxTCB).pxStack, (*pxTCB).uxStackDepth);
uxFree.min(UBaseType_t::MAX as usize) as UBaseType_t
}
#[cfg(feature = "stack-high-water-mark")]
pub unsafe fn uxTaskGetStackHighWaterMark2(xTask: TaskHandle_t) -> configSTACK_DEPTH_TYPE {
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
if pxTCB.is_null() {
return 0;
}
prvTaskCheckFreeStackSpace((*pxTCB).pxStack, (*pxTCB).uxStackDepth)
}
#[cfg(feature = "task-priority-set")]
pub unsafe fn uxTaskPriorityGet(xTask: TaskHandle_t) -> UBaseType_t {
let uxReturn: UBaseType_t;
taskENTER_CRITICAL();
{
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
uxReturn = (*pxTCB).uxPriority;
}
taskEXIT_CRITICAL();
uxReturn
}
#[cfg(feature = "task-priority-set")]
pub unsafe fn uxTaskPriorityGetFromISR(xTask: TaskHandle_t) -> UBaseType_t {
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
if pxTCB.is_null() {
return 0;
}
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
let uxReturn = (*pxTCB).uxPriority;
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
uxReturn
}
#[cfg(all(feature = "task-priority-set", feature = "use-mutexes"))]
pub unsafe fn uxTaskBasePriorityGet(xTask: TaskHandle_t) -> UBaseType_t {
let uxReturn: UBaseType_t;
taskENTER_CRITICAL();
{
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
uxReturn = (*pxTCB).uxBasePriority;
}
taskEXIT_CRITICAL();
uxReturn
}
#[cfg(all(feature = "task-priority-set", feature = "use-mutexes"))]
pub unsafe fn uxTaskBasePriorityGetFromISR(xTask: TaskHandle_t) -> UBaseType_t {
let uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
let pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
let uxReturn = (*pxTCB).uxBasePriority;
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
uxReturn
}
#[cfg(feature = "task-priority-set")]
pub unsafe fn vTaskPrioritySet(xTask: TaskHandle_t, uxNewPriority: UBaseType_t) {
let pxTCB: *mut TCB_t;
let uxCurrentBasePriority: UBaseType_t;
let uxPriorityUsedOnEntry: UBaseType_t;
let mut xYieldRequired: BaseType_t = pdFALSE;
configASSERT(uxNewPriority < configMAX_PRIORITIES);
let uxNewPriority = if uxNewPriority >= configMAX_PRIORITIES {
configMAX_PRIORITIES - 1
} else {
uxNewPriority
};
taskENTER_CRITICAL();
{
pxTCB = prvGetTCBFromHandle(xTask);
configASSERT(!pxTCB.is_null());
#[cfg(feature = "use-mutexes")]
{
uxCurrentBasePriority = (*pxTCB).uxBasePriority;
}
#[cfg(not(feature = "use-mutexes"))]
{
uxCurrentBasePriority = (*pxTCB).uxPriority;
}
if uxCurrentBasePriority != uxNewPriority {
if uxNewPriority > uxCurrentBasePriority {
if pxTCB != pxCurrentTCB {
if uxNewPriority > (*pxCurrentTCB).uxPriority {
xYieldRequired = pdTRUE;
}
}
} else if pxTCB == pxCurrentTCB {
xYieldRequired = pdTRUE;
}
uxPriorityUsedOnEntry = (*pxTCB).uxPriority;
#[cfg(feature = "use-mutexes")]
{
if (*pxTCB).uxBasePriority == (*pxTCB).uxPriority
|| uxNewPriority > (*pxTCB).uxPriority
{
(*pxTCB).uxPriority = uxNewPriority;
}
(*pxTCB).uxBasePriority = uxNewPriority;
}
#[cfg(not(feature = "use-mutexes"))]
{
(*pxTCB).uxPriority = uxNewPriority;
}
let xItemValue = listGET_LIST_ITEM_VALUE(ptr::addr_of!((*pxTCB).xEventListItem));
if (xItemValue & taskEVENT_LIST_ITEM_VALUE_IN_USE) == 0 {
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*pxTCB).xEventListItem),
(configMAX_PRIORITIES as TickType_t) - (uxNewPriority as TickType_t),
);
}
if listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[uxPriorityUsedOnEntry as usize]),
ptr::addr_of!((*pxTCB).xStateListItem),
) != pdFALSE
{
if uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem)) == 0 {
taskRESET_READY_PRIORITY(uxPriorityUsedOnEntry);
}
prvAddTaskToReadyList(pxTCB);
}
if xYieldRequired != pdFALSE {
portYIELD_WITHIN_API();
}
}
}
taskEXIT_CRITICAL();
}
pub unsafe fn eTaskGetState(xTask: TaskHandle_t) -> eTaskState {
let pxTCB = xTask as *const TCB_t;
configASSERT(!pxTCB.is_null());
if pxTCB == pxCurrentTCB {
return eTaskState::eRunning;
}
let pxStateList: *const List_t;
let pxEventList: *const List_t;
let pxDelayedList: *const List_t;
let pxOverflowedDelayedList: *const List_t;
taskENTER_CRITICAL();
{
pxStateList = listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xStateListItem));
pxEventList = listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem));
pxDelayedList = pxDelayedTaskList;
pxOverflowedDelayedList = pxOverflowDelayedTaskList;
}
taskEXIT_CRITICAL();
if pxEventList == ptr::addr_of!(xPendingReadyList) {
return eTaskState::eReady;
}
if pxStateList == pxDelayedList || pxStateList == pxOverflowedDelayedList {
return eTaskState::eBlocked;
}
#[cfg(feature = "task-suspend")]
{
if pxStateList == ptr::addr_of!(xSuspendedTaskList) {
if pxEventList.is_null() {
let mut is_blocked = false;
for i in 0..configTASK_NOTIFICATION_ARRAY_ENTRIES {
if prvReadNotifyState(pxTCB, i) == taskWAITING_NOTIFICATION {
is_blocked = true;
break;
}
}
if is_blocked {
return eTaskState::eBlocked;
} else {
return eTaskState::eSuspended;
}
} else {
return eTaskState::eBlocked;
}
}
}
#[cfg(feature = "task-delete")]
{
if pxStateList == ptr::addr_of!(xTasksWaitingTermination) || pxStateList.is_null() {
return eTaskState::eDeleted;
}
}
eTaskState::eReady
}
#[cfg(feature = "abort-delay")]
pub unsafe fn xTaskAbortDelay(xTask: TaskHandle_t) -> BaseType_t {
let pxTCB = xTask as *mut TCB_t;
configASSERT(!pxTCB.is_null());
let xReturn: BaseType_t;
vTaskSuspendAll();
{
if eTaskGetState(xTask) == eTaskState::eBlocked {
xReturn = pdPASS;
uxListRemove(ptr::addr_of_mut!((*pxTCB).xStateListItem));
taskENTER_CRITICAL();
{
if !listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem)).is_null() {
uxListRemove(ptr::addr_of_mut!((*pxTCB).xEventListItem));
(*pxTCB).ucDelayAborted = pdTRUE as u8;
}
}
taskEXIT_CRITICAL();
prvAddTaskToReadyList(pxTCB);
if configUSE_PREEMPTION != 0 {
if (*pxTCB).uxPriority > (*pxCurrentTCB).uxPriority {
xYieldPendings[0] = pdTRUE;
}
}
} else {
xReturn = pdFAIL;
}
}
xTaskResumeAll();
xReturn
}
#[cfg(feature = "trace-facility")]
pub unsafe fn uxTaskGetTaskNumber(xTask: TaskHandle_t) -> UBaseType_t {
if xTask.is_null() {
0
} else {
(*(xTask as *mut TCB_t)).uxTaskNumber
}
}
#[cfg(feature = "trace-facility")]
pub unsafe fn vTaskSetTaskNumber(xTask: TaskHandle_t, uxHandle: UBaseType_t) {
if !xTask.is_null() {
(*(xTask as *mut TCB_t)).uxTaskNumber = uxHandle;
}
}
#[cfg(feature = "trace-facility")]
pub unsafe fn vTaskGetInfo(
xTask: TaskHandle_t,
pxTaskStatus: *mut crate::types::TaskStatus_t,
xGetFreeStackSpace: BaseType_t,
eState: eTaskState,
) {
let pxTCB: *mut TCB_t = if xTask.is_null() {
pxCurrentTCB
} else {
xTask as *mut TCB_t
};
if pxTCB.is_null() || pxTaskStatus.is_null() {
return;
}
(*pxTaskStatus).xHandle = pxTCB as TaskHandle_t;
(*pxTaskStatus).pcTaskName = (*pxTCB).pcTaskName.as_ptr();
(*pxTaskStatus).xTaskNumber = (*pxTCB).uxTCBNumber;
(*pxTaskStatus).uxCurrentPriority = (*pxTCB).uxPriority;
#[cfg(feature = "use-mutexes")]
{
(*pxTaskStatus).uxBasePriority = (*pxTCB).uxBasePriority;
}
#[cfg(not(feature = "use-mutexes"))]
{
(*pxTaskStatus).uxBasePriority = 0;
}
#[cfg(feature = "generate-run-time-stats")]
{
(*pxTaskStatus).ulRunTimeCounter = unsafe { ulTaskGetRunTimeCounter(pxTCB.cast()) };
}
#[cfg(not(feature = "generate-run-time-stats"))]
{
(*pxTaskStatus).ulRunTimeCounter = 0;
}
(*pxTaskStatus).pxStackBase = (*pxTCB).pxStack;
if xGetFreeStackSpace != pdFALSE {
let uxFree = prvTaskCheckFreeStackSpace((*pxTCB).pxStack, (*pxTCB).uxStackDepth);
(*pxTaskStatus).usStackHighWaterMark = uxFree.min(u16::MAX as usize) as u16;
} else {
(*pxTaskStatus).usStackHighWaterMark = 0;
}
if eState != eTaskState::eInvalid {
if pxTCB == pxCurrentTCB {
(*pxTaskStatus).eCurrentState = eTaskState::eRunning as u8;
} else {
(*pxTaskStatus).eCurrentState = eState as u8;
#[cfg(feature = "task-suspend")]
if eState == eTaskState::eSuspended {
vTaskSuspendAll();
{
let mut xNotificationWaiting = pdFALSE;
let mut uxIndex = 0usize;
while uxIndex < configTASK_NOTIFICATION_ARRAY_ENTRIES {
if prvReadNotifyState(pxTCB, uxIndex) == taskWAITING_NOTIFICATION {
xNotificationWaiting = pdTRUE;
break;
}
uxIndex += 1;
}
if !listLIST_ITEM_CONTAINER(ptr::addr_of!((*pxTCB).xEventListItem)).is_null()
|| xNotificationWaiting != pdFALSE
{
(*pxTaskStatus).eCurrentState = eTaskState::eBlocked as u8;
}
}
let _ = xTaskResumeAll();
}
taskENTER_CRITICAL();
{
if listIS_CONTAINED_WITHIN(
ptr::addr_of!(xPendingReadyList),
ptr::addr_of!((*pxTCB).xEventListItem),
) != pdFALSE
{
(*pxTaskStatus).eCurrentState = eTaskState::eReady as u8;
}
}
taskEXIT_CRITICAL();
}
} else {
(*pxTaskStatus).eCurrentState = eTaskGetState(pxTCB as TaskHandle_t) as u8;
}
}
#[cfg(feature = "trace-facility")]
unsafe fn prvListTasksWithinSingleList(
pxTaskStatusArray: *mut crate::types::TaskStatus_t,
pxList: *mut List_t,
eState: eTaskState,
) -> UBaseType_t {
let mut uxTask: UBaseType_t = 0;
if listCURRENT_LIST_LENGTH(pxList) > 0 {
let pxListEnd = listGET_END_MARKER(pxList) as *mut ListItem_t;
let mut pxNextListItem = listGET_HEAD_ENTRY(pxList);
while pxNextListItem != pxListEnd {
let pxTCB = listGET_LIST_ITEM_OWNER(pxNextListItem) as *mut TCB_t;
vTaskGetInfo(
pxTCB as TaskHandle_t,
pxTaskStatusArray.add(uxTask as usize),
pdTRUE,
eState,
);
uxTask += 1;
pxNextListItem = listGET_NEXT(pxNextListItem);
}
}
uxTask
}
#[cfg(feature = "trace-facility")]
pub unsafe fn uxTaskGetSystemState(
pxTaskStatusArray: *mut crate::types::TaskStatus_t,
uxArraySize: UBaseType_t,
pulTotalRunTime: *mut configRUN_TIME_COUNTER_TYPE,
) -> UBaseType_t {
let mut uxTask: UBaseType_t = 0;
let mut uxQueue: UBaseType_t = configMAX_PRIORITIES;
if pxTaskStatusArray.is_null() {
if uxCurrentNumberOfTasks != 0 {
return 0;
}
if !pulTotalRunTime.is_null() {
#[cfg(feature = "generate-run-time-stats")]
{
*pulTotalRunTime = prvGetTotalRunTime();
}
#[cfg(not(feature = "generate-run-time-stats"))]
{
*pulTotalRunTime = 0;
}
}
return 0;
}
vTaskSuspendAll();
{
if uxArraySize >= uxCurrentNumberOfTasks {
loop {
uxQueue -= 1;
uxTask += prvListTasksWithinSingleList(
pxTaskStatusArray.add(uxTask as usize),
ptr::addr_of_mut!(pxReadyTasksLists[uxQueue as usize]),
eTaskState::eReady,
);
if uxQueue == 0 {
break;
}
}
uxTask += prvListTasksWithinSingleList(
pxTaskStatusArray.add(uxTask as usize),
pxDelayedTaskList,
eTaskState::eBlocked,
);
uxTask += prvListTasksWithinSingleList(
pxTaskStatusArray.add(uxTask as usize),
pxOverflowDelayedTaskList,
eTaskState::eBlocked,
);
#[cfg(feature = "task-delete")]
{
uxTask += prvListTasksWithinSingleList(
pxTaskStatusArray.add(uxTask as usize),
ptr::addr_of_mut!(xTasksWaitingTermination),
eTaskState::eDeleted,
);
}
#[cfg(feature = "task-suspend")]
{
uxTask += prvListTasksWithinSingleList(
pxTaskStatusArray.add(uxTask as usize),
ptr::addr_of_mut!(xSuspendedTaskList),
eTaskState::eSuspended,
);
}
if !pulTotalRunTime.is_null() {
#[cfg(feature = "generate-run-time-stats")]
{
*pulTotalRunTime = prvGetTotalRunTime();
}
#[cfg(not(feature = "generate-run-time-stats"))]
{
*pulTotalRunTime = 0;
}
}
} else {
uxTask = 0;
}
}
xTaskResumeAll();
uxTask
}
#[cfg(feature = "stats-formatting")]
pub struct BufferWriter<'a> {
buf: &'a mut [u8],
pos: usize,
}
#[cfg(feature = "stats-formatting")]
impl<'a> BufferWriter<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
if !buf.is_empty() {
buf[0] = 0;
}
BufferWriter { buf, pos: 0 }
}
pub fn len(&self) -> usize {
self.pos
}
pub fn is_empty(&self) -> bool {
self.pos == 0
}
}
#[cfg(feature = "stats-formatting")]
impl<'a> core::fmt::Write for BufferWriter<'a> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let bytes = s.as_bytes();
let available = self.buf.len().saturating_sub(1).saturating_sub(self.pos);
let copied = bytes.len().min(available);
if copied > 0 {
self.buf[self.pos..self.pos + copied].copy_from_slice(&bytes[..copied]);
self.pos += copied;
}
if !self.buf.is_empty() {
self.buf[self.pos] = 0;
}
if copied == bytes.len() {
Ok(())
} else {
Err(core::fmt::Error)
}
}
}
#[cfg(feature = "stats-formatting")]
fn prvGetTaskStateChar(eState: eTaskState) -> char {
match eState {
eTaskState::eRunning => 'X', eTaskState::eReady => 'R', eTaskState::eBlocked => 'B', eTaskState::eSuspended => 'S', eTaskState::eDeleted => 'D', eTaskState::eInvalid => '?', }
}
#[cfg(all(feature = "stats-formatting", feature = "trace-facility"))]
pub unsafe fn vTaskListTasks(pcWriteBuffer: *mut u8, uxBufferLength: usize) {
use core::fmt::Write;
configASSERT(!pcWriteBuffer.is_null());
configASSERT(uxBufferLength > 0);
let buffer = core::slice::from_raw_parts_mut(pcWriteBuffer, uxBufferLength);
let mut writer = BufferWriter::new(buffer);
let _ = writeln!(
writer,
"{:<16} {:>5} {:>4} {:>6} {:>4}",
"Name", "State", "Prio", "Stack", "Num"
);
let uxArraySize = uxTaskGetNumberOfTasks();
if uxArraySize == 0 {
return;
}
const MAX_STACK_TASKS: usize = 16;
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
{
extern crate alloc;
use alloc::vec::Vec;
let mut pxTaskStatusArray: Vec<crate::types::TaskStatus_t> = Vec::new();
if pxTaskStatusArray
.try_reserve_exact(uxArraySize as usize)
.is_err()
{
return;
}
pxTaskStatusArray.resize_with(uxArraySize as usize, crate::types::TaskStatus_t::new);
let uxTasksReturned =
uxTaskGetSystemState(pxTaskStatusArray.as_mut_ptr(), uxArraySize, ptr::null_mut());
for i in 0..uxTasksReturned as usize {
let pxTaskStatus = &pxTaskStatusArray[i];
let name = unsafe { prvGetTaskNameFromPtr(pxTaskStatus.pcTaskName) };
let state_char = prvGetTaskStateChar(core::mem::transmute::<u8, eTaskState>(
pxTaskStatus.eCurrentState,
));
let _ = writeln!(
writer,
"{:<16} {:>5} {:>4} {:>6} {:>4}",
name,
state_char,
pxTaskStatus.uxCurrentPriority,
pxTaskStatus.usStackHighWaterMark,
pxTaskStatus.xTaskNumber
);
}
}
#[cfg(not(any(feature = "alloc", feature = "heap-4", feature = "heap-5")))]
{
let mut pxTaskStatusArray: [crate::types::TaskStatus_t; MAX_STACK_TASKS] =
[const { crate::types::TaskStatus_t::new() }; MAX_STACK_TASKS];
let uxArraySizeClamped = if uxArraySize as usize > MAX_STACK_TASKS {
MAX_STACK_TASKS as UBaseType_t
} else {
uxArraySize
};
let uxTasksReturned = uxTaskGetSystemState(
pxTaskStatusArray.as_mut_ptr(),
uxArraySizeClamped,
ptr::null_mut(),
);
for i in 0..uxTasksReturned as usize {
let pxTaskStatus = &pxTaskStatusArray[i];
let name = unsafe { prvGetTaskNameFromPtr(pxTaskStatus.pcTaskName) };
let state_char = prvGetTaskStateChar(core::mem::transmute::<u8, eTaskState>(
pxTaskStatus.eCurrentState,
));
let _ = writeln!(
writer,
"{:<16} {:>5} {:>4} {:>6} {:>4}",
name,
state_char,
pxTaskStatus.uxCurrentPriority,
pxTaskStatus.usStackHighWaterMark,
pxTaskStatus.xTaskNumber
);
}
}
}
#[cfg(feature = "stats-formatting")]
unsafe fn prvGetTaskNameFromPtr<'a>(pcTaskName: *const u8) -> &'a str {
if pcTaskName.is_null() {
return "<null>";
}
let mut len = 0;
while len < configMAX_TASK_NAME_LEN && unsafe { *pcTaskName.add(len) } != 0 {
len += 1;
}
let bytes = unsafe { core::slice::from_raw_parts(pcTaskName, len) };
core::str::from_utf8(bytes).unwrap_or("<invalid>")
}
#[cfg(feature = "generate-run-time-stats")]
pub unsafe fn ulTaskGetRunTimeCounter(xTask: TaskHandle_t) -> configRUN_TIME_COUNTER_TYPE {
unsafe {
let pxTCB = prvGetTCBFromHandle(xTask);
if pxTCB.is_null() {
return 0;
}
taskENTER_CRITICAL();
let mut ulTaskRunTime = (*pxTCB).ulRunTimeCounter;
if pxTCB == pxCurrentTCB && xSchedulerRunning != pdFALSE {
let ulNow = crate::port::portGET_RUN_TIME_COUNTER_VALUE();
ulTaskRunTime =
ulTaskRunTime.wrapping_add(prvRunTimeDelta(ulNow, ulTaskSwitchedInTime));
}
taskEXIT_CRITICAL();
ulTaskRunTime
}
}
#[cfg(feature = "generate-run-time-stats")]
pub unsafe fn ulTaskGetRunTimePercent(xTask: TaskHandle_t) -> configRUN_TIME_COUNTER_TYPE {
let ulTaskRunTime = unsafe { ulTaskGetRunTimeCounter(xTask) };
let ulTotalTime = unsafe { prvGetTotalRunTime() };
if ulTotalTime == 0 {
return 0;
}
(((ulTaskRunTime as u64 * 100) / ulTotalTime as u64) as configRUN_TIME_COUNTER_TYPE).min(100)
}
#[cfg(feature = "generate-run-time-stats")]
pub fn ulTaskGetIdleRunTimeCounter() -> configRUN_TIME_COUNTER_TYPE {
unsafe {
let xIdleTaskHandle = xIdleTaskHandles[0];
if xIdleTaskHandle.is_null() {
return 0;
}
}
unsafe { ulTaskGetRunTimeCounter(xIdleTaskHandles[0]) }
}
#[cfg(feature = "generate-run-time-stats")]
pub fn ulTaskGetIdleRunTimePercent() -> configRUN_TIME_COUNTER_TYPE {
let xIdleTaskHandle = unsafe { xIdleTaskHandles[0] };
if xIdleTaskHandle.is_null() {
return 0;
}
unsafe { ulTaskGetRunTimePercent(xIdleTaskHandle) }
}
#[cfg(feature = "generate-run-time-stats")]
pub fn ulTaskGetTotalRunTime() -> configRUN_TIME_COUNTER_TYPE {
unsafe { prvGetTotalRunTime() }
}
#[cfg(all(
feature = "generate-run-time-stats",
feature = "stats-formatting",
feature = "trace-facility"
))]
pub unsafe fn vTaskGetRunTimeStatistics(pcWriteBuffer: *mut u8, uxBufferLength: usize) {
use core::fmt::Write;
configASSERT(!pcWriteBuffer.is_null());
configASSERT(uxBufferLength > 0);
let buffer = core::slice::from_raw_parts_mut(pcWriteBuffer, uxBufferLength);
let mut writer = BufferWriter::new(buffer);
let _ = writeln!(writer, "{:<16} {:>12} {:>8}", "Name", "Abs. Time", "% Time");
let uxArraySize = uxTaskGetNumberOfTasks();
if uxArraySize == 0 {
return;
}
let ulTotalTime = prvGetTotalRunTime();
if ulTotalTime == 0 {
let _ = writeln!(writer, "(No run time data collected yet)");
return;
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
{
extern crate alloc;
use alloc::vec::Vec;
let mut pxTaskStatusArray: Vec<crate::types::TaskStatus_t> = Vec::new();
if pxTaskStatusArray
.try_reserve_exact(uxArraySize as usize)
.is_err()
{
return;
}
pxTaskStatusArray.resize_with(uxArraySize as usize, crate::types::TaskStatus_t::new);
let mut ulTotalRunTimeReturned: u32 = 0;
let uxTasksReturned = uxTaskGetSystemState(
pxTaskStatusArray.as_mut_ptr(),
uxArraySize,
&mut ulTotalRunTimeReturned,
);
for i in 0..uxTasksReturned as usize {
let pxTaskStatus = &pxTaskStatusArray[i];
let ulStatsAsPercentage = if ulTotalTime > 0 {
((pxTaskStatus.ulRunTimeCounter as u64 * 100) / ulTotalTime as u64) as u32
} else {
0
};
let pcTaskName = pxTaskStatus.pcTaskName;
let name = unsafe { prvGetTaskNameFromPtr(pcTaskName) };
if ulStatsAsPercentage > 0 {
let _ = writeln!(
writer,
"{:<16} {:>12} {:>7}%",
name, pxTaskStatus.ulRunTimeCounter, ulStatsAsPercentage
);
} else {
let _ = writeln!(
writer,
"{:<16} {:>12} {:>7}",
name, pxTaskStatus.ulRunTimeCounter, "<1%"
);
}
}
}
#[cfg(not(any(feature = "alloc", feature = "heap-4", feature = "heap-5")))]
{
const MAX_STACK_TASKS: usize = 16;
let mut pxTaskStatusArray: [crate::types::TaskStatus_t; MAX_STACK_TASKS] =
[const { crate::types::TaskStatus_t::new() }; MAX_STACK_TASKS];
let uxActualArraySize = if uxArraySize as usize > MAX_STACK_TASKS {
MAX_STACK_TASKS as UBaseType_t
} else {
uxArraySize
};
let mut ulTotalRunTimeReturned: u32 = 0;
let uxTasksReturned = uxTaskGetSystemState(
pxTaskStatusArray.as_mut_ptr(),
uxActualArraySize,
&mut ulTotalRunTimeReturned,
);
for i in 0..uxTasksReturned as usize {
let pxTaskStatus = &pxTaskStatusArray[i];
let ulStatsAsPercentage = if ulTotalTime > 0 {
((pxTaskStatus.ulRunTimeCounter as u64 * 100) / ulTotalTime as u64) as u32
} else {
0
};
let pcTaskName = pxTaskStatus.pcTaskName;
let name = unsafe { prvGetTaskNameFromPtr(pcTaskName) };
if ulStatsAsPercentage > 0 {
let _ = writeln!(
writer,
"{:<16} {:>12} {:>7}%",
name, pxTaskStatus.ulRunTimeCounter, ulStatsAsPercentage
);
} else {
let _ = writeln!(
writer,
"{:<16} {:>12} {:>7}",
name, pxTaskStatus.ulRunTimeCounter, "<1%"
);
}
}
}
}
#[cfg(feature = "tickless-idle")]
fn prvGetExpectedIdleTime() -> TickType_t {
unsafe {
let mut xReturn: TickType_t;
let mut xHigherPriorityReadyTasks = pdFALSE;
if uxTopReadyPriority > tskIDLE_PRIORITY {
xHigherPriorityReadyTasks = pdTRUE;
}
if !pxCurrentTCB.is_null() && (*pxCurrentTCB).uxPriority > tskIDLE_PRIORITY {
xReturn = 0;
} else if listCURRENT_LIST_LENGTH(ptr::addr_of!(
pxReadyTasksLists[tskIDLE_PRIORITY as usize]
)) > 1
{
xReturn = 0;
} else if xHigherPriorityReadyTasks != pdFALSE {
xReturn = 0;
} else {
xReturn = xNextTaskUnblockTime;
xReturn = xReturn.wrapping_sub(xTickCount);
}
xReturn
}
}
#[cfg(feature = "tickless-idle")]
pub unsafe fn eTaskConfirmSleepModeStatus() -> crate::types::eSleepModeStatus {
use crate::types::eSleepModeStatus;
unsafe {
if listCURRENT_LIST_LENGTH(ptr::addr_of!(xPendingReadyList)) != 0 {
return eSleepModeStatus::eAbortSleep;
}
if xYieldPendings[0] != pdFALSE {
return eSleepModeStatus::eAbortSleep;
}
if xPendedTicks != 0 {
return eSleepModeStatus::eAbortSleep;
}
#[cfg(feature = "task-suspend")]
{
let uxNonApplicationTasks: UBaseType_t = configNUMBER_OF_CORES as UBaseType_t;
if listCURRENT_LIST_LENGTH(ptr::addr_of!(xSuspendedTaskList))
== (uxCurrentNumberOfTasks - uxNonApplicationTasks)
{
return eSleepModeStatus::eNoTasksWaitingTimeout;
}
}
eSleepModeStatus::eStandardSleep
}
}
#[cfg(feature = "tickless-idle")]
pub unsafe fn vTaskStepTick(xTicksToJump: TickType_t) {
unsafe {
let mut xTicksToJump = xTicksToJump;
let xUpdatedTickCount = xTickCount.wrapping_add(xTicksToJump);
configASSERT(xUpdatedTickCount <= xNextTaskUnblockTime);
if xUpdatedTickCount == xNextTaskUnblockTime {
configASSERT(uxSchedulerSuspended != 0);
configASSERT(xTicksToJump != 0);
taskENTER_CRITICAL();
{
xPendedTicks = xPendedTicks.wrapping_add(1);
}
taskEXIT_CRITICAL();
xTicksToJump -= 1;
}
xTickCount = xTickCount.wrapping_add(xTicksToJump);
crate::trace::traceINCREASE_TICK_COUNT(xTicksToJump);
}
}
pub unsafe fn xTaskCatchUpTicks(xTicksToCatchUp: TickType_t) -> BaseType_t {
configASSERT(uxSchedulerSuspended == 0);
vTaskSuspendAll();
taskENTER_CRITICAL();
{
xPendedTicks = xPendedTicks.wrapping_add(xTicksToCatchUp);
}
taskEXIT_CRITICAL();
xTaskResumeAll()
}
pub unsafe fn vTaskResetState() {
pxCurrentTCB = ptr::null_mut();
#[cfg(feature = "task-delete")]
{
uxDeletedTasksWaitingCleanUp = 0;
}
uxCurrentNumberOfTasks = 0;
xTickCount = configINITIAL_TICK_COUNT;
uxTopReadyPriority = tskIDLE_PRIORITY;
xSchedulerRunning = pdFALSE;
xPendedTicks = 0;
xYieldPendings[0] = pdFALSE;
xNumOfOverflows = 0;
uxTaskNumber = 0;
xNextTaskUnblockTime = 0;
uxSchedulerSuspended = 0;
#[cfg(feature = "generate-run-time-stats")]
{
ulTaskSwitchedInTime = 0;
ulRunTimeStart = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "stack-overflow-check")]
use core::sync::atomic::{AtomicUsize, Ordering};
extern "C" fn test_task(_parameter: *mut c_void) {}
#[cfg(feature = "stack-overflow-check")]
static STACK_OVERFLOW_CALLS: AtomicUsize = AtomicUsize::new(0);
#[cfg(feature = "stack-overflow-check")]
extern "C" fn stack_overflow_hook(_task: TaskHandle_t, _name: *const u8) {
STACK_OVERFLOW_CALLS.fetch_add(1, Ordering::SeqCst);
}
#[cfg(feature = "port-test")]
unsafe fn initialise_synthetic_tcb(tcb: *mut TCB_t, priority: UBaseType_t) {
(*tcb).uxPriority = priority;
vListInitialiseItem(ptr::addr_of_mut!((*tcb).xStateListItem));
vListInitialiseItem(ptr::addr_of_mut!((*tcb).xEventListItem));
listSET_LIST_ITEM_OWNER(ptr::addr_of_mut!((*tcb).xStateListItem), tcb.cast());
listSET_LIST_ITEM_OWNER(ptr::addr_of_mut!((*tcb).xEventListItem), tcb.cast());
listSET_LIST_ITEM_VALUE(
ptr::addr_of_mut!((*tcb).xEventListItem),
(configMAX_PRIORITIES - priority) as TickType_t,
);
}
#[cfg(feature = "port-test")]
unsafe fn reset_synthetic_scheduler_state() {
prvInitialiseTaskLists();
pxCurrentTCB = ptr::null_mut();
uxCurrentNumberOfTasks = 0;
uxTopReadyPriority = tskIDLE_PRIORITY;
xSchedulerRunning = pdFALSE;
xTickCount = configINITIAL_TICK_COUNT;
xPendedTicks = 0;
xYieldPendings[0] = pdFALSE;
xNumOfOverflows = 0;
xNextTaskUnblockTime = portMAX_DELAY;
uxSchedulerSuspended = 0;
}
#[cfg(all(feature = "port-test", feature = "use-mutexes"))]
unsafe fn initialise_synthetic_mutex_tcb(tcb: *mut TCB_t, priority: UBaseType_t) {
initialise_synthetic_tcb(tcb, priority);
(*tcb).uxBasePriority = priority;
}
#[test]
fn static_task_storage_matches_tcb_layout() {
assert_eq!(
core::mem::size_of::<StaticTask_t>(),
core::mem::size_of::<TCB_t>()
);
assert_eq!(
core::mem::align_of::<StaticTask_t>(),
core::mem::align_of::<TCB_t>()
);
}
#[test]
fn scheduler_state_values_match_the_public_freertos_contract() {
assert_eq!(taskSCHEDULER_SUSPENDED, 0);
assert_eq!(taskSCHEDULER_NOT_STARTED, 1);
assert_eq!(taskSCHEDULER_RUNNING, 2);
}
#[cfg(all(
feature = "port-test",
feature = "record-stack-high-address",
not(feature = "arch-64bit")
))]
#[test]
fn static_task_records_the_aligned_high_stack_address() {
#[repr(align(8))]
struct AlignedStack([StackType_t; 2]);
crate::port::test_port_reset();
let mut stack = AlignedStack([0; 2]);
let mut task_buffer = StaticTask_t::new();
let task_buffer_ptr = ptr::addr_of_mut!(task_buffer);
unsafe {
pxCurrentTCB = ptr::null_mut();
uxCurrentNumberOfTasks = 0;
xSchedulerRunning = pdFALSE;
uxTopReadyPriority = tskIDLE_PRIORITY;
let handle = xTaskCreateStatic(
test_task,
b"aligned\0".as_ptr(),
stack.0.len(),
ptr::null_mut(),
1,
stack.0.as_mut_ptr(),
task_buffer_ptr,
);
assert!(!handle.is_null());
let tcb = handle as *mut TCB_t;
assert_eq!((*tcb).pxEndOfStack, (*tcb).pxTopOfStack);
assert_eq!((*tcb).pxEndOfStack as usize % portBYTE_ALIGNMENT, 0);
let mut returned_stack = ptr::null_mut();
let mut returned_tcb = ptr::null_mut();
assert_eq!(
xTaskGetStaticBuffers(handle, &mut returned_stack, &mut returned_tcb),
pdTRUE
);
assert_eq!(returned_stack, stack.0.as_mut_ptr());
assert!(returned_tcb == task_buffer_ptr);
prvInitialiseTaskLists();
pxCurrentTCB = ptr::null_mut();
uxCurrentNumberOfTasks = 0;
uxTopReadyPriority = tskIDLE_PRIORITY;
}
}
#[cfg(feature = "stats-formatting")]
#[test]
fn diagnostic_buffer_writer_always_reserves_a_nul() {
use core::fmt::Write;
let mut untouched = [0xa5; 4];
let writer = BufferWriter::new(&mut untouched);
assert!(writer.is_empty());
assert_eq!(untouched[0], 0);
let mut truncated = [0xa5; 4];
let mut writer = BufferWriter::new(&mut truncated);
assert!(writer.write_str("abcdef").is_err());
assert_eq!(&truncated, b"abc\0");
}
#[cfg(feature = "port-test")]
#[test]
fn resume_all_drains_pending_ready_and_recomputes_unblock_time() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut pending = TCB_t::new();
let mut later = TCB_t::new();
let current_ptr = ptr::addr_of_mut!(current);
let pending_ptr = ptr::addr_of_mut!(pending);
let later_ptr = ptr::addr_of_mut!(later);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 2);
initialise_synthetic_tcb(pending_ptr, 2);
initialise_synthetic_tcb(later_ptr, 0);
prvAddTaskToReadyList(current_ptr);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*pending_ptr).xStateListItem), 10);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*later_ptr).xStateListItem), 20);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*pending_ptr).xStateListItem),
);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*later_ptr).xStateListItem),
);
vListInsertEnd(
ptr::addr_of_mut!(xPendingReadyList),
ptr::addr_of_mut!((*pending_ptr).xEventListItem),
);
pxCurrentTCB = current_ptr;
uxCurrentNumberOfTasks = 3;
xSchedulerRunning = pdTRUE;
xNextTaskUnblockTime = 10;
uxSchedulerSuspended = 1;
assert_eq!(xTaskResumeAll(), pdFALSE);
assert_eq!(xNextTaskUnblockTime, 20);
assert_eq!(xYieldPendings[0], pdFALSE);
assert!(
listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[(*pending_ptr).uxPriority as usize]),
ptr::addr_of!((*pending_ptr).xStateListItem),
) != pdFALSE
);
assert_eq!(crate::port::test_port_snapshot().yield_count, 0);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(feature = "port-test")]
#[test]
fn delay_does_not_yield_twice_when_resume_all_already_yielded() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut pending = TCB_t::new();
let current_ptr = ptr::addr_of_mut!(current);
let pending_ptr = ptr::addr_of_mut!(pending);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 1);
initialise_synthetic_tcb(pending_ptr, 3);
prvAddTaskToReadyList(current_ptr);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*pending_ptr).xStateListItem), 20);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*pending_ptr).xStateListItem),
);
vListInsertEnd(
ptr::addr_of_mut!(xPendingReadyList),
ptr::addr_of_mut!((*pending_ptr).xEventListItem),
);
pxCurrentTCB = current_ptr;
uxCurrentNumberOfTasks = 2;
xSchedulerRunning = pdTRUE;
xNextTaskUnblockTime = 20;
vTaskDelay(5);
assert_eq!(crate::port::test_port_snapshot().yield_count, 1);
assert_eq!(xNextTaskUnblockTime, 5);
assert!(
listIS_CONTAINED_WITHIN(
pxDelayedTaskList,
ptr::addr_of!((*current_ptr).xStateListItem),
) != pdFALSE
);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(all(feature = "port-test", feature = "tickless-idle"))]
#[test]
fn event_wakeup_latches_yield_and_recomputes_tickless_deadline() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut unblocked = TCB_t::new();
let mut later = TCB_t::new();
let mut event_list = List_t::new();
let current_ptr = ptr::addr_of_mut!(current);
let unblocked_ptr = ptr::addr_of_mut!(unblocked);
let later_ptr = ptr::addr_of_mut!(later);
let event_list_ptr = ptr::addr_of_mut!(event_list);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 1);
initialise_synthetic_tcb(unblocked_ptr, 3);
initialise_synthetic_tcb(later_ptr, 0);
vListInitialise(event_list_ptr);
prvAddTaskToReadyList(current_ptr);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*unblocked_ptr).xStateListItem), 10);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*later_ptr).xStateListItem), 20);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*unblocked_ptr).xStateListItem),
);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*later_ptr).xStateListItem),
);
vListInsert(
event_list_ptr,
ptr::addr_of_mut!((*unblocked_ptr).xEventListItem),
);
pxCurrentTCB = current_ptr;
uxCurrentNumberOfTasks = 3;
xSchedulerRunning = pdTRUE;
xNextTaskUnblockTime = 10;
taskENTER_CRITICAL();
let xYieldRequired = xTaskRemoveFromEventList(event_list_ptr);
taskEXIT_CRITICAL();
assert_eq!(xYieldRequired, pdTRUE);
assert_eq!(xYieldPendings[0], pdTRUE);
assert_eq!(xNextTaskUnblockTime, 20);
assert!(
listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[(*unblocked_ptr).uxPriority as usize]),
ptr::addr_of!((*unblocked_ptr).xStateListItem),
) != pdFALSE
);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(all(feature = "port-test", feature = "tickless-idle"))]
#[test]
fn task_notification_increment_wraps_and_updates_tickless_deadline() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut notified = TCB_t::new();
let mut later = TCB_t::new();
let current_ptr = ptr::addr_of_mut!(current);
let notified_ptr = ptr::addr_of_mut!(notified);
let later_ptr = ptr::addr_of_mut!(later);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 1);
initialise_synthetic_tcb(notified_ptr, 3);
initialise_synthetic_tcb(later_ptr, 0);
prvAddTaskToReadyList(current_ptr);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*notified_ptr).xStateListItem), 10);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*later_ptr).xStateListItem), 20);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*notified_ptr).xStateListItem),
);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*later_ptr).xStateListItem),
);
prvWriteNotifiedValue(notified_ptr, 0, u32::MAX);
prvWriteNotifyState(notified_ptr, 0, taskWAITING_NOTIFICATION);
pxCurrentTCB = current_ptr;
uxCurrentNumberOfTasks = 3;
xSchedulerRunning = pdTRUE;
xNextTaskUnblockTime = 10;
assert_eq!(
xTaskGenericNotify(
notified_ptr.cast(),
0,
0,
eNotifyAction::eIncrement as i32,
ptr::null_mut(),
),
pdPASS
);
assert_eq!(prvReadNotifiedValue(notified_ptr, 0), 0);
assert_eq!(xNextTaskUnblockTime, 20);
assert_eq!(crate::port::test_port_snapshot().yield_count, 1);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(all(feature = "port-test", feature = "tickless-idle"))]
#[test]
fn isr_notification_wakeup_updates_deadline_and_yield_outputs() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut notified = TCB_t::new();
let mut later = TCB_t::new();
let mut xHigherPriorityTaskWoken = pdFALSE;
let current_ptr = ptr::addr_of_mut!(current);
let notified_ptr = ptr::addr_of_mut!(notified);
let later_ptr = ptr::addr_of_mut!(later);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 1);
initialise_synthetic_tcb(notified_ptr, 3);
initialise_synthetic_tcb(later_ptr, 0);
prvAddTaskToReadyList(current_ptr);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*notified_ptr).xStateListItem), 10);
listSET_LIST_ITEM_VALUE(ptr::addr_of_mut!((*later_ptr).xStateListItem), 20);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*notified_ptr).xStateListItem),
);
vListInsert(
pxDelayedTaskList,
ptr::addr_of_mut!((*later_ptr).xStateListItem),
);
prvWriteNotifiedValue(notified_ptr, 0, u32::MAX);
prvWriteNotifyState(notified_ptr, 0, taskWAITING_NOTIFICATION);
pxCurrentTCB = current_ptr;
uxCurrentNumberOfTasks = 3;
xSchedulerRunning = pdTRUE;
xNextTaskUnblockTime = 10;
crate::port::test_port_set_inside_interrupt(true);
assert_eq!(
xTaskGenericNotifyFromISR(
notified_ptr.cast(),
0,
0,
eNotifyAction::eIncrement as i32,
ptr::null_mut(),
&mut xHigherPriorityTaskWoken,
),
pdPASS
);
crate::port::test_port_set_inside_interrupt(false);
assert_eq!(prvReadNotifiedValue(notified_ptr, 0), 0);
assert_eq!(xHigherPriorityTaskWoken, pdTRUE);
assert_eq!(xYieldPendings[0], pdTRUE);
assert_eq!(xNextTaskUnblockTime, 20);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(all(feature = "port-test", feature = "task-suspend"))]
#[test]
fn maximum_timeout_is_indefinite_only_with_task_suspend_enabled() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut timeout = TimeOut_t {
xOverflowCount: 0,
xTimeOnEntering: 5,
};
let mut ticks = portMAX_DELAY;
let current_ptr = ptr::addr_of_mut!(current);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 1);
pxCurrentTCB = current_ptr;
xTickCount = 10;
xNumOfOverflows = 1;
assert_eq!(xTaskCheckForTimeOut(&mut timeout, &mut ticks), pdFALSE);
assert_eq!(ticks, portMAX_DELAY);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(all(feature = "port-test", not(feature = "task-suspend")))]
#[test]
fn maximum_timeout_remains_finite_without_task_suspend() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut timeout = TimeOut_t {
xOverflowCount: 0,
xTimeOnEntering: 5,
};
let mut ticks = portMAX_DELAY;
let current_ptr = ptr::addr_of_mut!(current);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 1);
pxCurrentTCB = current_ptr;
xTickCount = 10;
xNumOfOverflows = 1;
assert_eq!(xTaskCheckForTimeOut(&mut timeout, &mut ticks), pdTRUE);
assert_eq!(ticks, 0);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(feature = "stats-formatting")]
#[test]
fn invalid_utf8_task_names_are_formatted_without_unchecked_str() {
let name = [0xff_u8, 0];
assert_eq!(unsafe { prvGetTaskNameFromPtr(name.as_ptr()) }, "<invalid>");
}
#[cfg(all(feature = "port-test", feature = "stack-high-water-mark"))]
#[test]
fn high_water_scan_is_bounded_and_reports_whole_unused_stack() {
let mut stack = [0 as StackType_t; 8];
unsafe {
ptr::write_bytes(
stack.as_mut_ptr().cast::<u8>(),
tskSTACK_FILL_BYTE,
core::mem::size_of_val(&stack),
);
}
let stack_ptr = stack.as_mut_ptr();
let mut tcb = TCB_t::new();
tcb.pxStack = stack_ptr;
tcb.uxStackDepth = stack.len();
assert_eq!(
unsafe { uxTaskGetStackHighWaterMark2((&mut tcb as *mut TCB_t).cast()) },
stack.len()
);
unsafe {
*stack_ptr
.cast::<u8>()
.add(3 * core::mem::size_of::<StackType_t>()) = 0;
}
assert_eq!(
unsafe { uxTaskGetStackHighWaterMark2((&mut tcb as *mut TCB_t).cast()) },
3
);
}
#[cfg(all(feature = "port-test", feature = "stack-overflow-check"))]
#[test]
fn corrupted_stack_guard_calls_registered_application_hook() {
crate::port::test_port_reset();
STACK_OVERFLOW_CALLS.store(0, Ordering::SeqCst);
let mut stack = [0 as StackType_t; 8];
let stack_ptr = stack.as_mut_ptr();
unsafe {
ptr::write_bytes(
stack_ptr.cast::<u8>(),
tskSTACK_FILL_BYTE,
core::mem::size_of_val(&stack),
);
}
let mut tcb = TCB_t::new();
tcb.pxStack = stack_ptr;
tcb.uxStackDepth = stack.len();
tcb.pxTopOfStack = unsafe { stack_ptr.add(stack.len() - 1) };
unsafe {
xSchedulerRunning = pdFALSE;
pxCurrentTCB = &mut tcb;
}
assert_eq!(vTaskSetStackOverflowHook(Some(stack_overflow_hook)), pdPASS);
unsafe {
*stack_ptr.cast::<u8>() = 0;
taskCHECK_FOR_STACK_OVERFLOW();
}
assert_eq!(STACK_OVERFLOW_CALLS.load(Ordering::SeqCst), 1);
assert_eq!(vTaskSetStackOverflowHook(None), pdPASS);
unsafe {
pxCurrentTCB = ptr::null_mut();
}
}
#[cfg(all(feature = "port-test", feature = "generate-run-time-stats"))]
#[test]
fn runtime_delta_handles_counter_wrap_and_current_interval() {
assert_eq!(prvRunTimeDelta(3, u32::MAX - 1), 5);
crate::port::test_port_reset();
let mut tcb = TCB_t::new();
tcb.ulRunTimeCounter = 7;
unsafe {
pxCurrentTCB = &mut tcb;
xSchedulerRunning = pdTRUE;
ulRunTimeStart = 0;
ulTaskSwitchedInTime = 0;
}
crate::port::portINCREMENT_RUN_TIME_COUNTER();
crate::port::portINCREMENT_RUN_TIME_COUNTER();
assert_eq!(
unsafe { ulTaskGetRunTimeCounter((&mut tcb as *mut TCB_t).cast()) },
9
);
assert_eq!(ulTaskGetTotalRunTime(), 2);
unsafe {
xSchedulerRunning = pdFALSE;
pxCurrentTCB = ptr::null_mut();
}
}
#[cfg(all(feature = "port-test", feature = "trace-facility"))]
#[test]
fn task_status_uses_unique_tcb_number_and_reports_current_task_prestart() {
let mut stack = [0 as StackType_t; 1];
unsafe {
ptr::write_bytes(
stack.as_mut_ptr().cast::<u8>(),
tskSTACK_FILL_BYTE,
core::mem::size_of_val(&stack),
);
}
let mut tcb = TCB_t::new();
tcb.pxStack = stack.as_mut_ptr();
tcb.uxStackDepth = stack.len();
tcb.uxTCBNumber = 41;
tcb.uxTaskNumber = 99;
let mut status = crate::types::TaskStatus_t::new();
unsafe {
xSchedulerRunning = pdFALSE;
pxCurrentTCB = &mut tcb;
vTaskGetInfo(
(&mut tcb as *mut TCB_t).cast(),
&mut status,
pdTRUE,
eTaskState::eReady,
);
pxCurrentTCB = ptr::null_mut();
}
assert_eq!(status.xTaskNumber, 41);
assert_eq!(status.usStackHighWaterMark, 1);
assert_eq!(status.eCurrentState, eTaskState::eRunning as u8);
}
#[cfg(all(
feature = "port-test",
feature = "trace-facility",
feature = "task-suspend"
))]
#[test]
fn task_info_corrects_indefinite_block_and_pending_ready_states() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let mut target = TCB_t::new();
let mut status = crate::types::TaskStatus_t::new();
let current_ptr = ptr::addr_of_mut!(current);
let target_ptr = ptr::addr_of_mut!(target);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 3);
initialise_synthetic_tcb(target_ptr, 1);
prvAddTaskToReadyList(current_ptr);
vListInsertEnd(
ptr::addr_of_mut!(xSuspendedTaskList),
ptr::addr_of_mut!((*target_ptr).xStateListItem),
);
prvWriteNotifyState(target_ptr, 0, taskWAITING_NOTIFICATION);
pxCurrentTCB = current_ptr;
uxCurrentNumberOfTasks = 2;
xSchedulerRunning = pdTRUE;
vTaskGetInfo(
target_ptr.cast(),
&mut status,
pdFALSE,
eTaskState::eSuspended,
);
assert_eq!(status.eCurrentState, eTaskState::eBlocked as u8);
prvWriteNotifyState(target_ptr, 0, taskNOT_WAITING_NOTIFICATION);
vListInsertEnd(
ptr::addr_of_mut!(xPendingReadyList),
ptr::addr_of_mut!((*target_ptr).xEventListItem),
);
uxSchedulerSuspended = 1;
vTaskGetInfo(
target_ptr.cast(),
&mut status,
pdFALSE,
eTaskState::eSuspended,
);
assert_eq!(status.eCurrentState, eTaskState::eReady as u8);
let _ = xTaskResumeAll();
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(feature = "port-test")]
#[test]
fn catch_up_ticks_processes_each_elapsed_tick() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
let current_ptr = ptr::addr_of_mut!(current);
unsafe {
reset_synthetic_scheduler_state();
initialise_synthetic_tcb(current_ptr, 1);
prvAddTaskToReadyList(current_ptr);
pxCurrentTCB = current_ptr;
uxCurrentNumberOfTasks = 1;
xSchedulerRunning = pdTRUE;
assert_eq!(xTaskCatchUpTicks(3), pdFALSE);
assert_eq!(xTickCount, configINITIAL_TICK_COUNT.wrapping_add(3));
assert_eq!(xPendedTicks, 0);
reset_synthetic_scheduler_state();
}
crate::port::test_port_reset();
}
#[cfg(all(feature = "port-test", feature = "task-delete"))]
#[test]
fn idle_cleanup_skips_critical_section_when_nothing_is_pending() {
crate::port::test_port_reset();
unsafe {
reset_synthetic_scheduler_state();
prvCheckTasksWaitingTermination();
}
let snapshot = crate::port::test_port_snapshot();
assert_eq!(snapshot.interrupt_disable_count, 0);
assert_eq!(snapshot.interrupt_enable_count, 0);
}
#[cfg(all(feature = "port-test", feature = "trace-facility"))]
#[test]
fn empty_system_state_accepts_null_zero_length_array() {
crate::port::test_port_reset();
let mut total = 123;
unsafe {
reset_synthetic_scheduler_state();
assert_eq!(uxTaskGetSystemState(ptr::null_mut(), 0, &mut total), 0);
}
}
#[cfg(feature = "port-test")]
#[test]
fn scheduler_suspend_preserves_preexisting_interrupt_mask() {
crate::port::test_port_reset();
unsafe {
uxSchedulerSuspended = 0;
}
portDISABLE_INTERRUPTS();
let before = crate::port::test_port_snapshot();
assert!(before.interrupts_masked);
unsafe { vTaskSuspendAll() };
let after = crate::port::test_port_snapshot();
assert!(after.interrupts_masked);
assert_eq!(after.interrupt_enable_count, before.interrupt_enable_count);
unsafe {
uxSchedulerSuspended = 0;
}
}
#[cfg(all(feature = "port-test", feature = "task-suspend"))]
#[test]
fn resume_from_isr_moves_a_live_suspended_task_to_ready() {
crate::port::test_port_reset();
let mut current = TCB_t::new();
current.uxPriority = 1;
let mut resumed = TCB_t::new();
resumed.uxPriority = 3;
let current_ptr = ptr::addr_of_mut!(current);
let resumed_ptr = ptr::addr_of_mut!(resumed);
unsafe {
prvInitialiseTaskLists();
vListInitialiseItem(ptr::addr_of_mut!((*resumed_ptr).xStateListItem));
vListInitialiseItem(ptr::addr_of_mut!((*resumed_ptr).xEventListItem));
listSET_LIST_ITEM_OWNER(
ptr::addr_of_mut!((*resumed_ptr).xStateListItem),
resumed_ptr.cast(),
);
listSET_LIST_ITEM_OWNER(
ptr::addr_of_mut!((*resumed_ptr).xEventListItem),
resumed_ptr.cast(),
);
vListInsertEnd(
ptr::addr_of_mut!(xSuspendedTaskList),
ptr::addr_of_mut!((*resumed_ptr).xStateListItem),
);
pxCurrentTCB = current_ptr;
uxSchedulerSuspended = 0;
crate::port::test_port_set_inside_interrupt(true);
let xYieldRequired = xTaskResumeFromISR(resumed_ptr.cast());
crate::port::test_port_set_inside_interrupt(false);
assert_eq!(xYieldRequired, pdTRUE);
assert!(
listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[(*resumed_ptr).uxPriority as usize]),
ptr::addr_of!((*resumed_ptr).xStateListItem),
) != pdFALSE
);
assert!(
listIS_CONTAINED_WITHIN(
ptr::addr_of!(xSuspendedTaskList),
ptr::addr_of!((*resumed_ptr).xStateListItem),
) == pdFALSE
);
prvInitialiseTaskLists();
pxCurrentTCB = ptr::null_mut();
xYieldPendings[0] = pdFALSE;
}
crate::port::test_port_reset();
}
#[cfg(feature = "port-test")]
#[test]
fn event_list_keys_are_unique_and_priority_ordered() {
crate::port::test_port_reset();
let mut previous_key: Option<TickType_t> = None;
let mut uxPriority = 0;
while uxPriority < configMAX_PRIORITIES {
let mut tcb = TCB_t::new();
let mut stack_word: StackType_t = 0;
let tcb_ptr = ptr::addr_of_mut!(tcb);
unsafe {
prvInitialiseNewTask(
test_task,
b"test\0".as_ptr(),
1,
ptr::null_mut(),
uxPriority,
ptr::null_mut(),
tcb_ptr,
&mut stack_word,
);
}
let key = unsafe { listGET_LIST_ITEM_VALUE(ptr::addr_of!((*tcb_ptr).xEventListItem)) };
assert_eq!(key, (configMAX_PRIORITIES - uxPriority) as TickType_t);
if let Some(previous) = previous_key {
assert!(key < previous);
}
previous_key = Some(key);
uxPriority += 1;
}
}
#[cfg(all(feature = "port-test", feature = "use-mutexes"))]
#[test]
fn priority_inheritance_reports_both_new_and_existing_inheritance() {
crate::port::test_port_reset();
let mut holder = TCB_t::new();
holder.uxMutexesHeld = 1;
let mut waiter = TCB_t::new();
let holder_ptr = ptr::addr_of_mut!(holder);
let waiter_ptr = ptr::addr_of_mut!(waiter);
unsafe {
initialise_synthetic_mutex_tcb(holder_ptr, 1);
initialise_synthetic_mutex_tcb(waiter_ptr, 3);
pxCurrentTCB = waiter_ptr;
assert_eq!(xTaskPriorityInherit(holder_ptr.cast()), pdTRUE);
assert_eq!((*holder_ptr).uxPriority, 3);
assert_eq!(
listGET_LIST_ITEM_VALUE(ptr::addr_of!((*holder_ptr).xEventListItem)),
(configMAX_PRIORITIES - (*waiter_ptr).uxPriority) as TickType_t
);
let mut second_waiter = TCB_t::new();
let second_waiter_ptr = ptr::addr_of_mut!(second_waiter);
initialise_synthetic_mutex_tcb(second_waiter_ptr, 2);
pxCurrentTCB = second_waiter_ptr;
assert_eq!(xTaskPriorityInherit(holder_ptr.cast()), pdTRUE);
assert_eq!((*holder_ptr).uxPriority, 3);
pxCurrentTCB = ptr::null_mut();
}
}
#[cfg(all(feature = "port-test", feature = "use-mutexes"))]
#[test]
fn timeout_disinherits_only_sole_mutex_and_repositions_ready_task() {
crate::port::test_port_reset();
let mut holder = TCB_t::new();
holder.uxMutexesHeld = 2;
let holder_ptr = ptr::addr_of_mut!(holder);
let mut waiter = TCB_t::new();
let waiter_ptr = ptr::addr_of_mut!(waiter);
unsafe {
initialise_synthetic_mutex_tcb(holder_ptr, 4);
(*holder_ptr).uxBasePriority = 1;
initialise_synthetic_mutex_tcb(waiter_ptr, 3);
prvInitialiseTaskLists();
prvAddTaskToReadyList(holder_ptr);
pxCurrentTCB = waiter_ptr;
vTaskPriorityDisinheritAfterTimeout(holder_ptr.cast(), 2);
assert_eq!((*holder_ptr).uxPriority, 4);
assert!(
listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[4]),
ptr::addr_of!((*holder_ptr).xStateListItem),
) != pdFALSE
);
(*holder_ptr).uxMutexesHeld = 1;
vTaskPriorityDisinheritAfterTimeout(holder_ptr.cast(), 2);
assert_eq!((*holder_ptr).uxPriority, 2);
assert_eq!(
listGET_LIST_ITEM_VALUE(ptr::addr_of!((*holder_ptr).xEventListItem)),
(configMAX_PRIORITIES - 2) as TickType_t
);
assert!(
listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[2]),
ptr::addr_of!((*holder_ptr).xStateListItem),
) != pdFALSE
);
pxCurrentTCB = ptr::null_mut();
}
}
#[cfg(all(feature = "port-test", feature = "use-mutexes"))]
#[test]
fn giving_last_of_multiple_mutexes_restores_base_priority() {
crate::port::test_port_reset();
let mut holder = TCB_t::new();
holder.uxMutexesHeld = 2;
let holder_ptr = ptr::addr_of_mut!(holder);
unsafe {
initialise_synthetic_mutex_tcb(holder_ptr, 4);
(*holder_ptr).uxBasePriority = 1;
prvInitialiseTaskLists();
prvAddTaskToReadyList(holder_ptr);
pxCurrentTCB = holder_ptr;
assert_eq!(xTaskPriorityDisinherit(holder_ptr.cast()), pdFALSE);
assert_eq!((*holder_ptr).uxMutexesHeld, 1);
assert_eq!((*holder_ptr).uxPriority, 4);
assert_eq!(xTaskPriorityDisinherit(holder_ptr.cast()), pdTRUE);
assert_eq!((*holder_ptr).uxMutexesHeld, 0);
assert_eq!((*holder_ptr).uxPriority, 1);
assert!(
listIS_CONTAINED_WITHIN(
ptr::addr_of!(pxReadyTasksLists[1]),
ptr::addr_of!((*holder_ptr).xStateListItem),
) != pdFALSE
);
pxCurrentTCB = ptr::null_mut();
}
}
}