C++ API

Common pitfalls

Common C++ API misuse patterns, why they fail, and how to fix them.

This page describes two important usage rules of the Blazeauth C++ API. Both are easy to violate when mixing synchronous and asynchronous code.

Rules first

Do not call sync API inside async callbacks

Async callbacks run on the library I/O thread. Inside async callbacks, call only other async methods.

Do not destroy session inside its own async callbacks

Do not let the last owning blaze::session instance die inside one of its own async callbacks. Destroy it only after control returns to your own thread.

Do not call synchronous API inside async callbacks

Async callbacks are executed on the library I/O thread. Synchronous API calls internally wait for async completion, so calling a sync method from that same thread would block the thread that must deliver the result.

Because of that, the library rejects such calls with std::errc::operation_would_block.

What happens

  • Throwing sync overloads throw std::system_error with std::errc::operation_would_block.
  • No-throw sync overloads store std::errc::operation_would_block into the passed std::error_code.

Why this happens

The synchronous API waits until the async operation completes. If that wait starts from the same I/O thread that must run the callback, the operation cannot make progress correctly.

Wrong

session.async_connect([&session](std::error_code ec, blaze::api_server) {
  if (ec) {
    return;
  }

  // Wrong: sync API inside async callback.
  blaze::application app = session.initialize("your-api-key", "client-id");
});

Correct

session.async_connect([&session](std::error_code ec, blaze::api_server) {
  if (ec) {
    return;
  }

  session.async_initialize("your-api-key", "client-id",
    [](std::error_code init_ec, blaze::application app) {
      if (init_ec) {
        return;
      }

      if (!app.good()) {
        return;
      }

      // Continue with other async calls here.
    });
});

Safe pattern

If your application prefers synchronous flow, finish the async callback quickly and hand control back to your own thread/executor. Do not switch from async API to sync API while still executing inside the library callback.

Do not destroy the session inside its own async callback

The internal websocket_session object owns the library I/O runtime. Destroying the last owning blaze::session from one of its own async callbacks means destruction starts on that same I/O thread.

That is not supported.

What happens

If destruction reaches the internal websocket session on the I/O thread, the library terminates the process.

Why this happens

During destruction, the session must:

  • shut down the websocket cleanly;
  • invoke shutdown-related cleanup;
  • stop the internal I/O runtime;
  • wait for the worker thread to finish.

Doing that from the worker thread itself is invalid. It turns destruction into a self-stop/self-join scenario, which the current architecture does not permit. The library therefore treats it as fatal misuse.

Wrong

std::shared_ptr<blaze::session> session = std::make_shared<blaze::session>();

session->async_connect([session](std::error_code ec, blaze::api_server) mutable {
  if (ec) {
    session.reset();
    return;
  }

  // Wrong: this may destroy the last owner on the library I/O thread.
  session.reset();
});

Correct

If you need to stop using the session from async code:

  1. call async_shutdown(...) if shutdown is needed;
  2. signal your outer code that the async chain is finished;
  3. destroy/reset the session on your own thread after the callback returns.
#include "blazeauth/api/api.hpp"

#include <future>
#include <memory>

int main() {
  std::shared_ptr<blaze::session> session = std::make_shared<blaze::session>();

  std::promise<void> done;
  std::future<void> result = done.get_future();

  session->async_connect([session, &done](std::error_code ec, blaze::api_server) {
    if (ec) {
      done.set_value();
      return;
    }

    session->async_shutdown([&done](std::error_code) {
      done.set_value();
    });
  });

  result.get();

  // Safe: destruction happens after callback chain is finished,
  // on the caller thread.
  session.reset();
  return 0;
}

Practical rules

  • If you start with async API, stay async for the whole callback chain.
  • Keep session ownership outside the library callbacks.
  • Use async shutdown inside async code.
  • Destroy the session only after control has returned to your own thread.

On this page