@@ -43,6 +43,7 @@ use crate::prom;
4343use {
4444 crate :: config:: { TpuOverrideInfo , TpuPortKind , TpuSenderConfig } ,
4545 bytes:: Bytes ,
46+ core:: fmt,
4647 derive_more:: Display ,
4748 futures:: task:: AtomicWaker ,
4849 quinn:: {
@@ -58,11 +59,12 @@ use {
5859 solana_tls_utils:: { QuicClientCertificate , SkipServerVerification , new_dummy_x509_certificate} ,
5960 std:: {
6061 collections:: { BTreeMap , HashMap , HashSet , VecDeque } ,
62+ future,
6163 net:: { IpAddr , Ipv4Addr , SocketAddr } ,
6264 num:: NonZeroUsize ,
6365 pin:: Pin ,
6466 sync:: { Arc , Mutex as StdMutex , atomic:: AtomicBool } ,
65- task:: Poll ,
67+ task:: { Poll , ready } ,
6668 time:: { Duration , Instant } ,
6769 } ,
6870 tokio:: {
7476 task:: { self , Id , JoinError , JoinHandle , JoinSet } ,
7577 time:: { Sleep , interval} ,
7678 } ,
79+ tokio_util:: sync:: PollSender ,
7780} ;
7881
7982/// This has been copy-pasted from `solana_streamer::nonblocking::quic::ALPN_TPU_PROTOCOL_ID`
@@ -149,10 +152,25 @@ struct MultiStepIdentitySynchronizationCommand {
149152/// Command to control driver behavior.
150153///
151154enum DriverCommand {
152- UpdateIdenttiy ( UpdateIdentityCommand ) ,
155+ UpdateIdentity ( UpdateIdentityCommand ) ,
153156 MultiStepIdentitySynchronization ( MultiStepIdentitySynchronizationCommand ) ,
154157}
155158
159+ impl fmt:: Debug for DriverCommand {
160+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
161+ match self {
162+ DriverCommand :: UpdateIdentity ( cmd) => f
163+ . debug_struct ( "UpdateIdentity" )
164+ . field ( "new_identity" , & cmd. new_identity . pubkey ( ) )
165+ . finish ( ) ,
166+ DriverCommand :: MultiStepIdentitySynchronization ( cmd) => f
167+ . debug_struct ( "MultiStepIdentitySynchronization" )
168+ . field ( "new_identity" , & cmd. new_identity . pubkey ( ) )
169+ . finish ( ) ,
170+ }
171+ }
172+ }
173+
156174enum DriverTaskMeta {
157175 DropAllWorkers ,
158176}
@@ -2696,7 +2714,7 @@ where
26962714
26972715 async fn handle_cnc ( & mut self , command : DriverCommand ) {
26982716 match command {
2699- DriverCommand :: UpdateIdenttiy ( cmd) => {
2717+ DriverCommand :: UpdateIdentity ( cmd) => {
27002718 let UpdateIdentityCommand {
27012719 new_identity,
27022720 callback,
@@ -3382,7 +3400,7 @@ impl TpuSenderDriverSpawner {
33823400 TpuSenderSessionContext {
33833401 driver_tx_sink : tx_inlet,
33843402 identity_updater : TpuSenderIdentityUpdater {
3385- cnc_tx : driver_cnc_tx,
3403+ cnc_tx : PollSender :: new ( driver_cnc_tx) ,
33863404 } ,
33873405 driver_join_handle : jh,
33883406 }
@@ -3392,11 +3410,12 @@ impl TpuSenderDriverSpawner {
33923410///
33933411/// Handle to update the identity used by the TPU sender driver.
33943412///
3413+ #[ derive( Clone ) ]
33953414pub struct TpuSenderIdentityUpdater {
33963415 ///
33973416 /// Command-and-control channel to send command to the QUIC driver
33983417 ///
3399- cnc_tx : mpsc :: Sender < DriverCommand > ,
3418+ cnc_tx : PollSender < DriverCommand > ,
34003419}
34013420
34023421///
@@ -3406,7 +3425,7 @@ impl TpuSenderIdentityUpdater {
34063425 ///
34073426 /// Changes the configured identity in the QUIC driver
34083427 ///
3409- pub async fn update_identity ( & mut self , identity : Keypair ) {
3428+ pub fn update_identity ( & mut self , identity : Keypair ) -> UpdateIdentity {
34103429 let shared = UpdateIdentityInner {
34113430 set : AtomicBool :: new ( false ) ,
34123431 waker : AtomicWaker :: new ( ) ,
@@ -3416,15 +3435,12 @@ impl TpuSenderIdentityUpdater {
34163435 new_identity : identity,
34173436 callback : Arc :: clone ( & shared) ,
34183437 } ;
3419- self . cnc_tx
3420- . send ( DriverCommand :: UpdateIdenttiy ( cmd) )
3421- . await
3422- . expect ( "disconnected" ) ;
3423- let update_identity = UpdateIdentity {
3438+ let cnc_tx = self . cnc_tx . clone ( ) ;
3439+
3440+ UpdateIdentity {
34243441 inner : shared,
3425- _this : self ,
3426- } ;
3427- update_identity. await
3442+ state : UpdateIdentityState :: Init { cnc_tx, cmd } ,
3443+ }
34283444 }
34293445
34303446 ///
@@ -3446,10 +3462,13 @@ impl TpuSenderIdentityUpdater {
34463462 new_identity : identity,
34473463 barrier,
34483464 } ;
3449- self . cnc_tx
3450- . send ( DriverCommand :: MultiStepIdentitySynchronization ( cmd ) )
3465+ let mut cnc_tx = self . cnc_tx . clone ( ) ;
3466+ future :: poll_fn ( |cx| cnc_tx . poll_reserve ( cx ) )
34513467 . await
34523468 . expect ( "disconnected" ) ;
3469+ cnc_tx
3470+ . send_item ( DriverCommand :: MultiStepIdentitySynchronization ( cmd) )
3471+ . expect ( "disconnected" ) ;
34533472 }
34543473}
34553474
@@ -3461,39 +3480,106 @@ struct UpdateIdentityInner {
34613480 waker : AtomicWaker ,
34623481}
34633482
3483+ enum UpdateIdentityState {
3484+ Init {
3485+ cnc_tx : PollSender < DriverCommand > ,
3486+ cmd : UpdateIdentityCommand ,
3487+ } ,
3488+ Closed ,
3489+ WaitingForCompletion ,
3490+ }
3491+
34643492///
34653493/// Future that waits for the identity update to complete.
34663494/// This future is used to ensure that the identity update is completed before proceeding.
34673495///
3468- pub struct UpdateIdentity < ' a > {
3496+ pub struct UpdateIdentity {
34693497 inner : Arc < UpdateIdentityInner > ,
3470- _this : & ' a TpuSenderIdentityUpdater , /* phantom data to prevent two threads from updating the identity at the same time */
3498+ state : UpdateIdentityState ,
3499+ }
3500+
3501+ impl UpdateIdentity {
3502+ fn take_state ( & mut self ) -> UpdateIdentityState {
3503+ std:: mem:: replace ( & mut self . state , UpdateIdentityState :: Closed )
3504+ }
34713505}
34723506
3473- impl Future for UpdateIdentity < ' _ > {
3474- type Output = ( ) ;
3507+ #[ derive( Debug , thiserror:: Error ) ]
3508+ #[ error( "tpu sender runtime closed" ) ]
3509+ pub struct UpdateIdentityError ;
3510+
3511+ impl Future for UpdateIdentity {
3512+ type Output = Result < ( ) , UpdateIdentityError > ;
34753513
34763514 fn poll (
34773515 self : std:: pin:: Pin < & mut Self > ,
34783516 cx : & mut std:: task:: Context < ' _ > ,
34793517 ) -> std:: task:: Poll < Self :: Output > {
3480- // quick check to avoid registration if already done.
3481- if self . inner . set . load ( std:: sync:: atomic:: Ordering :: Relaxed ) {
3482- return Poll :: Ready ( ( ) ) ;
3483- }
3484-
3485- self . inner . waker . register ( cx. waker ( ) ) ;
3518+ let this = self . get_mut ( ) ;
3519+
3520+ let ( result, next_state) = match this. take_state ( ) {
3521+ UpdateIdentityState :: Init { mut cnc_tx, cmd } => {
3522+ match ready ! ( cnc_tx. poll_reserve( cx) ) {
3523+ Ok ( ( ) ) => {
3524+ match cnc_tx. send_item ( DriverCommand :: UpdateIdentity ( cmd) ) {
3525+ Ok ( ( ) ) => {
3526+ this. inner . waker . register ( cx. waker ( ) ) ;
3527+ // Between the time we send and the registration, the driver may have already completed the update and set the atomic bool.
3528+ // So we need to immediately wake the waker, so that the next poll will check the atomic bool and return ready if the update is already complete.
3529+ cx. waker ( ) . wake_by_ref ( ) ;
3530+ ( None , UpdateIdentityState :: WaitingForCompletion )
3531+ }
3532+ Err ( _) => ( Some ( Err ( UpdateIdentityError ) ) , UpdateIdentityState :: Closed ) ,
3533+ }
3534+ }
3535+ Err ( _) => ( Some ( Err ( UpdateIdentityError ) ) , UpdateIdentityState :: Closed ) ,
3536+ }
3537+ }
3538+ UpdateIdentityState :: WaitingForCompletion => {
3539+ if this. inner . set . load ( std:: sync:: atomic:: Ordering :: Relaxed ) {
3540+ ( Some ( Ok ( ( ) ) ) , UpdateIdentityState :: Closed )
3541+ } else {
3542+ ( None , UpdateIdentityState :: WaitingForCompletion )
3543+ }
3544+ }
3545+ UpdateIdentityState :: Closed => {
3546+ panic ! ( "UpdateIdentity polled after completion" ) ;
3547+ }
3548+ } ;
34863549
3487- // Need to check condition **after** `register` to avoid a race
3488- // condition that would result in lost notifications.
3489- if self . inner . set . load ( std:: sync:: atomic:: Ordering :: Relaxed ) {
3490- Poll :: Ready ( ( ) )
3550+ this. state = next_state;
3551+ if let Some ( result) = result {
3552+ return Poll :: Ready ( result) ;
34913553 } else {
3492- Poll :: Pending
3554+ return Poll :: Pending ;
34933555 }
34943556 }
34953557}
34963558
3559+ // impl Future for UpdateIdentity<'_> {
3560+ // type Output = ();
3561+
3562+ // fn poll(
3563+ // self: std::pin::Pin<&mut Self>,
3564+ // cx: &mut std::task::Context<'_>,
3565+ // ) -> std::task::Poll<Self::Output> {
3566+ // // quick check to avoid registration if already done.
3567+ // if self.inner.set.load(std::sync::atomic::Ordering::Relaxed) {
3568+ // return Poll::Ready(());
3569+ // }
3570+
3571+ // self.inner.waker.register(cx.waker());
3572+
3573+ // // Need to check condition **after** `register` to avoid a race
3574+ // // condition that would result in lost notifications.
3575+ // if self.inner.set.load(std::sync::atomic::Ordering::Relaxed) {
3576+ // Poll::Ready(())
3577+ // } else {
3578+ // Poll::Pending
3579+ // }
3580+ // }
3581+ // }
3582+
34973583pub const fn module_path_for_test ( ) -> & ' static str {
34983584 module_path ! ( )
34993585}
@@ -3508,15 +3594,18 @@ mod test {
35083594 solana_pubkey:: Pubkey ,
35093595 std:: time:: Duration ,
35103596 tokio:: sync:: mpsc,
3597+ tokio_util:: sync:: PollSender ,
35113598 } ;
35123599
35133600 #[ tokio:: test]
35143601 async fn test_update_identity_fut ( ) {
35153602 let ( cnc_tx, mut cnc_rx) = mpsc:: channel ( 10 ) ;
3516- let mut updater = TpuSenderIdentityUpdater { cnc_tx } ;
3603+ let mut updater = TpuSenderIdentityUpdater {
3604+ cnc_tx : PollSender :: new ( cnc_tx) ,
3605+ } ;
35173606
35183607 let jh = tokio:: spawn ( async move {
3519- let DriverCommand :: UpdateIdenttiy ( UpdateIdentityCommand {
3608+ let DriverCommand :: UpdateIdentity ( UpdateIdentityCommand {
35203609 new_identity,
35213610 callback,
35223611 } ) = cnc_rx. recv ( ) . await . unwrap ( )
@@ -3533,7 +3622,10 @@ mod test {
35333622 } ) ;
35343623
35353624 let identity = Keypair :: new ( ) ;
3536- updater. update_identity ( identity. insecure_clone ( ) ) . await ;
3625+ updater
3626+ . update_identity ( identity. insecure_clone ( ) )
3627+ . await
3628+ . unwrap ( ) ;
35373629
35383630 let actual = jh. await . unwrap ( ) ;
35393631 assert_eq ! ( actual, identity)
0 commit comments