Event

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
#ifndef HeterogeneousCore_AlpakaCore_interface_alpaka_Event_h
#define HeterogeneousCore_AlpakaCore_interface_alpaka_Event_h

#include "DataFormats/Common/interface/Handle.h"
#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Utilities/interface/EDGetToken.h"
#include "FWCore/Utilities/interface/EDPutToken.h"
#include "HeterogeneousCore/AlpakaCore/interface/alpaka/DeviceProductType.h"
#include "HeterogeneousCore/AlpakaCore/interface/alpaka/EDGetToken.h"
#include "HeterogeneousCore/AlpakaCore/interface/alpaka/EDMetadata.h"
#include "HeterogeneousCore/AlpakaCore/interface/alpaka/EDPutToken.h"
#include "HeterogeneousCore/AlpakaInterface/interface/config.h"

namespace ALPAKA_ACCELERATOR_NAMESPACE::device {
  /**
   * The device::Event mimics edm::Event, and provides access to
   * EDProducts in the host memory space, and in the device memory
   * space defined by the backend (i.e. ALPAKA_ACCELERATOR_NAMESPACE).
   * The device::Event also gives access to the Queue object the
   * EDModule code should use to queue all the device operations.
   *
   * Access to device memory space products is synchronized properly.
   * For backends with synchronous Queue this is trivial. For
   * asynchronous Queue, either the Queue of the EDModule is taken
   * from the first data product, or a wait is inserted into the
   * EDModule's Queue to wait for the product's asynchronous
   * production to finish.
   *
   * Note that not full interface of edm::Event is replicated here. If
   * something important is missing, that can be added.
   */
  class Event {
  public:
    // To be called in produce()
    explicit Event(edm::Event& ev, std::shared_ptr<EDMetadata> metadata)
        : constEvent_(ev), event_(&ev), metadata_(std::move(metadata)) {}

    // To be called in acquire()
    explicit Event(edm::Event const& ev, std::shared_ptr<EDMetadata> metadata)
        : constEvent_(ev), metadata_(std::move(metadata)) {}

    Event(Event const&) = delete;
    Event& operator=(Event const&) = delete;
    Event(Event&&) = delete;
    Event& operator=(Event&&) = delete;

    auto streamID() const { return constEvent_.streamID(); }
    auto id() const { return constEvent_.id(); }

    // To be able to interact with non-Alpaka helper code that needs
    // to access edm::Event
    operator edm::Event const&() const { return constEvent_; }

    Device device() const { return metadata_->device(); }

    // Alpaka operations do not accept a temporary as an argument
    // TODO: Returning non-const reference here is BAD
    Queue& queue() const {
      queueUsed_ = true;
      return metadata_->queue();
    }

    // get()

    template <typename T>
    T const& get(edm::EDGetTokenT<T> const& token) const {
      return constEvent_.get(token);
    }

    template <typename T>
    T const& get(device::EDGetToken<T> const& token) const {
      auto const& deviceProduct = constEvent_.get(token.underlyingToken());
      if constexpr (detail::useProductDirectly) {
        return deviceProduct;
      } else {
        // try to re-use queue from deviceProduct if our queue has not yet been used
        T const& product = deviceProduct.template getSynchronized<EDMetadata>(*metadata_, not queueUsed_);
        queueUsed_ = true;
        return product;
      }
    }

    // getHandle()

    template <typename T>
    edm::Handle<T> getHandle(edm::EDGetTokenT<T> const& token) const {
      return constEvent_.getHandle(token);
    }

    template <typename T>
    edm::Handle<T> getHandle(device::EDGetToken<T> const& token) const {
      auto deviceProductHandle = constEvent_.getHandle(token.underlyingToken());
      if constexpr (detail::useProductDirectly) {
        return deviceProductHandle;
      } else {
        if (not deviceProductHandle) {
          return edm::Handle<T>(deviceProductHandle.whyFailedFactory());
        }
        // try to re-use queue from deviceProduct if our queue has not yet been used
        T const& product = deviceProductHandle->getSynchronized(*metadata_, not queueUsed_);
        queueUsed_ = true;
        return edm::Handle<T>(&product, deviceProductHandle.provenance());
      }
    }

    // emplace()

    template <typename T, typename... Args>
    edm::OrphanHandle<T> emplace(edm::EDPutTokenT<T> const& token, Args&&... args) {
      return event_->emplace(token, std::forward<Args>(args)...);
    }

    // TODO: what to do about the returned OrphanHandle object?
    // The idea for Ref-like things in this domain differs from earlier Refs anyway
    template <typename T, typename... Args>
    void emplace(device::EDPutToken<T> const& token, Args&&... args) {
      if constexpr (detail::useProductDirectly) {
        event_->emplace(token.underlyingToken(), std::forward<Args>(args)...);
      } else {
        event_->emplace(token.underlyingToken(), metadata_, std::forward<Args>(args)...);
      }
    }

    // put()

    template <typename T>
    edm::OrphanHandle<T> put(edm::EDPutTokenT<T> const& token, std::unique_ptr<T> product) {
      return event_->put(token, std::move(product));
    }

    template <typename T>
    void put(device::EDPutToken<T> const& token, std::unique_ptr<T> product) {
      if constexpr (detail::useProductDirectly) {
        event_->emplace(token.underlyingToken(), std::move(*product));
      } else {
        event_->emplace(token.underlyingToken(), metadata_, std::move(*product));
      }
    }

    // implementation details
    bool wasQueueUsed() const { return queueUsed_; }

  private:
    // Having both const and non-const here in order to serve the
    // clients with one device::Event class
    edm::Event const& constEvent_;
    edm::Event* event_ = nullptr;

    std::shared_ptr<EDMetadata> metadata_;

    // device::Event is not supposed to be const-thread-safe, so no
    // additional protection is needed.
    mutable bool queueUsed_ = false;
  };
}  // namespace ALPAKA_ACCELERATOR_NAMESPACE::device

#endif