2020-07-25 16:54:37 -05:00
/**
* \ file
*/
2020-07-22 19:26:55 -05:00
# include "cpu/halt.h"
# include "cpu/paging.h"
2020-07-22 19:35:23 -05:00
# include "cpu/serial.h"
2020-07-22 19:26:55 -05:00
# include "cpu/tasking_helpers.h"
2020-07-22 19:35:23 -05:00
# include "kmalloc.h"
# include "tasking.h"
# include <sys/types.h>
2020-07-25 16:54:37 -05:00
# define MAX_PROCS 32768 //!< Maximum number of processes that can be running at a time
# define HAS_UNBLOCKED_THREADS(proc) (proc->numThreads!=proc->numThreadsBlocked) //!< Macro to check whethe a process has unblocked threads
# define NUM_UNBLOCKED_THREADS(proc) (proc->numThreads-proc->numThreadsBlocked) //!< Macro to get the number of unblocked threads for a process
# define SAME_PROC(thread1,thread2) (thread1->process->pid==thread2->process->pid) //!< Macro to check whether two threads have the same PID
# define SAME_THREAD(thread1,thread2) (thread1->process->pid==thread2->process->pid&&thread1->tid==thread2->tid) //!< Macro to check whether two threads have the same PID and TID
pid_t next_pid = 0 ; //!< PID to use for the next created process
size_t num_procs = 0 ; //!< Number of non-exited processes
Process * processes [ MAX_PROCS ] ; //!< Array pf processes by PID
char proc_schedule_bmap [ MAX_PROCS / 8 ] ; //!< Bitmap of what processes are scheduled
2020-07-25 18:00:53 -05:00
Thread * current_thread ; //!< Currently running thread
static Thread * ready_to_run_head = NULL ; //!< Head of the linked list of ready to run threads
static Thread * ready_to_run_tail = NULL ; //!< Tail of the linked list of ready to run threads
2019-05-21 19:16:19 -05:00
2020-07-25 16:54:37 -05:00
/**
* Check whether a process is scheduled
* \ param index The PID to check
* \ return whether the process is scheduled
*/
2020-07-23 11:50:23 -05:00
static char is_proc_scheduled ( pid_t index ) {
size_t byte = index / 8 ;
size_t bit = index % 8 ;
2020-07-20 09:51:30 -05:00
char entry = proc_schedule_bmap [ byte ] ;
return ( entry & ( 1 < < bit ) ) > 0 ;
}
2019-05-21 19:16:19 -05:00
2020-07-25 16:54:37 -05:00
/**
* Mark a process as scheduled
* \ param index The PID to mark
*/
2020-07-23 11:50:23 -05:00
static void mark_proc_scheduled ( pid_t index ) {
2020-07-20 09:51:30 -05:00
if ( is_proc_scheduled ( index ) ) {
serial_printf ( " Attempt to schedule a thread in a process with a scheduled thread! (PID %d) \n " , index ) ;
halt ( ) ;
}
2020-07-23 11:50:23 -05:00
size_t byte = index / 8 ;
size_t bit = index % 8 ;
2020-07-20 09:51:30 -05:00
proc_schedule_bmap [ byte ] = proc_schedule_bmap [ byte ] | ( 1 < < bit ) ;
}
2019-02-11 09:30:28 -06:00
2020-07-25 16:54:37 -05:00
/**
* Unmark a process as scheduled
* \ param index The PID to unmark
*/
2020-07-23 11:50:23 -05:00
static void unmark_proc_scheduled ( pid_t index ) {
size_t byte = index / 8 ;
size_t bit = index % 8 ;
2020-07-20 09:51:30 -05:00
proc_schedule_bmap [ byte ] = proc_schedule_bmap [ byte ] & ( ~ ( 1 < < bit ) ) ;
2019-02-11 09:30:28 -06:00
}
2020-07-25 18:00:53 -05:00
void tasking_create_task ( void * eip , void * cr3 , char kmode , char param1_exists , void * param1_arg , char param2_exists , void * param2_arg , char isThread ) {
2020-07-20 09:51:30 -05:00
if ( next_pid > MAX_PROCS & & ! isThread ) {
serial_printf ( " Failed to create a process, as 32k processes have been created already. \n " ) ;
halt ( ) ; //Cannot ever create more than 32k processes, as I don't currently reuse PIDs.
}
2020-07-23 11:50:23 -05:00
void * param1 ;
2020-07-20 09:51:30 -05:00
if ( param1_exists ) {
param1 = param1_arg ;
} else {
2020-07-23 11:50:23 -05:00
param1 = NULL ;
2020-07-20 09:51:30 -05:00
}
2020-07-23 11:50:23 -05:00
void * param2 ;
2020-07-20 09:51:30 -05:00
if ( param2_exists ) {
if ( isThread ) {
serial_printf ( " Param2 in Thread! \n " ) ;
halt ( ) ;
2019-05-23 20:21:51 -05:00
}
2020-07-20 09:51:30 -05:00
param2 = param2_arg ;
} else {
2020-07-23 11:50:23 -05:00
param2 = NULL ;
2020-07-20 09:51:30 -05:00
}
Process * proc ;
Thread * thread = kmalloc ( sizeof ( Thread ) ) ;
if ( isThread ) {
proc = processes [ ( pid_t ) param2_arg ] ;
proc - > numThreads + + ;
thread - > cr3 = proc - > firstThread - > cr3 ;
} else {
proc = kmalloc ( sizeof ( Process ) ) ;
2020-07-25 18:00:53 -05:00
if ( current_thread ) {
proc - > priv = current_thread - > process - > priv ;
2019-08-25 17:06:19 -05:00
} else {
2020-07-20 09:51:30 -05:00
proc - > priv = 1 ;
2019-02-11 09:30:28 -06:00
}
2020-07-20 09:51:30 -05:00
proc - > pid = next_pid ;
2020-07-12 14:28:58 -05:00
next_pid + + ;
2020-07-20 09:51:30 -05:00
proc - > next_tid = 0 ;
proc - > numThreads = 1 ;
proc - > numThreadsBlocked = 0 ;
proc - > firstThread = thread ;
processes [ proc - > pid ] = proc ;
thread - > cr3 = cr3 ;
}
thread - > process = proc ;
thread - > errno = 0 ;
thread - > tid = proc - > next_tid ;
proc - > next_tid + + ;
2020-07-22 19:54:33 -05:00
setup_kstack ( thread , param1 , param2 , kmode , eip ) ;
2020-07-20 09:51:30 -05:00
thread - > prevReadyToRun = NULL ;
thread - > nextReadyToRun = NULL ;
if ( isThread ) {
thread - > nextThreadInProcess = proc - > firstThread ;
thread - > prevThreadInProcess = NULL ;
thread - > state = THREAD_READY ;
proc - > firstThread - > prevThreadInProcess = thread ;
proc - > firstThread = thread ;
} else {
thread - > nextThreadInProcess = NULL ;
thread - > prevThreadInProcess = NULL ;
if ( ! is_proc_scheduled ( proc - > pid ) ) {
2020-07-25 18:00:53 -05:00
if ( ready_to_run_tail ) {
2020-07-20 09:51:30 -05:00
thread - > state = THREAD_READY ;
2020-07-25 18:00:53 -05:00
ready_to_run_tail - > nextReadyToRun = thread ;
thread - > prevReadyToRun = ready_to_run_tail ;
ready_to_run_tail = thread ;
2020-07-20 09:51:30 -05:00
mark_proc_scheduled ( proc - > pid ) ;
2020-07-25 18:00:53 -05:00
} else if ( current_thread ) {
2020-07-20 09:51:30 -05:00
thread - > state = THREAD_READY ;
2020-07-25 18:00:53 -05:00
ready_to_run_head = thread ;
ready_to_run_tail = thread ;
2020-07-20 09:51:30 -05:00
mark_proc_scheduled ( proc - > pid ) ;
} else {
thread - > state = THREAD_RUNNING ;
2020-07-25 18:00:53 -05:00
current_thread = thread ;
2020-07-20 09:51:30 -05:00
}
2019-08-04 13:14:35 -05:00
}
2020-07-20 09:51:30 -05:00
}
if ( ! isThread ) {
num_procs + + ;
}
serial_printf ( " Created thread with PID %d and TID %d. \n " , proc - > pid , thread - > tid ) ;
2019-02-11 09:30:28 -06:00
}
2020-07-20 09:51:30 -05:00
void tasking_init ( ) {
2020-07-25 18:00:53 -05:00
tasking_create_task ( NULL , get_cr3 ( ) , 1 , 0 , 0 , 0 , 0 , 0 ) ;
2019-04-08 15:58:30 -05:00
}
2020-07-20 09:51:30 -05:00
2020-07-25 18:00:53 -05:00
char tasking_is_privleged ( ) {
return current_thread - > process - > priv ;
2020-07-20 09:51:30 -05:00
}
2020-07-25 18:00:53 -05:00
pid_t tasking_get_PID ( ) {
return current_thread - > process - > pid ;
2020-07-20 09:51:30 -05:00
}
int * tasking_get_errno_address ( ) {
2020-07-25 18:00:53 -05:00
return & current_thread - > errno ;
2019-02-25 15:42:23 -06:00
}
2020-07-23 11:50:23 -05:00
pid_t tasking_new_thread ( void * start , pid_t pid , char param_exists , void * param_arg ) {
2020-07-25 18:00:53 -05:00
tasking_create_task ( start , NULL , 0 , param_exists , param_arg , 0 , ( void * ) pid , 1 ) ;
2020-07-22 07:33:44 -05:00
return processes [ pid ] - > firstThread - > tid ;
2019-02-11 09:30:28 -06:00
}
2020-07-25 16:54:37 -05:00
/**
* Switch to a thread and schedule the next ready thread in the current process , if there is one .
* \ param thread The thread to switch to
*/
2020-07-20 09:51:30 -05:00
void switch_to_thread ( Thread * thread ) {
// Unlink the thread from the list of ready-to-run threads
2020-07-25 18:00:53 -05:00
if ( thread ! = ready_to_run_head ) {
2020-07-20 09:51:30 -05:00
thread - > prevReadyToRun - > nextReadyToRun = thread - > nextReadyToRun ;
if ( thread - > nextReadyToRun ) {
thread - > nextReadyToRun - > prevReadyToRun = thread - > prevReadyToRun ;
2020-07-12 14:28:58 -05:00
}
} else {
2020-07-25 18:00:53 -05:00
ready_to_run_head = thread - > nextReadyToRun ;
if ( ready_to_run_head = = NULL ) {
ready_to_run_tail = NULL ;
2020-07-12 14:28:58 -05:00
}
}
2020-07-20 09:51:30 -05:00
unmark_proc_scheduled ( thread - > process - > pid ) ;
thread - > prevReadyToRun = NULL ;
thread - > nextReadyToRun = NULL ;
2020-07-25 18:00:53 -05:00
if ( current_thread - > state = = THREAD_RUNNING ) {
current_thread - > state = THREAD_READY ;
2020-07-20 09:51:30 -05:00
}
2020-07-25 18:00:53 -05:00
Thread * current_threadNextReady = current_thread - > nextThreadInProcess ;
while ( ( current_threadNextReady & & current_threadNextReady - > state ! = THREAD_READY ) | | ( current_threadNextReady & & SAME_THREAD ( thread , current_threadNextReady ) ) ) {
current_threadNextReady = current_threadNextReady - > nextThreadInProcess ;
2020-07-20 09:51:30 -05:00
}
2020-07-25 18:00:53 -05:00
if ( ! current_threadNextReady ) {
current_threadNextReady = current_thread - > process - > firstThread ;
while ( ( current_threadNextReady & & current_threadNextReady - > state ! = THREAD_READY ) | | ( current_threadNextReady & & SAME_THREAD ( thread , current_threadNextReady ) ) ) {
current_threadNextReady = current_threadNextReady - > nextThreadInProcess ;
2020-07-20 09:51:30 -05:00
}
}
2020-07-25 18:00:53 -05:00
if ( ! current_threadNextReady ) {
2020-07-20 09:51:30 -05:00
//This process is fully blocked, try the process of the thread we're yielding to
2020-07-25 18:00:53 -05:00
current_threadNextReady = thread - > nextThreadInProcess ;
while ( ( current_threadNextReady & & current_threadNextReady - > state ! = THREAD_READY ) | | ( current_threadNextReady & & SAME_THREAD ( thread , current_threadNextReady ) ) ) {
current_threadNextReady = current_threadNextReady - > nextThreadInProcess ;
2020-07-20 09:51:30 -05:00
}
2020-07-25 18:00:53 -05:00
if ( ! current_threadNextReady ) {
current_threadNextReady = thread - > process - > firstThread ;
while ( ( current_threadNextReady & & current_threadNextReady - > state ! = THREAD_READY ) | | ( current_threadNextReady & & SAME_THREAD ( thread , current_threadNextReady ) ) ) {
current_threadNextReady = current_threadNextReady - > nextThreadInProcess ;
2020-07-20 09:51:30 -05:00
}
}
}
2020-07-25 18:00:53 -05:00
if ( current_threadNextReady & & ! is_proc_scheduled ( current_thread - > process - > pid ) ) {
2020-07-20 10:07:46 -05:00
// Link the thread onto the list of ready to run threads
2020-07-25 18:00:53 -05:00
if ( ready_to_run_tail ) {
current_threadNextReady - > prevReadyToRun = ready_to_run_tail ;
2020-07-27 17:58:49 -05:00
ready_to_run_tail - > nextReadyToRun = current_threadNextReady ;
2020-07-25 18:00:53 -05:00
ready_to_run_tail = current_threadNextReady ;
2020-07-12 14:28:58 -05:00
} else {
2020-07-25 18:00:53 -05:00
ready_to_run_head = current_threadNextReady ;
ready_to_run_tail = current_threadNextReady ;
2020-07-12 14:28:58 -05:00
}
2020-07-25 18:00:53 -05:00
mark_proc_scheduled ( current_thread - > process - > pid ) ;
2019-05-21 19:16:19 -05:00
}
2020-07-20 09:51:30 -05:00
serial_printf ( " Switching to PID %d TID %d. \n " , thread - > process - > pid , thread - > tid ) ;
switch_to_thread_asm ( thread ) ;
2020-07-12 14:28:58 -05:00
}
2020-07-20 09:51:30 -05:00
void tasking_yield ( ) {
serial_printf ( " Attempting to yield \n " ) ;
2020-07-25 18:00:53 -05:00
if ( ready_to_run_head ) {
serial_printf ( " Attempting to switch to PID %d TID %d \n " , ready_to_run_head - > process - > pid , ready_to_run_head - > tid ) ;
switch_to_thread ( ready_to_run_head ) ;
2020-07-20 09:51:30 -05:00
} else {
2020-07-25 18:00:53 -05:00
if ( NUM_UNBLOCKED_THREADS ( current_thread - > process ) > 1 ) {
2020-07-20 09:51:30 -05:00
serial_printf ( " The ready to run list is empty, and the current process has other unblocked threads? This is an invalid state! Halting! \n " ) ;
halt ( ) ;
2020-07-25 18:00:53 -05:00
} else if ( NUM_UNBLOCKED_THREADS ( current_thread - > process ) = = 1 ) {
2020-07-20 09:51:30 -05:00
serial_printf ( " Yield failed, no other ready processes \n " ) ;
return ;
} else {
if ( num_procs = = 0 ) {
serial_printf ( " All processes exited, halting \n " ) ;
halt ( ) ;
} else {
serial_printf ( " All threads in all processes blocked, waiting for an IRQ which unblocks a thread \n " ) ;
// All threads in all processes blocked, so wait for an IRQ whose handler unblocks a thread.
2020-07-25 18:00:53 -05:00
do { wait_for_unblocked_thread_asm ( ) ; } while ( ready_to_run_head = = NULL ) ;
2020-07-12 14:28:58 -05:00
}
2020-07-25 18:00:53 -05:00
serial_printf ( " Attempting to switch to PID %d TID %d \n " , ready_to_run_head - > process - > pid , ready_to_run_head - > tid ) ;
switch_to_thread ( ready_to_run_head ) ;
2020-07-12 16:29:57 -05:00
}
2020-07-20 09:51:30 -05:00
}
}
2020-07-25 18:00:53 -05:00
void tasking_block ( thread_state newstate ) {
if ( ready_to_run_head & & SAME_THREAD ( ready_to_run_head , current_thread ) ) {
ready_to_run_head = ready_to_run_head - > nextReadyToRun ;
if ( ready_to_run_head = = NULL ) {
ready_to_run_tail = NULL ;
2020-07-12 14:28:58 -05:00
}
2020-07-20 09:51:30 -05:00
}
2020-07-25 18:00:53 -05:00
if ( ready_to_run_tail & & SAME_THREAD ( ready_to_run_tail , current_thread ) ) {
ready_to_run_tail = ready_to_run_tail - > prevReadyToRun ;
if ( ready_to_run_tail = = NULL ) {
ready_to_run_head = NULL ;
2020-07-12 16:29:57 -05:00
}
2020-07-12 14:28:58 -05:00
}
2020-07-25 18:00:53 -05:00
if ( ready_to_run_head & & ready_to_run_head - > nextReadyToRun ) {
for ( Thread * thread = ready_to_run_head - > nextReadyToRun ; thread ! = NULL ; thread = thread - > nextReadyToRun ) {
if ( SAME_THREAD ( thread , current_thread ) ) {
2020-07-20 09:51:30 -05:00
thread - > prevReadyToRun - > nextReadyToRun = thread - > nextReadyToRun ;
if ( thread - > nextReadyToRun ) {
thread - > nextReadyToRun - > prevReadyToRun = thread - > prevReadyToRun ;
}
break ;
}
}
}
2020-07-25 18:00:53 -05:00
for ( Thread * thread = current_thread - > process - > firstThread ; thread ! = NULL ; thread = thread - > nextThreadInProcess ) {
if ( thread - > tid = = current_thread - > tid ) {
2020-07-20 09:51:30 -05:00
thread - > state = newstate ;
}
2019-08-25 17:32:08 -05:00
}
2019-07-20 11:03:27 -05:00
}
2020-07-23 11:50:23 -05:00
void tasking_unblock ( pid_t pid , pid_t tid ) {
2020-07-20 09:51:30 -05:00
serial_printf ( " Unblocking PID %d TID %d \n " , pid , tid ) ;
if ( ! processes [ pid ] ) {
serial_printf ( " PID %d does not exist! \n " , pid ) ;
}
Thread * thread = processes [ pid ] - > firstThread ;
for ( ; thread ! = NULL ; thread = thread - > nextThreadInProcess ) {
if ( thread - > tid = = tid ) {
break ;
}
}
if ( ! thread ) {
serial_printf ( " PID %d TID %d does not exist! \n " , pid , thread ) ;
2020-07-12 14:28:58 -05:00
}
2020-07-20 09:51:30 -05:00
if ( thread - > tid ! = tid ) {
serial_printf ( " Error! Got wrong thread! (Wanted TID %d, got TID %d) \n " , tid , thread - > tid ) ;
halt ( ) ;
}
2020-07-27 18:01:24 -05:00
if ( thread - > state = = THREAD_EXITED | | thread - > state = = THREAD_READY | | thread - > state = = THREAD_RUNNING ) {
serial_printf ( " Tried to unblock an exited/ready/running thread! \n " ) ;
return ;
}
2020-07-20 09:51:30 -05:00
thread - > state = THREAD_READY ;
if ( ! is_proc_scheduled ( thread - > process - > pid ) ) {
2020-07-20 10:07:46 -05:00
// Link the thread onto the list of ready to run threads
2020-07-25 18:00:53 -05:00
if ( ready_to_run_tail ) {
thread - > prevReadyToRun = ready_to_run_tail ;
ready_to_run_tail - > nextThreadInProcess = thread ;
ready_to_run_tail = thread ;
2020-07-20 09:51:30 -05:00
} else {
2020-07-25 18:00:53 -05:00
ready_to_run_head = thread ;
ready_to_run_tail = thread ;
2020-07-20 09:51:30 -05:00
}
mark_proc_scheduled ( thread - > process - > pid ) ;
}
2020-07-12 14:28:58 -05:00
}
2020-07-23 11:50:23 -05:00
void tasking_exit ( int code ) {
2020-07-25 18:00:53 -05:00
serial_printf ( " PID %d is exiting with code %d. \n " , current_thread - > process - > pid , code ) ;
if ( ready_to_run_head & & SAME_PROC ( ready_to_run_head , current_thread ) ) {
ready_to_run_head = ready_to_run_head - > nextReadyToRun ;
if ( ready_to_run_head = = NULL ) {
ready_to_run_tail = NULL ;
2020-07-20 09:51:30 -05:00
}
}
2020-07-25 18:00:53 -05:00
if ( ready_to_run_tail & & SAME_PROC ( ready_to_run_tail , current_thread ) ) {
ready_to_run_tail = ready_to_run_tail - > prevReadyToRun ;
if ( ready_to_run_tail = = NULL ) {
ready_to_run_head = NULL ;
2020-07-23 11:50:23 -05:00
2020-07-12 14:28:58 -05:00
}
2020-07-20 09:51:30 -05:00
}
2020-07-25 18:00:53 -05:00
if ( ready_to_run_head & & ready_to_run_head - > nextReadyToRun ) {
for ( Thread * thread = ready_to_run_head - > nextReadyToRun ; thread ! = NULL ; thread = thread - > nextReadyToRun ) {
if ( SAME_PROC ( thread , current_thread ) ) {
2020-07-20 09:51:30 -05:00
thread - > prevReadyToRun - > nextReadyToRun = thread - > nextReadyToRun ;
if ( thread - > nextReadyToRun ) {
thread - > nextReadyToRun - > prevReadyToRun = thread - > prevReadyToRun ;
}
break ;
}
}
}
2020-07-25 18:00:53 -05:00
unmark_proc_scheduled ( current_thread - > process - > pid ) ;
for ( Thread * thread = current_thread - > process - > firstThread ; thread ! = NULL ; thread = thread - > nextThreadInProcess ) {
2020-07-20 09:51:30 -05:00
thread - > state = THREAD_EXITED ;
}
2020-07-25 18:00:53 -05:00
current_thread - > process - > numThreadsBlocked = current_thread - > process - > numThreads ;
2020-07-20 09:51:30 -05:00
num_procs - - ;
tasking_yield ( ) ;
2020-07-12 14:28:58 -05:00
}