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  }