github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/actor/process_registry.go (about) 1 package actor 2 3 import ( 4 "sync/atomic" 5 6 murmur32 "github.com/twmb/murmur3" 7 8 cmap "github.com/orcaman/concurrent-map" 9 ) 10 11 type ProcessRegistryValue struct { 12 SequenceID uint64 13 ActorSystem *ActorSystem 14 Address string 15 LocalPIDs *SliceMap 16 RemoteHandlers []AddressResolver 17 } 18 19 type SliceMap struct { 20 LocalPIDs []cmap.ConcurrentMap 21 } 22 23 func newSliceMap() *SliceMap { 24 sm := &SliceMap{} 25 sm.LocalPIDs = make([]cmap.ConcurrentMap, 1024) 26 27 for i := 0; i < len(sm.LocalPIDs); i++ { 28 sm.LocalPIDs[i] = cmap.New() 29 } 30 31 return sm 32 } 33 34 func (s *SliceMap) GetBucket(key string) cmap.ConcurrentMap { 35 hash := murmur32.Sum32([]byte(key)) 36 index := int(hash) % len(s.LocalPIDs) 37 38 return s.LocalPIDs[index] 39 } 40 41 const ( 42 localAddress = "nonhost" 43 ) 44 45 func NewProcessRegistry(actorSystem *ActorSystem) *ProcessRegistryValue { 46 return &ProcessRegistryValue{ 47 ActorSystem: actorSystem, 48 Address: localAddress, 49 LocalPIDs: newSliceMap(), 50 } 51 } 52 53 // An AddressResolver is used to resolve remote actors 54 type AddressResolver func(*PID) (Process, bool) 55 56 func (pr *ProcessRegistryValue) RegisterAddressResolver(handler AddressResolver) { 57 pr.RemoteHandlers = append(pr.RemoteHandlers, handler) 58 } 59 60 const ( 61 digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~+" 62 ) 63 64 func uint64ToId(u uint64) string { 65 var buf [13]byte 66 i := 13 67 // base is power of 2: use shifts and masks instead of / and % 68 for u >= 64 { 69 i-- 70 buf[i] = digits[uintptr(u)&0x3f] 71 u >>= 6 72 } 73 // u < base 74 i-- 75 buf[i] = digits[uintptr(u)] 76 i-- 77 buf[i] = '$' 78 79 return string(buf[i:]) 80 } 81 82 func (pr *ProcessRegistryValue) NextId() string { 83 counter := atomic.AddUint64(&pr.SequenceID, 1) 84 85 return uint64ToId(counter) 86 } 87 88 func (pr *ProcessRegistryValue) Add(process Process, id string) (*PID, bool) { 89 bucket := pr.LocalPIDs.GetBucket(id) 90 91 return &PID{ 92 Address: pr.Address, 93 Id: id, 94 }, bucket.SetIfAbsent(id, process) 95 } 96 97 func (pr *ProcessRegistryValue) Remove(pid *PID) { 98 bucket := pr.LocalPIDs.GetBucket(pid.Id) 99 100 ref, _ := bucket.Pop(pid.Id) 101 if l, ok := ref.(*ActorProcess); ok { 102 atomic.StoreInt32(&l.dead, 1) 103 } 104 } 105 106 func (pr *ProcessRegistryValue) Get(pid *PID) (Process, bool) { 107 if pid == nil { 108 return pr.ActorSystem.DeadLetter, false 109 } 110 111 if pid.Address != localAddress && pid.Address != pr.Address { 112 for _, handler := range pr.RemoteHandlers { 113 ref, ok := handler(pid) 114 if ok { 115 return ref, true 116 } 117 } 118 119 return pr.ActorSystem.DeadLetter, false 120 } 121 122 bucket := pr.LocalPIDs.GetBucket(pid.Id) 123 ref, ok := bucket.Get(pid.Id) 124 125 if !ok { 126 return pr.ActorSystem.DeadLetter, false 127 } 128 129 return ref.(Process), true 130 } 131 132 func (pr *ProcessRegistryValue) GetLocal(id string) (Process, bool) { 133 bucket := pr.LocalPIDs.GetBucket(id) 134 ref, ok := bucket.Get(id) 135 136 if !ok { 137 return pr.ActorSystem.DeadLetter, false 138 } 139 140 return ref.(Process), true 141 }