handles.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Copyright 2021 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found at https://github.com/golang/go/blob/master/LICENSE.
  4. package frankenphp
  5. import (
  6. "sync"
  7. "sync/atomic"
  8. )
  9. // handle is based on the CL here: https://go-review.googlesource.com/c/go/+/600875
  10. type handle uintptr
  11. // slot represents a slot in the handles slice for concurrent access.
  12. type slot struct {
  13. value any
  14. }
  15. var (
  16. handles []*atomic.Pointer[slot]
  17. releasedIdx sync.Pool
  18. nilSlot = &slot{}
  19. growLock sync.RWMutex
  20. handLen atomic.Uint64
  21. )
  22. func init() {
  23. handles = make([]*atomic.Pointer[slot], 0)
  24. handLen = atomic.Uint64{}
  25. releasedIdx.New = func() interface{} {
  26. return nil
  27. }
  28. growLock = sync.RWMutex{}
  29. }
  30. func newHandle(v any) handle {
  31. var h uint64 = 1
  32. s := &slot{value: v}
  33. for {
  34. if released := releasedIdx.Get(); released != nil {
  35. h = released.(uint64)
  36. }
  37. if h > handLen.Load() {
  38. growLock.Lock()
  39. handles = append(handles, &atomic.Pointer[slot]{})
  40. handLen.Store(uint64(len(handles)))
  41. growLock.Unlock()
  42. }
  43. growLock.RLock()
  44. if handles[h-1].CompareAndSwap(nilSlot, s) {
  45. growLock.RUnlock()
  46. return handle(h)
  47. } else if handles[h-1].CompareAndSwap(nil, s) {
  48. growLock.RUnlock()
  49. return handle(h)
  50. } else {
  51. h++
  52. }
  53. growLock.RUnlock()
  54. }
  55. }
  56. func (h handle) Value() any {
  57. growLock.RLock()
  58. defer growLock.RUnlock()
  59. if h > handle(len(handles)) {
  60. panic("runtime/cgo: misuse of an invalid handle")
  61. }
  62. v := handles[h-1].Load()
  63. if v == nil || v == nilSlot {
  64. panic("runtime/cgo: misuse of an released handle")
  65. }
  66. return v.value
  67. }
  68. func (h handle) Delete() {
  69. growLock.RLock()
  70. defer growLock.RUnlock()
  71. if h == 0 {
  72. panic("runtime/cgo: misuse of an zero handle")
  73. }
  74. if h > handle(len(handles)) {
  75. panic("runtime/cgo: misuse of an invalid handle")
  76. }
  77. if v := handles[h-1].Swap(nilSlot); v == nil || v == nilSlot {
  78. panic("runtime/cgo: misuse of an released handle")
  79. }
  80. //nolint:staticcheck
  81. releasedIdx.Put(uint64(h))
  82. }