1package daemon
2
3// resumeCredentials owns every resume credential that is not held by a live
4// attachment: attachments between detach and park publication (parking), and
5// parked attachments waiting for a reconnect (parked). A token is unique
6// across both, and every lookup, publication, and retirement of either goes
7// through this type, so the credential lifecycle has one owner.
8//
9// resumeCredentials is guarded by Daemon.mu; every method requires it held.
10// It never takes another lock and never stops timers or closes transports
11// beyond closing its own done channels: external retirement stays with the
12// caller, after d.mu is released.
13type resumeCredentials struct {
14 parked map[uint64]*parkedAttachment
15 parking map[uint64]*parkingAttachment
16}
17
18func newResumeCredentials() resumeCredentials {
19 return resumeCredentials{
20 parked: make(map[uint64]*parkedAttachment),
21 parking: make(map[uint64]*parkingAttachment),
22 }
23}
24
25// issue returns a fresh nonzero token no parked or parking entry holds.
26func (c *resumeCredentials) issue() uint64 {
27 for {
28 token := newResumeToken()
29 if token == 0 {
30 continue
31 }
32 if c.parked[token] == nil && c.parking[token] == nil {
33 return token
34 }
35 }
36}
37
38// parkedFor returns the parked entry holding token, or nil.
39func (c *resumeCredentials) parkedFor(token uint64) *parkedAttachment { return c.parked[token] }
40
41// parkingFor returns the in-flight parking entry holding token, or nil.
42func (c *resumeCredentials) parkingFor(token uint64) *parkingAttachment { return c.parking[token] }
43
44// sessionFor returns the session a parked or parking token belongs to.
45func (c *resumeCredentials) sessionFor(token uint64) *session {
46 if parked := c.parked[token]; parked != nil {
47 return parked.sess
48 }
49 if pending := c.parking[token]; pending != nil {
50 return pending.sess
51 }
52 return nil
53}
54
55// retainsSession reports whether any parked or parking credential keeps sess.
56func (c *resumeCredentials) retainsSession(sess *session) bool {
57 for _, parked := range c.parked {
58 if parked.sess == sess {
59 return true
60 }
61 }
62 for _, pending := range c.parking {
63 if pending.sess == sess {
64 return true
65 }
66 }
67 return false
68}
69
70// markParking records or refreshes the in-flight marker for ac's token.
71func (c *resumeCredentials) markParking(sess *session, ac *attachedClient) {
72 token := ac.resumeToken
73 if token == 0 {
74 return
75 }
76 if existing := c.parking[token]; existing != nil {
77 if existing.ac == ac {
78 existing.sess = sess
79 return
80 }
81 delete(c.parking, token)
82 existing.closeDone()
83 }
84 c.parking[token] = &parkingAttachment{sess: sess, ac: ac, done: make(chan struct{})}
85}
86
87// clearParking removes token's marker, waking its waiters. A non-nil ac only
88// clears its own marker.
89func (c *resumeCredentials) clearParking(token uint64, ac *attachedClient) {
90 pending := c.parking[token]
91 if pending == nil || (ac != nil && pending.ac != ac) {
92 return
93 }
94 delete(c.parking, token)
95 pending.closeDone()
96}
97
98// purgeParking removes every marker for sess, or every marker when sess is
99// nil, so same-token waiters fail closed instead of hanging.
100func (c *resumeCredentials) purgeParking(sess *session) {
101 for token, pending := range c.parking {
102 if sess == nil || pending.sess == sess {
103 delete(c.parking, token)
104 pending.closeDone()
105 }
106 }
107}
108
109// publishParked stores parked under token, replacing any previous entry, and
110// clears the matching in-flight marker.
111func (c *resumeCredentials) publishParked(token uint64, parked *parkedAttachment) {
112 c.parked[token] = parked
113 c.clearParking(token, parked.ac)
114}
115
116// takeParked removes token's parked entry when it still holds parked. It
117// reports false when another path already replaced or removed it.
118func (c *resumeCredentials) takeParked(token uint64, parked *parkedAttachment) bool {
119 if parked == nil || c.parked[token] != parked {
120 return false
121 }
122 delete(c.parked, token)
123 return true
124}
125
126// parkedForSession returns every parked entry for sess, or every parked entry
127// when sess is nil, keyed by token.
128func (c *resumeCredentials) parkedForSession(sess *session) map[uint64]*parkedAttachment {
129 matches := make(map[uint64]*parkedAttachment)
130 for token, parked := range c.parked {
131 if sess == nil || parked.sess == sess {
132 matches[token] = parked
133 }
134 }
135 return matches
136}