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  }