@@ -471,11 +471,18 @@ func TestNextToken_CancelDrainClosedChannelStartsSecondResponse(t *testing.T) {
471471}
472472
473473// blockingTransport blocks Read until unblock is closed, then returns EOF.
474+ // It signals readEntered when a Read call begins, allowing deterministic
475+ // synchronization without time.Sleep.
474476type blockingTransport struct {
475- unblock chan struct {}
477+ unblock chan struct {}
478+ readEntered chan struct {}
476479}
477480
478481func (b * blockingTransport ) Read ([]byte ) (int , error ) {
482+ select {
483+ case b .readEntered <- struct {}{}:
484+ default :
485+ }
479486 <- b .unblock
480487 return 0 , io .EOF
481488}
@@ -487,14 +494,24 @@ func (b *blockingTransport) Close() error { return nil }
487494// the previous goroutine to finish before launching a new one.
488495func TestStartResponseReaderSerializes (t * testing.T ) {
489496 // First reader: transport blocks until we say so.
497+ readEntered := make (chan struct {}, 1 )
490498 unblock := make (chan struct {})
491499 sess := & tdsSession {
492- buf : newTdsBuffer (defaultPacketSize , & blockingTransport {unblock : unblock }),
500+ buf : newTdsBuffer (defaultPacketSize , & blockingTransport {
501+ unblock : unblock ,
502+ readEntered : readEntered ,
503+ }),
493504 }
494505
495506 ch1 := make (chan tokenStruct , 10 )
496507 sess .startResponseReader (context .Background (), ch1 , outputs {})
497- // First goroutine is now blocked inside processSingleResponse on Read.
508+
509+ // Wait for the first goroutine to actually enter Read, proving it's blocked.
510+ select {
511+ case <- readEntered :
512+ case <- time .After (5 * time .Second ):
513+ t .Fatal ("first reader never entered Read" )
514+ }
498515
499516 // Launch second startResponseReader in a goroutine; it should block on
500517 // <-sess.readDone until the first reader finishes.
@@ -505,14 +522,14 @@ func TestStartResponseReaderSerializes(t *testing.T) {
505522 secondStarted .Store (1 )
506523 }()
507524
508- // Give the goroutine time to reach the <-sess.readDone wait.
509- time . Sleep ( 50 * time . Millisecond )
525+ // The second goroutine cannot proceed while the first reader is blocked,
526+ // so secondStarted must still be 0.
510527 if secondStarted .Load () != 0 {
511528 t .Fatal ("second startResponseReader returned before first completed" )
512529 }
513530
514- // Unblock the first reader. processSingleResponse will hit EOF, recover
515- // from the panic, and close ch1 + readDone.
531+ // Unblock the first reader. processSingleResponse will receive EOF from
532+ // BeginRead as an error, send it to ch1, and return, closing readDone.
516533 close (unblock )
517534
518535 // Second call should now proceed.
0 commit comments