github.com/pion/webrtc/v3@v3.2.24/operations.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package webrtc
     5  
     6  import (
     7  	"container/list"
     8  	"sync"
     9  )
    10  
    11  // Operation is a function
    12  type operation func()
    13  
    14  // Operations is a task executor.
    15  type operations struct {
    16  	mu   sync.Mutex
    17  	busy bool
    18  	ops  *list.List
    19  }
    20  
    21  func newOperations() *operations {
    22  	return &operations{
    23  		ops: list.New(),
    24  	}
    25  }
    26  
    27  // Enqueue adds a new action to be executed. If there are no actions scheduled,
    28  // the execution will start immediately in a new goroutine.
    29  func (o *operations) Enqueue(op operation) {
    30  	if op == nil {
    31  		return
    32  	}
    33  
    34  	o.mu.Lock()
    35  	running := o.busy
    36  	o.ops.PushBack(op)
    37  	o.busy = true
    38  	o.mu.Unlock()
    39  
    40  	if !running {
    41  		go o.start()
    42  	}
    43  }
    44  
    45  // IsEmpty checks if there are tasks in the queue
    46  func (o *operations) IsEmpty() bool {
    47  	o.mu.Lock()
    48  	defer o.mu.Unlock()
    49  	return o.ops.Len() == 0
    50  }
    51  
    52  // Done blocks until all currently enqueued operations are finished executing.
    53  // For more complex synchronization, use Enqueue directly.
    54  func (o *operations) Done() {
    55  	var wg sync.WaitGroup
    56  	wg.Add(1)
    57  	o.Enqueue(func() {
    58  		wg.Done()
    59  	})
    60  	wg.Wait()
    61  }
    62  
    63  func (o *operations) pop() func() {
    64  	o.mu.Lock()
    65  	defer o.mu.Unlock()
    66  	if o.ops.Len() == 0 {
    67  		return nil
    68  	}
    69  
    70  	e := o.ops.Front()
    71  	o.ops.Remove(e)
    72  	if op, ok := e.Value.(operation); ok {
    73  		return op
    74  	}
    75  	return nil
    76  }
    77  
    78  func (o *operations) start() {
    79  	defer func() {
    80  		o.mu.Lock()
    81  		defer o.mu.Unlock()
    82  		if o.ops.Len() == 0 {
    83  			o.busy = false
    84  			return
    85  		}
    86  		// either a new operation was enqueued while we
    87  		// were busy, or an operation panicked
    88  		go o.start()
    89  	}()
    90  
    91  	fn := o.pop()
    92  	for fn != nil {
    93  		fn()
    94  		fn = o.pop()
    95  	}
    96  }