+ /* register the candidate */
+ candidates[candidate_nb++] = candidate;
+ candidate->fifo_checked = 0;
+ }
+ }
+ }
+
+ DEBUG1("Candidates found: %d", candidate_nb);
+
+ /* now we check every candidate task */
+ for (i = 0; i < candidate_nb; i++) {
+ candidate = candidates[i];
+
+ if (candidate->fifo_checked) {
+ continue; /* we have already evaluated that task */
+ }
+
+ xbt_assert2(__SD_task_is_in_fifo(candidate),
+ "Bad state of candidate '%s': %d",
+ SD_task_get_name(candidate), SD_task_get_state(candidate));
+
+ for (j = 0; j < candidate->workstation_nb && can_start; j++) {
+ workstation = candidate->workstation_list[j];
+
+ /* I can start on this workstation if the workstation is shared
+ or if I am the first task in the fifo */
+ can_start = workstation->access_mode == SD_WORKSTATION_SHARED_ACCESS ||
+ candidate ==
+ xbt_fifo_get_item_content(xbt_fifo_get_first_item
+ (workstation->task_fifo));
+ }
+
+ DEBUG2("Candidate '%s' can start: %d", SD_task_get_name(candidate),
+ can_start);
+
+ /* now we are sure that I can start! */
+ if (can_start) {
+ for (j = 0; j < candidate->workstation_nb && can_start; j++) {
+ workstation = candidate->workstation_list[j];
+
+ /* update the fifo */
+ if (workstation->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
+ candidate = xbt_fifo_shift(workstation->task_fifo); /* the return value is stored just for debugging */
+ DEBUG1("Head of the fifo: '%s'",
+ (candidate != NULL) ? SD_task_get_name(candidate) : "NULL");
+ xbt_assert0(candidate == candidates[i],
+ "Error in __SD_task_just_done: bad first task in the fifo");
+ }
+ } /* for each workstation */
+
+ /* finally execute the task */
+ DEBUG2("Task '%s' state: %d", SD_task_get_name(candidate),
+ SD_task_get_state(candidate));
+ __SD_task_really_run(candidate);
+
+ DEBUG4
+ ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
+ SD_task_get_name(candidate), candidate->state_set,
+ sd_global->running_task_set, __SD_task_is_running(candidate));
+ xbt_assert2(__SD_task_is_running(candidate),
+ "Bad state of task '%s': %d", SD_task_get_name(candidate),
+ SD_task_get_state(candidate));
+ DEBUG0("Okay, the task is running.");
+
+ } /* can start */
+ candidate->fifo_checked = 1;
+ } /* for each candidate */
+
+ xbt_free(candidates);
+}
+
+/* Remove all dependencies associated with a task. This function is called when the task is destroyed.
+ */
+static void __SD_task_remove_dependencies(SD_task_t task)
+{
+ /* we must destroy the dependencies carefuly (with SD_dependency_remove)
+ because each one is stored twice */
+ SD_dependency_t dependency;
+ while (xbt_dynar_length(task->tasks_before) > 0) {
+ xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
+ SD_task_dependency_remove(dependency->src, dependency->dst);
+ }
+
+ while (xbt_dynar_length(task->tasks_after) > 0) {
+ xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
+ SD_task_dependency_remove(dependency->src, dependency->dst);
+ }
+}
+
+/**
+ * \brief Returns the start time of a task
+ *
+ * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
+ *
+ * \param task: a task
+ * \return the start time of this task
+ */
+double SD_task_get_start_time(SD_task_t task)
+{
+ SD_CHECK_INIT_DONE();
+ xbt_assert0(task != NULL, "Invalid parameter");
+ if (task->surf_action)
+ return surf_workstation_model->action_get_start_time(task->surf_action);
+ else
+ return task->start_time;
+}
+
+/**
+ * \brief Returns the finish time of a task
+ *
+ * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
+ * If the state is not completed yet, the returned value is an
+ * estimation of the task finish time. This value can fluctuate
+ * until the task is completed.
+ *
+ * \param task: a task
+ * \return the start time of this task
+ */
+double SD_task_get_finish_time(SD_task_t task)
+{
+ SD_CHECK_INIT_DONE();
+ xbt_assert0(task != NULL, "Invalid parameter");
+
+ if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
+ return surf_workstation_model->action_get_finish_time(task->surf_action);
+ else
+ return task->finish_time;
+}
+
+/**
+ * \brief Destroys a task.
+ *
+ * The user data (if any) should have been destroyed first.
+ *
+ * \param task the task you want to destroy
+ * \see SD_task_create()
+ */
+void SD_task_destroy(SD_task_t task)
+{
+ SD_CHECK_INIT_DONE();
+ xbt_assert0(task != NULL, "Invalid parameter");
+
+ DEBUG1("Destroying task %s...", SD_task_get_name(task));
+
+ __SD_task_remove_dependencies(task);
+
+ /* if the task was scheduled or ready we have to free the scheduling parameters */
+ if (__SD_task_is_scheduled_or_ready(task))
+ __SD_task_destroy_scheduling_data(task);
+
+ if (task->name != NULL)
+ xbt_free(task->name);
+
+ if (task->surf_action != NULL)
+ surf_workstation_model->action_unref(task->surf_action);
+
+ if (task->workstation_list != NULL)
+ xbt_free(task->workstation_list);
+
+ xbt_dynar_free(&task->tasks_before);
+ xbt_dynar_free(&task->tasks_after);
+ xbt_free(task);
+
+ sd_global->task_number--;
+
+ DEBUG0("Task destroyed.");
+}
+
+
+static inline SD_task_t SD_task_create_sized(const char*name,void*data,double amount,int ws_count) {
+ SD_task_t task = SD_task_create(name,data,amount);
+ task->communication_amount = xbt_new0(double,ws_count*ws_count);
+ task->computation_amount = xbt_new0(double,ws_count);
+ task->workstation_nb = ws_count;
+ task->workstation_list = xbt_new0(SD_workstation_t,ws_count);
+ return task;
+}
+/** @brief create a end-to-end communication task that can then be auto-scheduled
+ *
+ * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
+ * allows to specify the task costs at creation, and decorelate them from the
+ * scheduling process where you just specify which resource should deliver the
+ * mandatory power.
+ *
+ * A end-to-end communication must be scheduled on 2 hosts, and the amount
+ * specified at creation is sent from hosts[0] to hosts[1].
+ */
+SD_task_t SD_task_create_comm_e2e(const char*name, void *data, double amount) {
+ SD_task_t res = SD_task_create_sized(name,data,0,2);
+ res->communication_amount[1] = amount;
+ res->kind=SD_TASK_COMM_E2E;
+ return res;
+}
+/** @brief create a sequential computation task that can then be auto-scheduled
+ *
+ * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
+ * allows to specify the task costs at creation, and decorelate them from the
+ * scheduling process where you just specify which resource should deliver the
+ * mandatory power.
+ *
+ * A sequential computation must be scheduled on 1 host, and the amount
+ * specified at creation to be run on hosts[0].
+ */
+SD_task_t SD_task_create_comp_seq(const char*name, void *data, double amount) {
+ SD_task_t res = SD_task_create_sized(name,data,0,1);
+ res->computation_amount[0]=amount;
+ res->kind=SD_TASK_COMP_SEQ;
+ return res;
+}
+
+/** @brief Auto-schedules a task.
+ *
+ * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
+ * allows to specify the task costs at creation, and decorelate them from the
+ * scheduling process where you just specify which resource should deliver the
+ * mandatory power.
+ *
+ * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
+ * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
+ * of them.
+ *
+ * @todo
+ * We should create tasks kind for the following categories:
+ * - Point to point communication (done)
+ * - Sequential computation (done)
+ * - group communication (redistribution, several kinds)
+ * - parallel tasks with no internal communication (one kind per speedup model such as amdal)
+ * - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
+ */
+void SD_task_schedulev(SD_task_t task, int count, const SD_workstation_t*list) {
+ int i;
+ xbt_assert1(task->kind != 0,"Task %s is not typed. Cannot automatically schedule it.",SD_task_get_name(task));
+ switch(task->kind) {
+ case SD_TASK_COMM_E2E:
+ case SD_TASK_COMP_SEQ:
+ xbt_assert(task->workstation_nb==count);
+ for (i=0;i<count;i++)
+ task->workstation_list[i]=list[i];
+ SD_task_do_schedule(task);
+ break;
+ default:
+ xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
+ SD_task_get_name(task)));
+ }
+ /* Iterate over all childs and parent being COMM_E2E to say where I am located (and start them if ready) */
+ if (task->kind == SD_TASK_COMP_SEQ) {
+ SD_dependency_t dep;
+ unsigned int cpt;
+ xbt_dynar_foreach(task->tasks_before,cpt,dep) {
+ SD_task_t before = dep->src;
+ if (before->kind == SD_TASK_COMM_E2E) {
+ before->workstation_list[1] = task->workstation_list[0];
+ if (before->workstation_list[0])
+ SD_task_do_schedule(before);
+ }
+ }
+ xbt_dynar_foreach(task->tasks_after,cpt,dep) {
+ SD_task_t after = dep->dst;
+ if (after->kind == SD_TASK_COMM_E2E) {
+ after->workstation_list[0] = task->workstation_list[0];
+ if (after->workstation_list[1])
+ SD_task_do_schedule(after);
+ }
+ }
+
+ }
+}
+/** @brief autoschedule a task on a list of workstations
+ *
+ * This function is very similar to SD_task_schedulev(),
+ * but takes the list of workstations to schedule onto as separate parameters.
+ * It builds a proper vector of workstations and then call SD_task_schedulev()
+ */
+void SD_task_schedulel(SD_task_t task, int count, ...) {
+ va_list ap;
+ SD_workstation_t *list=xbt_new(SD_workstation_t,count);
+ int i;
+ va_start(ap,count);
+ for (i=0;i<count;i++) {
+ list[i] = va_arg(ap,SD_workstation_t);
+ }
+ va_end(ap);
+ SD_task_schedulev(task,count,list);
+ free(list);