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-30 10:09:47 -05:00
# include <string.h>
2020-07-22 19:35:23 -05:00
2020-07-25 16:54:37 -05:00
# define MAX_PROCS 32768 //!< Maximum number of processes that can be running at a time
2020-07-28 06:50:26 -05:00
# define HAS_UNBLOCKED_THREADS(proc) (proc->num_threads!=proc->num_threads_blocked) //!< Macro to check whethe a process has unblocked threads
# define NUM_UNBLOCKED_THREADS(proc) (proc->num_threads-proc->num_threads_blocked) //!< Macro to get the number of unblocked threads for a process
2020-07-25 16:54:37 -05:00
# 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
2020-07-28 19:21:14 -05:00
Process processes [ MAX_PROCS ] ; //!< Array pf processes by PID
2020-07-25 16:54:37 -05:00
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-29 11:31:31 -05:00
/**
* Schedules a thread if the thread ' s prcess does not have a scheduled thread
* \ param thread The thread to schedule
*/
void schedule_thread ( Thread * thread ) {
if ( ! is_proc_scheduled ( thread - > process - > pid ) ) {
2020-08-23 08:22:14 -05:00
if ( ready_to_run_head ) {
2020-07-29 11:31:31 -05:00
thread - > state = THREAD_READY ;
2020-08-23 08:22:14 -05:00
// ready_to_run_tail->next_ready_to_run=thread;
// thread->prev_ready_to_run=ready_to_run_tail;
// ready_to_run_tail=thread;
thread - > next_ready_to_run = ready_to_run_head ;
ready_to_run_head - > prev_ready_to_run = thread ;
ready_to_run_head = thread ;
2020-07-29 11:31:31 -05:00
mark_proc_scheduled ( thread - > process - > pid ) ;
} else if ( current_thread ) {
thread - > state = THREAD_READY ;
ready_to_run_head = thread ;
ready_to_run_tail = thread ;
mark_proc_scheduled ( thread - > process - > pid ) ;
} else {
thread - > state = THREAD_RUNNING ;
current_thread = thread ;
}
}
}
2020-07-29 10:35:04 -05:00
void tasking_create_task ( void * eip , void * address_space , char kmode , void * param1 , void * param2 , 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-30 10:09:47 -05:00
pid_t pid = isThread ? ( pid_t ) param2 : next_pid + + ;
2020-07-29 11:45:07 -05:00
Process * proc = & processes [ pid ] ;
2020-07-20 09:51:30 -05:00
Thread * thread = kmalloc ( sizeof ( Thread ) ) ;
2020-07-30 10:09:47 -05:00
proc - > num_threads + + ;
2020-07-20 09:51:30 -05:00
thread - > process = proc ;
thread - > errno = 0 ;
2020-07-30 10:09:47 -05:00
thread - > tid = proc - > next_tid + + ;
2020-07-28 06:50:26 -05:00
thread - > prev_ready_to_run = NULL ;
thread - > next_ready_to_run = NULL ;
2020-07-30 10:09:47 -05:00
thread - > prev_thread_in_process = NULL ;
2020-08-02 14:37:23 -05:00
thread - > state = THREAD_READY ;
2020-07-20 09:51:30 -05:00
if ( isThread ) {
2020-07-30 10:09:47 -05:00
thread - > address_space = proc - > first_thread - > address_space ;
2020-07-28 06:50:26 -05:00
thread - > next_thread_in_process = proc - > first_thread ;
proc - > first_thread - > prev_thread_in_process = thread ;
2020-07-20 09:51:30 -05:00
} else {
2020-07-30 10:09:47 -05:00
thread - > address_space = address_space ;
2020-07-28 06:50:26 -05:00
thread - > next_thread_in_process = NULL ;
2020-07-30 10:09:47 -05:00
proc - > priv = current_thread ? current_thread - > process - > priv : 1 ;
proc - > pid = pid ;
2020-07-20 09:51:30 -05:00
num_procs + + ;
}
2020-07-30 10:09:47 -05:00
proc - > first_thread = thread ;
setup_kstack ( thread , param1 , param2 , kmode , eip ) ;
schedule_thread ( thread ) ;
2020-08-23 08:22:14 -05:00
// 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-28 19:21:14 -05:00
for ( size_t i = 0 ; i < MAX_PROCS ; i + + ) {
2020-07-30 10:09:47 -05:00
memset ( & processes [ i ] , 0 , sizeof ( Process ) ) ;
2020-07-28 19:21:14 -05:00
}
2020-07-29 10:35:04 -05:00
tasking_create_task ( NULL , get_address_space ( ) , 1 , NULL , NULL , 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
}
2020-08-02 14:37:23 -05:00
pid_t tasking_get_TID ( ) {
return current_thread - > tid ;
}
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-29 10:35:04 -05:00
pid_t tasking_new_thread ( void * start , pid_t pid , void * param ) {
tasking_create_task ( start , NULL , 0 , param , ( void * ) pid , 1 ) ;
2020-07-28 19:21:14 -05:00
return processes [ pid ] . first_thread - > tid ;
2019-02-11 09:30:28 -06:00
}
2020-07-30 10:09:47 -05:00
/**
* Get the next ready thread in a list of threads , starting at the specified thread ' s next thread
* \ param thread The start thread
* \ param thread_to_skip A thread to skip even if it ' s ready
* \ return the next ready thread
*/
static Thread * get_next_ready_thread ( Thread * thread , Thread * thread_to_skip ) {
# ifndef DOXYGEN_SHOULD_SKIP_THIS
# define HELPER \
while ( thread & & ( thread - > state ! = THREAD_READY | | SAME_THREAD ( thread , thread_to_skip ) ) ) { \
thread = thread - > next_thread_in_process ; \
}
//end define
# endif
Thread * start_of_list = thread - > process - > first_thread ;
thread = thread - > next_thread_in_process ;
HELPER ;
if ( ! thread ) {
thread = start_of_list ;
HELPER ;
}
return thread ;
# undef HELPER
}
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-28 06:50:26 -05:00
thread - > prev_ready_to_run - > next_ready_to_run = thread - > next_ready_to_run ;
if ( thread - > next_ready_to_run ) {
thread - > next_ready_to_run - > prev_ready_to_run = thread - > prev_ready_to_run ;
2020-07-12 14:28:58 -05:00
}
} else {
2020-07-28 06:50:26 -05:00
ready_to_run_head = thread - > next_ready_to_run ;
2020-07-25 18:00:53 -05:00
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 ) ;
2020-07-28 06:50:26 -05:00
thread - > prev_ready_to_run = NULL ;
thread - > next_ready_to_run = 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-30 10:09:47 -05:00
//Get the next ready thread in the current process
Thread * current_thread_next_ready = get_next_ready_thread ( current_thread , thread ) ;
2020-07-28 06:50:26 -05:00
if ( ! current_thread_next_ready ) {
2020-07-20 09:51:30 -05:00
//This process is fully blocked, try the process of the thread we're yielding to
2020-08-02 14:37:23 -05:00
current_thread_next_ready = get_next_ready_thread ( thread , thread ) ;
2020-07-20 09:51:30 -05:00
}
2020-08-02 14:37:23 -05:00
if ( current_thread_next_ready ) {
2020-07-29 11:31:31 -05:00
schedule_thread ( current_thread_next_ready ) ;
2020-07-30 10:09:47 -05:00
}
2020-08-02 14:37:23 -05:00
thread - > state = THREAD_RUNNING ;
2020-08-23 08:22:14 -05:00
// serial_printf("Switching to PID %d TID %d.\n",thread->process->pid,thread->tid);
2020-07-20 09:51:30 -05:00
switch_to_thread_asm ( thread ) ;
2020-07-12 14:28:58 -05:00
}
2020-07-20 09:51:30 -05:00
void tasking_yield ( ) {
2020-07-25 18:00:53 -05:00
if ( ready_to_run_head ) {
2020-08-23 08:22:14 -05:00
// serial_printf("Attempting to switch to PID %d TID %d\n",ready_to_run_head->process->pid,ready_to_run_head->tid);
2020-07-25 18:00:53 -05:00
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-08-02 14:37:23 -05:00
// Thread* thread=get_next_ready_thread(current_thread,current_thread);
// schedule_thread(thread);
// yield();
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
return ;
} else {
if ( num_procs = = 0 ) {
serial_printf ( " All processes exited, halting \n " ) ;
2020-08-23 08:22:14 -05:00
asm volatile ( " cli " ) ;
for ( ; ; ) ;
2020-07-20 09:51:30 -05:00
halt ( ) ;
} else {
2020-08-23 08:22:14 -05:00
// serial_printf("All threads in all processes blocked, waiting for an IRQ which unblocks a thread\n");
2020-07-20 09:51:30 -05:00
// 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-08-23 08:22:14 -05:00
// serial_printf("Attempting to switch to PID %d TID %d\n",ready_to_run_head->process->pid,ready_to_run_head->tid);
2020-07-25 18:00:53 -05:00
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 ) ) {
2020-07-28 06:50:26 -05:00
ready_to_run_head = ready_to_run_head - > next_ready_to_run ;
2020-07-25 18:00:53 -05:00
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 ) ) {
2020-07-28 06:50:26 -05:00
ready_to_run_tail = ready_to_run_tail - > prev_ready_to_run ;
2020-07-25 18:00:53 -05:00
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-28 06:50:26 -05:00
if ( ready_to_run_head & & ready_to_run_head - > next_ready_to_run ) {
for ( Thread * thread = ready_to_run_head - > next_ready_to_run ; thread ! = NULL ; thread = thread - > next_ready_to_run ) {
2020-07-25 18:00:53 -05:00
if ( SAME_THREAD ( thread , current_thread ) ) {
2020-07-28 06:50:26 -05:00
thread - > prev_ready_to_run - > next_ready_to_run = thread - > next_ready_to_run ;
if ( thread - > next_ready_to_run ) {
thread - > next_ready_to_run - > prev_ready_to_run = thread - > prev_ready_to_run ;
2020-07-20 09:51:30 -05:00
}
break ;
}
}
}
2020-07-28 06:50:26 -05:00
for ( Thread * thread = current_thread - > process - > first_thread ; thread ! = NULL ; thread = thread - > next_thread_in_process ) {
2020-07-25 18:00:53 -05:00
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
}
2020-08-02 14:37:23 -05:00
current_thread - > process - > num_threads_blocked + + ;
unmark_proc_scheduled ( current_thread - > process - > pid ) ;
tasking_yield ( ) ;
2019-07-20 11:03:27 -05:00
}
2020-07-30 10:09:47 -05:00
/**
* Get a thread
* \ param pid The PID of the thread
* \ param tid The TID of the thread
* \ return the thread wih the specified PID and TID
*/
static Thread * get_thread ( pid_t pid , pid_t tid ) {
if ( processes [ pid ] . num_threads = = 0 ) {
serial_printf ( " PID %d does not exist! \n " , pid ) ;
return NULL ;
}
Thread * thread = processes [ pid ] . first_thread ;
for ( ; thread ! = NULL ; thread = thread - > next_thread_in_process ) {
if ( thread - > tid = = tid ) {
break ;
}
}
if ( ! thread ) {
serial_printf ( " PID %d TID %d does not exist! \n " , pid , thread ) ;
return NULL ;
}
if ( thread - > tid ! = tid ) {
serial_printf ( " Error! Got wrong thread! (Wanted TID %d, got TID %d) \n " , tid , thread - > tid ) ;
return NULL ;
}
return thread ;
}
2020-07-23 11:50:23 -05:00
void tasking_unblock ( pid_t pid , pid_t tid ) {
2020-08-23 08:22:14 -05:00
// serial_printf("Unblocking PID %d TID %d\n",pid,tid);
2020-07-30 10:09:47 -05:00
Thread * thread = get_thread ( pid , tid ) ;
if ( thread = = NULL ) {
return ;
2020-07-20 09:51:30 -05:00
}
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 ;
2020-08-02 14:37:23 -05:00
thread - > process - > num_threads_blocked - - ;
2020-07-29 11:31:31 -05:00
schedule_thread ( thread ) ;
2020-07-12 14:28:58 -05:00
}
2020-07-23 11:50:23 -05:00
void tasking_exit ( int code ) {
2020-08-23 08:22:14 -05:00
// serial_printf("PID %d is exiting with code %d.\n",current_thread->process->pid,code);
2020-07-25 18:00:53 -05:00
if ( ready_to_run_head & & SAME_PROC ( ready_to_run_head , current_thread ) ) {
2020-07-28 06:50:26 -05:00
ready_to_run_head = ready_to_run_head - > next_ready_to_run ;
2020-07-25 18:00:53 -05:00
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 ) ) {
2020-07-28 06:50:26 -05:00
ready_to_run_tail = ready_to_run_tail - > prev_ready_to_run ;
2020-07-25 18:00:53 -05:00
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-28 06:50:26 -05:00
if ( ready_to_run_head & & ready_to_run_head - > next_ready_to_run ) {
for ( Thread * thread = ready_to_run_head - > next_ready_to_run ; thread ! = NULL ; thread = thread - > next_ready_to_run ) {
2020-07-25 18:00:53 -05:00
if ( SAME_PROC ( thread , current_thread ) ) {
2020-07-28 06:50:26 -05:00
thread - > prev_ready_to_run - > next_ready_to_run = thread - > next_ready_to_run ;
if ( thread - > next_ready_to_run ) {
thread - > next_ready_to_run - > prev_ready_to_run = thread - > prev_ready_to_run ;
2020-07-20 09:51:30 -05:00
}
break ;
}
}
}
2020-07-25 18:00:53 -05:00
unmark_proc_scheduled ( current_thread - > process - > pid ) ;
2020-07-28 06:50:26 -05:00
for ( Thread * thread = current_thread - > process - > first_thread ; thread ! = NULL ; thread = thread - > next_thread_in_process ) {
2020-07-20 09:51:30 -05:00
thread - > state = THREAD_EXITED ;
}
2020-07-28 06:50:26 -05:00
current_thread - > process - > num_threads_blocked = current_thread - > process - > num_threads ;
2020-07-20 09:51:30 -05:00
num_procs - - ;
tasking_yield ( ) ;
2020-07-12 14:28:58 -05:00
}
2020-07-30 10:09:47 -05:00
void * tasking_get_address_space ( pid_t pid ) {
return processes [ pid ] . first_thread - > address_space ;
}
2020-08-02 14:37:23 -05:00
void tasking_set_rpc_calling_thread ( pid_t pid , pid_t tid ) {
Thread * thread = get_thread ( pid , tid ) ;
thread - > rpc_calling_pid = current_thread - > process - > pid ;
thread - > rpc_calling_tid = current_thread - > tid ;
}
pid_t tasking_get_rpc_calling_thread ( pid_t * tid ) {
* tid = current_thread - > rpc_calling_tid ;
return current_thread - > rpc_calling_pid ;
}
void tasking_set_rpc_ret_buf ( void * buf ) {
pid_t tid ;
pid_t pid = tasking_get_rpc_calling_thread ( & tid ) ;
Thread * thread = get_thread ( pid , tid ) ;
thread - > rpc_ret_buf = buf ;
}
void * tasking_get_rpc_ret_buf ( ) {
return current_thread - > rpc_ret_buf ;
}
void tasking_thread_exit ( ) {
tasking_block ( THREAD_EXITED ) ;
}
char tasking_check_proc_exists ( pid_t pid ) {
if ( processes [ pid ] . num_threads = = 0 ) {
return 0 ;
}
char num_exited_threads = 0 ;
for ( Thread * thread = processes [ pid ] . first_thread ; thread ! = NULL ; thread = thread - > next_thread_in_process ) {
if ( thread - > state = = THREAD_EXITED ) {
num_exited_threads + + ;
}
}
if ( ( num_exited_threads = processes [ pid ] . num_threads ) & & kernel_get_num_rpc_funcs ( pid ) = = 0 ) {
return 0 ;
} else {
return 1 ;
}
}