status-go/vendor/github.com/pion/webrtc/v3/operations.go

155 lines
3.2 KiB
Go
Raw Normal View History

2024-05-15 23:15:00 +00:00
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
2022-03-10 09:44:48 +00:00
package webrtc
import (
"container/list"
"sync"
)
// Operation is a function
type operation func()
// Operations is a task executor.
type operations struct {
mu sync.Mutex
busyCh chan struct{}
ops *list.List
updateNegotiationNeededFlagOnEmptyChain *atomicBool
onNegotiationNeeded func()
isClosed bool
2022-03-10 09:44:48 +00:00
}
func newOperations(
updateNegotiationNeededFlagOnEmptyChain *atomicBool,
onNegotiationNeeded func(),
) *operations {
2022-03-10 09:44:48 +00:00
return &operations{
ops: list.New(),
updateNegotiationNeededFlagOnEmptyChain: updateNegotiationNeededFlagOnEmptyChain,
onNegotiationNeeded: onNegotiationNeeded,
2022-03-10 09:44:48 +00:00
}
}
// Enqueue adds a new action to be executed. If there are no actions scheduled,
// the execution will start immediately in a new goroutine. If the queue has been
// closed, the operation will be dropped. The queue is only deliberately closed
// by a user.
2022-03-10 09:44:48 +00:00
func (o *operations) Enqueue(op operation) {
o.mu.Lock()
defer o.mu.Unlock()
_ = o.tryEnqueue(op)
}
// tryEnqueue attempts to enqueue the given operation. It returns false
// if the op is invalid or the queue is closed. mu must be locked by
// tryEnqueue's caller.
func (o *operations) tryEnqueue(op operation) bool {
2022-03-10 09:44:48 +00:00
if op == nil {
return false
2022-03-10 09:44:48 +00:00
}
if o.isClosed {
return false
}
2022-03-10 09:44:48 +00:00
o.ops.PushBack(op)
if o.busyCh == nil {
o.busyCh = make(chan struct{})
2022-03-10 09:44:48 +00:00
go o.start()
}
return true
2022-03-10 09:44:48 +00:00
}
// IsEmpty checks if there are tasks in the queue
func (o *operations) IsEmpty() bool {
o.mu.Lock()
defer o.mu.Unlock()
return o.ops.Len() == 0
}
// Done blocks until all currently enqueued operations are finished executing.
// For more complex synchronization, use Enqueue directly.
func (o *operations) Done() {
var wg sync.WaitGroup
wg.Add(1)
o.mu.Lock()
enqueued := o.tryEnqueue(func() {
2022-03-10 09:44:48 +00:00
wg.Done()
})
o.mu.Unlock()
if !enqueued {
return
}
2022-03-10 09:44:48 +00:00
wg.Wait()
}
// GracefulClose waits for the operations queue to be cleared and forbids
// new operations from being enqueued.
func (o *operations) GracefulClose() {
o.mu.Lock()
if o.isClosed {
o.mu.Unlock()
return
}
// do not enqueue anymore ops from here on
// o.isClosed=true will also not allow a new busyCh
// to be created.
o.isClosed = true
busyCh := o.busyCh
o.mu.Unlock()
if busyCh == nil {
return
}
<-busyCh
}
2022-03-10 09:44:48 +00:00
func (o *operations) pop() func() {
o.mu.Lock()
defer o.mu.Unlock()
if o.ops.Len() == 0 {
return nil
}
e := o.ops.Front()
o.ops.Remove(e)
2024-05-15 23:15:00 +00:00
if op, ok := e.Value.(operation); ok {
return op
}
return nil
2022-03-10 09:44:48 +00:00
}
func (o *operations) start() {
defer func() {
o.mu.Lock()
defer o.mu.Unlock()
// this wil lbe the most recent busy chan
close(o.busyCh)
if o.ops.Len() == 0 || o.isClosed {
o.busyCh = nil
2022-03-10 09:44:48 +00:00
return
}
2022-03-10 09:44:48 +00:00
// either a new operation was enqueued while we
// were busy, or an operation panicked
o.busyCh = make(chan struct{})
2022-03-10 09:44:48 +00:00
go o.start()
}()
fn := o.pop()
for fn != nil {
fn()
fn = o.pop()
}
if !o.updateNegotiationNeededFlagOnEmptyChain.get() {
return
}
o.updateNegotiationNeededFlagOnEmptyChain.set(false)
o.onNegotiationNeeded()
2022-03-10 09:44:48 +00:00
}