[stable] [das] allow interleaved requests in the message scheduler

Issue description: disallowing interleaved message handling requests was causing major performance issues.
What is the fix: stop awaiting handlers and allow interleaving.
Why cherry-pick: this performance issue is heavily impacting a number of users.
Risk: Low, but not none. Some issues have been identified (in the linked issue) and are being worked on concurrently.

Bug: https://github.com/dart-lang/sdk/issues/60440
Change-Id: I176371f5609138eeb8a81a231f4a51a3a00dd37d
Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/419340
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421341
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Kevin Chisholm <kevinjchisholm@google.com>
Reviewed-by: Alexander Thomas <athom@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3b9f8e..e417012 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 3.7.3
+
+This is a patch release that:
+
+- Fixes a performance regression in the analysis server (issue [#60335]).
+
+[#60335]: https://github.com/dart-lang/sdk/issues/60335
+
 ## 3.7.2
 
 **Released on:** 2025-03-12
diff --git a/pkg/analysis_server/lib/src/server/message_scheduler.dart b/pkg/analysis_server/lib/src/server/message_scheduler.dart
index a15b33b..662b7d4 100644
--- a/pkg/analysis_server/lib/src/server/message_scheduler.dart
+++ b/pkg/analysis_server/lib/src/server/message_scheduler.dart
@@ -75,6 +75,9 @@
 /// and the Diagnostic server. The [MessageScheduler] acts as a hub for all
 /// incoming messages and forwards the messages to the appropriate handlers.
 final class MessageScheduler {
+  /// A flag to allow disabling overlapping message handlers.
+  static bool allowOverlappingHandlers = true;
+
   /// The [AnalysisServer] associated with the scheduler.
   late final AnalysisServer server;
 
@@ -278,7 +281,20 @@
               completer,
             );
         }
-        await completer.future;
+
+        // Blocking here with an await on the future was intended to prevent unwanted
+        // interleaving but was found to cause a significant performance regression.
+        // For more context see: https://github.com/dart-lang/sdk/issues/60440.
+        // To re-disable interleaving, set [allowOverlappingHandlers] to `false`.
+        if (!allowOverlappingHandlers) {
+          await completer.future;
+        }
+
+        // NOTE that this message is not accurate if [allowOverlappingHandlers] is `true`
+        // as in that case we're not blocking anymore and the future may not be complete.
+        // TODO(pq): if not awaited, consider adding a `then` so we can track when the future completes
+        // but note that we may see some flakiness in tests as message handling gets
+        // non-deterministically interleaved.
         testView?.messageLog.add(
           '  Complete ${message.runtimeType}: ${message.toString()}',
         );
diff --git a/pkg/analysis_server/test/integration/server/message_scheduler_test.dart b/pkg/analysis_server/test/integration/server/message_scheduler_test.dart
index 2e50de8..6a9d7d7 100644
--- a/pkg/analysis_server/test/integration/server/message_scheduler_test.dart
+++ b/pkg/analysis_server/test/integration/server/message_scheduler_test.dart
@@ -67,6 +67,8 @@
   }
 
   Future<void> test_multipleRequests() async {
+    if (MessageScheduler.allowOverlappingHandlers) return;
+
     var futures = <Future<void>>[];
     futures.add(setRoots(included: [workspaceRootPath], excluded: []));
     var request = ExecutionCreateContextParams(
@@ -103,6 +105,8 @@
   }
 
   Future<void> test_documentChange() async {
+    if (MessageScheduler.allowOverlappingHandlers) return;
+
     const content = '''
 void f() {
   print('Test!');
@@ -176,6 +180,8 @@
   }
 
   Future<void> test_duplicateRequests() async {
+    if (MessageScheduler.allowOverlappingHandlers) return;
+
     const content = '''
 class B {
   @^
@@ -252,6 +258,8 @@
   }
 
   Future<void> test_multipleRequests() async {
+    if (MessageScheduler.allowOverlappingHandlers) return;
+
     const content = '''
 void main() {
   print('Hello world!!');
@@ -288,6 +296,8 @@
   }
 
   Future<void> test_response() async {
+    if (MessageScheduler.allowOverlappingHandlers) return;
+
     const content = '''
 void f() {
   print('Test!');