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  }