@@ -1031,11 +1031,38 @@ impl State {
10311031 }
10321032
10331033 fn set_complete ( cell : & AtomicUsize ) -> State {
1034- // TODO: This could be `Release`, followed by an `Acquire` fence *if*
1035- // the `RX_TASK_SET` flag is set. However, `loom` does not support
1036- // fences yet.
1037- let val = cell. fetch_or ( VALUE_SENT , AcqRel ) ;
1038- State ( val)
1034+ // This method is a compare-and-swap loop rather than a fetch-or like
1035+ // other `set_$WHATEVER` methods on `State`. This is because we must
1036+ // check if the state has been closed before setting the `VALUE_SENT`
1037+ // bit.
1038+ //
1039+ // We don't want to set both the `VALUE_SENT` bit if the `CLOSED`
1040+ // bit is already set, because `VALUE_SENT` will tell the receiver that
1041+ // it's okay to access the inner `UnsafeCell`. Immediately after calling
1042+ // `set_complete`, if the channel was closed, the sender will _also_
1043+ // access the `UnsafeCell` to take the value back out, so if a
1044+ // `poll_recv` or `try_recv` call is occurring concurrently, both
1045+ // threads may try to access the `UnsafeCell` if we were to set the
1046+ // `VALUE_SENT` bit on a closed channel.
1047+ let mut state = cell. load ( Ordering :: Relaxed ) ;
1048+ loop {
1049+ if State ( state) . is_closed ( ) {
1050+ break ;
1051+ }
1052+ // TODO: This could be `Release`, followed by an `Acquire` fence *if*
1053+ // the `RX_TASK_SET` flag is set. However, `loom` does not support
1054+ // fences yet.
1055+ match cell. compare_exchange_weak (
1056+ state,
1057+ state | VALUE_SENT ,
1058+ Ordering :: AcqRel ,
1059+ Ordering :: Acquire ,
1060+ ) {
1061+ Ok ( _) => break ,
1062+ Err ( actual) => state = actual,
1063+ }
1064+ }
1065+ State ( state)
10391066 }
10401067
10411068 fn is_rx_task_set ( self ) -> bool {
0 commit comments