Documentation
¶
Overview ¶
plugin_connect_deadline.go — THE bound on the plugin readiness READ, plus the bounded teardown that lets a failed connect actually report.
charly#588 (RCA): `charly box build` (image-build step) and `charly check live` (plugin resolution) were each observed to block INDEFINITELY — the process parked (state S / futex_wait), its `__plugin serve` children alive and idle at 0.0% CPU, no step output, no error, and no summary.yml for 25+ minutes. The unbounded wait is the readiness READ that follows the handshake: describe() → conn.Meta.Describe(ctx, …) (plugin_grpc.go), a plain gRPC unary call issued on the loader's context, which is context.Background() at every call site — so a plugin child that completes the go-plugin handshake and then never answers leaves that call parked FOREVER.
The other legs are already bounded, which is why the bound belongs HERE and not around the whole connect: go-plugin bounds the spawn→handshake leg with StartTimeout (client.go: `timeout := time.After(c.config.StartTimeout)`, set from the project's resolved readiness per_attempt — plugin_transport.go's localPluginClientConfig), and the build BEFORE the connect is legitimately slow (a cold `go build` of a candy plus its module graph takes minutes), so it must not be bounded by a readiness deadline at all. Reproduced before the fix, and kept as a regression test (plugin_connect_deadline_test.go): LocalTransport.Connect against a child whose Describe never returns was still parked after 60s, while the identical connect under this bound returned in ~the bound with "plugin describe: rpc error: code = DeadlineExceeded".
WHY A FIXED CONSTANT AND NOT the resolved readiness per_attempt: resolving readiness from inside the connect path is a DEADLOCK, measured while building this fix. loadedReadiness() runs LoadUnified, which materialises the project through the plugin-loader candy, which calls loadBuiltinPluginUnits → builtinGateOnce.Do — and the builtin gate is ITSELF a caller of the connect seam, so a seam that resolved readiness would re-enter that non-reentrant sync.Once from inside its own callback and park forever (SIGQUIT dump: goroutine 1 in loadBuiltinPluginUnits → sync.Once.doSlow → loadBuiltinPluginUnits.func1 → loadedReadiness → sync.Once.doSlow → loadBuiltinPluginUnits). The bound therefore stays a plain constant, and it costs nothing: the per_attempt knob keeps governing the SPAWN leg (go-plugin's StartTimeout) while this constant governs only the post-handshake READ, which is a sub-second operation for a healthy plugin (its schema is a CUE string already in the child's memory) — the two never interact, so raising per_attempt for a heavy roster still moves what it always moved.
Code generated by charly/internal/pluginsgen (run by scripts/bootstrap-charly.sh). DO NOT EDIT.
One registerCompiledPlugin() call per plugin candy named in charly.yml `compiled_plugins:`. Regenerated by `scripts/bootstrap-charly.sh` before `go build`. Each compiled-in plugin module is a `require` pin in charly/go.mod resolved from the module proxy (no `use` directive in go.work — see the generator's go.work writer); a dev build (-dev-plugin) resolves a plugin through go.work.dev instead. Edit charly.yml's compiled_plugins and re-run the generator, never this file.
Code generated by charly/internal/pluginsgen (run by scripts/bootstrap-charly.sh). DO NOT EDIT.
pluginProviderRefs maps "<class>:<word>" -> the canonical candy ref of the plugin that serves it, DERIVED from every plugin repo's own `plugin:` block (its `providers:` + `source:`). This is the ONE word->provider fact: it lives in the plugin repos, never in charly. Regenerate with the rest of the wiring.
Source Files
¶
- agent_target_cmd.go
- bootstrap_phase.go
- build_overlay.go
- builder_venue.go
- check_cmd.go
- check_endpoint_resolve.go
- check_graphics_endpoint.go
- check_kit_adapter.go
- check_venue_resolve.go
- checkrun.go
- checkrun_act.go
- checkrun_charly_verbs.go
- checkspec.go
- checkverb_result.go
- cli_model_cmd.go
- commands.go
- credential_plugin.go
- cue_schema.go
- deploy_add_cmd.go
- deploy_target_dispatch.go
- deploy_tree.go
- dispatch_build_ensure.go
- distro_resolve.go
- embed_defaults.go
- engine_provider.go
- git_client.go
- gpu_allocate.go
- gpu_shim.go
- host_build_box_fetch_resolve.go
- host_build_box_ref_resolve.go
- host_build_buildengine.go
- host_build_check_bed_gpu_prereq.go
- host_build_check_load_plugins.go
- host_build_cli.go
- host_build_construct_step.go
- host_build_deploy_node_del_dispatch.go
- host_build_deploy_plugins_connect.go
- host_build_loader_floor.go
- host_build_pod_lifecycle_dispatch.go
- host_build_remote_image_resolve.go
- host_build_resolve_target_add.go
- host_build_retention_defaults.go
- host_exec.go
- image.go
- init_resolve.go
- layers.go
- load_executor_host.go
- loader_threaded.go
- main.go
- materialize.go
- node_desugar.go
- oci_step_emit.go
- planrun_adapter.go
- plugin_activity.go
- plugin_build_stamp.go
- plugin_checkcontext_reverse.go
- plugin_cmd.go
- plugin_command_prescan.go
- plugin_connect_deadline.go
- plugin_dispatch_reverse.go
- plugin_executor_reverse.go
- plugin_grpc.go
- plugin_inproc.go
- plugin_inproc_reverse.go
- plugin_loader.go
- plugin_prescan.go
- plugin_provider_common.go
- plugin_providers_cmd.go
- plugin_requires.go
- plugin_transport.go
- plugins_generated.go
- plugins_refs_generated.go
- pod_lifecycle_dispatch.go
- pod_lifecycle_verb.go
- preflight_phase.go
- provider.go
- provider_checkenv.go
- provider_command.go
- provider_command_external.go
- provider_deploy.go
- provider_invoke.go
- provider_kind.go
- provider_kind_invoke.go
- provider_registry.go
- provider_step.go
- provider_verb.go
- readiness_config.go
- registry_bootstrap.go
- reserved_registry.go
- resource_resolve.go
- step_emit_hostbuild.go
- substrate_template_resolve.go
- tasks.go
- unified_targets.go
- update_deploy_dispatch.go
- validate_project_host.go
- verb_builtins.go
- version.go
- vm_plugin_client.go
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
pluginsgen
command
Command pluginsgen emits the compiled-in plugin wiring from a charly.yml's `compiled_plugins:` list: charly/plugins_generated.go (one registerCompiledPlugin call per plugin candy) AND the repo-root go.work (a `use` directive per candy, so `go build ./charly` resolves the candy imports in plugins_generated.go).
|
Command pluginsgen emits the compiled-in plugin wiring from a charly.yml's `compiled_plugins:` list: charly/plugins_generated.go (one registerCompiledPlugin call per plugin candy) AND the repo-root go.work (a `use` directive per candy, so `go build ./charly` resolves the candy imports in plugins_generated.go). |