Realm
A distributed, event-based tasking library
Loading...
Searching...
No Matches
event_impl.h
Go to the documentation of this file.
1/*
2 * Copyright 2025 Stanford University, NVIDIA Corporation
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18// Event/UserEvent implementations for Realm
19
20#ifndef REALM_EVENT_IMPL_H
21#define REALM_EVENT_IMPL_H
22
23#include "realm/event.h"
24#include "realm/id.h"
25#include "realm/nodeset.h"
26#include "realm/faults.h"
27
28#include "realm/network.h"
29#include <realm/activemsg.h>
30
31#include "realm/lists.h"
32#include "realm/threads.h"
33#include "realm/logging.h"
34#include "realm/redop.h"
35#include "realm/bgwork.h"
36#include "realm/dynamic_table.h"
37
38#include <vector>
39#include <map>
40#include <memory>
41
42namespace Realm {
43
44 class GenEventImpl;
45
46 extern Logger log_poison; // defined in event_impl.cc
47 class ProcessorImpl; // defined in proc_impl.h
48
50 public:
51 virtual ~EventWaiter(void) {}
52 virtual void event_triggered(bool poisoned, TimeLimit work_until) = 0;
53 virtual void print(std::ostream &os) const = 0;
54 virtual Event get_finish_event(void) const = 0;
55
61 };
62
63 // triggering events can often result in recursive expansion of work -
64 // this widget flattens the call stack and defers excessive triggers
65 // to avoid stalling the initial triggerer longer than they want
83
84 // parent class of GenEventImpl and BarrierImpl
85 class EventImpl {
86 public:
87 typedef unsigned gen_t;
88
89 EventImpl(void);
90 virtual ~EventImpl(void);
91
92 // test whether an event has triggered without waiting
93 virtual bool has_triggered(gen_t needed_gen, bool &poisoned) = 0;
94
95 virtual void subscribe(gen_t subscribe_gen) = 0;
96
97 // causes calling thread to block until event has occurred
98 // void wait(Event::gen_t needed_gen);
99
100 virtual void external_wait(gen_t needed_gen, bool &poisoned) = 0;
101 virtual bool external_timedwait(gen_t needed_gen, bool &poisoned,
102 long long max_ns) = 0;
103
104 // helper to create the Event for an arbitrary generation
106
107 virtual bool add_waiter(gen_t needed_gen,
108 EventWaiter *waiter /*, bool pre_subscribed = false*/) = 0;
109
110 static bool add_waiter(Event needed, EventWaiter *waiter);
111
112 // use this sparingly - it has to hunt through waiter lists while
113 // holding locks
114 virtual bool remove_waiter(gen_t needed_gen, EventWaiter *waiter) = 0;
115
116 static bool detect_event_chain(Event search_from, Event target, int max_depth,
117 bool print_chain);
118
119 public:
123 };
124
125 class GenEventImpl;
126
128 public:
130 {
131 EARLY,
133 };
134
137
138 bool is_active(void) const;
139
140 void prepare_merger(Event _finish_event, bool _ignore_faults,
141 std::optional<size_t> expected_events = std::optional<size_t>(),
142 FaultPropagation _fault_propagation = FaultPropagation::EARLY);
143
144 void add_precondition(Event wait_for);
145
146 void arm_merger(void);
147
149 public:
150 MergeEventPrecondition(void) = default;
153 virtual ~MergeEventPrecondition(void) = default;
154
155 public:
157
158 virtual void event_triggered(bool poisoned, TimeLimit work_until);
159 virtual void print(std::ostream &os) const;
160 virtual Event get_finish_event(void) const;
161 };
162
163 // as an alternative to add_precondition, get_next_precondition can
164 // be used to get a precondition that can manually be added to a waiter
165 // list
167
168 protected:
169 void precondition_triggered(bool poisoned, TimeLimit work_until,
170 MergeEventPrecondition *precondition = nullptr);
171
173
182
183 static constexpr size_t MAX_INLINE_PRECONDITIONS = 6;
185 // std::deque does not invalidate references on resize
186 std::deque<MergeEventPrecondition> overflow_preconditions;
188 };
189
191 public:
192 virtual ~EventCommunicator() = default;
193
194 virtual void trigger(Event event, NodeID owner, bool poisoned);
195
196 virtual void update(Event event, NodeSet to_update,
197 span<EventImpl::gen_t> poisoned_generations);
198
199 virtual void update(Event event, NodeID to_update,
200 span<EventImpl::gen_t> poisoned_generations);
201
202 virtual void subscribe(Event event, NodeID owner,
203 EventImpl::gen_t previous_subscribe_gen);
204 };
205
206 class GenEventImpl : public EventImpl {
207 public:
209
211 GenEventImpl(EventTriggerNotifier *_event_triggerer, EventCommunicator *_event_comm);
213
214 void init(ID _me, unsigned _init_owner);
215
216 static GenEventImpl *
217 create_genevent(void); // TODO: remove this once we get rid of get_runtime()
218
220
221 static ID make_id(const GenEventImpl &dummy, int owner, ID::IDType index)
222 {
223 return ID::make_event(owner, index, 0);
224 }
225
226 // get the Event (id+generation) for the current (i.e. untriggered) generation
227 Event current_event(void) const;
228
229 // test whether an event has triggered without waiting
230 virtual bool has_triggered(gen_t needed_gen, bool &poisoned);
231
232 virtual void subscribe(gen_t subscribe_gen);
233 void handle_remote_subscription(NodeID sender, gen_t subscribe_gen,
234 gen_t previous_subscribe_gen);
235
236 virtual void external_wait(gen_t needed_gen, bool &poisoned);
237 virtual bool external_timedwait(gen_t needed_gen, bool &poisoned, long long max_ns);
238
239 virtual bool add_waiter(gen_t needed_gen, EventWaiter *waiter);
240
241 // use this sparingly - it has to hunt through waiter lists while
242 // holding locks
243 virtual bool remove_waiter(gen_t needed_gen, EventWaiter *waiter);
244
245 // creates an event that won't trigger until all input events have
246 static Event merge_events(span<const Event> wait_for, bool ignore_faults);
249 Event ev6 = Event::NO_EVENT);
250 static Event ignorefaults(Event wait_for);
251
252 // record that the event has triggered and notify anybody who cares
253 bool trigger(gen_t gen_triggered, int trigger_node, bool poisoned,
254 TimeLimit work_until);
255
256 // helper for triggering with an Event (which must be backed by a GenEventImpl)
257 static void trigger(Event e, bool poisoned);
258 static void trigger(Event e, bool poisoned, TimeLimit work_until);
259
260 // process an update message from the owner
261 void process_update(gen_t current_gen, const gen_t *new_poisoned_generations,
262 int new_poisoned_count, TimeLimit work_until);
263
264 // Set the operation that will trigger this event's generation. On success the
265 // event takes ownership of the caller's reference to 'op' and releases it when
266 // 'gen' triggers, whether or not this node owns the event (see trigger()).
267 // Returns false if 'gen' has already been observed to trigger, in which case the
268 // caller keeps its reference.
270 // Get the operation that will trigger this event's generation.
271 // The returned operation's reference is incremented and must be removed by the
272 // caller.
274
277
278 GenEventImplAllocator(void) = default;
279
283
284 void construct(GenEventImpl *storage, ID id, unsigned owner) const
285 {
286 storage->~GenEventImpl();
287 new(storage) GenEventImpl(triggerer, new EventCommunicator());
288 storage->init(id, owner);
289 }
290 };
291
292 public: // protected:
293 // these state variables are monotonic, so can be checked without a lock for
294 // early-out conditions
298 bool has_local_triggers = false;
299
300 bool is_generation_poisoned(gen_t gen) const; // helper function - linear search
301
302 // this is only manipulated when the event is "idle"
304
305 // used for merge_events and delayed UserEvent triggers
307
309 std::unique_ptr<EventCommunicator> event_comm{nullptr};
310
311 // everything below here protected by this mutex
313
314 // The operation that will trigger generation 'current_trigger_op_gen' - the event
315 // holds a reference to it (see set_trigger_op). For a remote event our view of
316 // 'generation' can lag the owner's, so the generation is tracked explicitly.
319
320 // local waiters are tracked by generation - an easily-accessed list is used
321 // for the "current" generation, whereas a map-by-generation-id is used for
322 // "future" generations (i.e. ones ahead of what we've heard about if we're
323 // not the owner)
325 std::map<gen_t, EventWaiter::EventWaiterList> future_local_waiters;
326
327 // external waiters on this node are notifies via a condition variable
329 // use kernel mutex for timedwait functionality
332
333 // remote waiters are kept in a bitmask for the current generation - this is
334 // only maintained on the owner, who never has to worry about more than one
335 // generation
337
338 // we'll set an upper bound on how many times any given event can be poisoned - this
339 // keeps update messages from growing without bound
340 static const int POISONED_GENERATION_LIMIT = 16;
341
342 // note - we don't bother sorting the list below - the overhead of a binary search
343 // dominates for short lists
344 // we also can't use an STL vector because reallocation prevents us from reading the
345 // list without the lock - instead we'll allocate the max size if/when we need
346 // any space
348
349 // local triggerings - if we're not the owner, but we've triggered/poisoned events,
350 // we need to give consistent answers for those generations, so remember what we've
351 // done until our view of the distributed event catches up
352 // value stored in map is whether generation was poisoned
353 std::map<gen_t, bool> local_triggers;
354
355 // these resolve a race condition between the early trigger of a
356 // poisoned merge and the last precondition
358 friend class EventMerger;
359 };
360}; // namespace Realm
361
362#include "realm/event_impl.inl"
363
364#endif // ifndef REALM_EVENT_IMPL_H
Definition bgwork.h:129
Definition threads.h:428
Definition event_impl.h:190
virtual void trigger(Event event, NodeID owner, bool poisoned)
virtual void update(Event event, NodeID to_update, span< EventImpl::gen_t > poisoned_generations)
virtual void subscribe(Event event, NodeID owner, EventImpl::gen_t previous_subscribe_gen)
virtual ~EventCommunicator()=default
virtual void update(Event event, NodeSet to_update, span< EventImpl::gen_t > poisoned_generations)
Definition event_impl.h:85
virtual bool remove_waiter(gen_t needed_gen, EventWaiter *waiter)=0
NodeID owner
Definition event_impl.h:122
virtual bool has_triggered(gen_t needed_gen, bool &poisoned)=0
Event make_event(gen_t gen) const
virtual void external_wait(gen_t needed_gen, bool &poisoned)=0
virtual bool external_timedwait(gen_t needed_gen, bool &poisoned, long long max_ns)=0
unsigned gen_t
Definition event_impl.h:87
virtual void subscribe(gen_t subscribe_gen)=0
static bool detect_event_chain(Event search_from, Event target, int max_depth, bool print_chain)
ProcessorImpl * owning_processor
Definition event_impl.h:121
static bool add_waiter(Event needed, EventWaiter *waiter)
ID me
Definition event_impl.h:120
virtual bool add_waiter(gen_t needed_gen, EventWaiter *waiter)=0
virtual ~EventImpl(void)
virtual void event_triggered(bool poisoned, TimeLimit work_until)
EventMerger * merger
Definition event_impl.h:156
MergeEventPrecondition(const MergeEventPrecondition &)=delete
MergeEventPrecondition(MergeEventPrecondition &&)=delete
virtual ~MergeEventPrecondition(void)=default
virtual Event get_finish_event(void) const
virtual void print(std::ostream &os) const
Definition event_impl.h:127
std::deque< MergeEventPrecondition > overflow_preconditions
Definition event_impl.h:186
static constexpr size_t MAX_INLINE_PRECONDITIONS
Definition event_impl.h:183
FaultPropagation
Definition event_impl.h:130
GenEventImpl * event_impl
Definition event_impl.h:174
bool ignore_faults
Definition event_impl.h:177
void arm_merger(void)
atomic< int > faults_observed
Definition event_impl.h:181
MergeEventPrecondition * get_next_precondition(void)
EventImpl::gen_t finish_gen
Definition event_impl.h:175
bool recycle_preconditions
Definition event_impl.h:179
void add_precondition(Event wait_for)
unsigned precondition_offset
Definition event_impl.h:176
EventMerger(GenEventImpl *_event_impl)
EventWaiter::EventWaiterList free_preconditions
Definition event_impl.h:187
atomic< int > count_needed
Definition event_impl.h:180
MergeEventPrecondition inline_preconditions[MAX_INLINE_PRECONDITIONS]
Definition event_impl.h:184
void prepare_merger(Event _finish_event, bool _ignore_faults, std::optional< size_t > expected_events=std::optional< size_t >(), FaultPropagation _fault_propagation=FaultPropagation::EARLY)
FaultPropagation fault_propagation
Definition event_impl.h:178
void precondition_triggered(bool poisoned, TimeLimit work_until, MergeEventPrecondition *precondition=nullptr)
bool is_active(void) const
Definition event_impl.h:66
Mutex mutex
Definition event_impl.h:76
virtual bool do_work(TimeLimit work_until)
static thread_local EventWaiter::EventWaiterList * nested_poisoned
Definition event_impl.h:81
static thread_local EventWaiter::EventWaiterList * nested_normal
Definition event_impl.h:80
EventWaiter::EventWaiterList delayed_poisoned
Definition event_impl.h:78
void trigger_event_waiters(EventWaiter::EventWaiterList &to_trigger, bool poisoned, TimeLimit trigger_until)
EventWaiter::EventWaiterList delayed_normal
Definition event_impl.h:77
Definition event_impl.h:49
virtual ~EventWaiter(void)
Definition event_impl.h:51
REALM_PMTA_DEFN(EventWaiter, IntrusiveListLink< EventWaiter >, ew_list_link)
IntrusiveListLink< EventWaiter > ew_list_link
Definition event_impl.h:56
virtual Event get_finish_event(void) const =0
IntrusiveList< EventWaiter, REALM_PMTA_USE(EventWaiter, ew_list_link), DummyLock > EventWaiterList
Definition event_impl.h:60
virtual void event_triggered(bool poisoned, TimeLimit work_until)=0
virtual void print(std::ostream &os) const =0
Definition event.h:50
static const Event NO_EVENT
The value should be usued to initialize an event handle. NO_EVENT is always in has triggered state .
Definition event.h:71
Definition event_impl.h:206
Operation * current_trigger_op
Definition event_impl.h:317
gen_t current_trigger_op_gen
Definition event_impl.h:318
bool is_generation_poisoned(gen_t gen) const
std::map< gen_t, bool > local_triggers
Definition event_impl.h:353
static void trigger(Event e, bool poisoned, TimeLimit work_until)
atomic< gen_t > gen_subscribed
Definition event_impl.h:296
static Event merge_events(span< const Event > wait_for, bool ignore_faults)
virtual bool add_waiter(gen_t needed_gen, EventWaiter *waiter)
bool set_trigger_op(gen_t gen, Operation *op)
void init(ID _me, unsigned _init_owner)
gen_t * poisoned_generations
Definition event_impl.h:347
NodeSet remote_waiters
Definition event_impl.h:336
static Event ignorefaults(Event wait_for)
static const ID::ID_Types ID_TYPE
Definition event_impl.h:208
KernelMutex external_waiter_mutex
Definition event_impl.h:330
std::unique_ptr< EventCommunicator > event_comm
Definition event_impl.h:309
Operation * get_trigger_op(gen_t gen)
bool trigger(gen_t gen_triggered, int trigger_node, bool poisoned, TimeLimit work_until)
void handle_remote_subscription(NodeID sender, gen_t subscribe_gen, gen_t previous_subscribe_gen)
KernelMutex::CondVar external_waiter_condvar
Definition event_impl.h:331
bool free_list_insertion_delayed
Definition event_impl.h:357
virtual bool remove_waiter(gen_t needed_gen, EventWaiter *waiter)
GenEventImpl(EventTriggerNotifier *_event_triggerer, EventCommunicator *_event_comm)
static void trigger(Event e, bool poisoned)
static Event merge_events(Event ev1, Event ev2, Event ev3=Event::NO_EVENT, Event ev4=Event::NO_EVENT, Event ev5=Event::NO_EVENT, Event ev6=Event::NO_EVENT)
virtual void subscribe(gen_t subscribe_gen)
EventTriggerNotifier * event_triggerer
Definition event_impl.h:308
EventWaiter::EventWaiterList current_local_waiters
Definition event_impl.h:324
bool has_external_waiters
Definition event_impl.h:328
Event current_event(void) const
static GenEventImpl * create_genevent(void)
EventMerger merger
Definition event_impl.h:306
virtual bool has_triggered(gen_t needed_gen, bool &poisoned)
atomic< int > num_poisoned_generations
Definition event_impl.h:297
static const int POISONED_GENERATION_LIMIT
Definition event_impl.h:340
bool has_local_triggers
Definition event_impl.h:298
static ID make_id(const GenEventImpl &dummy, int owner, ID::IDType index)
Definition event_impl.h:221
std::map< gen_t, EventWaiter::EventWaiterList > future_local_waiters
Definition event_impl.h:325
virtual bool external_timedwait(gen_t needed_gen, bool &poisoned, long long max_ns)
void process_update(gen_t current_gen, const gen_t *new_poisoned_generations, int new_poisoned_count, TimeLimit work_until)
Mutex mutex
Definition event_impl.h:312
virtual void external_wait(gen_t needed_gen, bool &poisoned)
GenEventImpl * next_free
Definition event_impl.h:303
atomic< gen_t > generation
Definition event_impl.h:295
static GenEventImpl * create_genevent(RuntimeImpl *runtime_impl)
Definition id.h:30
static ID make_event(unsigned creator_node, unsigned gen_event_idx, unsigned generation)
ID_Types
Definition id.h:250
@ ID_EVENT
Definition id.h:253
::realm_id_t IDType
Definition id.h:32
Definition lists.h:66
Definition mutex.h:359
Definition mutex.h:273
Definition nodeset.h:117
Definition operation.h:32
Definition proc_impl.h:51
Definition runtime_impl.h:267
Definition timers.h:129
Definition mutex.h:223
Definition atomics.h:31
Definition utils.h:84
#define REALM_PMTA_USE(structtype, name)
Definition lists.h:42
Definition activemsg.h:42
Logger log_poison
int NodeID
Definition nodeset.h:40
GenEventImplAllocator(EventTriggerNotifier *t)
Definition event_impl.h:280
void construct(GenEventImpl *storage, ID id, unsigned owner) const
Definition event_impl.h:284
EventTriggerNotifier * triggerer
Definition event_impl.h:276