Storing executors in implementation of then()

已查看 59 次
跳至第一个未读帖子

daniel...@gmail.com

未读,
2018年6月16日 07:58:282018/6/16
收件人 ISO C++ Standard - Discussion
Hi,

I’m following the executor proposals for quite some time and I’m interested in writing my own executors. There is one detail however which is puzzling me since the beginning. I remember some talk which said that executors are supposed to be lightweight objects so I assume they are essentially pointers to the execution context similar to iterators are pointers to the buffers of containers. If this is true, then I don’t know how the following implementation detail of then() found at https://github.com/jaredhoberock/future is allowed to store the one way executor safely and invoke execute() on it at some later point in time. What if the execution context’s destructor has already been called by the time the future gets ready?


template<class Executor, class Function>
void set_continuation(const Executor& exec, Function&& f)
{
  // create a continuation that calls execution::execute()
  auto continuation = [exec, f = move(f)] (experimental::future<T> predecessor_future) mutable
  {
    execution::execute(exec, [f = move(f), predecessor_future = move(predecessor_future)] () mutable
    {
      f(move(predecessor_future));
    });
  };

  unique_lock<mutex> lock(this->mutex_);

  if(this->is_ready_)
  {
    // the state is ready, invoke the continuation immediately

    // unlock here so that the future passed to the continuation below doesn't block in .get()
    lock.unlock();

    continuation(this->to_future());
  }
  else
  {
    // the state is not yet ready, store the continuation for later
    this->continuation_ = move(continuation);
  }
}

 

I see three alternatives:

  1. The user has to make sure that the execution context is not destructed before the continuation is passed to execute(). This is in my opinion very hard to impossible to guarantee, because a user could just destroy the future returned by then() because of an exception and then there is no way to synchronize with the destruction of the execution context.
  2. Something similar to outstanding_work (not yet sure how the properties actually work) which must be used to signal that the executor stays valid until it has been used to run the continuation using execute(). This means that only executors supporting this mechanism can be used in calls to then().
  3. The executors are not necessarily always as lightweight as a pointer and could actually store something like a shared_ptr or weak_ptr internally. In this case I’m wondering why executors are often passed as const reference and copied instead of forwarded. So it seems to me that this is not the authors intention.

Maybe I misunderstood the papers, please clarify. Having some executors implemented in terms of shared_ptr or weak_ptr and forwarding executors instead of copying makes most sense to me.


Thank you,
Daniel

charles....@gmail.com

未读,
2018年7月25日 13:41:302018/7/25
收件人 ISO C++ Standard - Discussion、daniel...@gmail.com
I think the outstanding_work property can be used to ensure the execution context stays valid until all Executor objects with that property are destructed.  But even without outstanding_work, remember that in this example the continuation is executed in a thread of execution created by the Executor itself.  Presumably, the associated execution context (like some thread pool or whatever) will join with all execution threads in its destructor.  So it would not be possible for the thread of execution that invokes execute(...) to even be running if the execution context is invalid.  But this wouldn't apply if you had multiple execution contexts each storing each other's executors inside continuations (as would happen with chained futures).  In that case you would need some outstanding work tracking mechanism to be safe.

Richard Hodges

未读,
2018年7月25日 14:09:082018/7/25
收件人 std-dis...@isocpp.org
If the std proposals are modelling the fabulous boost::asio executor concepts, then destroying the execution context while it is performing work would be UB.

At least in asio, it's a requirement that handlers are not running when the execution context is destroyed.



--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
回复全部
回复作者
转发
0 个新帖子