]> git.lizzy.rs Git - go-anidb.git/blob - intent.go
anidb: Try to get (*AniDB).User() from the cache if it's unset
[go-anidb.git] / intent.go
1 package anidb
2
3 import (
4         "github.com/Kovensky/go-fscache"
5         "strings"
6         "sync"
7 )
8
9 type notification interface{}
10
11 type intentStruct struct {
12         sync.Mutex
13         chs []chan notification
14 }
15
16 type intentMapStruct struct {
17         sync.Mutex
18         intentLock sync.Mutex // used by the Intent function
19
20         m map[string]*intentStruct
21 }
22
23 var intentMap = &intentMapStruct{
24         m: map[string]*intentStruct{},
25 }
26
27 func intentKey(key ...fscache.CacheKey) string {
28         return strings.Join(fscache.Stringify(key...), "-")
29 }
30
31 // Register a channel to be notified when the specified keys are notified.
32 // Returns whether the caller was the first to register intent for the given
33 // keys.
34 //
35 // Cache checks should be done after registering intent, since it's possible to
36 // register Intent while a Notify is running, and the Notify is done after
37 // setting the cache.
38 func (m *intentMapStruct) Intent(ch chan notification, keys ...fscache.CacheKey) bool {
39         key := intentKey(keys...)
40
41         m.intentLock.Lock()
42         defer m.intentLock.Unlock()
43
44         m.Lock()
45         s, ok := m.m[key]
46         if !ok {
47                 s = &intentStruct{}
48         }
49         m.Unlock()
50
51         s.Lock()
52         s.chs = append(s.chs, ch)
53         s.Unlock()
54
55         m.Lock()
56         // key might have been deleted while only the struct itself was locked -- recheck
57         _, ok = m.m[key]
58         m.m[key] = s
59         m.Unlock()
60
61         return ok
62 }
63
64 // Locks the requested keys and return the locked intentStruct.
65 //
66 // The intentStruct can be directly unlocked, or given to Free to also
67 // remove it from the intent map.
68 func (m *intentMapStruct) LockIntent(keys ...fscache.CacheKey) *intentStruct {
69         m.Lock()
70         defer m.Unlock()
71
72         return m._lockIntent(keys...)
73 }
74
75 func (m *intentMapStruct) _lockIntent(keys ...fscache.CacheKey) *intentStruct {
76         s, ok := m.m[intentKey(keys...)]
77         if !ok {
78                 return nil
79         }
80
81         s.Lock()
82         return s
83 }
84
85 // Removes the given intent from the intent map and unlocks the intentStruct.
86 func (m *intentMapStruct) Free(is *intentStruct, keys ...fscache.CacheKey) {
87         m.Lock()
88         defer m.Unlock()
89
90         m._free(is, keys...)
91 }
92
93 func (m *intentMapStruct) _free(is *intentStruct, keys ...fscache.CacheKey) {
94         // deletes the key before unlocking, Intent needs to recheck key status
95         delete(m.m, intentKey(keys...))
96         // better than unlocking then deleting -- could delete a "brand new" entry
97         is.Unlock()
98 }
99
100 // Notifies and closes all channels that are listening for the specified keys;
101 // also removes them from the intent map.
102 //
103 // Should be called after setting the cache.
104 func (m *intentMapStruct) NotifyClose(v notification, keys ...fscache.CacheKey) {
105         m.Lock()
106         defer m.Unlock()
107
108         is := m._lockIntent(keys...)
109         defer m._free(is, keys...)
110
111         is.NotifyClose(v)
112 }
113
114 // Closes all channels that are listening for the specified keys
115 // and removes them from the intent map.
116 func (m *intentMapStruct) Close(keys ...fscache.CacheKey) {
117         m.Lock()
118         defer m.Unlock()
119
120         is := m._lockIntent(keys...)
121         defer m._free(is, keys...)
122
123         is.Close()
124 }
125
126 // Notifies all channels that are listening for the specified keys,
127 // but doesn't close or remove them from the intent map.
128 func (m *intentMapStruct) Notify(v notification, keys ...fscache.CacheKey) {
129         m.Lock()
130         defer m.Unlock()
131
132         is := m._lockIntent(keys...)
133         defer is.Unlock()
134
135         is.Notify(v)
136 }
137
138 // NOTE: does not lock the stuct
139 func (s *intentStruct) Notify(v notification) {
140         for _, ch := range s.chs {
141                 ch <- v
142         }
143 }
144
145 // NOTE: does not lock the struct
146 func (s *intentStruct) Close() {
147         for _, ch := range s.chs {
148                 close(ch)
149         }
150         s.chs = nil
151 }
152
153 // NOTE: does not lock the struct
154 func (s *intentStruct) NotifyClose(v notification) {
155         for _, ch := range s.chs {
156                 ch <- v
157                 close(ch)
158         }
159         s.chs = nil
160 }