@@ -9,17 +9,21 @@ import (
9
9
)
10
10
11
11
// A Unit (named to represent its resemblance to the systemd concept) is a kind of lock that encodes metadata
12
- // about the state of a resource. Units areprimarilymeant to be sections of processes such as coder scripts
12
+ // about the state of a resource. Units areprimarily meant to be sections of processes such as coder scripts
13
13
// that encapsulate a contended resource, such as a database lock or a socket.
14
14
//
15
- // In most cases, `coder_script` resources will create and manage units by invocation of `coder agent lock <unit>`.
16
- // Locks may be acquired with no intention of releasing them as a signal to other scripts that
15
+ // In most cases, `coder_script` resources will create and manage units by invocations of
16
+ // * `coder agent unit start <unit> [--wants <unit>]`
17
+ // * `coder agent unit complete <unit>`
18
+ // * `coder agent unit fail <unit>`
19
+ // Units may be acquired with no intention of releasing them as a signal to other scripts that
17
20
// a contended resource has been provided and is available. For example, a script that installs curl
18
- // might acquire alock called "curl-install" to signal to other scripts that curl has been installed
19
- // and is available. In this case, thelock will bereleased when the agent is stopped.
21
+ // might acquire aunit called "curl-install" to signal to other scripts that curl has been installed
22
+ // and is available. In this case, theunit will becompleted when the agent is stopped.
20
23
type Unit struct {
21
24
Name string
22
25
history []Event
26
+ Wants []* Unit
23
27
}
24
28
25
29
type Event struct {
@@ -134,16 +138,11 @@ func (s *memoryUnitCoordinator) acquireUnitInternal(unitName string, ttl *time.D
134
138
s .mu .Lock ()
135
139
defer s .mu .Unlock ()
136
140
137
- // Check iflock is already held and not expired
138
- if s .hasUnitHeld (unitName )&& ! s . isUnitExpired ( unitName ) {
141
+ // Check ifunit is already held
142
+ if s .hasUnitHeld (unitName ) {
139
143
return false
140
144
}
141
145
142
- // Clean up expired lock if it exists
143
- if s .hasUnitHeld (unitName )&& s .isUnitExpired (unitName ) {
144
- s .expireUnitInternal (unitName )
145
- }
146
-
147
146
if _ ,exists := s .units [unitName ];! exists {
148
147
s .units [unitName ]= Unit {
149
148
Name :unitName ,
@@ -210,11 +209,6 @@ func (s *memoryUnitCoordinator) IsUnitHeld(unitName string) bool {
210
209
return false
211
210
}
212
211
213
- // Check if unit has expired
214
- if s .isUnitExpired (unitName ) {
215
- return false
216
- }
217
-
218
212
return true
219
213
}
220
214
@@ -234,85 +228,13 @@ func (s *memoryUnitCoordinator) hasUnitHeld(unitName string) bool {
234
228
return lastEvent .Type == UnitEventTypeAcquired
235
229
}
236
230
237
- // isLockExpired checks if a lock has expired based on its TTL (must be called with lock held)
238
- func (s * memoryUnitCoordinator )isUnitExpired (unitName string )bool {
239
- unit ,exists := s .units [unitName ]
240
- if ! exists {
241
- return false
242
- }
243
-
244
- // No TTL means no expiration
245
- if unit .ttl == nil || unit .acquiredAt == nil {
246
- return false
247
- }
248
-
249
- // Check if TTL has passed
250
- return time .Since (* unit .acquiredAt )> * unit .ttl
251
- }
252
-
253
- // expireLockInternal marks a lock as expired (must be called with write lock held)
254
- func (s * memoryUnitCoordinator )expireUnitInternal (unitName string ) {
255
- unit ,exists := s .units [unitName ]
256
- if ! exists {
257
- return
258
- }
259
-
260
- // Cancel timer if it exists
261
- if unit .timer != nil {
262
- unit .timer .Stop ()
263
- unit .timer = nil
264
- }
265
-
266
- // Add expiration event to history
267
- unit .history = append (unit .history ,Event {
268
- Type :UnitEventTypeExpired ,
269
- Timestamp :time .Now (),
270
- })
271
- // Clear TTL and acquiredAt to prevent further expiration checks
272
- unit .ttl = nil
273
- unit .acquiredAt = nil
274
- s .units [unitName ]= unit
275
-
276
- // Notify listeners of expiration
277
- if listeners ,exists := s .listeners [unitName ];exists {
278
- for _ ,listener := range listeners {
279
- go listener (context .Background (),UnitEventTypeExpired )
280
- }
281
- }
282
- }
283
-
284
- // expireUnitWithTimer is called by the TTL timer to expire a unit
285
- func (s * memoryUnitCoordinator )expireUnitWithTimer (unitName string ) {
286
- if atomic .LoadInt32 (& s .closed )== 1 {
287
- return
288
- }
289
-
290
- s .mu .Lock ()
291
- defer s .mu .Unlock ()
292
-
293
- // Double-check the unit still exists and hasn't been released
294
- if ! s .hasUnitHeld (unitName ) {
295
- return
296
- }
297
-
298
- // Expire the unit
299
- s .expireUnitInternal (unitName )
300
- }
301
-
302
231
// Close shuts down the state coordinator
303
232
func (s * memoryUnitCoordinator )Close ()error {
304
233
atomic .StoreInt32 (& s .closed ,1 )
305
234
306
235
s .mu .Lock ()
307
236
defer s .mu .Unlock ()
308
237
309
- // Stop all TTL timers before clearing locks
310
- for _ ,unit := range s .units {
311
- if unit .timer != nil {
312
- unit .timer .Stop ()
313
- }
314
- }
315
-
316
238
// Clear all listeners and events
317
239
s .listeners = make (map [string ]map [uint64 ]Listener )
318
240
s .units = make (map [string ]Unit )