@@ -896,14 +896,14 @@ mod tests {
896896 }
897897
898898 #[ test]
899- fn test_reclaim_then_poll_preserves_order ( ) {
899+ fn test_reclaim_discards_readonly_completions ( ) {
900900 let ring = make_ring ( 8 ) ;
901901 let ( mut producer, mut consumer, _) = make_test_producer ( & ring) ;
902902
903903 // Submit 3 entries: RO, RW, RO
904- let tok_ro1 = send_readonly ( & mut producer, b"log1" ) ;
904+ let _tok_ro1 = send_readonly ( & mut producer, b"log1" ) ;
905905 let tok_rw = send_readwrite ( & mut producer, b"call" , 64 ) ;
906- let tok_ro2 = send_readonly ( & mut producer, b"log2" ) ;
906+ let _tok_ro2 = send_readonly ( & mut producer, b"log2" ) ;
907907
908908 // Consumer processes all 3
909909 let ( _, c1) = consumer. poll ( 1024 ) . unwrap ( ) . unwrap ( ) ;
@@ -919,24 +919,16 @@ mod tests {
919919 let ( _, c3) = consumer. poll ( 1024 ) . unwrap ( ) . unwrap ( ) ;
920920 consumer. complete ( c3) . unwrap ( ) ; // ack RO
921921
922- // Reclaim all 3
922+ // Reclaim all 3 - RO completions are discarded, only RW is buffered
923923 let count = producer. reclaim ( ) . unwrap ( ) ;
924924 assert_eq ! ( count, 3 ) ;
925925
926- // poll() returns them in order
927- let cqe1 = producer. poll ( ) . unwrap ( ) . unwrap ( ) ;
928- assert_eq ! ( cqe1. token, tok_ro1) ;
929- assert ! ( cqe1. data. is_empty( ) ) ;
930-
931- let cqe2 = producer. poll ( ) . unwrap ( ) . unwrap ( ) ;
932- assert_eq ! ( cqe2. token, tok_rw) ;
933- assert_eq ! ( & cqe2. data[ ..] , b"result" ) ;
934-
935- let cqe3 = producer. poll ( ) . unwrap ( ) . unwrap ( ) ;
936- assert_eq ! ( cqe3. token, tok_ro2) ;
937- assert ! ( cqe3. data. is_empty( ) ) ;
926+ // poll() returns only the RW completion
927+ let cqe = producer. poll ( ) . unwrap ( ) . unwrap ( ) ;
928+ assert_eq ! ( cqe. token, tok_rw) ;
929+ assert_eq ! ( & cqe. data[ ..] , b"result" ) ;
938930
939- // No more
931+ // No more - RO completions were discarded
940932 assert ! ( producer. poll( ) . unwrap( ) . is_none( ) ) ;
941933 }
942934
@@ -973,11 +965,7 @@ mod tests {
973965 assert_eq ! ( & cqe2. data[ ..] , b"reply" ) ;
974966 }
975967
976- /// Regression test: reclaim + submit must not cause token collisions.
977- ///
978- /// Before the monotonic generation counter, Token wrapped the descriptor
979- /// ID which gets recycled. This caused stale pending completions to
980- /// match newly submitted entries with the same recycled descriptor ID.
968+ /// reclaim + submit must not cause token collisions.
981969 #[ test]
982970 fn test_reclaim_submit_no_token_collision ( ) {
983971 let ring = make_ring ( 8 ) ;
@@ -989,7 +977,6 @@ mod tests {
989977 let ( _, c) = consumer. poll ( 1024 ) . unwrap ( ) . unwrap ( ) ;
990978 consumer. complete ( c) . unwrap ( ) ;
991979
992- // Reclaim pushes the completion to pending (token = tok_old)
993980 let count = producer. reclaim ( ) . unwrap ( ) ;
994981 assert_eq ! ( count, 1 ) ;
995982
@@ -1010,15 +997,42 @@ mod tests {
1010997 wc. write_all ( b"result" ) . unwrap ( ) ;
1011998 consumer. complete ( wc. into ( ) ) . unwrap ( ) ;
1012999
1013- // Poll should return the stale ReadOnly completion first (wrong token )
1014- let cqe1 = producer. poll ( ) . unwrap ( ) . unwrap ( ) ;
1015- assert_eq ! ( cqe1 . token, tok_old ) ;
1016- assert ! ( cqe1 . data. is_empty ( ) ) ;
1000+ // Poll returns only the RW completion (RO was discarded by reclaim )
1001+ let cqe = producer. poll ( ) . unwrap ( ) . unwrap ( ) ;
1002+ assert_eq ! ( cqe . token, tok_new ) ;
1003+ assert_eq ! ( & cqe . data[ .. ] , b"result" ) ;
10171004
1018- // Then the new ReadWrite completion (matching token)
1019- let cqe2 = producer. poll ( ) . unwrap ( ) . unwrap ( ) ;
1020- assert_eq ! ( cqe2. token, tok_new) ;
1021- assert_eq ! ( & cqe2. data[ ..] , b"result" ) ;
1005+ // No stale RO completion in the queue
1006+ assert ! ( producer. poll( ) . unwrap( ) . is_none( ) ) ;
1007+ }
1008+
1009+ /// Verify that repeated oneshot submit/reclaim cycles do not accumulate pending completions.
1010+ #[ test]
1011+ fn test_reclaim_readonly_does_not_leak_pending ( ) {
1012+ let ring = make_ring ( 4 ) ;
1013+ let ( mut producer, mut consumer, _) = make_test_producer ( & ring) ;
1014+
1015+ for _ in 0 ..10 {
1016+ // Fill the ring
1017+ for _ in 0 ..4 {
1018+ send_readonly ( & mut producer, b"msg" ) ;
1019+ }
1020+
1021+ // Consumer acks all
1022+ while let Some ( ( _, completion) ) = consumer. poll ( 1024 ) . unwrap ( ) {
1023+ consumer. complete ( completion) . unwrap ( ) ;
1024+ }
1025+
1026+ // Reclaim frees ring slots; empty completions are discarded
1027+ let count = producer. reclaim ( ) . unwrap ( ) ;
1028+ assert_eq ! ( count, 4 ) ;
1029+
1030+ // No completions should be buffered in pending
1031+ assert ! (
1032+ producer. poll( ) . unwrap( ) . is_none( ) ,
1033+ "pending should be empty after reclaiming RO entries"
1034+ ) ;
1035+ }
10221036 }
10231037}
10241038#[ cfg( all( test, loom) ) ]
0 commit comments