Server mode

Pass --server <url> to make Visao act as a long-lived analysis service. Clients exchange JSON requests and responses with the server over NNG REQ/REP sockets. They manage workspaces and analyses explicitly, and consume results as they arrive. The server keeps its state between requests until you stop it.

Quick start

visao --server tcp://127.0.0.1:5500

The server prints Server is ready to receive incoming requests and waits. A client then adds a workspace, attaches the files, starts an analysis, polls its state, and destroys the analysis and the workspace.

For programmatic clients see the libasp (Analysis Server Protocol) library. It wraps the request and response vocabulary in a typed C/C++ API and ships demo projects.

Architecture: actors

The server is built on a small in-tree actor framework (frontend/userver/actor/) instead of a single thread + mutex. Four cooperating components handle each request:

┌───────────────────┐  Request   ┌──────────────────────────┐
│ NetworkReceiver   │ ─────────► │ RequestProcessorActor    │
│ (sockets, poll)   │            │ (workspaces, analyses)   │
└───────────────────┘            └────────────┬─────────────┘
                                              │ spawn / cancel
                                              ▼
                                 ┌──────────────────────────┐
                                 │ AnalysisWorker (per      │
                                 │ analysis, sacore_thread) │
                                 └────────────┬─────────────┘
                                              │ final Response
                                              ▼
                                 ┌──────────────────────────┐
                                 │ ResponseSenderActor      │
                                 │ (per-pipe queue, ticked) │
                                 └────────────┬─────────────┘
                                              │ proto->send()
                                              ▼
                                            network
  • NetworkReceiver owns the bound sockets. A single thread polls proto->receive() (5 ms NNG timeout), deserialises the request, and posts a request_envelope to RequestProcessor.

  • RequestProcessorActor owns the shared analysis state (common_data: workspaces map, analyses map, FrontendInstance) privately. No other actor reads or writes those structures. Requests are processed strictly serially on the actor’s own thread. Each response goes out as a response_envelope to ResponseSender.

  • AnalysisWorker is spawned per AnalysisStart request. It drives the runOneSubJob loop on its own thread, then emits a CheckState response back through ResponseSender to notify the client. AnalysisDestroy cancels and joins it.

  • ResponseSenderActor holds one response queue for each pipe. NNG REQ/REP needs alternation (one REP for each client REQ). Therefore the actor drains a queue only when proto->is_queue_ready reports that the pipe is ready. A tick of 5 ms checks the readiness again when no new response arrives.

Inter-actor communication is by value through std::queue inboxes; there is no shared mutable state outside each actor.

Tuning flags

These options apply only in --server mode and are ignored in console mode:

--server-max-analyses <N>

Cap on concurrent AnalysisWorker threads. Requests past the cap come back as Error("Server analysis cap reached"). 0 (default) means unlimited.

--server-response-tick-ms <N>

ResponseSender drain interval in milliseconds. Default 5. Lower means snappier reply when a pipe becomes ready; higher reduces wakeups when idle.

--server-log-traffic

Print every received Request and emitted Response to stdout. Off by default. Useful for protocol debugging; noisy under load.

--server-graceful / --server-no-graceful

On by default. When on, the server installs SIGINT/SIGTERM handlers that set an atomic shutdown flag. The main thread breaks out of its idle loop. Stack-unwind runs actor destructors in LIFO order (NetworkReceiver stops first → drains RequestProcessor → drains ResponseSender). When off, the default signal action kills the process immediately. Use this form for a hard stop in CI.

Binding multiple endpoints

Multiple --server arguments bind multiple endpoints on the same process; they share the workspace/analysis state.

visao --server tcp://127.0.0.1:5500 \
      --server tcp://10.0.0.5:5500

Lifecycle and cache

Toolchain detection runs once during FrontendInstance construction. On a fresh CI image, run visao --update-toolchains before you launch the server. The first client request then uses the warm cache.

The server holds workspaces and analyses in memory for the lifetime of the process. Each run issues new Workspace and Analysis IDs. For persistence, use the optional DAL (Database Access Layer) integration. DAL journals analysis results to MongoDB, and the #ifdef ENABLE_DAL switch selects that backend at build time.

libasp

Hand-rolling JSON requests gets old fast. For real applications use libasp (Analysis Server Protocol library), which gives you a typed C/C++ client API, request/response factories, and a small event loop. The ASP documentation in the asp subproject contains demo projects and the full protocol description.