Download this file
| 1 | package console |
| 2 | |
| 3 | import "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. |
| 12 | type keyedLocks struct { |
| 13 | mu sync.Mutex |
| 14 | m map[string]*keyedLock |
| 15 | } |
| 16 | |
| 17 | type 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)() |
| 25 | func (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. |
| 55 | func (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). |
| 59 | func (s *Server) lockApp(sqid, app string) func() { return s.appLocks.lock(sqid + "/" + app) } |
| 60 | |