]> git.lizzy.rs Git - go-anidb.git/blobdiff - intent.go
anidb: Handle invalid AID HTTP API response
[go-anidb.git] / intent.go
index 853d8270902be287bd95bb4b67b171d4a5df0509..56900a437ef44a08feb2dcaf5718a830473c0f53 100644 (file)
--- a/intent.go
+++ b/intent.go
@@ -9,6 +9,8 @@ type intentStruct struct {
 
 type intentMapStruct struct {
        sync.Mutex
+       intentLock sync.Mutex // used by the Intent function
+
        m map[string]*intentStruct
 }
 
@@ -17,6 +19,8 @@ var intentMap = &intentMapStruct{
 }
 
 // Register a channel to be notified when the specified keys are notified.
+// Returns whether the caller was the first to register intent for the given
+// keys.
 //
 // Cache checks should be done after registering intent, since it's possible to
 // register Intent while a Notify is running, and the Notify is done after
@@ -24,11 +28,13 @@ var intentMap = &intentMapStruct{
 func (m *intentMapStruct) Intent(ch chan Cacheable, keys ...cacheKey) bool {
        key := cachePath(keys...)
 
+       m.intentLock.Lock()
+       defer m.intentLock.Unlock()
+
        m.Lock()
        s, ok := m.m[key]
        if !ok {
                s = &intentStruct{}
-               m.m[key] = s
        }
        m.Unlock()
 
@@ -36,28 +42,109 @@ func (m *intentMapStruct) Intent(ch chan Cacheable, keys ...cacheKey) bool {
        s.chs = append(s.chs, ch)
        s.Unlock()
 
+       m.Lock()
+       // key might have been deleted while only the struct itself was locked -- recheck
+       _, ok = m.m[key]
+       m.m[key] = s
+       m.Unlock()
+
        return ok
 }
 
-// Notify all channels that are listening for the specified keys.
+// Locks the requested keys and return the locked intentStruct.
 //
-// Should be called after setting the cache.
-func (m *intentMapStruct) Notify(v Cacheable, keys ...cacheKey) {
-       key := cachePath(keys...)
-
+// The intentStruct can be directly unlocked, or given to Free to also
+// remove it from the intent map.
+func (m *intentMapStruct) LockIntent(keys ...cacheKey) *intentStruct {
        m.Lock()
        defer m.Unlock()
-       s, ok := m.m[key]
+
+       return m._lockIntent(keys...)
+}
+
+func (m *intentMapStruct) _lockIntent(keys ...cacheKey) *intentStruct {
+       s, ok := m.m[cachePath(keys...)]
        if !ok {
-               return
+               return nil
        }
 
        s.Lock()
-       defer s.Unlock()
+       return s
+}
+
+// Removes the given intent from the intent map and unlocks the intentStruct.
+func (m *intentMapStruct) Free(is *intentStruct, keys ...cacheKey) {
+       m.Lock()
+       defer m.Unlock()
+
+       m._free(is, keys...)
+}
 
+func (m *intentMapStruct) _free(is *intentStruct, keys ...cacheKey) {
+       // deletes the key before unlocking, Intent needs to recheck key status
+       delete(m.m, cachePath(keys...))
+       // better than unlocking then deleting -- could delete a "brand new" entry
+       is.Unlock()
+}
+
+// Notifies and closes all channels that are listening for the specified keys;
+// also removes them from the intent map.
+//
+// Should be called after setting the cache.
+func (m *intentMapStruct) NotifyClose(v Cacheable, keys ...cacheKey) {
+       m.Lock()
+       defer m.Unlock()
+
+       is := m._lockIntent(keys...)
+       defer m._free(is, keys...)
+
+       is.NotifyClose(v)
+}
+
+// Closes all channels that are listening for the specified keys
+// and removes them from the intent map.
+func (m *intentMapStruct) Close(keys ...cacheKey) {
+       m.Lock()
+       defer m.Unlock()
+
+       is := m._lockIntent(keys...)
+       defer m._free(is, keys...)
+
+       is.Close()
+}
+
+// Notifies all channels that are listening for the specified keys,
+// but doesn't close or remove them from the intent map.
+func (m *intentMapStruct) Notify(v Cacheable, keys ...cacheKey) {
+       m.Lock()
+       defer m.Unlock()
+
+       is := m._lockIntent(keys...)
+       defer is.Unlock()
+
+       is.Notify(v)
+}
+
+// NOTE: does not lock the stuct
+func (s *intentStruct) Notify(v Cacheable) {
+       for _, ch := range s.chs {
+               ch <- v
+       }
+}
+
+// NOTE: does not lock the struct
+func (s *intentStruct) Close() {
        for _, ch := range s.chs {
-               go func(c chan Cacheable) { c <- v }(ch)
+               close(ch)
        }
+       s.chs = nil
+}
 
-       delete(m.m, key)
+// NOTE: does not lock the struct
+func (s *intentStruct) NotifyClose(v Cacheable) {
+       for _, ch := range s.chs {
+               ch <- v
+               close(ch)
+       }
+       s.chs = nil
 }