PSA: Robustly handle fatal errors in LLCPP

2 views
Skip to first unread message

Yifei Teng

unread,
Jan 10, 2022, 6:46:28 PM1/10/22
to fidl-dev, anno...@fuchsia.dev

~ If you don't use the low-level C++ FIDL bindings you may stop reading now ~


TLDR: Avoiding switch case on the fidl::Reason enum, prefer one of the more readable accessors instead.


Most LLCPP result/error objects provide a way to access the underlying reason, a fidl::Reason, describing the cause. However, there are some downsides:


  • fidl::Reason is an enum with lots of members, some of which have subtle meanings. We have seen code that misunderstood particular enums and wrote incorrect handling. They were hard to catch because testing for error conditions exhaustively is tricky.

  • Having an enum is challenging for future extensions because users might try to exhaustively match on the members (example in tree). If we add new kinds of reasons, those switch-case would be broken either at compile time or silently at runtime, because they were not prepared for that extra member.


To address those shortcomings, we now provide boolean-returning accessors that help determine if the error is of a particular kind, instead of always exhaustively switching on all potential reasons:



What's changed:

  • While one could still reach for the underlying enum and write switch case if they really need it, it is now discouraged. When writing a switch case on the enum, make sure to add a default: case.

  • Consider if some of the above accessors lead to simpler error handling code than switching on the reason. Here's an example showing before/after using the accessors:


Before:


fidl::OnUnboundFn<EchoImpl> unbound_handler =

  [self = std::move(self)](

    EchoImpl* impl, fidl::UnbindInfo info,

    fidl::ServerEnd<fuchsia_examples::Echo> server_end

) {

      switch (info.reason()) {

        case fidl::Reason::kUnbind:

        case fidl::Reason::kClose:

          // These are initiated by ourselves.

          return;

        case fidl::Reason::kPeerClosed:

          std::cout << "Client disconnected" << std::endl;

          return;

        default:

          std::cerr << "server error: " << info

<< std::endl;

          return;

     }

   };


After:


fidl::OnUnboundFn<EchoImpl> unbound_handler =

  [self = std::move(self)](

    EchoImpl* impl, fidl::UnbindInfo info,

    fidl::ServerEnd<fuchsia_examples::Echo> server_end

) {

      if (info.is_user_initiated()) {

        return;

      }

      if (info.is_peer_closed()) {

        std::cout << "Client disconnected" << std::endl;

        return;

      }

      std::cerr << "server error: " << info << std::endl;

};



The accessors work in regular FIDL call results too:


fidl::WireResult result = client->Foo();

if (!result.ok()) {

  if (result.error().is_peer_closed()) {

    // failed because the server closed their endpoint

  }

}


Please let us know on fidl...@fuchsia.dev if you have questions or suggestions. Thanks.


Cheers, Yifei


Reply all
Reply to author
Forward
0 new messages