/* __ompc_remove_task_from_pool_default:
 *Takes task from the task pool.
 *
 * Takes a task from the task pool. First tries to get a task from the current
 * thread's task queue. If that doesn't work, then it will attempt to steal a
 * task from another task queue (so long as there are no other tasks, not in a
 * barrier, that are tied to the current thread).
 */
omp_task_t *__ompc_remove_task_from_pool_default(omp_task_pool_t *pool)
{
  omp_task_t *task, *current_task;
  omp_team_t *team;
  omp_v_thread_t *current_thread;
  omp_queue_t *my_queue;
  omp_queue_t *victim_queue;
  omp_task_queue_level_t *per_thread;
  int myid = __omp_myid;

  Is_True(pool != NULL, ("__ompc_remove_task_from_pool: task pool is uninitialized"));

  current_task = __omp_current_task;
  current_thread = __omp_current_v_thread;
  per_thread = &pool->level[PER_THREAD];

  /* We get only from the tail for tied tasks. This is necessary to guarantee
   * that tied tasks are only scheduled if they are descendants of every
   * suspended tied task not at a barrier */
  task = __ompc_queue_get_tail(&per_thread->task_queue[TIED_IDX(myid)]);

  /* for untied tasks, we can get from the head or tail, depending on what
   * O64_OMP_TASK_QUEUE is set to */
  if (task == NULL)
    task = __ompc_task_queue_get(&per_thread->task_queue[UNTIED_IDX(myid)]);


  /* check if there are any untied tasks available in the other task queues */
  if (task == NULL) {
    int first_victim, victim = 0;
    int team_size = pool->team_size;

    if (team_size < 2)
      return NULL;

    victim = (rand_r(&__omp_seed) % (team_size - 1));
    if (victim >= myid) victim++;
    /* cycle through to find a queue with work to steal */
    first_victim = victim;
    while (1) {
      while (__ompc_queue_lockless_is_empty(
                     &per_thread->task_queue[UNTIED_IDX(victim)])) {
        victim++;
        if (victim == myid)
          victim++;
        if (victim == team_size)
          victim = 0;
        if (victim == first_victim)
         goto CHECK_TIED_TASK_QUEUES;
      }
      task = __ompc_task_queue_steal(
                     &per_thread->task_queue[UNTIED_IDX(victim)]);
      if ( task != NULL ) {
        /*
        if (!__ompc_task_state_is_unscheduled(task)) {
          // Is_True(0, ("state of task from queue was not unscheduled"));
          printf("\n... (1) skipping over a task with state %s; queue size is %d \n",
                  __ompc_task_get_state_string(task),
         __ompc_queue_num_used_slots(&per_thread->task_queue[UNTIED_IDX(victim)]));
          task = NULL;
        }
        */
        return task;
      }
    }
  }

  /* if no task in local queue and no available untied tasks, we will look in
   * another queue so long as there are no suspended tasks tied to thread and
   * the current task is either in a barrier or its not tied
   */
CHECK_TIED_TASK_QUEUES:
  if (task == NULL && !current_thread->num_suspended_tied_tasks &&
      (__ompc_task_state_is_in_barrier(current_task) ||
       !__ompc_task_is_tied(current_task))) {
    int first_victim, victim = 0;
    int team_size = pool->team_size;

    victim = (rand_r(&__omp_seed) % (team_size - 1));
    if (victim >= myid) victim++;
    /* cycle through to find a queue with work to steal */
    first_victim = victim;
    while (1) {
      while (__ompc_queue_is_empty(
                         &per_thread->task_queue[TIED_IDX(victim)])) {
        victim++;
        if (victim == myid)
          victim++;
        if (victim == team_size)
          victim = 0;
        if (victim == first_victim)
          return NULL;
      }
      /* Always steal from the head for tied tasks. Note also that by not
       * using the task_queue API, CFIFO implementation will not be used */
      task = __ompc_queue_steal_head(
                         &per_thread->task_queue[TIED_IDX(victim)]);
      if ( task != NULL ) {
        /*
        if (!__ompc_task_state_is_unscheduled(task)) {
          // Is_True(0, ("state of task from queue was not unscheduled"));
          printf("\n... (2) skipping over a task with state %s; queue size is %d \n",
                  __ompc_task_get_state_string(task),
         __ompc_queue_num_used_slots(&per_thread->task_queue[TIED_IDX(victim)]));
          task = NULL;
        }
        */
        return task;
      }
    }
  }

  /*
  if ( task != NULL ) {
    if (!__ompc_task_state_is_unscheduled(task)) {
      // Is_True(0, ("state of task from queue was not unscheduled"));
      printf("\n... (3) skipping over a task with state %s; queue size is %d \n",
          __ompc_task_get_state_string(task),
          __ompc_queue_num_used_slots(&per_thread->task_queue[UNTIED_IDX(myid)]));
      task = NULL;
    }
  }
  */
  return task;
}
/* __ompc_remove_task_from_pool_simple_2level:
 * Takes a task from the task pool. First tries to get a task from the current
 * thread's task queue. If that doesn't work, then it will look for work in
 * the community queue. If that's also empty, then it will attempt to steal a
 * task from another task queue (so long as there are no other tasks, not in a
 * barrier, that are tied to the current thread).
 *
 * Note: The restriction on stealing is overly conservative. Even if there are
 * tasks tied to the current thread and not in a barrier ([*]), we should be
 * able to steal any untied tasks, or tied tasks that descend from all tasks
 * in [*]. But this implementation does not separate untied tasks from tied
 * tasks, and also does not track descendants in the task pool.
 */
