github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/actor/root_context.go (about) 1 package actor 2 3 import ( 4 "log/slog" 5 "time" 6 ) 7 8 type RootContext struct { 9 actorSystem *ActorSystem 10 senderMiddleware SenderFunc 11 spawnMiddleware SpawnFunc 12 headers messageHeader 13 guardianStrategy SupervisorStrategy 14 } 15 16 var ( 17 _ SenderContext = &RootContext{} 18 _ SpawnerContext = &RootContext{} 19 _ stopperPart = &RootContext{} 20 ) 21 22 func NewRootContext(actorSystem *ActorSystem, header map[string]string, middleware ...SenderMiddleware) *RootContext { 23 if header == nil { 24 header = make(map[string]string) 25 } 26 27 return &RootContext{ 28 actorSystem: actorSystem, 29 senderMiddleware: makeSenderMiddlewareChain(middleware, func(_ SenderContext, target *PID, envelope *MessageEnvelope) { 30 target.sendUserMessage(actorSystem, envelope) 31 }), 32 headers: header, 33 } 34 } 35 36 func (rc RootContext) Copy() *RootContext { 37 return &rc 38 } 39 40 func (rc *RootContext) ActorSystem() *ActorSystem { 41 return rc.actorSystem 42 } 43 44 func (rc *RootContext) Logger() *slog.Logger { 45 return rc.actorSystem.Logger() 46 } 47 48 func (rc *RootContext) WithHeaders(headers map[string]string) *RootContext { 49 rc.headers = headers 50 51 return rc 52 } 53 54 func (rc *RootContext) WithSenderMiddleware(middleware ...SenderMiddleware) *RootContext { 55 rc.senderMiddleware = makeSenderMiddlewareChain(middleware, func(_ SenderContext, target *PID, envelope *MessageEnvelope) { 56 target.sendUserMessage(rc.actorSystem, envelope) 57 }) 58 59 return rc 60 } 61 62 func (rc *RootContext) WithSpawnMiddleware(middleware ...SpawnMiddleware) *RootContext { 63 rc.spawnMiddleware = makeSpawnMiddlewareChain(middleware, func(actorSystem *ActorSystem, id string, props *Props, parentContext SpawnerContext) (pid *PID, e error) { 64 return props.spawn(actorSystem, id, rc) 65 }) 66 67 return rc 68 } 69 70 func (rc *RootContext) WithGuardian(guardian SupervisorStrategy) *RootContext { 71 rc.guardianStrategy = guardian 72 73 return rc 74 } 75 76 // 77 // Interface: info 78 // 79 80 func (rc *RootContext) Parent() *PID { 81 return nil 82 } 83 84 func (rc *RootContext) Self() *PID { 85 if rc.guardianStrategy != nil { 86 return rc.actorSystem.Guardians.getGuardianPid(rc.guardianStrategy) 87 } 88 89 return nil 90 } 91 92 func (rc *RootContext) Sender() *PID { 93 return nil 94 } 95 96 func (rc *RootContext) Actor() Actor { 97 return nil 98 } 99 100 // 101 // Interface: sender 102 // 103 104 func (rc *RootContext) Message() interface{} { 105 return nil 106 } 107 108 func (rc *RootContext) MessageHeader() ReadonlyMessageHeader { 109 return rc.headers 110 } 111 112 func (rc *RootContext) Send(pid *PID, message interface{}) { 113 rc.sendUserMessage(pid, message) 114 } 115 116 func (rc *RootContext) Request(pid *PID, message interface{}) { 117 rc.sendUserMessage(pid, message) 118 } 119 120 func (rc *RootContext) RequestWithCustomSender(pid *PID, message interface{}, sender *PID) { 121 env := &MessageEnvelope{ 122 Header: nil, 123 Message: message, 124 Sender: sender, 125 } 126 rc.sendUserMessage(pid, env) 127 } 128 129 // RequestFuture sends a message to a given PID and returns a Future. 130 func (rc *RootContext) RequestFuture(pid *PID, message interface{}, timeout time.Duration) *Future { 131 future := NewFuture(rc.actorSystem, timeout) 132 env := &MessageEnvelope{ 133 Header: nil, 134 Message: message, 135 Sender: future.PID(), 136 } 137 rc.sendUserMessage(pid, env) 138 139 return future 140 } 141 142 func (rc *RootContext) sendUserMessage(pid *PID, message interface{}) { 143 if rc.senderMiddleware != nil { 144 // Request based middleware 145 rc.senderMiddleware(rc, pid, WrapEnvelope(message)) 146 } else { 147 // tell based middleware 148 pid.sendUserMessage(rc.actorSystem, message) 149 } 150 } 151 152 // 153 // Interface: spawner 154 // 155 156 // Spawn starts a new actor based on props and named with a unique id. 157 func (rc *RootContext) Spawn(props *Props) *PID { 158 pid, err := rc.SpawnNamed(props, rc.actorSystem.ProcessRegistry.NextId()) 159 if err != nil { 160 panic(err) 161 } 162 163 return pid 164 } 165 166 // SpawnPrefix starts a new actor based on props and named using a prefix followed by a unique id. 167 func (rc *RootContext) SpawnPrefix(props *Props, prefix string) *PID { 168 pid, err := rc.SpawnNamed(props, prefix+rc.actorSystem.ProcessRegistry.NextId()) 169 if err != nil { 170 panic(err) 171 } 172 173 return pid 174 } 175 176 // SpawnNamed starts a new actor based on props and named using the specified name 177 // 178 // # ErrNameExists will be returned if id already exists 179 // 180 // Please do not use name sharing same pattern with system actors, for example "YourPrefix$1", "Remote$1", "future$1". 181 func (rc *RootContext) SpawnNamed(props *Props, name string) (*PID, error) { 182 rootContext := rc 183 if props.guardianStrategy != nil { 184 rootContext = rc.Copy().WithGuardian(props.guardianStrategy) 185 } 186 187 if rootContext.spawnMiddleware != nil { 188 return rc.spawnMiddleware(rc.actorSystem, name, props, rootContext) 189 } 190 191 return props.spawn(rc.actorSystem, name, rootContext) 192 } 193 194 // 195 // Interface: StopperContext 196 // 197 198 // Stop will stop actor immediately regardless of existing user messages in mailbox. 199 func (rc *RootContext) Stop(pid *PID) { 200 pid.ref(rc.actorSystem).Stop(pid) 201 } 202 203 // StopFuture will stop actor immediately regardless of existing user messages in mailbox, and return its future. 204 func (rc *RootContext) StopFuture(pid *PID) *Future { 205 future := NewFuture(rc.actorSystem, 10*time.Second) 206 207 pid.sendSystemMessage(rc.actorSystem, &Watch{Watcher: future.pid}) 208 rc.Stop(pid) 209 210 return future 211 } 212 213 // Poison will tell actor to stop after processing current user messages in mailbox. 214 func (rc *RootContext) Poison(pid *PID) { 215 pid.sendUserMessage(rc.actorSystem, poisonPillMessage) 216 } 217 218 // PoisonFuture will tell actor to stop after processing current user messages in mailbox, and return its future. 219 func (rc *RootContext) PoisonFuture(pid *PID) *Future { 220 future := NewFuture(rc.actorSystem, 10*time.Second) 221 222 pid.sendSystemMessage(rc.actorSystem, &Watch{Watcher: future.pid}) 223 rc.Poison(pid) 224 225 return future 226 }