github.com/sunvim/utils@v0.1.0/queue/queue.go (about)

     1  package queue
     2  
     3  import (
     4  	"runtime"
     5  	"sync"
     6  	"sync/atomic"
     7  	"time"
     8  
     9  	"gopkg.in/eapache/queue.v1"
    10  )
    11  
    12  // Queue queue
    13  type Queue struct {
    14  	sync.Mutex
    15  	popable *sync.Cond
    16  	buffer  *queue.Queue
    17  	closed  bool
    18  	count   int32
    19  	cc      chan interface{}
    20  	once    sync.Once
    21  }
    22  
    23  // New
    24  func New() *Queue {
    25  	ch := &Queue{
    26  		buffer: queue.New(),
    27  	}
    28  	ch.popable = sync.NewCond(&ch.Mutex)
    29  	return ch
    30  }
    31  
    32  // Pop
    33  func (q *Queue) Pop() (v interface{}) {
    34  	c := q.popable
    35  
    36  	q.Mutex.Lock()
    37  	defer q.Mutex.Unlock()
    38  
    39  	for q.Len() == 0 && !q.closed {
    40  		c.Wait()
    41  	}
    42  
    43  	if q.closed {
    44  		return
    45  	}
    46  
    47  	if q.Len() > 0 {
    48  		buffer := q.buffer
    49  		v = buffer.Peek()
    50  		buffer.Remove()
    51  		atomic.AddInt32(&q.count, -1)
    52  	}
    53  	return
    54  }
    55  
    56  // TryPop
    57  func (q *Queue) TryPop() (v interface{}, ok bool) {
    58  	buffer := q.buffer
    59  
    60  	q.Mutex.Lock()
    61  	defer q.Mutex.Unlock()
    62  
    63  	if q.Len() > 0 {
    64  		v = buffer.Peek()
    65  		buffer.Remove()
    66  		atomic.AddInt32(&q.count, -1)
    67  		ok = true
    68  	} else if q.closed {
    69  		ok = true
    70  	}
    71  
    72  	return
    73  }
    74  
    75  // TryPopTimeout
    76  func (q *Queue) TryPopTimeout(tm time.Duration) (v interface{}, ok bool) {
    77  	q.once.Do(func() {
    78  		q.cc = make(chan interface{}, 1)
    79  	})
    80  	go func() {
    81  		q.popChan(&q.cc)
    82  	}()
    83  
    84  	ok = true
    85  	timeout := time.After(tm)
    86  	select {
    87  	case v = <-q.cc:
    88  	case <-timeout:
    89  		if !q.closed {
    90  			q.popable.Signal()
    91  		}
    92  		ok = false
    93  	}
    94  
    95  	return
    96  }
    97  
    98  // Pop
    99  func (q *Queue) popChan(v *chan interface{}) {
   100  	c := q.popable
   101  
   102  	q.Mutex.Lock()
   103  	defer q.Mutex.Unlock()
   104  
   105  	for q.Len() == 0 && !q.closed {
   106  		c.Wait()
   107  	}
   108  
   109  	if q.closed {
   110  		*v <- nil
   111  		return
   112  	}
   113  
   114  	if q.Len() > 0 {
   115  		buffer := q.buffer
   116  		tmp := buffer.Peek()
   117  		buffer.Remove()
   118  		atomic.AddInt32(&q.count, -1)
   119  		*v <- tmp
   120  	} else {
   121  		*v <- nil
   122  	}
   123  	return
   124  }
   125  
   126  // Push
   127  func (q *Queue) Push(v interface{}) {
   128  	q.Mutex.Lock()
   129  	defer q.Mutex.Unlock()
   130  	if !q.closed {
   131  		q.buffer.Add(v)
   132  		atomic.AddInt32(&q.count, 1)
   133  		q.popable.Signal()
   134  	}
   135  }
   136  
   137  // Len
   138  func (q *Queue) Len() int {
   139  	return (int)(atomic.LoadInt32(&q.count))
   140  }
   141  
   142  // Close Queue
   143  // After close, Pop will return nil without block, and TryPop will return v=nil, ok=True
   144  func (q *Queue) Close() {
   145  	q.Mutex.Lock()
   146  	defer q.Mutex.Unlock()
   147  	if !q.closed {
   148  		q.closed = true
   149  		atomic.StoreInt32(&q.count, 0)
   150  		q.popable.Broadcast()
   151  	}
   152  }
   153  
   154  // IsClose check is closed
   155  func (q *Queue) IsClose() bool {
   156  	return q.closed
   157  }
   158  
   159  // Wait
   160  func (q *Queue) Wait() {
   161  	for {
   162  		if q.closed || q.Len() == 0 {
   163  			break
   164  		}
   165  
   166  		runtime.Gosched()
   167  	}
   168  }