@@ -17,9 +17,7 @@ use sled_storage::manager::NestedDatasetConfig;
17
17
use sled_storage:: manager:: NestedDatasetListOptions ;
18
18
use sled_storage:: manager:: NestedDatasetLocation ;
19
19
use slog:: Logger ;
20
- use slog:: warn;
21
20
use std:: sync:: Arc ;
22
- use std:: sync:: Mutex ;
23
21
use std:: sync:: OnceLock ;
24
22
use tokio:: sync:: watch;
25
23
@@ -59,6 +57,16 @@ pub enum TimeSyncConfig {
59
57
Skip ,
60
58
}
61
59
60
+ #[ derive( Debug ) ]
61
+ pub struct ConfigReconcilerHandleSpawnToken {
62
+ key_requester : StorageKeyRequester ,
63
+ time_sync_config : TimeSyncConfig ,
64
+ reconciler_result_tx : watch:: Sender < ReconcilerResult > ,
65
+ currently_managed_zpools_tx : watch:: Sender < Arc < CurrentlyManagedZpools > > ,
66
+ ledger_task_log : Logger ,
67
+ reconciler_task_log : Logger ,
68
+ }
69
+
62
70
#[ derive( Debug ) ]
63
71
pub struct ConfigReconcilerHandle {
64
72
raw_disks_tx : RawDisksSender ,
@@ -67,15 +75,8 @@ pub struct ConfigReconcilerHandle {
67
75
reconciler_result_rx : watch:: Receiver < ReconcilerResult > ,
68
76
currently_managed_zpools_rx : CurrentlyManagedZpoolsReceiver ,
69
77
70
- // `None` until `spawn_reconciliation_task()` is called.
78
+ // Empty until `spawn_reconciliation_task()` is called.
71
79
ledger_task : OnceLock < LedgerTaskHandle > ,
72
-
73
- // We have a two-phase initialization: we get some of our dependencies when
74
- // `Self::new()` is called and the rest when
75
- // `Self::spawn_reconciliation_task()` is called. We hold the dependencies
76
- // that are available in `new` but not needed until
77
- // `spawn_reconciliation_task` in this field.
78
- reconciler_task_dependencies : Mutex < Option < ReconcilerTaskDependencies > > ,
79
80
}
80
81
81
82
impl ConfigReconcilerHandle {
@@ -93,7 +94,7 @@ impl ConfigReconcilerHandle {
93
94
key_requester : StorageKeyRequester ,
94
95
time_sync_config : TimeSyncConfig ,
95
96
base_log : & Logger ,
96
- ) -> Self {
97
+ ) -> ( Self , ConfigReconcilerHandleSpawnToken ) {
97
98
let mount_config = Arc :: new ( mount_config) ;
98
99
99
100
// Spawn the task that monitors our internal disks (M.2s).
@@ -111,14 +112,27 @@ impl ConfigReconcilerHandle {
111
112
base_log,
112
113
) ;
113
114
114
- // Stash the dependencies the reconciler task will need in
115
- // `spawn_reconciliation_task()`.
116
115
let ( reconciler_result_tx, reconciler_result_rx) =
117
116
watch:: channel ( ReconcilerResult :: default ( ) ) ;
118
117
let ( currently_managed_zpools_tx, currently_managed_zpools_rx) =
119
118
watch:: channel ( Arc :: default ( ) ) ;
120
- let reconciler_task_dependencies =
121
- Mutex :: new ( Some ( ReconcilerTaskDependencies {
119
+
120
+ (
121
+ Self {
122
+ raw_disks_tx,
123
+ internal_disks_rx,
124
+ dataset_task,
125
+ ledger_task : OnceLock :: new ( ) ,
126
+ reconciler_result_rx,
127
+ currently_managed_zpools_rx :
128
+ CurrentlyManagedZpoolsReceiver :: new (
129
+ currently_managed_zpools_rx,
130
+ ) ,
131
+ } ,
132
+ // Stash the dependencies the reconciler task will need in
133
+ // `spawn_reconciliation_task()` inside this token that the caller
134
+ // has to hold until it has the other outside dependencies ready.
135
+ ConfigReconcilerHandleSpawnToken {
122
136
key_requester,
123
137
time_sync_config,
124
138
reconciler_result_tx,
@@ -127,25 +141,19 @@ impl ConfigReconcilerHandle {
127
141
. new ( slog:: o!( "component" => "SledConfigLedgerTask" ) ) ,
128
142
reconciler_task_log : base_log
129
143
. new ( slog:: o!( "component" => "ConfigReconcilerTask" ) ) ,
130
- } ) ) ;
131
-
132
- Self {
133
- raw_disks_tx,
134
- internal_disks_rx,
135
- dataset_task,
136
- ledger_task : OnceLock :: new ( ) ,
137
- reconciler_result_rx,
138
- currently_managed_zpools_rx : CurrentlyManagedZpoolsReceiver :: new (
139
- currently_managed_zpools_rx,
140
- ) ,
141
- reconciler_task_dependencies,
142
- }
144
+ } ,
145
+ )
143
146
}
144
147
145
148
/// Spawn the primary config reconciliation task.
146
149
///
147
- /// This method can effectively only be called once; any subsequent calls
148
- /// will log a warning and do nothing.
150
+ /// This method can effectively only be called once, because the caller must
151
+ /// supply the `token` returned by `new()` when this handle was created.
152
+ ///
153
+ /// # Panics
154
+ ///
155
+ /// Panics if called multiple times, which is statically impossible outside
156
+ /// shenanigans to get a second `ConfigReconcilerHandleSpawnToken`.
149
157
pub fn spawn_reconciliation_task <
150
158
T : SledAgentFacilities ,
151
159
U : SledAgentArtifactStore ,
@@ -154,26 +162,16 @@ impl ConfigReconcilerHandle {
154
162
underlay_vnic : EtherstubVnic ,
155
163
sled_agent_facilities : T ,
156
164
sled_agent_artifact_store : U ,
157
- log : & Logger ,
165
+ token : ConfigReconcilerHandleSpawnToken ,
158
166
) {
159
- let ReconcilerTaskDependencies {
167
+ let ConfigReconcilerHandleSpawnToken {
160
168
key_requester,
161
169
time_sync_config,
162
170
reconciler_result_tx,
163
171
currently_managed_zpools_tx,
164
172
ledger_task_log,
165
173
reconciler_task_log,
166
- } = match self . reconciler_task_dependencies . lock ( ) . unwrap ( ) . take ( ) {
167
- Some ( dependencies) => dependencies,
168
- None => {
169
- warn ! (
170
- log,
171
- "spawn_reconciliation_task() called multiple times \
172
- (ignored after first call)"
173
- ) ;
174
- return ;
175
- }
176
- } ;
174
+ } = token;
177
175
178
176
// Spawn the task that manages our config ledger.
179
177
let ( ledger_task, current_config_rx) =
@@ -184,10 +182,13 @@ impl ConfigReconcilerHandle {
184
182
) ;
185
183
match self . ledger_task . set ( ledger_task) {
186
184
Ok ( ( ) ) => ( ) ,
187
- // We know via the `lock().take()` above that this only executes
188
- // once, so `ledger_task` is always set here.
185
+ // We can only be called with the `token` we returned in `new()` and
186
+ // we document that we panic if called multiple times via some
187
+ // multi-token shenanigans.
189
188
Err ( _) => {
190
- unreachable ! ( "spawn_reconciliation_task() only executes once" )
189
+ panic ! (
190
+ "spawn_reconciliation_task() called with multiple tokens"
191
+ )
191
192
}
192
193
}
193
194
0 commit comments