Hi Kenton,

I think I may be running into some of the cleanup complication you're 
implying. During that, I think I'm seeing a straight-up bug, though. The 
snippet of code below is from async-win32.c++, just with the comment blocks 
removed.

  Win32IocpEventPort::~IoPromiseAdapter() {
    if (handle != INVALID_HANDLE_VALUE) {
      if (!CancelIoEx(handle, this)) {
        DWORD error = GetLastError();


        if (error != ERROR_NOT_FOUND && error != ERROR_INVALID_HANDLE) {
          KJ_FAIL_WIN32("CancelIoEx()", error, handle);
        }
 // if error was ERROR_INVALID_HANDLE, the handle will be != 
INVALID_HANDLE_VALUE
      }


      while (handle != INVALID_HANDLE_VALUE) {
        port.waitIocp(INFINITE); // this call chokes on an invalid handle 
and throws an exception
      }
    }
  }


Here's one possible fix:
  ~IoPromiseAdapter() {
    if (handle != INVALID_HANDLE_VALUE) {
      if (!CancelIoEx(handle, this)) {
        DWORD error = GetLastError();


        if( error == ERROR_INVALID_HANDLE ) {
          // we know the handle has gone invalid, so just forget our handle
          handle = INVALID_HANDLE_VALUE;
        }
        else if (error != ERROR_NOT_FOUND ) {
          KJ_FAIL_WIN32("CancelIoEx()", error, handle);
        }
      }


      while (handle != INVALID_HANDLE_VALUE) {
        port.waitIocp(INFINITE);
      }
    }
  }



If it would help, I can express that in the form of a pull request.


Joe


On Friday, April 19, 2019 at 10:03:32 AM UTC-7, Kenton Varda wrote:
>
> Hi Joe,
>
> Take a look at the doc comments for thisCap():
>
>   inline Capability::Client thisCap();
>   // Get a capability pointing to this object, much like the `this` 
> keyword.
>   //
>   // The effect of this method is undefined if:
>   // - No capability client has been created pointing to this object. 
> (This is always the case in
>   //   the server's constructor.)
>   // - The capability client pointing at this object has been destroyed. 
> (This is always the case
>   //   in the server's destructor.)
>   // - Multiple capability clients have been created around the same 
> server (possible if the server
>   //   is refcounted, which is not recommended since the client itself 
> provides refcounting).
>
> Your code is hitting the first bullet point -- you're trying to call 
> thisCap() before any capability client has been created.
>
> What you really want to do here is take advantage of the fact that 
> capability clients are copyable using reference counting. You can create a 
> client, and then keep a copy for yourself, like so:
>
>     auto server = kj::heap<MyTypeImpl>(...);
>     auto& serverRef = *server;
>     MyType::Client capability = kj::mv(server);
>
>     // Send a ref to the client.
>     context.getResults().setApp(capability);
>
>     // Also keep one for ourself, along with a reference to the 
> implementation
>     myServers.add(ClientRefPair { client, serverRef });
>
> By keeping a `Client` that points to your server object, you ensure that 
> the refcount remains non-zero so the object stays alive. As shown above, 
> you can separately keep a reference or pointer to the underlying server 
> object if you need to be able to access it's C++ interface directly.
>
> Note that your server's destructor is only called after all references -- 
> yours, and remote references -- are dropped. If you need to force your 
> server object to be destroyed at a specific time, then you'll need to do 
> one of the following:
>
> - Use a layer of indirection where your server object wraps another object 
> that can be nulled out later, such that all the RPC methods start throwing 
> exceptions once nulled.
> - Wrap the Client object in a membrane (capnp/membrane.h) which you later 
> revoke.
>
> -Kenton
>
> On Thu, Apr 18, 2019 at 10:00 PM Joe Ludwig <[email protected] 
> <javascript:>> wrote:
>
>> I have a really simple RPC protocol:
>> interface AvServer
>> {
>>  createApp @0 ( name: Text ) -> ( app: AvApp );
>> }
>>
>>
>> interface AvApp
>> {
>>  name @0 () -> ( name: Text );
>>
>>
>>  destroy @1 ();
>> }
>>
>>
>> I implement that on the server with two concrete classes:
>>
>>  class AvServerImpl final : public AvServer::Server 
>>  class CAardvarkApp final: public AvApp::Server
>>
>>
>>
>> And then when the client calls createApp I want to create an AvApp, 
>> return a reference to it, and keep ownership of the app itself in some 
>> datastructure on the server:
>>
>>  ::kj::Promise<void> AvServerImpl::createApp( CreateAppContext context )
>>  {
>>  // "works"
>>  context.getResults().setApp( kj::heap<CAardvarkApp>( context.getParams
>> ().getName() ) );
>>
>>
>>  // crashes because "thisHook" is null
>>  auto pApp = kj::heap<CAardvarkApp>( context.getParams().getName() );
>>  context.getResults().setApp( pApp->GetClient() ); // GetClient() is 
>> just a call to thisCap()
>>  m_listApps.push_back( std::move( pApp ) );
>>
>>
>>  return kj::READY_NOW;
>>  }
>>
>>
>> The first approach there works, but the AvServerImpl class doesn't keep 
>> any kind of record of the new CAardvarkApp object, so it won't be able to 
>> do anything with the app outside of a call from this one particular client.
>>
>> What I'm attempting to do in the second approach is create an object on 
>> the heap and set it in the results, but keep ownership of it in the list. I 
>> don't get that far, though, because thisCap crashes because thisHook 
>> returns nullptr.
>>
>> I think I must be missing something really fundamental about how all this 
>> is supposed to work. Am I required to create a unique foo::Server object 
>> for each incoming call that refers to a single logical object? If I have to 
>> pass ownership to the results in order to return values, it seems like 
>> something along that line will be called for. That doesn't sound right, 
>> though, since I'd have to manage an unbounded number of those objects as 
>> requests come in from different clients.
>>
>> Help? :)
>>
>>
>> Joe
>>
>> -- 
>> 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 [email protected] <javascript:>.
>> Visit this group at https://groups.google.com/group/capnproto.
>>
>

-- 
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 [email protected].
Visit this group at https://groups.google.com/group/capnproto.

Reply via email to