Skip to content

Commit 2644558

Browse files
committed
Add task termination infrastructure for fault handling
Implement deferred cleanup mechanism to safely terminate tasks that encounter unrecoverable faults. Tasks cannot immediately free their own resources since they execute on the stack being freed. The solution uses a two-phase approach: when termination is requested, the task is marked as suspended with a special flag indicating pending cleanup, then yields to another task. After the context switch completes and we're executing in a different context, the scheduler detects the marked task and safely frees its resources. This infrastructure enables graceful handling of PMP violations and other unrecoverable task faults without panicking the entire system.
1 parent 0b17a78 commit 2644558

File tree

2 files changed

+95
-3
lines changed

2 files changed

+95
-3
lines changed

include/sys/task.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ enum task_states {
4444
TASK_SUSPENDED /* Task paused/excluded from scheduling until resumed */
4545
};
4646

47+
/* Task Flags */
48+
#define TASK_FLAG_ZOMBIE 0x01 /* Task terminated, awaiting cleanup */
49+
4750
/* Priority Level Constants for Priority-Aware Time Slicing */
4851
#define TASK_PRIORITY_LEVELS 8 /* Number of priority levels (0-7) */
4952
#define TASK_HIGHEST_PRIORITY 0 /* Highest priority level */
@@ -83,7 +86,7 @@ typedef struct tcb {
8386
uint16_t delay; /* Ticks remaining for task in TASK_BLOCKED state */
8487
uint16_t id; /* Unique task ID, assigned by kernel upon creation */
8588
uint8_t state; /* Current lifecycle state (e.g., TASK_READY) */
86-
uint8_t flags; /* Task flags for future extensions (reserved) */
89+
uint8_t flags; /* Task flags (TASK_FLAG_ZOMBIE for deferred cleanup) */
8790

8891
/* Real-time Scheduling Support */
8992
void *rt_prio; /* Opaque pointer for custom real-time scheduler hook */
@@ -281,6 +284,16 @@ uint64_t mo_uptime(void);
281284
*/
282285
void _sched_block(queue_t *wait_q);
283286

287+
/* Terminates the currently running task due to unrecoverable fault.
288+
*
289+
* Marks the current task as suspended and sets the zombie flag for deferred
290+
* cleanup. Forces an immediate context switch to another task. The marked
291+
* task's resources will be freed by the scheduler after the switch completes.
292+
*
293+
* This function does not return - execution continues in another task.
294+
*/
295+
void task_terminate_current(void) __attribute__((noreturn));
296+
284297
/* Application Entry Point */
285298

286299
/* The main entry point for the user application.

kernel/task.c

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,48 @@ static int32_t noop_rtsched(void)
458458
return -1;
459459
}
460460

461+
/* Helper to clean up zombie task resources from safe context */
462+
static void cleanup_zombie_task(tcb_t *zombie)
463+
{
464+
if (!zombie || !(zombie->flags & TASK_FLAG_ZOMBIE))
465+
return;
466+
467+
/* Find and remove task node from list */
468+
CRITICAL_ENTER();
469+
470+
list_node_t *node = NULL;
471+
list_node_t *iter = kcb->tasks->head;
472+
while (iter) {
473+
if (iter->data == zombie) {
474+
node = iter;
475+
break;
476+
}
477+
iter = iter->next;
478+
}
479+
480+
if (node) {
481+
list_remove(kcb->tasks, node);
482+
kcb->task_count--;
483+
484+
/* Clear from cache */
485+
for (int i = 0; i < TASK_CACHE_SIZE; i++) {
486+
if (task_cache[i].task == zombie) {
487+
task_cache[i].id = 0;
488+
task_cache[i].task = NULL;
489+
}
490+
}
491+
}
492+
493+
CRITICAL_LEAVE();
494+
495+
/* Free resources outside critical section */
496+
if (zombie->mspace)
497+
mo_memspace_destroy(zombie->mspace);
498+
499+
free(zombie->stack);
500+
free(zombie);
501+
}
502+
461503
/* The main entry point from the system tick interrupt. */
462504
void dispatcher(void)
463505
{
@@ -497,12 +539,17 @@ void dispatch(void)
497539
list_foreach(kcb->tasks, delay_update_batch, &ready_count);
498540

499541
/* Save old task before scheduler modifies task_current */
500-
memspace_t *old_mspace = ((tcb_t *) kcb->task_current->data)->mspace;
542+
tcb_t *old_task = (tcb_t *)kcb->task_current->data;
543+
memspace_t *old_mspace = old_task->mspace;
501544

502545
/* Hook for real-time scheduler - if it selects a task, use it */
503546
if (kcb->rt_sched() < 0)
504547
sched_select_next_task(); /* Use O(1) priority scheduler */
505548

549+
/* Clean up zombie task from previous context switch */
550+
if (old_task->flags & TASK_FLAG_ZOMBIE)
551+
cleanup_zombie_task(old_task);
552+
506553
hal_interrupt_tick();
507554

508555
/* Switch PMP configuration if tasks have different memory spaces */
@@ -535,10 +582,15 @@ void yield(void)
535582
list_foreach(kcb->tasks, delay_update, NULL);
536583

537584
/* Save old task before scheduler modifies task_current */
538-
memspace_t *old_mspace = ((tcb_t *) kcb->task_current->data)->mspace;
585+
tcb_t *old_task = (tcb_t *)kcb->task_current->data;
586+
memspace_t *old_mspace = old_task->mspace;
539587

540588
sched_select_next_task(); /* Use O(1) priority scheduler */
541589

590+
/* Clean up zombie task from previous context switch */
591+
if (old_task->flags & TASK_FLAG_ZOMBIE)
592+
cleanup_zombie_task(old_task);
593+
542594
/* Switch PMP configuration if tasks have different memory spaces */
543595
memspace_t *new_mspace = ((tcb_t *) kcb->task_current->data)->mspace;
544596
pmp_switch_context(old_mspace, new_mspace);
@@ -714,6 +766,33 @@ int32_t mo_task_cancel(uint16_t id)
714766
return ERR_OK;
715767
}
716768

769+
void task_terminate_current(void)
770+
{
771+
NOSCHED_ENTER();
772+
773+
/* Verify we have a current task */
774+
if (unlikely(!kcb || !kcb->task_current || !kcb->task_current->data)) {
775+
NOSCHED_LEAVE();
776+
panic(ERR_NO_TASKS);
777+
}
778+
779+
tcb_t *self = kcb->task_current->data;
780+
781+
/* Mark as suspended to prevent re-scheduling */
782+
self->state = TASK_SUSPENDED;
783+
784+
/* Set zombie flag for deferred cleanup */
785+
self->flags |= TASK_FLAG_ZOMBIE;
786+
787+
NOSCHED_LEAVE();
788+
789+
/* Force immediate context switch - never returns */
790+
_dispatch();
791+
792+
/* Unreachable */
793+
__builtin_unreachable();
794+
}
795+
717796
void mo_task_yield(void)
718797
{
719798
_yield();

0 commit comments

Comments
 (0)