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