github.com/livekit/protocol@v1.39.3/webhook/resource_queue.go (about) 1 // Copyright 2023 LiveKit, Inc. 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 webhook 16 17 import ( 18 "context" 19 "errors" 20 "sync" 21 "time" 22 23 "github.com/gammazero/deque" 24 25 "github.com/livekit/protocol/livekit" 26 ) 27 28 var ( 29 errQueueFull = errors.New("queue is full") 30 errQueueClosed = errors.New("queue is closed") 31 ) 32 33 type item struct { 34 ctx context.Context 35 queuedAt time.Time 36 event *livekit.WebhookEvent 37 params *ResourceURLNotifierParams 38 } 39 40 type resourceQueueParams struct { 41 MaxDepth int 42 43 Poster poster 44 } 45 46 type resourceQueue struct { 47 params resourceQueueParams 48 49 mu sync.Mutex 50 items deque.Deque[*item] 51 cond *sync.Cond 52 53 closed bool 54 drain bool 55 } 56 57 func newResourceQueue(params resourceQueueParams) *resourceQueue { 58 r := &resourceQueue{ 59 params: params, 60 } 61 r.items.SetBaseCap(int(min(params.MaxDepth, 16))) 62 r.cond = sync.NewCond(&r.mu) 63 64 go r.worker() 65 return r 66 } 67 68 func (r *resourceQueue) Stop(force bool) { 69 r.mu.Lock() 70 defer r.mu.Unlock() 71 72 if !r.closed { 73 r.closed = true 74 r.drain = !force 75 76 r.cond.Broadcast() 77 } 78 } 79 80 func (r *resourceQueue) Enqueue(ctx context.Context, whEvent *livekit.WebhookEvent, params *ResourceURLNotifierParams) (int, error) { 81 return r.EnqueueAt(ctx, time.Now(), whEvent, params) 82 } 83 84 func (r *resourceQueue) EnqueueAt(ctx context.Context, at time.Time, whEvent *livekit.WebhookEvent, params *ResourceURLNotifierParams) (int, error) { 85 r.mu.Lock() 86 defer r.mu.Unlock() 87 88 if r.closed { 89 return r.items.Len(), errQueueClosed 90 } 91 92 if r.items.Len() >= r.params.MaxDepth { 93 return r.items.Len(), errQueueFull 94 } 95 96 r.items.PushBack(&item{ctx, at, whEvent, params}) 97 r.cond.Broadcast() 98 return r.items.Len(), nil 99 } 100 101 func (r *resourceQueue) worker() { 102 for { 103 r.mu.Lock() 104 for { 105 if r.closed && (!r.drain || r.items.Len() == 0) { 106 r.mu.Unlock() 107 return 108 } 109 110 if r.items.Len() != 0 { 111 break 112 } 113 r.cond.Wait() 114 } 115 116 item := r.items.PopFront() 117 qLen := r.items.Len() 118 r.mu.Unlock() 119 120 r.params.Poster.Process(item.ctx, item.queuedAt, item.event, item.params, qLen) 121 } 122 }