2019-02-06 14:05:47 +00:00
# include <AK/TemporaryChange.h>
2019-07-09 12:23:12 +00:00
# include <Kernel/Arch/i386/PIT.h>
2019-05-15 19:40:41 +00:00
# include <Kernel/Devices/PCSpeaker.h>
2019-06-07 09:43:58 +00:00
# include <Kernel/FileSystem/FileDescription.h>
2019-06-07 17:29:34 +00:00
# include <Kernel/Process.h>
# include <Kernel/RTC.h>
# include <Kernel/Scheduler.h>
2018-11-07 21:15:02 +00:00
2019-07-19 15:21:13 +00:00
struct SchedulerData {
typedef IntrusiveList < Thread , & Thread : : m_runnable_list_node > ThreadList ;
ThreadList m_runnable_threads ;
ThreadList m_nonrunnable_threads ;
ThreadList & thread_list_for_state ( Thread : : State state )
{
if ( Thread : : is_runnable_state ( state ) )
return m_runnable_threads ;
return m_nonrunnable_threads ;
}
} ;
static SchedulerData * g_scheduler_data ;
void Scheduler : : init_thread ( Thread & thread )
{
g_scheduler_data - > m_nonrunnable_threads . append ( thread ) ;
}
void Scheduler : : update_state_for_thread ( Thread & thread )
{
auto & list = g_scheduler_data - > thread_list_for_state ( thread . state ( ) ) ;
if ( list . contains ( thread ) )
return ;
list . append ( thread ) ;
}
IterationDecision Scheduler : : for_each_runnable_func ( Function < IterationDecision ( Thread & ) > & & callback )
{
ASSERT_INTERRUPTS_DISABLED ( ) ;
auto & tl = g_scheduler_data - > m_runnable_threads ;
for ( auto it = tl . begin ( ) ; it ! = tl . end ( ) ; ) {
auto thread = * it ;
it = + + it ;
if ( callback ( * thread ) = = IterationDecision : : Break )
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
}
IterationDecision Scheduler : : for_each_nonrunnable_func ( Function < IterationDecision ( Thread & ) > & & callback )
{
ASSERT_INTERRUPTS_DISABLED ( ) ;
auto & tl = g_scheduler_data - > m_nonrunnable_threads ;
for ( auto it = tl . begin ( ) ; it ! = tl . end ( ) ; ) {
auto thread = * it ;
it = + + it ;
if ( callback ( * thread ) = = IterationDecision : : Break )
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
}
2018-11-07 21:24:20 +00:00
//#define LOG_EVERY_CONTEXT_SWITCH
2018-11-07 21:15:02 +00:00
//#define SCHEDULER_DEBUG
2019-07-17 12:17:49 +00:00
//#define SCHEDULER_RUNNABLE_DEBUG
2018-11-07 21:15:02 +00:00
2019-07-03 19:17:35 +00:00
static u32 time_slice_for ( Process : : Priority priority )
2019-02-07 11:21:17 +00:00
{
// One time slice unit == 1ms
switch ( priority ) {
case Process : : HighPriority :
return 50 ;
2019-02-12 11:11:22 +00:00
case Process : : NormalPriority :
return 15 ;
case Process : : LowPriority :
return 5 ;
2019-04-20 13:58:45 +00:00
case Process : : IdlePriority :
return 1 ;
2019-02-07 11:21:17 +00:00
}
2019-02-12 11:11:22 +00:00
ASSERT_NOT_REACHED ( ) ;
2019-02-07 11:21:17 +00:00
}
2018-11-07 21:15:02 +00:00
2019-03-23 21:03:17 +00:00
Thread * current ;
Thread * g_last_fpu_thread ;
Thread * g_finalizer ;
2018-11-07 21:15:02 +00:00
static Process * s_colonel_process ;
2019-07-03 19:17:35 +00:00
u64 g_uptime ;
static u64 s_beep_timeout ;
2018-11-07 21:15:02 +00:00
2018-11-07 22:13:38 +00:00
struct TaskRedirectionData {
2019-07-03 19:17:35 +00:00
u16 selector ;
2018-11-07 22:13:38 +00:00
TSS32 tss ;
} ;
static TaskRedirectionData s_redirection ;
2019-02-06 14:05:47 +00:00
static bool s_active ;
bool Scheduler : : is_active ( )
{
return s_active ;
}
2018-11-07 22:13:38 +00:00
2019-05-15 19:40:41 +00:00
void Scheduler : : beep ( )
{
PCSpeaker : : tone_on ( 440 ) ;
s_beep_timeout = g_uptime + 100 ;
}
2019-07-19 11:19:47 +00:00
Thread : : FileDescriptionBlocker : : FileDescriptionBlocker ( const FileDescription & description )
2019-07-18 14:22:26 +00:00
: m_blocked_description ( description )
{ }
2019-07-19 11:19:47 +00:00
const FileDescription & Thread : : FileDescriptionBlocker : : blocked_description ( ) const
2019-07-18 14:22:26 +00:00
{
return m_blocked_description ;
}
2019-07-19 11:19:47 +00:00
Thread : : AcceptBlocker : : AcceptBlocker ( const FileDescription & description )
2019-07-18 16:12:37 +00:00
: FileDescriptionBlocker ( description )
2019-07-18 14:22:26 +00:00
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : AcceptBlocker : : should_unblock ( Thread & , time_t , long )
2019-07-18 14:22:26 +00:00
{
2019-07-19 11:19:47 +00:00
auto & socket = * blocked_description ( ) . socket ( ) ;
2019-07-18 14:22:26 +00:00
return socket . can_accept ( ) ;
}
2019-07-19 11:19:47 +00:00
Thread : : ReceiveBlocker : : ReceiveBlocker ( const FileDescription & description )
2019-07-18 16:12:37 +00:00
: FileDescriptionBlocker ( description )
2019-07-18 14:22:26 +00:00
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : ReceiveBlocker : : should_unblock ( Thread & , time_t now_sec , long now_usec )
2019-07-18 14:22:26 +00:00
{
2019-07-19 11:19:47 +00:00
auto & socket = * blocked_description ( ) . socket ( ) ;
2019-07-18 14:22:26 +00:00
// FIXME: Block until the amount of data wanted is available.
bool timed_out = now_sec > socket . receive_deadline ( ) . tv_sec | | ( now_sec = = socket . receive_deadline ( ) . tv_sec & & now_usec > = socket . receive_deadline ( ) . tv_usec ) ;
2019-07-19 11:19:47 +00:00
if ( timed_out | | blocked_description ( ) . can_read ( ) )
2019-07-18 14:22:26 +00:00
return true ;
return false ;
}
2019-07-19 11:19:47 +00:00
Thread : : ConnectBlocker : : ConnectBlocker ( const FileDescription & description )
2019-07-18 16:12:37 +00:00
: FileDescriptionBlocker ( description )
2019-07-18 14:22:26 +00:00
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : ConnectBlocker : : should_unblock ( Thread & , time_t , long )
2019-07-18 14:22:26 +00:00
{
2019-07-19 11:19:47 +00:00
auto & socket = * blocked_description ( ) . socket ( ) ;
2019-07-18 14:22:26 +00:00
return socket . is_connected ( ) ;
}
2019-07-19 11:19:47 +00:00
Thread : : WriteBlocker : : WriteBlocker ( const FileDescription & description )
2019-07-18 16:12:37 +00:00
: FileDescriptionBlocker ( description )
2019-07-18 14:22:26 +00:00
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : WriteBlocker : : should_unblock ( Thread & , time_t , long )
2019-07-18 14:22:26 +00:00
{
2019-07-19 11:19:47 +00:00
return blocked_description ( ) . can_write ( ) ;
2019-07-18 14:22:26 +00:00
}
2019-07-19 11:19:47 +00:00
Thread : : ReadBlocker : : ReadBlocker ( const FileDescription & description )
2019-07-18 16:12:37 +00:00
: FileDescriptionBlocker ( description )
2019-07-18 14:22:26 +00:00
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : ReadBlocker : : should_unblock ( Thread & , time_t , long )
2019-07-18 14:22:26 +00:00
{
// FIXME: Block until the amount of data wanted is available.
2019-07-19 11:19:47 +00:00
return blocked_description ( ) . can_read ( ) ;
2019-07-18 14:22:26 +00:00
}
2019-07-19 11:19:47 +00:00
Thread : : ConditionBlocker : : ConditionBlocker ( const char * state_string , Function < bool ( ) > & & condition )
2019-07-18 14:22:26 +00:00
: m_block_until_condition ( move ( condition ) )
2019-07-19 07:51:48 +00:00
, m_state_string ( state_string )
2019-07-18 14:22:26 +00:00
{
ASSERT ( m_block_until_condition ) ;
}
2019-07-18 16:12:37 +00:00
bool Thread : : ConditionBlocker : : should_unblock ( Thread & , time_t , long )
2019-07-18 14:22:26 +00:00
{
return m_block_until_condition ( ) ;
}
2019-07-18 16:12:37 +00:00
Thread : : SleepBlocker : : SleepBlocker ( u64 wakeup_time )
2019-07-18 15:26:11 +00:00
: m_wakeup_time ( wakeup_time )
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : SleepBlocker : : should_unblock ( Thread & , time_t , long )
2019-07-18 15:26:11 +00:00
{
return m_wakeup_time < = g_uptime ;
}
2019-07-19 07:04:12 +00:00
Thread : : SelectBlocker : : SelectBlocker ( const timeval & tv , bool select_has_timeout , const FDVector & read_fds , const FDVector & write_fds , const FDVector & except_fds )
2019-07-18 15:39:49 +00:00
: m_select_timeout ( tv )
, m_select_has_timeout ( select_has_timeout )
, m_select_read_fds ( read_fds )
, m_select_write_fds ( write_fds )
, m_select_exceptional_fds ( except_fds )
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : SelectBlocker : : should_unblock ( Thread & thread , time_t now_sec , long now_usec )
2019-07-18 15:39:49 +00:00
{
if ( m_select_has_timeout ) {
if ( now_sec > m_select_timeout . tv_sec | | ( now_sec = = m_select_timeout . tv_sec & & now_usec > = m_select_timeout . tv_usec ) )
return true ;
}
auto & process = thread . process ( ) ;
for ( int fd : m_select_read_fds ) {
if ( process . m_fds [ fd ] . description - > can_read ( ) )
return true ;
}
for ( int fd : m_select_write_fds ) {
if ( process . m_fds [ fd ] . description - > can_write ( ) )
return true ;
}
return false ;
}
2019-07-18 16:12:37 +00:00
Thread : : WaitBlocker : : WaitBlocker ( int wait_options , pid_t & waitee_pid )
2019-07-18 16:05:19 +00:00
: m_wait_options ( wait_options )
, m_waitee_pid ( waitee_pid )
{
}
2019-07-18 16:12:37 +00:00
bool Thread : : WaitBlocker : : should_unblock ( Thread & thread , time_t , long )
2019-07-18 16:05:19 +00:00
{
bool should_unblock = false ;
thread . process ( ) . for_each_child ( [ & ] ( Process & child ) {
if ( m_waitee_pid ! = - 1 & & m_waitee_pid ! = child . pid ( ) )
return IterationDecision : : Continue ;
bool child_exited = child . is_dead ( ) ;
bool child_stopped = child . main_thread ( ) . state ( ) = = Thread : : State : : Stopped ;
bool wait_finished = ( ( m_wait_options & WEXITED ) & & child_exited )
| | ( ( m_wait_options & WSTOPPED ) & & child_stopped ) ;
if ( ! wait_finished )
return IterationDecision : : Continue ;
m_waitee_pid = child . pid ( ) ;
should_unblock = true ;
return IterationDecision : : Break ;
} ) ;
return should_unblock ;
}
2019-07-19 07:34:11 +00:00
Thread : : SemiPermanentBlocker : : SemiPermanentBlocker ( Reason reason )
: m_reason ( reason )
{ }
bool Thread : : SemiPermanentBlocker : : should_unblock ( Thread & , time_t , long )
{
// someone else has to unblock us
return false ;
}
2019-07-18 12:10:28 +00:00
// Called by the scheduler on threads that are blocked for some reason.
// Make a decision as to whether to unblock them or not.
void Thread : : consider_unblock ( time_t now_sec , long now_usec )
{
switch ( state ( ) ) {
case Thread : : Invalid :
case Thread : : Runnable :
case Thread : : Running :
case Thread : : Dead :
case Thread : : Stopped :
/* don't know, don't care */
return ;
2019-07-19 07:37:34 +00:00
case Thread : : Blocked :
2019-07-21 10:14:58 +00:00
ASSERT ( ! m_blockers . is_empty ( ) ) ;
if ( m_blockers . first ( ) - > should_unblock ( * this , now_sec , now_usec ) )
2019-07-18 12:10:28 +00:00
unblock ( ) ;
return ;
case Thread : : Skip1SchedulerPass :
set_state ( Thread : : Skip0SchedulerPasses ) ;
return ;
case Thread : : Skip0SchedulerPasses :
set_state ( Thread : : Runnable ) ;
return ;
case Thread : : Dying :
ASSERT ( g_finalizer ) ;
2019-07-19 07:34:11 +00:00
if ( g_finalizer - > is_blocked ( ) )
2019-07-18 12:10:28 +00:00
g_finalizer - > unblock ( ) ;
return ;
}
}
2018-11-07 21:15:02 +00:00
bool Scheduler : : pick_next ( )
{
ASSERT_INTERRUPTS_DISABLED ( ) ;
2019-02-06 14:05:47 +00:00
ASSERT ( ! s_active ) ;
TemporaryChange < bool > change ( s_active , true ) ;
ASSERT ( s_active ) ;
2018-11-07 21:15:02 +00:00
if ( ! current ) {
// XXX: The first ever context_switch() goes to the idle process.
// This to setup a reliable place we can return to.
2019-03-23 21:03:17 +00:00
return context_switch ( s_colonel_process - > main_thread ( ) ) ;
2018-11-07 21:15:02 +00:00
}
2019-03-25 01:06:57 +00:00
struct timeval now ;
kgettimeofday ( now ) ;
2019-07-18 12:10:28 +00:00
2019-03-25 01:06:57 +00:00
auto now_sec = now . tv_sec ;
auto now_usec = now . tv_usec ;
2019-03-13 12:13:23 +00:00
2019-03-23 21:03:17 +00:00
// Check and unblock threads whose wait conditions have been met.
2019-07-19 15:21:13 +00:00
Scheduler : : for_each_nonrunnable ( [ & ] ( Thread & thread ) {
2019-07-18 12:10:28 +00:00
thread . consider_unblock ( now_sec , now_usec ) ;
2019-03-23 21:03:17 +00:00
return IterationDecision : : Continue ;
} ) ;
2019-06-07 09:43:58 +00:00
Process : : for_each ( [ & ] ( Process & process ) {
2019-03-23 21:03:17 +00:00
if ( process . is_dead ( ) ) {
if ( current ! = & process . main_thread ( ) & & ( ! process . ppid ( ) | | ! Process : : from_pid ( process . ppid ( ) ) ) ) {
2018-11-28 21:01:24 +00:00
auto name = process . name ( ) ;
auto pid = process . pid ( ) ;
auto exit_status = Process : : reap ( process ) ;
2019-01-01 02:56:39 +00:00
dbgprintf ( " reaped unparented process %s(%u), exit status: %u \n " , name . characters ( ) , pid , exit_status ) ;
2018-11-28 21:01:24 +00:00
}
2019-06-07 09:30:07 +00:00
return IterationDecision : : Continue ;
}
if ( process . m_alarm_deadline & & g_uptime > process . m_alarm_deadline ) {
process . m_alarm_deadline = 0 ;
process . send_signal ( SIGALRM , nullptr ) ;
2018-11-07 22:59:49 +00:00
}
2019-06-07 09:30:07 +00:00
return IterationDecision : : Continue ;
2018-11-07 21:15:02 +00:00
} ) ;
// Dispatch any pending signals.
// FIXME: Do we really need this to be a separate pass over the process list?
2019-07-19 10:16:00 +00:00
Thread : : for_each_living ( [ ] ( Thread & thread ) - > IterationDecision {
2019-03-23 21:03:17 +00:00
if ( ! thread . has_unmasked_pending_signals ( ) )
2019-07-19 10:16:00 +00:00
return IterationDecision : : Continue ;
2019-03-05 11:50:55 +00:00
// FIXME: It would be nice if the Scheduler didn't have to worry about who is "current"
// For now, avoid dispatching signals to "current" and do it in a scheduling pass
// while some other process is interrupted. Otherwise a mess will be made.
2019-03-23 21:03:17 +00:00
if ( & thread = = current )
2019-07-19 10:16:00 +00:00
return IterationDecision : : Continue ;
2018-11-07 21:15:02 +00:00
// We know how to interrupt blocked processes, but if they are just executing
// at some random point in the kernel, let them continue. They'll be in userspace
// sooner or later and we can deliver the signal then.
// FIXME: Maybe we could check when returning from a syscall if there's a pending
// signal and dispatch it then and there? Would that be doable without the
// syscall effectively being "interrupted" despite having completed?
2019-03-23 21:03:17 +00:00
if ( thread . in_kernel ( ) & & ! thread . is_blocked ( ) & & ! thread . is_stopped ( ) )
2019-07-19 10:16:00 +00:00
return IterationDecision : : Continue ;
2018-11-28 22:30:06 +00:00
// NOTE: dispatch_one_pending_signal() may unblock the process.
2019-03-23 21:03:17 +00:00
bool was_blocked = thread . is_blocked ( ) ;
if ( thread . dispatch_one_pending_signal ( ) = = ShouldUnblockThread : : No )
2019-07-19 10:16:00 +00:00
return IterationDecision : : Continue ;
2018-11-28 22:30:06 +00:00
if ( was_blocked ) {
2019-03-23 21:03:17 +00:00
dbgprintf ( " Unblock %s(%u) due to signal \n " , thread . process ( ) . name ( ) . characters ( ) , thread . pid ( ) ) ;
2019-07-21 10:14:58 +00:00
ASSERT ( ! thread . m_blockers . is_empty ( ) ) ;
thread . m_blockers . first ( ) - > set_interrupted_by_signal ( ) ;
2019-03-23 21:03:17 +00:00
thread . unblock ( ) ;
2018-11-07 21:15:02 +00:00
}
2019-07-19 10:16:00 +00:00
return IterationDecision : : Continue ;
2018-11-07 21:15:02 +00:00
} ) ;
2019-07-17 12:17:49 +00:00
# ifdef SCHEDULER_RUNNABLE_DEBUG
2019-05-20 01:44:45 +00:00
dbgprintf ( " Non-runnables: \n " ) ;
2019-07-19 10:18:40 +00:00
Thread : : for_each_nonrunnable ( [ ] ( Thread & thread ) - > IterationDecision {
auto & process = thread . process ( ) ;
dbgprintf ( " [K%x] %-12s %s(%u:%u) @ %w:%x \n " , & process , thread . state_string ( ) , process . name ( ) . characters ( ) , process . pid ( ) , thread . tid ( ) , thread . tss ( ) . cs , thread . tss ( ) . eip ) ;
return IterationDecision : : Continue ;
} ) ;
2019-05-20 01:44:45 +00:00
dbgprintf ( " Runnables: \n " ) ;
2019-07-19 10:18:40 +00:00
Thread : : for_each_runnable ( [ ] ( Thread & thread ) - > IterationDecision {
auto & process = thread . process ( ) ;
dbgprintf ( " [K%x] %-12s %s(%u:%u) @ %w:%x \n " , & process , thread . state_string ( ) , process . name ( ) . characters ( ) , process . pid ( ) , thread . tid ( ) , thread . tss ( ) . cs , thread . tss ( ) . eip ) ;
return IterationDecision : : Continue ;
} ) ;
2018-11-07 21:15:02 +00:00
# endif
2019-07-19 15:21:13 +00:00
auto & runnable_list = g_scheduler_data - > m_runnable_threads ;
2019-07-19 11:04:42 +00:00
if ( runnable_list . is_empty ( ) )
2019-05-18 18:24:55 +00:00
return context_switch ( s_colonel_process - > main_thread ( ) ) ;
2019-07-19 11:04:42 +00:00
auto * previous_head = runnable_list . first ( ) ;
2018-11-07 21:15:02 +00:00
for ( ; ; ) {
// Move head to tail.
2019-08-01 17:32:16 +00:00
runnable_list . append ( * runnable_list . first ( ) ) ;
2019-07-19 11:04:42 +00:00
auto * thread = runnable_list . first ( ) ;
2018-11-07 21:15:02 +00:00
2019-03-23 21:03:17 +00:00
if ( ! thread - > process ( ) . is_being_inspected ( ) & & ( thread - > state ( ) = = Thread : : Runnable | | thread - > state ( ) = = Thread : : Running ) ) {
2018-11-07 21:15:02 +00:00
# ifdef SCHEDULER_DEBUG
2019-07-17 12:17:49 +00:00
dbgprintf ( " switch to %s(%u:%u) @ %w:%x \n " , thread - > process ( ) . name ( ) . characters ( ) , thread - > process ( ) . pid ( ) , thread - > tid ( ) , thread - > tss ( ) . cs , thread - > tss ( ) . eip ) ;
2018-11-07 21:15:02 +00:00
# endif
2019-03-23 21:03:17 +00:00
return context_switch ( * thread ) ;
2018-11-07 21:15:02 +00:00
}
2019-03-23 21:03:17 +00:00
if ( thread = = previous_head ) {
2018-11-07 22:13:38 +00:00
// Back at process_head, nothing wants to run. Send in the colonel!
2019-03-23 21:03:17 +00:00
return context_switch ( s_colonel_process - > main_thread ( ) ) ;
2018-11-07 21:15:02 +00:00
}
}
}
2019-03-23 21:03:17 +00:00
bool Scheduler : : donate_to ( Thread * beneficiary , const char * reason )
2019-02-07 10:12:23 +00:00
{
2019-04-17 10:41:51 +00:00
InterruptDisabler disabler ;
if ( ! Thread : : is_thread ( beneficiary ) )
return false ;
2019-02-07 10:12:23 +00:00
( void ) reason ;
unsigned ticks_left = current - > ticks_left ( ) ;
2019-04-17 10:41:51 +00:00
if ( ! beneficiary | | beneficiary - > state ( ) ! = Thread : : Runnable | | ticks_left < = 1 )
2019-02-07 10:12:23 +00:00
return yield ( ) ;
2019-03-23 21:03:17 +00:00
unsigned ticks_to_donate = min ( ticks_left - 1 , time_slice_for ( beneficiary - > process ( ) . priority ( ) ) ) ;
2019-02-07 10:12:23 +00:00
# ifdef SCHEDULER_DEBUG
2019-03-23 21:03:17 +00:00
dbgprintf ( " %s(%u:%u) donating %u ticks to %s(%u:%u), reason=%s \n " , current - > process ( ) . name ( ) . characters ( ) , current - > pid ( ) , current - > tid ( ) , ticks_to_donate , beneficiary - > process ( ) . name ( ) . characters ( ) , beneficiary - > pid ( ) , beneficiary - > tid ( ) , reason ) ;
2019-02-07 10:12:23 +00:00
# endif
context_switch ( * beneficiary ) ;
beneficiary - > set_ticks_left ( ticks_to_donate ) ;
switch_now ( ) ;
2019-04-17 10:41:51 +00:00
return false ;
2019-02-07 10:12:23 +00:00
}
2018-11-07 21:15:02 +00:00
bool Scheduler : : yield ( )
{
2019-01-23 04:05:45 +00:00
InterruptDisabler disabler ;
2019-02-06 14:05:47 +00:00
ASSERT ( current ) ;
2019-06-07 09:43:58 +00:00
// dbgprintf("%s(%u:%u) yield()\n", current->process().name().characters(), current->pid(), current->tid());
2018-11-07 21:15:02 +00:00
2019-02-06 14:05:47 +00:00
if ( ! pick_next ( ) )
2019-04-17 10:41:51 +00:00
return false ;
2018-11-07 21:15:02 +00:00
2019-06-07 09:43:58 +00:00
// dbgprintf("yield() jumping to new process: sel=%x, %s(%u:%u)\n", current->far_ptr().selector, current->process().name().characters(), current->pid(), current->tid());
2018-11-07 21:15:02 +00:00
switch_now ( ) ;
2019-04-17 10:41:51 +00:00
return true ;
2018-11-07 21:15:02 +00:00
}
void Scheduler : : pick_next_and_switch_now ( )
{
bool someone_wants_to_run = pick_next ( ) ;
ASSERT ( someone_wants_to_run ) ;
switch_now ( ) ;
}
void Scheduler : : switch_now ( )
{
2018-12-02 23:39:25 +00:00
Descriptor & descriptor = get_gdt_entry ( current - > selector ( ) ) ;
2018-11-07 21:15:02 +00:00
descriptor . type = 9 ;
2018-12-02 23:39:25 +00:00
flush_gdt ( ) ;
2018-11-07 21:15:02 +00:00
asm ( " sti \n "
2019-06-07 09:43:58 +00:00
" ljmp *(%%eax) \n " : : " a " ( & current - > far_ptr ( ) ) ) ;
2018-11-07 21:15:02 +00:00
}
2019-03-23 21:03:17 +00:00
bool Scheduler : : context_switch ( Thread & thread )
2018-11-07 21:15:02 +00:00
{
2019-03-23 21:03:17 +00:00
thread . set_ticks_left ( time_slice_for ( thread . process ( ) . priority ( ) ) ) ;
thread . did_schedule ( ) ;
2018-11-07 21:15:02 +00:00
2019-03-23 21:03:17 +00:00
if ( current = = & thread )
2018-11-07 21:15:02 +00:00
return false ;
if ( current ) {
// If the last process hasn't blocked (still marked as running),
// mark it as runnable for the next round.
2019-03-23 21:03:17 +00:00
if ( current - > state ( ) = = Thread : : Running )
current - > set_state ( Thread : : Runnable ) ;
2018-11-07 21:24:20 +00:00
# ifdef LOG_EVERY_CONTEXT_SWITCH
2019-03-23 21:03:17 +00:00
dbgprintf ( " Scheduler: %s(%u:%u) -> %s(%u:%u) %w:%x \n " ,
2019-06-07 09:43:58 +00:00
current - > process ( ) . name ( ) . characters ( ) , current - > process ( ) . pid ( ) , current - > tid ( ) ,
thread . process ( ) . name ( ) . characters ( ) , thread . process ( ) . pid ( ) , thread . tid ( ) ,
thread . tss ( ) . cs , thread . tss ( ) . eip ) ;
2018-11-07 21:24:20 +00:00
# endif
2018-11-07 21:15:02 +00:00
}
2019-03-23 21:03:17 +00:00
current = & thread ;
thread . set_state ( Thread : : Running ) ;
2018-11-07 21:15:02 +00:00
2019-03-23 21:03:17 +00:00
if ( ! thread . selector ( ) ) {
thread . set_selector ( gdt_alloc_entry ( ) ) ;
auto & descriptor = get_gdt_entry ( thread . selector ( ) ) ;
descriptor . set_base ( & thread . tss ( ) ) ;
2019-01-31 16:31:23 +00:00
descriptor . set_limit ( 0xffff ) ;
2018-11-07 21:15:02 +00:00
descriptor . dpl = 0 ;
descriptor . segment_present = 1 ;
descriptor . granularity = 1 ;
descriptor . zero = 0 ;
descriptor . operation_size = 1 ;
descriptor . descriptor_type = 0 ;
}
2019-03-23 21:03:17 +00:00
auto & descriptor = get_gdt_entry ( thread . selector ( ) ) ;
2018-11-07 21:15:02 +00:00
descriptor . type = 11 ; // Busy TSS
2018-12-02 23:39:25 +00:00
flush_gdt ( ) ;
2018-11-07 21:15:02 +00:00
return true ;
}
2018-11-07 22:13:38 +00:00
static void initialize_redirection ( )
2018-11-07 21:15:02 +00:00
{
2018-12-02 23:39:25 +00:00
auto & descriptor = get_gdt_entry ( s_redirection . selector ) ;
2019-01-31 16:31:23 +00:00
descriptor . set_base ( & s_redirection . tss ) ;
descriptor . set_limit ( 0xffff ) ;
2018-11-07 22:13:38 +00:00
descriptor . dpl = 0 ;
descriptor . segment_present = 1 ;
descriptor . granularity = 1 ;
descriptor . zero = 0 ;
descriptor . operation_size = 1 ;
descriptor . descriptor_type = 0 ;
descriptor . type = 9 ;
2018-12-02 23:39:25 +00:00
flush_gdt ( ) ;
2018-11-07 21:15:02 +00:00
}
void Scheduler : : prepare_for_iret_to_new_process ( )
{
2018-12-02 23:39:25 +00:00
auto & descriptor = get_gdt_entry ( s_redirection . selector ) ;
2018-11-07 22:13:38 +00:00
descriptor . type = 9 ;
s_redirection . tss . backlink = current - > selector ( ) ;
load_task_register ( s_redirection . selector ) ;
2018-11-07 21:15:02 +00:00
}
2019-03-23 21:03:17 +00:00
void Scheduler : : prepare_to_modify_tss ( Thread & thread )
2018-11-07 21:15:02 +00:00
{
2018-11-07 22:59:49 +00:00
// This ensures that a currently running process modifying its own TSS
// in order to yield() and end up somewhere else doesn't just end up
// right after the yield().
2019-03-23 21:03:17 +00:00
if ( current = = & thread )
2018-11-07 22:59:49 +00:00
load_task_register ( s_redirection . selector ) ;
2018-11-07 21:15:02 +00:00
}
2019-02-04 09:28:12 +00:00
Process * Scheduler : : colonel ( )
{
return s_colonel_process ;
}
2018-11-07 21:15:02 +00:00
void Scheduler : : initialize ( )
{
2019-07-19 15:21:13 +00:00
g_scheduler_data = new SchedulerData ;
2018-11-07 22:13:38 +00:00
s_redirection . selector = gdt_alloc_entry ( ) ;
initialize_redirection ( ) ;
2018-12-24 22:10:48 +00:00
s_colonel_process = Process : : create_kernel_process ( " colonel " , nullptr ) ;
2019-03-23 21:22:01 +00:00
// Make sure the colonel uses a smallish time slice.
2019-04-20 13:58:45 +00:00
s_colonel_process - > set_priority ( Process : : IdlePriority ) ;
2018-11-07 22:13:38 +00:00
load_task_register ( s_redirection . selector ) ;
2018-11-07 21:15:02 +00:00
}
2018-11-07 23:24:59 +00:00
void Scheduler : : timer_tick ( RegisterDump & regs )
{
if ( ! current )
return ;
2019-04-13 23:29:14 +00:00
+ + g_uptime ;
2018-11-07 23:24:59 +00:00
2019-05-15 19:40:41 +00:00
if ( s_beep_timeout & & g_uptime > s_beep_timeout ) {
PCSpeaker : : tone_off ( ) ;
s_beep_timeout = 0 ;
}
2018-11-07 23:24:59 +00:00
if ( current - > tick ( ) )
return ;
current - > tss ( ) . gs = regs . gs ;
current - > tss ( ) . fs = regs . fs ;
current - > tss ( ) . es = regs . es ;
current - > tss ( ) . ds = regs . ds ;
current - > tss ( ) . edi = regs . edi ;
current - > tss ( ) . esi = regs . esi ;
current - > tss ( ) . ebp = regs . ebp ;
current - > tss ( ) . ebx = regs . ebx ;
current - > tss ( ) . edx = regs . edx ;
current - > tss ( ) . ecx = regs . ecx ;
current - > tss ( ) . eax = regs . eax ;
current - > tss ( ) . eip = regs . eip ;
current - > tss ( ) . cs = regs . cs ;
current - > tss ( ) . eflags = regs . eflags ;
// Compute process stack pointer.
// Add 12 for CS, EIP, EFLAGS (interrupt mechanic)
current - > tss ( ) . esp = regs . esp + 12 ;
current - > tss ( ) . ss = regs . ss ;
if ( ( current - > tss ( ) . cs & 3 ) ! = 0 ) {
current - > tss ( ) . ss = regs . ss_if_crossRing ;
current - > tss ( ) . esp = regs . esp_if_crossRing ;
}
if ( ! pick_next ( ) )
return ;
prepare_for_iret_to_new_process ( ) ;
// Set the NT (nested task) flag.
asm (
" pushf \n "
" orl $0x00004000, (%esp) \n "
2019-06-07 09:43:58 +00:00
" popf \n " ) ;
2018-11-07 23:24:59 +00:00
}