Program Listing for File resource.hpp

Return to documentation for file (camp/resource.hpp)

/*
Copyright (c) 2016-18, Lawrence Livermore National Security, LLC.
Produced at the Lawrence Livermore National Laboratory
Maintained by Tom Scogland <scogland1@llnl.gov>
CODE-756261, All rights reserved.
This file is part of camp.
For details about use and distribution, please read LICENSE and NOTICE from
http://github.com/llnl/camp
*/

#ifndef __CAMP_RESOURCE_HPP
#define __CAMP_RESOURCE_HPP

#include <cstring>
#include <memory>
#include <mutex>
#include <type_traits>

#include "camp/helpers.hpp"
#include "camp/resource/event.hpp"
#include "camp/resource/host.hpp"

#if defined(CAMP_HAVE_CUDA)
#include "camp/resource/cuda.hpp"
#endif
#if defined(CAMP_HAVE_HIP)
#include "camp/resource/hip.hpp"
#endif
#if defined(CAMP_HAVE_SYCL)
#include "camp/resource/sycl.hpp"
#endif

#if defined(CAMP_HAVE_OMP_OFFLOAD)
#include "camp/resource/omp_target.hpp"
#endif

// last to ensure we don't hide breakage in the others
#include "camp/resource/platform.hpp"

namespace camp
{
namespace resources
{
  inline namespace v1
  {
    class Resource
    {
    public:
      Resource(Resource &&) = default;
      Resource(Resource const &) = default;
      Resource &operator=(Resource &&) = default;
      Resource &operator=(Resource const &) = default;
      template <typename T,
                typename = typename std::enable_if<
                    !std::is_same<typename std::decay<T>::type,
                                  Resource>::value>::type>
      Resource(T &&value)
      {
        m_value.reset(new ContextModel<type::ref::rem<T>>(forward<T>(value)));
      }
      template <typename T>
      T *try_get()
      {
        auto result = dynamic_cast<ContextModel<T> *>(m_value.get());
        return result ? result->get() : nullptr;
      }
      template <typename T>
      T get()
      {
        auto result = dynamic_cast<ContextModel<T> *>(m_value.get());
        if (result == nullptr) {
          ::camp::throw_re("Incompatible Resource type get cast.");
        }
        return *result->get();
      }
      Platform get_platform() const { return m_value->get_platform(); }
      template <typename T>
      T *allocate(size_t size, MemoryAccess ma = MemoryAccess::Device)
      {
        return (T *)m_value->allocate(size * sizeof(T), ma);
      }
      void *calloc(size_t size, MemoryAccess ma = MemoryAccess::Device) { return m_value->calloc(size, ma); }
      void deallocate(void *p, MemoryAccess ma = MemoryAccess::Device) { m_value->deallocate(p, ma); }
      void memcpy(void *dst, const void *src, size_t size)
      {
        m_value->memcpy(dst, src, size);
      }
      void memset(void *p, int val, size_t size)
      {
        m_value->memset(p, val, size);
      }
      Event get_event() { return m_value->get_event(); }
      Event get_event_erased() { return m_value->get_event_erased(); }
      void wait_for(Event *e) { m_value->wait_for(e); }
      void wait() { m_value->wait(); }

    private:
      class ContextInterface
      {
      public:
        virtual ~ContextInterface() {}
        virtual Platform get_platform() const = 0;
        virtual void *allocate(size_t size, MemoryAccess ma = MemoryAccess::Device) = 0;
        virtual void *calloc(size_t size, MemoryAccess ma = MemoryAccess::Device) = 0;
        virtual void deallocate(void *p, MemoryAccess ma = MemoryAccess::Device) = 0;
        virtual void memcpy(void *dst, const void *src, size_t size) = 0;
        virtual void memset(void *p, int val, size_t size) = 0;
        virtual Event get_event() = 0;
        virtual Event get_event_erased() = 0;
        virtual void wait_for(Event *e) = 0;
        virtual void wait() = 0;
      };

      template <typename T>
      class ContextModel : public ContextInterface
      {
      public:
        ContextModel(T const &modelVal) : m_modelVal(modelVal) {}
        Platform get_platform() const override { return m_modelVal.get_platform(); }
        void *allocate(size_t size, MemoryAccess ma = MemoryAccess::Device) override { return m_modelVal.template allocate<char>(size, ma); }
        void *calloc(size_t size, MemoryAccess ma = MemoryAccess::Device) override { return m_modelVal.calloc(size, ma); }
        void deallocate(void *p, MemoryAccess ma = MemoryAccess::Device) override { m_modelVal.deallocate(p, ma); }
        void memcpy(void *dst, const void *src, size_t size) override
        {
          m_modelVal.memcpy(dst, src, size);
        }
        void memset(void *p, int val, size_t size) override
        {
          m_modelVal.memset(p, val, size);
        }
        Event get_event() override { return m_modelVal.get_event_erased(); }
        Event get_event_erased() override
        {
          return m_modelVal.get_event_erased();
        }
        void wait_for(Event *e) override { m_modelVal.wait_for(e); }
        void wait() override { m_modelVal.wait(); }
        T *get() { return &m_modelVal; }

      private:
        T m_modelVal;
      };

      std::shared_ptr<ContextInterface> m_value;
    };

    template <Platform p>
    struct resource_from_platform;
    template <>
    struct resource_from_platform<Platform::host> {
      using type = ::camp::resources::Host;
    };
#if defined(CAMP_HAVE_CUDA)
    template <>
    struct resource_from_platform<Platform::cuda> {
      using type = ::camp::resources::Cuda;
    };
#endif
#if defined(CAMP_HAVE_HIP)
    template <>
    struct resource_from_platform<Platform::hip> {
      using type = ::camp::resources::Hip;
    };
#endif
#if defined(CAMP_HAVE_SYCL)
    template <>
    struct resource_from_platform<Platform::sycl> {
      using type = ::camp::resources::Sycl;
    };
#endif
#if defined(CAMP_HAVE_OMP_OFFLOAD)
    template <>
    struct resource_from_platform<Platform::omp_target> {
      using type = ::camp::resources::Omp;
    };
#endif

    namespace detail
    {
      template <typename Res>
      using get_event_type =
          typename std::decay<decltype(std::declval<Res>().get_event())>::type;

      template <typename T>
      using is_erased_resource_or_proxy =
          typename std::is_same<get_event_type<T>, Event>::type;
    }  // namespace detail

    template <typename Res>
    struct EventProxy : ::camp::resources::detail::EventProxyBase {
      using native_event = ::camp::resources::detail::get_event_type<Res>;

      EventProxy(EventProxy &&) = default;
      EventProxy(EventProxy const &) = delete;
      EventProxy &operator=(EventProxy &&) = default;
      EventProxy &operator=(EventProxy const &) = delete;

      EventProxy(Res r) : resource_{move(r)} {}

      template <typename T = Res>
      typename std::enable_if<!detail::is_erased_resource_or_proxy<T>::value,
                              native_event>::type
      get()
      {
        return resource_.get_event();
      }

      template <typename T = Res>
      typename std::enable_if<detail::is_erased_resource_or_proxy<T>::value,
                              Event>::type
      get()
      {
        return resource_.get_event_erased();
      }

      template <typename T = Res>
      operator typename std::enable_if<
          !detail::is_erased_resource_or_proxy<T>::value,
          native_event>::type()
      {
        return resource_.get_event();
      }

      operator Event() { return resource_.get_event_erased(); }

      Res resource_;
    };

  }  // namespace v1
}  // namespace resources
}  // namespace camp
#endif /* __CAMP_RESOURCE_HPP */