@@ -20,8 +20,7 @@ import (
2020 "bytes"
2121 "fmt"
2222 "sync"
23-
24- "github.com/ethereum/go-ethereum/rlp"
23+ "sync/atomic"
2524
2625 "github.com/ethereum/go-ethereum/common"
2726 "github.com/ethereum/go-ethereum/core"
@@ -31,6 +30,7 @@ import (
3130 "github.com/ethereum/go-ethereum/log"
3231 "github.com/ethereum/go-ethereum/node"
3332 "github.com/ethereum/go-ethereum/p2p"
33+ "github.com/ethereum/go-ethereum/rlp"
3434 "github.com/ethereum/go-ethereum/rpc"
3535)
3636
@@ -68,6 +68,8 @@ type Service struct {
6868 lastBlock * types.Block
6969 // Whether or not the block data is streamed alongside the state diff data in the subscription payload
7070 streamBlock bool
71+ // Whether or not we have any subscribers; only if we do, do we processes state diffs
72+ subscribers int32
7173}
7274
7375// NewStateDiffService creates a new StateDiffingService
@@ -110,6 +112,11 @@ func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
110112 //Notify chain event channel of events
111113 case chainEvent := <- chainEventCh :
112114 log .Debug ("Event received from chainEventCh" , "event" , chainEvent )
115+ // if we don't have any subscribers, do not process a statediff
116+ if atomic .LoadInt32 (& sds .subscribers ) == 0 {
117+ log .Debug ("Currently no subscribers to the statediffing service; processing is halted" )
118+ continue
119+ }
113120 currentBlock := chainEvent .Block
114121 parentHash := currentBlock .ParentHash ()
115122 var parentBlock * types.Block
@@ -125,7 +132,7 @@ func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
125132 "current block number" , currentBlock .Number ())
126133 continue
127134 }
128- if err := sds .process (currentBlock , parentBlock ); err != nil {
135+ if err := sds .processStateDiff (currentBlock , parentBlock ); err != nil {
129136 log .Error ("Error building statediff" , "block number" , currentBlock .Number (), "error" , err )
130137 }
131138 case err := <- errCh :
@@ -140,8 +147,8 @@ func (sds *Service) Loop(chainEventCh chan core.ChainEvent) {
140147 }
141148}
142149
143- // process method builds the state diff payload from the current and parent block and streams it to listening subscriptions
144- func (sds * Service ) process (currentBlock , parentBlock * types.Block ) error {
150+ // processStateDiff method builds the state diff payload from the current and parent block and sends it to listening subscriptions
151+ func (sds * Service ) processStateDiff (currentBlock , parentBlock * types.Block ) error {
145152 stateDiff , err := sds .Builder .BuildStateDiff (parentBlock .Root (), currentBlock .Root (), currentBlock .Number (), currentBlock .Hash ())
146153 if err != nil {
147154 return err
@@ -170,6 +177,9 @@ func (sds *Service) process(currentBlock, parentBlock *types.Block) error {
170177// Subscribe is used by the API to subscribe to the StateDiffingService loop
171178func (sds * Service ) Subscribe (id rpc.ID , sub chan <- Payload , quitChan chan <- bool ) {
172179 log .Info ("Subscribing to the statediff service" )
180+ if atomic .CompareAndSwapInt32 (& sds .subscribers , 0 , 1 ) {
181+ log .Info ("State diffing subscription received; beginning statediff processing" )
182+ }
173183 sds .Lock ()
174184 sds .Subscriptions [id ] = Subscription {
175185 PayloadChan : sub ,
@@ -187,6 +197,11 @@ func (sds *Service) Unsubscribe(id rpc.ID) error {
187197 return fmt .Errorf ("cannot unsubscribe; subscription for id %s does not exist" , id )
188198 }
189199 delete (sds .Subscriptions , id )
200+ if len (sds .Subscriptions ) == 0 {
201+ if atomic .CompareAndSwapInt32 (& sds .subscribers , 1 , 0 ) {
202+ log .Info ("No more subscriptions; halting statediff processing" )
203+ }
204+ }
190205 sds .Unlock ()
191206 return nil
192207}
@@ -208,15 +223,29 @@ func (sds *Service) Stop() error {
208223 return nil
209224}
210225
211- // send is used to fan out and serve a payload to any subscriptions
226+ // send is used to fan out and serve the statediff payload to all subscriptions
212227func (sds * Service ) send (payload Payload ) {
213228 sds .Lock ()
214229 for id , sub := range sds .Subscriptions {
215230 select {
216231 case sub .PayloadChan <- payload :
217232 log .Info (fmt .Sprintf ("sending state diff payload to subscription %s" , id ))
218233 default :
219- log .Info (fmt .Sprintf ("unable to send payload to subscription %s" , id ))
234+ log .Info (fmt .Sprintf ("unable to send payload to subscription %s; channel has no receiver" , id ))
235+ // in this case, try to close the bad subscription and remove it
236+ select {
237+ case sub .QuitChan <- true :
238+ log .Info (fmt .Sprintf ("closing subscription %s" , id ))
239+ default :
240+ log .Info (fmt .Sprintf ("unable to close subscription %s; channel has no receiver" , id ))
241+ }
242+ delete (sds .Subscriptions , id )
243+ }
244+ }
245+ // If after removing all bad subscriptions we have none left, halt processing
246+ if len (sds .Subscriptions ) == 0 {
247+ if atomic .CompareAndSwapInt32 (& sds .subscribers , 1 , 0 ) {
248+ log .Info ("No more subscriptions; halting statediff processing" )
220249 }
221250 }
222251 sds .Unlock ()
@@ -228,11 +257,11 @@ func (sds *Service) close() {
228257 for id , sub := range sds .Subscriptions {
229258 select {
230259 case sub .QuitChan <- true :
231- delete (sds .Subscriptions , id )
232260 log .Info (fmt .Sprintf ("closing subscription %s" , id ))
233261 default :
234262 log .Info (fmt .Sprintf ("unable to close subscription %s; channel has no receiver" , id ))
235263 }
264+ delete (sds .Subscriptions , id )
236265 }
237266 sds .Unlock ()
238267}
0 commit comments