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
}