4
4
5
5
//! API for controlling multiple instances on a sled.
6
6
7
+ use crate :: instance:: propolis_zone_name;
7
8
use crate :: instance:: Instance ;
8
- use crate :: instance:: InstanceZoneBundleResult ;
9
9
use crate :: nexus:: NexusClientWithResolver ;
10
10
use crate :: params:: ZoneBundleMetadata ;
11
11
use crate :: params:: {
@@ -23,7 +23,6 @@ use omicron_common::api::internal::nexus::InstanceRuntimeState;
23
23
use slog:: Logger ;
24
24
use std:: collections:: BTreeMap ;
25
25
use std:: sync:: { Arc , Mutex } ;
26
- use tokio:: sync:: Mutex as TokioMutex ;
27
26
use uuid:: Uuid ;
28
27
29
28
#[ derive( thiserror:: Error , Debug ) ]
@@ -58,7 +57,7 @@ struct InstanceManagerInternal {
58
57
// instance, we could avoid the methods within "instance.rs" that panic
59
58
// if the Propolis client hasn't been initialized.
60
59
/// A mapping from a Sled Agent "Instance ID" to ("Propolis ID", [Instance]).
61
- instances : TokioMutex < BTreeMap < Uuid , ( Uuid , Instance ) > > ,
60
+ instances : Mutex < BTreeMap < Uuid , ( Uuid , Instance ) > > ,
62
61
63
62
vnic_allocator : VnicAllocator < Etherstub > ,
64
63
port_manager : PortManager ,
@@ -86,7 +85,7 @@ impl InstanceManager {
86
85
87
86
// no reservoir size set on startup
88
87
reservoir_size : Mutex :: new ( ByteCount :: from_kibibytes_u32 ( 0 ) ) ,
89
- instances : TokioMutex :: new ( BTreeMap :: new ( ) ) ,
88
+ instances : Mutex :: new ( BTreeMap :: new ( ) ) ,
90
89
vnic_allocator : VnicAllocator :: new ( "Instance" , etherstub) ,
91
90
port_manager,
92
91
storage,
@@ -176,7 +175,7 @@ impl InstanceManager {
176
175
) ;
177
176
178
177
let instance = {
179
- let mut instances = self . inner . instances . lock ( ) . await ;
178
+ let mut instances = self . inner . instances . lock ( ) . unwrap ( ) ;
180
179
if let Some ( ( existing_propolis_id, existing_instance) ) =
181
180
instances. get ( & instance_id)
182
181
{
@@ -233,7 +232,7 @@ impl InstanceManager {
233
232
instance_id : Uuid ,
234
233
) -> Result < InstanceUnregisterResponse , Error > {
235
234
let instance = {
236
- let instances = self . inner . instances . lock ( ) . await ;
235
+ let instances = self . inner . instances . lock ( ) . unwrap ( ) ;
237
236
let instance = instances. get ( & instance_id) ;
238
237
if let Some ( ( _, instance) ) = instance {
239
238
instance. clone ( )
@@ -257,7 +256,7 @@ impl InstanceManager {
257
256
target : InstanceStateRequested ,
258
257
) -> Result < InstancePutStateResponse , Error > {
259
258
let instance = {
260
- let instances = self . inner . instances . lock ( ) . await ;
259
+ let instances = self . inner . instances . lock ( ) . unwrap ( ) ;
261
260
let instance = instances. get ( & instance_id) ;
262
261
263
262
if let Some ( ( _, instance) ) = instance {
@@ -300,7 +299,7 @@ impl InstanceManager {
300
299
. inner
301
300
. instances
302
301
. lock ( )
303
- . await
302
+ . unwrap ( )
304
303
. get ( & instance_id)
305
304
. ok_or_else ( || Error :: NoSuchInstance ( instance_id) ) ?
306
305
. clone ( ) ;
@@ -315,7 +314,7 @@ impl InstanceManager {
315
314
snapshot_id : Uuid ,
316
315
) -> Result < ( ) , Error > {
317
316
let instance = {
318
- let instances = self . inner . instances . lock ( ) . await ;
317
+ let instances = self . inner . instances . lock ( ) . unwrap ( ) ;
319
318
let ( _, instance) = instances
320
319
. get ( & instance_id)
321
320
. ok_or ( Error :: NoSuchInstance ( instance_id) ) ?;
@@ -333,20 +332,26 @@ impl InstanceManager {
333
332
& self ,
334
333
name : & str ,
335
334
) -> Result < ZoneBundleMetadata , BundleError > {
336
- for ( _propolis_id, instance) in
337
- self . inner . instances . lock ( ) . await . values ( )
338
- {
339
- match instance. request_zone_bundle ( name) . await {
340
- InstanceZoneBundleResult :: NotMe => continue ,
341
- InstanceZoneBundleResult :: NotRunning => {
342
- return Err ( BundleError :: Unavailable {
343
- name : name. to_string ( ) ,
344
- } ) ;
345
- }
346
- InstanceZoneBundleResult :: Ok ( result) => return result,
347
- }
348
- }
349
- Err ( BundleError :: NoSuchZone { name : name. to_string ( ) } )
335
+ // We need to find the instance and take its lock, but:
336
+ //
337
+ // 1. The instance-map lock is sync, and
338
+ // 2. we don't want to hold the instance-map lock for the entire
339
+ // bundling duration.
340
+ //
341
+ // Instead, we cheaply clone the instance through its `Arc` around the
342
+ // `InstanceInner`, which is ultimately what we want.
343
+ let Some ( ( _propolis_id, instance) ) = self
344
+ . inner
345
+ . instances
346
+ . lock ( )
347
+ . unwrap ( )
348
+ . values ( )
349
+ . find ( |( propolis_id, _instance) | name == propolis_zone_name ( propolis_id) )
350
+ . cloned ( )
351
+ else {
352
+ return Err ( BundleError :: NoSuchZone { name : name. to_string ( ) } ) ;
353
+ } ;
354
+ instance. request_zone_bundle ( ) . await
350
355
}
351
356
}
352
357
@@ -368,15 +373,7 @@ impl InstanceTicket {
368
373
/// themselves after stopping.
369
374
pub fn terminate ( & mut self ) {
370
375
if let Some ( inner) = self . inner . take ( ) {
371
- // NOTE: We cannot call methods like `Mutex::blocking_lock()` from
372
- // an asynchronous context. We wrap this in a call to
373
- // `block_in_place()`. This does block the current thread (and any
374
- // concurrent tasks in the same task), but that's not a distinction
375
- // from the previous implementation which called
376
- // `std::sync::Mutex::lock()` here.
377
- tokio:: task:: block_in_place ( || {
378
- inner. instances . blocking_lock ( ) . remove ( & self . id ) ;
379
- } ) ;
376
+ inner. instances . lock ( ) . unwrap ( ) . remove ( & self . id ) ;
380
377
}
381
378
}
382
379
}
0 commit comments