1414#define LLVM_EXECUTIONENGINE_ORC_TASKDISPATCH_H
1515
1616#include " llvm/Config/llvm-config.h"
17+ #include " llvm/ADT/BitmaskEnum.h"
1718#include " llvm/Support/Compiler.h"
1819#include " llvm/Support/Debug.h"
1920#include " llvm/Support/ErrorHandling.h"
@@ -115,7 +116,7 @@ class LLVM_ABI TaskDispatcher {
115116public:
116117 virtual ~TaskDispatcher ();
117118
118- // / Run the given task.
119+ // / Schedule the given task to run .
119120 virtual void dispatch (std::unique_ptr<Task> T) = 0;
120121
121122 // / Called by ExecutionSession. Waits until all tasks have completed.
@@ -125,15 +126,21 @@ class LLVM_ABI TaskDispatcher {
125126 virtual void work_until (future_base &F) = 0;
126127};
127128
128- // / Runs all tasks on the current thread.
129+ // / Runs all tasks on the current thread, at the next work_until yield point .
129130class LLVM_ABI InPlaceTaskDispatcher : public TaskDispatcher {
130131public:
131132 void dispatch (std::unique_ptr<Task> T) override ;
132133 void shutdown () override ;
133134 void work_until (future_base &F) override ;
134135
135136private:
136- thread_local static SmallVector<std::unique_ptr<Task>> TaskQueue;
137+ // / C++ does not support non-static thread_local variables, so this needs to
138+ // / store both the task and the associated dispatcher queue so that shutdown
139+ // / can wait for the correct tasks to finish.
140+ #if LLVM_ENABLE_THREADS
141+ thread_local static
142+ #endif
143+ SmallVector<std::pair<std::unique_ptr<Task>, InPlaceTaskDispatcher*>> TaskQueue;
137144
138145#if LLVM_ENABLE_THREADS
139146 std::mutex DispatchMutex;
@@ -172,32 +179,55 @@ class LLVM_ABI DynamicThreadPoolTaskDispatcher : public TaskDispatcher {
172179
173180#endif // LLVM_ENABLE_THREADS
174181
175- // / ORC-TaskDispatch aware promise/future class that can help with task dispatch while waiting
176- // TODO: docs
182+ // / @name ORC Promise/Future Classes
183+ // /
184+ // / ORC-aware promise/future implementation that integrates with the
185+ // / TaskDispatcher system to allow efficient cooperative multitasking while
186+ // / waiting for results (with certain limitations on what can be awaited).
187+ // / Together they provide building blocks for a full async/await-like runtime
188+ // / for llvm that supports multiple threads.
189+ // /
190+ // / Unlike std::promise/std::future alone, these classes can help dispatch other
191+ // / tasks while waiting, preventing deadlocks and improving overall system
192+ // / throughput. They have a similar API, though with some important differences
193+ // / and some features simply not currently implemented.
194+ // /
195+ // / @{
177196
178197// / Status for future/promise state
179- enum class FutureStatus : uint8_t { NotReady = 0 , Ready = 1 , NotValid = 2 };
198+ enum class FutureStatus : uint8_t { NotReady = 0 , Ready = 1 };
199+
200+ // / Status for promise state tracking
201+ enum class PromiseStatus : uint8_t {
202+ Initial = 0 ,
203+ FutureCreated = 1 ,
204+ ValueSet = 2 ,
205+ LLVM_MARK_AS_BITMASK_ENUM (ValueSet)
206+ };
180207
181- // / Type-erased base class for futures
208+ // / Type-erased base class for futures, generally for scheduler use to avoid
209+ // / needing virtual dispatches
182210class future_base {
183211public:
212+ // / Check if the future is now ready with a value (precondition: should be valid)
184213 bool ready () const {
185214 return state_->status_ .load (std::memory_order_acquire) !=
186215 FutureStatus::NotReady;
187216 }
188217
189218 // / Check if the future is in a valid state (not moved-from and not consumed)
190219 bool valid () const {
191- return state_ && state_->status_ .load (std::memory_order_acquire) !=
192- FutureStatus::NotValid;
220+ return state_ != nullptr ;
193221 }
194222
195223 // / Wait for the future to be ready, helping with task dispatch
196224 void wait (TaskDispatcher &D) {
197225 // Keep helping with task dispatch until our future is ready
198- if (!ready ())
226+ if (!ready ()) {
199227 D.work_until (*this );
200- assert (ready ());
228+ if (state_->status_ .load (std::memory_order_relaxed) != FutureStatus::Ready)
229+ report_fatal_error (" work_until() returned without this future being ready" );
230+ }
201231 }
202232
203233protected:
@@ -207,19 +237,22 @@ class future_base {
207237
208238 future_base (state_base *state) : state_(state) {}
209239 future_base () = default ;
240+
241+ // / Only allow deleting the future once it is invalid
210242 ~future_base () {
211243 if (valid ())
212244 report_fatal_error (" get() must be called before future destruction" );
213- delete state_;
245+ // state_ is already nullptr if get() was called, otherwise we have an error above
214246 }
215247
216248 // Move constructor and assignment
217249 future_base (future_base &&other) noexcept : state_(other.state_) {
218250 other.state_ = nullptr ;
219251 }
252+
220253 future_base &operator =(future_base &&other) noexcept {
221254 if (this != &other) {
222- delete state_ ;
255+ this -> ~future_base () ;
223256 state_ = other.state_ ;
224257 other.state_ = nullptr ;
225258 }
@@ -229,12 +262,33 @@ class future_base {
229262 state_base *state_;
230263};
231264
232- // / ORC-aware future class that can help with task dispatch while waiting
265+ // / TaskDispatcher-aware future class for cooperative await.
266+ // /
267+ // / @tparam T The type of value this future will provide. Use void for futures that
268+ // / signal completion without providing a value.
269+ // /
270+ // / This future implementation is similar to `std::future`, so most code can
271+ // / transition to it easily`. However, it differs from `std::future` in a few key
272+ // / ways to be aware of:
273+ // / - No exception support (or the overhead for it).
274+ // / - Waiting operations (get(&D), wait(&D)) help dispatch other tasks while
275+ // / blocked, requiring an additional argument of which TaskDispatcher object of
276+ // / where all associated work will be scheduled.
277+ // / - Must call get() exactly once before destruction (enforced with
278+ // / `report_fatal_error`). Internal state is freed when `get` returns.
279+ // /
280+ // / Other notable features, in common with `std::future`:
281+ // / - Supports both value types and void specialization through the same interface.
282+ // / - Thread-safe through atomic operations.
283+ // / - Provides acquire-release ordering with `std::promise::set_value()`.
284+ // / - Concurrent access to any method (including to `ready`) on multiple threads
285+ // / is not allowed.
233286
234287template <typename T> class future ;
235288template <typename T> class promise ;
236289template <typename T> class future : public future_base {
237290public:
291+ // Template the state struct so that future<void> has no wasted overhead for the value
238292 struct state : public future_base ::state_base {
239293 template <typename U> struct value_storage {
240294 U value_;
@@ -254,15 +308,11 @@ template <typename T> class future : public future_base {
254308 future &operator =(future &&) = default ;
255309
256310 // / Get the value, helping with task dispatch while waiting.
257- // / This will destroy the underlying value, so this must only be called once.
311+ // / This will destroy the underlying value, so this must be called exactly once.
258312 T get (TaskDispatcher &D) {
259313 if (!valid ())
260314 report_fatal_error (" get() must only be called once" );
261315 wait (D);
262- auto old_status = state_->status_ .exchange (FutureStatus::NotValid,
263- std::memory_order_release);
264- if (old_status != FutureStatus::Ready)
265- report_fatal_error (" get() must only be called once" );
266316 return take_value ();
267317 }
268318
@@ -271,56 +321,93 @@ template <typename T> class future : public future_base {
271321
272322 template <typename U = T>
273323 typename std::enable_if<!std::is_void<U>::value, U>::type take_value () {
274- return std::move (
324+ T result = std::move (
275325 static_cast <typename future<T>::state *>(state_)->storage .value_ );
326+ delete state_;
327+ state_ = nullptr ;
328+ return result;
276329 }
277330
278331 template <typename U = T>
279- typename std::enable_if<std::is_void<U>::value, U>::type take_value () {}
332+ typename std::enable_if<std::is_void<U>::value, U>::type take_value () {
333+ delete state_;
334+ state_ = nullptr ;
335+ }
280336
281337 explicit future (state *state) : future_base(state) {}
282338};
283339
284- // / ORC-aware promise class that works with ORC future
340+ // / TaskDispatcher-aware promise class that provides values to associated futures.
341+ // /
342+ // / @tparam T The type of value this promise will provide. Use void for promises that
343+ // / signal completion without providing a value.
344+ // /
345+ // / This promise implementation provides the value-setting side of the promise/future
346+ // / pair and integrates with the ORC TaskDispatcher system. Key characteristics:
347+ // / - Must call get_future() no more than once to create the associated future.
348+ // / - Must call set_value() exactly once to provide the result.
349+ // / - Automatically cleans up state if get_future() is never called.
350+ // / - Thread-safe from set_value to get.
351+ // / - Move-only semantics to prevent accidental copying.
352+ // /
353+ // / @par Error Handling:
354+ // / The promise/future system uses report_fatal_error() for misuse:
355+ // / - Calling get_future() more than once.
356+ // / - Calling set_value() more than once.
357+ // / - Destroying a future without calling get().
358+ // / - Calling get() more than once on a future.
359+ // /
360+ // / @par Thread Safety:
361+ // / - Each promise/future must only be accessed by one thread, as concurrent
362+ // / calls to the API functions may result in crashes.
363+ // / - Multiple threads can safely access different promise/future pairs.
364+ // / - set_value() and get() operations are atomic and thread-safe.
365+ // / - Move operations should only be performed by a single thread.
285366template <typename T> class promise {
286367 friend class future <T>;
287368
288369public:
289- promise () : state_(new typename future<T>::state()), future_created_( false ) {}
370+ promise () : state_(new typename future<T>::state()), status_(PromiseStatus::Initial ) {}
290371
291372 ~promise () {
373+ // Assert proper promise lifecycle: ensure set_value was called if
374+ // get_future() was called. This can catch deadlocks where get_future() is
375+ // called but set_value() is never called, though only if the promise is
376+ // moved from instead of borrowed from the frame with the future.
377+ assert ((bool )(status_ & PromiseStatus::ValueSet) == (bool )(status_ & PromiseStatus::FutureCreated) &&
378+ " Promise destroyed with mismatched ValueSet and FutureCreated state" );
292379 // Delete state only if get_future() was never called
293- if (!future_created_ ) {
380+ if (!(status_ & PromiseStatus::FutureCreated) ) {
294381 delete state_;
382+ } else {
383+ assert ((bool )(status_ & PromiseStatus::ValueSet));
295384 }
296385 }
297386
298387 promise (const promise &) = delete ;
299388 promise &operator =(const promise &) = delete ;
300389
301390 promise (promise &&other) noexcept
302- : state_(other.state_), future_created_ (other.future_created_ ) {
391+ : state_(other.state_), status_ (other.status_ ) {
303392 other.state_ = nullptr ;
304- other.future_created_ = false ;
393+ other.status_ = PromiseStatus::Initial ;
305394 }
306395
307396 promise &operator =(promise &&other) noexcept {
308397 if (this != &other) {
309- if (!future_created_) {
310- delete state_;
311- }
398+ this ->~promise ();
312399 state_ = other.state_ ;
313- future_created_ = other.future_created_ ;
400+ status_ = other.status_ ;
314401 other.state_ = nullptr ;
315- other.future_created_ = false ;
402+ other.status_ = PromiseStatus::Initial ;
316403 }
317404 return *this ;
318405 }
319406
320407 // / Get the associated future (must only be called once)
321408 future<T> get_future () {
322- assert (!future_created_ && " get_future() can only be called once" );
323- future_created_ = true ;
409+ assert (!(status_ & PromiseStatus::FutureCreated) && " get_future() can only be called once" );
410+ status_ |= PromiseStatus::FutureCreated ;
324411 return future<T>(state_);
325412 }
326413
@@ -334,15 +421,19 @@ template <typename T> class promise {
334421 set_value (const typename std::conditional<std::is_void<T>::value,
335422 std::nullopt_t , T>::type &value) {
336423 assert (state_ && " Invalid promise state" );
424+ assert (!(status_ & PromiseStatus::ValueSet) && " set_value() can only be called once" );
337425 state_->storage .value_ = value;
426+ status_ |= PromiseStatus::ValueSet;
338427 state_->status_ .store (FutureStatus::Ready, std::memory_order_release);
339428 }
340429
341430 template <typename U = T>
342431 void set_value (typename std::conditional<std::is_void<T>::value,
343432 std::nullopt_t , T>::type &&value) {
344433 assert (state_ && " Invalid promise state" );
434+ assert (!(status_ & PromiseStatus::ValueSet) && " set_value() can only be called once" );
345435 state_->storage .value_ = std::move (value);
436+ status_ |= PromiseStatus::ValueSet;
346437 state_->status_ .store (FutureStatus::Ready, std::memory_order_release);
347438 }
348439
@@ -357,21 +448,25 @@ template <typename T> class promise {
357448 template <typename U = T>
358449 typename std::enable_if<std::is_void<U>::value, void >::type set_value () {
359450 assert (state_ && " Invalid promise state" );
451+ assert (!(status_ & PromiseStatus::ValueSet) && " set_value() can only be called once" );
452+ status_ |= PromiseStatus::ValueSet;
360453 state_->status_ .store (FutureStatus::Ready, std::memory_order_release);
361454 }
362455
363456 // / Swap with another promise
364457 void swap (promise &other) noexcept {
365458 using std::swap;
366459 swap (state_, other.state_ );
367- swap (future_created_ , other.future_created_ );
460+ swap (status_ , other.status_ );
368461 }
369462
370463private:
371464 typename future<T>::state *state_;
372- bool future_created_ ;
465+ PromiseStatus status_ ;
373466};
374467
468+ // / @}
469+
375470} // End namespace orc
376471} // End namespace llvm
377472
0 commit comments