perf(std csp): signal-one on channel put/get instead of broadcast

ober

e111d08853164c7bf45f0a88f5b3dcafc8d499e5

diff --git a/lib/std/csp.ss b/lib/std/csp.ss
index 3bafed3..ea10df0 100644
--- a/lib/std/csp.ss
+++ b/lib/std/csp.ss
@@ -186,7 +186,10 @@
   ;; past a raw put-check.
   (def (%chan-enqueue-raw! ch val)
     (q-enqueue! ch val)
-    (condition-broadcast (channel-not-empty ch))
+    ;; One enqueue can only satisfy one waiting reader; signal-one
+    ;; avoids the thundering-herd cost of waking every blocked worker
+    ;; just to have all but one go back to sleep.
+    (condition-signal (channel-not-empty ch))
     ch)
 
   ;; Put one value into a channel with the mutex already held, running
@@ -208,7 +211,7 @@
              (condition-broadcast (channel-not-full ch))))]
         [else
          (q-enqueue! ch val)
-         (condition-broadcast (channel-not-empty ch))])))
+         (condition-signal (channel-not-empty ch))])))
 
   (def (chan-put! ch val)
     (with-mutex (channel-mutex ch)
@@ -254,7 +257,8 @@
         (cond
           [(not (q-empty? ch))
            (let ([val (q-dequeue! ch)])
-             (condition-broadcast (channel-not-full ch))
+             ;; One dequeue frees exactly one slot for one writer.
+             (condition-signal (channel-not-full ch))
              val)]
           [(channel-closed? ch)
            (eof-object)]
@@ -267,7 +271,7 @@
       (cond
         [(not (q-empty? ch))
          (let ([val (q-dequeue! ch)])
-           (condition-broadcast (channel-not-full ch))
+           (condition-signal (channel-not-full ch))
            val)]
         [else #f])))