Skip to content
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ deps += $(LIB_OBJS:%.o=%.o.d)
APPS := coop echo hello mqueues semaphore mutex cond \
pipes pipes_small pipes_struct prodcons progress \
rtsched suspend test64 timer timer_kill \
cpubench test_libc umode
cpubench test_utils umode

# Output files for __link target
IMAGE_BASE := $(BUILD_DIR)/image
Expand Down
7 changes: 1 addition & 6 deletions app/rtsched.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,12 +404,7 @@ static int32_t edf_sched(void)
/* Scan all tasks to find the one with earliest deadline */
list_node_t *node = list_next(kcb->tasks->head);
while (node && node != kcb->tasks->tail) {
if (!node->data) {
node = list_next(node);
continue;
}

tcb_t *task = (tcb_t *) node->data;
tcb_t *task = tcb_from_global_node(node);

/* Consider both READY and RUNNING RT tasks for preemptive scheduling */
if ((task->state == TASK_READY || task->state == TASK_RUNNING) &&
Expand Down
44 changes: 44 additions & 0 deletions app/test_libc.c → app/test_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,49 @@ void test_mixed_formats(void)
ASSERT_TEST(buf[test_strlen(buf)] == '\0', "Mixed format null termination");
}

/* Test 11: List helpers behavior */
typedef struct {
int val;
list_node_t node;
} list_node_item_t;

void test_list_pushback_and_remove(void)
{
list_t *list = list_create();

list_node_item_t first = {.node.next = NULL, .val = 1};
list_node_item_t second = {.node.next = NULL, .val = 2};
list_node_item_t third = {.node.next = NULL, .val = 3};

/* Check node push back normally - unlinked and linked */
list_pushback(list, &first.node);
ASSERT_TEST(list->length == 1, "Push back first node ");

list_pushback(list, &second.node);
list_node_item_t *item =
container_of(list->head->next, list_node_item_t, node);
ASSERT_TEST(list->length == 2 && item->val == 1,
"Push back second node and order preserved ");


list_pushback(list, &third.node);
item = container_of(list->head->next->next->next, list_node_item_t, node);
ASSERT_TEST(list->length == 3 && item->val == 3, "Push back third node ");

/* Remove second node */
list_remove(list, &second.node);
item = container_of(list->head->next, list_node_item_t, node);
ASSERT_TEST(list->length == 2 && item->val == 1, "Remove second node ");

/* Remove non-existing node (second time) */

item = container_of(list_pop(list), list_node_item_t, node);
ASSERT_TEST(list->length == 1 && item->val == 1, "Pop node ");

list_clear(list);
ASSERT_TEST(list_is_empty(list), "List is cleared ");
}

void test_runner(void)
{
printf("\n=== LibC Test Suite ===\n");
Expand All @@ -313,6 +356,7 @@ void test_runner(void)
test_buffer_boundaries();
test_isr_safety();
test_mixed_formats();
test_list_pushback_and_remove();

printf("\n=== Test Summary ===\n");
printf("Tests run: %d\n", tests_run);
Expand Down
11 changes: 10 additions & 1 deletion app/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,22 @@ int32_t app_main(void)
mo_timer_create(timer_callback, 1000, (void *) 1);
mo_timer_create(timer_callback, 3000, (void *) 2);
mo_timer_create(timer_callback, 500, (void *) 3);
mo_timer_create(timer_callback, 100, (void *) 4);

/* Start all created timers in auto-reload mode.
* Note: In this simple case, the IDs will be 0x6000, 0x6001, and 0x6002.
*/
mo_timer_start(0x6000, TIMER_AUTORELOAD);
mo_timer_start(0x6001, TIMER_AUTORELOAD);
mo_timer_start(0x6001, TIMER_ONESHOT);
mo_timer_start(0x6002, TIMER_AUTORELOAD);
mo_timer_start(0x6003, TIMER_AUTORELOAD);

/* Timer destroy to confirm functions workable; now only timer 3 will run
* and timer 2 only one shot */
mo_timer_destroy(0x6000);
mo_timer_cancel(0x6003);
/* Destroyed timer can't be resumed */
mo_timer_start(0x6000, TIMER_AUTORELOAD);

/* Spawn a single idle task to keep the kernel running. */
mo_task_spawn(idle_task, DEFAULT_STACK_SIZE);
Expand Down
2 changes: 1 addition & 1 deletion arch/riscv/hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ void hal_switch_stack(void **old_sp, void *new_sp)
*/
void hal_interrupt_tick(void)
{
tcb_t *task = kcb->task_current->data;
tcb_t *task = tcb_from_global_node(kcb->task_current);
if (unlikely(!task))
hal_panic();

Expand Down
28 changes: 9 additions & 19 deletions include/lib/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
/* List node */
typedef struct list_node {
struct list_node *next;
void *data;
} list_node_t;

/* Public list descriptor */
Expand Down Expand Up @@ -45,10 +44,8 @@ static inline list_t *list_create(void)
}

head->next = tail;
head->data = NULL;

tail->next = NULL;
tail->data = NULL;

list->head = head;
list->tail = tail;
Expand Down Expand Up @@ -78,16 +75,11 @@ static inline list_node_t *list_cnext(const list_t *list,

/* Push and pop */

static inline list_node_t *list_pushback(list_t *list, void *data)
static inline list_node_t *list_pushback(list_t *list, list_node_t *node)
{
if (unlikely(!list))
return NULL;

list_node_t *node = malloc(sizeof(*node));
if (unlikely(!node))
if (unlikely(!list || !node || node->next))
return NULL;

node->data = data;
node->next = list->tail;

/* Insert before tail sentinel */
Expand All @@ -100,22 +92,21 @@ static inline list_node_t *list_pushback(list_t *list, void *data)
return node;
}

static inline void *list_pop(list_t *list)
static inline list_node_t *list_pop(list_t *list)
{
if (unlikely(list_is_empty(list)))
return NULL;

list_node_t *first = list->head->next;
list->head->next = first->next;
first->next = NULL;

void *data = first->data;
free(first);
list->length--;
return data;
return first;
}

/* Remove a specific node; returns its data */
static inline void *list_remove(list_t *list, list_node_t *target)
/* Remove a specific node from the list */
static inline list_node_t *list_remove(list_t *list, list_node_t *target)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return value is required for the exception handling when removing the waiter node from the mutex waiting list.

{
if (unlikely(!list || !target || list_is_empty(list)))
return NULL;
Expand All @@ -128,10 +119,9 @@ static inline void *list_remove(list_t *list, list_node_t *target)
return NULL; /* node not found */

prev->next = target->next;
void *data = target->data;
free(target);
target->next = NULL;
list->length--;
return data;
return target;
}

/* Iteration */
Expand Down
22 changes: 22 additions & 0 deletions include/private/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@
*/

#include <lib/libc.h>
#include <stddef.h>

/*
* container_of - get the pointer to the parent structure from a member pointer
*
* @ptr: pointer to the struct member
* @type: type of the parent structure
* @member: name of the member within the parent structure
*
* This macro computes the address of the parent structure by subtracting
* the member's offset within the structure.
*/
#define container_of(ptr, type, member) \
((type *) ((char *) (ptr) - offsetof(type, member)))

/* tcb list node helpers */
#define tcb_from_global_node(p) container_of(p, tcb_t, global_node)
#define tcb_from_mutex_node(p) container_of(p, tcb_t, mutex_node)

/* timer list node helpers */
#define timer_from_node(p) container_of(p, timer_t, t_node)
#define timer_from_running_node(p) container_of(p, timer_t, t_running_node)

/* Compiler Optimization Hints
*
Expand Down
4 changes: 4 additions & 0 deletions include/sys/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ typedef struct tcb {

/* Stack Protection */
uint32_t canary; /* Random stack canary for overflow detection */

/* Embedded nodes */
list_node_t global_node; /* Global task list */
list_node_t mutex_node; /* Mutex waiting list */
} tcb_t;

/* Kernel Control Block (KCB)
Expand Down
4 changes: 4 additions & 0 deletions include/sys/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ typedef struct {
/* Callback Configuration */
void *(*callback)(void *arg); /* Function to execute upon timer expiry */
void *arg; /* User-defined argument passed to callback */

/* Embedded node for timer */
list_node_t t_node; /* All timer list node*/
list_node_t t_running_node; /* Running timer list node */
} timer_t;

/* Timer Management Functions */
Expand Down
2 changes: 1 addition & 1 deletion kernel/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ int32_t main(void)
* 'kcb->task_current' was set by the first call to mo_task_spawn.
* This function transfers control and does not return.
*/
tcb_t *first_task = kcb->task_current->data;
tcb_t *first_task = tcb_from_global_node(kcb->task_current);
if (!first_task)
panic(ERR_NO_TASKS);

Expand Down
37 changes: 18 additions & 19 deletions kernel/mutex.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,35 +45,33 @@ static inline void cond_invalidate(cond_t *c)
*/
static bool remove_self_from_waiters(list_t *waiters)
{
if (unlikely(!waiters || !kcb || !kcb->task_current ||
!kcb->task_current->data))
if (unlikely(!waiters || !kcb || !kcb->task_current))
return false;

tcb_t *self = kcb->task_current->data;
tcb_t *self = tcb_from_global_node(kcb->task_current);

/* Search for and remove self from waiters list */
list_node_t *curr = waiters->head->next;
while (curr && curr != waiters->tail) {
if (curr->data == self) {
list_remove(waiters, curr);
list_node_t *curr_mutex_node = waiters->head->next;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is better aligned with the embedded list node.

while (curr_mutex_node && curr_mutex_node != waiters->tail) {
if (tcb_from_mutex_node(curr_mutex_node) == self) {
list_remove(waiters, curr_mutex_node);
return true;
}
curr = curr->next;
curr_mutex_node = curr_mutex_node->next;
}
return false;
}

/* Atomic block operation with enhanced error checking */
static void mutex_block_atomic(list_t *waiters)
{
if (unlikely(!waiters || !kcb || !kcb->task_current ||
!kcb->task_current->data))
if (unlikely(!waiters || !kcb || !kcb->task_current))
panic(ERR_SEM_OPERATION);

tcb_t *self = kcb->task_current->data;
tcb_t *self = tcb_from_global_node(kcb->task_current);

/* Add to waiters list */
if (unlikely(!list_pushback(waiters, self)))
if (unlikely(!list_pushback(waiters, &self->mutex_node)))
panic(ERR_SEM_OPERATION);

/* Block and yield atomically */
Expand Down Expand Up @@ -218,8 +216,8 @@ int32_t mo_mutex_timedlock(mutex_t *m, uint32_t ticks)
}

/* Slow path: must block with timeout using delay mechanism */
tcb_t *self = kcb->task_current->data;
if (unlikely(!list_pushback(m->waiters, self))) {
tcb_t *self = tcb_from_global_node(kcb->task_current);
if (unlikely(!list_pushback(m->waiters, &self->mutex_node))) {
NOSCHED_LEAVE();
panic(ERR_SEM_OPERATION);
}
Expand Down Expand Up @@ -277,7 +275,8 @@ int32_t mo_mutex_unlock(mutex_t *m)
m->owner_tid = 0;
} else {
/* Transfer ownership to next waiter (FIFO) */
tcb_t *next_owner = (tcb_t *) list_pop(m->waiters);
list_node_t *next_owner_node = (list_node_t *) list_pop(m->waiters);
tcb_t *next_owner = tcb_from_mutex_node(next_owner_node);
if (likely(next_owner)) {
/* Validate task state before waking */
if (likely(next_owner->state == TASK_BLOCKED)) {
Expand Down Expand Up @@ -378,11 +377,11 @@ int32_t mo_cond_wait(cond_t *c, mutex_t *m)
if (unlikely(!mo_mutex_owned_by_current(m)))
return ERR_NOT_OWNER;

tcb_t *self = kcb->task_current->data;
tcb_t *self = tcb_from_global_node(kcb->task_current);

/* Atomically add to wait list */
NOSCHED_ENTER();
if (unlikely(!list_pushback(c->waiters, self))) {
if (unlikely(!list_pushback(c->waiters, &self->mutex_node))) {
NOSCHED_LEAVE();
panic(ERR_SEM_OPERATION);
}
Expand Down Expand Up @@ -420,11 +419,11 @@ int32_t mo_cond_timedwait(cond_t *c, mutex_t *m, uint32_t ticks)
return ERR_TIMEOUT;
}

tcb_t *self = kcb->task_current->data;
tcb_t *self = tcb_from_global_node(kcb->task_current);

/* Atomically add to wait list with timeout */
NOSCHED_ENTER();
if (unlikely(!list_pushback(c->waiters, self))) {
if (unlikely(!list_pushback(c->waiters, &self->mutex_node))) {
NOSCHED_LEAVE();
panic(ERR_SEM_OPERATION);
}
Expand Down
Loading
Loading