github.com/braveheart12/insolar-09-08-19@v0.8.7/network/transport/future.go (about)

     1  /*
     2   * The Clear BSD License
     3   *
     4   * Copyright (c) 2019 Insolar Technologies
     5   *
     6   * All rights reserved.
     7   *
     8   * Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
     9   *
    10   *  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    11   *  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    12   *  Neither the name of Insolar Technologies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    13   *
    14   * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    15   *
    16   */
    17  
    18  package transport
    19  
    20  import (
    21  	"errors"
    22  	"sync/atomic"
    23  	"time"
    24  
    25  	"github.com/insolar/insolar/metrics"
    26  	"github.com/insolar/insolar/network"
    27  	"github.com/insolar/insolar/network/transport/host"
    28  	"github.com/insolar/insolar/network/transport/packet"
    29  )
    30  
    31  var (
    32  	// ErrTimeout is returned when the operation timeout is exceeded.
    33  	ErrTimeout = errors.New("timeout")
    34  	// ErrChannelClosed is returned when the input channel is closed.
    35  	ErrChannelClosed = errors.New("channel closed")
    36  )
    37  
    38  // Future is network response future.
    39  type Future interface {
    40  
    41  	// ID returns packet sequence number.
    42  	ID() network.RequestID
    43  
    44  	// Actor returns the initiator of the packet.
    45  	Actor() *host.Host
    46  
    47  	// Request returns origin request.
    48  	Request() *packet.Packet
    49  
    50  	// Result is a channel to listen for future result.
    51  	Result() <-chan *packet.Packet
    52  
    53  	// SetResult makes packet to appear in result channel.
    54  	SetResult(*packet.Packet)
    55  
    56  	// GetResult gets the future result from Result() channel with a timeout set to `duration`.
    57  	GetResult(duration time.Duration) (*packet.Packet, error)
    58  
    59  	// Cancel closes all channels and cleans up underlying structures.
    60  	Cancel()
    61  }
    62  
    63  // CancelCallback is a callback function executed when cancelling Future.
    64  type CancelCallback func(Future)
    65  
    66  type future struct {
    67  	result         chan *packet.Packet
    68  	actor          *host.Host
    69  	request        *packet.Packet
    70  	requestID      network.RequestID
    71  	cancelCallback CancelCallback
    72  	finished       uint32
    73  }
    74  
    75  // NewFuture creates new Future.
    76  func NewFuture(requestID network.RequestID, actor *host.Host, msg *packet.Packet, cancelCallback CancelCallback) Future {
    77  	metrics.NetworkFutures.WithLabelValues(msg.Type.String()).Inc()
    78  	return &future{
    79  		result:         make(chan *packet.Packet, 1),
    80  		actor:          actor,
    81  		request:        msg,
    82  		requestID:      requestID,
    83  		cancelCallback: cancelCallback,
    84  	}
    85  }
    86  
    87  // ID returns RequestID of packet.
    88  func (future *future) ID() network.RequestID {
    89  	return future.requestID
    90  }
    91  
    92  // Actor returns Host address that was used to create packet.
    93  func (future *future) Actor() *host.Host {
    94  	return future.actor
    95  }
    96  
    97  // Request returns original request packet.
    98  func (future *future) Request() *packet.Packet {
    99  	return future.request
   100  }
   101  
   102  // Result returns result packet channel.
   103  func (future *future) Result() <-chan *packet.Packet {
   104  	return future.result
   105  }
   106  
   107  // SetResult write packet to the result channel.
   108  func (future *future) SetResult(msg *packet.Packet) {
   109  	if atomic.CompareAndSwapUint32(&future.finished, 0, 1) {
   110  		future.result <- msg
   111  		future.finish()
   112  	}
   113  }
   114  
   115  // GetResult gets the future result from Result() channel with a timeout set to `duration`.
   116  func (future *future) GetResult(duration time.Duration) (*packet.Packet, error) {
   117  	select {
   118  	case result, ok := <-future.Result():
   119  		if !ok {
   120  			return nil, ErrChannelClosed
   121  		}
   122  		return result, nil
   123  	case <-time.After(duration):
   124  		future.Cancel()
   125  		metrics.NetworkPacketTimeoutTotal.WithLabelValues(future.request.Type.String()).Inc()
   126  		return nil, ErrTimeout
   127  	}
   128  }
   129  
   130  // Cancel allows to cancel Future processing.
   131  func (future *future) Cancel() {
   132  	if atomic.CompareAndSwapUint32(&future.finished, 0, 1) {
   133  		future.finish()
   134  		metrics.NetworkFutures.WithLabelValues(future.request.Type.String()).Dec()
   135  	}
   136  }
   137  
   138  func (future *future) finish() {
   139  	close(future.result)
   140  	future.cancelCallback(future)
   141  }