]> git.lizzy.rs Git - go-anidb.git/blob - intent.go
Modernize
[go-anidb.git] / intent.go
1 package anidb
2
3 import (
4         "github.com/EliasFleckenstein03/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         if is != nil {
98                 is.Unlock()
99         }
100 }
101
102 // Notifies and closes all channels that are listening for the specified keys;
103 // also removes them from the intent map.
104 //
105 // Should be called after setting the cache.
106 func (m *intentMapStruct) NotifyClose(v notification, keys ...fscache.CacheKey) {
107         m.Lock()
108         defer m.Unlock()
109
110         is := m._lockIntent(keys...)
111         defer m._free(is, keys...)
112
113         if is != nil {
114                 is.NotifyClose(v)
115         }
116 }
117
118 // Closes all channels that are listening for the specified keys
119 // and removes them from the intent map.
120 func (m *intentMapStruct) Close(keys ...fscache.CacheKey) {
121         m.Lock()
122         defer m.Unlock()
123
124         is := m._lockIntent(keys...)
125         defer m._free(is, keys...)
126
127         if is != nil {
128                 is.Close()
129         }
130 }
131
132 // Notifies all channels that are listening for the specified keys,
133 // but doesn't close or remove them from the intent map.
134 func (m *intentMapStruct) Notify(v notification, keys ...fscache.CacheKey) {
135         m.Lock()
136         defer m.Unlock()
137
138         is := m._lockIntent(keys...)
139         if is != nil {
140                 defer is.Unlock()
141
142                 is.Notify(v)
143         }
144 }
145
146 // NOTE: does not lock the stuct
147 func (s *intentStruct) Notify(v notification) {
148         for _, ch := range s.chs {
149                 if ch != nil {
150                         ch <- v
151                 }
152         }
153 }
154
155 // NOTE: does not lock the struct
156 func (s *intentStruct) Close() {
157         for _, ch := range s.chs {
158                 if ch != nil {
159                         close(ch)
160                 }
161         }
162         s.chs = nil
163 }
164
165 // NOTE: does not lock the struct
166 func (s *intentStruct) NotifyClose(v notification) {
167         for _, ch := range s.chs {
168                 if ch != nil {
169                         ch <- v
170                         close(ch)
171                 }
172         }
173         s.chs = nil
174 }