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