]> git.lizzy.rs Git - go-anidb.git/commitdiff
anidb: Refactor the intent map
authorDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Tue, 16 Jul 2013 16:25:51 +0000 (13:25 -0300)
committerDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Tue, 16 Jul 2013 16:25:51 +0000 (13:25 -0300)
Allows notifications without closing the channels. All current users of
Notify were changed to NotifyClose.

animecache.go
episodecache.go
filecache.go
groupcache.go
intent.go

index 3299034051b2ecedf4e10c176919a3f2462bcdd4..99b89015081833512858db5923f88c6d9bc93429 100644 (file)
@@ -71,13 +71,13 @@ func (adb *AniDB) AnimeByID(aid AID) <-chan *Anime {
        }
 
        if !cache.CheckValid(keys...) {
-               intentMap.Notify((*Anime)(nil), keys...)
+               intentMap.NotifyClose((*Anime)(nil), keys...)
                return ch
        }
 
        anime := aid.Anime()
        if !anime.IsStale() {
-               intentMap.Notify(anime, keys...)
+               intentMap.NotifyClose(anime, keys...)
                return ch
        }
 
@@ -146,9 +146,9 @@ func (adb *AniDB) AnimeByID(aid AID) <-chan *Anime {
                        if ok {
                                cache.Set(anime, keys...)
                        }
-                       intentMap.Notify(anime, keys...)
+                       intentMap.NotifyClose(anime, keys...)
                } else {
-                       intentMap.Notify((*Anime)(nil), keys...)
+                       intentMap.NotifyClose((*Anime)(nil), keys...)
                }
        }()
        return ch
index ad289c12fec3c58f392f43657bdcf0b6da12a41b..3c3a91e18310d5182dbabb522b181338251acce1 100644 (file)
@@ -61,13 +61,13 @@ func (adb *AniDB) EpisodeByID(eid EID) <-chan *Episode {
        }
 
        if !cache.CheckValid(keys...) {
-               intentMap.Notify((*Episode)(nil), keys...)
+               intentMap.NotifyClose((*Episode)(nil), keys...)
                return ch
        }
 
        e := eid.Episode()
        if !e.IsStale() {
-               intentMap.Notify(e, keys...)
+               intentMap.NotifyClose(e, keys...)
                return ch
        }
 
@@ -136,7 +136,7 @@ func (adb *AniDB) EpisodeByID(eid EID) <-chan *Episode {
                                }
                        }
                }
-               intentMap.Notify(e, keys...)
+               intentMap.NotifyClose(e, keys...)
        }()
        return ch
 }
index 026cd13903e6fe3801d71fcfbd2b050023f27922..d928b463cff7b0af8fda2d9e7b8ffb34c35b4990 100644 (file)
@@ -100,13 +100,13 @@ func (adb *AniDB) FileByID(fid FID) <-chan *File {
        }
 
        if !cache.CheckValid(keys...) {
-               intentMap.Notify((*File)(nil), keys...)
+               intentMap.NotifyClose((*File)(nil), keys...)
                return ch
        }
 
        f := fid.File()
        if !f.IsStale() {
-               intentMap.Notify(f, keys...)
+               intentMap.NotifyClose(f, keys...)
                return ch
        }
 
@@ -127,7 +127,7 @@ func (adb *AniDB) FileByID(fid FID) <-chan *File {
                        cache.MarkInvalid(keys...)
                }
 
-               intentMap.Notify(f, keys...)
+               intentMap.NotifyClose(f, keys...)
        }()
        return ch
 }
@@ -161,7 +161,7 @@ func (adb *AniDB) FileByEd2kSize(ed2k string, size int64) <-chan *File {
        }
 
        if !cache.CheckValid(keys...) {
-               intentMap.Notify(FID(0), keys...)
+               intentMap.NotifyClose(FID(0), keys...)
                return ch
        }
 
@@ -169,7 +169,7 @@ func (adb *AniDB) FileByEd2kSize(ed2k string, size int64) <-chan *File {
 
        var ec fidCache
        if cache.Get(&ec, keys...) == nil && !ec.IsStale() {
-               intentMap.Notify(ec.FID, keys...)
+               intentMap.NotifyClose(ec.FID, keys...)
                return ch
        }
        fid = ec.FID
@@ -197,7 +197,7 @@ func (adb *AniDB) FileByEd2kSize(ed2k string, size int64) <-chan *File {
                        panic("Don't know what to do with " + strings.Join(reply.Lines(), "\n"))
                }
 
-               intentMap.Notify(fid, keys...)
+               intentMap.NotifyClose(fid, keys...)
        }()
        return ch
 }
index b73665e7385c89217f927e62ffa5771d13f0356a..8d0f92cfd5f7c41f39d48fa30297e0b25bde6ef9 100644 (file)
@@ -74,13 +74,13 @@ func (adb *AniDB) GroupByID(gid GID) <-chan *Group {
        }
 
        if !cache.CheckValid(keys...) {
-               intentMap.Notify((*Group)(nil), keys...)
+               intentMap.NotifyClose((*Group)(nil), keys...)
                return ch
        }
 
        g := gid.Group()
        if !g.IsStale() {
-               intentMap.Notify(g, keys...)
+               intentMap.NotifyClose(g, keys...)
                return ch
        }
 
@@ -99,7 +99,7 @@ func (adb *AniDB) GroupByID(gid GID) <-chan *Group {
                        cache.Delete(keys...) // deleted group?
                }
 
-               intentMap.Notify(g, keys...)
+               intentMap.NotifyClose(g, keys...)
        }()
        return ch
 }
@@ -130,7 +130,7 @@ func (adb *AniDB) GroupByName(gname string) <-chan *Group {
        }
 
        if !cache.CheckValid(keys...) {
-               intentMap.Notify(GID(0), keys...)
+               intentMap.NotifyClose(GID(0), keys...)
                return ch
        }
 
@@ -138,14 +138,14 @@ func (adb *AniDB) GroupByName(gname string) <-chan *Group {
 
        var gc gidCache
        if cache.Get(&gc, keys...) == nil && !gc.IsStale() {
-               intentMap.Notify(gc.GID, keys...)
+               intentMap.NotifyClose(gc.GID, keys...)
                return ch
        }
        gid = gc.GID
 
        if gid == 0 {
                if cache.Get(&gc, altKeys...) == nil && !gc.IsStale() {
-                       intentMap.Notify(gc.GID, keys...)
+                       intentMap.NotifyClose(gc.GID, keys...)
                        return ch
                }
                gid = gc.GID
@@ -170,7 +170,7 @@ func (adb *AniDB) GroupByName(gname string) <-chan *Group {
                        cache.Delete(altKeys...)
                }
 
-               intentMap.Notify(gid, keys...)
+               intentMap.NotifyClose(gid, keys...)
        }()
        return ch
 }
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
 }