Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4e780ad
fix(keyboard): implement proper blocking logic and clarify fgets beha…
Galfurian Feb 27, 2026
185f7b8
fix(scheduler): handle empty runqueue and null picks; add idle loop
Galfurian Feb 27, 2026
ce36244
Standardize comments.
Galfurian Feb 27, 2026
79f2ff8
docs(wait): improve logging, prevent duplicate queue entries
Galfurian Feb 27, 2026
098d356
fix(scheduler): add idle hlt loop in empty runqueue
Galfurian Feb 27, 2026
49876ab
fix(mm): copy source page directory in mm_clone
Galfurian Feb 27, 2026
9090e93
fix(mm): revert pd clone to main pgd and zero user entries
Galfurian Feb 27, 2026
b774d5b
fix(mm): revert page directory clone and zero user entries
Galfurian Mar 2, 2026
fca9a4a
fix(scheduler): properly remove tasks from wait queues on signal deli…
Galfurian Mar 2, 2026
39f3932
fix(scheduler): implement proper blocking in sys_waitpid
Galfurian Mar 2, 2026
5c3ea7e
fix(wait): remove problematic entry reuse logic in sleep_on
Galfurian Mar 2, 2026
09dd96d
fix(process): correct wait/wakeup queue handling and scheduler intera…
Galfurian Mar 3, 2026
b4384e6
fix(scheduler): prevent double-enqueue/dequeue and improve debugging
Galfurian Mar 6, 2026
4e34111
fix(scheduler): restore wake-up scheduling for sleeping tasks
Galfurian Mar 6, 2026
383fb0d
fix(pipe): re-enqueue tasks woken from pipe wait queues
Galfurian Mar 6, 2026
251b557
refactor(scheduler): centralize task wake-up management
Galfurian Mar 6, 2026
1ec4b77
refactor(signal): stop tasks stay on runqueue, not wait queues
Galfurian Mar 6, 2026
e12553b
fix(wait): keep sleeping tasks on runqueue and centralize wake handling
Galfurian Mar 6, 2026
3cad24e
fix(timer): close nanosleep sleep-arm race
Galfurian Mar 6, 2026
95a498f
fix(signal): wake interruptible sleepers for pending signals
Galfurian Mar 6, 2026
5893988
fix(waitpid): avoid double dequeue on zombie reap
Galfurian Mar 6, 2026
6b12273
fix(signal): wake stopped tasks on SIGKILL to allow processing
Galfurian Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@
"*.h": "c",
"*.tcc": "c",
"functional": "c"
}
},
"editor.wordWrapColumn": 80,
"rewrap.wrappingColumn": 80,
"editor.rulers": [
80,
120
],
}
7 changes: 7 additions & 0 deletions kernel/inc/drivers/keyboard/keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ int keyboard_peek_front(void);
/// @return 0 on success, 1 on error.
int keyboard_initialize(void);

/// @brief Sleep until a keypress is available on the global keyboard queue.
///
/// This is a simple helper used by higher‑level input code to block the
/// current process until a character arrives. It is safe to call from
/// kernel contexts that know they have the keyboard lock.
void keyboard_wait(void);

/// @brief De-initializes the keyboard drivers.
/// @return 0 on success, 1 on error.
int keyboard_finalize(void);
Expand Down
5 changes: 5 additions & 0 deletions kernel/inc/hardware/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ void remove_timer(struct timer_list *timer);
/// the process.
int sys_nanosleep(const struct timespec *req, struct timespec *rem);

/// @brief Cancels nanosleep timer for a task interrupted by a signal.
/// @param task The task whose sleep timer should be canceled.
/// @return 0 on success, -1 if no sleep timer exists.
int cancel_sleep_timer(struct task_struct *task);

/// @brief Send signal to calling thread after desired seconds.
/// @param seconds The number of seconds in the interval
/// @return the number of seconds remaining until any previously scheduled
Expand Down
2 changes: 2 additions & 0 deletions kernel/inc/klib/spinlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#define SPINLOCK_FREE 0
/// Determines if the spinlock is busy.
#define SPINLOCK_BUSY 1
/// Initializes a spinlock with the value of SPINLOCK_FREE.
#define SPINLOCK_INIT SPINLOCK_FREE

