1//
2// Copyright 2012 Francisco Jerez
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20// OTHER DEALINGS IN THE SOFTWARE.
21//
22
23#ifndef CLOVER_CORE_EVENT_HPP
24#define CLOVER_CORE_EVENT_HPP
25
26#include <condition_variable>
27#include <functional>
28
29#include "core/object.hpp"
30#include "core/queue.hpp"
31#include "core/timestamp.hpp"
32#include "util/lazy.hpp"
33
34namespace clover {
35   ///
36   /// Class that represents a task that might be executed
37   /// asynchronously at some point in the future.
38   ///
39   /// An event consists of a list of dependencies, a boolean
40   /// signalled() flag, and an associated task.  An event is
41   /// considered signalled as soon as all its dependencies (if any)
42   /// are signalled as well, and the trigger() method is called; at
43   /// that point the associated task will be started through the
44   /// specified \a action_ok.  If the abort() method is called
45   /// instead, the specified \a action_fail is executed and the
46   /// associated task will never be started.  Dependent events will
47   /// be aborted recursively.
48   ///
49   /// The execution status of the associated task can be queried
50   /// using the status() method, and it can be waited for completion
51   /// using the wait() method.
52   ///
53   class event : public ref_counter, public _cl_event {
54   public:
55      typedef std::function<void (event &)> action;
56
57      event(clover::context &ctx, const ref_vector<event> &deps,
58            action action_ok, action action_fail);
59      virtual ~event();
60
61      event(const event &ev) = delete;
62      event &
63      operator=(const event &ev) = delete;
64
65      void trigger();
66      void abort(cl_int status);
67      bool signalled() const;
68
69      virtual cl_int status() const;
70      virtual command_queue *queue() const = 0;
71      virtual cl_command_type command() const = 0;
72      virtual void wait() const;
73
74      virtual struct pipe_fence_handle *fence() const {
75         return NULL;
76      }
77
78      const intrusive_ref<clover::context> context;
79
80   protected:
81      void chain(event &ev);
82
83      std::vector<intrusive_ref<event>> deps;
84
85   private:
86      std::vector<intrusive_ref<event>> trigger_self();
87      std::vector<intrusive_ref<event>> abort_self(cl_int status);
88
89      unsigned wait_count;
90      cl_int _status;
91      action action_ok;
92      action action_fail;
93      std::vector<intrusive_ref<event>> _chain;
94      mutable std::condition_variable cv;
95      mutable std::mutex mutex;
96   };
97
98   ///
99   /// Class that represents a task executed by a command queue.
100   ///
101   /// Similar to a normal clover::event.  In addition it's associated
102   /// with a given command queue \a q and a given OpenCL \a command.
103   /// hard_event instances created for the same queue are implicitly
104   /// ordered with respect to each other, and they are implicitly
105   /// triggered on construction.
106   ///
107   /// A hard_event is considered complete when the associated
108   /// hardware task finishes execution.
109   ///
110   class hard_event : public event {
111   public:
112      hard_event(command_queue &q, cl_command_type command,
113                 const ref_vector<event> &deps,
114                 action action = [](event &){});
115      ~hard_event();
116
117      virtual cl_int status() const;
118      virtual command_queue *queue() const;
119      virtual cl_command_type command() const;
120      virtual void wait() const;
121
122      const lazy<cl_ulong> &time_queued() const;
123      const lazy<cl_ulong> &time_submit() const;
124      const lazy<cl_ulong> &time_start() const;
125      const lazy<cl_ulong> &time_end() const;
126
127      friend class command_queue;
128
129      virtual struct pipe_fence_handle *fence() const {
130         return _fence;
131      }
132
133   private:
134      virtual void fence(pipe_fence_handle *fence);
135      action profile(command_queue &q, const action &action) const;
136
137      const intrusive_ref<command_queue> _queue;
138      cl_command_type _command;
139      pipe_fence_handle *_fence;
140      lazy<cl_ulong> _time_queued, _time_submit, _time_start, _time_end;
141   };
142
143   ///
144   /// Class that represents a software event.
145   ///
146   /// A soft_event is not associated with any specific hardware task
147   /// or command queue.  It's considered complete as soon as all its
148   /// dependencies finish execution.
149   ///
150   class soft_event : public event {
151   public:
152      soft_event(clover::context &ctx, const ref_vector<event> &deps,
153                 bool trigger, action action = [](event &){});
154
155      virtual cl_int status() const;
156      virtual command_queue *queue() const;
157      virtual cl_command_type command() const;
158      virtual void wait() const;
159   };
160}
161
162#endif
163