From e27ba77fedabb93f1bb48c405ee64c808d6105e8 Mon Sep 17 00:00:00 2001 From: tchernobog Date: Thu, 17 Aug 2006 23:30:49 +0000 Subject: [PATCH] - Merge branch https://lowca.thgnet.it/swe/branches/0.3-r847--simplify-scheduler revisions 846:897 into trunk, with approval and peer review of manager (Luca). git-svn-id: svn://svn.gna.org/svn/sgpemv2/trunk@898 3ecf2c5c-341e-0410-92b4-d18e462d057c --- Makefile.am | 16 + plugins/pyloader/po/POTFILES.in | 4 + plugins/xmlsave/po/POTFILES.in | 5 + po/POTFILES.in | 118 +- po/sgpemv2.pot | 710 ++++++---- src/backend/concrete_environment.cc | 2 +- src/backend/dynamic_process.cc | 173 +-- src/backend/scheduler.cc | 1156 ++++++++--------- src/backend/scheduler.hh | 35 +- src/templates/sequences.tcc | 18 +- .../scheduling-wizards/wizard-block-fail | 6 + .../scheduling-wizards/wizard-deadlock-test | 59 + .../wizard-priority-inversion-porno | 88 ++ .../scheduling-wizards/wizard-unblock-test | 43 + src/text_simulation.cc | 10 +- 15 files changed, 1399 insertions(+), 1044 deletions(-) create mode 100644 src/testsuite/scheduling-wizards/wizard-deadlock-test create mode 100644 src/testsuite/scheduling-wizards/wizard-priority-inversion-porno create mode 100644 src/testsuite/scheduling-wizards/wizard-unblock-test diff --git a/Makefile.am b/Makefile.am index 512133e..bb381ff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,6 +49,7 @@ bin_PROGRAMS = pkglib_LTLIBRARIES = plugin_LTLIBRARIES = noinst_HEADERS = +noinst_DATA = pkginclude_HEADERS = EXTRA_DIST = MAINTAINERCLEANFILES = @@ -408,3 +409,18 @@ CLEANFILES += \ endif #~ if COND_TESTS +# ############################################################ +# +# extra files to distribute +# +# ############################################################ + +noinst_DATA += \ + src/testsuite/scheduling-wizards \ + src/testsuite/scheduling-wizards/wizard-deadlock-test \ + src/testsuite/scheduling-wizards/wizard-gap-fail \ + src/testsuite/scheduling-wizards/wizard-assert-fail \ + src/testsuite/scheduling-wizards/wizard-unblock-test \ + src/testsuite/scheduling-wizards/wizard-complex-test \ + src/testsuite/scheduling-wizards/wizard-priority-inversion-porno \ + src/testsuite/scheduling-wizards/wizard-block-fail diff --git a/plugins/pyloader/po/POTFILES.in b/plugins/pyloader/po/POTFILES.in index e69de29..6aae4a6 100644 --- a/plugins/pyloader/po/POTFILES.in +++ b/plugins/pyloader/po/POTFILES.in @@ -0,0 +1,4 @@ +src/plugin.cc +src/python_cpu_policy.cc +src/python_cpu_policy_manager.cc +src/testsuite/test-python_loader.cc diff --git a/plugins/xmlsave/po/POTFILES.in b/plugins/xmlsave/po/POTFILES.in index e69de29..5fb4fe7 100644 --- a/plugins/xmlsave/po/POTFILES.in +++ b/plugins/xmlsave/po/POTFILES.in @@ -0,0 +1,5 @@ +src/plugin.cc +src/testsuite/test-xml_serializer.cc +src/xml_serializer.cc +src/xml_serializer_factory.cc +src/xml_visitor.cc diff --git a/po/POTFILES.in b/po/POTFILES.in index b30dc12..841e2bf 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,46 +1,74 @@ # List of source files which contain translatable strings. -./src/parse_opts.cc -./src/parse_opts.hh -./src/graphical_simulation.hh -./src/observer.cc -./src/observer.hh -./src/start_gui.cc -./src/start_gui.hh -./src/main_window.cc -./src/main_window.hh -./src/main.cc -./src/main.hh -./src/io_manager.hh -./src/graphical_terminal_io.cc -./src/graphical_terminal_io.hh -./src/simulation.cc -./src/simulation.hh -./src/backend/observed_subject.cc -./src/backend/observed_subject.hh -./src/backend/schedulable.cc -./src/backend/schedulable.hh -./src/backend/policy_parameters.cc -./src/backend/policy_parameters.hh -./src/backend/policy_manager.cc -./src/backend/policy_manager.hh -./src/backend/string_utils.cc -./src/backend/string_utils.hh -./src/backend/policy.cc -./src/backend/policy.hh -./src/backend/history.cc -./src/backend/history.hh -./src/backend/scheduler.cc -./src/backend/scheduler.hh -./src/backend/slice.cc -./src/backend/slice.hh -./src/backend/schedulable_status.cc -./src/backend/schedulable_status.hh -./src/backend/process.cc -./src/backend/process.hh -./src/backend/schedulable_queue.cc -./src/backend/schedulable_queue.hh -./src/standard_io.cc -./src/standard_io.hh -./src/text_simulation.cc -./src/text_simulation.hh -./src/templates/smartp.hh +src/backend/concrete_environment.cc +src/backend/concrete_history.cc +src/backend/concrete_simulation.cc +src/backend/cpu_policies_gatekeeper.cc +src/backend/cpu_policy.cc +src/backend/cpu_policy_exception.cc +src/backend/cpu_policy_manager.cc +src/backend/dynamic_process.cc +src/backend/dynamic_request.cc +src/backend/dynamic_resource.cc +src/backend/dynamic_schedulable.cc +src/backend/dynamic_sub_request.cc +src/backend/dynamic_thread.cc +src/backend/environment.cc +src/backend/global_preferences.cc +src/backend/global_preferences_serializer.cc +src/backend/history.cc +src/backend/history_observer.cc +src/backend/holt_graph.cc +src/backend/invalid_plugin_exception.cc +src/backend/key_file.cc +src/backend/malformed_policy_exception.cc +src/backend/module.cc +src/backend/null_policy_exception.cc +src/backend/plugin_manager.cc +src/backend/policy_parameters.cc +src/backend/process.cc +src/backend/ready_queue.cc +src/backend/request.cc +src/backend/resource.cc +src/backend/resource_policies_gatekeeper.cc +src/backend/resource_policy.cc +src/backend/resource_policy_manager.cc +src/backend/schedulable.cc +src/backend/scheduler.cc +src/backend/serializer.cc +src/backend/serializer_error.cc +src/backend/serializers_gatekeeper.cc +src/backend/serialize_visitor.cc +src/backend/simulation.cc +src/backend/static_process.cc +src/backend/static_request.cc +src/backend/static_resource.cc +src/backend/static_schedulable.cc +src/backend/static_sub_request.cc +src/backend/static_thread.cc +src/backend/string_utils.cc +src/backend/sub_request.cc +src/backend/thread.cc +src/backend/user_interrupt_exception.cc +src/cairo_elements.cc +src/cairo_widget.cc +src/graphical_preferences_editor.cc +src/gui_builder.cc +src/main.cc +src/parse_opts.cc +src/schedulables_tree_widget.cc +src/simulation_widget.cc +src/templates/deletor.tcc +src/templates/parameter.tcc +src/templates/prova.cc +src/templates/sequences.tcc +src/templates/singleton.tcc +src/templates/smartp.tcc +src/testsuite/stubs/history.cc +src/testsuite/stubs/policy_manager.cc +src/testsuite/stubs/prrpolicy.cc +src/testsuite/test-global_preferences_serialization.cc +src/testsuite/test-history.cc +src/testsuite/test-key_file.cc +src/testsuite/test-parse_command.cc +src/testsuite/test-stepforward.cc +src/text_simulation.cc diff --git a/po/sgpemv2.pot b/po/sgpemv2.pot index 5565a7a..2ac5d68 100644 --- a/po/sgpemv2.pot +++ b/po/sgpemv2.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: matteo@member.fsf.org\n" -"POT-Creation-Date: 2006-06-13 15:23+0200\n" +"POT-Creation-Date: 2006-08-18 01:35+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,266 +16,542 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: src/parse_opts.cc:89 -#, c-format +#: src/backend/concrete_simulation.cc:180 +msgid "unable to change policy and to restore the previous: " +msgstr "" + +#: src/backend/concrete_simulation.cc:186 +msgid "unable to change policy: " +msgstr "" + +#: src/backend/string_utils.cc:88 src/backend/string_utils.cc:120 +msgid "too few or too many tokens" +msgstr "" + +#: src/backend/string_utils.cc:110 +msgid "incorrect number format" +msgstr "" + +#: src/backend/string_utils.cc:142 +msgid "incorrect boolean" +msgstr "" + +#: src/graphical_preferences_editor.cc:144 +#: src/graphical_preferences_editor.cc:173 +msgid "Select a directory to add" +msgstr "" + +#: src/gui_builder.cc:137 +msgid "Filename to open: " +msgstr "" + +#: src/parse_opts.cc:68 +msgid "starts the program in command line mode" +msgstr "" + +#: src/parse_opts.cc:69 +msgid "adds this directory to the default modules search path" +msgstr "" + +#: src/parse_opts.cc:70 +msgid "adds this directory to default plugin search path" +msgstr "" + +#: src/parse_opts.cc:71 +msgid "a list of savefiles; only the first will be opened" +msgstr "" + +#: src/parse_opts.cc:91 msgid "" -"[EE] Wrong number of parameters. Please see \n" -"%s --help\n" +"SGPEMv2, a graphical simulator for process scheduling in a multitasking " +"computer" msgstr "" -#: src/parse_opts.cc:108 -#, c-format +#: src/parse_opts.cc:136 msgid "" -"SGPEMv2 is an educational software acting as a process scheduling simulator\n" -"\n" -"\n" -"Usage : sgpemv2 [options] filename\n" -"\n" -"Options:\n" -"\t-h, --help this help you're reading\n" -"\t-N, --no-gui starts the program in command line mode\n" -"\t-P dir, --policies-dir=dir\n" -"\t add this directory to the default modules\n" -"\t search path\n" -"\t-M dir, --modules-dir=dir\n" -"\t add this directory to default plugin\n" -"\t search path\n" -"\n" -"Filename:\n" -"\t a valid SGPEMv2 XML file\n" -"\t to be opened.\n" -"\n" -"Long options are available only on GNU systems.\n" -"\n" +" [II] To see a list of commands available,\n" +" [II] please type \"help\" and hit the ENTER key." msgstr "" -#: src/main_window.cc:42 -msgid "Exit" +#: src/parse_opts.cc:160 +msgid "Bad invocation: " msgstr "" -#: src/graphical_terminal_io.cc:46 -msgid "Textual Simulation Log" +#: src/parse_opts.cc:161 +msgid "Use the `-?' or `--help' option to see the help" msgstr "" -#: src/graphical_terminal_io.cc:69 -msgid "Send Command" +#: src/text_simulation.cc:108 +msgid "ERROR: this command requires at least " msgstr "" -#: src/backend/scheduler.cc:97 +#: src/text_simulation.cc:108 +msgid " arguments\n" +msgstr "" + +#: src/text_simulation.cc:114 +msgid "WARNING: some arguments will be ignored\n" +msgstr "" + +#: src/text_simulation.cc:124 msgid "" -"\n" -"No initial state inserted!!\n" +"WARNING: Simulation was not recently saved. If you continue some changes to " +"the simulation might be lost.\n" msgstr "" -#: src/text_simulation.cc:103 -msgid "" -"\n" -"-- RUN COMMAND --\n" -"Starts the simulation. It can be continuous or step-by-step depending on the " -"mode configured with SetMode (default=continuous).\n" -"\n" -"The output of RUN is one or more rows each of which represents the state of " -"the schedulable entities. It can be RUNNING, READY, BLOCKED, FUTURE or " -"TERMINATED.\n" -"The row begins with the number of the instant described by the following " -"lists of states. The instant 0 represents the INITIAL STATE during which no " -"process is running. The scheduler activity begins at instant 1. Each " -"schedulable entity is represented by its name followed by its priority " -"enclosed between round parenthesis." +#: src/text_simulation.cc:129 +msgid "Continue? [y/n] " msgstr "" -#: src/text_simulation.cc:120 -msgid "" -"\n" -"ERROR: " +#: src/text_simulation.cc:137 +msgid "n" msgstr "" -#: src/text_simulation.cc:122 -msgid "" -"\n" -"Simulation is now stopped" +#: src/text_simulation.cc:139 +msgid "y" msgstr "" -#: src/text_simulation.cc:131 -msgid "" -"\n" -"-- PAUSE COMMAND --\n" -"Pauses the simulation. The next call to RUN will restart it." -msgstr "" - -#: src/text_simulation.cc:141 -msgid "" -"\n" -"-- STOP COMMAND --\n" -"Stops the simulation. The next call to RUN will bring the simulation to the " -"FIRST instant and start it." -msgstr "" - -#: src/text_simulation.cc:152 -msgid "" -"\n" -"-- RESET COMMAND --\n" -"Resets the simulation jumping back to the first instant." -msgstr "" - -#: src/text_simulation.cc:162 -msgid "" -"\n" -"-- QUIT COMMAND --\n" -"Exits the program." -msgstr "" - -#: src/text_simulation.cc:165 -msgid "" -"\n" -"\n" -"*** Thank you for using SGPEM by Sirius Cybernetics Corporation ***\n" -"\n" -msgstr "" - -#: src/text_simulation.cc:173 -msgid "" -"\n" -"-- Do you really want me to explain what HELP means? --\n" -" ************** YOU ARE JOKING ME !!! ************\n" -"\n" -msgstr "" - -#: src/text_simulation.cc:195 -msgid "" -"\n" -"-- SetMode COMMAND --\n" -"Permits to change the mode of the simulation.\n" -"\n" -"Sintax: Setmode \n" -"\t can take values:\n" -"\n" -"\t\tCONTINUOUS - when calling RUN the simulation will show an animation " -"using the wait-interval set by SETTIMER\n" -"\n" -"\t\tSTEP - when calling RUN the simulation will show only one step of the " -"animation\n" -msgstr "" - -#: src/text_simulation.cc:202 -msgid "" -"\n" -"ERROR: wrong number of parameters.\n" -"Type HELP SETMODE for the description of the sintax" -msgstr "" - -#: src/text_simulation.cc:211 -msgid "" -"\n" -"ERROR: the second parameter can be only CONTINUOUS or STEP" -msgstr "" - -#: src/text_simulation.cc:219 -msgid "" -"\n" -"-- GetMode COMMAND --\n" -"Returns\n" -"\tCONTINUOUS : if the simulation is shown with an animation\n" -"\tSTEP : if if the simulation is shown step-by-step" -msgstr "" - -#: src/text_simulation.cc:224 -msgid "" -"\n" -"CONTINUOUS" -msgstr "" - -#: src/text_simulation.cc:226 -msgid "" -"\n" -"STEP" -msgstr "" - -#: src/text_simulation.cc:233 -msgid "" -"\n" -"-- SetTimer COMMAND --\n" -"Permits to change the interval between a step and the following one during a " -"continuous animation.\n" -"\n" -"Sintax: SetTimer \n" -"\t must be an integer value > 0 and < 10000.\n" -msgstr "" - -#: src/text_simulation.cc:239 -msgid "" -"\n" -"ERROR: wrong number of parameters.\n" -"Type HELP SETTIMER for the description of the sintax" +#: src/text_simulation.cc:242 +msgid "ERROR: Provided value is out of range\n" msgstr "" #: src/text_simulation.cc:248 -msgid "" -"\n" -"ERROR: the second parameter has a wrong value.\n" -"Type HELP SETTIMER for the description of the sintax" +msgid "ERROR: Please provide a valid numeric value\n" msgstr "" -#: src/text_simulation.cc:256 -msgid "" -"\n" -"-- GetTimer COMMAND --\n" -"Returns the number of milliseconds the simulation in the continuous mode " -"waits between a step and the following one" +#: src/text_simulation.cc:257 +msgid "ERROR: This is a mandatory attribute; you MUST provide a valid value!\n" msgstr "" -#: src/text_simulation.cc:269 -msgid "" -"\n" -"-- JumpTo COMMAND --\n" -"Permits to jump to a desired instant of the simulation. All states of the " -"simulation before will be recalculated and printed out.\n" -"\n" -"Sintax: JumpTo \n" -"\t must be an integer value >= 0" +#: src/text_simulation.cc:293 src/text_simulation.cc:345 +msgid "ERROR: This is a mandatory atribute; you MUST provide a valid value!\n" msgstr "" -#: src/text_simulation.cc:276 -msgid "" -"\n" -"ERROR: wrong number of parameters.\n" -"Type HELP JUMPTO for the description of the sintax" +#: src/text_simulation.cc:339 +msgid "ERROR: Please provide a valid boolean value ('true' or 'false')\n" msgstr "" -#: src/text_simulation.cc:285 -msgid "" -"\n" -"ERROR: the second parameter has a wrong value.\n" -"Type HELP JUMPTO for the description of the sintax" +#: src/text_simulation.cc:367 src/text_simulation.cc:373 +#: src/text_simulation.cc:391 src/text_simulation.cc:695 +#: src/text_simulation.cc:1386 src/text_simulation.cc:1423 +msgid "ERROR: " msgstr "" -#: src/text_simulation.cc:293 +#: src/text_simulation.cc:369 src/text_simulation.cc:393 msgid "" "\n" -"-- GetPolicy COMMAND --\n" -"Returns the name and the description of the current applied policy." +"Simulation is now stopped\n" msgstr "" -#: src/text_simulation.cc:303 +#: src/text_simulation.cc:375 msgid "" "\n" -"-- GetPolicyAttributes COMMAND --\n" -"Returns the list of attributes of the current applied policy.\n" -"The description of each parameter includes:\n" -"\tthe NAME of the marameter with its type\n" -"\tits current VALUE\n" -"\tits LOWER and UPPER bounds\n" -"\twhether the parameter is REQUIRED" +"Simulation is now stopped, and the current policy will be deactivated\n" msgstr "" -#: src/text_simulation.cc:365 -msgid "" -"\n" -"Command not recognized: " +#: src/text_simulation.cc:384 +msgid "FATAL ERROR: unable to deactivate the policy: " msgstr "" -#: src/text_simulation.cc:367 +#: src/text_simulation.cc:424 +msgid "ERROR: No policy actually selected for the simulation\n" +msgstr "" + +#: src/text_simulation.cc:430 +msgid "Please provide a value for each attribute:\n" +msgstr "" + +#: src/text_simulation.cc:431 +msgid "" +"Mandatory arguments are marked with an asterisk (*)\n" +"\n" +msgstr "" + +#: src/text_simulation.cc:433 +msgid "Integer arguments:\n" +msgstr "" + +#: src/text_simulation.cc:453 msgid "" "\n" -"Typer HELP for a list of avaiable commands." +"Floating-point arguments:\n" +msgstr "" + +#: src/text_simulation.cc:475 +msgid "" +"\n" +"String arguments:\n" +msgstr "" + +#: src/text_simulation.cc:507 +msgid "" +"Avaiable commands:\n" +"RUN\n" +"STOP\n" +"PAUSE\n" +"CONFIGURE-CPU-POLICY\n" +"HELP\n" +"GET\n" +"SET\n" +"SHOW\n" +"ADD\n" +"REMOVE\n" +"SAVE\n" +"LOAD\n" +"QUIT\n" +"\n" +"HELP followed by a command name shows help about it.\n" +"ex. `HELP RUN` shows help about the command RUN\n" +msgstr "" + +#: src/text_simulation.cc:513 +msgid "" +"-- RUN COMMAND --\n" +"Starts 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" +msgstr "" + +#: src/text_simulation.cc:522 +msgid "" +"-- STOP COMMAND --\n" +"Stops the simulation. The next call to RUN will bring the simulation to the " +"first instant and start it.\n" +msgstr "" + +#: src/text_simulation.cc:525 +msgid "" +"-- PAUSE COMMAND --\n" +"Pauses the simulation. The next call to RUN will continue it.\n" +msgstr "" + +#: src/text_simulation.cc:528 +msgid "" +"-- CONFIGURE-CPU-POLICY COMMAND --\n" +"Configure parameters exposed by the cpu policy.\n" +"\n" +"This is currently the only way to control the behaviour of cpu policies " +"without modifying their source code.\n" +msgstr "" + +#: src/text_simulation.cc:532 +msgid "" +"-- HELP COMMAND --\n" +"The help you're reading.\n" +msgstr "" + +#: src/text_simulation.cc:534 +msgid "" +"-- GET COMMAND --\n" +"Syntax: GET \n" +"\twhere may be simulation-tick or continuous.\n" +msgstr "" + +#: src/text_simulation.cc:537 +msgid "" +"-- SET COMMAND --\n" +"Syntax: SET [=] \n" +"\twhere may be simulation-tick, continuous or cpu-policy.\n" +msgstr "" + +#: src/text_simulation.cc:540 +msgid "" +"-- SHOW COMMAND --\n" +"Displays 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 ` with being the numeric identifier " +"of the parent process\n" +"`SHOW requests ` with being the numeric " +"identifier of the thread child of process identified by \n" +"`SHOW subrequests ` where the numeric " +"ids follow the same logic of the previous commands\n" +msgstr "" + +#: src/text_simulation.cc:551 +msgid "" +"-- ADD COMMAND --\n" +"Adds an entity by using a questionary-like approach.\n" +"\n" +"Syntax depends from entity being added:\n" +"`ADD process|resource`\n" +"`ADD thread ` with being the numeric identifier of " +"the parent process\n" +"`ADD request ` with being the numeric " +"identifier of the thread child of process identified by \n" +"`ADD subrequest ` where the numeric ids " +"follow the same logic of the previous commands\n" +msgstr "" + +#: src/text_simulation.cc:561 +msgid "" +"-- REMOVE COMMAND --\n" +"Removes an entity.\n" +"\n" +"Syntax depends from entity being removed:\n" +"`REMOVE process|resource ` where is the process or resource " +"identifier\n" +"`REMOVE thread ` with being the " +"identifier of the parent process, and the id of the thread to be " +"removed\n" +"`REMOVE request ` where the numeric ids " +"follow the same logic of the previous commands\n" +"`REMOVE subrequest ` " +"where the numeric ids follow the same logic of the previous commands\n" +msgstr "" + +#: src/text_simulation.cc:571 +msgid "" +"-- SAVE COMMAND --\n" +"Saves the simulation.\n" +"\n" +"Syntax: SAVE \n" +msgstr "" + +#: src/text_simulation.cc:574 +msgid "" +"-- LOAD COMMAND --\n" +"Loads the simulation.\n" +"\n" +"Syntax: LOAD \n" +msgstr "" + +#: src/text_simulation.cc:577 +msgid "" +"-- QUIT COMMAND --\n" +"Gently closes the program.\n" +msgstr "" + +#: src/text_simulation.cc:579 +msgid "ERROR: Sorry, no help available for this command.\n" +msgstr "" + +#: src/text_simulation.cc:590 +msgid "" +"\n" +"Bye.\n" +"\n" +msgstr "" + +#: src/text_simulation.cc:617 +msgid "" +"ERROR: invalid attribute name. Accepted are: simulation-tick, continuous\n" +msgstr "" + +#: src/text_simulation.cc:652 +msgid "ERROR: you must provide a valid unsigned integer value\n" +msgstr "" + +#: src/text_simulation.cc:691 +msgid "ERROR: invalid unsigned integer or not a valid policy index\n" +msgstr "" + +#: src/text_simulation.cc:711 +msgid "ERROR: you must provide a valid boolean value ('true' or 'false')\n" +msgstr "" + +#: src/text_simulation.cc:715 +msgid "" +"ERROR: invalid attribute name. Accepted are: simulation-tick, cpu-policy, " +"continuous\n" +msgstr "" + +#: src/text_simulation.cc:746 src/text_simulation.cc:947 +#: src/text_simulation.cc:1163 +msgid "ERROR: invalid argument\n" +msgstr "" + +#: src/text_simulation.cc:793 +msgid "ERROR: provided process identifier is not a valid integer\n" +msgstr "" + +#: src/text_simulation.cc:798 +msgid "ERROR: this process identifier does not belong to an existing process\n" +msgstr "" + +#: src/text_simulation.cc:829 src/text_simulation.cc:867 +#: src/text_simulation.cc:1009 src/text_simulation.cc:1058 +#: src/text_simulation.cc:1104 src/text_simulation.cc:1187 +#: src/text_simulation.cc:1224 src/text_simulation.cc:1260 +#: src/text_simulation.cc:1300 src/text_simulation.cc:1343 +msgid "ERROR: provided identifier(s) not a valid integer\n" +msgstr "" + +#: src/text_simulation.cc:834 src/text_simulation.cc:872 +#: src/text_simulation.cc:1014 src/text_simulation.cc:1063 +#: src/text_simulation.cc:1109 src/text_simulation.cc:1192 +#: src/text_simulation.cc:1229 src/text_simulation.cc:1265 +#: src/text_simulation.cc:1305 src/text_simulation.cc:1348 +msgid "ERROR: the identifier(s) do not belong to an existing entity\n" +msgstr "" + +#: src/text_simulation.cc:912 +msgid "FIXME: Not implemented\n" +msgstr "" + +#: src/text_simulation.cc:927 +msgid "WARNING: Simulation is not stopped, it will be automatically stopped\n" +msgstr "" + +#: src/text_simulation.cc:957 src/text_simulation.cc:975 +#: src/text_simulation.cc:1018 +msgid "name" +msgstr "" + +#: src/text_simulation.cc:958 src/text_simulation.cc:1020 +msgid "arrival time" +msgstr "" + +#: src/text_simulation.cc:959 src/text_simulation.cc:1650 +msgid "priority" +msgstr "" + +#: src/text_simulation.cc:976 +msgid "pre-emptable?" +msgstr "" + +#: src/text_simulation.cc:977 +msgid "places" +msgstr "" + +#: src/text_simulation.cc:978 +msgid "availability" +msgstr "" + +#: src/text_simulation.cc:1019 +msgid "cpu time" +msgstr "" + +#: src/text_simulation.cc:1021 +msgid "base priority" +msgstr "" + +#: src/text_simulation.cc:1067 +msgid "instant" +msgstr "" + +#: src/text_simulation.cc:1113 +msgid "resource key" +msgstr "" + +#: src/text_simulation.cc:1114 +msgid "duration" +msgstr "" + +#: src/text_simulation.cc:1123 +msgid "ERROR: invalid resource identifier\n" +msgstr "" + +#: src/text_simulation.cc:1145 +msgid "WARNING: Simulation is not stopped, it will be automatically stopped" +msgstr "" + +#: src/text_simulation.cc:1220 +msgid "invalid resource id" +msgstr "" + +#: src/text_simulation.cc:1382 src/text_simulation.cc:1419 +msgid "ERROR: No registered serializer available\n" +msgstr "" + +#: src/text_simulation.cc:1489 +msgid "ERROR: command not supported\n" +msgstr "" + +#: src/text_simulation.cc:1507 +msgid "RUNNING @" +msgstr "" + +#: src/text_simulation.cc:1510 +msgid "READY" +msgstr "" + +#: src/text_simulation.cc:1513 +msgid "BLOCKED" +msgstr "" + +#: src/text_simulation.cc:1516 src/text_simulation.cc:1540 +msgid "FUTURE" +msgstr "" + +#: src/text_simulation.cc:1519 +msgid "TERMINATED" +msgstr "" + +#: src/text_simulation.cc:1534 +msgid "UNALLOCABLE" +msgstr "" + +#: src/text_simulation.cc:1537 +msgid "ALLOCATED" +msgstr "" + +#: src/text_simulation.cc:1543 +msgid "EXHAUSTED" +msgstr "" + +#: src/text_simulation.cc:1546 +msgid "ALLOCABLE" +msgstr "" + +#: src/text_simulation.cc:1573 +msgid "READY QUEUE: { " +msgstr "" + +#: src/text_simulation.cc:1581 +msgid " ~ " +msgstr "" + +#: src/text_simulation.cc:1584 +msgid "}" +msgstr "" + +#: src/text_simulation.cc:1598 +msgid "RESOURCES:" +msgstr "" + +#: src/text_simulation.cc:1604 +msgid ", with " +msgstr "" + +#: src/text_simulation.cc:1605 +msgid " places" +msgstr "" + +#: src/text_simulation.cc:1610 +msgid "queue: { " +msgstr "" + +#: src/text_simulation.cc:1626 +msgid " }" +msgstr "" + +#: src/text_simulation.cc:1644 +msgid "PROCESSES:" +msgstr "" + +#: src/text_simulation.cc:1646 +msgid "state" +msgstr "" + +#: src/text_simulation.cc:1647 +msgid "arrival" +msgstr "" + +#: src/text_simulation.cc:1648 +msgid "requiring" +msgstr "" + +#: src/text_simulation.cc:1649 +msgid "elapsed" +msgstr "" + +#: src/text_simulation.cc:1651 +msgid "resource_id" msgstr "" diff --git a/src/backend/concrete_environment.cc b/src/backend/concrete_environment.cc index 4f9488e..b656f9c 100644 --- a/src/backend/concrete_environment.cc +++ b/src/backend/concrete_environment.cc @@ -60,7 +60,7 @@ ConcreteEnvironment::ConcreteEnvironment(const ConcreteEnvironment& ce) : { const Processes& ce_proc = ce._processes; insert_iterator dest(_processes, _processes.begin()); - for (Iseq orig = iseq(ce_proc); orig; orig++) + for (Iseq orig = const_iseq(ce_proc); orig; orig++) *dest++ = new DynamicProcess(dynamic_cast(**orig)); } diff --git a/src/backend/dynamic_process.cc b/src/backend/dynamic_process.cc index 3d17872..3990b1e 100644 --- a/src/backend/dynamic_process.cc +++ b/src/backend/dynamic_process.cc @@ -24,14 +24,20 @@ #include "serialize_visitor.hh" #include "deletor.tcc" +#include "sequences.tcc" #include #include +#include #include using namespace sgpem; using namespace std; +typedef std::vector::const_iterator ConstThreadIt; +typedef std::vector::iterator ThreadIt; + + DynamicProcess::DynamicProcess(StaticProcess* core) : DynamicSchedulable(), _core(core) { @@ -41,13 +47,9 @@ DynamicProcess::DynamicProcess(StaticProcess* core) : DynamicProcess::DynamicProcess(const DynamicProcess &other) : Schedulable(), DynamicSchedulable(other), Process(), _core(other._core) -{ - typedef vector::const_iterator ThreadIt; - - const vector& other_threads = other._dynamic_threads; - - for (ThreadIt it = other_threads.begin(); it != other_threads.end(); ++it) - new DynamicThread(*(*it), this); +{ + for (Iseq seq = const_iseq(other._dynamic_threads); seq; ++seq) + new DynamicThread(*(*seq), this); } DynamicProcess::~DynamicProcess() @@ -71,101 +73,36 @@ DynamicProcess::get_threads() const Schedulable::state DynamicProcess::get_state() const { - int total = _dynamic_threads.size(); - int running = 0; - int ready = 0; - int blocked = 0; - int terminated = 0; - int future = 0; - - unsigned int closest = 0; - - vector::const_iterator it = _dynamic_threads.begin(); - for (; it != _dynamic_threads.end(); it++) - { - if ((**it).get_state() == state_running) running++; - if ((**it).get_state() == state_ready) ready++; - if ((**it).get_state() == state_blocked) blocked++; - if ((**it).get_state() == state_terminated) terminated++; - if ((**it).get_state() == state_future) - { - unsigned int arrival = (**it).get_arrival_time(); - // if this is the first future occurrence, record its arrival; - // else record its arrival if and only if it is smaller then the recorded one - if (future == 0) - closest = arrival; - else - closest = (closest < arrival) ? closest : arrival; - future++; - } - } - - assert(total > 0); - assert(running == 1 || running == 0); - assert(running + ready + blocked + terminated + future == total); - - if (running > 0) - return state_running; - if (ready > 0) // running == 0 - return state_ready; - if (blocked > 0) // running == 0 && ready == 0 - return state_blocked; - // Now check if a "hole" happens: if all threads are terminated - // or blocked the next - // thread to start, e.g. the one with the least arrival_time, has - // start time greater than the current process elapsed time, then - // pass from state_future to state_terminated: - if (closest > get_elapsed_time()) - return state_terminated; - if (terminated > 0) // running == 0 && ready == 0 && blocked == 0 - return state_terminated; - if (future > 0) // running == 0 && ready == 0 && blocked == 0 && terminated == 0 - return state_future; - - // I'm not sure if we can get here (maybe if there are no threads?), - // but I don't like this compiler warning: 'control reaches end of non-void function' - return state_future; - - // Since premature optimization is the root of all evil, and the - // following code was very fast but also very wrong, the coder - // will be punished by allowing her to code in C++ just after - // having passed "Algoritmi 3" exam with full marks. - - /* - typedef vector::const_iterator ThreadIt; static const int uninitialized = -1; - assert(_dynamic_threads.size() > 0); - - state result = state_future; + state result = state_terminated; int next_thread_starts_at = uninitialized; - for(ThreadIt it = _dynamic_threads.begin(); it != _dynamic_threads.end(); ++it) + // This is the logic behind the code: + // If there is at least one running thread, the result is + // running. If not, it may be either blocked, ready, future or terminated. + + // We have these cases (a state takes precedence over some other one): + // (a) if a thread is running, return immediately state_running + // (b) if a thread is ready, puts unconditionally result as state_ready, + // and continue iterating (to see if there's a running thread) + // (c) if a thread is blocked, and result is not state_ready, result + // becomes state_blocked, and continue iterating (to see if there are + // ready or running threads) + // (d) if a thread is future, and result is not state_ready or + // state_blocked, put result as state_future, and remember + // when the next thread will start (d1) (see at the end of this + // method for the rationale (d2)). Then continue iterating. + // (e) else (if all threads are state_terminated) put result as + // state_terminated. + + + for(Iseq seq = const_iseq(_dynamic_threads); seq; ++seq) { - state thread_state = (*it)->get_state(); - - // This is the logic behind the code: - // If there is at least one running thread, the result is - // running. If not, it may be either blocked, ready, future or terminated. - - // We have these cases (a state takes precedence over some other one): - // (a) if a thread is running, return immediately state_running - // (b) if a thread is ready, puts unconditionally result as state_ready, - // and continue iterating (to see if there's a running thread) - // (c) if a thread is blocked, and result is not state_ready, result - // becomes state_blocked, and continue iterating (to see if there are - // ready or running threads) - // (d) if a thread is future, and result is not state_ready or - // state_blocked, put result as state_future, and remember - // when the next thread will start (d1) (see at the end of this - // method for the rationale (d2)). Then continue iterating. - // (e) else (if all threads are state_terminated) put result as - // state_terminated. + state thread_state = (*seq)->get_state(); // TODO Is this OK? Must be tested... - - int thread_starts_at; switch(thread_state) { case state_running: // (a) @@ -174,23 +111,34 @@ DynamicProcess::get_state() const result = state_ready; continue; case state_blocked: // (c) - result = state_blocked; + if((result & state_ready) == 0) + result = state_blocked; continue; case state_future: // (d) - result = state_future; - thread_starts_at = (*it)->get_arrival_time(); - if(next_thread_starts_at == uninitialized) // (d1) - next_thread_starts_at = thread_starts_at; - else - next_thread_starts_at = std::min(thread_starts_at, next_thread_starts_at); + if((result & (state_ready|state_blocked)) == 0) + { + result = state_future; + int thread_starts_at = (*seq)->get_arrival_time(); + if(next_thread_starts_at == uninitialized) // (d1) + next_thread_starts_at = thread_starts_at; + else + next_thread_starts_at = std::min(thread_starts_at, next_thread_starts_at); + } + continue; + case state_terminated: // (e) + // already put into terminated state as the default value continue; - default: // (e) - result = state_terminated; } } //~ "for" iterating over threads - // reused hole checking system - */ + // (d2) Now check if a "hole" happens: if all other threads are terminated + // the next thread to start, e.g. the one with the least arrival_time, + // has start time greater than the current process elapsed time, then + // pass from state_future to state_terminated: + if (result == state_future && next_thread_starts_at > static_cast(get_elapsed_time())) + return state_terminated; + + return result; } @@ -225,10 +173,9 @@ unsigned int DynamicProcess::get_elapsed_time() const { unsigned int result = 0; - for (std::vector::const_iterator it = _dynamic_threads.begin(); - it != _dynamic_threads.end(); it++) + for (Iseq seq = const_iseq(_dynamic_threads); seq; ++seq) { - result += (*it)->get_elapsed_time(); + result += (*seq)->get_elapsed_time(); } return result; } @@ -237,10 +184,9 @@ int DynamicProcess::get_last_acquisition() const { int result = -1; - for (std::vector::const_iterator it = _dynamic_threads.begin(); - it != _dynamic_threads.end(); it++) + for (Iseq seq = const_iseq(_dynamic_threads); seq; ++seq) { - int acq = (*it)->get_last_acquisition(); + int acq = (*seq)->get_last_acquisition(); if (result < acq) result = acq; } @@ -251,10 +197,9 @@ int DynamicProcess::get_last_release() const { int result = -1; - for (std::vector::const_iterator it = _dynamic_threads.begin(); - it != _dynamic_threads.end(); it++) + for (Iseq seq = const_iseq(_dynamic_threads); seq; ++seq) { - int acq = (*it)->get_last_release(); + int acq = (*seq)->get_last_release(); if (result < acq) result = acq; } diff --git a/src/backend/scheduler.cc b/src/backend/scheduler.cc index 030b9f8..e0f0713 100644 --- a/src/backend/scheduler.cc +++ b/src/backend/scheduler.cc @@ -21,14 +21,17 @@ #include "concrete_environment.hh" #include "concrete_history.hh" #include "cpu_policy.hh" -#include "scheduler.hh" #include "cpu_policy_exception.hh" +#include "scheduler.hh" + // Do not include full template definition in the header file #include "singleton.tcc" +#include "sequences.tcc" #include -#include + +#include #include #include @@ -38,18 +41,327 @@ using namespace sgpem; // Explicit template instantiation to allow to export symbols from the DSO. template class SG_DLLEXPORT Singleton; + typedef std::vector Processes; typedef std::vector Requests; typedef std::vector SubRequests; typedef std::vector Threads; +typedef Environment::Resources Resources; +typedef Environment::SubRequestQueue SubRequestQueue; -Scheduler::Scheduler() - : _ready_queue(NULL), _policy(NULL), _step_mutex() +// ------------------ Static helper functions -------------- + +static void collect_threads(const std::vector& procs, Threads& collected_threads); +static void prepare_ready_queue(ConcreteEnvironment& snapshot, const Threads& all_threads); +static void terminate_all_requests_of(DynamicThread& thread, ConcreteEnvironment& environment); +static void update_allocated_requests(DynamicThread& running_thread, ConcreteEnvironment& environment); +static void raise_new_requests(DynamicThread& running_thread, ConcreteEnvironment& environment); +static void look_for_mutant_request_states(ConcreteEnvironment& environment, unsigned int& alive_threads); +static void determine_subr_allocable_status(const DynamicRequest& req, DynamicSubRequest& subr, + const Resource& res, SubRequestQueue& queue); + +// --------------------------------------------------------- + + + +// Collects all threads of an environment into a single vector +void +collect_threads(const std::vector& procs, + Threads& collected_threads) { + collected_threads.clear(); + for (Iseq::const_iterator> seq = const_iseq(procs); seq; ++seq) + { + const Threads& ts = ((DynamicProcess&) **seq).get_dynamic_threads(); + collected_threads.insert(collected_threads.end(), ts.begin(), ts.end()); + } } +void +prepare_ready_queue(ConcreteEnvironment& snapshot, + const Threads& all_threads) +{ + ReadyQueue& queue = snapshot.get_sorted_queue(); + assert(queue.size() == 0); + for (Iseq seq = const_iseq(all_threads); seq; ++seq) + { + if ((*seq)->get_state() == Schedulable::state_ready) + queue.append(**seq); + } +} + + + +// When a thread terminates, unconditionally kill all its requests +void +terminate_all_requests_of(DynamicThread& thread, + ConcreteEnvironment& environment) +{ + Requests& reqs = thread.get_dynamic_requests(); + for (Iseq r_it = iseq(reqs); r_it; ++r_it) + { + SubRequests& subreqs = (*r_it)->get_dynamic_subrequests(); + for (Iseq s_it = iseq(subreqs); s_it; ++s_it) + { + (*s_it)->set_state(Request::state_exhausted); + Environment::resource_key_t rkey = (*s_it)->get_resource_key(); + SubRequestQueue& queue = environment.get_request_queue(rkey); + SubRequestQueue::iterator removable = find(queue.begin(), queue.end(), *s_it); + if(removable != queue.end()) queue.erase(removable); + } + } +} + + +// For the current thread, see if there are requests that are exhausted +void +update_allocated_requests(DynamicThread& running_thread, + ConcreteEnvironment& environment) +{ + // Go for all dynamic requests of this thread + Requests& reqs = running_thread.get_dynamic_requests(); + for (Iseq req_it = iseq(reqs); req_it; ++req_it) + { + SubRequests& cur_request = (*req_it)->get_dynamic_subrequests(); + for (Iseq subr_it = iseq(cur_request); subr_it; ++subr_it) + { + DynamicSubRequest& cur_subr = **subr_it; + if(cur_subr.get_state() == Request::state_allocated) + { + cur_subr.decrease_remaining_time(); + if(cur_subr.get_remaining_time() == 0) + { + cur_subr.set_state(Request::state_exhausted); + Environment::resource_key_t rkey = cur_subr.get_resource_key(); + SubRequestQueue& queue = environment.get_request_queue(rkey); + SubRequestQueue::iterator removable = find(queue.begin(), queue.end(), &cur_subr); + if(removable != queue.end()) queue.erase(removable); + } + } + } //~ for(over subrequests) + } //~ for(over requests) +} + + +// This function main role is to raise the requests of a thread which is trying to run. +// After finding those future requests that should not be future any more, each of their +// subrequests is added to the queue of a resource. Once put in the queue, their state +// is either ALLOCABLE or UNALLOCABLE. +// Remember that a thread may run only if all of its requests are either FUTURE, +// ALLOCATED or EXHAUSTED. +void +raise_new_requests(DynamicThread& running_thread, ConcreteEnvironment& environment) +{ + // Go for all dynamic requests of this thread + Requests& reqs = running_thread.get_dynamic_requests(); + for (Iseq req_it = iseq(reqs); req_it; ++req_it) + { + DynamicRequest& cur_req = **req_it; + SubRequests& subreqs = (*req_it)->get_dynamic_subrequests(); + + // Add to the queue only requests passing from future to another state: + if(cur_req.get_state() == Request::state_future && + cur_req.get_instant() == running_thread.get_elapsed_time()) + { + for (Iseq subr_it = iseq(subreqs); subr_it; ++subr_it) + { + // Do the proper adding: + DynamicSubRequest& cur_subr = **subr_it; + Environment::resource_key_t rkey = cur_subr.get_resource_key(); + SubRequestQueue& queue = environment.get_request_queue(rkey); + queue.push_back(&cur_subr); + + /// TODO: right here, right now we should call the resource policy to + /// update the queue. Updates the state of the subrequest depending + /// on the position in the queue, as explained before. + + // Get the number of places for the corresponding resource + Resource& resource = *environment.get_resources().find(rkey)->second; + + // See if the subrequest is allocable or unallocable, and set its state. + // It's important that the subrequest has already been added to the queue. + determine_subr_allocable_status(cur_req, cur_subr, resource, queue); + + } //~ for(over subrequests) + } //~ if(request is future and is time to allocate it) + + + // A request may be ALLOCATED only when it is ALLOCABLE, i.e. when all its subrequests + // are ALLOCABLE. A request is allocated when all its subrequests are either TERMINATED + // or ALLOCATED, but at least one is ALLOCATED. + // Now, since the thread is willing to run, we must allocate the request if possible. + + // All requests we treat are at the moment non-preemptable, so it is not permitted to temporarily + // preempt a resource to a thread to free a place and potentially allow one other thread + // to use that place. This is why we need to allocate requests (which means allocating + // resources to threads). + + // If it is actually allocable, allocate it + switch(cur_req.get_state()) + { + case Request::state_allocable: + for(Iseq it_dsrs = const_iseq(subreqs); it_dsrs; ++it_dsrs) + { + DynamicSubRequest& subreq = **it_dsrs; + assert(subreq.get_state() == Request::state_allocable); + + // Move this request up the queue, to the back of the allocated + // subrequests. This is mainly for display. :-) + // The rest of the queue sorting business is up to the resource policy. + Environment::resource_key_t rkey = subreq.get_resource_key(); + SubRequestQueue& queue = environment.get_request_queue(rkey); + assert(queue.size() > 0); + SubRequestQueue::iterator alloc_it = queue.begin(); + for(; (*alloc_it)->get_state() == Request::state_allocated; ++alloc_it) + assert(alloc_it != queue.end()); // We cannot reach the end without having found the current subr! + SubRequestQueue::iterator this_subreq = find(alloc_it, queue.end(), &subreq); + assert(this_subreq != queue.end()); + swap(*alloc_it, *this_subreq); + + subreq.set_state(Request::state_allocated); + } + break; + + case Request::state_unallocable: + // If it does exist at least one unallocable request, the thread may not run! + running_thread.set_state(Schedulable::state_blocked); + break; + + default: + break; + }//~ switch(request state) + + } //~ for(over requests) +} + + +// The following loop determines how many places in the resource are +// really available for a thread, and how many places for a specific +// resource a thread really needs. Admittedly, it's a bit of a hack (in +// the way it's written, not conceptually!) +void +determine_subr_allocable_status(const DynamicRequest& req, DynamicSubRequest& subr, + const Resource& res, SubRequestQueue& queue) +{ + unsigned int total_places = res.get_places(); + unsigned int free_places = total_places; + unsigned int needed_places = 0; + for(Iseq queue_it = const_iseq(queue); + queue_it && free_places >= needed_places; queue_it++) + { + SubRequest& sr = **queue_it; + if(sr.get_state() == Request::state_allocated) + { + assert(free_places > 0); // Just to be sure... + free_places--; + } + // Okay, this won't win a beauty contest... + if(&sr.get_request() == &req) + needed_places++; + } //~ for(over subrequest queue) + + // If the number of places this thread need for a resource are + // less or equal the places left free, it's allocable. + if(needed_places <= free_places) + subr.set_state(Request::state_allocable); + else + { + subr.set_state(Request::state_unallocable); + + // Okay, this is difficult to understand, so read carefully: + // when we make a subrequest unallocable, it means that the + // whole request is unallocable. However, it may happen that + // there are other subrequests just marked allocable for + // a given resource. Since a request is atomic, either we're + // given *all* the places we asked for in a resource, or none. + // This doesn't affect the state for other subrequests on other + // resources, which may stay (atomically) allocable. + // (Maybe it was better to implement the number of "places" + // needed directly into subrequests, after all...) + for(Iseq queue_it = iseq(queue); queue_it; queue_it++) + { + DynamicSubRequest& x = reinterpret_cast(**queue_it); + if(&x.get_request() == &req && x.get_state() == Request::state_allocable) + x.set_state(Request::state_unallocable); + } + } +} + + +// This function checks if there are some allocable or unallocable +// requests that should change their state according to the previous +// step of the simulation. It also put previously blocked threads +// back into ready state if need arises. +void +look_for_mutant_request_states(ConcreteEnvironment& environment, + unsigned int& alive_threads) +{ + // Now listening to: Testament's ``First Strike Is Still Deadly'' + // The name of this function evokes mighty monsters from the abyss. In + // fact, it's what it actually does (okay, okay, pull the other one, + // it's got brass bells on). + + // We start assuming that SubRequestsQueues are up-to-date + Resources& resources = environment.get_resources(); + for(Iseq res_it = iseq(resources); res_it; ++res_it) + { + Environment::resource_key_t rkey = res_it->first; + SubRequestQueue& queue = environment.get_request_queue(rkey); + + unsigned int queue_pos = 0; + for(Iseq subr_it = iseq(queue); subr_it; ++subr_it, ++queue_pos) + { + DynamicSubRequest& subr = (DynamicSubRequest&) **subr_it; + DynamicRequest& req = subr.get_request(); + Request::state prev_req_state = req.get_state(); + + // If a request is already allocated, we don't have to touch it! + if(prev_req_state == Request::state_allocated) + continue; + + // Update the state of the subrequest, either from allocable to + // unallocable or vice-versa + determine_subr_allocable_status(req, subr, *res_it->second, queue); + + // TODO: The following is a moderately expensive operation + // to do here. See if we can move it somewhere else. + + // If a request changes state from allocable to unallocable, + // the corresponding thread should be blocked, and vice-versa + DynamicThread& thread = req.get_thread(); + if(prev_req_state == Request::state_allocable && + req.get_state() == Request::state_unallocable) + { + if(thread.get_state() != Schedulable::state_blocked) + alive_threads--; + thread.set_state(Schedulable::state_blocked); + } + else if(prev_req_state == Request::state_unallocable && + req.get_state() == Request::state_allocable) + { + if(thread.get_state() == Schedulable::state_blocked) + alive_threads++; + thread.set_state(Schedulable::state_ready); + } + + } //~ for(over subrequests in the queue) + } //~ for(over resources) + +} + + +// --------------------------------------------------------- + + + +//private constructor. +Scheduler::Scheduler() + : _ready_queue(NULL), _policy(NULL), _step_mutex() +{} + + ReadyQueue* Scheduler::get_ready_queue() { @@ -60,664 +372,216 @@ Scheduler::get_ready_queue() CPUPolicy* Scheduler::get_policy() { - return _policy; + return _policy; } -//............................................................................. -//............................................................................. -// StepForward Preludium -//............................................................................. -//............................................................................. - - - // Introduces newly arrived processes. - // Postcondition: - // for each process p in next_snapshot - // ( - // for each thread t belonging to p - // ( - // if the arrival time of p is equal to front, then - // ( - // the state of p is state_ready and - // if the remaining time of t is zero, then - // the state of t is state_terminated - // ) - // ) - // ) -static void -update_future_processes(unsigned int front, auto_ptr& next_snapshot) -{ - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - if(dp.get_arrival_time() == front) - { - assert(dp.get_elapsed_time() == 0); - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_arrival_time() == dp.get_elapsed_time()) - { - dt.set_state(Schedulable::state_ready); - // in this way we will never have threads ready having remaining time == 0 - if (dt.get_elapsed_time() == dt.get_total_cpu_time()) - dt.set_state(Schedulable::state_terminated); - } - } - } - } -} - - // Updates running process and thread - // Postcondition: - // for each thread t in next_snapshot - // ( - // if the state of t is state_running then - // ( - // the remaining time is one less than the one in the previous snapshot - // if the remaining time is zero then - // the state of t is state_terminated - // ) - // ) -static void -advance_running_process_and_thread(unsigned int front, auto_ptr& next_snapshot) -{ - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - if(dp.get_state() == Schedulable::state_running) - { - front = dp.get_elapsed_time(); - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_state() == Schedulable::state_running) - { - dt.decrease_remaining_time(); - if(dt.get_total_cpu_time() == dt.get_elapsed_time()) - { - dt.set_state(Schedulable::state_terminated); - typedef std::vector DynamicRequests; - DynamicRequests& drs = dt.get_dynamic_requests(); - for(DynamicRequests::const_iterator it_drs = drs.begin(); it_drs != drs.end(); it_drs++) - { - DynamicRequest& dr = **it_drs; - if(dr.get_state() != Request::state_exhausted) - { - typedef std::vector DynamicSubRequests; - DynamicSubRequests dsrs = dr.get_dynamic_subrequests(); - for(DynamicSubRequests::const_iterator it_dsrs = dsrs.begin(); it_dsrs != dsrs.end(); it_dsrs++) - { - DynamicSubRequest& dsr = **it_dsrs; - if(dsr.get_state() == Request::state_allocated) - { - while(dsr.get_remaining_time() != 0) - dsr.decrease_remaining_time(); - /// Remove the subrequest (pointer) from the queue. - bool removed = false; - typedef Environment::SubRequestQueue SubRequestQueue; - SubRequestQueue& queue = next_snapshot->get_request_queue(dsr.get_resource_key()); - for (SubRequestQueue::iterator it = queue.begin(); !removed && it != queue.end(); it++) - { - if ((*it) == &dsr) - { - queue.erase(it); - removed = true; - } - } - } - dsr.set_state(Request::state_exhausted); - } - } - } - } - } - } - } - } -} - - // Introduces newly arrived for(unsigned int i = 0; i < queue.size(); i++) - // Postcondition: - // for each process p in next_snapshot - // ( - // for each thread t belonging to p - // ( - // if the arrival time of p is equal to the elapsed time of p, then - // ( - // the state of p is state_ready and - // if the remaining time of t is zero, then - // the state of t is state_terminated - // ) - // ) - // ) -static void -update_future_threads(unsigned int front, auto_ptr& next_snapshot) -{ - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - if(dp.get_arrival_time() <= front) - { - front = dp.get_elapsed_time(); - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_arrival_time() == front) - { - dt.set_state(Schedulable::state_ready); - // in this way we will never have threads ready having remaining time == 0 - if (dt.get_elapsed_time() == dt.get_total_cpu_time()) - dt.set_state(Schedulable::state_terminated); - } - } - } - } -} - - // Updates allocated resources and subresources - // Postcondition: - // for each subrequest sr in next_snapshot - // ( - // if the state of sr is state_running then - // ( - // the remaining time is one less than the one in the previous snapshot - // if the remaining time is zero then - // the state of sr is state_exhausted - // ) - // ) -static void -advance_allocated_requests_and_subrequests(unsigned int front, auto_ptr& next_snapshot) -{ - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - if(dp.get_state() == Schedulable::state_running) - { - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_state() == Schedulable::state_running) - { - typedef std::vector DynamicRequests; - DynamicRequests& drs = dt.get_dynamic_requests(); - for(DynamicRequests::const_iterator it_drs = drs.begin(); it_drs != drs.end(); it_drs++) - { - DynamicRequest& dr = **it_drs; - if(dr.get_state() == Request::state_allocated) - { - typedef std::vector DynamicSubRequests; - DynamicSubRequests dsrs = dr.get_dynamic_subrequests(); - for(DynamicSubRequests::const_iterator it_dsrs = dsrs.begin(); it_dsrs != dsrs.end(); it_dsrs++) - { - DynamicSubRequest& dsr = **it_dsrs; - if(dsr.get_state() == Request::state_allocated) - { - dsr.decrease_remaining_time(); - if(dsr.get_remaining_time() == 0) - { - dsr.set_state(Request::state_exhausted); - /// Remove the subrequest (pointer) from the queue. - bool removed = false; - typedef Environment::SubRequestQueue SubRequestQueue; - SubRequestQueue& queue = next_snapshot->get_request_queue(dsr.get_resource_key()); - for (SubRequestQueue::iterator it = queue.begin(); !removed && it != queue.end(); it++) - { - if ((*it) == &dsr) - { - queue.erase(it); - removed = true; - } - } - } - } - } - } - } - } - } - } - } -} - - // Updates unallocated subrequests - // Postcondition: - // for each subrequest sr in next_snapshot - // ( - // let r be the resource specified in the subrequest - // let q be the queue associated with r - // let s be the state of sr - // let p be the number of places of r - // if sr is in q then - // if the position of sr in q is lesser or equal to p then - // the state of sr is state_allocable - // else - // the state of sr is state_unallocable - // ) - // for each thread t in next_snapshot - // ( - // let a be the number of requests of t whose state is state_allocable - // let u be the number of requests of t whose state is state_unallocable - // if a is not zero and u is zero then - // the state of t is state_ready - // if a is not zero and u is not zero then - // the state of t is state_blocked - // ) -static void -update_nonrunning_threads_and_unallocated_subrequests(unsigned int front, auto_ptr& next_snapshot) -{ - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_state() == Schedulable::state_ready || dt.get_state() == Schedulable::state_blocked) - { - bool blocked = false; - typedef std::vector DynamicRequests; - DynamicRequests drs = dt.get_dynamic_requests(); - for(DynamicRequests::const_iterator it_drs = drs.begin(); it_drs != drs.end(); it_drs++) - { - DynamicRequest& dr = **it_drs; - if(dr.get_state() == Request::state_allocable || dr.get_state() == Request::state_unallocable) - { - typedef std::vector DynamicSubRequests; - DynamicSubRequests dsrs = dr.get_dynamic_subrequests(); - for(DynamicSubRequests::const_iterator it_dsrs = dsrs.begin(); it_dsrs != dsrs.end(); it_dsrs++) - { - DynamicSubRequest& dsr = **it_dsrs; - assert(dsr.get_state() == Request::state_allocable || dsr.get_state() == Request::state_unallocable); - unsigned int position = 0; - Environment::SubRequestQueue& queue = next_snapshot->get_request_queue(dsr.get_resource_key()); - Environment::SubRequestQueue::iterator it = queue.begin(); - while (it != queue.end()) - { - if (*it == &dsr) - break; - it++; - position++; - } - /// Watch out: in a resource with 2 places, 0 and 1 are valid queue - /// positions, 2 is right one place out. - if (position >= next_snapshot->get_resources().find(dsr.get_resource_key())->second->get_places()) - dsr.set_state(Request::state_unallocable); - else - dsr.set_state(Request::state_allocable); - } - } - if (dr.get_state() == Request::state_unallocable) - blocked = true; - } - if (blocked) - dt.set_state(Schedulable::state_blocked); - else - dt.set_state(Schedulable::state_ready); - } - } - } -} - - // prepares the environment advancing counters and updating states appropriately -static void -advance_and_update_all(unsigned int front, auto_ptr& next_snapshot) -{ - // Checks if there are future processes that should be marked as ready, - // since their arrival time is equal to the simulation current instant. - update_future_processes(front, next_snapshot); - - // Updates the counters of the running entities. - // Checks if the running thread (if any) should be marked as - // terminated. - advance_running_process_and_thread(front, next_snapshot); - - // Checks if there are subrequests associated to the running thread that have - // just exausted, or if the old running thread has terminated. Then adjust - // the subrequests' list accordingly (freeing associated resources); - advance_allocated_requests_and_subrequests(front, next_snapshot); - - // Checks if there are future threads that should be marked as ready, - // since their arrival time is equal to their owner process' elapsed time. - update_future_threads(front, next_snapshot); - - // If any thread was blocked on a now freed resource, pass it from blocked - // to ready state, by discovering just-now-allocable requests. - // If any thread was ready on a now busy resource, pass it from ready to - // blocked state, by discovering just-now-unallocable requests. - // This is obtained by - update_nonrunning_threads_and_unallocated_subrequests(front, next_snapshot); - -} - - - // Function ?? FIXME - // Postcondition: - // if the policy is priority-preemptable or the quantum is finished then - // for each thread t in next_snapshot - // ( - // if the policy is priority-preemptable and the state of t was state_running - // the state of t is state_ready - // if the policy is quantum-preemptable and the state of t was state_running and the quantum has finished - // the state of t is state_ready - // ) -static void -manage_preemption(unsigned int front, auto_ptr& next_snapshot, bool preemptible_policy, unsigned int time_slice) -{ - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_state() == Schedulable::state_running) - { - if(preemptible_policy || time_slice == front - dt.get_last_acquisition()) - { - dt.set_state(Schedulable::state_ready); - dt.set_last_release(front); - } - } - } - } -} - - - - // FIXME: document me please -static void -build_ready_queue(unsigned int front, auto_ptr& next_snapshot) -{ - ReadyQueue& queue = next_snapshot->get_sorted_queue(); - assert(queue.size() == 0); - - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_state() == Schedulable::state_ready) - { - queue.append(dt); - } - } - } -} - - - // returns true if and only if the snapshot contains a candidate running thread, - // the thread's state is state_running. -static bool -find_a_candidate(unsigned int front, auto_ptr& next_snapshot) -{ - // look for a currently running process first - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - if (dp.get_state() == Schedulable::state_running) - { - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_state() == Schedulable::state_running) - return true; - } - } - } - // if no process has been found, select the first on the ready queue. - ReadyQueue& queue = next_snapshot->get_sorted_queue(); - if (queue.size() == 0) - return false; - DynamicThread& candidate = dynamic_cast(queue.get_item_at(0)); - candidate.set_state(Schedulable::state_running); - // HACK HACK HACK - // we do not remove the candidate from the ready queue. this information is useful - // since we chose to set the last_aquisition and the last_release just after a - // successful selection. see try_to_run(). - return true; -} - - - - - - - - // returns true if and oly if the simulation is terminated -static bool -check_if_simulation_is_terminated(unsigned int front, auto_ptr& next_snapshot) -{ - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - if (dp.get_state() == Schedulable::state_running) - return false; - if (dp.get_state() == Schedulable::state_future) - return false; - } - return true; -} - - -static bool -try_to_run(unsigned int front, auto_ptr& next_snapshot) -{ - bool success = false; - typedef std::vector Processes; - Processes ps = next_snapshot->get_processes(); - for(Processes::const_iterator it_ps = ps.begin(); it_ps != ps.end(); it_ps++) - { - DynamicProcess& dp = dynamic_cast(**it_ps); - if (dp.get_state() == Schedulable::state_running) - { - typedef std::vector DynamicThreads; - DynamicThreads dts = dp.get_dynamic_threads(); - for(DynamicThreads::const_iterator it_dts = dts.begin(); it_dts != dts.end(); it_dts++) - { - DynamicThread& dt = **it_dts; - if(dt.get_state() == Schedulable::state_running) - { - // this is our candidate - success = true; - // let's see if it is runnable or if it blocks: - typedef std::vector DynamicRequests; - DynamicRequests drs = dt.get_dynamic_requests(); - for(DynamicRequests::const_iterator it_drs = drs.begin(); it_drs != drs.end(); it_drs++) - { - DynamicRequest& dr = **it_drs; - // if it's time to do it, raise a request - if(dr.get_state() == Request::state_future && dr.get_instant() == dt.get_elapsed_time()) - { - typedef std::vector DynamicSubRequests; - DynamicSubRequests dsrs = dr.get_dynamic_subrequests(); - for(DynamicSubRequests::const_iterator it_dsrs = dsrs.begin(); it_dsrs != dsrs.end(); it_dsrs++) - { - DynamicSubRequest& dsr = **it_dsrs; - assert(dsr.get_state() == Request::state_future); - Environment::SubRequestQueue& queue = next_snapshot->get_request_queue(dsr.get_resource_key()); - /// Enqueue the subrequest at the back of the queue. - queue.push_back(&dsr); - - /// TODO: right here, right now we should call the resource policy to - /// update the queue. Updates the state of the subrequest depending - /// on the position in the queue, as explained before. - unsigned int places = next_snapshot->get_resources().find(dsr.get_resource_key())->second->get_places(); - dsr.set_state(queue.size() > places ? Request::state_unallocable : Request::state_allocable); - } // for each subrequest - // if it is actually allocable, allocate it - if (dr.get_state() == Request::state_allocable) - { - for(DynamicSubRequests::const_iterator it_dsrs = dsrs.begin(); it_dsrs != dsrs.end(); it_dsrs++) - { - DynamicSubRequest& dsr = **it_dsrs; - assert(dsr.get_state() == Request::state_allocable); - dsr.set_state(Request::state_allocated); - } - } - } // end of request raising - // if it does exist at least one unallocable request, the thread may not run! - if (dr.get_state() == Request::state_unallocable) - { - dt.set_state(Schedulable::state_blocked); - success = false; - } - } // end for loop over all running thread requests - - // HACK HACK HACK - // this check is legal, since all pointers in the sorted queue should be valid. - // if the current thread is the first in the ready queue, we need to remove it - if (next_snapshot->get_sorted_queue().size() != 0 && - &next_snapshot->get_sorted_queue().get_item_at(0) == &dt) - { - // in case of success in allocating the cpu, we must update the last_aquisition - if (success) - dt.set_last_acquisition(front); - next_snapshot->get_sorted_queue().erase_first(); - } // if the current thread was the one that was previously running - else - { - // we must update the last_release - if (!success) - dt.set_last_release(front); - else - return success; - //cpu_policy.sort_queue(); - } - - - - } // end things to do if thread was running - } // end for loop over all threads of a running process - } // end if process is running - } // end for loop over all processes - return false; -}// end main loop - -//............................................................................. -//............................................................................. -// The Return of the Son of the Monster Step Forward -// -// The refactoring of this method has been carried on listening to Frank -// Zappa's The Return of the Son of the Monster Magnet. -// The magic word for tonight is.. procedural! -//............................................................................. -//............................................................................. bool -Scheduler::step_forward(ConcreteHistory& concrete_history, CPUPolicy& cpu_policy) - throw(UserInterruptException, MalformedPolicyException) +Scheduler::step_forward(History& history, CPUPolicy& cpu_policy) + throw(UserInterruptException, MalformedPolicyException) { - // Preconditions: - assert (concrete_history.get_size() > 0); + // This very method should be exclusive: no concurrent behaviour, from when we + // store a readyqueue and policy pointer for the user-policy to retrieve, to when + // the policy returns + Glib::Mutex::Lock lock (_step_mutex); - // Concurrency: - // This very method should be exclusive: no concurrent behaviour, from when - // we store a readyqueue and policy pointer for the user-policy to retrieve, - // to when the policy returns. - Glib::Mutex::Lock lock(_step_mutex); + // NOTE: Be sure to read the *ORIGINAL* documentation in the design document for this method! - // The instant we are going to build a snapshot for: - // if history has size 1, then the snapshot we will add will describe the - // environment at instan 0, i.e. (1 - 1). - unsigned int next_instant = concrete_history.get_size() - 1; + unsigned int alive_threads = 0; // Assume we've finished. Then prove me wrong. + int current_instant = history.get_size() - 1; /* They should be equivalent */ - // The snapshot we are building: it is built as a copy og the last one. - // We use an auto_ptr since we've some exceptions in the coming... - auto_ptr next_snapshot(new ConcreteEnvironment(concrete_history.get_last_environment())); + // Safe cast: + ConcreteHistory& concrete_history = reinterpret_cast(history); - // Temporarily set the _ready_queue param and the _policy one for - // use from external plugins - _policy = &cpu_policy; - _ready_queue = &next_snapshot->get_sorted_queue(); + // Use an auto_ptr since we've some exceptions in the coming... + auto_ptr new_snapshot(new ConcreteEnvironment(concrete_history.get_last_environment())); - // begin procedure step forward - // ahw, I lowe soo munch all thissh olt fasshoned procedureal programing stylus - advance_and_update_all(next_instant, next_snapshot); + Threads all_threads; + DynamicThread* running_thread = NULL; - try - { - // If the user-policy is preemptible, or if the assigned time slice has - // terminated, pass the running thread to ready state. - manage_preemption(next_instant, next_snapshot, cpu_policy.is_pre_emptive(), cpu_policy.get_time_slice()); + collect_threads(new_snapshot->get_processes(), all_threads); - // Build the schedulables ready queue - build_ready_queue(next_instant, next_snapshot); - - // and pass it to the policy itself -// if (next_snapshot->get_sorted_queue().size() != 0) -// cpu_policy.sort_queue(); - - - bool running = false; - bool idle = false; - bool terminated = false; - - do + // When a new instant cames, we could have to update the state of future + // threads to make them ready, or running threads to make them terminated + // We also update the other properties of the running thread, and keep a + // count of the alive threads + for (Iseq it = iseq(all_threads); it; ++it) { - if (find_a_candidate(next_instant, next_snapshot)) - running = try_to_run(next_instant, next_snapshot); - else - if (check_if_simulation_is_terminated(next_instant, next_snapshot)) - terminated = true; - else - idle = true; - } - while (!running && !idle && !terminated); + DynamicThread& current = **it; - // append the new snapshot, releasing the auto_ptr! - concrete_history.append_new_environment(next_snapshot.release()); + // 1. mark future threads as ready, if appropriate + if (current.get_state() == Schedulable::state_future) + { + Process& parent = current.get_process(); + if ((long) parent.get_arrival_time() <= current_instant && + parent.get_elapsed_time() == current.get_arrival_time()) + current.set_state(Schedulable::state_ready); + } - // Reset values that the policy doesn't need anymore - _policy = NULL; - _ready_queue = NULL; + // Save the current running thread for future usage, if it hasn't ended + // its allotted time + if (current.get_state() == Schedulable::state_running) + { + assert(running_thread == NULL); // ... only one thread must be running at a time. + running_thread = ¤t; // Even if we can change its state to terminate - return !terminated; // watch out for the ! - } - catch(CPUPolicyException& e) - { - // Reset values that the policy doesn't need anymore - _policy = NULL; - _ready_queue = NULL; + // increasing the time elapsed of the running thread + process + // should be done here as the first thing, instead than + // directly after selecting them + if (current.get_total_cpu_time() - current.get_elapsed_time() > 0) + current.decrease_remaining_time(); - // Do we need to update/reset something else? - // Going up unwinding the stack, tell: - // - the user that the policy sucks - // - SimulationController that everything stopped - throw; - } + // 4a. Look for exhausted requests for the running thread + update_allocated_requests(current, *new_snapshot); + + // 2. mark threads that used all their allotted time as terminated, + // and put their requests as exhausted + if (current.get_total_cpu_time() - current.get_elapsed_time() == 0) + { + current.set_state(Schedulable::state_terminated); + terminate_all_requests_of(current, *new_snapshot); + } + } + + // 3. check for simulation termination (we can directly use threads + // for this check, since processes' state is based upon threads' one) + if ((current.get_state() & (Schedulable::state_blocked | Schedulable::state_terminated)) == 0) + alive_threads++; + + } //~ for over all_threads + + // ?. Time to see if some unallocable request became allocable, so + // the thread can pass from blocked to ready state, or the other way + // round + look_for_mutant_request_states(*new_snapshot, alive_threads); + + // Now if the simulation ended we append the newly + // created environment and return false + if (alive_threads == 0) goto final_cleanup; + + // Use the CPU Policy to sort the ready queue, and manage + // requests for the newly selected running thread + try + { + // Temporarily set the _ready_queue param and the _policy one for + // use from external plugins. In fact, this is how get_ready_queue() + // acts as a callback function. + _policy = &cpu_policy; + _ready_queue = &new_snapshot->get_sorted_queue(); + + // Determine if the policy is pre_emptive, and what time slice it uses + bool preemptible_policy = cpu_policy.is_pre_emptive(); + int time_slice = cpu_policy.get_time_slice(); + + // ?. See if old running_thread has to be put to ready state + // This happens when the policy makes use of preemptability by + // priority, or when a time slice ended + if (running_thread != NULL && running_thread->get_state() == Schedulable::state_running && + (preemptible_policy || + time_slice == current_instant - running_thread->get_last_acquisition()) ) + { + running_thread->set_state(Schedulable::state_ready); + running_thread->set_last_release(current_instant); + } + + + // ?. Ask the policy to sort the queue. If we must select + // a new thread and it can't run for some reason (it goes blocked, or + // terminates), then we remove it from the built ReadyQueue and + // check if the next one can run. + prepare_ready_queue(*new_snapshot, all_threads); + if(_ready_queue->size() > 0) cpu_policy.sort_queue(); + + // If we don't have to select a new running thread, because the old one didn't + // have to release the CPU, our work may end here: + // * if the current running thread doesn't block, we can perform the final cleanup, + // since the queue is already sorted + // * else we've to select another running thread, so we continue down in the method + if(running_thread != NULL && running_thread->get_state() == Schedulable::state_running) + { + raise_new_requests(*running_thread, *new_snapshot); + if(running_thread->get_state() != Schedulable::state_blocked) + goto final_cleanup; + else + { + running_thread->set_last_release(current_instant); + running_thread = NULL; + alive_threads--; + // Proceed to select a new running thread, below + } + } + + bool we_ve_got_a_winner = false; + while(_ready_queue->size() > 0 && !we_ve_got_a_winner) // No sense in trying to schedule something that isn't there + { + // Else, it's time to see if the first candidate can run + DynamicThread& candidate = (DynamicThread&) _ready_queue->get_item_at(0); + candidate.set_last_acquisition(current_instant); + + // If a thread has been created with duration "0" (silly, but possible); + // if you think it's safe, you can change this condition with an assert + // and delete the body of the ``if''. + if(candidate.get_total_cpu_time() - candidate.get_elapsed_time() == 0) + { + candidate.set_last_release(current_instant); + candidate.set_state(Schedulable::state_terminated); + // Put every request of this thread to state_exhausted + terminate_all_requests_of(candidate, *new_snapshot); + _ready_queue->erase_first(); + alive_threads--; + + continue; + } + + // Now we check if our candidate blocks on a new request + raise_new_requests(candidate, *new_snapshot); + if(candidate.get_state() != Schedulable::state_blocked) + // If we got here, our candidate can run + we_ve_got_a_winner /*!hurrah!*/ = true; + else // if blocked, we've to remove it from the ready queue + { + _ready_queue->erase_first(); + alive_threads--; + } + } + + + // ?. Finally select the new thread (if appropriate); now we're sure + // the one we have can run + if (we_ve_got_a_winner) + { + // Fix fields of running thread + DynamicThread& new_running = (DynamicThread&) _ready_queue->get_item_at(0); + new_running.set_state(Schedulable::state_running); + } + + } + catch (const CPUPolicyException& e) + { + // Reset values that the policy doesn't need anymore + _policy = NULL; + _ready_queue = NULL; + + // Do we need to update/reset something else? + + // Going up unwinding the stack, tell: + // - the user that the policy sucks + // - SimulationController that everything stopped + throw; + } + +final_cleanup: + + // append the new snapshot... + // ...and remember to release the auto_ptr! + concrete_history.append_new_environment(new_snapshot.release()); + + // Reset values that the policy doesn't need anymore + _policy = NULL; + _ready_queue = NULL; + + // If we got there, a step has been performed. + // Return if we can perform another step. + return alive_threads != 0; } diff --git a/src/backend/scheduler.hh b/src/backend/scheduler.hh index 9f9d7be..7a16cc1 100644 --- a/src/backend/scheduler.hh +++ b/src/backend/scheduler.hh @@ -28,7 +28,7 @@ namespace sgpem #include "config.h" -#include "concrete_history.hh" +#include "history.hh" #include "cpu_policy.hh" #include "ready_queue.hh" #include "user_interrupt_exception.hh" @@ -59,18 +59,6 @@ namespace sgpem { friend class Singleton; public: - /** - Returns a pointer to the queue containing all the ready - schedulable objects (for the policy to sort it). - \return a pointer to the queue containing all the ready - schedulable objects (for the policy to sort it). - */ - ReadyQueue* get_ready_queue(); - /** - Resets the simulation to the initial state. - Deprecated. - */ -// void reset_status(); /** Generates a new ReadyQueue representing the status of the processes at the simulation instant next to the current one, and extends the History by @@ -79,19 +67,32 @@ namespace sgpem \return false If the simulation has ended, true otherwise */ - bool step_forward(ConcreteHistory& history, CPUPolicy& cpu_policy) throw(UserInterruptException, MalformedPolicyException); + bool step_forward(History& history, CPUPolicy& cpu_policy) throw(UserInterruptException, MalformedPolicyException); /** - Returns the policy that will be used to generate the simulation at the next instant. - \return the policy that will be used to generate the simulation at the next instant. + \brief Returns the policy that will be used to generate the simulation at the next instant. + + \warning This is a callback method: it can only be used in methods of CPUPolicy called + by step_forward(). Else, a NULL pointer will be returned. + \return A pointer to the active policy, or NULL if not inside step_forward() */ CPUPolicy* get_policy(); + + /** + \brief Returns a pointer to the queue containing all the ready + schedulable objects (for the policy to sort it). + + \warning This is a callback method: it can only be used in methods of CPUPolicy called + by step_forward(). Else, a NULL pointer will be returned. + \return A pointer to the queue, or NULL if not inside step_forward() + */ + ReadyQueue* get_ready_queue(); private: Scheduler(); //private constructor. ReadyQueue* _ready_queue; - CPUPolicy* _policy; + CPUPolicy* _policy; Glib::Mutex _step_mutex; }; diff --git a/src/templates/sequences.tcc b/src/templates/sequences.tcc index e221a38..b1727c4 100644 --- a/src/templates/sequences.tcc +++ b/src/templates/sequences.tcc @@ -51,6 +51,18 @@ public: const In& operator->() const { return Iseq::first; } + + bool operator==(const Iseq& i) const + { return Iseq::first == i.first; } + + bool operator!=(const Iseq& i) const + { return Iseq::first != i.first; } + +// bool operator==(const In& i) const +// { return Iseq::first == i; } + +// bool operator!=(const In& i) const +// { return Iseq::first != i; } }; @@ -59,7 +71,7 @@ public: template Iseq -iseq(const Container& c) +const_iseq(const Container& c) { return Iseq(c.begin(), c.end()); } @@ -77,7 +89,7 @@ iseq(Container& c) template Iseq -r_iseq(const Container& c) +const_riseq(const Container& c) { return Iseq(c.rbegin(), c.rend()); } @@ -85,7 +97,7 @@ r_iseq(const Container& c) template Iseq -r_iseq(Container& c) +riseq(Container& c) { return Iseq(c.rbegin(), c.rend()); } diff --git a/src/testsuite/scheduling-wizards/wizard-block-fail b/src/testsuite/scheduling-wizards/wizard-block-fail index 7500ffa..a93ba05 100644 --- a/src/testsuite/scheduling-wizards/wizard-block-fail +++ b/src/testsuite/scheduling-wizards/wizard-block-fail @@ -37,6 +37,12 @@ add subrequest 1 1 1 1 4 +add request 1 1 +2 +add subrequest 1 1 2 +1 +3 + add request 1 2 0 add subrequest 1 2 1 diff --git a/src/testsuite/scheduling-wizards/wizard-deadlock-test b/src/testsuite/scheduling-wizards/wizard-deadlock-test new file mode 100644 index 0000000..71d99b3 --- /dev/null +++ b/src/testsuite/scheduling-wizards/wizard-deadlock-test @@ -0,0 +1,59 @@ +set cpu-policy 2 + +configure-cpu-policy +0 +1 + +add resource +scythe +false +1 +0 +add resource +sword +false +1 +0 + + +add process +Antrophomorphic personification +0 +0 + +add thread 1 +Death +4 +0 +0 +add thread 1 +Susan Sto Helit +4 +0 +0 + +add request 1 1 +0 +add subrequest 1 1 1 +0 +2 + +add request 1 1 +1 +add subrequest 1 1 2 +1 +2 + +add request 1 2 +0 +add subrequest 1 2 1 +1 +2 + +add request 1 2 +1 +add subrequest 1 2 2 +0 +2 + +run diff --git a/src/testsuite/scheduling-wizards/wizard-priority-inversion-porno b/src/testsuite/scheduling-wizards/wizard-priority-inversion-porno new file mode 100644 index 0000000..9ab8e41 --- /dev/null +++ b/src/testsuite/scheduling-wizards/wizard-priority-inversion-porno @@ -0,0 +1,88 @@ +# Shows a problem up to revision 893, fixed(?) in 894. + +# Look out for Mandingo: while it (he?) blocks at +# a certain point, he could immediately run, +# having higher priority than Peter North. Instead +# Peter continues his great fuck with Jenna, although +# he should pre-empt, because Rocco comes before +# Mandingo in the Jenna allocation queue. + +# We can both assume: a) this is good as it is: +# since Rocco requested Jenna before Mandingo, he's +# his due right to mutilate her pussy for first; or b) +# this is wrong, because it lets a lower priority +# thread to run instead of a thread with higher +# priority that could use the other Jenna's hole +# while it's free (admittedly, Jenna Jameson as a +# resource should have at least four or five places, +# but for the sake of semplicity of this example +# we assumed that mouth, earholes and navel don't +# count). + +# TO SEE THIS PROBLEM CLEARLY, look with +# attention at the resource queue at instant 2 + +# Use round robin with preemption by +# priority and a time slice of 1 +set cpu-policy 2 +configure-cpu-policy +1 +1 + +add resource +Jenna Jameson +false +2 +0 + +add process +"Wet Dreams IV" +0 +0 + +add thread 1 +Peter North +3 +0 +10 + +add request 1 1 +0 +add subrequest 1 1 1 +0 +3 + + +add process +"A Night With Jenna" +2 +0 + +add thread 2 +Rocco Siffredi +3 +0 +0 + +add thread 2 +Mandingo +3 +0 +5 + +add request 2 1 +0 +add subrequest 2 1 1 +0 +3 +add subrequest 2 1 1 +0 +3 + +add request 2 2 +0 +add subrequest 2 2 1 +0 +3 + +run diff --git a/src/testsuite/scheduling-wizards/wizard-unblock-test b/src/testsuite/scheduling-wizards/wizard-unblock-test new file mode 100644 index 0000000..80378ee --- /dev/null +++ b/src/testsuite/scheduling-wizards/wizard-unblock-test @@ -0,0 +1,43 @@ +set cpu-policy 2 + +configure-cpu-policy +0 +1 + +add resource +banana +false +1 +0 + + + +add process +Unseen University +0 +0 + +add thread 1 +The Librarian +4 +0 +0 +add thread 1 +Rincewind +2 +0 +0 + +add request 1 1 +0 +add subrequest 1 1 1 +0 +3 + +add request 1 2 +1 +add subrequest 1 2 1 +0 +2 + +run diff --git a/src/text_simulation.cc b/src/text_simulation.cc index eb8db4d..44e0e36 100644 --- a/src/text_simulation.cc +++ b/src/text_simulation.cc @@ -1471,8 +1471,16 @@ TextSimulation::parse_command(TextSimulation& sim, const ustring& str) Tokens arguments = tokenize(str); + // Ignore empty lines if (arguments.size() == 0) return; + + // Allow also comments into batch scripts (w/ echo): + if(arguments[0].at(0) == '#') + { + cout << str; + return; + } ustring key = arguments[0].uppercase(); @@ -1698,7 +1706,7 @@ TextSimulation::update(const History& changed_history) oss << setw(fill1) << r.get_instant(); oss << setw(fill1) << sr.get_length(); oss << setw(fill1) << sr.get_length() - sr.get_remaining_time(); - oss << setw(fill1) << sr.get_resource_key(); + oss << setw(fill1*2) << sr.get_resource_key(); oss << endl; p_stdout(oss.str());