sgpemv2/src/text_simulation.cc

1566 lines
42 KiB
C++
Raw Normal View History

// src/backend/text_simulation.cc - Copyright 2005, 2006, University
// of Padova, dept. of Pure and Applied
// Mathematics
//
// This file is part of SGPEMv2.
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// SGPEMv2 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SGPEMv2; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "backend/string_utils.hh"
#include "backend/policies_gatekeeper.hh"
#include "backend/policy_manager.hh"
#include "backend/policy_parameters.hh"
#include "backend/history.hh"
#include "backend/static_process.hh"
#include "backend/static_resource.hh"
#include "backend/static_thread.hh"
#include "backend/static_request.hh"
#include "backend/static_sub_request.hh"
#include "backend/concrete_history.hh"
#include "text_simulation.hh"
#include <sstream>
using namespace std;
using namespace sgpem;
using namespace memory;
using Glib::Thread;
using Glib::ustring;
#include "smartp.tcc"
namespace sgpem
{
//TODO move this class to another file... (?)
template <typename T>
class CommandParameter
{
public:
CommandParameter(const ustring& _description,
const T& _low_bound,
const T& _up_bound,
bool _required,
const T& _preset);
CommandParameter(const PolicyParameters::Parameter<T>& pparam);
ustring description;
T low_bound;
T up_bound;
bool required;
T preset;
T value;
};
template <typename T>
CommandParameter<T>::CommandParameter(const ustring& _description,
const T& _low_bound,
const T& _up_bound,
bool _required,
const T& _preset) :
description(_description), low_bound(_low_bound),
up_bound(_up_bound), required(_required), preset(_preset),
value(_preset)
{
}
template <typename T>
CommandParameter<T>::CommandParameter(const PolicyParameters::Parameter<T>& pparam) :
description(pparam.get_name()),
low_bound(pparam.get_lower_bound()),
up_bound(pparam.get_upper_bound()), required(pparam.is_required()),
preset(pparam.get_default()), value(pparam.get_value())
{
}
}
TextSimulation::TextSimulation()
{
}
TextSimulation::~TextSimulation()
{
}
/**
Adds an IO_device and creates a thread which loops the read-parse-execute process
*/
void
TextSimulation::add_io_device(smart_ptr<IOManager> io)
{
_devices.push_back(io);
//pair<TextSimulation*, int> p(this, 0);
//if (!io->is_full_duplex())
// Thread::create( sigc::bind(&TextSimulation::_io_loop, p), true);
}
bool
TextSimulation::check_arguments_num(const Tokens& arguments, unsigned int num)
{
if(arguments.size() < num)
{
ostringstream oss;
oss << _("ERROR: this command requires at least ") << num << _(" arguments\n");
p_stderr(oss.str());
return false;
}
else if(arguments.size() > num)
p_stderr(_("WARNING: some arguments will be ignored\n"));
return true;
}
template <typename Container>
void
TextSimulation::show(const Container& entities)
{
for(unsigned int i = 0; i < entities.size(); ++i)
{
ostringstream oss;
oss << i + 1 << ". " << entities[i]->get_name() << endl;
p_stdout(oss.str());
}
}
// Specializations need to go explicitly inside the namespace. why?
namespace sgpem
{
template <>
void
TextSimulation::show<vector<Request*> >(const vector<Request*>& entities)
{
for(unsigned int i = 0; i < entities.size(); ++i)
{
ostringstream oss;
oss << i + 1 << ". instant: " << entities[i]->get_instant() << endl;
p_stdout(oss.str());
}
}
template <>
void
TextSimulation::show<vector<SubRequest*> >(const vector<SubRequest*>& entities)
{
for(unsigned int i = 0; i < entities.size(); ++i)
{
ostringstream oss;
oss << i + 1 << ". resource: " << entities[i]->get_resource_key() << endl;
p_stdout(oss.str());
}
}
template <>
void
TextSimulation::show<map<int, Resource*> >(const map<int, Resource*>& entities)
{
typedef map<int, Resource*>::const_iterator ResourceIt;
for(ResourceIt it = entities.begin(); it != entities.end(); ++it)
{
ostringstream oss;
oss << it->first << ". " << it->second->get_name();
oss << "[" << it->second->get_places() << "]" << endl;
p_stdout(oss.str());
}
}
}
template <typename T>
void
TextSimulation::get_parameter(CommandParameter<T>& parameter)
{
bool correct;
do
{
correct = true;
ostringstream buf;
if(parameter.required)
buf << "*";
buf << parameter.description << " (range: [" << parameter.low_bound << ", " <<
parameter.up_bound << "] current: " << parameter.value << ") : ";
p_stdout(buf.str());
ustring input = readline();
T value;
// FIXME semi-hack, it's a bit overkill to tokenize the string
// to find if it's only composed of white spaces...
// Indedeed there's a pro: by using extensively tokenize() we are more sure
// it's correct ;-)
if(tokenize(input).size() > 0)
{
try
{
value = string_to<T>(input);
if(value > parameter.up_bound || value < parameter.low_bound)
{
p_stderr(_("ERROR: Provided value is out of range\n"));
correct = false;
}
}
catch(domain_error e)
{
p_stderr(_("ERROR: Please provide a valid numeric value\n"));
correct = false;
}
if(correct)
parameter.value = value;
}
else if(parameter.required)
{
p_stderr(_("ERROR: This is a mandatory attribute; you MUST provide a valid value!\n"));
correct = false;
}
}
while(!correct);
}
// Specializations need to go explicitly inside the namespace. why?
namespace sgpem
{
template <>
void
TextSimulation::get_parameter<ustring>(CommandParameter<ustring>& parameter)
{
bool loop = true;
while(loop)
{
ustring buf;
if(parameter.required)
buf += "*";
p_stdout(buf + parameter.description +
" (current: \"" + parameter.value + "\") : ");
buf = readline();
// FIXME semi-hack, it's a bit overkill to tokenize the string
// to find if it's only composed of white spaces...
// Indedeed there's a pro: by using extensively tokenize() we are more sure
// it's correct ;-)
Tokens tokens = tokenize(buf);
if(tokens.size() == 0 && parameter.required)
p_stderr(_("ERROR: This is a mandatory atribute; you MUST provide a valid value!\n"));
else
{
// FIXME should we assign the entire line here or just a token?
parameter.value = buf;
loop = false;
}
}
}
template <>
void
TextSimulation::get_parameter<bool>(CommandParameter<bool>& parameter)
{
bool correct;
do
{
correct = true;
ostringstream buf;
if(parameter.required)
buf << "*";
buf << parameter.description <<
" (current: " << boolalpha << parameter.value << ") : ";
p_stdout(buf.str());
ustring str = readline();
// FIXME semi-hack, it's a bit overkill to tokenize the string
// to find if it's only composed of white spaces...
// Indedeed there's a pro: by using extensively tokenize() we are more sure
// it's correct ;-)
Tokens tokens = tokenize(str);
if(tokens.size() != 0)
{
try
{
parameter.value = string_to<bool>(str);
}
catch(domain_error e)
{
p_stderr(_("ERROR: Please provide a valid boolean value ('true' or 'false')\n"));
correct = false;
}
}
else if(parameter.required)
{
p_stderr(_("ERROR: This is a mandatory atribute; you MUST provide a valid value!\n"));
correct = false;
}
}
while(!correct);
}
}
void
TextSimulation::on_run(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
// Listen for updates only during scheduling
Simulation::get_instance().get_history().attach(*this);
try
{
Simulation::get_instance().run();
}
catch(UserInterruptException e)
{
p_stderr(_("ERROR: "));
p_stderr(e.what());
p_stderr(_("\nSimulation is now stopped\n"));
}
catch(NullPolicyException e)
{
p_stderr(_("ERROR: "));
p_stderr(e.what());
p_stderr(_("\nSimulation is now stopped\n"));
}
Simulation::get_instance().get_history().detach(*this);
}
void
TextSimulation::on_pause(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
Simulation::get_instance().pause();
}
void
TextSimulation::on_stop(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
Simulation::get_instance().stop();
}
void
TextSimulation::on_configure_cpu_policy(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
Policy* policy = Simulation::get_instance().get_policy();
if(policy == NULL)
{
p_stderr(_("ERROR: No policy actually selected for the simulation\n"));
return;
}
PolicyParameters& parameters = policy->get_parameters();
p_stdout(_("Please provide a value for each attribute:\n"));
p_stdout(_("Mandatory arguments are marked with an asterisk (*)\n\n"));
p_stdout(_("Integer arguments:\n"));
typedef map<ustring, PolicyParameters::Parameter<int> > IntParams;
typedef map<ustring, PolicyParameters::Parameter<float> > FloatParams;
typedef map<ustring, PolicyParameters::Parameter<ustring> > StringParams;
IntParams int_params = parameters.get_registered_int_parameters();
for(IntParams::iterator it = int_params.begin(); it != int_params.end();)
{
PolicyParameters::Parameter<int> &p = it->second;
CommandParameter<int> cmd_p(p);
get_parameter(cmd_p);
parameters.set_int(p.get_name(), cmd_p.value);
++it;
}
p_stdout(_("\nFloating-point arguments:\n"));
// NOTE this piece code is a verbatim copy of the one above.
// I tried solving this issue by using templates, but to make
// it work will require adding to PolicyParameters a member template
// method with 2 specializations...
FloatParams float_params = parameters.get_registered_float_parameters();
for(FloatParams::iterator it = float_params.begin(); it != float_params.end();)
{
PolicyParameters::Parameter<float> &p = it->second;
CommandParameter<float> cmd_p(p);
get_parameter(cmd_p);
parameters.set_float(p.get_name(), cmd_p.value);
++it;
}
p_stdout(_("\nString arguments:\n"));
StringParams string_params = parameters.get_registered_string_parameters();
for(StringParams::iterator it = string_params.begin(); it != string_params.end();)
{
PolicyParameters::Parameter<ustring> &p = it->second;
CommandParameter<ustring> cmd_p(p);
get_parameter(cmd_p);
parameters.set_string(p.get_name(), cmd_p.value);
++it;
}
}
void
TextSimulation::on_help(const Tokens& arguments)
{
ustring command;
if(arguments.size() > 0)
{
command = arguments[0].uppercase();
// print warning if necessary
check_arguments_num(arguments, 1);
}
if(command.size() == 0)
p_stdout(_("Avaiable commands:\nRUN\nSTOP\nPAUSE\n"
"CONFIGURE-CPU-POLICY\nHELP\nGET\nSET\nSHOW\nADD\n"
"REMOVE\nQUIT\n\n"
"HELP followed by a command name shows help about it.\n"
"ex. `HELP RUN` shows help about the command RUN\n"));
else if(command == "RUN")
p_stdout(_("-- RUN COMMAND --\nStarts the simulation. It can be "
"continuous or step-by-step depending on the mode configured with "
"SET CONTINUOUS (default=true).\n\n"
"The output of RUN is a snapshot of the state of the simulation at each "
"instant.\n"
"The instant 0 represents the initial state, "
"during which no process is running. The scheduler "
"activity begins at instant 1.\n"));
else if(command == "STOP")
p_stdout(_("-- STOP COMMAND --\nStops the simulation. The next call to RUN will "
"bring the simulation to the first instant and start it.\n"));
else if(command == "PAUSE")
p_stdout(_("-- PAUSE COMMAND --\nPauses the simulation. The next call to RUN will "
"continue it.\n"));
else if(command == "CONFIGURE-CPU-POLICY")
p_stdout(_("-- CONFIGURE-CPU-POLICY COMMAND --\nConfigure parameters exposed by "
"the cpu policy.\n\nThis is currently the only way to control the behaviour of "
"cpu policies without modifying their source code.\n"));
else if(command == "HELP")
p_stdout(_("-- HELP COMMAND --\nThe help you're reading.\n"));
else if(command == "GET")
p_stdout(_("-- GET COMMAND --\nSyntax: GET <attr_name>\n"
"\twhere <attr_name> may be simulation-tick or continuous.\n"));
else if(command == "SET")
p_stdout(_("-- SET COMMAND --\nSyntax: SET <attr_name> [=] <value>\n"
"\twhere <attr_name> may be simulation-tick, continuous or cpu-policy.\n"));
else if(command == "SHOW")
p_stderr(_("-- SHOW COMMAND --\nDisplays the name of the entities (if available) "
"and other informations prefixed by its numeric identifier.\n\n"
"Syntax depends from entities being displayed:\n"
"`SHOW processes|resources|cpu-policies|resource-policies`\n"
"`SHOW threads <process_id>` with <process_id> being the numeric identifier of "
"the parent process\n"
"`SHOW requests <process_id> <thread_id>` with <thread_id> being the numeric "
"identifier of the thread child of process identified by <process_id>\n"
"`SHOW subrequests <process_id> <thread_id> <request_id>` where the numeric ids "
"follow the same logic of the previous commands\n"));
else if(command == "ADD")
p_stderr(_("-- ADD COMMAND --\nAdds an entity by using a questionary-like approach.\n\n"
"Syntax depends from entity being added:\n"
"`ADD process|resource`\n"
"`ADD thread <process_id>` with <process_id> being the numeric identifier of "
"the parent process\n"
"`ADD request <process_id> <thread_id>` with <thread_id> being the numeric "
"identifier of the thread child of process identified by <process_id>\n"
"`ADD subrequest <process_id> <thread_id> <request_id>` where the numeric ids "
"follow the same logic of the previous commands\n"));
else if(command == "REMOVE")
p_stderr(_("-- REMOVE COMMAND --\nRemoves an entity.\n\n"
"Syntax depends from entity being removed:\n"
"`REMOVE process|resource <id>` where <id> is the process or resource identifier\n"
"`REMOVE thread <process_id> <thread_id>` with <process_id> being the identifier of "
"the parent process, and <thread_id> the id of the thread to be removed\n"
"`REMOVE request <process_id> <thread_id> <request_id>` where the "
"numeric ids follow the same logic of the previous commands\n"
"`REMOVE subrequest <process_id> <thread_id> <request_id> <subrequest_id>` where the "
"numeric ids follow the same logic of the previous commands\n"));
else if(command == "QUIT")
p_stderr(_("-- QUIT COMMAND --\nGently closes the program.\n"));
else
p_stderr(_("ERROR: Sorry, no help available for this command.\n"));
}
void
TextSimulation::on_quit(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
p_stdout(_("\n\n*** Thank you for using SGPEM by Sirius Cybernetics Corporation ***\n\n"));
// Is this ok? Really? Oh, sure, if it we always did it in this way, it is surely a Good Thing!
exit(1);
}
void
TextSimulation::on_get(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 1))
return;
ustring attr = arguments[0].uppercase();
if(attr == "SIMULATION-TICK")
{
ostringstream oss;
oss << "simulation-tick = " << Simulation::get_instance().get_timer() << "ms" << endl;
p_stdout(oss.str());
}
else if(attr == "CONTINUOUS")
{
ostringstream oss;
oss << "continuous = " << boolalpha << Simulation::get_instance().get_mode() << endl;
p_stdout(oss.str());
}
else
p_stderr(_("ERROR: invalid attribute name. Accepted are: simulation-tick, continuous\n"));
}
void
TextSimulation::on_set(const Tokens& arguments)
{
// handle the optional "=' (I knew that I was buying myself a problem when I
// decided to support the assigment operator!)
if(arguments.size() >= 3)
check_arguments_num(arguments, 3);
else if(!check_arguments_num(arguments, 2))
return;
ustring attr = arguments[0].uppercase();
ustring value;
if(arguments[1] == "=")
value = arguments[2];
else
value = arguments[1];
if(attr == "SIMULATION-TICK")
{
try
{
int timer = string_to<int>(value);
if(timer < 0)
throw domain_error("");
Simulation::get_instance().set_timer(timer);
}
catch(domain_error e)
{
p_stderr(_("ERROR: you must provide a valid unsigned integer value\n"));
}
}
else if(attr == "CPU-POLICY")
{
int policy;
try
{
policy = string_to<int>(value) - 1;
if(policy < 0)
throw domain_error("");
typedef vector<PolicyManager*> ManagerVec;
typedef vector<Policy*>::iterator PolicyIt;
PoliciesGatekeeper& gatekeeper = PoliciesGatekeeper::get_instance();
ManagerVec managers = gatekeeper.get_registered();
for(ManagerVec::iterator it = managers.begin(); it != managers.end(); ++it)
{
vector<Policy*> policies = (*it)->get_avail_policies();
for(PolicyIt it = policies.begin(); it != policies.end(); ++it)
{
if(policy == 0)
Simulation::get_instance().set_policy(*it);
--policy;
}
}
if(policy >= 0)
throw domain_error("");
}
catch(domain_error e)
{
p_stderr(_("ERROR: invalid unsigned integer or not a valid policy index\n"));
}
}
else if(attr == "CONTINUOUS")
{
try
{
bool continuous = string_to<bool>(value);
Simulation::get_instance().set_mode(continuous);
}
catch(domain_error e)
{
p_stderr(_("ERROR: you must provide a valid boolean value ('true' or 'false')\n"));
}
}
else
p_stderr(_("ERROR: invalid attribute name. Accepted are: simulation-tick, cpu-policy, continuous\n"));
}
void
TextSimulation::on_show(const Tokens& arguments)
{
if(arguments.size() < 1)
{
//print error
check_arguments_num(arguments, 1);
return;
}
//make a local copy which we'll probably modify
Tokens args = arguments;
ustring entities = args[0].uppercase();
args.erase(args.begin());
typedef void (TextSimulation::*f_ptr)(const Tokens&);
map<ustring, f_ptr> entities_handlers;
entities_handlers["PROCESSES"] = &TextSimulation::on_show_processes;
entities_handlers["RESOURCES"] = &TextSimulation::on_show_resources;
entities_handlers["THREADS"] = &TextSimulation::on_show_threads;
entities_handlers["REQUESTS"] = &TextSimulation::on_show_requests;
entities_handlers["SUBREQUESTS"] = &TextSimulation::on_show_subrequests;
entities_handlers["CPU-POLICIES"] = &TextSimulation::on_show_cpu_policies;
entities_handlers["RESOURCE-POLICIES"] = &TextSimulation::on_show_resource_policies;
if(entities_handlers.find(entities) == entities_handlers.end())
p_stderr(_("ERROR: invalid argument\n"));
else
(this->*(entities_handlers[entities]))(args);
}
void
TextSimulation::on_show_processes(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
show(processes);
}
void
TextSimulation::on_show_resources(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Resources& resources = env.get_resources();
show(resources);
}
void
TextSimulation::on_show_threads(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 1))
return;
ustring process = arguments[0];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
vector<Thread*> threads;
try
{
int pid = string_to<int>(process) - 1;
threads = processes.at(pid)->get_threads();
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided process identifier is not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: this process identifier does not belong to an existing process\n"));
return;
}
show(threads);
}
void
TextSimulation::on_show_requests(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 2))
return;
ustring process = arguments[0];
ustring thread = arguments[1];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
vector<Request*> requests;
try
{
int pid = string_to<int>(process) - 1;
int tid = string_to<int>(thread) - 1;
vector<Thread*> threads = processes.at(pid)->get_threads();
requests = threads.at(tid)->get_requests();
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
show(requests);
}
void
TextSimulation::on_show_subrequests(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 3))
return;
ustring process = arguments[0];
ustring thread = arguments[1];
ustring request = arguments[2];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
vector<SubRequest*> subrequests;
try
{
int pid = string_to<int>(process) - 1;
int tid = string_to<int>(thread) - 1;
int rid = string_to<int>(request) - 1;
vector<Thread*> threads = processes.at(pid)->get_threads();
vector<Request*> requests = threads.at(tid)->get_requests();
subrequests = requests.at(rid)->get_subrequests();
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
show(subrequests);
}
void
TextSimulation::on_show_cpu_policies(const Tokens& arguments)
{
typedef vector<PolicyManager*> ManagerVec;
typedef vector<Policy*>::iterator PolicyIt;
check_arguments_num(arguments, 0);
PoliciesGatekeeper& gatekeeper = PoliciesGatekeeper::get_instance();
ManagerVec managers = gatekeeper.get_registered();
unsigned int index = 1;
for(ManagerVec::iterator it = managers.begin(); it != managers.end(); ++it)
{
vector<Policy*> policies = (*it)->get_avail_policies();
for(PolicyIt it = policies.begin(); it != policies.end(); ++it)
{
ostringstream oss;
oss << index << ". " << (*it)->get_name() << endl;
oss << "\t" << (*it)->get_description() << endl;
p_stdout(oss.str());
++index;
}
}
}
void
TextSimulation::on_show_resource_policies(const Tokens& arguments)
{
// Waiting for the coder to implementat resource policies
// But wait a moment, the coder is me!!!
p_stderr(_("FIXME: Not implemented\n"));
}
void
TextSimulation::on_add(const Tokens& arguments)
{
if(arguments.size() < 1)
{
//print error
check_arguments_num(arguments, 1);
return;
}
if(Simulation::get_instance().get_state() != Simulation::state_stopped)
{
p_stderr(_("WARNING: Simulation is not stopped, it will be automatically stopped\n"));
Simulation::get_instance().stop();
}
//make a local copy which we'll probably modify
Tokens args = arguments;
ustring entity = args[0].uppercase();
args.erase(args.begin());
typedef void (TextSimulation::*f_ptr)(const Tokens&);
map<ustring, f_ptr> entity_handlers;
entity_handlers["PROCESS"] = &TextSimulation::on_add_process;
entity_handlers["RESOURCE"] = &TextSimulation::on_add_resource;
entity_handlers["THREAD"] = &TextSimulation::on_add_thread;
entity_handlers["REQUEST"] = &TextSimulation::on_add_request;
entity_handlers["SUBREQUEST"] = &TextSimulation::on_add_subrequest;
if(entity_handlers.find(entity) == entity_handlers.end())
p_stderr(_("ERROR: invalid argument\n"));
else
(this->*(entity_handlers[entity]))(args);
}
void
TextSimulation::on_add_process(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
CommandParameter<ustring> name(_("name"), "", "", false, "");
CommandParameter<int> arrival_time(_("arrival time"), 0, INT_MAX, false, 0);
CommandParameter<int> base_priority(_("priority"), 0, INT_MAX, false, 0);
get_parameter(name);
get_parameter(arrival_time);
get_parameter(base_priority);
History& h = Simulation::get_instance().get_history();
h.add_process(name.value, arrival_time.value, base_priority.value);
}
void
TextSimulation::on_add_resource(const Tokens& arguments)
{
check_arguments_num(arguments, 0);
CommandParameter<ustring> name(_("name"), "", "", false, "");
CommandParameter<bool> preemptable(_("pre-emptable?"), false, false, false, false);
CommandParameter<int> places(_("places"), 0, INT_MAX, false, 1);
CommandParameter<int> availability(_("availability"), 0, INT_MAX, false, 0);
get_parameter(name);
get_parameter(preemptable);
get_parameter(places);
get_parameter(availability);
History& h = Simulation::get_instance().get_history();
h.add_resource(name.value, preemptable.value, places.value, availability.value);
}
void
TextSimulation::on_add_thread(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 1))
return;
ustring process = arguments[0];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
Process* p;
try
{
p = processes.at(string_to<int>(process) - 1);
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
CommandParameter<ustring> name(_("name"), "", "", false, "");
CommandParameter<int> cpu_time(_("cpu time"), 1, INT_MAX, true, 0);
CommandParameter<int> arrival_time(_("arrival time"), 0, INT_MAX, false, 0);
CommandParameter<int> base_priority(_("base priority"), 0, INT_MAX, false, 0);
get_parameter(name);
get_parameter(cpu_time);
get_parameter(arrival_time);
get_parameter(base_priority);
History& h = Simulation::get_instance().get_history();
h.add_thread(name.value, *p, cpu_time.value, arrival_time.value,
base_priority.value);
}
void
TextSimulation::on_add_request(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 2))
return;
ustring process = arguments[0];
ustring thread = arguments[1];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
Thread* t;
try
{
int pid = string_to<int>(process) - 1;
int tid = string_to<int>(thread) - 1;
vector<Thread*> threads = processes.at(pid)->get_threads();
t = threads.at(tid);
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
CommandParameter<int> instant(_("instant"), 0, INT_MAX, true, 0);
get_parameter(instant);
History& h = Simulation::get_instance().get_history();
h.add_request(*t, instant.value);
}
void
TextSimulation::on_add_subrequest(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 3))
return;
ustring process = arguments[0];
ustring thread = arguments[1];
ustring request = arguments[2];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
Request* r;
try
{
int pid = string_to<int>(process) - 1;
int tid = string_to<int>(thread) - 1;
int rid = string_to<int>(request) - 1;
vector<Thread*> threads = processes.at(pid)->get_threads();
vector<Request*> requests = threads.at(tid)->get_requests();
r = requests.at(rid);
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
CommandParameter<int> resource_key(_("resource key"), 0, INT_MAX, true, 0);
CommandParameter<int> duration(_("duration"), 0, INT_MAX, true, 0);
const Environment::Resources& resources = env.get_resources();
do
{
get_parameter(resource_key);
if(resources.find(resource_key.value) == resources.end())
p_stderr(_("ERROR: invalid resource identifier\n"));
}
while(resources.find(resource_key.value) == resources.end());
get_parameter(duration);
History& h = Simulation::get_instance().get_history();
h.add_subrequest(*r, resource_key.value, duration.value);
}
void
TextSimulation::on_remove(const Tokens& arguments)
{
if(arguments.size() < 1)
{
//print error
check_arguments_num(arguments, 1);
return;
}
if(Simulation::get_instance().get_state() != Simulation::state_stopped)
p_stderr(_("WARNING: Simulation is not stopped, it will be automatically stopped"));
//make a local copy which we'll probably modify
Tokens args = arguments;
ustring entity = args[0].uppercase();
args.erase(args.begin());
typedef void (TextSimulation::*f_ptr)(const Tokens&);
map<ustring, f_ptr> entity_handlers;
entity_handlers["PROCESS"] = &TextSimulation::on_remove_process;
entity_handlers["RESOURCE"] = &TextSimulation::on_remove_resource;
entity_handlers["THREAD"] = &TextSimulation::on_remove_thread;
entity_handlers["REQUEST"] = &TextSimulation::on_remove_request;
entity_handlers["SUBREQUEST"] = &TextSimulation::on_remove_subrequest;
if(entity_handlers.find(entity) == entity_handlers.end())
p_stderr(_("ERROR: invalid argument\n"));
else
(this->*(entity_handlers[entity]))(args);
}
void
TextSimulation::on_remove_process(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 1))
return;
ustring process = arguments[0];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
Process* p;
try
{
p = processes.at(string_to<int>(process) - 1);
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
History& h = Simulation::get_instance().get_history();
h.remove(*p);
}
void
TextSimulation::on_remove_resource(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 1))
return;
ustring resource = arguments[0];
History& h = Simulation::get_instance().get_history();
History::resource_key_t rid;
try
{
rid = string_to<int>(resource);
const Environment::Resources& resources =
h.get_environment_at(0).get_resources();
if(resources.find(rid) == resources.end())
throw out_of_range(_("invalid resource id"));
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
h.remove(rid);
}
void
TextSimulation::on_remove_thread(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 2))
return;
ustring process = arguments[0];
ustring thread = arguments[1];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
Thread* t;
try
{
int pid = string_to<int>(process) - 1;
int tid = string_to<int>(thread) - 1;
vector<Thread*> threads = processes.at(pid)->get_threads();
t = threads.at(tid);
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
History& h = Simulation::get_instance().get_history();
h.remove(*t);
}
void
TextSimulation::on_remove_request(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 3))
return;
ustring process = arguments[0];
ustring thread = arguments[1];
ustring request = arguments[2];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
Request* r;
try
{
int pid = string_to<int>(process) - 1;
int tid = string_to<int>(thread) - 1;
int rid = string_to<int>(request) - 1;
vector<Thread*> threads = processes.at(pid)->get_threads();
vector<Request*> requests = threads.at(tid)->get_requests();
r = requests.at(rid);
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
History& h = Simulation::get_instance().get_history();
h.remove(*r);
}
void
TextSimulation::on_remove_subrequest(const Tokens& arguments)
{
if(!check_arguments_num(arguments, 4))
return;
ustring process = arguments[0];
ustring thread = arguments[1];
ustring request = arguments[2];
ustring subrequest = arguments[3];
const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
const Environment::Processes& processes = env.get_processes();
SubRequest* r;
try
{
int pid = string_to<int>(process) - 1;
int tid = string_to<int>(thread) - 1;
int rid = string_to<int>(request) - 1;
int srid = string_to<int>(subrequest) - 1;
vector<Thread*> threads = processes.at(pid)->get_threads();
vector<Request*> requests = threads.at(tid)->get_requests();
vector<SubRequest*> subrequests = requests.at(rid)->get_subrequests();
r = subrequests.at(srid);
}
catch(domain_error e)
{
p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
return;
}
catch(out_of_range e)
{
p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
return;
}
History& h = Simulation::get_instance().get_history();
h.remove(*r);
}
void
TextSimulation::p_stdout(const ustring& str)
{
cout << str;
}
void
TextSimulation::p_stderr(const ustring& str)
{
cerr << str;
}
ustring
TextSimulation::readline()
{
string str;
getline(cin, str);
return str;
}
void
TextSimulation::parse_command(TextSimulation& sim, const ustring& str)
{
typedef void (TextSimulation::*f_ptr)(const Tokens&);
map<ustring, f_ptr> command_handlers;
command_handlers["RUN"] = &TextSimulation::on_run;
command_handlers["STOP"] = &TextSimulation::on_stop;
command_handlers["PAUSE"] = &TextSimulation::on_pause;
command_handlers["CONFIGURE-CPU-POLICY"] = &TextSimulation::on_configure_cpu_policy;
command_handlers["HELP"] = &TextSimulation::on_help;
command_handlers["GET"] = &TextSimulation::on_get;
command_handlers["SET"] = &TextSimulation::on_set;
command_handlers["SHOW"] = &TextSimulation::on_show;
command_handlers["ADD"] = &TextSimulation::on_add;
command_handlers["REMOVE"] = &TextSimulation::on_remove;
command_handlers["QUIT"] = &TextSimulation::on_quit;
Tokens arguments = tokenize(str);
if(arguments.size() == 0)
return;
ustring key = arguments[0].uppercase();
if(command_handlers.find(key) == command_handlers.end())
{
p_stderr(_("ERROR: command not supported\n"));
return;
}
arguments.erase(arguments.begin());
(sim.*(command_handlers[key]))(arguments);
}
static ostream&
operator<<(ostream& os, Schedulable::state state)
{
switch(state)
{
case Schedulable::state_running:
os << "RUNNING";
break;
case Schedulable::state_ready:
os << "READY";
break;
case Schedulable::state_blocked:
os << "BLOCKED";
break;
case Schedulable::state_future:
os << "FUTURE";
break;
case Schedulable::state_terminated:
os << "TERMINATED";
break;
default:
os.setstate(ios_base::failbit);
}
return os;
}
static ostream&
operator<<(ostream& os, Request::state state)
{
switch(state)
{
case Request::state_unallocable:
os << "UNALLOCABLE";
break;
case Request::state_allocated:
os << "ALLOCATED";
break;
case Request::state_future:
os << "FUTURE";
break;
case Request::state_exhausted:
os << "EXHAUSTED";
break;
case Request::state_allocable:
os << "ALLOCABLE";
break;
default:
os.setstate(ios_base::failbit);
}
return os;
}
void
TextSimulation::update(const History& changed_history)
{
ostringstream oss;
oss << ">>>> " << changed_history.get_size() - 1 << _("\nREADY QUEUE: { ");
p_stdout(oss.str());
oss.str(string());
const Environment& env = changed_history.get_last_environment();
const ReadyQueue& q = env.get_sorted_queue();
for(unsigned int i = 0; i < q.size(); ++i)
{
const Thread& t = q.get_item_at(i);
p_stdout(t.get_name() + " ~ ");
}
p_stdout("}\n");
p_stdout(_("RESOURCES:\n"));
const Environment::Resources& resources = env.get_resources();
typedef Environment::Resources::const_iterator ResourceIt;
for(ResourceIt it = resources.begin(); it != resources.end(); ++it)
{
const Resource& r = *it->second;
Environment::resource_key_t key = it->first;
oss << " " << key << ") " << r.get_name() << _(", with ");
oss << r.get_places() << _(" places\n");
p_stdout(oss.str());
oss.str(string());
// FIXME this code causes a segfault because an invalid reference is
// returned from get_request_queue()
// const Environment::SubRequestQueue& req_queue =
// env.get_request_queue(it->first);
//
// p_stdout(_("\t\t\tqueue: { "));
//
// for(unsigned int i = 0; i < req_queue.size(); ++i)
// {
// oss << req_queue[i]->get_request().get_thread().get_name() << " ~ ";
// p_stdout(oss.str());
// oss.str(string());
// }
//
// p_stdout("}\n");
}
p_stdout(_("PROCESSES:\n"));
const Environment::Processes& processes = env.get_processes();
for(unsigned int pi = 0; pi < processes.size(); ++pi)
{
Process& p = *processes[pi];
oss << " " << pi + 1 << ") " << p.get_name() << " ";
oss << "[" << p.get_state() << "] ";
oss << _("arriving at ") << p.get_arrival_time() << ", ";
oss << _("requiring ") << p.get_total_cpu_time() << ", ";
oss << _("elapsed ") << p.get_elapsed_time() << ", ";
oss << _("priority ") << p.get_current_priority() << endl;
p_stdout(oss.str());
oss.str(string());
vector<Thread*> threads = p.get_threads();
for(unsigned int ti = 0; ti < threads.size(); ++ti)
{
Thread& t = *threads[ti];
oss << " " << ti + 1 << ") " << t.get_name() << " ";
oss << "[" << t.get_state() << "] ";
oss << _("arriving at ") << t.get_arrival_time() << ", ";
oss << _("requiring ") << t.get_total_cpu_time() << ", ";
oss << _("elapsed ") << t.get_elapsed_time() << ", ";
oss << _("priority ") << t.get_current_priority() << endl;
p_stdout(oss.str());
oss.str(string());
vector<Request*> requests = t.get_requests();
for(unsigned int ri = 0; ri < requests.size(); ++ri)
{
Request& r = *requests[ri];
vector<SubRequest*> subrequests = r.get_subrequests();
for(unsigned int sri = 0; sri < subrequests.size(); ++sri)
{
SubRequest& sr = *subrequests[sri];
oss << _(" --> request ") << ri + 1 << "." << sri + 1 << ") ";
oss << _("at ") << r.get_instant() << " ";
ResourceIt point = resources.find(sr.get_resource_key());
oss << _("for resource ") << (point->second)->get_name() << " ";
oss << _("with id ") << sr.get_resource_key() << " ";
oss << "[" << sr.get_state() << "] ";
oss << _("requiring ") << sr.get_length() << ", ";
oss << _("remaining ") << sr.get_remaining_time() << endl;
p_stdout(oss.str());
oss.str(string());
}
}
}
}
p_stdout("\n");
}
void
TextSimulation::_io_loop(pair<TextSimulation* , int > pun)
{
while(true)
{
//reads the command
ustring str;
//the sgpem cursor appears only in the console
//if (!pun.first->_devices[pun.second]->is_full_duplex())
pun.first->_devices[pun.second]->write_buffer("\nSGPEM=> ");
str = pun.first->_devices[pun.second]->read_command();
pair< pair<TextSimulation*, IOManager*>, const ustring > p
(pair<TextSimulation*, IOManager*>(pun.first, &(*pun.first->_devices[pun.second])), str);
//if (pun.first->_devices[pun.second]->is_full_duplex())
//if the IOManager can read AND write at the same time then create a new thread
//thath will write to it while going on reading the next command
//Thread::create( sigc::bind(&TextSimulation::parse_command, p), true);
//else
//no read is possible: only write
// FIXME what was the purpose of this (_io_loop()) method
// need to comment out to make the code compile
//pun.first->parse_command(p);
}
}