@@ -78,6 +78,10 @@ const DAEMON_SHUTDOWN_DEADLINE: Duration = Duration::from_secs(45);
7878const DAEMON_CLIENT_DRAIN_DEADLINE : Duration = Duration :: from_secs ( 15 ) ;
7979#[ cfg( unix) ]
8080const DAEMON_TASK_ABORT_DEADLINE : Duration = Duration :: from_secs ( 2 ) ;
81+ /// How long a project open may queue behind an unrelated writer before the
82+ /// client is told to retry. The open itself keeps running in the background.
83+ #[ cfg( unix) ]
84+ const CONTENDED_PROJECT_OPEN_GRACE : Duration = Duration :: from_millis ( 500 ) ;
8185
8286#[ derive( Clone , Default ) ]
8387pub ( crate ) struct DaemonLifecycle {
@@ -2911,13 +2915,18 @@ async fn serve_broker_socket_client(
29112915 }
29122916 }
29132917 let server = if let Some ( project_path) = handshake. project_path . as_ref ( ) {
2918+ // Queuing behind an unrelated writer can take that writer's whole
2919+ // operation, so answer with a retry hint rather than holding the
2920+ // client. An uncontended open is this client's own work and must run
2921+ // to completion, otherwise one-shot callers never get a result.
2922+ let contended = engine. store_administration . writer_is_busy ( ) ;
29142923 let mut project_open = engine. spawn_direct_project_server_open ( handshake. clone ( ) ) ;
2915- let server = match tokio :: time :: timeout (
2916- std :: time:: Duration :: from_millis ( 500 ) ,
2917- & mut project_open ,
2918- )
2919- . await
2920- {
2924+ let opened = if contended {
2925+ tokio :: time:: timeout ( CONTENDED_PROJECT_OPEN_GRACE , & mut project_open ) . await
2926+ } else {
2927+ Ok ( ( & mut project_open ) . await )
2928+ } ;
2929+ let server = match opened {
29212930 Ok ( Ok ( Ok ( server) ) ) => server,
29222931 Ok ( Ok ( Err ( error) ) ) => {
29232932 write_project_open_error ( & mut transport, & first_request_line, & error) . await ?;
0 commit comments