github.com/ssetin/penguincast@v0.2.0/src/server/buffer.go (about) 1 // Package iceserver - icecast streaming server 2 package iceserver 3 4 import ( 5 "sync" 6 "sync/atomic" 7 ) 8 9 //BufElement - kind of buffer page 10 type BufElement struct { 11 locked int32 12 len int 13 buffer []byte 14 next *BufElement 15 prev *BufElement 16 mux sync.Mutex 17 } 18 19 // BufferQueue - queue, which stores stream fragments from SOURCE 20 type BufferQueue struct { 21 mux sync.Mutex 22 size int 23 maxBufferSize int 24 minBufferSize int 25 first, last *BufElement 26 pool *sync.Pool 27 } 28 29 // BufferInfo - struct for monitoring 30 type BufferInfo struct { 31 Size int 32 SizeBytes int 33 Graph string 34 InUse int 35 } 36 37 // Reset ... 38 func (q *BufElement) Reset(pool *sync.Pool) { 39 q.mux.Lock() 40 defer q.mux.Unlock() 41 q.len = 0 42 q.locked = 0 43 if q.next != nil { 44 q.next.prev = nil 45 q.next = nil 46 } 47 if q.prev != nil { 48 q.prev.next = nil 49 q.prev = nil 50 } 51 //q.buffer = nil 52 q.buffer = q.buffer[:0] 53 pool.Put(q.buffer) 54 } 55 56 // Next - getting next element 57 func (q *BufElement) Next() *BufElement { 58 q.mux.Lock() 59 defer q.mux.Unlock() 60 return q.next 61 } 62 63 // Lock - mark element as used by listener 64 func (q *BufElement) Lock() { 65 atomic.AddInt32(&q.locked, 1) 66 } 67 68 // UnLock - mark element as unused by listener 69 func (q *BufElement) UnLock() { 70 atomic.AddInt32(&q.locked, -1) 71 } 72 73 // IsLocked (logical lock, mean it's in use) 74 func (q *BufElement) IsLocked() bool { 75 if atomic.LoadInt32(&q.locked) <= 0 { 76 return false 77 } 78 return true 79 } 80 81 //*************************************** 82 83 // Init - initiates buffer queue 84 func (q *BufferQueue) Init(minsize int, pool *sync.Pool) { 85 q.mux.Lock() 86 defer q.mux.Unlock() 87 q.size = 0 88 q.maxBufferSize = minsize * 8 89 q.minBufferSize = minsize 90 q.first = nil 91 q.last = nil 92 q.pool = pool 93 } 94 95 // NewBufElement - returns new buffer element (page) 96 func (q *BufferQueue) newBufElement(buffer []byte, readed int) *BufElement { 97 t := &BufElement{} 98 99 if q.pool == nil { 100 return nil 101 } 102 103 t.buffer = q.pool.Get().([]byte) 104 t.buffer = t.buffer[:readed] 105 //t.buffer = make([]byte, readed) 106 t.len = readed 107 copy(t.buffer, buffer) 108 return t 109 } 110 111 // Size - returns buffer queue size 112 func (q *BufferQueue) Size() int { 113 q.mux.Lock() 114 defer q.mux.Unlock() 115 return q.size 116 } 117 118 // Info - returns buffer state 119 func (q *BufferQueue) Info() BufferInfo { 120 var result BufferInfo 121 var t *BufElement 122 str := "" 123 124 q.mux.Lock() 125 defer q.mux.Unlock() 126 127 t = q.first 128 129 for { 130 if t == nil { 131 break 132 } 133 result.Size++ 134 result.SizeBytes += t.len 135 136 if t.IsLocked() { 137 str = str + "1" 138 result.InUse++ 139 } else { 140 str = str + "0" 141 } 142 t = t.next 143 } 144 145 result.Graph = str 146 147 return result 148 } 149 150 // First - returns the first element in buffer queue 151 func (q *BufferQueue) First() *BufElement { 152 q.mux.Lock() 153 defer q.mux.Unlock() 154 return q.first 155 } 156 157 // Last - returns the last element in buffer queue 158 func (q *BufferQueue) Last() *BufElement { 159 q.mux.Lock() 160 defer q.mux.Unlock() 161 return q.last 162 } 163 164 // Start - returns the element to start with 165 func (q *BufferQueue) Start(burstSize int) *BufElement { 166 q.mux.Lock() 167 defer q.mux.Unlock() 168 169 burst := 0 170 var t *BufElement 171 t = q.last 172 if t == nil { 173 return nil 174 } 175 176 for { 177 if t.prev == nil || burst > burstSize { 178 break 179 } 180 burst += t.len 181 t = t.prev 182 } 183 184 return t 185 } 186 187 // checkAndTruncate - check if the max buffer size is reached and try to truncate it 188 // taking into account pages, which still in use 189 func (q *BufferQueue) checkAndTruncate() { 190 if q.Size() < q.maxBufferSize { 191 return 192 } 193 194 q.mux.Lock() 195 defer q.mux.Unlock() 196 var t *BufElement 197 198 for { 199 t = q.first 200 if t == nil || q.size <= 1 { 201 break 202 } 203 if t.IsLocked() { 204 break 205 } else { 206 if t.next != nil { 207 if q.size <= q.minBufferSize { 208 break 209 } 210 q.first = t.next 211 t.Reset(q.pool) 212 t = nil 213 q.size-- 214 } else { 215 break 216 } 217 } 218 } 219 } 220 221 // Append - appends new page to the end of the buffer queue 222 func (q *BufferQueue) Append(buffer []byte, readed int) { 223 t := q.newBufElement(buffer, readed) 224 if t == nil { 225 return 226 } 227 228 q.mux.Lock() 229 defer q.mux.Unlock() 230 231 if q.size == 0 { 232 q.size = 1 233 t.next = nil 234 t.prev = nil 235 q.first = t 236 q.last = t 237 return 238 } 239 240 q.last.mux.Lock() 241 q.last.next = t 242 t.prev = q.last 243 q.last.mux.Unlock() 244 q.last = t 245 q.size++ 246 }