Implementation:Vespa engine Vespa ConfigSubscriber Close
| Field | Value |
|---|---|
| Sources | Vespa |
| Domains | Configuration, Distributed_Systems |
| Type | API Doc |
| Last Updated | 2026-02-09 12:00 GMT |
Overview
ConfigSubscriber::close gracefully terminates all configuration subscriptions, interrupting any pending blocking calls and releasing all associated resources.
Description
The close() method is the entry point for shutting down a config subscriber. It delegates to ConfigSubscriptionSet::close(), which performs the actual shutdown sequence:
- State transition: Under a mutex lock, sets the subscription set state to
CLOSEDand notifies the condition variable. This immediately unblocks any thread waiting innextConfig()ornextGeneration(). - Unsubscribe: For each subscription in the subscription list, calls
IConfigManager::unsubscribe()to deregister the subscription from the config manager. This stops the config manager from fetching updates for this subscription. - Close subscriptions: Calls
close()on each individualConfigSubscription, which sets its closed flag and releases the underlying source.
The ConfigSubscriptionSet destructor also calls close(), providing RAII-style cleanup as a safety net.
After close, the following behavior is guaranteed:
isClosed()returnstruenextConfig()andnextGeneration()returnfalseimmediatelysubscribe()throwsConfigRuntimeException
Code Reference
Source Location
- Repository
vespa-engine/vespa- File (ConfigSubscriber declaration)
config/src/vespa/config/subscription/configsubscriber.h- Line
- 96
- File (ConfigSubscriber definition)
config/src/vespa/config/subscription/configsubscriber.cpp- Lines
- 33--36
- File (ConfigSubscriptionSet definition)
config/src/vespa/config/subscription/configsubscriptionset.cpp- Lines
- 113--124
ConfigSubscriber::close Signature
void ConfigSubscriber::close();
ConfigSubscriber::close Implementation
void
ConfigSubscriber::close()
{
_set.close();
}
ConfigSubscriptionSet::close Implementation
void
ConfigSubscriptionSet::close()
{
{
std::lock_guard guard(_lock);
_state = CLOSED;
_cond.notify_all();
}
for (const auto & subscription : _subscriptionList) {
_mgr.unsubscribe(*subscription);
subscription->close();
}
}
isClosed Check
bool ConfigSubscriber::isClosed() const {
return _set.isClosed();
}
bool ConfigSubscriptionSet::isClosed() const noexcept {
return (_state.load(std::memory_order_relaxed) == CLOSED);
}
I/O Contract
Inputs
The close() method takes no parameters.
Outputs
| Name | Type | Description |
|---|---|---|
| return | void |
No return value. The method completes when all subscriptions have been unsubscribed and closed. |
Side Effects
- Sets the subscription set state to
CLOSED(atomic write with relaxed ordering) - Notifies the condition variable, waking any thread blocked in
acquireSnapshot() - Unsubscribes all subscriptions from the config manager
- Closes each individual
ConfigSubscription - After return, the subscriber is permanently closed and cannot be reused
Thread Safety
The state transition and condition variable notification are performed under a mutex lock, making close() safe to call from a different thread than the one blocked in nextConfig()/nextGeneration(). This is the intended usage pattern: a control thread calls close() to signal a worker thread to stop its config polling loop.
State Machine
The subscription set follows this state machine:
OPEN --[nextConfig/nextGeneration]--> FROZEN --[acquireSnapshot succeeds]--> CONFIGURED
| | |
+-----[close()]----> CLOSED <---------+----------[close()]--------------------+
The CLOSED state is terminal. There is no way to reopen a closed subscriber; a new ConfigSubscriber must be created.
Usage Examples
Graceful Shutdown from Control Thread
#include <vespa/config/subscription/configsubscriber.h>
#include "config-myapp.h"
#include <thread>
using namespace config;
ConfigSubscriber subscriber;
auto handle = subscriber.subscribe<MyappConfig>("myapp/instance1");
// Worker thread runs the config polling loop
std::thread worker([&subscriber, &handle]() {
subscriber.nextConfig();
auto cfg = handle->getConfig();
applyConfig(*cfg);
while (!subscriber.isClosed()) {
if (subscriber.nextConfig(60s)) {
auto cfg = handle->getConfig();
applyConfig(*cfg);
}
}
// Thread exits cleanly when subscriber is closed
});
// ... later, from the control thread ...
subscriber.close(); // Interrupts nextConfig(), worker exits loop
worker.join();
RAII-Based Cleanup
void runWithConfig() {
ConfigSubscriber subscriber;
auto handle = subscriber.subscribe<MyappConfig>("myapp/instance1");
subscriber.nextConfig();
auto cfg = handle->getConfig();
doWork(*cfg);
// subscriber destructor calls close() automatically
// All subscriptions are unsubscribed and resources released
}
Checking Closed State
if (subscriber.isClosed()) {
LOG(info, "Subscriber has been closed, exiting config loop");
return;
}