@cryptotaxi247 / kubo / commits / fb5779661

fix(bs/notifications) use SubOnceEach to provide uniqueness guarantee

License: MIT Signed-off-by: Brian Tiger Chow <brian@perfmode.com> vendor forked pubsub to get SubOnceEach License: MIT Signed-off-by: Brian Tiger Chow <brian@perfmode.com>

Brian Tiger Chow committed Nov 23, 2014 at 22:46 UTC fb5779661b40d2d2ac66e65feb36c7f606dce899
8 files changed +85 -37
Godeps/Godeps.json
+4 -4
@@ -136,6 +136,10 @@
136 "Comment": "v0.6.0-5-gf92b795",
137 "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2"
138 },
139 + {
140 + "ImportPath": "github.com/maybebtc/pubsub",
141 + "Rev": "39ce5f556423a4c7223b370fa17a3bbd75b2d197"
142 + },
143 {
144 "ImportPath": "github.com/mitchellh/go-homedir",
145 "Rev": "7d2d8c8a4e078ce3c58736ab521a40b37a504c52"
@@ -144,10 +148,6 @@
148 "ImportPath": "github.com/syndtr/goleveldb/leveldb",
149 "Rev": "99056d50e56252fbe0021d5c893defca5a76baf8"
150 },
147 - {
148 - "ImportPath": "github.com/tuxychandru/pubsub",
149 - "Rev": "02de8aa2db3d570c5ab1be5ba67b456fd0fb7c4e"
150 - },
151 {
152 "ImportPath": "gopkg.in/natefinch/lumberjack.v2",
153 "Comment": "v1.0-12-gd28785c",
Godeps/_workspace/src/github.com/maybebtc/pubsub/Godeps/Godeps.json new
+13
@@ -0,0 +1,13 @@
1 +{
2 + "ImportPath": "github.com/maybebtc/pubsub",
3 + "GoVersion": "go1.3.3",
4 + "Packages": [
5 + "./..."
6 + ],
7 + "Deps": [
8 + {
9 + "ImportPath": "gopkg.in/check.v1",
10 + "Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673"
11 + }
12 + ]
13 +}
Godeps/_workspace/src/github.com/maybebtc/pubsub/Godeps/Readme new
+5
@@ -0,0 +1,5 @@
1 +This directory tree is generated automatically by godep.
2 +
3 +Please do not edit.
4 +
5 +See https://github.com/tools/godep for more information.
Godeps/_workspace/src/github.com/maybebtc/pubsub/Makefile new
+2
@@ -0,0 +1,2 @@
1 +vendor:
2 + godep save -r ./...
Godeps/_workspace/src/github.com/maybebtc/pubsub/README.md renamed
+1 -1
@@ -6,7 +6,7 @@ View the [API Documentation](http://godoc.org/github.com/tuxychandru/pubsub).
6
7 ## License
8
9 -Copyright (c) 2013, Chandra Sekar S
9 +Copyright (c) 2013, Chandra Sekar S
10 All rights reserved.
11
12 Redistribution and use in source and binary forms, with or without
Godeps/_workspace/src/github.com/maybebtc/pubsub/pubsub.go renamed
+36 -9
@@ -15,6 +15,7 @@ type operation int
15 const (
16 sub operation = iota
17 subOnce
18 + subOnceEach
19 pub
20 unsub
21 unsubAll
@@ -55,6 +56,12 @@ func (ps *PubSub) SubOnce(topics ...string) chan interface{} {
56 return ps.sub(subOnce, topics...)
57 }
58
59 +// SubOnceEach returns a channel on which callers receive, at most, one message
60 +// for each topic.
61 +func (ps *PubSub) SubOnceEach(topics ...string) chan interface{} {
62 + return ps.sub(subOnceEach, topics...)
63 +}
64 +
65 func (ps *PubSub) sub(op operation, topics ...string) chan interface{} {
66 ch := make(chan interface{}, ps.capacity)
67 ps.cmdChan <- cmd{op: op, topics: topics, ch: ch}
@@ -66,6 +73,12 @@ func (ps *PubSub) AddSub(ch chan interface{}, topics ...string) {
73 ps.cmdChan <- cmd{op: sub, topics: topics, ch: ch}
74 }
75
76 +// AddSubOnceEach adds subscriptions to an existing channel with SubOnceEach
77 +// behavior.
78 +func (ps *PubSub) AddSubOnceEach(ch chan interface{}, topics ...string) {
79 + ps.cmdChan <- cmd{op: subOnceEach, topics: topics, ch: ch}
80 +}
81 +
82 // Pub publishes the given message to all subscribers of
83 // the specified topics.
84 func (ps *PubSub) Pub(msg interface{}, topics ...string) {
@@ -98,7 +111,7 @@ func (ps *PubSub) Shutdown() {
111
112 func (ps *PubSub) start() {
113 reg := registry{
101 - topics: make(map[string]map[chan interface{}]bool),
114 + topics: make(map[string]map[chan interface{}]subtype),
115 revTopics: make(map[chan interface{}]map[string]bool),
116 }
117
@@ -119,10 +132,13 @@ loop:
132 for _, topic := range cmd.topics {
133 switch cmd.op {
134 case sub:
122 - reg.add(topic, cmd.ch, false)
135 + reg.add(topic, cmd.ch, stNorm)
136
137 case subOnce:
125 - reg.add(topic, cmd.ch, true)
138 + reg.add(topic, cmd.ch, stOnceAny)
139 +
140 + case subOnceEach:
141 + reg.add(topic, cmd.ch, stOnceEach)
142
143 case pub:
144 reg.send(topic, cmd.msg)
@@ -146,15 +162,23 @@ loop:
162 // registry maintains the current subscription state. It's not
163 // safe to access a registry from multiple goroutines simultaneously.
164 type registry struct {
149 - topics map[string]map[chan interface{}]bool
165 + topics map[string]map[chan interface{}]subtype
166 revTopics map[chan interface{}]map[string]bool
167 }
168
153 -func (reg *registry) add(topic string, ch chan interface{}, once bool) {
169 +type subtype int
170 +
171 +const (
172 + stOnceAny = iota
173 + stOnceEach
174 + stNorm
175 +)
176 +
177 +func (reg *registry) add(topic string, ch chan interface{}, st subtype) {
178 if reg.topics[topic] == nil {
155 - reg.topics[topic] = make(map[chan interface{}]bool)
179 + reg.topics[topic] = make(map[chan interface{}]subtype)
180 }
157 - reg.topics[topic][ch] = once
181 + reg.topics[topic][ch] = st
182
183 if reg.revTopics[ch] == nil {
184 reg.revTopics[ch] = make(map[string]bool)
@@ -163,12 +187,15 @@ func (reg *registry) add(topic string, ch chan interface{}, once bool) {
187 }
188
189 func (reg *registry) send(topic string, msg interface{}) {
166 - for ch, once := range reg.topics[topic] {
190 + for ch, st := range reg.topics[topic] {
191 ch <- msg
168 - if once {
192 + switch st {
193 + case stOnceAny:
194 for topic := range reg.revTopics[ch] {
195 reg.remove(topic, ch)
196 }
197 + case stOnceEach:
198 + reg.remove(topic, ch)
199 }
200 }
201 }
Godeps/_workspace/src/github.com/maybebtc/pubsub/pubsub_test.go renamed
+18 -1
@@ -5,7 +5,7 @@
5 package pubsub
6
7 import (
8 - check "launchpad.net/gocheck"
8 + check "gopkg.in/check.v1"
9 "runtime"
10 "testing"
11 "time"
@@ -181,6 +181,23 @@ func (s *Suite) TestMultiSubOnce(c *check.C) {
181 ps.Shutdown()
182 }
183
184 +func (s *Suite) TestMultiSubOnceEach(c *check.C) {
185 + ps := New(1)
186 + ch := ps.SubOnceEach("t1", "t2")
187 +
188 + ps.Pub("hi", "t1")
189 + c.Check(<-ch, check.Equals, "hi")
190 +
191 + ps.Pub("hi!", "t1") // ignored
192 +
193 + ps.Pub("hello", "t2")
194 + c.Check(<-ch, check.Equals, "hello")
195 +
196 + _, ok := <-ch
197 + c.Check(ok, check.Equals, false)
198 + ps.Shutdown()
199 +}
200 +
201 func (s *Suite) TestMultiPub(c *check.C) {
202 ps := New(1)
203 ch1 := ps.Sub("t1")
exchange/bitswap/notifications/notifications.go
+6 -22
@@ -2,7 +2,7 @@ package notifications
2
3 import (
4 context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
5 - pubsub "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/tuxychandru/pubsub"
5 + pubsub "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/maybebtc/pubsub"
6
7 blocks "github.com/jbenet/go-ipfs/blocks"
8 u "github.com/jbenet/go-ipfs/util"
@@ -39,20 +39,16 @@ func (ps *impl) Shutdown() {
39 func (ps *impl) Subscribe(ctx context.Context, keys ...u.Key) <-chan *blocks.Block {
40
41 blocksCh := make(chan *blocks.Block, len(keys))
42 - valuesCh := make(chan interface{}, len(keys))
43 - ps.wrapped.AddSub(valuesCh, toStrings(keys)...)
44 -
42 + if len(keys) == 0 {
43 + close(blocksCh)
44 + return blocksCh
45 + }
46 + valuesCh := ps.wrapped.SubOnceEach(toStrings(keys)...)
47 go func() {
48 defer func() {
47 - ps.wrapped.Unsub(valuesCh, toStrings(keys)...)
49 close(blocksCh)
50 }()
50 - seen := make(map[u.Key]struct{})
51 - i := 0 // req'd because it only counts unique block sends
51 for {
53 - if i >= len(keys) {
54 - return
55 - }
52 select {
53 case <-ctx.Done():
54 return
@@ -64,22 +60,10 @@ func (ps *impl) Subscribe(ctx context.Context, keys ...u.Key) <-chan *blocks.Blo
60 if !ok {
61 return
62 }
67 - if _, ok := seen[block.Key()]; ok {
68 - continue
69 - }
63 select {
64 case <-ctx.Done():
65 return
66 case blocksCh <- block: // continue
74 - // Unsub alone is insufficient for keeping out duplicates.
75 - // It's a race to unsubscribe before pubsub handles the
76 - // next Publish call. Therefore, must also check for
77 - // duplicates manually. Unsub is a performance
78 - // consideration to avoid lots of unnecessary channel
79 - // chatter.
80 - ps.wrapped.Unsub(valuesCh, string(block.Key()))
81 - i++
82 - seen[block.Key()] = struct{}{}
67 }
68 }
69 }