/*
 * StarPU
 * Copyright (C) Université Bordeaux 1, CNRS 2008-2010 (see AUTHORS file)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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 Lesser General Public License in COPYING.LGPL for more details.
 */

#ifndef __JOBS_H__
#define __JOBS_H__

#include <starpu.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <pthread.h>
#include <common/config.h>
#include <common/timing.h>
#include <common/list.h>
#include <common/fxt.h>
#include <core/dependencies/tags.h>
#include <datawizard/datawizard.h>
#include <core/perfmodel/perfmodel.h>
#include <core/errorcheck.h>

#ifdef STARPU_USE_CUDA
#include <cuda.h>
#endif

struct starpu_worker_s;

/* codelet function */
typedef void (*cl_func)(void **, void *);
typedef void (*callback)(void *);

#define STARPU_CPU_MAY_PERFORM(j)	((j)->task->cl->where & STARPU_CPU)
#define STARPU_CUDA_MAY_PERFORM(j)      ((j)->task->cl->where & STARPU_CUDA)
#define STARPU_SPU_MAY_PERFORM(j)	((j)->task->cl->where & STARPU_SPU)
#define STARPU_GORDON_MAY_PERFORM(j)	((j)->task->cl->where & STARPU_GORDON)
#define STARPU_OPENCL_MAY_PERFORM(j)	((j)->task->cl->where & STARPU_OPENCL)

/* A job is the internal representation of a task. */
LIST_TYPE(starpu_job,
	/* The task associated to that job */
	struct starpu_task *task;

	/* These synchronization structures are used to wait for the job to be
	 * available or terminated for instance. */
	pthread_mutex_t sync_mutex;
	pthread_cond_t sync_cond;

	/* To avoid deadlocks, we reorder the different buffers accessed to by
	 * the task so that we always grab the rw-lock associated to the
	 * handles in the same order. */
	struct starpu_buffer_descr_t ordered_buffers[STARPU_NMAXBUFS];
	
	/* If a tag is associated to the job, this points to the internal data
	 * structure that describes the tag status. */
	struct starpu_tag_s *tag;

	/* Maintain a list of all the completion groups that depend on the job.
	 * */
	struct starpu_cg_list_s job_successors;

	/* The value of the footprint that identifies the job may be stored in
	 * this structure. */
	unsigned footprint_is_computed;
	uint32_t footprint;

	/* Indicates whether the task associated to that job has already been
	 * submitted to StarPU or not (using starpu_task_submit). */
	unsigned submitted;

	/* Indicates whether the task associated to this job is terminated or
	 * not. */
	unsigned terminated;

	/* Should that task appear in the debug tools ? (eg. the DAG generated
	 * with dot) */
        unsigned exclude_from_dag;

	/* Each job is attributed a unique id. */
	unsigned long job_id;

#ifdef STARPU_USE_FXT
	/* A symbol name may be associated to the job directly for debug
	 * purposes (for instance if the codelet is NULL). */
        const char *model_name;
#endif
	struct bound_task *bound_task;
);

/* Create an internal starpu_job_t structure to encapsulate the task. */
starpu_job_t __attribute__((malloc)) _starpu_job_create(struct starpu_task *task);

/* Destroy the data structure associated to the job structure */
void _starpu_job_destroy(starpu_job_t j);

/* Wait for the termination of the job */
void _starpu_wait_job(starpu_job_t j);

/* Specify that the task should not appear in the DAG generated by debug tools. */
void _starpu_exclude_task_from_dag(struct starpu_task *task);

/* try to submit job j, enqueue it if it's not schedulable yet */
unsigned _starpu_enforce_deps_and_schedule(starpu_job_t j, unsigned job_is_already_locked);
unsigned _starpu_enforce_deps_starting_from_task(starpu_job_t j, unsigned job_is_already_locked);


/* This function must be called after the execution of a job, this triggers all
 * job's dependencies and perform the callback function if any. */
void _starpu_handle_job_termination(starpu_job_t j, unsigned job_is_already_locked);

/* Get the sum of the size of the data accessed by the job. */
size_t _starpu_job_get_data_size(starpu_job_t j);

/* Get a task from the local pool of tasks that were explicitly attributed to
 * that worker. */
starpu_job_t _starpu_pop_local_task(struct starpu_worker_s *worker);

/* Put a task into the pool of tasks that are explicitly attributed to the
 * specified worker. */
int _starpu_push_local_task(struct starpu_worker_s *worker, starpu_job_t j);

/* Returns the symbol associated to that job if any. */
const char *_starpu_get_model_name(starpu_job_t j);

#endif // __JOBS_H__