/// @brief Spinlock structure.
typedef atomic_t spinlock_t;
Expand Down
5 changes: 5 additions & 0 deletions kernel/inc/process/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ typedef struct task_struct {

/// Timer for alarm syscall.
struct timer_list *real_timer;
/// Timer for nanosleep syscall (cancelled on signal delivery).
struct timer_list *sleep_timer;

/// Next value for the real timer (ITIMER_REAL).
unsigned long it_real_incr;
Expand All @@ -158,6 +160,9 @@ typedef struct task_struct {
/// Buffer for managing inputs from keyboard.
rb_keybuffer_t keyboard_rb;

/// Wait queue this task is currently sleeping on (NULL if not sleeping).
struct wait_queue_head *waiting_on;

//==== Future work =========================================================
// - task's attributes:
// struct task_struct __rcu *real_parent;
Expand Down
11 changes: 11 additions & 0 deletions kernel/inc/process/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ void scheduler_enqueue_task(task_struct *process);
/// @param process Process that has to be activated.
void scheduler_dequeue_task(task_struct *process);

/// @brief Wake up a sleeping/stopped task and make it runnable.
/// @details Handles all state transitions, queue management, and cleanup atomically:
/// - Transitions task to TASK_RUNNING state
/// - Removes task from any wait queue it's sleeping on
/// - Cancels any active sleep timer
/// - Enqueues task to scheduler runqueue
/// - Clears wait queue tracking fields
/// @param task The task to wake up
/// @return 0 on success, negative on error
int wake_up_process(task_struct *task);

/// @brief The RR implementation of the scheduler.
/// @param f The context of the process.
void scheduler_run(pt_regs_t *f);
Expand Down
36 changes: 32 additions & 4 deletions kernel/inc/process/wait.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,9 @@

/// @brief Head of the waiting queue.
typedef struct wait_queue_head {
/// Locking element for the waiting queque.
spinlock_t lock;
/// Head of the waiting queue, it contains wait_queue_entry_t elements.
struct list_head task_list;
const char *name; ///< Name of the wait queue (Added for debug purpose).
spinlock_t lock; ///< Locking element for the waiting queque.
struct list_head task_list; ///< Head of the waiting queue, it contains wait_queue_entry_t elements.
} wait_queue_head_t;

/// @brief Entry of the waiting queue.
Expand Down Expand Up @@ -123,3 +122,32 @@ int default_wake_function(wait_queue_entry_t *entry, unsigned mode, int sync);
/// @return Pointer to the entry inside the wq representing the
/// sleeping process.
wait_queue_entry_t *sleep_on(wait_queue_head_t *head);

/// @brief Sets the state of the current process to TASK_INTERRUPTIBLE
/// and inserts it into the specified wait queue.
///
/// @param head Waitqueue where to sleep.
/// @return Pointer to the entry inside the wq representing the
/// sleeping process.
wait_queue_entry_t *sleep_on_interruptible(wait_queue_head_t *head);

/// @brief Wake all tasks waiting on the queue.
///
/// Calls each entry's wake function and removes entries whose function
/// returns non‑zero. This is the generic helper used by drivers when
/// data arrives.
void wake_up_all(wait_queue_head_t *head);

/// @brief Wake up a specific process if it is waiting on the given queue.
/// @param head The wait queue head to search.
/// @param task The specific task to wake up.
/// @return 1 if the task was found and woken up, 0 otherwise.
int wake_up_process_on_queue(wait_queue_head_t *head, struct task_struct *task);

/// @brief Wake a specific wait queue entry.
/// @param head The wait queue head that owns the entry.
/// @param entry The wait queue entry to evaluate and wake.
/// @param mode Wake mode passed to the entry wake function.
/// @param sync Wake sync flag passed to the entry wake function.
/// @return 1 if the task was woken, 0 otherwise.
int wake_up_wait_queue_entry(wait_queue_head_t *head, wait_queue_entry_t *entry, unsigned mode, int sync);
28 changes: 27 additions & 1 deletion kernel/src/drivers/keyboard/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "io/port_io.h"
#include "io/video.h"
#include "process/scheduler.h"
#include "process/wait.h"
#include "ring_buffer.h"
#include "string.h"
#include "sys/bitops.h"
Expand All @@ -33,6 +34,15 @@ rb_keybuffer_t scancodes;
/// Spinlock to protect access to the scancode buffer.
spinlock_t scancodes_lock;

/* wait queue used by keyboard_pop_back(), initialized in
* keyboard_initialize(). Readers sleep here when the scancode buffer
* is empty; the ISR wakes them when a new key arrives. */
static wait_queue_head_t keyboard_wait_queue = {
.name = "keyboard_wait_queue",
.lock = SPINLOCK_INIT,
.task_list = LIST_HEAD_INIT(keyboard_wait_queue.task_list),
};

#define KBD_LEFT_SHIFT (1 << 0) ///< Flag which identifies the left shift.
#define KBD_RIGHT_SHIFT (1 << 1) ///< Flag which identifies the right shift.
#define KBD_CAPS_LOCK (1 << 2) ///< Flag which identifies the caps lock.
Expand Down Expand Up @@ -91,6 +101,9 @@ static inline void keyboard_push_back(unsigned int c)

// Unlock the buffer after the push operation is complete.
spinlock_unlock(&scancodes_lock);

/* wake any readers waiting for input */
wake_up_all(&keyboard_wait_queue);
}

/// @brief Pushes a character into the scancode ring buffer.
Expand All @@ -105,6 +118,9 @@ static inline void keyboard_push_front(unsigned int c)

// Unlock the buffer after the push operation is complete.
spinlock_unlock(&scancodes_lock);

/* wake readers as well; front or back both add data */
wake_up_all(&keyboard_wait_queue);
}

