Skip to content
Snippets Groups Projects
  • Nat Goodspeed's avatar
    e4d6383c
    DRTVWR-476: Fix overflow case in llcoro::postAndSuspend(). · e4d6383c
    Nat Goodspeed authored
    Actually the fix is in postAndSuspendSetup(), which affects postAndSuspend(),
    postAndSuspendWithTimeout(), suspendUntilEventOnWithTimeout() and
    suspendUntilEventOn().
    
    By "overflow case" we mean the special circumstance in which:
    
    * the LLEventPump in question is an LLEventMailDrop, meaning its listeners
      eventually expect to see every post()ed value
    * one of the listeners is supposed to consume those values (has called
      LLCoros::set_consuming(true))
    * post() is called more than once before that listener is resumed.
    
    The magic of postAndSuspend() (et al.) is a temporary LLCoros::Promise. The
    waiting coroutine calls get() on the corresponding Future, causing it to
    suspend (as promised) until the Promise is fulfilled.
    
    With the Boost.Fiber implementation of coroutines, fulfilling the Promise
    doesn't immediately resume the suspended coroutine -- it merely marks it ready
    to resume, next time the scheduler gets control.
    
    A second post() call before the suspended coroutine is resumed results in a
    second call to Promise::set_value(). But Promise is a one-shot entity. This
    results in a promise_already_satisfied exception. Because a second post() call
    during that time window is perfectly reasonable, we catch that exception and
    carry on.
    
    The tricky part is: when that exception is thrown, what should the listener
    return? Previously we were returning the listener's current consuming setting,
    just as when the set_value() call succeeds.
    
    But when the LLEventPump is an LLEventMailDrop, and the listener's consuming
    flag is true, that told LLEventMailDrop::post() that the value got through,
    and that it needn't bother to save it in its history queue. The net effect was
    to discard the value.
    
    Instead, return the listener's consuming flag only when Promise::set_value()
    succeeds. When it throws promise_already_satisfied, unconditionally return
    false. That directs LLEventMailDrop::post() to enqueue the undelivered value
    so that the *next* suspendUntilEventOn() call can pick it up.
    e4d6383c
    History
    DRTVWR-476: Fix overflow case in llcoro::postAndSuspend().
    Nat Goodspeed authored
    Actually the fix is in postAndSuspendSetup(), which affects postAndSuspend(),
    postAndSuspendWithTimeout(), suspendUntilEventOnWithTimeout() and
    suspendUntilEventOn().
    
    By "overflow case" we mean the special circumstance in which:
    
    * the LLEventPump in question is an LLEventMailDrop, meaning its listeners
      eventually expect to see every post()ed value
    * one of the listeners is supposed to consume those values (has called
      LLCoros::set_consuming(true))
    * post() is called more than once before that listener is resumed.
    
    The magic of postAndSuspend() (et al.) is a temporary LLCoros::Promise. The
    waiting coroutine calls get() on the corresponding Future, causing it to
    suspend (as promised) until the Promise is fulfilled.
    
    With the Boost.Fiber implementation of coroutines, fulfilling the Promise
    doesn't immediately resume the suspended coroutine -- it merely marks it ready
    to resume, next time the scheduler gets control.
    
    A second post() call before the suspended coroutine is resumed results in a
    second call to Promise::set_value(). But Promise is a one-shot entity. This
    results in a promise_already_satisfied exception. Because a second post() call
    during that time window is perfectly reasonable, we catch that exception and
    carry on.
    
    The tricky part is: when that exception is thrown, what should the listener
    return? Previously we were returning the listener's current consuming setting,
    just as when the set_value() call succeeds.
    
    But when the LLEventPump is an LLEventMailDrop, and the listener's consuming
    flag is true, that told LLEventMailDrop::post() that the value got through,
    and that it needn't bother to save it in its history queue. The net effect was
    to discard the value.
    
    Instead, return the listener's consuming flag only when Promise::set_value()
    succeeds. When it throws promise_already_satisfied, unconditionally return
    false. That directs LLEventMailDrop::post() to enqueue the undelivered value
    so that the *next* suspendUntilEventOn() call can pick it up.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.