github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/gossip/state/payloads_buffer.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package state 8 9 import ( 10 "fmt" 11 "strconv" 12 "sync" 13 "sync/atomic" 14 15 "github.com/hyperledger/fabric/gossip/util" 16 proto "github.com/hyperledger/fabric/protos/gossip" 17 "github.com/op/go-logging" 18 ) 19 20 // PayloadsBuffer is used to store payloads into which used to 21 // support payloads with blocks reordering according to the 22 // sequence numbers. It also will provide the capability 23 // to signal whenever expected block has arrived. 24 type PayloadsBuffer interface { 25 // Adds new block into the buffer 26 Push(payload *proto.Payload) error 27 28 // Returns next expected sequence number 29 Next() uint64 30 31 // Remove and return payload with given sequence number 32 Pop() *proto.Payload 33 34 // Get current buffer size 35 Size() int 36 37 // Channel to indicate event when new payload pushed with sequence 38 // number equal to the next expected value. 39 Ready() chan struct{} 40 41 Close() 42 } 43 44 // PayloadsBufferImpl structure to implement PayloadsBuffer interface 45 // store inner state of available payloads and sequence numbers 46 type PayloadsBufferImpl struct { 47 next uint64 48 49 buf map[uint64]*proto.Payload 50 51 readyChan chan struct{} 52 53 mutex sync.RWMutex 54 55 logger *logging.Logger 56 } 57 58 // NewPayloadsBuffer is factory function to create new payloads buffer 59 func NewPayloadsBuffer(next uint64) PayloadsBuffer { 60 return &PayloadsBufferImpl{ 61 buf: make(map[uint64]*proto.Payload), 62 readyChan: make(chan struct{}, 0), 63 next: next, 64 logger: util.GetLogger(util.LoggingStateModule, ""), 65 } 66 } 67 68 // Ready function returns the channel which indicates whenever expected 69 // next block has arrived and one could safely pop out 70 // next sequence of blocks 71 func (b *PayloadsBufferImpl) Ready() chan struct{} { 72 return b.readyChan 73 } 74 75 // Push new payload into the buffer structure in case new arrived payload 76 // sequence number is below the expected next block number payload will be 77 // thrown away and error will be returned. 78 func (b *PayloadsBufferImpl) Push(payload *proto.Payload) error { 79 b.mutex.Lock() 80 defer b.mutex.Unlock() 81 82 seqNum := payload.SeqNum 83 84 if seqNum < b.next || b.buf[seqNum] != nil { 85 return fmt.Errorf("Payload with sequence number = %s has been already processed", 86 strconv.FormatUint(payload.SeqNum, 10)) 87 } 88 89 b.buf[seqNum] = payload 90 91 // Send notification that next sequence has arrived 92 if seqNum == b.next { 93 // Do not block execution of current routine 94 go func() { 95 b.readyChan <- struct{}{} 96 }() 97 } 98 return nil 99 } 100 101 // Next function provides the number of the next expected block 102 func (b *PayloadsBufferImpl) Next() uint64 { 103 // Atomically read the value of the top sequence number 104 return atomic.LoadUint64(&b.next) 105 } 106 107 // Pop function extracts the payload according to the next expected block 108 // number, if no next block arrived yet, function returns nil. 109 func (b *PayloadsBufferImpl) Pop() *proto.Payload { 110 b.mutex.Lock() 111 defer b.mutex.Unlock() 112 113 result := b.buf[b.Next()] 114 115 if result != nil { 116 // If there is such sequence in the buffer need to delete it 117 delete(b.buf, b.Next()) 118 // Increment next expect block index 119 atomic.AddUint64(&b.next, 1) 120 } 121 return result 122 } 123 124 // Size returns current number of payloads stored within buffer 125 func (b *PayloadsBufferImpl) Size() int { 126 b.mutex.Lock() 127 defer b.mutex.Unlock() 128 return len(b.buf) 129 } 130 131 // Close cleanups resources and channels in maintained 132 func (b *PayloadsBufferImpl) Close() { 133 close(b.readyChan) 134 }