limiter.go 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. package limiter
  2. import "sync"
  3. type Key = interface{}
  4. // Manages resources with a limited number of concurrent slots for use for each key.
  5. type Instance struct {
  6. SlotsPerKey int
  7. mu sync.Mutex
  8. // Limits concurrent use of a resource. Push into the channel to use a slot, and receive to free
  9. // up a slot.
  10. active map[Key]*activeValueType
  11. }
  12. type activeValueType struct {
  13. ch chan struct{}
  14. refs int
  15. }
  16. type ActiveValueRef struct {
  17. v *activeValueType
  18. k Key
  19. i *Instance
  20. }
  21. // Returns the limiting channel. Send to it to obtain a slot, and receive to release the slot.
  22. func (me ActiveValueRef) C() chan struct{} {
  23. return me.v.ch
  24. }
  25. // Drop the reference to a key, this allows keys to be reclaimed when they're no longer in use.
  26. func (me ActiveValueRef) Drop() {
  27. me.i.mu.Lock()
  28. defer me.i.mu.Unlock()
  29. me.v.refs--
  30. if me.v.refs == 0 {
  31. delete(me.i.active, me.k)
  32. }
  33. }
  34. // Get a reference to the values for a key. You should make sure to call Drop exactly once on the
  35. // returned value when done.
  36. func (i *Instance) GetRef(key Key) ActiveValueRef {
  37. i.mu.Lock()
  38. defer i.mu.Unlock()
  39. if i.active == nil {
  40. i.active = make(map[Key]*activeValueType)
  41. }
  42. v, ok := i.active[key]
  43. if !ok {
  44. v = &activeValueType{
  45. ch: make(chan struct{}, i.SlotsPerKey),
  46. }
  47. i.active[key] = v
  48. }
  49. v.refs++
  50. return ActiveValueRef{
  51. v: v,
  52. k: key,
  53. i: i,
  54. }
  55. }