:
On 25 Aug 2015 at 8:22, Thomas Heller wrote:
As far as I can see, there is not a single function documented
Am 25.08.2015 3:24 nachm. schrieb "Niall Douglas" returns anything than some equivalent to "shared_future The synchronous single shot functions return an afio::handle_ptr or
some type appropriate for that function (e.g. enumerate() returns a
vector of directory_entry). The asynchronous single shot functions return an afio::future<T>
where T is usually void. afio::future<T>.get_handle() returns an
afio::handle_ptr, while afio::future<T>.get() returns whatever T is
(which might be void). The batch functions return vectors of afio::future<T>. Ahh i missed the enumerate (btw, the sync enumerate example only shows
the usage of the async version...) and stat functions (and maybe some
others). The question remains:
Why do they also need to return a handle?
[snip]
Why does it need to be shared_ptr<handle> in the first place? Why not
just handle? A shared_ptr always implies shared ownership, when for
example a boost::afio::file function returns, with whom is the ownership
shared?
[snip]
It is misdesigned in the way that it implies shared ownership from the
start on, that is something that should be left to the user.
[snip]
That's actually a very strange statement. shared_ptr implies shared
ownership, aka some shared state. Sharing state requires synchronization
to protect it from races. And yes, if you modify this shared from two
threads without synchronization, you accidentally introduced a race.
[snip]
That doesn't make a lot of sense ... Of course, if your state is not
shared, you don't introduce races, but if you don't have a shared state,
why use shared_ptr? I think Thomas you have missed something very fundamental about the
AFIO design. I hope you don't mind me not replying to the rest of it
(which was useful) and I'll just dig into the above as others may be
where you are. 1. An afio::future<> is *always* a future handle to a file. You asked
why all functions always return a future handle to a file instead of
just the functions which open a file. I'm getting the feeling you
didn't understand that: future ... expands, quite literally as you'll see in afio.hpp, into: return precondition.then(detail::async_enumerate()); ... which is a Concurrency TS continuation. detail::async_enumerate
might then be effectively implemented as: struct async_enumerate
{
future In other words, when the future precondition becomes ready, it will
execute the continuation which calls the call operator on the
instance of detail::async_enumerate. That call operator will schedule
(via await) the directory enumeration of the file handle just become
ready in the future precondition. Erm no, await doesn't schedule the directory enumeration, it just continues
the control flow after the future returned from
do_async_directory_enumeration returns. In fact, await would have been
better suited to wait for the precondition. This is why afio::future<> *always* is a future to a handle to a
file. Does this make sense? No, not really, that doesn't answer my question at all. 2. The reason why it's shared_ptr<handle> not handle * is precisely
because of how to manage lifetime over unknown continuations. If the
user writes: future<> oh=async_file("niall.txt");
future<> reads[100];
for(size_t n=0; n<100; n++)
reads[n]=async_read(oh, buffers[n], 10, n*4096); So this schedules niall.txt to be opened, and then adds 100
continuations onto that file open each of which schedules the read of
10 bytes from a 4Kb multiple. All those 100 continuations occur in
*parallel*, and could complete in *any* order. So how do you keep the handle to the niall.txt file around until the
last continuation using that file has completed which is at some,
unknown point in the future? The natural fit is shared_ptr. It's
exactly what it was designed for. Does this make sense? No, not at all, sorry. The file handle should be kept alive by the future
holding on to it and probably also by the continuations attached by
async_read. Anything else should be handled by the user. Furthermore, if
I'm not completely mistaken, do all OSes internally reference count open
file handles, why duplicate that in user space again? 3. You have stated that you dislike the idea of afio::future<T>
having shared future semantics for the future handle. But can you see
that it is not possible to schedule 100 parallel data read
continuations onto a file open unless the handle has shared future
semantics? After all, if it had future semantics, you could only schedule
exactly one read to each file open because then you would consume the
future. It would not be possible to schedule parallel operations on
any single preceding operation! That's correct. However you miss an important point:
shared_future<T>::shared_future(future<T>&&)
Meaning, a rvalue reference to future<T> can be implicitly converted to a
shared_future<T> (same is true for shared_ptr and unique_ptr, BTW). With
that, no publicly facing api function should ever return a
shared_future<T>, in my book as this doesn't make a lot of sense when a
future is seen as a return value transport endpoint.
It should be in the users responsibility how to deal with that. Some
options:
- just ".get()" the handle and continue with it as a named variable. This
will save you the overhead of attaching continuations over and over again.
- just ".then()" and continue as above
- await it (my favorite actually) and continue as above
- put it into a shared_future and continue as you outlined it.
I understand, of course, that this might get clumsy in your current design,
hence my criticism. Perhaps I am not understanding your concerns correctly? Looks like it. Niall --
ned Productions Limited Consulting
http://www.nedproductions.biz/
http://ie.linkedin.com/in/nialldouglas/ _______________________________________________
Unsubscribe & other changes: