Skip to content

docs(protocol): threading, lifecycle and config contracts match the host - #235

Merged
facontidavide merged 1 commit into
mainfrom
docs/contract-text-matches-host
Nov 13, 2026
Merged

facontidavide merged 1 commit into
mainfrom
docs/contract-text-matches-host

Conversation

@facontidavide

Copy link
Copy Markdown
Contributor

What

Comments and Markdown only. The written DataSource and Toolbox contracts are brought in line with what the reference host (PlotJuggler 4) does, before 1.0 freezes them. No code token changes: for each of the six edited headers, the comment-stripped preprocessor output is byte-identical before and after.

1. Threading

A normative per-slot table on PJ_data_source_vtable_t and PJ_toolbox_vtable_t, with the guides, REQUIREMENTS.md and ARCHITECTURE.md made to agree. What was wrong before:

  • start/stop were tagged [main-thread]. In the host, a file import's start runs on a worker and a stream's start runs on the GUI thread; stop runs on the poll worker.
  • The guide promised no overlapping callbacks. set_active_topics runs on the GUI thread concurrently with poll.
  • The toolbox guide said "host thread only" while the header tagged the parser-ingest slots [thread-safe].
  • A comment said no host implements register_object_topic_on_dataset and set_object_topic_retention. The host implements both.

Two tags change, and tags are normative, so please check them:

  • show_message_box: [main-thread] → [thread-safe]. Its own text already said "may call from any thread".
  • current_state: [thread-safe] → [main-thread]. The SDK base classes keep a plain, non-atomic state written by start/stop.

One rule is kept on purpose: a plugin must not call [stream-thread] host slots from a thread of its own after start returns. The host's poll worker calls flushPending() after every poll, which walks a map the host itself documents as unsynchronised.

2. Advisory slots

notify_state, pause, resume, current_state and on_data_changed are documented as advisory: the host has an empty notify_state, pauses host-side through write targets, and never calls the other four. The "MUST call notifyState" rule and the six-state machine are replaced by the signals the host acts on: the start and poll results, request_stop, is_stop_requested and complete_ingest. The loader's required-slot rule is not changed.

3. Config persistence

save_config/load_config now state the real contract: a UTF-8 JSON object; the keys the host writes into it (filepath, _parser_config, __pj_saved_filepath) and the one the plugin writes for the host (__pj_fanout); load_config("") on a stream's first run. Passages about ConfigEnvelope are untouched here; #231 removes them.

Open questions this could not settle from code

  1. on_data_changed: is "the host never calls it" intended, or a host bug? toolbox_quaternion relies on it to reinstall its node when a layout loads before its data, and toolbox_fft to invalidate stale results. The text says "do not rely on it".
  2. Push from a plugin's own thread: forbidden as written. Supporting it needs locking in the host.
  3. Stream and file layouts persist different things: a stream layout stores the config passed to load_config; a file layout stores save_config output.
  4. set_object_topic_retention is documented to return false on a bad handle; the host returns true and ignores it. Not edited here.
  5. The _ key prefix is reserved for the host in the new text. The host only uses _parser_config today.
  6. StreamSourceBase::start does not call onStop() when onStart() fails, and the host destroys without stop after a failed start, so threads spawned before the failure can leak. Documented; a base-class fix would be a code change.

Verification

No build is needed (comments and Markdown). pre-commit (clang-format 22) is clean. Rebased onto main after the 1.0.0-dev.1 commit.

🤖 Generated with Claude Code

@facontidavide
facontidavide force-pushed the docs/contract-text-matches-host branch from d69195c to e620dae Compare November 13, 2026 23:19
Comments and Markdown only; no code token changes.

- Normative per-slot threading tables for the DataSource and Toolbox vtables,
  derived from what the host does: a stream's start runs on the GUI thread, a
  file import's on a worker; set_active_topics runs concurrently with poll;
  [stream-thread] host slots are callable only inside start, poll or stop.
- notify_state, pause, resume, current_state and on_data_changed are documented
  as advisory. The lifecycle is described by the signals the host acts on.
- save_config/load_config state the real contract: a JSON object, the keys the
  host writes into it, and load_config("") on a first run.
- The two toolbox-host tail slots a comment called unimplemented are
  implemented by the host and are retagged.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@facontidavide
facontidavide force-pushed the docs/contract-text-matches-host branch from e620dae to 8b57afe Compare November 13, 2026 23:22
@facontidavide
facontidavide merged commit 1be8a51 into main Nov 13, 2026
4 checks passed
@facontidavide
facontidavide deleted the docs/contract-text-matches-host branch November 13, 2026 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant