/*!
  \file
  File containing a tasks which allocate and free pinned memory in CUDA
*/

#ifndef SCIGPU_LEGION_CUDAPINNEDMEMORYTASK_HPP
#define SCIGPU_LEGION_CUDAPINNEDMEMORYTASK_HPP


#include "cudatask.hpp"
#include <cuda_runtime.h>

namespace SciGPU {
  namespace Legion {

    //! Task to allocate pinned host memory in CUDA
    class CUDAhostAllocTask : public CUDAtask {

    public:
      //! Constructor passes tag through to base Task
      CUDAhostAllocTask( void** dstPtr = NULL,
			 const size_t& nBytes=0,
			 const TagType _tag = 0,
			 const boost::thread::id& _gpu = boost::thread::id() ) : CUDAtask(_tag,_gpu),
										 ptr(dstPtr),
										 bytes(nBytes) {} ;

      //! Destructor
      virtual ~CUDAhostAllocTask( void ) {};

      //! Pointer we will allocate
      void** ptr;
      //! Number of bytes to allocate
      size_t bytes;

      //! The method to do the allocation
      virtual bool Perform( void ) {

	unsigned int allocFlags;

	// Check for specific CUDA context
	if( this->tid != boost::thread::id() ) {
	  // Only matching Maniple will have picked this Task up
	  allocFlags = cudaHostAllocDefault;
	} else {
	  // Make usable by any context
	  allocFlags = cudaHostAllocPortable;
	}

	cudaError_t err = cudaHostAlloc( this->ptr, this->bytes, allocFlags );
	

	return( err == cudaSuccess );
      }

    };

    //! Task to release pinned host memory in CUDA
    class CUDAhostFreeTask : public CUDAtask {

    public:
      //! Constructor
      CUDAhostFreeTask( void** _ptr = NULL,
			const TagType _tag = 0,
			const boost::thread::id& _gpu = boost::thread::id() ) : CUDAtask(_tag,_gpu),
										ptr(_ptr) {} ;

      //! Destructor
      virtual ~CUDAhostFreeTask( void ) {} ;

      //! Pointer we will release
      void** ptr;

      //! Method to perform the release
      virtual bool Perform( void ) {

	// The thread IDs will already match
	cudaError_t err = cudaFreeHost( *(this->ptr) );
	
	*(this->ptr) = NULL;

	return( err == cudaSuccess );
      }

    };


  }
}



#endif
