diff options
Diffstat (limited to 'rsnix/src/sched.rs')
| -rw-r--r-- | rsnix/src/sched.rs | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/rsnix/src/sched.rs b/rsnix/src/sched.rs index 0c4938e..107dba6 100644 --- a/rsnix/src/sched.rs +++ b/rsnix/src/sched.rs | |||
| @@ -396,6 +396,31 @@ pub fn spawn(entry: fn(), name: &str) -> Option<usize> { | |||
| 396 | if ok { Some(id) } else { None } | 396 | if ok { Some(id) } else { None } |
| 397 | } | 397 | } |
| 398 | 398 | ||
| 399 | /// Reserve an empty task slot without installing a frame. The slot is | ||
| 400 | /// marked Ready-with-no-frame is NOT valid to schedule — callers must | ||
| 401 | /// follow with `launch_at` before yielding (exec does both back-to-back | ||
| 402 | /// under the preemption lock window of the caller). | ||
| 403 | pub fn reserve_slot() -> Option<usize> { | ||
| 404 | lock(); | ||
| 405 | let id = unsafe { | ||
| 406 | let mut slot = None; | ||
| 407 | for i in 2..MAX_TASKS { | ||
| 408 | if TASKS[i].state == TaskState::Done && TASKS[i].stack != 0 { | ||
| 409 | reap(i); | ||
| 410 | slot = Some(i); | ||
| 411 | break; | ||
| 412 | } | ||
| 413 | if TASKS[i].state == TaskState::Done && TASKS[i].stack == 0 && TASKS[i].name_len == 0 { | ||
| 414 | slot = Some(i); | ||
| 415 | break; | ||
| 416 | } | ||
| 417 | } | ||
| 418 | slot | ||
| 419 | }; | ||
| 420 | unlock(); | ||
| 421 | id | ||
| 422 | } | ||
| 423 | |||
| 399 | unsafe fn reap(id: usize) { | 424 | unsafe fn reap(id: usize) { |
| 400 | unsafe { | 425 | unsafe { |
| 401 | let t = &mut TASKS[id]; | 426 | let t = &mut TASKS[id]; |
| @@ -408,6 +433,19 @@ unsafe fn reap(id: usize) { | |||
| 408 | } | 433 | } |
| 409 | } | 434 | } |
| 410 | 435 | ||
| 436 | /// Install the initial frame for a reserved slot and mark it Ready. | ||
| 437 | /// The slot must have come from `reserve_slot` and must not have been | ||
| 438 | /// scheduled in between (exec stores its context between the two calls). | ||
| 439 | pub fn launch_at(id: usize, entry: fn(), name: &str) -> bool { | ||
| 440 | if id >= MAX_TASKS { | ||
| 441 | return false; | ||
| 442 | } | ||
| 443 | lock(); | ||
| 444 | let ok = spawn_at(id, entry, name); | ||
| 445 | unlock(); | ||
| 446 | ok | ||
| 447 | } | ||
| 448 | |||
| 411 | fn spawn_at(id: usize, entry: fn(), name: &str) -> bool { | 449 | fn spawn_at(id: usize, entry: fn(), name: &str) -> bool { |
| 412 | let Some(base) = memory::alloc_pages(STACK_PAGES) else { | 450 | let Some(base) = memory::alloc_pages(STACK_PAGES) else { |
| 413 | return false; | 451 | return false; |
| @@ -483,7 +521,7 @@ unsafe extern "C" fn trampoline(entry: usize) -> ! { | |||
| 483 | task_exit() | 521 | task_exit() |
| 484 | } | 522 | } |
| 485 | 523 | ||
| 486 | fn task_exit() -> ! { | 524 | pub fn task_exit() -> ! { |
| 487 | lock(); | 525 | lock(); |
| 488 | unsafe { | 526 | unsafe { |
| 489 | TASKS[CURRENT].state = TaskState::Done; | 527 | TASKS[CURRENT].state = TaskState::Done; |
| @@ -648,6 +686,12 @@ pub fn abort_current(msg: &str) -> ! { | |||
| 648 | TASKS[id].stack, | 686 | TASKS[id].stack, |
| 649 | TASKS[id].guard | 687 | TASKS[id].guard |
| 650 | ); | 688 | ); |
| 689 | // An exec'd binary died: unblock the waiting spawner with a | ||
| 690 | // completion message (exit code -1 encoded as u64::MAX). The | ||
| 691 | // shell's queue is the only waiter; best-effort by design. | ||
| 692 | if id >= 2 { | ||
| 693 | let _ = push_msg(SHELL_TID, TaskMsg::User(u64::MAX)); | ||
| 694 | } | ||
| 651 | PREEMPT_DEPTH.store(0, Ordering::Relaxed); | 695 | PREEMPT_DEPTH.store(0, Ordering::Relaxed); |
| 652 | } | 696 | } |
| 653 | yield_int(); | 697 | yield_int(); |
