--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/capnproto/e4d62be1-d478-47f4-83bf-6b715237c414n%40googlegroups.com.
Yeah, so I'm still working through getting executeSync working. I do wish executeAsync would still be able to do the right thing when invoked from off-thread to avoid creating artificial synchronization points, but I can see it being challenging to accomplish something like that without adding synchronization into the happy path too. Basically the challenges I'm having is trouble terminating the event loop of a server. I *think* the answer I've stumbled upon is to wrap any futures I schedule with a cancellation object that I then cancel when I go to shut down the thread. I'm now seeing a crash on the non shutdown path though that I'm trying to figure out.
I couldn't figure out how to get the message passing going. None of the I/O facilities in KJ look like they are amenable to having one end not connected to the event loop that it was created on. If you have any tips on how to make this work I'd definitely be interested in learning more. It also wasn't clear from the documentation (which I may have overlooked) if I/O objects created on 1 thread's loop can have one end living on another loop and moreover how to adapt them back into a fashion that would be friendly to not being on an event loop at all. For example, some of the documentation around capability transfers says it doesn't work on Windows but then other pieces seem to imply it might (maybe it's only if I'm trying to .
Re #3, that I too couldn't figure out. AsyncIoStream is a bit amenable to that because you can shutdown off-thread since it's a syscall that doesn't return a promise, but that doesn't work for the other ones that don't have a way of closing the FD or changing the FD to be blocking so that it can be used off-thread.
I did actually do #4 but where I got stuck is that I still ended up stuck at step 1 where I wasn't sure how to bridge a native KJ thread & a non-KJ thread. Additionally, various facilities within KJ don't play well with (I *think*) with having a separate event loop (eg. kj::getCurrentThreadExecutor() IIRC) & I couldn't get it quite to work well (+ various facilities like setupIoAsync aren't available & a bunch of I/O code seemingly needs to be reimplemented from scratch to get parity on certain things). There's also a lot of KJ re-implemented in the nodeJS piece in addition to the nodeJS integration so it's a bit hard to at a glance figure out which is the loop adaption pieces needed & which are just for bridging with v8 (at least, not without spending a lot of time to fully groking that file).
On Sun, Jul 5, 2020 at 1:48 PM Vitali Lovich <vlo...@gmail.com> wrote:Yeah, so I'm still working through getting executeSync working. I do wish executeAsync would still be able to do the right thing when invoked from off-thread to avoid creating artificial synchronization points, but I can see it being challenging to accomplish something like that without adding synchronization into the happy path too. Basically the challenges I'm having is trouble terminating the event loop of a server. I *think* the answer I've stumbled upon is to wrap any futures I schedule with a cancellation object that I then cancel when I go to shut down the thread. I'm now seeing a crash on the non shutdown path though that I'm trying to figure out.Make sure you're using Executor::addRef() to take a strong reference from the calling thread. Then if the receiving thread quits, the calling thread should get a DISCONNECTED exception, but shouldn't crash.
FWIW I think we *could* easily have something like execAsync() that doesn't require the caller to be a KJ thread -- but the lambda (and all its owned captures) would be destroyed in the receiving thread, and the calling thread wouldn't get any notification of completion (it would have to implement its own notification if needed). But that's probably fine for most use cases.
I couldn't figure out how to get the message passing going. None of the I/O facilities in KJ look like they are amenable to having one end not connected to the event loop that it was created on. If you have any tips on how to make this work I'd definitely be interested in learning more. It also wasn't clear from the documentation (which I may have overlooked) if I/O objects created on 1 thread's loop can have one end living on another loop and moreover how to adapt them back into a fashion that would be friendly to not being on an event loop at all. For example, some of the documentation around capability transfers says it doesn't work on Windows but then other pieces seem to imply it might (maybe it's only if I'm trying to .Well, it's true you can't use newTwoWayPipe() because it returns a pair of KJ objects which can't transfer between threads.But if you create your own OS pipe and then pass the file descriptors (or HANDLEs) to LowLevelAsyncIoProvider to wrap them, then you can certainly wrap one end in the KJ thread and do whatever you want with the other end.Alternatively you could have the KJ thread listen for network connections on a loopback port, and then connect to it from another thread.Windows unfortunately doesn't have socketpair(), which makes these things a lot easier.
On Sun, Jul 5, 2020 at 3:02 PM Kenton Varda <ken...@cloudflare.com> wrote:On Sun, Jul 5, 2020 at 1:48 PM Vitali Lovich <vlo...@gmail.com> wrote:Yeah, so I'm still working through getting executeSync working. I do wish executeAsync would still be able to do the right thing when invoked from off-thread to avoid creating artificial synchronization points, but I can see it being challenging to accomplish something like that without adding synchronization into the happy path too. Basically the challenges I'm having is trouble terminating the event loop of a server. I *think* the answer I've stumbled upon is to wrap any futures I schedule with a cancellation object that I then cancel when I go to shut down the thread. I'm now seeing a crash on the non shutdown path though that I'm trying to figure out.Make sure you're using Executor::addRef() to take a strong reference from the calling thread. Then if the receiving thread quits, the calling thread should get a DISCONNECTED exception, but shouldn't crash.Am I missing something? Executor doesn't inherit from refcount...
Yeah, that's what I figured. The KJ API is soooo much more user friendly though :). It would be cool to be able to consume the raw file descriptor/socket out of the pipes the LowlevelIoProvider constructs to simplify code/error handling.
On Mon, Jul 6, 2020 at 8:02 AM Vitali Lovich <vlo...@gmail.com> wrote:On Sun, Jul 5, 2020 at 3:02 PM Kenton Varda <ken...@cloudflare.com> wrote:On Sun, Jul 5, 2020 at 1:48 PM Vitali Lovich <vlo...@gmail.com> wrote:Yeah, so I'm still working through getting executeSync working. I do wish executeAsync would still be able to do the right thing when invoked from off-thread to avoid creating artificial synchronization points, but I can see it being challenging to accomplish something like that without adding synchronization into the happy path too. Basically the challenges I'm having is trouble terminating the event loop of a server. I *think* the answer I've stumbled upon is to wrap any futures I schedule with a cancellation object that I then cancel when I go to shut down the thread. I'm now seeing a crash on the non shutdown path though that I'm trying to figure out.Make sure you're using Executor::addRef() to take a strong reference from the calling thread. Then if the receiving thread quits, the calling thread should get a DISCONNECTED exception, but shouldn't crash.Am I missing something? Executor doesn't inherit from refcount...It has a method `addRef()`: https://github.com/capnproto/capnproto/blob/0050fd757683f140b4a07fdc3eab986db0d00c8d/c++/src/kj/async.h#L764This was added fairly recently, though. Maybe it wasn't in 0.8.0.
Yeah, that's what I figured. The KJ API is soooo much more user friendly though :). It would be cool to be able to consume the raw file descriptor/socket out of the pipes the LowlevelIoProvider constructs to simplify code/error handling.Yeah, I think adding `kj::Maybe<int> getFd();` (and `kj::Maybe<void*> getHandle();` on Windows) to the `AsyncIoStream` interface is probably something we should do. I resisted this for a long time since it breaks the abstraction a bit, but there's just too many places where it would be useful, especially for interoperability and optimizations.
Yeah, that's what I figured. The KJ API is soooo much more user friendly though :). It would be cool to be able to consume the raw file descriptor/socket out of the pipes the LowlevelIoProvider constructs to simplify code/error handling.Yeah, I think adding `kj::Maybe<int> getFd();` (and `kj::Maybe<void*> getHandle();` on Windows) to the `AsyncIoStream` interface is probably something we should do. I resisted this for a long time since it breaks the abstraction a bit, but there's just too many places where it would be useful, especially for interoperability and optimizations.And similarly transfer ownership out by having AsyncIoProvider have an unwrap methd that take in an Own<AsyncIoStream> and return an OwnFd?
-Kenton
I don't feel great about the duplication approach (or allowing retrieving the raw handle directly) as it can be subtly tricky to actually use correctly. For example, if you dup & make the dup'ed FD blocking, that will impact correct behavior within cap'n'proto. So as a user you'd end up wanting to always close the other FD after retrieving it. Since all usages have you invalidating the stream & consuming the FD, putting it in the provider is nice: it's symmetrical, you clearly have ownership of the stream, and it's a natural spot to have flags to automatically make the socket/FD blocking again.
-Kenton
I think a potential middle ground for this might be to have the stream give you the fd and a fulfiller. Any I/O operations on the stream then are blocked from executing until after the promise to return the FD is fulfilled.
I don't think the WebServer would need an FdObserver because you could return the FD in blocking mode & the user could dispatch the I/O to a background thread & get back onto the executor to fulfill the promise once complete. Not like sendfile and non-blocking interact all that well.