c++promisecapnprotokj

Pass context through composed promises in KJ


Playing with the KJ library, I wrote a small TCP servers that reads a "PING" and responds a "PONG". I managed to compose the promises like this:

char buffer[4];
kj::Own<kj::AsyncIoStream> clientStream;

addr->listen()->accept()
    .then([&buffer, &clientStream](kj::Own<kj::AsyncIoStream> stream) {
      clientStream = kj::mv(stream);
      return clientStream->tryRead(buffer, 4, 4);
    }).then([&buffer, &clientStream](size_t read) {
      KJ_LOG(INFO, kj::str("Received", read, " bytes: ", buffer));
      return clientStream->write("PONG", 4);
    }).wait(waitScope);

I had to keep buffer out of the promises and pass a reference to it. This means that buffer has to stay in scope until the last promise finishes. That's the case here, but is there a solution in case it isn't?

Same thing for clientStream: I had to declare it before, then wait until I receives it from accept(), and at this point move it outside and use the reference to it.

Is there a better way to do it? Say like a way to pass some kind of context from promise to promise, always owned by the promises and therefore not having to stay "outside"?


Solution

  • It seems your problem is that your second lambda wants access to the scope of the first lambda, but the way you've organised things prevents that. You've worked around that by just adding variables to their shared "global" scope.

    Instead, you could put the second lambda inside the first, something like this:

    addr->listen()->accept()
        .then([](kj::Own<kj::AsyncIoStream> stream) {
          auto buffer = kj::heapArray<char>(4);
          auto promise = stream->tryRead(buffer.begin(),4,4);
          return promise.then([stream=kj::mv(stream), buffer=kj::mv(buffer)] (size_t read) mutable {
            KJ_LOG(INFO, kj::str("Received", read, " bytes: ", buffer));
            return stream->write("PONG", 4);
          });
        }).wait(waitScope);