github.com/claygod/queue@v0.0.0-20221013165802-9870c6dace8e/queue.go (about)

     1  package queue
     2  
     3  // Queue
     4  // API
     5  // Copyright © 2016-2018 Eduard Sesigin. All rights reserved. Contacts: <claygod@yandex.ru>
     6  
     7  import (
     8  	// "fmt"
     9  	"runtime"
    10  	"sync"
    11  	"sync/atomic"
    12  )
    13  
    14  const sizeBlockDefault int = 1000
    15  const sizeQueueMax int = 100000
    16  const trialLimit int = 20000000
    17  
    18  // Queue - main struct.
    19  type Queue struct {
    20  	m         *sync.Mutex
    21  	hasp      int32
    22  	db        []interface{}
    23  	dbReserve []interface{}
    24  	head      int
    25  	tail      int
    26  	sizeQueue int
    27  	sizeBlock int
    28  	sizeIn    int
    29  }
    30  
    31  // New - create new queue.
    32  // The optional argument: the initial size of the queue.
    33  func New(args ...int) *Queue {
    34  	var sizeBlock int
    35  	if len(args) > 0 {
    36  		sizeBlock = args[0]
    37  	} else {
    38  		sizeBlock = sizeBlockDefault
    39  	}
    40  	q := Queue{
    41  		m:         &sync.Mutex{},
    42  		hasp:      0,
    43  		db:        make([]interface{}, sizeBlock),
    44  		dbReserve: make([]interface{}, sizeBlock),
    45  		head:      sizeBlock / 2,
    46  		tail:      sizeBlock / 2,
    47  		sizeQueue: sizeBlock,
    48  		sizeBlock: sizeBlock, // nil,
    49  		sizeIn:    sizeBlock,
    50  	}
    51  	// q.unlock() // q.hasp = 0
    52  	return &q
    53  }
    54  
    55  // PushTail - Insert element in the tail queue
    56  func (q *Queue) PushTail(n interface{}) bool {
    57  	// q.lock()
    58  	q.m.Lock()
    59  	defer q.m.Unlock()
    60  	if q.sizeQueue >= sizeQueueMax { //  || !q.lock()
    61  		//q.m.Unlock()
    62  		//q.unlock()
    63  		return false
    64  	}
    65  	q.db[q.tail] = n
    66  	q.tail++
    67  	if q.tail >= q.sizeQueue {
    68  		q.db = append(q.db, make([]interface{}, q.sizeBlock)...)
    69  		q.sizeQueue += q.sizeBlock
    70  	}
    71  	//q.unlock() // q.hasp = 0
    72  	//q.m.Unlock()
    73  	return true
    74  }
    75  
    76  // PushHead - Paste item in the queue head
    77  func (q *Queue) PushHead(n interface{}) bool {
    78  	q.m.Lock()
    79  	defer q.m.Unlock()
    80  	if q.sizeQueue >= sizeQueueMax { //  || !q.lock()
    81  		//q.m.Unlock()
    82  		return false
    83  	}
    84  	q.head--
    85  	if q.head == 0 {
    86  		newDb := make([]interface{}, q.sizeQueue+q.sizeBlock)
    87  		copy(newDb[q.sizeBlock:], q.db)
    88  		q.db = newDb
    89  		q.head += q.sizeBlock
    90  		q.tail += q.sizeBlock
    91  		q.sizeQueue = q.sizeQueue + q.sizeBlock
    92  	}
    93  	q.db[q.head] = n
    94  	//q.unlock() // q.hasp = 0
    95  	//q.m.Unlock()
    96  	return true
    97  }
    98  
    99  // PopHead - Get the first element of the queue
   100  func (q *Queue) PopHead() (interface{}, bool) {
   101  	q.m.Lock()
   102  	defer q.m.Unlock()
   103  	var n interface{}
   104  	//if !q.lock() {
   105  	//	return n, false
   106  	//}
   107  	if q.tail == q.head {
   108  		//q.unlock() // q.hasp = 0
   109  		return n, false
   110  	}
   111  	n, q.db[q.head] = q.db[q.head], nil
   112  	q.head++
   113  	if q.head == q.tail { //  && q.sizeQueue >= q.sizeBlock*3
   114  		q.clean()
   115  	}
   116  	//q.unlock() // q.hasp = 0
   117  	return n, true
   118  }
   119  
   120  func (q *Queue) PopHeadList(num int) []interface{} {
   121  	//q.lock()
   122  	q.m.Lock()
   123  	defer q.m.Unlock()
   124  	//if !q.lock() {
   125  	//	return make([]interface{}, 0), false
   126  	//}
   127  	if q.tail == q.head {
   128  		//q.unlock() // q.hasp = 0
   129  		//q.m.Unlock()
   130  		//q.unlock()
   131  		return make([]interface{}, 0)
   132  	}
   133  	end := q.head + num
   134  	if end > q.tail {
   135  		end = q.tail
   136  	}
   137  	out := make([]interface{}, end-q.head)
   138  	copy(out, q.db[q.head:end])
   139  	q.head = end
   140  	if q.head == q.tail { //  && q.sizeQueue >= q.sizeBlock*3
   141  		q.clean()
   142  	}
   143  	// q.unlock() // q.hasp = 0
   144  	//q.m.Unlock()
   145  	//q.unlock()
   146  	return out
   147  }
   148  
   149  func (q *Queue) PopAll() []interface{} {
   150  	ndb := make([]interface{}, q.sizeIn)
   151  	q.m.Lock()
   152  	defer q.m.Unlock()
   153  	out := q.db[q.head:q.tail]
   154  
   155  	q.hasp = 0
   156  	q.db = ndb
   157  	q.head = q.sizeIn / 2
   158  	q.tail = q.sizeIn / 2
   159  	q.sizeQueue = q.sizeIn
   160  	q.sizeBlock = q.sizeIn
   161  	//q.m.Unlock()
   162  	return out
   163  }
   164  
   165  // PopTail - Get the item from the queue tail
   166  func (q *Queue) PopTail() (interface{}, bool) {
   167  	var n interface{}
   168  	//if !q.lock() {
   169  	//	return n, false
   170  	//}
   171  	q.m.Lock()
   172  	defer q.m.Unlock()
   173  	if q.head == q.tail {
   174  		//q.unlock() // q.hasp = 0
   175  		return n, false
   176  	}
   177  	q.tail--
   178  	n, q.db[q.tail] = q.db[q.tail], nil
   179  	if q.head == q.tail { // && q.sizeQueue >= q.sizeBlock*3
   180  		q.clean()
   181  	}
   182  	//q.unlock() // q.hasp = 0
   183  	return n, true
   184  }
   185  
   186  // LenQueue - The number of elements in the queue
   187  func (q *Queue) LenQueue() int {
   188  	q.m.Lock()
   189  	defer q.m.Unlock()
   190  	//q.lock()
   191  	ln := q.tail - q.head
   192  	//q.unlock() // q.hasp = 0
   193  	return ln
   194  }
   195  
   196  // SizeQueue - The size reserved for queue
   197  func (q *Queue) SizeQueue() int {
   198  	q.m.Lock()
   199  	defer q.m.Unlock()
   200  	//q.lock()
   201  	ln := q.sizeQueue
   202  	//q.unlock() // q.hasp = 0
   203  	return ln
   204  }
   205  
   206  func (q *Queue) cleanAlternative() {
   207  	// fmt.Print("\r\n------------ CLEAN!!\r\n")
   208  	if q.dbReserve == nil {
   209  		q.db = make([]interface{}, q.sizeBlock)
   210  	} else {
   211  		q.db = q.dbReserve // make([]interface{}, q.sizeBlock)
   212  	}
   213  
   214  	q.head = q.sizeBlock / 2
   215  	q.tail = q.sizeBlock / 2
   216  	q.sizeQueue = q.sizeBlock
   217  	q.dbReserve = nil
   218  	go q.genDbReserve()
   219  }
   220  
   221  // cleanAndReplace - Resetting the queue (not thread-safe, is called only after the lock)
   222  func (q *Queue) cleanAndReplace() {
   223  	q.db = make([]interface{}, q.sizeBlock)
   224  	q.head = q.sizeBlock / 2
   225  	q.tail = q.sizeBlock / 2
   226  	q.sizeQueue = q.sizeBlock
   227  }
   228  
   229  func (q *Queue) clean() {
   230  	if q.sizeQueue >= sizeQueueMax/2 { // q.sizeBlock*3
   231  		q.db = make([]interface{}, q.sizeBlock)
   232  		q.head = q.sizeBlock / 2
   233  		q.tail = q.sizeBlock / 2
   234  		q.sizeQueue = q.sizeBlock
   235  	} else {
   236  		q.head = q.sizeQueue / 2
   237  		q.tail = q.sizeQueue / 2
   238  	}
   239  
   240  }
   241  
   242  func (q *Queue) genDbReserve() {
   243  	q.dbReserve = make([]interface{}, q.sizeBlock)
   244  
   245  }
   246  
   247  // lock - block queue
   248  func (q *Queue) lock() bool {
   249  	for { // i := trialLimit; i > 0; i--
   250  		if q.hasp == 0 && atomic.CompareAndSwapInt32(&q.hasp, 0, 1) {
   251  			break
   252  		}
   253  		//if i == 0 {
   254  		//	return false
   255  		//}
   256  		runtime.Gosched()
   257  	}
   258  	return true
   259  }
   260  
   261  func (q *Queue) unlock() {
   262  	atomic.StoreInt32(&q.hasp, 0)
   263  	// q.hasp = 0
   264  }