/// @brief Pushes a sequence of characters (scancodes) into the keyboard buffer.
Expand Down Expand Up @@ -143,12 +159,22 @@ static inline void keyboard_push_front_sequence(char *sequence)
/// @return the value we removed from the ring buffer.
int keyboard_pop_back(void)
{
int c;

spinlock_lock(&scancodes_lock);
int c = rb_keybuffer_pop_back(&scancodes);
c = rb_keybuffer_pop_back(&scancodes);
spinlock_unlock(&scancodes_lock);

/* simply return what we have; caller decides what to do when empty */
return c;
}

/// @brief Put the current task to sleep waiting for keyboard data.
void keyboard_wait(void)
{
sleep_on(&keyboard_wait_queue);
}

int keyboard_peek_back(void)
{
spinlock_lock(&scancodes_lock);
Expand Down
31 changes: 9 additions & 22 deletions kernel/src/fs/pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,17 +496,13 @@ int pipe_read_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync)

// Validate that data is available in the pipe for reading.
if ((pipe_info_has_data(pipe_info) > 0) || (pipe_info->writers == 0)) {
// Check if the task is in an appropriate sleep state to be woken up.
if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) {
// Set the task's state to the specified wake-up mode.
wait->task->state = mode;

// Signal that the task has been woken up.
// Check if the task is in uninterruptible sleep state (the state it
// entered when starting the pipe read operation).
if (wait->task->state == TASK_UNINTERRUPTIBLE) {
pr_debug("Data available or no more writers, waking up reader %d.\n", wait->task->pid);
return 1;
}
pr_debug("Reader %d not in the correct state for wake-up.\n", wait->task->pid);

} else {
pr_debug("No data available, reader %d should continue waiting.\n", wait->task->pid);
}
Expand All @@ -533,17 +529,13 @@ int pipe_write_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync)

// Check if there is available space in the pipe for writing.
if (pipe_info_has_space(pipe_info) > 0) {
// Only tasks in the state TASK_UNINTERRUPTIBLE or TASK_STOPPED can be woken up.
if ((wait->task->state == TASK_UNINTERRUPTIBLE) || (wait->task->state == TASK_STOPPED)) {
// Set the wake-up mode for the task.
wait->task->state = mode;

// Signal that the task has been woken up.
// Check if the task is in uninterruptible sleep state (the state it
// entered when starting the pipe write operation).
if (wait->task->state == TASK_UNINTERRUPTIBLE) {
pr_debug("Space available, waking up writer %d.\n", wait->task->pid);
return 1;
}
pr_debug("Writer %d not in the correct state for wake-up.\n", wait->task->pid);

} else {
pr_debug("No space available, writer %d should continue waiting.\n", wait->task->pid);
}
Expand All @@ -566,15 +558,10 @@ static void pipe_wake_up_tasks(wait_queue_head_t *wait_queue, const char *debug_
list_for_each_safe_decl(it, store, &wait_queue->task_list)
{
wait_queue_entry_t *wait_queue_entry = list_entry(it, wait_queue_entry_t, task_list);
int target_pid = wait_queue_entry->task ? wait_queue_entry->task->pid : -1;

// Run the wakeup test function for the waiting task.
if (wait_queue_entry->func(wait_queue_entry, TASK_RUNNING, 0)) {
// Task is ready, remove from the wait queue.
remove_wait_queue(wait_queue, wait_queue_entry);

// Log and free the memory associated with the wait entry.
pr_debug("%s: Process %d woken up.\n", debug_msg, wait_queue_entry->task->pid);
wait_queue_entry_dealloc(wait_queue_entry);
if (wake_up_wait_queue_entry(wait_queue, wait_queue_entry, TASK_RUNNING, 0)) {
pr_debug("%s: waking up process %d\n", debug_msg, target_pid);
}
}
}
Expand Down
Loading