#include "tcb.hh" #include "global_scheduler.hh" #include "sporadic_task.hh" #include "options.hh" #include "time_io.hh" #include #include #include #include #include #include #include #include #include void tcb::store() { t->store(); } tcb::~tcb() { bool error = false; if (fork_processes) { if (kill(kill_group_pid, SIGKILL) == -1) { std::cerr << "Couldn't send kill signal." << std::endl; error = true; } } delete t; if (error) { exit(EXIT_FAILURE); } } tcb::tcb() { t = new Sporadic_Task(); s = State::UNEMPLOYED; // todo: duplicate } tcb::tcb(std::ifstream& f) { t = new Sporadic_Task(f); s = State::UNEMPLOYED; // todo: duplicate if (fork_processes) { pid = fork(); if (pid == -1) { std::cerr << "Fork failed." << std::endl; exit(EXIT_FAILURE); } else if (pid == 0) { t->launch_extern_program(); } else if (pid > 0) { if (setpgid(pid, pid) == -1) { std::cerr << "Couldn't change PGID." << std::endl; exit(EXIT_FAILURE); } kill_group_pid = -1*pid; sleep(1); // can't have this if (kill(kill_group_pid, SIGSTOP) == -1) { std::cerr << "STOP to " << kill_group_pid << " failed." << std::endl; exit(EXIT_FAILURE); } } } if (debug) { std::cout << "Contruction of TCB done." << std::endl; } } bool tcb::is_employed() { return ((s == State::READY) or (s == State::RUNNING)); } void tcb::wierd_task_state() { std::cerr << "Wierd task state. Go find the bug." << std::endl; exit(EXIT_FAILURE); } void tcb::cont_process() { if (kill(kill_group_pid, SIGCONT) == -1) { std::cerr << "Couldn't CONT: " << kill_group_pid << std::endl; exit(EXIT_FAILURE); } } void tcb::stop_process() { if (kill(kill_group_pid, SIGSTOP) == -1) { std::cerr << "Couldn't STOP: " << kill_group_pid << std::endl; exit(EXIT_FAILURE); } } void tcb::usr1_process() { if (kill(kill_group_pid, SIGUSR1) == -1) { std::cerr << "Couldn't USR1: " << kill_group_pid << std::endl; exit(EXIT_FAILURE); } std::cerr << "USR1 sent to: " << kill_group_pid << std::endl; } void tcb::kill_process() { if (kill(kill_group_pid, SIGKILL) == -1) { std::cerr << "Couldn't KILL: " << kill_group_pid << std::endl; exit(EXIT_FAILURE); } } void tcb::extern_process_change_state(tcb::State state) { switch (state) { case State::NOT_INIT: wierd_task_state(); break; case State::READY: case State::HOLD: case State::UNEMPLOYED: stop_process(); break; case State::RUNNING: cont_process(); break; case State::DELAYED: kill_process(); break; } } void tcb::change_state(State state) { if (!quiet) { std::cout << "task " << get_id() << ": " << std::setw(10); print_state(); } switch (state) { case State::NOT_INIT: wierd_task_state(); break; case State::READY: assert((s == State::UNEMPLOYED) or (s == State::RUNNING)); break; case State::RUNNING: assert(s == State::READY); break; case State::HOLD: assert(s == State::RUNNING); break; case State::UNEMPLOYED: assert(s == State::HOLD); break; case State::DELAYED: assert(is_employed()); break; } s = state; if (fork_processes) { extern_process_change_state(s); } if (!quiet) { std::cout << " -> "; print_state(); std::cout << std::endl; } } /* release is UNEMPLOYED -> READY */ void tcb::release() { change_state(State::READY); CPU_time = ms_t(0); rt_a = std::chrono::system_clock::now(); rt_c = rt_a + get_c(); rt_d = rt_a + get_d(); rt_t = rt_a + get_t(); if (fork_processes) { usr1_process(); } } /* run is READY -> RUNNING */ void tcb::run() { change_state(State::RUNNING); } /* stop is RUNNING -> READY. The reason there is no assertion is it might be useful to just tell a bunch of tasks to stop and don't bother what state they have, unless for the one RUNNING in what case it is READY (i.e., stopped). */ void tcb::stop() { if (s == State::RUNNING) { change_state(State::READY); } } void tcb::tick(ms_t tick_time) { switch (s) { case State::NOT_INIT: case State::DELAYED: case State::READY: case State::HOLD: case State::UNEMPLOYED: break; case State::RUNNING: tick_running(tick_time); break; } update_state(); if (print_task_tick) { std::cout << this; } } void tcb::tick_running(ms_t tick_time) { assert(s == State::RUNNING); t->run(tick_time); CPU_time += tick_time; check_done(); } void tcb::update_state() { check_release(); check_period_over(); check_delayed(); } /* check_release is UNEMPLOYED -> READY */ void tcb::check_release() { if (s == State::UNEMPLOYED) { if (random_delay) { int r = rand() % 10; if (r == 0) { release(); } } else { release(); } } } /* check_done is RUNNING -> HOLD */ void tcb::check_done() { if (CPU_time >= get_c()) { change_state(State::HOLD); } } /* check_period_over is HOLD -> UNEMPLOYED */ void tcb::check_period_over() { if (s == State::HOLD) { if (std::chrono::system_clock::now() >= rt_t) { change_state(State::UNEMPLOYED); } } } /* only a READY or RUNNING task can be DELAYED. HOLD is as "unemployed" as UNEMPLOYED */ void tcb::check_delayed() { if (is_employed()) { if (std::chrono::system_clock::now() >= rt_d) { change_state(State::DELAYED); if (not really_quiet) { std::cout << "Oh, no! Delayed task!" << std::endl; } if (fork_processes) { kill_process(); } if (hard) { crash = true; } } } } void tcb::print_state(std::ostream& os) { switch(s) { case State::UNEMPLOYED: os << "unemployed"; break; case State::NOT_INIT: os << "not_init"; break; case State::HOLD: os << "hold"; break; case State::READY: os << "ready"; break; case State::RUNNING: os << "running"; break; case State::DELAYED: os << "delayed"; break; } } void tcb::print_runtime_data(std::ostream& os) { print_completed(os); print_actual_deadline(os); print_end_of_period(os); } void tcb::print_end_of_period(std::ostream& os) { os << "Earliest possible next instance: " << rt_t << std::endl; } void tcb::print_completed(std::ostream& os) { os << "Running time (CPU-time) so far: " << CPU_time << std::endl; } void tcb::print_actual_deadline(std::ostream& os) { os << "Actual deadline: " << rt_d << std::endl; } void tcb::print_relevant_runtime_data(std::ostream& os) { switch (s) { case State::NOT_INIT: case State::UNEMPLOYED: break; case State::HOLD: print_end_of_period(os); break; case State::DELAYED: case State::READY: case State::RUNNING: print_completed(os); print_actual_deadline(os); break; } } void tcb::print_task_parameters(std::ostream& os) { if (s != State::NOT_INIT) { os << t << std::endl; } else { std::cerr << "Not initialized: " << this << std::endl; } } void tcb::print(std::ostream& os) { if (!quiet) { os << "--- task " << get_id() << " ---" << std::endl; print_task_parameters(os); print_state(os); print_relevant_runtime_data(os); std::cout << std::endl; } } std::ostream& operator<<(std::ostream& os, tcb* tcb) { tcb->print(os); return os; } tp_t tcb::get_rt_c() { return rt_c; }