omp_task_t *__ompc_remove_task_from_pool_simple_2level(omp_task_pool_t *pool)
{
  omp_task_t *task, *current_task;
  omp_team_t *team;
  omp_v_thread_t *current_thread;
  omp_queue_t *my_queue;
  omp_queue_t *victim_queue;
  omp_task_queue_level_t *per_thread, *community;
  int myid = __omp_myid;

  Is_True(pool != NULL, ("__ompc_remove_task_from_pool: task pool is uninitialized"));

  current_task = __omp_current_task;
  current_thread = __omp_current_v_thread;
  per_thread = &pool->level[PER_THREAD];
  community = &pool->level[COMMUNITY];

  task = __ompc_task_queue_get(&per_thread->task_queue[myid]);

  /* if no task in local queue, we will look in another queue so long as there
   * are no suspended tasks tied to thread and the current task is either in a
   * barrier or its not tied
   */
  if (task == NULL && !current_thread->num_suspended_tied_tasks &&
      (__ompc_task_state_is_in_barrier(current_task) ||
       !__ompc_task_is_tied(current_task))) {

    if (__omp_task_chunk_size > 1) {
      /* this will steal a chunk of tasks, instead of just 1, from the
       * community
       * queue */
      task = __ompc_task_queue_steal_chunk(community->task_queue,
          &per_thread->task_queue[myid],
          __omp_task_chunk_size);
    } else {
      task = __ompc_task_queue_steal(community->task_queue);
    }

    if (task == NULL) {
      int first_victim, victim = 0;
      int team_size = pool->team_size;
      victim = (rand_r(&__omp_seed) % (team_size - 1));
      if (victim >= myid) victim++;
      /* cycle through to find a queue with work to steal */
      first_victim = victim;
      while (1) {
        while (__ompc_queue_is_empty(&per_thread->task_queue[victim])) {
          victim++;
          if (victim == myid)
            victim++;
          if (victim == team_size)
            victim = 0;
          if (victim == first_victim)
            return NULL;
        }
        task = __ompc_task_queue_steal(&per_thread->task_queue[victim]);
        if ( task != NULL ) return task;
      }
    }
  }

  return task;
}
void __ompc_task_switch(omp_task_t *new_task)
{
  omp_v_thread_t *current_thread = __omp_current_v_thread;
  omp_task_t *orig_task = __omp_current_task;

  __ompc_task_set_state(new_task, OMP_TASK_RUNNING);
  __omp_current_task = new_task;
  new_task->sdepth = orig_task->sdepth + 1;

#ifdef OMPT
  __ompt_suspended_task_id = orig_task->task_id;
  __ompt_resumed_task_id = new_task->task_id;
  __ompt_event_callback(ompt_event_task_switch);
#endif

#ifdef USE_COLLECTOR_TASK
  __omp_collector_task = __omp_current_task;
  omp_v_thread_t *p_vthread = __ompc_get_v_thread_by_num( __omp_myid);
  OMP_COLLECTOR_API_THR_STATE temp_state =
      (OMP_COLLECTOR_API_THR_STATE)p_vthread->state;
  __ompc_ompt_set_state(THR_WORK_STATE, ompt_state_work_parallel, 0);
  __ompc_event_callback(OMP_EVENT_THR_BEGIN_EXEC_TASK);
#endif


#ifdef OMPT
  new_task->frame_s.exit_runtime_frame = __builtin_frame_address(0);

  if(__ompc_task_is_implicit(new_task))
	  __ompt_event_callback(ompt_event_implicit_task_begin);
#endif

  if (__ompc_task_is_tied(orig_task) &&
      !__ompc_task_state_is_in_barrier(orig_task)) {
    ++(current_thread->num_suspended_tied_tasks);
    new_task->t.func(new_task->firstprivates, new_task->frame_pointer);
    --(current_thread->num_suspended_tied_tasks);
  } else {
    new_task->t.func(new_task->firstprivates, new_task->frame_pointer);
  }

#ifdef OMPT
  new_task->frame_s.reenter_runtime_frame = __builtin_frame_address(0);

  if(__ompc_task_is_implicit(new_task))
	  __ompt_event_callback(ompt_event_implicit_task_end);
#endif


#ifdef USE_COLLECTOR_TASK
  __ompc_set_state(temp_state);
#endif
  Is_True(__ompc_task_state_is_exiting(new_task),
      ("__ompc_task_switch: task returned but not in EXITING state"));

  if (new_task->num_children == 0) {
    __ompc_task_delete(new_task);
  } else {
    __ompc_task_set_state(new_task, OMP_TASK_FINISHED);
    __ompc_unlock(&new_task->lock);
  }

  __omp_current_task = orig_task;
#ifdef USE_COLLECTOR_TASK
  __omp_collector_task = __omp_current_task;
#endif
}
/* __ompc_remove_task_from_pool_simple:
 * Takes a task from the task pool. First tries to get a task from the current
 * thread's task queue. If that doesn't work, then it will attempt to steal a
 * task from another task queue (so long as there are no other tasks, not in a
 * barrier, that are tied to the current thread).
 *
 * Note: The restriction on stealing is overly conservative. Even if there are
 * tasks tied to the current thread and not in a barrier ([*]), we should be
 * able to steal any untied tasks, or tied tasks that descend from all tasks
 * in [*]. But this implementation does not separate untied tasks from tied
 * tasks, and also does not track descendants in the task pool.
 */
