github.com/dubbogo/gost@v1.14.0/container/queue/queue.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package gxqueue
    19  
    20  import (
    21  	"errors"
    22  	"runtime"
    23  	"sync"
    24  	"sync/atomic"
    25  	"time"
    26  )
    27  
    28  var (
    29  	// ErrDisposed is returned when an operation is performed on a disposed
    30  	// queue.
    31  	ErrDisposed = errors.New(`queue: disposed`)
    32  
    33  	// ErrTimeout is returned when an applicable queue operation times out.
    34  	ErrTimeout = errors.New(`queue: poll timed out`)
    35  
    36  	// ErrEmptyQueue is returned when an non-applicable queue operation was called
    37  	// due to the queue's empty item state
    38  	ErrEmptyQueue = errors.New(`queue: empty queue`)
    39  )
    40  
    41  // waiters is the struct responsible for store sema(waiter better) of queue.
    42  type waiters []*sema
    43  
    44  func (w *waiters) get() *sema {
    45  	if len(*w) == 0 {
    46  		return nil
    47  	}
    48  
    49  	sema := (*w)[0]
    50  	copy((*w)[0:], (*w)[1:])
    51  	(*w)[len(*w)-1] = nil // or the zero value of T
    52  	*w = (*w)[:len(*w)-1]
    53  	return sema
    54  }
    55  
    56  func (w *waiters) put(sema *sema) {
    57  	*w = append(*w, sema)
    58  }
    59  
    60  func (w *waiters) remove(sema *sema) {
    61  	if len(*w) == 0 {
    62  		return
    63  	}
    64  	for i := range *w {
    65  		if (*w)[i] == sema {
    66  			*w = append((*w)[:i], (*w)[i+1:]...)
    67  			return
    68  		}
    69  	}
    70  }
    71  
    72  // items is the struct responsible for store queue data
    73  type items []interface{}
    74  
    75  func (items *items) get(number int64) []interface{} {
    76  	index := int(number)
    77  	if int(number) > len(*items) {
    78  		index = len(*items)
    79  	}
    80  
    81  	returnItems := make([]interface{}, 0, index)
    82  	returnItems = returnItems[:index]
    83  
    84  	copy(returnItems[:index], (*items))
    85  
    86  	*items = (*items)[index:]
    87  	return returnItems
    88  }
    89  
    90  func (items *items) peek() (interface{}, bool) {
    91  	if len(*items) == 0 {
    92  		return nil, false
    93  	}
    94  
    95  	return (*items)[0], true
    96  }
    97  
    98  func (items *items) getUntil(checker func(item interface{}) bool) []interface{} {
    99  	length := len(*items)
   100  
   101  	if len(*items) == 0 {
   102  		// returning nil here actually wraps that nil in a list
   103  		// of interfaces... thanks go
   104  		return []interface{}{}
   105  	}
   106  
   107  	returnItems := make([]interface{}, 0, length)
   108  	index := -1
   109  	for i, item := range *items {
   110  		if !checker(item) {
   111  			break
   112  		}
   113  
   114  		returnItems = append(returnItems, item)
   115  		index = i
   116  		(*items)[i] = nil // prevent memory leak
   117  	}
   118  
   119  	*items = (*items)[index+1:]
   120  	return returnItems
   121  }
   122  
   123  // sema is the struct responsible for tracking the state
   124  // of waiter. blocking poll if no data, notify if new data comes in.
   125  type sema struct {
   126  	ready    chan bool
   127  	response *sync.WaitGroup
   128  }
   129  
   130  func newSema() *sema {
   131  	return &sema{
   132  		ready:    make(chan bool, 1),
   133  		response: &sync.WaitGroup{},
   134  	}
   135  }
   136  
   137  // Queue is the struct responsible for tracking the state
   138  // of the queue.
   139  type Queue struct {
   140  	waiters  waiters
   141  	items    items
   142  	lock     sync.Mutex
   143  	disposed int32
   144  }
   145  
   146  // New is a constructor for a new threadsafe queue.
   147  func New(hint int64) *Queue {
   148  	return &Queue{
   149  		items: make([]interface{}, 0, hint),
   150  	}
   151  }
   152  
   153  // Put will add the specified items to the queue.
   154  func (q *Queue) Put(items ...interface{}) error {
   155  	if len(items) == 0 {
   156  		return nil
   157  	}
   158  
   159  	q.lock.Lock()
   160  	defer q.lock.Unlock()
   161  
   162  	if atomic.LoadInt32(&q.disposed) == 1 {
   163  		return ErrDisposed
   164  	}
   165  
   166  	q.items = append(q.items, items...)
   167  	for {
   168  		sema := q.waiters.get()
   169  		if sema == nil {
   170  			break
   171  		}
   172  		sema.response.Add(1)
   173  		select {
   174  		case sema.ready <- true:
   175  			sema.response.Wait()
   176  		default:
   177  			// This semaphore timed out.
   178  		}
   179  		if len(q.items) == 0 {
   180  			break
   181  		}
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  // Get retrieves items from the queue.  If there are some items in the
   188  // queue, get will return a number UP TO the number passed in as a
   189  // parameter.  If no items are in the queue, this method will pause
   190  // until items are added to the queue.
   191  func (q *Queue) Get(number int64) ([]interface{}, error) {
   192  	return q.Poll(number, 0)
   193  }
   194  
   195  // Poll retrieves items from the queue.  If there are some items in the queue,
   196  // Poll will return a number UP TO the number passed in as a parameter.  If no
   197  // items are in the queue, this method will pause until items are added to the
   198  // queue or the provided timeout is reached.  A non-positive timeout will block
   199  // until items are added.  If a timeout occurs, ErrTimeout is returned.
   200  func (q *Queue) Poll(number int64, timeout time.Duration) ([]interface{}, error) {
   201  	if number < 1 {
   202  		// thanks again go
   203  		return []interface{}{}, nil
   204  	}
   205  
   206  	q.lock.Lock()
   207  
   208  	if atomic.LoadInt32(&q.disposed) == 1 {
   209  		q.lock.Unlock()
   210  		return nil, ErrDisposed
   211  	}
   212  
   213  	var items []interface{}
   214  
   215  	if len(q.items) == 0 {
   216  		sema := newSema()
   217  		q.waiters.put(sema)
   218  		q.lock.Unlock()
   219  
   220  		var timeoutC <-chan time.Time
   221  		if timeout > 0 {
   222  			timer := time.NewTimer(timeout)
   223  			defer timer.Stop()
   224  
   225  			timeoutC = timer.C
   226  		}
   227  		select {
   228  		case <-sema.ready:
   229  			// we are now inside the put's lock
   230  			if atomic.LoadInt32(&q.disposed) == 1 {
   231  				return nil, ErrDisposed
   232  			}
   233  			items = q.items.get(number)
   234  			sema.response.Done()
   235  			return items, nil
   236  		case <-timeoutC:
   237  			// cleanup the sema that was added to waiters
   238  			select {
   239  			case sema.ready <- true:
   240  				// we called this before Put() could
   241  				// Remove sema from waiters.
   242  				q.lock.Lock()
   243  				q.waiters.remove(sema)
   244  				q.lock.Unlock()
   245  			default:
   246  				// Put() got it already, we need to call Done() so Put() can move on
   247  				sema.response.Done()
   248  			}
   249  			return nil, ErrTimeout
   250  		}
   251  	}
   252  
   253  	items = q.items.get(number)
   254  	q.lock.Unlock()
   255  	return items, nil
   256  }
   257  
   258  // Peek returns a the first item in the queue by value
   259  // without modifying the queue.
   260  func (q *Queue) Peek() (interface{}, error) {
   261  	q.lock.Lock()
   262  	defer q.lock.Unlock()
   263  
   264  	if atomic.LoadInt32(&q.disposed) == 1 {
   265  		return nil, ErrDisposed
   266  	}
   267  
   268  	peekItem, ok := q.items.peek()
   269  	if !ok {
   270  		return nil, ErrEmptyQueue
   271  	}
   272  
   273  	return peekItem, nil
   274  }
   275  
   276  // GetUntil gets a function and returns a list of items that
   277  // match the checker until the checker returns false.  This does not
   278  // wait if there are no items in the queue.
   279  func (q *Queue) GetUntil(checker func(item interface{}) bool) ([]interface{}, error) {
   280  	if checker == nil {
   281  		return nil, nil
   282  	}
   283  
   284  	q.lock.Lock()
   285  
   286  	if atomic.LoadInt32(&q.disposed) == 1 {
   287  		q.lock.Unlock()
   288  		return nil, ErrDisposed
   289  	}
   290  
   291  	result := q.items.getUntil(checker)
   292  	q.lock.Unlock()
   293  	return result, nil
   294  }
   295  
   296  // Empty returns a bool indicating if this bool is empty.
   297  func (q *Queue) Empty() bool {
   298  	q.lock.Lock()
   299  	defer q.lock.Unlock()
   300  
   301  	return len(q.items) == 0
   302  }
   303  
   304  // Len returns the number of items in this queue.
   305  func (q *Queue) Len() int64 {
   306  	q.lock.Lock()
   307  	defer q.lock.Unlock()
   308  
   309  	return int64(len(q.items))
   310  }
   311  
   312  // Disposed returns a bool indicating if this queue
   313  // has had disposed called on it.
   314  func (q *Queue) Disposed() bool {
   315  	q.lock.Lock()
   316  	defer q.lock.Unlock()
   317  
   318  	return atomic.LoadInt32(&q.disposed) == 1
   319  }
   320  
   321  // Dispose will dispose of this queue and returns
   322  // the items disposed. Any subsequent calls to Get
   323  // or Put will return an error.
   324  func (q *Queue) Dispose() []interface{} {
   325  	q.lock.Lock()
   326  	defer q.lock.Unlock()
   327  
   328  	atomic.StoreInt32(&q.disposed, 1)
   329  	for _, waiter := range q.waiters {
   330  		waiter.response.Add(1)
   331  		select {
   332  		case waiter.ready <- true:
   333  			// release Poll immediately
   334  		default:
   335  			// ignore if it's a timeout or in the get
   336  		}
   337  	}
   338  
   339  	disposedItems := q.items
   340  
   341  	q.items = nil
   342  	q.waiters = nil
   343  
   344  	return disposedItems
   345  }
   346  
   347  // ExecuteInParallel will (in parallel) call the provided function
   348  // with each item in the queue until the queue is exhausted.  When the queue
   349  // is exhausted execution is complete and all goroutines will be killed.
   350  // This means that the queue will be disposed so cannot be used again.
   351  func ExecuteInParallel(q *Queue, fn func(interface{})) {
   352  	if q == nil {
   353  		return
   354  	}
   355  
   356  	q.lock.Lock() // so no one touches anything in the middle
   357  	// of this process
   358  	length, count := uint64(len(q.items)), int64(-1)
   359  	// this is important or we might face an infinite loop
   360  	if length == 0 {
   361  		return
   362  	}
   363  
   364  	numCPU := 1
   365  	if runtime.NumCPU() > 1 {
   366  		numCPU = runtime.NumCPU() - 1
   367  	}
   368  
   369  	var wg sync.WaitGroup
   370  	wg.Add(numCPU)
   371  	items := q.items
   372  
   373  	for i := 0; i < numCPU; i++ {
   374  		go func() {
   375  			for {
   376  				index := atomic.AddInt64(&count, 1)
   377  				if index >= int64(length) {
   378  					wg.Done()
   379  					break
   380  				}
   381  
   382  				fn(items[index])
   383  				items[index] = 0
   384  			}
   385  		}()
   386  	}
   387  	wg.Wait()
   388  	q.lock.Unlock()
   389  	q.Dispose()
   390  }