The docs is nowadays fairly clear on the close callback:
//! @item //! When an error or an end-of-stream in the read direction //! occurs, @[close_cb] will be called. @[errno] will return the //! error, or zero in the case of an end-of-stream. //! //! The name of this callback is rather unfortunate since it //! really has nothing to do with a close: The stream is still //! open when @[close_cb] is called (you might not be able to read //! and/or write to it, but you can still use things like //! @[query_address], and the underlying file descriptor is still //! allocated). Also, this callback will not be called for a local //! close, neither by a call to @[close] or by destructing this //! object. //! //! Also, @[close_cb] will not be called if a remote close only //! occurs in the write direction; that is handled by @[write_cb] //! (or possibly @[write_oob_cb]). //! //! Events to @[read_cb] and @[close_cb] will be automatically //! deregistered if an end-of-stream occurs, and all events in the //! case of an error. I.e. there won't be any more calls to the //! callbacks unless they are reinstalled. This doesn't affect the //! callback settings - @[query_read_callback] et<A0>al will still //! return the installed callbacks.
Thus a call to Stdio.File.close is necessary to close the fd, i.e. this works:
for(int i=0; i<300; i++) {object q=Stdio.File();q->connect("127.0.0.1", 14741);q->write("GET / HTTP/1.0\r\n\r\n");q->set_id(q);q->set_nonblocking(lambda(){werror("data");},lambda(){},lambda(object q){werror("closed\n");q->close();});}
The ref from the backend is only there as long as async events are requested. As the doc says the read and close events are deregistered when the close callback is called, but in your example you still have the write event (if the remote end only has closed its write direction, you could possibly still write on the local end). So registering no write callback also works:
for(int i=0; i<300; i++) {object q=Stdio.File();q->connect("127.0.0.1", 14741);q->write("GET / HTTP/1.0\r\n\r\n");q->set_nonblocking(lambda(){werror("data");},0,lambda(){werror("closed\n");});}