@@ -753,35 +753,66 @@ func (tab *Table) deleteNode(n *enode.Node) {
753753
754754// waitForNodes blocks until the table contains at least n nodes.
755755func (tab * Table ) waitForNodes (ctx context.Context , n int ) error {
756+ // Wrap ctx so the forwarder goroutine exits when waitForNodes returns,
757+ // regardless of whether the caller's ctx is canceled.
758+ ctx , cancel := context .WithCancel (ctx )
759+ defer cancel ()
760+
761+ // Set up a notification channel that gets unblocked when there was any activity on
762+ // the table. Ultimately this reads from the table's nodeFeed, but can't use the feed
763+ // directly on the same goroutine that takes Table.mutex, it would deadlock.
764+ var notify chan struct {}
765+ var notifyErr error
766+ initsub := func () event.Subscription {
767+ notify = make (chan struct {}, 1 )
768+ newnode := make (chan * enode.Node , 1 )
769+ sub := tab .nodeFeed .Subscribe (newnode )
770+ go func () {
771+ defer close (notify )
772+ for {
773+ select {
774+ case <- newnode :
775+ select {
776+ case notify <- struct {}{}:
777+ default :
778+ }
779+ case <- ctx .Done ():
780+ notifyErr = ctx .Err ()
781+ return
782+ case <- tab .closeReq :
783+ notifyErr = errClosed
784+ return
785+ }
786+ }
787+ }()
788+ return sub
789+ }
790+
756791 getlength := func () (count int ) {
757792 for _ , b := range & tab .buckets {
758793 count += len (b .entries )
759794 }
760795 return count
761796 }
762797
763- var ch chan * enode.Node
764798 for {
765799 tab .mutex .Lock ()
766800 if getlength () >= n {
767801 tab .mutex .Unlock ()
768802 return nil
769803 }
770- if ch == nil {
771- // Init subscription.
772- ch = make ( chan * enode. Node )
773- sub := tab . nodeFeed . Subscribe ( ch )
804+ if notify == nil {
805+ // Lazily init the subscription. Do this while holding the
806+ // lock so we don't miss any events that change the node count.
807+ sub := initsub ( )
774808 defer sub .Unsubscribe ()
775809 }
776810 tab .mutex .Unlock ()
777811
778- // Wait for a node add event.
779- select {
780- case <- ch :
781- case <- ctx .Done ():
782- return ctx .Err ()
783- case <- tab .closeReq :
784- return errClosed
812+ // Wait for table event.
813+ if _ , ok := <- notify ; ! ok {
814+ break
785815 }
786816 }
817+ return notifyErr
787818}
0 commit comments