pike.git / lib / modules / Concurrent.pmod

version» Context lines:

pike.git/lib/modules/Concurrent.pmod:1:   #pike __REAL_VERSION__      //! Module for handling multiple concurrent events.   //!   //! The @[Future] and @[Promise] API was inspired by   //! @url{https://github.com/couchdeveloper/FutureLib@}.    - protected enum State { + local protected enum State { +  STATE_NO_FUTURE = -1,    STATE_PENDING = 0,    STATE_FULFILLED,    STATE_REJECTED,   };      protected Thread.Mutex mux = Thread.Mutex();   protected Thread.Condition cond = Thread.Condition();      //! Global failure callback, called when a promise without failure   //! callback fails. This is useful to log exceptions, so they are not
pike.git/lib/modules/Concurrent.pmod:83:    //! Wait for fulfillment and return the value.    //!    //! @throws    //! Throws on rejection.    mixed get()    {    State s = state;    mixed res = result;    if (!s) {    Thread.MutexKey key = mux->lock(); -  while (!state) { +  while (state <= STATE_PENDING) {    cond->wait(key);    }       s = state;    res = result;    }       if (s == STATE_REJECTED) {    throw(res);    }
pike.git/lib/modules/Concurrent.pmod:118:    //! @[cb] will always be called from the main backend.    //!    //! @seealso    //! @[on_failure()]    this_program on_success(function(mixed, mixed ... : void) cb, mixed ... extra)    {    switch (state) {    case STATE_FULFILLED:    callout(cb, 0, result, @extra);    break; +  case STATE_NO_FUTURE:    case STATE_PENDING:    // Rely on interpreter lock to add to success_cbs before state changes    // again    success_cbs += ({ ({ cb, @extra }) });    }    return this_program::this;    }       //! Register a callback that is to be called on failure.    //!
pike.git/lib/modules/Concurrent.pmod:147:    //! @[cb] will always be called from the main backend.    //!    //! @seealso    //! @[on_success()]    this_program on_failure(function(mixed, mixed ... : void) cb, mixed ... extra)    {    switch (state) {    case STATE_REJECTED:    callout(cb, 0, result, @extra);    break; +  case STATE_NO_FUTURE:    case STATE_PENDING:    // Rely on interpreter lock to add to failure_cbs before state changes    // again    failure_cbs += ({ ({ cb, @extra }) });    }    return this_program::this;    }       //! Apply @[fun] with @[val] followed by the contents of @[ctx],    //! and update @[p] with the result.
pike.git/lib/modules/Concurrent.pmod:565:    Promise p = Promise();    on_failure(p->failure);    on_success(p->success);    call_out(p->try_failure, seconds, ({ "Timeout.\n", backtrace() }));    return p->future();    }       protected string _sprintf(int t)    {    return t=='O' && sprintf("%O(%s,%O)", this_program, -  ([ STATE_PENDING : "pending", +  ([ STATE_NO_FUTURE : "no future", +  STATE_PENDING : "pending",    STATE_REJECTED : "rejected",    STATE_FULFILLED : "fulfilled" ])[state],    result);    }   }      protected class AggregateState   {    private Promise promise;    private int(0..) promises;
pike.git/lib/modules/Concurrent.pmod:629:    }       private void cb_failure(mixed value, int idx)    {    Promise p; // Cache it, to cover a failure race    if (p = promise)    {    Thread.MutexKey key = mux->lock();    do    { -  if (!p->state) +  if (p->state <= STATE_PENDING)    {    ++failed;    if (max_failures < failed && max_failures >= 0)    {    key = 0;    p->try_failure(value);    break;    }    int success = succeeded + failed == promises;    key = 0;
pike.git/lib/modules/Concurrent.pmod:664:    }       private void cb_success(mixed value, int idx)    {    Promise p; // Cache it, to cover a failure race    if (p = promise)    {    Thread.MutexKey key = mux->lock();    do    { -  if (!p->state) +  if (p->state <= STATE_PENDING)    {    ++succeeded;    if (promises - min_failures < succeeded)    {    key = 0;    p->try_failure(value);    break;    }    int success = succeeded + failed == promises;    key = 0;
pike.git/lib/modules/Concurrent.pmod:709:   //! @seealso   //! @[Future], @[future()]   class Promise   {    inherit Future;       final int _materialised;    final AggregateState _astate;       //! Creates a new promise, optionally initialised from a traditional callback -  //! driven method via @expr{executor(resolve, reject, extra ... )@}. +  //! driven method via @expr{executor(success, failure, @@extra)@}.    //!    //! @seealso    //! @url{https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise@}    protected void create(void|    function(function(mixed:void),    function(mixed:void), mixed ...:void) executor, mixed ... extra)    { -  +  state = STATE_NO_FUTURE;    if (executor)    executor(success, failure, @extra);    }       Future on_success(function(mixed, mixed ... : void) cb, mixed ... extra)    {    if (_astate)    _astate->materialise();    return ::on_success(cb, @extra);    }
pike.git/lib/modules/Concurrent.pmod:745:    mixed get()    {    if (_astate)    _astate->materialise();    return ::get();    }       //! The future value that we promise.    Future future()    { +  if (state == STATE_NO_FUTURE) state = STATE_PENDING;    return Future::this;    }       protected this_program finalise(State newstate, mixed value, int try,    array(array(function(mixed, mixed ...: void)|array(mixed))) cbs,    void|function(mixed : void) globalfailure)    {    Thread.MutexKey key = mux->lock(); -  if (!state) +  if (state <= STATE_PENDING)    {    state = newstate;    result = value;    key = 0;    cond->broadcast();    if (sizeof(cbs))    {    foreach(cbs; ; array cb)    if (cb)    callout(cb[0], 0, value, @cb[1..]);
pike.git/lib/modules/Concurrent.pmod:812:    //! Result of the @[Future].    //!    //! Mark the @[Future] as fulfilled if it hasn't already been fulfilled    //! or failed, and in that case schedule the @[on_success()] callbacks    //! to be called as soon as possible.    //!    //! @seealso    //! @[success()], @[try_failure()], @[failure()], @[on_success()]    inline this_program try_success(mixed value)    { -  return state ? this_program::this : success(value, 1); +  return (state > STATE_PENDING) ? this_program::this : success(value, 1);    }       //! @decl this_program failure(mixed value)    //!    //! Reject the @[Future] value.    //!    //! @param value    //! Failure result of the @[Future].    //!    //! @throws
pike.git/lib/modules/Concurrent.pmod:850:    //! Failure result of the @[Future].    //!    //! Mark the @[Future] as failed if it hasn't already been fulfilled,    //! and in that case schedule the @[on_failure()] callbacks to be    //! called as soon as possible.    //!    //! @seealso    //! @[failure()], @[success()], @[on_failure()]    inline this_program try_failure(mixed value)    { -  return state ? this_program::this : failure(value, 1); +  return (state > STATE_PENDING) ? this_program::this : failure(value, 1);    }       inline private void fill_astate()    {    if (!_astate)    _astate = AggregateState(this);    }       //! Add futures to the list of futures which the current object depends upon.    //!
pike.git/lib/modules/Concurrent.pmod:1011:    //!    //! @seealso    //! @[depend()], @[max_failures()]    this_program any_results()    {    return max_failures(-1);    }       protected void _destruct()    { -  if (!state) +  // NB: Don't complain about dropping STATE_NO_FUTURE on the floor. +  if (state == STATE_PENDING)    try_failure(({ "Promise broken.\n", backtrace() }));    }   }      //! @returns   //! A @[Future] that represents the first   //! of the @expr{futures@} that completes.   //!   //! @seealso   //! @[race()], @[Promise.first_completed()]