Plugin statuses and dependencies
v0.1.14
Every plugin record carries a status, and the status decides whether the plugin is part of its event's chain. This page explains the five statuses, the two decisions behind them, how dependencies hold and release plugins, how the library orders a chain, and what each lifecycle call does to a status.
The five statuses
| Status | Meaning | Reached by |
|---|---|---|
:registered | Stored, and not started yet. | The first registration. |
:started | Running, and part of its event's chain. | start |
:restarted | Running, and part of its event's chain. | restart, enable, or the release of a held plugin. |
:stopped | An operator switched it off. It survives restarts and dependency changes. | stop |
:held | At least one dependency is not active. The library releases it when they are. | Registration, enable, or a change to a dependency. |
When the library rebuilds an event, it keeps each plugin that is not :stopped or :held and whose dependencies are all active. A dependency is active when its status is :started or :restarted.
Two decisions that never override each other
Only two statuses record a decision. :stopped is the decision of an operator. :held is the decision of the library, taken because a dependency is not active.
- Releasing a dependency never switches on a plugin an operator stopped.
startandrestartrefuse a stopped plugin. Onlyenablebrings it back, so no reload or reset path can undo the operator.- A held plugin can still be stopped, because being blocked by a dependency and being switched off are different things.
- A boot never starts a stopped plugin on its own.
Status writes are durable. The library commits them synchronously and flushes the log, so a plugin an operator stopped stays stopped after an unclean shutdown.
Dependencies
depends: [OtherPlugin] means "do not run me unless OtherPlugin runs". The list holds plugin modules, and a dependency may belong to another event. All of them must be active.
- Stopping, unregistering or holding a plugin holds every plugin that depends on it. The hold is transitive, so a chain of dependents is held as a unit, and it crosses events. The held plugins leave their compiled chains in the same call.
- Starting, restarting or enabling the plugin releases what it was holding. A plugin with two dependencies stays held until both are active.
- Nothing keeps a list of dependents. The library reads the reverse edges from the stored
dependsof every plugin, whichEvent.dependents/1returns. - A plugin whose dependency is not active when it registers is stored as
:held. On boot, a plugin held because a dependency on another event has not started yet is released when that dependency starts. - The library rejects a dependency cycle at registration, so a plugin can never stay held forever behind itself. The error has the action
:registerand the field:depends.
Order
A chain runs in this order:
- A plugin runs after every plugin it depends on, whatever their priorities.
- Among the plugins that are ready, the smaller
priorityruns first. Priority is an integer from0to100, and the default is100. - Equal priorities run in order of the plugin's module name.
What each call does
| Call | Refused when | Result |
|---|---|---|
register | The module is not loaded, the priority is out of range, or the dependencies form a cycle. | A new plugin is stored as :registered, or :held. An existing plugin takes the priority, extra data and dependencies from the code and keeps its status, except that it moves into or out of :held as its dependencies require. |
start | The plugin has no record, is stopped, or has a dependency that is not active. | :started |
restart | The plugin has no record, is stopped, or has a dependency that is not active. | :restarted |
stop | The plugin has no record, or is already stopped. | :stopped. Dependents are held. |
enable | The plugin has no record. | :restarted, or :held when a dependency is not active. Dependents are released. |
unregister | The plugin has no record. | The record is deleted and the process stops. Dependents are held. |
Each call that changes a chain rebuilds the events it touches, so the stored statuses and the compiled chains agree.
Example: a dependency that stops
Take Root, Mid and Leaf, where Mid depends on Root and Leaf depends on Mid. Each row starts with all three plugins started, each on its own event.
| Step | Root | Mid | Leaf |
|---|---|---|---|
Stop Root | :stopped | :held | :held |
Enable Root | :restarted | :restarted | :restarted |
Stop Mid, stop Root, enable Root | :restarted | :stopped | :held |
In the last row Mid stays stopped, because an operator stopped it, and Leaf stays held, because Mid is not active. The library's dependency tests check these sequences.