github.com/richardwilkes/toolbox@v1.121.0/rate/limiter.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  // Package rate provides rate limiting which supports a hierarchy of limiters,
    11  // each capped by their parent.
    12  package rate
    13  
    14  import (
    15  	"sync"
    16  	"time"
    17  
    18  	"github.com/richardwilkes/toolbox/errs"
    19  )
    20  
    21  type limiter struct {
    22  	controller *controller
    23  	parent     *limiter
    24  	children   []*limiter
    25  	last       int
    26  	capacity   int
    27  	used       int
    28  	closed     bool
    29  }
    30  
    31  type controller struct {
    32  	root    *limiter
    33  	ticker  *time.Ticker
    34  	done    chan bool
    35  	waiting []*request
    36  	lock    sync.RWMutex
    37  }
    38  
    39  type request struct {
    40  	limiter *limiter
    41  	done    chan error
    42  	amount  int
    43  }
    44  
    45  // New creates a new top-level rate limiter. 'capacity' is the number of units (bytes, for example) allowed to be used
    46  // in a particular time 'period'.
    47  func New(capacity int, period time.Duration) Limiter {
    48  	c := &controller{
    49  		ticker: time.NewTicker(period),
    50  		done:   make(chan bool),
    51  	}
    52  	l := &limiter{
    53  		controller: c,
    54  		capacity:   capacity,
    55  	}
    56  	c.root = l
    57  	go func() {
    58  		for {
    59  			select {
    60  			case <-c.ticker.C:
    61  				c.lock.Lock()
    62  				c.root.reset()
    63  				remaining := make([]*request, 0, len(c.waiting))
    64  				for _, req := range c.waiting {
    65  					if req.limiter.closed {
    66  						req.done <- errs.New("Limiter is closed")
    67  						continue
    68  					}
    69  					if req.amount > req.limiter.capacity {
    70  						req.done <- errs.Newf("Amount (%d) is greater than capacity (%d)", req.amount, req.limiter.capacity)
    71  						continue
    72  					}
    73  					if c.root.capacity-c.root.used > 0 {
    74  						available := req.limiter.capacity - req.limiter.used
    75  						p := req.limiter.parent
    76  						for p != nil {
    77  							pa := p.capacity - p.used
    78  							if pa < available {
    79  								available = pa
    80  							}
    81  							p = p.parent
    82  						}
    83  						if available >= req.amount {
    84  							req.limiter.used += req.amount
    85  							p = req.limiter.parent
    86  							for p != nil {
    87  								p.used += req.amount
    88  								p = p.parent
    89  							}
    90  							req.done <- nil
    91  							continue
    92  						}
    93  					}
    94  					remaining = append(remaining, req)
    95  				}
    96  				c.waiting = remaining
    97  				c.lock.Unlock()
    98  			case <-c.done:
    99  				c.ticker.Stop()
   100  				c.lock.Lock()
   101  				for _, req := range c.waiting {
   102  					req.done <- errs.New("Limiter is closed")
   103  				}
   104  				c.waiting = make([]*request, 0)
   105  				c.lock.Unlock()
   106  				return
   107  			}
   108  		}
   109  	}()
   110  	return l
   111  }
   112  
   113  func (l *limiter) New(capacity int) Limiter {
   114  	l.controller.lock.Lock()
   115  	defer l.controller.lock.Unlock()
   116  	if l.closed {
   117  		return nil
   118  	}
   119  	child := &limiter{
   120  		controller: l.controller,
   121  		parent:     l,
   122  		capacity:   capacity,
   123  	}
   124  	l.children = append(l.children, child)
   125  	return child
   126  }
   127  
   128  func (l *limiter) Cap(applyParentCaps bool) int {
   129  	l.controller.lock.RLock()
   130  	defer l.controller.lock.RUnlock()
   131  	capacity := l.capacity
   132  	if applyParentCaps {
   133  		p := l.parent
   134  		for p != nil {
   135  			if p.capacity < capacity {
   136  				capacity = p.capacity
   137  			}
   138  			p = p.parent
   139  		}
   140  	}
   141  	return capacity
   142  }
   143  
   144  func (l *limiter) SetCap(capacity int) {
   145  	l.controller.lock.Lock()
   146  	l.capacity = capacity
   147  	l.controller.lock.Unlock()
   148  }
   149  
   150  func (l *limiter) LastUsed() int {
   151  	l.controller.lock.RLock()
   152  	defer l.controller.lock.RUnlock()
   153  	return l.last
   154  }
   155  
   156  func (l *limiter) Use(amount int) <-chan error {
   157  	done := make(chan error, 1)
   158  	if amount < 0 {
   159  		done <- errs.Newf("Amount (%d) must be positive", amount)
   160  		return done
   161  	}
   162  	if amount == 0 {
   163  		done <- nil
   164  		return done
   165  	}
   166  	l.controller.lock.Lock()
   167  	if l.closed {
   168  		l.controller.lock.Unlock()
   169  		done <- errs.New("Limiter is closed")
   170  		return done
   171  	}
   172  	if amount > l.capacity {
   173  		capacity := l.capacity
   174  		l.controller.lock.Unlock()
   175  		done <- errs.Newf("Amount (%d) is greater than capacity (%d)", amount, capacity)
   176  		return done
   177  	}
   178  	available := l.capacity - l.used
   179  	p := l.parent
   180  	for p != nil {
   181  		pa := p.capacity - p.used
   182  		if pa < available {
   183  			available = pa
   184  		}
   185  		p = p.parent
   186  	}
   187  	if available >= amount {
   188  		l.used += amount
   189  		p = l.parent
   190  		for p != nil {
   191  			p.used += amount
   192  			p = p.parent
   193  		}
   194  		l.controller.lock.Unlock()
   195  		done <- nil
   196  		return done
   197  	}
   198  	l.controller.waiting = append(l.controller.waiting, &request{
   199  		limiter: l,
   200  		amount:  amount,
   201  		done:    done,
   202  	})
   203  	l.controller.lock.Unlock()
   204  	return done
   205  }
   206  
   207  func (l *limiter) reset() {
   208  	l.last = l.used
   209  	l.used = 0
   210  	for _, child := range l.children {
   211  		child.reset()
   212  	}
   213  }
   214  
   215  func (l *limiter) Closed() bool {
   216  	l.controller.lock.RLock()
   217  	defer l.controller.lock.RUnlock()
   218  	return l.closed
   219  }
   220  
   221  func (l *limiter) Close() {
   222  	l.controller.lock.Lock()
   223  	if !l.closed {
   224  		l.close()
   225  		if l.parent == nil {
   226  			l.controller.done <- true
   227  		} else {
   228  			for i, child := range l.parent.children {
   229  				if child != l {
   230  					continue
   231  				}
   232  				j := len(l.parent.children) - 1
   233  				l.parent.children[i] = l.parent.children[j]
   234  				l.parent.children[j] = nil
   235  				l.parent.children = l.parent.children[:j]
   236  				break
   237  			}
   238  			l.closed = true
   239  		}
   240  	}
   241  	l.controller.lock.Unlock()
   242  }
   243  
   244  func (l *limiter) close() {
   245  	l.closed = true
   246  	for _, child := range l.children {
   247  		child.close()
   248  	}
   249  }