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)
{
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;
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