github.com/zhyoulun/cilium@v1.6.12/pkg/eventqueue/eventqueue.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package eventqueue
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"sync"
    21  	"sync/atomic"
    22  
    23  	"github.com/cilium/cilium/pkg/lock"
    24  	"github.com/cilium/cilium/pkg/logging"
    25  	"github.com/cilium/cilium/pkg/logging/logfields"
    26  	"github.com/cilium/cilium/pkg/option"
    27  	"github.com/cilium/cilium/pkg/spanstat"
    28  
    29  	"github.com/sirupsen/logrus"
    30  )
    31  
    32  var (
    33  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "eventqueue")
    34  )
    35  
    36  // EventQueue is a structure which is utilized to handle Events in a first-in,
    37  // first-out order. An EventQueue may be closed, in which case all events which
    38  // are queued up, but have not been processed yet, will be cancelled (i.e., not
    39  // ran). It is guaranteed that no events will be scheduled onto an EventQueue
    40  // after it has been closed; if any event is attempted to be scheduled onto an
    41  // EventQueue after it has been closed, it will be cancelled immediately. For
    42  // any event to be processed by the EventQueue, it must implement the
    43  // `EventHandler` interface. This allows for different types of events to be
    44  // processed by anything which chooses to utilize an `EventQueue`.
    45  type EventQueue struct {
    46  
    47  	// This should always be a buffered channel.
    48  	events chan *Event
    49  
    50  	// close is closed once the EventQueue has been closed.
    51  	close chan struct{}
    52  
    53  	// drain is closed when the EventQueue is stopped. Any Event which is
    54  	// Enqueued after this channel is closed will be cancelled / not processed
    55  	// by the queue. If an Event has been Enqueued, but has not been processed
    56  	// before this channel is closed, it will be cancelled and not processed
    57  	// as well.
    58  	drain chan struct{}
    59  
    60  	// eventQueueOnce is used to ensure that the EventQueue business logic can
    61  	// only be ran once.
    62  	eventQueueOnce sync.Once
    63  
    64  	// closeOnce is used to ensure that the EventQueue can only be closed once.
    65  	closeOnce sync.Once
    66  
    67  	// name is used to differentiate this EventQueue from other EventQueues that
    68  	// are also running in logs
    69  	name string
    70  
    71  	eventsMu lock.RWMutex
    72  }
    73  
    74  // NewEventQueue returns an EventQueue with a capacity for only one event at
    75  // a time.
    76  func NewEventQueue() *EventQueue {
    77  	return NewEventQueueBuffered("", 1)
    78  
    79  }
    80  
    81  func (q *EventQueue) getLogger() *logrus.Entry {
    82  	return log.WithFields(
    83  		logrus.Fields{
    84  			"name": q.name,
    85  		})
    86  }
    87  
    88  // NewEventQueueBuffered returns an EventQueue with a capacity of,
    89  // numBufferedEvents at a time, and all other needed fields initialized.
    90  func NewEventQueueBuffered(name string, numBufferedEvents int) *EventQueue {
    91  	log.WithFields(logrus.Fields{
    92  		"name":              name,
    93  		"numBufferedEvents": numBufferedEvents,
    94  	}).Debug("creating new EventQueue")
    95  	return &EventQueue{
    96  		name: name,
    97  		// Up to numBufferedEvents can be Enqueued until Enqueueing blocks.
    98  		events: make(chan *Event, numBufferedEvents),
    99  		close:  make(chan struct{}),
   100  		drain:  make(chan struct{}),
   101  	}
   102  
   103  }
   104  
   105  // Event is an event that can be enqueued onto an EventQueue.
   106  type Event struct {
   107  	// Metadata is the information about the event which is sent
   108  	// by its queuer. Metadata must implement the EventHandler interface in
   109  	// order for the Event to be successfully processed by the EventQueue.
   110  	Metadata EventHandler
   111  
   112  	// eventResults is a channel on which the results of the event are sent.
   113  	// It is populated by the EventQueue itself, not by the queuer. This channel
   114  	// is closed if the event is cancelled.
   115  	eventResults chan interface{}
   116  
   117  	// cancelled signals that the given Event was not ran. This can happen
   118  	// if the EventQueue processing this Event was closed before the Event was
   119  	// Enqueued onto the Event queue, or if the Event was Enqueued onto an
   120  	// EventQueue, and the EventQueue on which the Event was scheduled was
   121  	// closed.
   122  	cancelled chan struct{}
   123  
   124  	// stats is a field which contains information about when this event is
   125  	// enqueued, dequeued, etc.
   126  	stats eventStatistics
   127  
   128  	// enqueued is an atomic boolean that specifies whether this event has
   129  	// been enqueued on an EventQueue.
   130  	enqueued int32
   131  }
   132  
   133  type eventStatistics struct {
   134  
   135  	// waitEnqueue shows how long a given event was waiting on the queue before
   136  	// it was actually processed.
   137  	waitEnqueue spanstat.SpanStat
   138  
   139  	// durationStat shows how long the actual processing of the event took. This
   140  	// is the time for how long Handle() takes for the event.
   141  	durationStat spanstat.SpanStat
   142  
   143  	// waitConsumeOffQueue shows how long it took for the event to be consumed
   144  	// plus the time it the event waited in the queue.
   145  	waitConsumeOffQueue spanstat.SpanStat
   146  }
   147  
   148  // NewEvent returns an Event with all fields initialized.
   149  func NewEvent(meta EventHandler) *Event {
   150  	return &Event{
   151  		Metadata:     meta,
   152  		eventResults: make(chan interface{}, 1),
   153  		cancelled:    make(chan struct{}),
   154  		stats:        eventStatistics{},
   155  	}
   156  }
   157  
   158  // WasCancelled returns whether the cancelled channel for the given Event has
   159  // been closed or not. Cancellation occurs if the event was not processed yet
   160  // by an EventQueue onto which this Event was Enqueued, and the queue is closed,
   161  // or if the event was attempted to be scheduled onto an EventQueue which has
   162  // already been closed.
   163  func (ev *Event) WasCancelled() bool {
   164  	select {
   165  	case <-ev.cancelled:
   166  		return true
   167  	default:
   168  		return false
   169  	}
   170  }
   171  
   172  // Enqueue pushes the given event onto the EventQueue. If the queue has been
   173  // stopped, the Event will not be enqueued, and its cancel channel will be
   174  // closed, indicating that the Event was not ran. This function may block if
   175  // the queue is at its capacity for events. If a single Event has Enqueue
   176  // called on it multiple times asynchronously, there is no guarantee as to
   177  // which one will return the channel which passes results back to the caller.
   178  // It is up to the caller to check whether the returned channel is nil, as
   179  // waiting to receive on such a channel will block forever. Returns an error
   180  // if the Event has been previously enqueued, if the Event is nil, or the queue
   181  // itself is not initialized properly.
   182  func (q *EventQueue) Enqueue(ev *Event) (<-chan interface{}, error) {
   183  	if q.notSafeToAccess() || ev == nil {
   184  		return nil, fmt.Errorf("unable to Enqueue event")
   185  	}
   186  
   187  	// Multiple Enqueues can occur at the same time. Ensure that events channel
   188  	// is not closed while we are enqueueing events.
   189  	q.eventsMu.RLock()
   190  	defer q.eventsMu.RUnlock()
   191  
   192  	// Events can only be enqueued once.
   193  	if atomic.AddInt32(&ev.enqueued, 1) > 1 {
   194  		return nil, fmt.Errorf("unable to Enqueue event; event has already had Enqueue called on it")
   195  	}
   196  
   197  	select {
   198  	// The event should be drained from the queue (e.g., it should not be
   199  	// processed).
   200  	case <-q.drain:
   201  		// Closed eventResults channel signifies cancellation.
   202  		close(ev.cancelled)
   203  		close(ev.eventResults)
   204  
   205  		return ev.eventResults, nil
   206  	default:
   207  		// The events channel may be closed even if an event has been pushed
   208  		// onto the events channel, as events are consumed off of the events
   209  		// channel asynchronously! If the EventQueue is closed before this
   210  		// event is processed, then it will be cancelled.
   211  
   212  		ev.stats.waitEnqueue.Start()
   213  		ev.stats.waitConsumeOffQueue.Start()
   214  		q.events <- ev
   215  		ev.stats.waitEnqueue.End(true)
   216  		return ev.eventResults, nil
   217  	}
   218  }
   219  
   220  func (ev *Event) printStats(q *EventQueue) {
   221  	if option.Config.Debug {
   222  		q.getLogger().WithFields(logrus.Fields{
   223  			"eventType":                    reflect.TypeOf(ev.Metadata).String(),
   224  			"eventHandlingDuration":        ev.stats.durationStat.Total(),
   225  			"eventEnqueueWaitTime":         ev.stats.waitEnqueue.Total(),
   226  			"eventConsumeOffQueueWaitTime": ev.stats.waitConsumeOffQueue.Total(),
   227  		}).Debug("EventQueue event processing statistics")
   228  	}
   229  }
   230  
   231  // Run consumes events that have been queued for this EventQueue. It
   232  // is presumed that the eventQueue is a buffered channel with a length of one
   233  // (i.e., only one event can be processed at a time). All business logic for
   234  // handling queued events is contained within this function. The events in the
   235  // queue must implement the EventHandler interface. If the event queue is
   236  // closed, then all events which were queued up, but not processed, are
   237  // cancelled; any event which is currently being processed will not be
   238  // cancelled.
   239  func (q *EventQueue) Run() {
   240  
   241  	if q.notSafeToAccess() {
   242  		return
   243  	}
   244  
   245  	go q.eventQueueOnce.Do(func() {
   246  		for ev := range q.events {
   247  			select {
   248  			case <-q.drain:
   249  				ev.stats.waitConsumeOffQueue.End(false)
   250  				close(ev.cancelled)
   251  				close(ev.eventResults)
   252  				ev.printStats(q)
   253  			default:
   254  				ev.stats.waitConsumeOffQueue.End(true)
   255  				ev.stats.durationStat.Start()
   256  				ev.Metadata.Handle(ev.eventResults)
   257  				// Always indicate success for now.
   258  				ev.stats.durationStat.End(true)
   259  				// Ensures that no more results can be sent as the event has
   260  				// already been processed.
   261  				ev.printStats(q)
   262  				close(ev.eventResults)
   263  			}
   264  		}
   265  	})
   266  }
   267  
   268  func (q *EventQueue) notSafeToAccess() bool {
   269  	return q == nil || q.close == nil || q.drain == nil || q.events == nil
   270  }
   271  
   272  // Stop stops any further events from being processed by the EventQueue. Any
   273  // event which is currently being processed by the EventQueue will continue to
   274  // run. All other events waiting to be processed, and all events that may be
   275  // enqueued will not be processed by the event queue; they will be cancelled.
   276  // If the queue has already been stopped, this is a no-op.
   277  func (q *EventQueue) Stop() {
   278  	if q.notSafeToAccess() {
   279  		return
   280  	}
   281  
   282  	q.closeOnce.Do(func() {
   283  		q.getLogger().Debug("stopping EventQueue")
   284  		// Any event that is sent to the queue at this point will be cancelled
   285  		// immediately in Enqueue().
   286  		close(q.drain)
   287  
   288  		// Signal that the queue has been drained.
   289  		close(q.close)
   290  
   291  		q.eventsMu.Lock()
   292  		close(q.events)
   293  		q.eventsMu.Unlock()
   294  	})
   295  }
   296  
   297  // WaitToBeDrained returns the channel which waits for the EventQueue to have been
   298  // stopped. This allows for queuers to ensure that all events in the queue have
   299  // been processed or cancelled. If the queue is nil, returns immediately.
   300  func (q *EventQueue) WaitToBeDrained() {
   301  	if q == nil {
   302  		return
   303  	}
   304  	<-q.close
   305  }
   306  
   307  // EventHandler is an interface for allowing an EventQueue to handle events
   308  // in a generic way. To be processed by the EventQueue, all event types must
   309  // implement any function specified in this interface.
   310  type EventHandler interface {
   311  	Handle(chan interface{})
   312  }