github.com/emate/nomad@v0.8.2-wo-binpacking/client/lib/streamframer/framer.go (about)

     1  package framer
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  var (
    11  	// HeartbeatStreamFrame is the StreamFrame to send as a heartbeat, avoiding
    12  	// creating many instances of the empty StreamFrame
    13  	HeartbeatStreamFrame = &StreamFrame{}
    14  )
    15  
    16  // StreamFrame is used to frame data of a file when streaming
    17  type StreamFrame struct {
    18  	// Offset is the offset the data was read from
    19  	Offset int64 `json:",omitempty"`
    20  
    21  	// Data is the read data
    22  	Data []byte `json:",omitempty"`
    23  
    24  	// File is the file that the data was read from
    25  	File string `json:",omitempty"`
    26  
    27  	// FileEvent is the last file event that occurred that could cause the
    28  	// streams position to change or end
    29  	FileEvent string `json:",omitempty"`
    30  }
    31  
    32  // IsHeartbeat returns if the frame is a heartbeat frame
    33  func (s *StreamFrame) IsHeartbeat() bool {
    34  	return s.Offset == 0 && len(s.Data) == 0 && s.File == "" && s.FileEvent == ""
    35  }
    36  
    37  func (s *StreamFrame) Clear() {
    38  	s.Offset = 0
    39  	s.Data = nil
    40  	s.File = ""
    41  	s.FileEvent = ""
    42  }
    43  
    44  func (s *StreamFrame) IsCleared() bool {
    45  	if s.Offset != 0 {
    46  		return false
    47  	} else if s.Data != nil {
    48  		return false
    49  	} else if s.File != "" {
    50  		return false
    51  	} else if s.FileEvent != "" {
    52  		return false
    53  	} else {
    54  		return true
    55  	}
    56  }
    57  
    58  // StreamFramer is used to buffer and send frames as well as heartbeat.
    59  type StreamFramer struct {
    60  	out chan<- *StreamFrame
    61  
    62  	frameSize int
    63  
    64  	heartbeat *time.Ticker
    65  	flusher   *time.Ticker
    66  
    67  	shutdown   bool
    68  	shutdownCh chan struct{}
    69  	exitCh     chan struct{}
    70  
    71  	// The mutex protects everything below
    72  	l sync.Mutex
    73  
    74  	// The current working frame
    75  	f    StreamFrame
    76  	data *bytes.Buffer
    77  
    78  	// Captures whether the framer is running and any error that occurred to
    79  	// cause it to stop.
    80  	running bool
    81  	err     error
    82  }
    83  
    84  // NewStreamFramer creates a new stream framer that will output StreamFrames to
    85  // the passed output channel.
    86  func NewStreamFramer(out chan<- *StreamFrame,
    87  	heartbeatRate, batchWindow time.Duration, frameSize int) *StreamFramer {
    88  
    89  	// Create the heartbeat and flush ticker
    90  	heartbeat := time.NewTicker(heartbeatRate)
    91  	flusher := time.NewTicker(batchWindow)
    92  
    93  	return &StreamFramer{
    94  		out:        out,
    95  		frameSize:  frameSize,
    96  		heartbeat:  heartbeat,
    97  		flusher:    flusher,
    98  		data:       bytes.NewBuffer(make([]byte, 0, 2*frameSize)),
    99  		shutdownCh: make(chan struct{}),
   100  		exitCh:     make(chan struct{}),
   101  	}
   102  }
   103  
   104  // Destroy is used to cleanup the StreamFramer and flush any pending frames
   105  func (s *StreamFramer) Destroy() {
   106  	s.l.Lock()
   107  
   108  	wasShutdown := s.shutdown
   109  	s.shutdown = true
   110  
   111  	if !wasShutdown {
   112  		close(s.shutdownCh)
   113  	}
   114  
   115  	s.heartbeat.Stop()
   116  	s.flusher.Stop()
   117  	running := s.running
   118  	s.l.Unlock()
   119  
   120  	// Ensure things were flushed
   121  	if running {
   122  		<-s.exitCh
   123  	}
   124  	if !wasShutdown {
   125  		close(s.out)
   126  	}
   127  }
   128  
   129  // Run starts a long lived goroutine that handles sending data as well as
   130  // heartbeating
   131  func (s *StreamFramer) Run() {
   132  	s.l.Lock()
   133  	defer s.l.Unlock()
   134  	if s.running {
   135  		return
   136  	}
   137  
   138  	s.running = true
   139  	go s.run()
   140  }
   141  
   142  // ExitCh returns a channel that will be closed when the run loop terminates.
   143  func (s *StreamFramer) ExitCh() <-chan struct{} {
   144  	return s.exitCh
   145  }
   146  
   147  // Err returns the error that caused the StreamFramer to exit
   148  func (s *StreamFramer) Err() error {
   149  	s.l.Lock()
   150  	defer s.l.Unlock()
   151  	return s.err
   152  }
   153  
   154  // run is the internal run method. It exits if Destroy is called or an error
   155  // occurs, in which case the exit channel is closed.
   156  func (s *StreamFramer) run() {
   157  	var err error
   158  	defer func() {
   159  		s.l.Lock()
   160  		s.running = false
   161  		s.err = err
   162  		s.l.Unlock()
   163  		close(s.exitCh)
   164  	}()
   165  
   166  OUTER:
   167  	for {
   168  		select {
   169  		case <-s.shutdownCh:
   170  			break OUTER
   171  		case <-s.flusher.C:
   172  			// Skip if there is nothing to flush
   173  			s.l.Lock()
   174  			if s.f.IsCleared() {
   175  				s.l.Unlock()
   176  				continue
   177  			}
   178  
   179  			// Read the data for the frame, and send it
   180  			s.f.Data = s.readData()
   181  			err = s.send(&s.f)
   182  			s.f.Clear()
   183  			s.l.Unlock()
   184  			if err != nil {
   185  				return
   186  			}
   187  		case <-s.heartbeat.C:
   188  			// Send a heartbeat frame
   189  			if err = s.send(HeartbeatStreamFrame); err != nil {
   190  				return
   191  			}
   192  		}
   193  	}
   194  
   195  	s.l.Lock()
   196  	if !s.f.IsCleared() {
   197  		s.f.Data = s.readData()
   198  		err = s.send(&s.f)
   199  		s.f.Clear()
   200  	}
   201  	s.l.Unlock()
   202  }
   203  
   204  // send takes a StreamFrame, encodes and sends it
   205  func (s *StreamFramer) send(f *StreamFrame) error {
   206  	sending := *f
   207  	f.Data = nil
   208  
   209  	select {
   210  	case s.out <- &sending:
   211  		return nil
   212  	case <-s.exitCh:
   213  		return nil
   214  	}
   215  }
   216  
   217  // readData is a helper which reads the buffered data returning up to the frame
   218  // size of data. Must be called with the lock held. The returned value is
   219  // invalid on the next read or write into the StreamFramer buffer
   220  func (s *StreamFramer) readData() []byte {
   221  	// Compute the amount to read from the buffer
   222  	size := s.data.Len()
   223  	if size > s.frameSize {
   224  		size = s.frameSize
   225  	}
   226  	if size == 0 {
   227  		return nil
   228  	}
   229  	d := s.data.Next(size)
   230  	return d
   231  }
   232  
   233  // Send creates and sends a StreamFrame based on the passed parameters. An error
   234  // is returned if the run routine hasn't run or encountered an error. Send is
   235  // asynchronous and does not block for the data to be transferred.
   236  func (s *StreamFramer) Send(file, fileEvent string, data []byte, offset int64) error {
   237  	s.l.Lock()
   238  	defer s.l.Unlock()
   239  
   240  	// If we are not running, return the error that caused us to not run or
   241  	// indicated that it was never started.
   242  	if !s.running {
   243  		if s.err != nil {
   244  			return s.err
   245  		}
   246  
   247  		return fmt.Errorf("StreamFramer not running")
   248  	}
   249  
   250  	// Check if not mergeable
   251  	if !s.f.IsCleared() && (s.f.File != file || s.f.FileEvent != fileEvent) {
   252  		// Flush the old frame
   253  		s.f.Data = s.readData()
   254  		select {
   255  		case <-s.exitCh:
   256  			return nil
   257  		default:
   258  		}
   259  		err := s.send(&s.f)
   260  		s.f.Clear()
   261  		if err != nil {
   262  			return err
   263  		}
   264  	}
   265  
   266  	// Store the new data as the current frame.
   267  	if s.f.IsCleared() {
   268  		s.f.Offset = offset
   269  		s.f.File = file
   270  		s.f.FileEvent = fileEvent
   271  	}
   272  
   273  	// Write the data to the buffer
   274  	s.data.Write(data)
   275  
   276  	// Handle the delete case in which there is no data
   277  	force := s.data.Len() == 0 && s.f.FileEvent != ""
   278  
   279  	// Flush till we are under the max frame size
   280  	for s.data.Len() >= s.frameSize || force {
   281  		// Clear since are flushing the frame and capturing the file event.
   282  		// Subsequent data frames will be flushed based on the data size alone
   283  		// since they share the same fileevent.
   284  		if force {
   285  			force = false
   286  		}
   287  
   288  		// Create a new frame to send it
   289  		s.f.Data = s.readData()
   290  		select {
   291  		case <-s.exitCh:
   292  			return nil
   293  		default:
   294  		}
   295  
   296  		if err := s.send(&s.f); err != nil {
   297  			return err
   298  		}
   299  
   300  		// Update the offset
   301  		s.f.Offset += int64(len(s.f.Data))
   302  	}
   303  
   304  	if s.data.Len() == 0 {
   305  		s.f.Clear()
   306  	}
   307  
   308  	return nil
   309  }