Repository navigation
Expand file tree
/
Copy pathcontext.go
More file actions
124 lines (104 loc) · 4.71 KB
/
Copy pathcontext.go
File metadata and controls
124 lines (104 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2026 Hamed Yousefi
// SPDX-License-Identifier: MPL-2.0
package lockstep
import (
"io"
"math/rand/v2"
"sync/atomic"
"github.com/hmdsefi/lockstep/internal/engine"
)
// Context is an incarnation's handle to the runtime. The runtime passes the same *Context to
// every turn of one incarnation and a new one to the next incarnation. Self and Parent may be
// called at any time; every other method panics when called outside a turn of its incarnation.
// Call its methods only from the goroutine that runs the handler. Every method panics on a nil
// *Context.
type Context[M any] struct {
cell *cell[M]
inc uint32 // the incarnation number (SUP-020)
inTurn atomic.Bool // set for the duration of each handler call of this incarnation (ACT-120)
// failure is the first Fail argument of the handler call that runs, nil if none (SUP-140).
// Turn-owned: callHandler clears it before each handler call and reads it after.
failure error
// watched is the incarnation's watched set (ACT-600). Turn-owned: only Watch, Unwatch and
// signal delivery, all in a turn of this incarnation, touch it; so does the stop sequence,
// which clears it in the turn that starts the stop (SUP-060 step 1).
watched watchedSet
// rebuild is set while the incarnation's Started hook runs in a rebuild context (OVL §3):
// spawns from it are reserve spawns (OVL-030). Turn-owned: begin sets it around the Started
// call, and Spawn reads it during that call.
rebuild bool
// overloadSub is set while the incarnation is subscribed to Overloaded (OVL-170). Turn-owned:
// SubscribeOverload and UnsubscribeOverload set it, signal delivery reads it, and the stop
// sequence clears it in the turn that starts the end of the incarnation.
overloadSub bool
// overloadGen counts the incarnation's subscriptions, so that a signal queued for one is not
// delivered to a later one. Turn-owned.
overloadGen uint64
// owned is the incarnation's resource list in registration order (SUP-120). Turn-owned:
// Own appends in a turn of this incarnation, and the stop sequence closes the list after
// Stopped, in the turn that completes the stop. The next incarnation has a Context and so a
// list of its own.
owned []io.Closer
}
// Self returns the actor's PID. It is the same for every incarnation of the actor.
func (c *Context[M]) Self() PID[M] {
c.mustNotNil()
return PID[M]{addr: c.cell.addr}
}
// Parent returns the parent's address: the user guardian's for actors spawned from a System.
func (c *Context[M]) Parent() Address {
c.mustNotNil()
return c.cell.parent
}
// mustNotNil panics on a nil *Context (ACT §7). Self and Parent, which work outside a turn, call it
// first; every other method gets it from mustInTurn.
func (c *Context[M]) mustNotNil() {
if c == nil {
panic("lockstep: nil Context")
}
}
// mustInTurn panics on a nil *Context (ACT §7) and unless a handler of c's incarnation is running
// (ACT-120). Every Context method but Self and Parent, and every function given c as Spawner,
// Sender or ctx, calls it first.
func (c *Context[M]) mustInTurn() {
c.mustNotNil()
if !c.inTurn.Load() {
panic("lockstep: Context of " + c.cell.addr.String() + " used outside its turn")
}
}
// Rand returns the random source of the current incarnation. Every call in one incarnation
// returns the same *rand.Rand; a restart gets a new one. Use it only inside the actor's handlers.
func (c *Context[M]) Rand() *rand.Rand {
c.mustInTurn()
return c.cell.random()
}
func (*Context[M]) spawner() {}
func (*Context[M]) sender() {}
// nilSpawner reports whether c is a nil *Context (ACT-200).
func (c *Context[M]) nilSpawner() bool { return c == nil }
// spawnParent returns the system, the actor as the parent's address, and the actor's cell, the
// turn that spawns. It panics outside a turn (ACT-300 step 2).
func (c *Context[M]) spawnParent() (*System, Address, engine.ActorRef) {
c.mustInTurn()
return c.cell.sys, c.cell.addr, c.cell
}
// parentStopping reports whether the actor is Stopping, Restarting or Stopped (SUP-090). Only
// Stopped runs in a turn then.
func (c *Context[M]) parentStopping() bool { return c.cell.ending() }
// rebuilding reports whether the incarnation's Started hook is running in a rebuild context
// (OVL §3).
func (c *Context[M]) rebuilding() bool { return c.rebuild }
// CancelEffect cancels the pending effect id of this incarnation and reports whether it was
// pending; done is then never called for it. It returns false for 0, for an ID of another
// incarnation, and for an effect that already has an outcome.
func (c *Context[M]) CancelEffect(id EffectID) bool {
c.mustInTurn()
cl := c.cell
cl.emu.Lock()
pe := cl.effects[id]
cl.emu.Unlock()
if pe == nil || pe.inc != c.inc {
return false
}
return cl.cancelEffect(pe)
}