@@ -138,20 +138,18 @@ class IAsyncQueueDispatcherBase
138
138
139
139
public:
140
140
template <typename T>
141
- class future_t : private core ::StorageTrivializer<T>, protected future_base_t
141
+ class future_t : private core ::StorageTrivializer<T>, public future_base_t
142
142
{
143
143
using storage_t = core::StorageTrivializer<T>;
144
- inline void discard_common ()
145
- {
146
- storage_t::destruct ();
147
- state.exchangeNotify <true >(STATE::INITIAL,STATE::LOCKED);
148
- }
144
+
145
+ friend class storage_lock_t ;
149
146
150
147
public:
151
148
inline future_t () : future_base_t() {}
152
- inline ~future_t ()
149
+ virtual inline ~future_t ()
153
150
{
154
- discard ();
151
+ if (auto lock=acquire ())
152
+ lock.discard ();
155
153
}
156
154
157
155
// !
@@ -193,70 +191,93 @@ class IAsyncQueueDispatcherBase
193
191
return retval;
194
192
}
195
193
196
- // ! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `unique_lock`
197
- // and other RAII locks as the blocking aquire can fail and that needs to be handled.
198
-
199
- // ! ANY THREAD [except WORKER]: If we're READY transition to LOCKED
200
- [[nodiscard]] inline T* try_acquire ()
201
- {
202
- auto expected = STATE::READY;
203
- if (state.tryTransition (STATE::LOCKED,expected))
204
- return storage_t::getStorage ();
205
- return nullptr ;
206
- }
207
- // ! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL
208
- // this accounts for being cancelled or consumed while waiting
209
- [[nodiscard]] inline T* acquire ()
210
- {
211
- if (state.waitAbortableTransition (STATE::LOCKED,STATE::READY,STATE::INITIAL))
212
- return storage_t::getStorage ();
213
- return nullptr ;
214
- }
215
- // ! ANY THREAD [except WORKER]: Release an acquired lock
216
- inline void release ()
217
- {
218
- state.exchangeNotify <true >(STATE::READY,STATE::LOCKED);
219
- }
220
-
221
194
// ! NOTE: You're in charge of ensuring future doesn't transition back to INITIAL (e.g. lock or use sanely!)
222
195
inline const T* get () const
223
196
{
224
197
if (ready ())
225
198
return storage_t::getStorage ();
226
199
return nullptr ;
227
200
}
228
- inline T* get ()
201
+
202
+ // ! Utility to write less code, WILL ASSERT IF IT FAILS! So don't use on futures that might be cancelled or fail.
203
+ inline T copy () const
229
204
{
230
- if ( future_base_t ::state. query () != future_base_t ::STATE::LOCKED)
231
- return nullptr ;
232
- return storage_t::getStorage ();
205
+ const bool success = wait ();
206
+ assert (success) ;
207
+ return * get ();
233
208
}
234
209
235
- // ! Can only be called once! If returns false means has been cancelled and nothing happened
236
- [[nodiscard]] inline bool move_into (T& dst)
210
+ // ! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `std::unique_lock`
211
+ // and other RAII locks as the blocking aquire can fail and that needs to be handled.
212
+ class storage_lock_t final
237
213
{
238
- T* pSrc = acquire ();
239
- if (!pSrc)
240
- return false ;
241
- dst = std::move (*pSrc);
242
- discard_common ();
243
- return true ;
244
- }
214
+ using state_enum = future_base_t ::STATE;
215
+ future_t <T>* m_future;
216
+
217
+ // ! constructor, arg is nullptr if locked
218
+ friend class future_t <T>;
219
+ inline storage_lock_t (future_t <T>* _future) : m_future(_future)
220
+ {
221
+ assert (m_future->state .query ()==state_enum::LOCKED);
222
+ }
223
+ // ! as usual for "unique" things
224
+ inline storage_lock_t (const storage_lock_t &) = delete;
225
+ inline storage_lock_t & operator =(const storage_lock_t &) = delete ;
226
+
227
+ public:
228
+ inline ~storage_lock_t ()
229
+ {
230
+ if (m_future)
231
+ m_future->state .exchangeNotify <true >(state_enum::READY,state_enum::LOCKED);
232
+ }
233
+
234
+ // !
235
+ inline explicit operator bool ()
236
+ {
237
+ return m_future;
238
+ }
239
+ inline bool operator !()
240
+ {
241
+ return !m_future;
242
+ }
245
243
246
- // ! Utility to write less code, WILL ASSERT IF IT FAILS TO ACQUIRE! So don't use on futures that might be cancelled or fail.
247
- inline T copy_out ()
244
+ // !
245
+ inline T* operator ->() const
246
+ {
247
+ if (m_future)
248
+ return m_future->getStorage ();
249
+ return nullptr ;
250
+ }
251
+ template <typename U=T> requires (std::is_same_v<U,T> && !std::is_void_v<U>)
252
+ inline U& operator*() const {return *operator ->();}
253
+
254
+ // ! Can only be called once!
255
+ inline void discard ()
256
+ {
257
+ assert (m_future);
258
+ m_future->destruct ();
259
+ m_future->state .exchangeNotify <true >(state_enum::INITIAL,state_enum::LOCKED);
260
+ }
261
+ // ! Can only be called once!
262
+ template <typename U=T> requires (std::is_same_v<U,T> && !std::is_void_v<U>)
263
+ inline void move_into(U& dst)
264
+ {
265
+ dst = std::move (operator *());
266
+ discard ();
267
+ }
268
+ };
269
+
270
+ // ! ANY THREAD [except WORKER]: If we're READY transition to LOCKED
271
+ inline storage_lock_t try_acquire ()
248
272
{
249
- T retval;
250
- const bool success = move_into (retval);
251
- assert (success);
252
- return retval;
273
+ auto expected = STATE::READY;
274
+ return storage_lock_t (state.tryTransition (STATE::LOCKED,expected) ? this :nullptr );
253
275
}
254
-
255
- // !
256
- virtual inline void discard ()
276
+ // ! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL
277
+ // this accounts for being cancelled or consumed while waiting
278
+ inline storage_lock_t acquire ()
257
279
{
258
- if (acquire ())
259
- discard_common ();
280
+ return storage_lock_t (state.waitAbortableTransition (STATE::LOCKED,STATE::READY,STATE::INITIAL) ? this :nullptr );
260
281
}
261
282
262
283
protected:
@@ -276,6 +297,12 @@ class IAsyncQueueDispatcherBase
276
297
std::atomic<request_base_t *> request = nullptr ;
277
298
278
299
public:
300
+ inline ~cancellable_future_t ()
301
+ {
302
+ // try to cancel
303
+ cancel ();
304
+ }
305
+
279
306
// ! ANY THREAD [except WORKER]: Cancel pending request if we can, returns whether we actually managed to cancel
280
307
inline bool cancel ()
281
308
{
@@ -305,16 +332,6 @@ class IAsyncQueueDispatcherBase
305
332
return false ;
306
333
}
307
334
308
- inline void discard () override final
309
- {
310
- // try to cancel
311
- cancel ();
312
- // sanity check
313
- assert (request==nullptr );
314
- // proceed with the usual
315
- base_t::discard ();
316
- }
317
-
318
335
private:
319
336
inline void associate_request (request_base_t * req) override final
320
337
{
@@ -338,18 +355,7 @@ class IAsyncQueueDispatcherBase
338
355
339
356
protected:
340
357
template <typename T>
341
- class future_constructor_t final
342
- {
343
- future_t <T>* pFuture;
344
- public:
345
- inline future_constructor_t (future_base_t * _future_base) : pFuture(static_cast <future_t <T>*>(_future_base)) {}
346
-
347
- template <typename ... Args>
348
- inline void operator ()(Args&&... args)
349
- {
350
- pFuture->construct (std::forward<Args>(args)...);
351
- }
352
- };
358
+ static inline core::StorageTrivializer<T>* future_storage_cast (future_base_t * _future_base) {return static_cast <future_t <T>*>(_future_base);}
353
359
};
354
360
355
361
inline void IAsyncQueueDispatcherBase::request_base_t::finalize (future_base_t * fut)
0 commit comments