------------------------------------------------------------------------------ ------------------------------------------------------------------------------ -- Cheddar is a GNU GPL real-time scheduling analysis tool. -- This program provides services to automatically check schedulability and -- other performance criteria of real-time architecture models. -- -- Copyright (C) 2002-2020, Frank Singhoff, Alain Plantec, Jerome Legrand, -- Hai Nam Tran, Stephane Rubini -- -- The Cheddar project was started in 2002 by -- Frank Singhoff, Lab-STICC UMR CNRS 6285, Universite de Bretagne Occidentale -- -- Cheddar has been published in the "Agence de Protection des Programmes/France" in 2008. -- Since 2008, Ellidiss technologies also contributes to the development of -- Cheddar and provides industrial support. -- -- The full list of contributors and sponsors can be found in AUTHORS.txt and SPONSORS.txt -- -- This program 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. -- -- This program 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 this program; if not, write to the Free Software -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- -- -- Contact : cheddar@listes.univ-brest.fr -- ------------------------------------------------------------------------------ -- Last update : -- $Rev$ -- $Date$ -- $Author: singhoff $ ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; with scheduler_io; use scheduler_io; with statements; use statements; with expressions; use expressions; use expressions.variables_type_package; with processor_interface; use processor_interface; with core_units; use core_units; use core_units.core_units_table_package; with tasks; use tasks; with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO; with unbounded_strings; use unbounded_strings; with scheduler; use scheduler; with scheduler_interface; use scheduler_interface; with interpreter.extended; use interpreter.extended; with dependency_services; use dependency_services; with translate; use translate; with io_tools; use io_tools; with Ada.IO_Exceptions; use Ada.IO_Exceptions; with Ada.Exceptions; use Ada.Exceptions; with xml_generic_parsers; use xml_generic_parsers; with xml_generic_parsers.event_table; use xml_generic_parsers.event_table; with Input_Sources.File; use Input_Sources.File; with Sax.Readers; use Sax.Readers; with Text_IO; use Text_IO; with parser; use parser; with sections; use sections; with section_set; use section_set; with address_spaces; use address_spaces; with debug; use debug; package body partitioning_services is procedure partition_small_task (src_processors : in processors_set; src_tasks : in tasks_set; msg : in out Unbounded_String; result_tasks : in out tasks_set; processor_result : out processors_iterator) is a_task_result : periodic_task_ptr; my_task_iterator_result : tasks_iterator; a_processor : generic_processor_ptr; my_processor_iterator : processors_iterator; processor_util, beta : Float := 0.0; s_cur : Float := 1.0; first_run : Boolean := True; begin validate_multiprocessor_tasks (src_processors, src_tasks); duplicate (src_tasks, result_tasks); sort (result_tasks, increasing_si'access); reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); reset_iterator (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); loop if first_run = False then if is_last_element (src_processors, my_processor_iterator) then duplicate (src_tasks, result_tasks); raise no_such_processors; end if; next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); end if; first_run := False; if Float (a_task_result.capacity) / Float (a_task_result.period) > 1.0 then duplicate (src_tasks, result_tasks); raise no_such_processors; end if; a_task_result.cpu_name := a_processor.name; processor_util := Float (a_task_result.capacity) / Float (a_task_result.period); s_cur := Float (Log (Float (a_task_result.period), 2.0)) - Float'floor (Float (Log (Float (a_task_result.period), 2.0))); beta := 0.0; exit when is_last_element (result_tasks, my_task_iterator_result); loop if is_last_element (result_tasks, my_task_iterator_result) then first_run := True; exit; end if; next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); beta := (Float (Log (Float (a_task_result.period), 2.0)) - Float'floor (Float (Log (Float (a_task_result.period), 2.0)))) - s_cur; processor_util := processor_util + Float (a_task_result.capacity) / Float (a_task_result.period); if (processor_util <= Float'max (Float (Log (2.0, e)), 1.0 - beta * Float (Log (2.0, e)))) then a_task_result.cpu_name := a_processor.name; else exit; end if; end loop; end loop; processor_result := my_processor_iterator; sort (result_tasks, increasing_name'access); end partition_small_task; procedure partition_small_task (src_processors : in processors_set; src_tasks : in tasks_set; msg : in out Unbounded_String; result_tasks : in out tasks_set) is tmp : processors_iterator; begin msg := To_Unbounded_String (" (") & lb_see (current_language) & To_Unbounded_String ("[9], [10]) "); partition_small_task (src_processors, src_tasks, msg, result_tasks, tmp); end partition_small_task; procedure partition_small_task (src_processors : in processors_set; result_tasks : in out tasks_set; processor_result : out processors_iterator) is src_tasks : tasks_set; msg : Unbounded_String; begin duplicate (result_tasks, src_tasks); partition_small_task (src_processors, src_tasks, msg, result_tasks, processor_result); end partition_small_task; procedure rm_general_tasks (src_processors : in processors_set; result_tasks : in out tasks_set) is a_task_result : periodic_task_ptr; a_task_tmp : periodic_task_ptr; a_task_iterator : tasks_iterator; my_task_iterator_result : tasks_iterator; tmp_iterator : tasks_iterator; a_processor : generic_processor_ptr; my_processor_iterator : processors_iterator; src_tasks : tasks_set; num_of_tasks : Integer := 0; task_utilization, total_utilization : Float := 0.0; goto_first_loop : Boolean := False; begin duplicate (result_tasks, src_tasks); reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); reset_iterator (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); sort (result_tasks, increasing_period'access); -- Zero processor locations -- loop a_task_result.cpu_name := To_Unbounded_String (""); exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); loop loop if goto_first_loop then goto_first_loop := False; exit; end if; -- Fetch information for condition-IP -- tmp_iterator := my_task_iterator_result; reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); num_of_tasks := 0; total_utilization := 0.0; loop if a_task_result.cpu_name = a_processor.name then num_of_tasks := num_of_tasks + 1; total_utilization := total_utilization + Float (a_task_result.capacity) / Float (a_task_result.period); end if; exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; my_task_iterator_result := tmp_iterator; current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); task_utilization := Float (a_task_result.capacity) / Float (a_task_result.period); if task_utilization <= 2.0 * (1.0 / ((1.0 + total_utilization / Float (num_of_tasks)))** num_of_tasks) - 1.0 then if total_utilization = 0.0 then a_task_result.cpu_name := a_processor.name; exit; end if; reset_iterator (result_tasks, a_task_iterator); current_element (result_tasks, generic_task_ptr (a_task_tmp), a_task_iterator); loop if a_task_tmp.cpu_name = a_processor.name and a_task_tmp /= a_task_result then if a_task_result.period < a_task_tmp.period and (Integer (Float'floor (Float (a_task_tmp.period) / Float (a_task_result.period))) * (a_task_tmp.period - a_task_result.capacity) >= a_task_tmp.capacity or a_task_tmp.period >= Integer (Float'ceiling (Float (a_task_tmp.period) / Float (a_task_result.period))) * a_task_result.capacity + a_task_tmp.capacity) then a_task_result.cpu_name := a_processor.name; reset_iterator (src_processors, my_processor_iterator); goto_first_loop := True; exit; else if a_task_result.period >= a_task_tmp.period and (( (Integer (Float'floor (Float (a_task_result.period) / Float (a_task_tmp.period))) * (a_task_result.period - a_task_tmp.capacity)) >= a_task_result.capacity) or (a_task_result.period >= (Integer (Float'ceiling (Float (a_task_result.period) / Float (a_task_tmp.period))) * a_task_tmp.capacity + a_task_result.capacity))) then a_task_result.cpu_name := a_processor.name; reset_iterator (src_processors, my_processor_iterator); goto_first_loop := True; exit; else if is_last_element (src_processors, my_processor_iterator) then duplicate (src_tasks, result_tasks); raise no_such_processors; end if; next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); exit; end if; end if; end if; exit when is_last_element (result_tasks, a_task_iterator); next_element (result_tasks, a_task_iterator); current_element (result_tasks, generic_task_ptr (a_task_tmp), a_task_iterator); end loop; else if is_last_element (src_processors, my_processor_iterator) then duplicate (src_tasks, result_tasks); raise no_such_processors; end if; next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); end if; end loop; exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); end loop; end rm_general_tasks; procedure partition_general_task (src_processors : in processors_set; src_tasks : in tasks_set; msg : in out Unbounded_String; result_tasks : in out tasks_set) is a_task_result : periodic_task_ptr; my_task_iterator_result : tasks_iterator; my_processor_iterator : processors_iterator; a_processor : generic_processor_ptr; rmst_tasks : tasks_set; rmgt_tasks : tasks_set; tmp_cpus : processors_set; rmst_index, rmgt, num_of_tasks : Integer := 0; begin validate_multiprocessor_tasks (src_processors, src_tasks); msg := To_Unbounded_String (" (") & lb_see (current_language) & To_Unbounded_String ("[9], [10]) "); duplicate (src_tasks, result_tasks); sort (result_tasks, increasing_utilization'access); -- Scan for max Alfa to make division between RMST and RMGT tasks -- reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); loop if Float (a_task_result.capacity) / Float (a_task_result.period) > 1.0 / 3.0 then exit; else rmgt := rmgt + 1; end if; exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; -- Make bin-packing for RMST and RMGT -- reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); loop exit when rmst_index = rmgt; add (rmst_tasks, generic_task_ptr (a_task_result)); rmst_index := rmst_index + 1; next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; num_of_tasks := Integer (get_number_of_elements (result_tasks)); if rmgt /= num_of_tasks then loop current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); add (rmgt_tasks, generic_task_ptr (a_task_result)); exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); end loop; end if; -- Call RMST and RMGT functions respectively -- if (is_empty (rmst_tasks) = False) then partition_small_task (src_processors, rmst_tasks, my_processor_iterator); end if; if False = is_last_element (src_processors, my_processor_iterator) and is_empty (rmgt_tasks) = False then -- Make processor set for RMGT -- next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); loop add (tmp_cpus, a_processor); exit when is_last_element (src_processors, my_processor_iterator); next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); end loop; rm_general_tasks (tmp_cpus, rmgt_tasks); else if is_last_element (src_processors, my_processor_iterator) and is_empty (rmgt_tasks) = False then duplicate (src_tasks, result_tasks); raise no_such_processors; end if; end if; -- Put results of two functions to returning task set -- if not is_empty (rmst_tasks) then duplicate (rmst_tasks, result_tasks); end if; if not is_empty (rmgt_tasks) then reset_iterator (rmgt_tasks, my_task_iterator_result); current_element (rmgt_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); loop add (result_tasks, generic_task_ptr (a_task_result)); exit when is_last_element (rmgt_tasks, my_task_iterator_result); next_element (rmgt_tasks, my_task_iterator_result); current_element (rmgt_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; end if; sort (result_tasks, increasing_name'access); end partition_general_task; procedure partition_next_fit (src_processors : in processors_set; src_tasks : in tasks_set; msg : in out Unbounded_String; result_tasks : in out tasks_set) is a_task_result : periodic_task_ptr; my_task_iterator_result : tasks_iterator; a_processor : generic_processor_ptr; my_processor_iterator : processors_iterator; m : Integer := 2; total_utilization : Float := 0.0; condition_ip : Boolean := False; begin validate_multiprocessor_tasks (src_processors, src_tasks); msg := To_Unbounded_String (" (") & lb_see (current_language) & To_Unbounded_String ("[9], [10]) "); duplicate (src_tasks, result_tasks); sort (result_tasks, increasing_period'access); reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); reset_iterator (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); loop if Float (a_task_result.capacity) / Float (a_task_result.period) <= 1.0 then a_task_result.cpu_name := a_processor.name; else duplicate (src_tasks, result_tasks); raise no_such_processors; end if; exit when is_last_element (result_tasks, my_task_iterator_result); loop exit when is_last_element (result_tasks, my_task_iterator_result); total_utilization := total_utilization + Float (a_task_result.capacity) / Float (a_task_result.period); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); if Float (a_task_result.capacity) / Float (a_task_result.period) <= 2.0 * (1.0 / (1.0 + total_utilization / Float ((m - 1)))**(m - 1)) - 1.0 and total_utilization <= Float (m - 1) * (2.0**(1.0 / Float (m - 1)) - 1.0) then condition_ip := True; else condition_ip := False; end if; if condition_ip then a_task_result.cpu_name := a_processor.name; m := m + 1; else if is_last_element (src_processors, my_processor_iterator) then duplicate (src_tasks, result_tasks); raise no_such_processors; end if; next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); total_utilization := 0.0; m := 2; exit; end if; end loop; end loop; sort (result_tasks, increasing_name'access); end partition_next_fit; procedure partition_first_fit (src_processors : in processors_set; src_tasks : in tasks_set; msg : in out Unbounded_String; result_tasks : in out tasks_set) is a_task_result : periodic_task_ptr; my_task_iterator_result : tasks_iterator; temp_task_iterator : tasks_iterator; a_processor : generic_processor_ptr; my_processor_iterator : processors_iterator; num_of_tasks : Integer := 0; task_utilization, total_utilization : Float := 0.0; begin validate_multiprocessor_tasks (src_processors, src_tasks); msg := To_Unbounded_String (" (") & lb_see (current_language) & To_Unbounded_String ("[9], [10]) "); duplicate (src_tasks, result_tasks); sort (result_tasks, increasing_period'access); reset_iterator (result_tasks, my_task_iterator_result); temp_task_iterator := my_task_iterator_result; current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); reset_iterator (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); -- Zero processor locations -- loop a_task_result.cpu_name := To_Unbounded_String (""); exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); loop loop -- Fetch information for condition-IP -- reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); num_of_tasks := 0; total_utilization := 0.0; loop if a_task_result.cpu_name = a_processor.name then num_of_tasks := num_of_tasks + 1; total_utilization := total_utilization + Float (a_task_result.capacity) / Float (a_task_result.period); end if; exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; my_task_iterator_result := temp_task_iterator; current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); task_utilization := Float (a_task_result.capacity) / Float (a_task_result.period); if (task_utilization <= 2.0 * (1.0 / (1.0 + total_utilization / Float (num_of_tasks))** num_of_tasks) - 1.0) then a_task_result.cpu_name := a_processor.name; reset_iterator (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); exit; else if is_last_element (src_processors, my_processor_iterator) then duplicate (src_tasks, result_tasks); raise no_such_processors; end if; next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); end if; end loop; exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); temp_task_iterator := my_task_iterator_result; end loop; sort (result_tasks, increasing_name'access); end partition_first_fit; procedure partition_best_fit (src_processors : in processors_set; src_tasks : in tasks_set; msg : in out Unbounded_String; result_tasks : in out tasks_set) is a_task_result : periodic_task_ptr; my_task_iterator_result : tasks_iterator; a_processor : generic_processor_ptr; my_processor_iterator : processors_iterator; temp_task_iterator : tasks_iterator; best_cpu : processors_iterator; m : Integer := 1; best_fit_value : Float := 2.0; best_fit_tmp_value, tot_util : Float; first_task : Boolean := True; assignable : Boolean; begin validate_multiprocessor_tasks (src_processors, src_tasks); msg := To_Unbounded_String (" (") & lb_see (current_language) & To_Unbounded_String ("[9], [10]) "); duplicate (src_tasks, result_tasks); sort (result_tasks, increasing_period'access); reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); -- Zero processor locations -- loop a_task_result.cpu_name := To_Unbounded_String (""); exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); loop reset_iterator (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); -- Loop scans best processor for task to be placed -- loop temp_task_iterator := my_task_iterator_result; -- get CPU data -- reset_iterator (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); m := 1; tot_util := 0.0; loop if a_task_result.cpu_name = a_processor.name then m := m + 1; tot_util := tot_util + Float (a_task_result.capacity) / Float (a_task_result.period); end if; exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; my_task_iterator_result := temp_task_iterator; current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); -- test for condition IP -- if first_task then if Float (a_task_result.capacity) / Float (a_task_result.period) <= 1.0 then reset_iterator (src_processors, best_cpu); my_processor_iterator := best_cpu; first_task := False; assignable := True; exit; else assignable := False; exit; end if; else if Float (a_task_result.capacity) / Float (a_task_result.period) <= 2.0 * (1.0 / (1.0 + tot_util / Float ((m - 1)))**(m - 1)) - 1.0 then -- Tricky Condition IP testing because mathematics function -- doesn't seem to understand handle -- zero number very well in following check (M := 0 -- happens when processor is empty) -- if m > 1 then if tot_util <= Float (m - 1) * (2.0**Float (1.0 / Float (m - 1)) - 1.0) then best_fit_tmp_value := 2.0 * (1.0 / ((1.0 + tot_util / Float (m - 1))**(m - 1))) - 1.0; if best_fit_tmp_value < best_fit_value then best_fit_value := best_fit_tmp_value; best_cpu := my_processor_iterator; assignable := True; end if; end if; else best_fit_tmp_value := 2.0 * (1.0 / (1.0 + tot_util / Float (m - 1)**(m - 1))) - 1.0; if best_fit_tmp_value < best_fit_value then best_fit_value := best_fit_tmp_value; best_cpu := my_processor_iterator; assignable := True; end if; end if; -- End of tricky testing -- end if; end if; exit when is_last_element (src_processors, my_processor_iterator); next_element (src_processors, my_processor_iterator); current_element (src_processors, a_processor, my_processor_iterator); end loop; if assignable then -- save task to best CPU -- current_element (src_processors, a_processor, best_cpu); a_task_result.cpu_name := a_processor.name; assignable := False; best_fit_value := 2.0; else duplicate (src_tasks, result_tasks); raise no_such_processors; end if; exit when is_last_element (result_tasks, my_task_iterator_result); next_element (result_tasks, my_task_iterator_result); current_element (result_tasks, generic_task_ptr (a_task_result), my_task_iterator_result); end loop; sort (result_tasks, increasing_name'access); end partition_best_fit; procedure validate_multiprocessor_tasks (src_processors : in processors_set; src_tasks : in tasks_set) is a_processor : generic_processor_ptr; my_processor_iterator : processors_iterator; a_core : core_unit_ptr; begin -- check if tasks are periodic -- reset_iterator (src_processors, my_processor_iterator); loop current_element (src_processors, a_processor, my_processor_iterator); periodic_control (src_tasks, a_processor.name); exit when True = is_last_element (src_processors, my_processor_iterator); next_element (src_processors, my_processor_iterator); end loop; -- check if processors are rate-monotonic and non pre-emptive -- reset_iterator (src_processors, my_processor_iterator); loop current_element (src_processors, a_processor, my_processor_iterator); a_core := get_a_core (a_processor); if a_core.scheduling.scheduler_type /= rate_monotonic_protocol then raise invalid_scheduler_multiprocessors; end if; if a_core.scheduling.preemptive_type = not_preemptive then raise invalid_scheduler_multiprocessors; end if; exit when is_last_element (src_processors, my_processor_iterator); next_element (src_processors, my_processor_iterator); end loop; end validate_multiprocessor_tasks; end partitioning_services;