[asio] Closing posix::stream_descriptor sets it back to blocking

I have two posix::stream_descriptors to the same element, in this case, a dup() of the STDIN_FILENO, reated as such (basically): class MyClass { char buf[4096]; posix::stream_descriptor in; void read(const system::error_code &err, size_t xfer) { // Do something ... then ... in.async_read_some(buffer(buf, 4096), &read); } public: MyClass(io_service &svc) : in(svc, dup(STDIN_FILENO)) { in.async_read_some(buffer(buf, 4096), &read); } }; MyClass *myc; // ... io_service svc; void init() [ char buf[4096]; posix::stream_descriptor stdin(svc, dup(STDIN_FILENO)); stdin.async_read_some(buffer(buf, 4096), &read_func); while (!myc) sleep(1); } // ... void read_func(const system::error_code &err, size_t xfer) { // ... do some stuff ... myc = new MyClass(svc); } The reason I do this is a design one, and it's a lot more complicated than the above, but suffice to say there are lots of other factors involved, and I cannot just pass around the stdin object and send it to the constructor of my MyClass. Making multiple dup's of STDIN_FILENO is really not a problem. And having them in posix::stream_descriptors means they even get cleaned up nicely. That's all well and good, but the problem comes in with how stream_descriptor handles non-blocking I/O. When I do my async_read_some on the stdin object (created in init()), it sets non-blocking I/O, and then sets a flag inside the stdin object. When I create the in object inside of MyClass, it thinks the socket is non-blocking, but when I call the async_read_some function on THAT object, it again sets the FD to non-blocking I/O and sets the internal flag. A little redundant but I can deal with this. In reality that flag should be set by finding out what the blocking state is rather than assuming it's blocking. Here is where the problems start. When the stdin object is destructed (goes out of scope), it CHANGES the blocking state to blocking (because the internal flag says it's non-blocking) before it closes the FD. The code that does this is in detail/reactive_descriptor_service.hpp in both reactive_descriptor_service::destroy() and reactive_descriptor_service::close(). Because both objects are dup's of the same source FD, they all share blocking state, and this now sets my in object inside of MyClass to blocking I/O - without changing the internal flag. Meaning that when I DO get some data, and call MyClass::read, and then go to call async_read_some, now this will be a blocking call because reactive_descriptor_service::async_read_some() sees the flag already set indicating we are non-blocking, and will not re-do it. There is no reason to change the blocking state on a descriptor you are just about to close or destroy. The very idea seems just wrong, you're about to make the descriptor invalid, yet you take the time to change it's blocking state before it becomes invalid? To what purpose? Additionally, there is no way to manually OVERRIDE this internal state with boost calls. For example, executing the following: posix::stream_descriptor::non_blocking_io nb(true); in.command(nb); Will just set a FLAG inside the reactive_descriptor_service that says the user wants the handle to go to non-blocking mode when the next blocking action takes place. It does not alter the state immediately. And since the internal flag state (which is now wrong) is checked first, setting this flag has no effect. The only thing in the end I can do is manually call ioctl myself in my code the first time MyClass::read() is invoked. This then basically makes the blocking state match the internal flag of the 'in' object and from then on everything is fine. But this is klugy. So I would like to request the asio maintainer either a) STOP setting the object back to blocking on close/destroy, given that it serves no purpose when you're just about to close the damn descriptor anyway. b) Set the internal blocking state flag based on the actual blocking state on construction, rather than just assuming it's non-blocking. And c) provide a way to actually change the blocking state of the descriptor, rather than just setting a flag saying we want it changed when you next go to do a blocking I/O operation. There may be a good reason to make such a request, such as the user knowing the state has changed without the reactive_descriptor_service having any knowledge of it, such as in this case. PreZ :)
participants (1)
-
Preston A. Elder