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  }