@@ -539,21 +539,24 @@ pub fn handle_configure(context: Arc<Context>, id: u32, params: Value) {
539539
540540 // Expand glob patterns before acquiring the write lock so we
541541 // don't block readers/writers while traversing the filesystem.
542- let workspace_directories = configure_options. workspace_directories . map ( |dirs| {
543- let start = Instant :: now ( ) ;
544- let result: Vec < PathBuf > = expand_glob_patterns ( & dirs)
545- . into_iter ( )
546- . filter ( |p| p. is_dir ( ) )
547- . collect ( ) ;
548- trace ! (
549- "Expanded workspace directory patterns ({:?}) in {:?}" ,
550- dirs,
551- start. elapsed( )
552- ) ;
553- result
554- } ) ;
555- let environment_directories =
556- configure_options. environment_directories . map ( |dirs| {
542+ let workspace_directories =
543+ configure_options. workspace_directories . clone ( ) . map ( |dirs| {
544+ let start = Instant :: now ( ) ;
545+ let result: Vec < PathBuf > = expand_glob_patterns ( & dirs)
546+ . into_iter ( )
547+ . filter ( |p| p. is_dir ( ) )
548+ . collect ( ) ;
549+ trace ! (
550+ "Expanded workspace directory patterns ({:?}) in {:?}" ,
551+ dirs,
552+ start. elapsed( )
553+ ) ;
554+ result
555+ } ) ;
556+ let environment_directories = configure_options
557+ . environment_directories
558+ . clone ( )
559+ . map ( |dirs| {
557560 let start = Instant :: now ( ) ;
558561 let result: Vec < PathBuf > = expand_glob_patterns ( & dirs)
559562 . into_iter ( )
@@ -575,33 +578,13 @@ pub fn handle_configure(context: Arc<Context>, id: u32, params: Value) {
575578 ) ;
576579 }
577580
578- let config = {
579- let mut state = context. configuration . write ( ) . unwrap ( ) ;
580- state. config . workspace_directories = workspace_directories;
581- state. config . conda_executable = configure_options. conda_executable ;
582- state. config . environment_directories = environment_directories;
583- state. config . pipenv_executable = configure_options. pipenv_executable ;
584- state. config . poetry_executable = configure_options. poetry_executable ;
585- // We will not support changing the cache directories once set.
586- // No point, supporting such a use case.
587- if let Some ( cache_directory) = configure_options. cache_directory {
588- set_cache_directory ( cache_directory. clone ( ) ) ;
589- state. config . cache_directory = Some ( cache_directory) ;
590- }
591- state. generation += 1 ;
592- // Reset missing-env reporting so that the next refresh
593- // after reconfiguration can trigger it again (Fixes #395).
594- // Done inside the write lock to avoid a TOCTOU window with
595- // concurrent refresh threads reading the generation.
596- MISSING_ENVS_REPORTING_STATE . store ( MISSING_ENVS_AVAILABLE , Ordering :: Release ) ;
597- trace ! (
598- "Configuring locators with generation {}: {:?}" ,
599- state. generation,
600- state. config
601- ) ;
602- state. config . clone ( )
603- } ;
604- configure_locators ( & context. locators , & config) ;
581+ apply_configure_options (
582+ context. configuration . as_ref ( ) ,
583+ & context. locators ,
584+ configure_options,
585+ workspace_directories,
586+ environment_directories,
587+ ) ;
605588 info ! ( "Configure completed in {:?}" , now. elapsed( ) ) ;
606589 send_reply ( id, None :: < ( ) > ) ;
607590 } ) ;
@@ -665,6 +648,39 @@ fn parse_refresh_options(params: Value) -> Result<RefreshOptions, serde_json::Er
665648 . map ( |options| canonicalize_refresh_options ( options. unwrap_or_default ( ) ) )
666649}
667650
651+ fn apply_configure_options (
652+ configuration : & RwLock < ConfigurationState > ,
653+ locators : & Arc < Vec < Arc < dyn Locator > > > ,
654+ configure_options : ConfigureOptions ,
655+ workspace_directories : Option < Vec < PathBuf > > ,
656+ environment_directories : Option < Vec < PathBuf > > ,
657+ ) {
658+ let mut state = configuration. write ( ) . unwrap ( ) ;
659+ state. config . workspace_directories = workspace_directories;
660+ state. config . conda_executable = configure_options. conda_executable ;
661+ state. config . environment_directories = environment_directories;
662+ state. config . pipenv_executable = configure_options. pipenv_executable ;
663+ state. config . poetry_executable = configure_options. poetry_executable ;
664+ // We will not support changing the cache directories once set.
665+ // No point, supporting such a use case.
666+ if let Some ( cache_directory) = configure_options. cache_directory {
667+ set_cache_directory ( cache_directory. clone ( ) ) ;
668+ state. config . cache_directory = Some ( cache_directory) ;
669+ }
670+ state. generation += 1 ;
671+ // Reset missing-env reporting so that the next refresh after
672+ // reconfiguration can trigger it again (Fixes #395). Done inside the write
673+ // lock to avoid a TOCTOU window with concurrent refresh threads reading the
674+ // generation.
675+ MISSING_ENVS_REPORTING_STATE . store ( MISSING_ENVS_AVAILABLE , Ordering :: Release ) ;
676+ trace ! (
677+ "Configuring locators with generation {}: {:?}" ,
678+ state. generation,
679+ state. config
680+ ) ;
681+ configure_locators ( locators, & state. config ) ;
682+ }
683+
668684fn configure_locators ( locators : & Arc < Vec < Arc < dyn Locator > > > , config : & Configuration ) {
669685 for locator in locators. iter ( ) {
670686 locator. configure ( config) ;
@@ -1244,6 +1260,12 @@ mod tests {
12441260 reported : Mutex < bool > ,
12451261 }
12461262
1263+ struct BlockingConfigureLocator {
1264+ started : mpsc:: Sender < ( ) > ,
1265+ release : Mutex < mpsc:: Receiver < ( ) > > ,
1266+ configured_workspace_directories : Mutex < Option < Vec < PathBuf > > > ,
1267+ }
1268+
12471269 impl Reporter for RecordingReporter {
12481270 fn report_manager ( & self , manager : & EnvManager ) {
12491271 self . managers . lock ( ) . unwrap ( ) . push ( manager. clone ( ) ) ;
@@ -1275,6 +1297,29 @@ mod tests {
12751297 }
12761298 }
12771299
1300+ impl Locator for BlockingConfigureLocator {
1301+ fn get_kind ( & self ) -> LocatorKind {
1302+ LocatorKind :: Venv
1303+ }
1304+
1305+ fn configure ( & self , config : & Configuration ) {
1306+ self . started . send ( ( ) ) . unwrap ( ) ;
1307+ self . release . lock ( ) . unwrap ( ) . recv ( ) . unwrap ( ) ;
1308+ * self . configured_workspace_directories . lock ( ) . unwrap ( ) =
1309+ config. workspace_directories . clone ( ) ;
1310+ }
1311+
1312+ fn supported_categories ( & self ) -> Vec < PythonEnvironmentKind > {
1313+ vec ! [ PythonEnvironmentKind :: Venv ]
1314+ }
1315+
1316+ fn try_from ( & self , _env : & pet_core:: env:: PythonEnv ) -> Option < PythonEnvironment > {
1317+ None
1318+ }
1319+
1320+ fn find ( & self , _reporter : & dyn Reporter ) { }
1321+ }
1322+
12781323 fn make_refresh_key ( generation : u64 , options : RefreshOptions ) -> RefreshKey {
12791324 RefreshKey :: new ( & options, generation)
12801325 }
@@ -1402,6 +1447,59 @@ mod tests {
14021447 assert ! ( * inner. reported. lock( ) . unwrap( ) ) ;
14031448 }
14041449
1450+ #[ test]
1451+ fn test_configure_publishes_state_after_shared_locators_are_configured ( ) {
1452+ let configuration = Arc :: new ( RwLock :: new ( ConfigurationState :: default ( ) ) ) ;
1453+ let ( started_tx, started_rx) = mpsc:: channel ( ) ;
1454+ let ( release_tx, release_rx) = mpsc:: channel ( ) ;
1455+ let locator = Arc :: new ( BlockingConfigureLocator {
1456+ started : started_tx,
1457+ release : Mutex :: new ( release_rx) ,
1458+ configured_workspace_directories : Mutex :: new ( None ) ,
1459+ } ) ;
1460+ let locators = Arc :: new ( vec ! [ locator. clone( ) as Arc <dyn Locator >] ) ;
1461+ let workspace_directories = vec ! [ PathBuf :: from( "/workspace" ) ] ;
1462+
1463+ let worker = {
1464+ let configuration = configuration. clone ( ) ;
1465+ let locators = locators. clone ( ) ;
1466+ let workspace_directories = workspace_directories. clone ( ) ;
1467+ thread:: spawn ( move || {
1468+ apply_configure_options (
1469+ configuration. as_ref ( ) ,
1470+ & locators,
1471+ ConfigureOptions {
1472+ workspace_directories : None ,
1473+ conda_executable : None ,
1474+ pipenv_executable : None ,
1475+ poetry_executable : None ,
1476+ environment_directories : None ,
1477+ cache_directory : None ,
1478+ } ,
1479+ Some ( workspace_directories) ,
1480+ None ,
1481+ ) ;
1482+ } )
1483+ } ;
1484+
1485+ started_rx. recv ( ) . unwrap ( ) ;
1486+ assert ! ( configuration. try_read( ) . is_err( ) ) ;
1487+
1488+ release_tx. send ( ( ) ) . unwrap ( ) ;
1489+ worker. join ( ) . unwrap ( ) ;
1490+
1491+ let state = configuration. read ( ) . unwrap ( ) ;
1492+ assert_eq ! ( state. generation, 1 ) ;
1493+ assert_eq ! (
1494+ state. config. workspace_directories,
1495+ Some ( workspace_directories. clone( ) )
1496+ ) ;
1497+ assert_eq ! (
1498+ * locator. configured_workspace_directories. lock( ) . unwrap( ) ,
1499+ Some ( workspace_directories)
1500+ ) ;
1501+ }
1502+
14051503 #[ test]
14061504 fn test_stale_generation_does_not_begin_missing_env_reporting ( ) {
14071505 let _guard = MISSING_ENVS_TEST_LOCK . lock ( ) . unwrap ( ) ;
0 commit comments