@@ -161,8 +161,8 @@ class AsyncTask : public HeapObject, public Job {
161
161
ResumeTask (this , currentExecutor, ResumeContext);
162
162
}
163
163
164
- // / Check whether this task has been cancelled. Checking this is,
165
- // / of course, inherently race-prone on its own.
164
+ // / Check whether this task has been cancelled.
165
+ // / Checking this is, of course, inherently race-prone on its own.
166
166
bool isCancelled () const {
167
167
return Status.load (std::memory_order_relaxed).isCancelled ();
168
168
}
@@ -190,12 +190,16 @@ class AsyncTask : public HeapObject, public Job {
190
190
}
191
191
};
192
192
193
+ // TODO: rename? all other functions are is... rather than has...Fragment
193
194
bool hasChildFragment () const { return Flags.task_isChildTask (); }
195
+
194
196
ChildFragment *childFragment () {
195
197
assert (hasChildFragment ());
196
198
return reinterpret_cast <ChildFragment*>(this + 1 );
197
199
}
198
200
201
+ // ==== Future ---------------------------------------------------------------
202
+
199
203
class FutureFragment {
200
204
public:
201
205
// / Describes the status of the future.
@@ -248,7 +252,7 @@ class AsyncTask : public HeapObject, public Job {
248
252
const Metadata *resultType;
249
253
250
254
// Trailing storage for the result itself. The storage will be uninitialized,
251
- // contain an instance of \c resultType, or contaon an an \c Error.
255
+ // contain an instance of \c resultType, or contain an an \c Error.
252
256
253
257
friend class AsyncTask ;
254
258
@@ -315,6 +319,197 @@ class AsyncTask : public HeapObject, public Job {
315
319
// / executor.
316
320
void completeFuture (AsyncContext *context, ExecutorRef executor);
317
321
322
+ // ==== Channel --------------------------------------------------------------
323
+
324
+ class ChannelFragment {
325
+ public:
326
+ // / Describes the status of the channel.
327
+ enum class ReadyQueueStatus : uintptr_t {
328
+ // / The channel is empty, no tasks are pending.
329
+ // / Return immediately, there is no point in suspending.
330
+ // /
331
+ // / The storage is not accessible.
332
+ Empty = 0 ,
333
+
334
+ // / The channel has pending tasks
335
+ // /
336
+ // / The storage is not accessible.
337
+ Pending = 1 ,
338
+
339
+ // / The future has completed with result (of type \c resultType).
340
+ Success = 2 ,
341
+
342
+ // / The future has completed by throwing an error (an \c Error
343
+ // / existential).
344
+ Error = 3 ,
345
+
346
+ // /// No tasks are ready, yet there exist pending tasks (they were
347
+ // /// `add()`-ed and not completed yet). Suspend and await a wake-up
348
+ // /// when any task completes.
349
+ // Pending = 1,
350
+ //
351
+ // /// At least one task is completed and waiting in the queue.
352
+ // /// Pull immediately without suspending to obtain it.
353
+ // Ready = 2,
354
+ };
355
+
356
+ // / Describes the status of the future.
357
+ // /
358
+ // / Futures always begin in the "Executing" state, and will always
359
+ // / make a single state change to either Success or Error.
360
+ enum class Status : uintptr_t {
361
+ // / No-one is waiting.
362
+ // / The storage is not accessible.
363
+ Pending = 0 ,
364
+
365
+ // / The future has completed with result (of type \c resultType).
366
+ Success = 1 ,
367
+
368
+ // / The future has completed by throwing an error (an \c Error
369
+ // / existential).
370
+ Error = 2 ,
371
+ };
372
+
373
+ // / An item within the message queue of a channel.
374
+ struct ReadyQueueItem {
375
+ // / Mask used for the low status bits in a message queue item.
376
+ static const uintptr_t statusMask = 0x03 ;
377
+
378
+ uintptr_t storage;
379
+
380
+ ReadyQueueStatus getStatus () const {
381
+ return static_cast <ReadyQueueStatus>(storage & statusMask);
382
+ }
383
+
384
+ AsyncTask *getTask () const {
385
+ return reinterpret_cast <AsyncTask *>(storage & ~statusMask);
386
+ }
387
+
388
+ static ReadyQueueItem get (ReadyQueueStatus status, AsyncTask *task) {
389
+ return ReadyQueueItem{
390
+ reinterpret_cast <uintptr_t >(task) | static_cast <uintptr_t >(status)};
391
+ }
392
+ };
393
+
394
+ // / An item within the wait queue, which includes the status and the
395
+ // / head of the list of tasks.
396
+ struct WaitQueueItem {
397
+ // / Mask used for the low status bits in a wait queue item.
398
+ static const uintptr_t statusMask = 0x03 ;
399
+
400
+ uintptr_t storage;
401
+
402
+ Status getStatus () const {
403
+ return static_cast <Status>(storage & statusMask);
404
+ }
405
+
406
+ AsyncTask *getTask () const {
407
+ return reinterpret_cast <AsyncTask *>(storage & ~statusMask);
408
+ }
409
+
410
+ static WaitQueueItem get (Status status, AsyncTask *task) {
411
+ return WaitQueueItem{
412
+ reinterpret_cast <uintptr_t >(task) | static_cast <uintptr_t >(status)};
413
+ }
414
+ };
415
+
416
+ private:
417
+ // TODO we likely can collapse these into one queue if we try hard enough
418
+
419
+ // / Queue containing completed tasks offered into this channel.
420
+ // /
421
+ // / The low bits contain the status, the rest of the pointer is the
422
+ // / AsyncTask.
423
+ std::atomic<ReadyQueueItem> readyQueue;
424
+
425
+ // / Queue containing all of the tasks that are waiting in `get()`.
426
+ // /
427
+ // / The low bits contain the status, the rest of the pointer is the
428
+ // / AsyncTask.
429
+ std::atomic<WaitQueueItem> waitQueue;
430
+
431
+ // / The type of the result that will be produced by the channel.
432
+ const Metadata *resultType;
433
+
434
+ // FIXME: seems shady...?
435
+ // Trailing storage for the result itself. The storage will be uninitialized.
436
+ // Use the `readyQueue` to poll for values from the channel instead.
437
+ friend class AsyncTask ; // TODO: remove this?
438
+
439
+ public:
440
+ explicit ChannelFragment (const Metadata *resultType)
441
+ : readyQueue(ReadyQueueItem::get(ReadyQueueStatus::Empty, nullptr )),
442
+ waitQueue(WaitQueueItem::get(Status::Pending, nullptr )),
443
+ resultType(resultType) { }
444
+
445
+ // / Destroy the storage associated with the channel.
446
+ void destroy ();
447
+
448
+ // /// Offer a completed task to the channel to enable polling for it.
449
+ // void offer(AsyncTask *completed, AsyncContext *context, ExecutorRef executor);
450
+ //
451
+ // /// Poll the channel for any
452
+ // ChannelFragment::ReadyQueueStatus channelPoll(AsyncTask *waitingTask);
453
+
454
+ // /// Retrieve a pointer to the storage of result.
455
+ // OpaqueValue *getStoragePtr() {
456
+ // return reinterpret_cast<OpaqueValue *>(
457
+ // reinterpret_cast<char *>(this) + storageOffset(resultType));
458
+ // }
459
+ //
460
+ // /// Retrieve the error.
461
+ // SwiftError *&getError() {
462
+ // return *reinterpret_cast<SwiftError **>(
463
+ // reinterpret_cast<char *>(this) + storageOffset(resultType));
464
+ // }
465
+
466
+ // /// Compute the offset of the storage from the base of the channel
467
+ // /// fragment.
468
+ // static size_t storageOffset(const Metadata *resultType) {
469
+ // size_t offset = sizeof(ChannelFragment);
470
+ // size_t alignment =
471
+ // std::max(resultType->vw_alignment(), alignof(SwiftError *));
472
+ // return (offset + alignment - 1) & ~(alignment - 1);
473
+ // }
474
+ //
475
+ // /// Determine the size of the channel fragment given a particular channel
476
+ // /// result type.
477
+ // static size_t fragmentSize(const Metadata *resultType) {
478
+ // return storageOffset(resultType) +
479
+ // std::max(resultType->vw_size(), sizeof(SwiftError *));
480
+ // }
481
+ };
482
+
483
+ bool isChannel () const { return Flags.task_isChannel (); }
484
+
485
+ ChannelFragment *channelFragment () {
486
+ assert (isChannel ());
487
+ // FIXME: isn't it also a future at the same time?
488
+ if (hasChildFragment ()) { // TODO: make sure those all are correct; we can have many fragments
489
+ return reinterpret_cast <ChannelFragment *>(
490
+ reinterpret_cast <ChildFragment*>(this + 1 ) + 1 );
491
+ }
492
+
493
+ return reinterpret_cast <ChannelFragment *>(this + 1 );
494
+ }
495
+
496
+ // / Offer result of a task into this channel.
497
+ // / The value is enqueued at the end of the channel.
498
+ // /
499
+ // / Upon enqueue, any waiting tasks will be scheduled on the given executor. // TODO: not precisely right
500
+ void channelOffer (AsyncTask *completed, AsyncContext *context, ExecutorRef executor);
501
+
502
+ // / Wait for for channel to become non-empty.
503
+ // /
504
+ // / \returns the status of the queue. TODO more docs
505
+ ChannelFragment::ReadyQueueStatus channelPoll (AsyncTask *waitingTask);
506
+
507
+ // ==== ----------------------------------------------------------------------
508
+
509
+ bool isGroupChild () const { return Flags.task_isGroupChild (); }
510
+
511
+ // ==== ----------------------------------------------------------------------
512
+
318
513
static bool classof (const Job *job) {
319
514
return job->isAsyncTask ();
320
515
}
@@ -431,6 +626,10 @@ class FutureAsyncContext : public AsyncContext {
431
626
SwiftError *errorResult = nullptr ;
432
627
OpaqueValue *indirectResult;
433
628
629
+
630
+ // TODO: this is to support "offer into queue on complete"
631
+ AsyncContext *parentChannel = nullptr ; // TODO: no idea if we need this or not
632
+
434
633
using AsyncContext::AsyncContext;
435
634
};
436
635
0 commit comments