omp_task_t *__ompc_remove_task_from_pool_simple(omp_task_pool_t *pool)
{
  omp_task_t *task, *current_task;
  omp_team_t *team;
  omp_v_thread_t *current_thread;
  omp_queue_t *my_queue;
  omp_queue_t *victim_queue;
  omp_task_queue_level_t *per_thread;
  int myid = __omp_myid;
  int team_size;

  Is_True(pool != NULL, ("__ompc_remove_task_from_pool: task pool is uninitialized"));

  current_task = __omp_current_task;
  current_thread = __omp_current_v_thread;
  per_thread = &pool->level[PER_THREAD];

  task = __ompc_task_queue_get(&per_thread->task_queue[myid]);

  /* if no task in local queue, we will look in another queue so long as there
   * are no suspended tasks tied to thread and the current task is either in a
   * barrier or its not tied
   */
  team_size = pool->team_size;
  if (task == NULL && (team_size > 1) &&
      !current_thread->num_suspended_tied_tasks &&
      (__ompc_task_state_is_in_barrier(current_task) ||
       !__ompc_task_is_tied(current_task))) {
    int first_victim, victim = 0, go_right;
    go_right = (rand_r(&__omp_seed) % (2));
    victim = (rand_r(&__omp_seed) % (team_size - 1));
    if (victim >= myid) victim++;
    first_victim = victim;
    if (go_right) {
      /* cycle through to find a queue with work to steal */
      while (1) {
        while (__ompc_queue_is_empty(&per_thread->task_queue[victim])) {
          victim++;
          if (victim == myid)
            victim++;
          if (victim == team_size)
            victim = 0;
          if (victim == first_victim)
            return NULL;
        }
        task = __ompc_task_queue_steal(&per_thread->task_queue[victim]);
        if ( task != NULL ) return task;
      }
    } else {
      /* cycle through to find a queue with work to steal */
      while (1) {
        while (__ompc_queue_is_empty(&per_thread->task_queue[victim])) {
          victim--;
          if (victim == myid)
            victim--;
          if (victim == -1)
            victim = team_size-1;
          if (victim == first_victim)
            return NULL;
        }
        task = __ompc_task_queue_steal(&per_thread->task_queue[victim]);
        if ( task != NULL ) return task;
      }
    }
  }

  return task;
}