carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import "sync"
4
5// keyedLocks is a refcounted mutex per string key: storelock.go's shape with
6// the key built by the caller. Entries drop at zero references, so an app
7// locked once does not hold a mutex for the life of the process. The zero
8// value is ready, which keeps &Server{} literals in tests safe.
9//
10// IN-PROCESS ONLY, like storeWalkLocks: temporary static sites spec §2 assumes
11// one console per deployment bucket, which is how every deployment runs.
12type keyedLocks struct {
13 mu sync.Mutex
14 m map[string]*keyedLock
15}
16
17type keyedLock struct {
18 mu sync.Mutex
19 refs int
20}
21
22// lock blocks until key's mutex is held and returns the release:
23//
24// defer s.lockApp(acct.Sqid, app)()
25func (l *keyedLocks) lock(key string) func() {
26 l.mu.Lock()
27 if l.m == nil {
28 l.m = make(map[string]*keyedLock)
29 }
30 e := l.m[key]
31 if e == nil {
32 e = &keyedLock{}
33 l.m[key] = e
34 }
35 e.refs++
36 l.mu.Unlock()
37
38 e.mu.Lock()
39 return func() {
40 e.mu.Unlock()
41 l.mu.Lock()
42 e.refs--
43 if e.refs == 0 {
44 delete(l.m, key)
45 }
46 l.mu.Unlock()
47 }
48}
49
50// lockAccount serialises counting temporary apps against creating or
51// restoring one, which is what makes the cap exact (spec §3, §6).
52//
53// LOCK ORDER: account before app, never the other way round. Only
54// /new-temporary and restoreApp take this lock.
55func (s *Server) lockAccount(sqid string) func() { return s.acctLocks.lock(sqid) }
56
57// lockApp is the per-app mutex every lifecycle transition takes: keep, ttl,
58// restore, trash, the purge door and each step of the expiry pass (spec §5).
59func (s *Server) lockApp(sqid, app string) func() { return s.appLocks.lock(sqid + "/" + app) }
60