github.com/xxRanger/go-ethereum@v1.8.23/swarm/storage/localstore/subscription_push.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package localstore 18 19 import ( 20 "context" 21 "sync" 22 23 "github.com/ethereum/go-ethereum/log" 24 "github.com/ethereum/go-ethereum/swarm/shed" 25 "github.com/ethereum/go-ethereum/swarm/storage" 26 ) 27 28 // SubscribePush returns a channel that provides storage chunks with ordering from push syncing index. 29 // Returned stop function will terminate current and further iterations, and also it will close 30 // the returned channel without any errors. Make sure that you check the second returned parameter 31 // from the channel to stop iteration when its value is false. 32 func (db *DB) SubscribePush(ctx context.Context) (c <-chan storage.Chunk, stop func()) { 33 chunks := make(chan storage.Chunk) 34 trigger := make(chan struct{}, 1) 35 36 db.pushTriggersMu.Lock() 37 db.pushTriggers = append(db.pushTriggers, trigger) 38 db.pushTriggersMu.Unlock() 39 40 // send signal for the initial iteration 41 trigger <- struct{}{} 42 43 stopChan := make(chan struct{}) 44 var stopChanOnce sync.Once 45 46 go func() { 47 // close the returned chunkInfo channel at the end to 48 // signal that the subscription is done 49 defer close(chunks) 50 // sinceItem is the Item from which the next iteration 51 // should start. The first iteration starts from the first Item. 52 var sinceItem *shed.Item 53 for { 54 select { 55 case <-trigger: 56 // iterate until: 57 // - last index Item is reached 58 // - subscription stop is called 59 // - context is done 60 err := db.pushIndex.Iterate(func(item shed.Item) (stop bool, err error) { 61 // get chunk data 62 dataItem, err := db.retrievalDataIndex.Get(item) 63 if err != nil { 64 return true, err 65 } 66 67 select { 68 case chunks <- storage.NewChunk(dataItem.Address, dataItem.Data): 69 // set next iteration start item 70 // when its chunk is successfully sent to channel 71 sinceItem = &item 72 return false, nil 73 case <-stopChan: 74 // gracefully stop the iteration 75 // on stop 76 return true, nil 77 case <-db.close: 78 // gracefully stop the iteration 79 // on database close 80 return true, nil 81 case <-ctx.Done(): 82 return true, ctx.Err() 83 } 84 }, &shed.IterateOptions{ 85 StartFrom: sinceItem, 86 // sinceItem was sent as the last Address in the previous 87 // iterator call, skip it in this one 88 SkipStartFromItem: true, 89 }) 90 if err != nil { 91 log.Error("localstore push subscription iteration", "err", err) 92 return 93 } 94 case <-stopChan: 95 // terminate the subscription 96 // on stop 97 return 98 case <-db.close: 99 // terminate the subscription 100 // on database close 101 return 102 case <-ctx.Done(): 103 err := ctx.Err() 104 if err != nil { 105 log.Error("localstore push subscription", "err", err) 106 } 107 return 108 } 109 } 110 }() 111 112 stop = func() { 113 stopChanOnce.Do(func() { 114 close(stopChan) 115 }) 116 117 db.pushTriggersMu.Lock() 118 defer db.pushTriggersMu.Unlock() 119 120 for i, t := range db.pushTriggers { 121 if t == trigger { 122 db.pushTriggers = append(db.pushTriggers[:i], db.pushTriggers[i+1:]...) 123 break 124 } 125 } 126 } 127 128 return chunks, stop 129 } 130 131 // triggerPushSubscriptions is used internally for starting iterations 132 // on Push subscriptions. Whenever new item is added to the push index, 133 // this function should be called. 134 func (db *DB) triggerPushSubscriptions() { 135 db.pushTriggersMu.RLock() 136 triggers := db.pushTriggers 137 db.pushTriggersMu.RUnlock() 138 139 for _, t := range triggers { 140 select { 141 case t <- struct{}{}: 142 default: 143 } 144 } 145 }