chriskohlhoff / asio-tr2

C++ TR2 proposal based on Asio
17 stars 5 forks source link

Need wording about lifetime of execution contexts #218

Open jwakely opened 9 years ago

jwakely commented 9 years ago

I can't see any wording that makes it explicit that trying to submit functions (or other similar operations) to a context that has been destroyed is undefined e.g.

io_context c1;
system_timer t{c1};
{
  io_context c2;
  t = system_time{c2};
}
t.get_executor();   // undefined, refers to c2
jwakely commented 9 years ago

A related question is whether objects such as timers can be used in their moved-from state. The move constructors say:

— get_executor() is equal to the prior value of rhs.get_executor().

which suggests that rhs loses its executor. Is it valid to submit work via rhs when it's been moved-from?

Presumably it's OK if it gets re-assigned a new state:

io_context c;
system_timer t{c};
{
  auto sink = std::move(t);
}
// t is invalid here
t = system_timer{c};
// is t valid again?

One option, which I don't like very much, would be to add a valid() member to the Executor requirements in Table 3, and say that any operations on an invalid executor are undefined except calling valid() or destroying it.

That would cover the move construction and move assignment cases, as rhs.get_executor().valid() would be allowed (but not required) to be false after a move. It wouldn't help with the example in the first comment above, as t.get_executor().context() would still be a dangling reference, but t.get_executor().valid() would be true because there's no way to invalidate all associated executors when the context is destroyed.

So maybe adding valid() doesn't really improve things, and we should just say that:

chriskohlhoff commented 9 years ago

Asio's current behaviour (and my intention for the proposal) is that for moved-from for timer and socket objects, executors are unchanged by the move. A moved-from socket is intended to be in the same state as one that was newly constructed from the io_context. I can fix the move constructor postconditions to not say "prior value of rhs.get_executor()". Do we need any more than that?

For executors more generally though, a moved-from executor isn't required to be valid. I guess wording for your last three bullet points should be added to the executor requirements?

jwakely commented 9 years ago

A moved-from socket is intended to be in the same state as one that was newly constructed from the io_context.

OK, then my third bullet isn't needed.

I can fix the move constructor postconditions to not say "prior value of rhs.get_executor()". Do we need any more than that?

The word "prior" was definitely what suggested to me that the timer and socket executors are allowed to change after a move, so simply saying "get_executor() == rhs.get_executor()" in the postconditions would clarify the intention.

Something like first and second bullets would be useful in the executor requirements, maybe combined into a single sentence and with a statement that submitting work to an invalid executor is undefined. That would address the original issue I raised here, before my diversion into moved-from states!

chriskohlhoff commented 9 years ago

Fixed basic_socket and basic_waitable timer move constructors in 40d575e5e9b35f7dc3db56658f33afa63f910258. Turns out basic_socket_acceptor and basic_resolver already did the right thing.

Still need to do the change to the executor requirements.

chriskohlhoff commented 9 years ago

Changed executor requirements in 74387c6a6f51451e3ecf8dcbdcd43040096d9234.