1. # An Awaitable is something we can use the `await` operator on. To support
  2. # this, it requires a `get-await-handle` method be implemented, which returns
  3. # an `Awaitable::AwaitHandle`.
  4. my role Awaitable {
  5. method get-await-handle() { ... }
  6. }
  7. # An Awaitable::Handle implementation is an immutable object that conveys the
  8. # status of the requested asynchronous result at the point we obtain the
  9. # handle. If the `.already` property is `True`, then there is no need to block
  10. # or suspend execution; the `.result` or `.cause` of failure can be used right
  11. # away (depending on the value of `.success). Otherwise, the consumer of the
  12. # handle should call the `subscribe-awaiter` method with its unblock/resume
  13. # handler, and then proceed to block/suspend. In this case, the handler will
  14. # be passed two arguments: a `Bool` success, and a result/cause (result if
  15. # success is `True`, cause if it's `False`). The `Awaitable::Handle` will
  16. # *not* have its success/result/cause updated; this would open the door to
  17. # data races (including subtle ones related to read/write ordering), when
  18. # the point of the fast-path is to test if we've got a result already with
  19. # minimal overhead (and thus minimal concurrency control).
  20. my role Awaitable::Handle {
  21. has Bool $.already;
  22. has Bool $.success;
  23. has Mu $.result;
  24. has Exception $.cause;
  25. method already-success(\result) {
  26. nqp::create(self)!already-success(result)
  27. }
  28. method !already-success(\result) {
  29. $!already := True;
  30. $!success := True;
  31. $!result := result;
  32. self
  33. }
  34. method already-failure(\cause) {
  35. self.CREATE!already-failure(cause)
  36. }
  37. method !already-failure(\cause) {
  38. $!already := True;
  39. $!success := False;
  40. $!cause := cause;
  41. self
  42. }
  43. method subscribe-awaiter(&subscriber) { ... }
  44. }