go.talks: inline the reader package in each main,
since playground doesn't currently handle packages
in other directories.  Move the real implementation
to a separate realmain.go, so that the talk examples
don't depend on the external rss package.  Update
the slides to reference fakemain.go.

R=adg
CC=golang-dev
https://golang.org/cl/9679044
diff --git a/2013/advconc.slide b/2013/advconc.slide
index 3f3869f..af61ce5 100644
--- a/2013/advconc.slide
+++ b/2013/advconc.slide
@@ -39,15 +39,15 @@
 
 * Example: ping-pong
 
-.play advconc/pingpong1.go /STARTMAIN1/,/STOPMAIN1/
+.play advconc/pingpong/pingpong.go /STARTMAIN1/,/STOPMAIN1/
 
 * Deadlock detection
 
-.play advconc/pingpongdeadlock.go /STARTMAIN1/,/STOPMAIN1/
+.play advconc/pingpongdeadlock/pingpongdeadlock.go /STARTMAIN1/,/STOPMAIN1/
 
 * Panic dumps the stacks
 
-.play advconc/pingpongpanic.go /STARTMAIN1/,/STOPMAIN1/
+.play advconc/pingpongpanic/pingpongpanic.go /STARTMAIN1/,/STOPMAIN1/
 
 * It's easy to go, but how to stop?
 
@@ -111,7 +111,7 @@
 
 * Example
 
-.play advconc/fakemain.go /func main/,/^}/
+.play advconc/fakemain/fakemain.go /func main/,/^}/
 
 * Subscribe
 
@@ -140,7 +140,7 @@
 
 To implement the `Subscription` interface, define `Updates` and `Close`.
 
-.code advconc/reader/reader.go /func.* Updates/,/^}/
+.code advconc/fakemain/fakemain.go /func.* Updates/,/^}/
 
   func (s *sub) Close() error {
       // TODO: make loop exit
@@ -157,16 +157,16 @@
 * Naive Implementation
 
 # Not quite enough room for this; retry after format change:
-# .play advconc/naivemain.go /naiveSub\) loop/,/^}/
+# .play advconc/naivemain/naivemain.go /naiveSub\) loop/,/^}/
 # also on subsequent slides.
 
-.play advconc/naivemain.go /STARTNAIVE/,/STOPNAIVE/
-.code advconc/naivemain.go /naiveSub\) Close/,/^}/
+.play advconc/naivemain/naivemain.go /STARTNAIVE /,/STOPNAIVE /
+.code advconc/naivemain/naivemain.go /naiveSub\) Close/,/^}/
 
 * Bug 1: unsynchronized access to s.closed/s.err
 
-.code advconc/naivemain.go /STARTNAIVE/,/STOPNAIVE/ HLsync
-.code advconc/naivemain.go /naiveSub\) Close/,/^}/ HLsync
+.code advconc/naivemain/naivemain.go /STARTNAIVE /,/STOPNAIVE / HLsync
+.code advconc/naivemain/naivemain.go /naiveSub\) Close/,/^}/ HLsync
 
 * Race Detector
 
@@ -174,19 +174,16 @@
 
 # original is 400x1500
 .image advconc/race.png 150 562
-.play advconc/naivemain.go /STARTNAIVE/,/s.err/ HLsync
-.code advconc/naivemain.go /naiveSub\) Close/,/^}/ HLsync
-
-#* Race demo
-#.play go1.1/race.go
+.play advconc/naivemain/naivemain.go /STARTNAIVE /,/s.err/ HLsync
+.code advconc/naivemain/naivemain.go /naiveSub\) Close/,/^}/ HLsync
 
 * Bug 2: time.Sleep may keep loop running
 
-.code advconc/naivemain.go /STARTNAIVE/,/STOPNAIVE/ HLsleep
+.code advconc/naivemain/naivemain.go /STARTNAIVE/,/STOPNAIVE/ HLsleep
 
 * Bug 3: loop may block forever on s.updates
 
-.code advconc/naivemain.go /STARTNAIVE/,/STOPNAIVE/ HLsend
+.code advconc/naivemain/naivemain.go /STARTNAIVE /,/STOPNAIVE / HLsend
 
 * Solution
 
@@ -237,17 +234,17 @@
 
 `Close` asks loop to exit and waits for a response.
 
-.code advconc/reader/reader.go /\*sub\) Close/,/^}/ HLchan
+.code advconc/fakemain/fakemain.go /\*sub\) Close/,/^}/ HLchan
 
 `loop` handles `Close` by replying with the `Fetch` error and exiting.
 
-.code advconc/reader/reader.go /STARTCLOSEONLY /,/STOPCLOSEONLY / HLchan
+.code advconc/fakemain/fakemain.go /STARTCLOSEONLY /,/STOPCLOSEONLY / HLchan
 
 * Case 2: Fetch
 
 Schedule the next `Fetch` after some delay.
 
-.code advconc/reader/reader.go /STARTFETCHONLY /,/STOPFETCHONLY /
+.code advconc/fakemain/fakemain.go /STARTFETCHONLY /,/STOPFETCHONLY /
 
 * Case 3: Send
 
@@ -271,19 +268,19 @@
 
 Select never selects a blocking case.
 
-.play advconc/nilselect.go /func main/,/^}/
+.play advconc/nilselect/nilselect.go /func main/,/^}/
 
 * Case 3: Send (fixed)
 
 Enable send only when pending is non-empty.
 
-.code advconc/reader/reader.go /STARTSENDONLY /,/STOPSENDONLY / HLupdates
+.code advconc/fakemain/fakemain.go /STARTSENDONLY /,/STOPSENDONLY / HLupdates
 
 * Select
 
 Put the three cases together:
 
-.code advconc/reader/reader.go /STARTSELECT /,/STOPSELECT /
+.code advconc/fakemain/fakemain.go /STARTSELECT /,/STOPSELECT /
 
 The cases interact via `err`, `next`, and `pending`.
 
@@ -295,35 +292,35 @@
 - Bug 2: `time.Sleep` may keep loop running
 - Bug 3: `loop` may block forever sending on `s.updates`
 
-.code advconc/reader/reader.go /STARTSELECT /,/STOPSELECT / HLcases
+.code advconc/fakemain/fakemain.go /STARTSELECT /,/STOPSELECT / HLcases
 
 * We can improve loop further
 
 * Issue: Fetch may return duplicates
 
-.code advconc/reader/reader.go /STARTFETCHVARS /,/STOPFETCHVARS / HLfetch
-.code advconc/reader/reader.go /STARTFETCHCASE /,/STOPFETCHCASE / HLfetch
+.code advconc/fakemain/fakemain.go /STARTFETCHVARS /,/STOPFETCHVARS / HLfetch
+.code advconc/fakemain/fakemain.go /STARTFETCHCASE /,/STOPFETCHCASE / HLfetch
 
 * Fix: Filter items before adding to pending
 
-.code advconc/reader/reader.go /STARTSEEN /,/STOPSEEN / HLseen
-.code advconc/reader/reader.go /STARTDEDUPE /,/STOPDEDUPE / HLdupe
+.code advconc/fakemain/fakemain.go /STARTSEEN /,/STOPSEEN / HLseen
+.code advconc/fakemain/fakemain.go /STARTDEDUPE /,/STOPDEDUPE / HLdupe
 
 * Issue: Pending queue grows without bound
 
-.code advconc/reader/reader.go /STARTDEDUPE /,/STOPDEDUPE / HLdupe
+.code advconc/fakemain/fakemain.go /STARTDEDUPE /,/STOPDEDUPE / HLdupe
 
 * Fix: Disable fetch case when too much pending
 
   const maxPending = 10
 
-.code advconc/reader/reader.go /STARTCAP /,/STOPCAP / HLcap
+.code advconc/fakemain/fakemain.go /STARTCAP /,/STOPCAP / HLcap
 
 Could instead drop older items from the head of `pending`.
 
 * Issue: Loop blocks on Fetch
 
-.code advconc/reader/reader.go /STARTDEDUPE /,/STOPDEDUPE / HLfetch
+.code advconc/fakemain/fakemain.go /STARTDEDUPE /,/STOPDEDUPE / HLfetch
 
 * Fix: Run Fetch asynchronously
 
@@ -331,9 +328,9 @@
 
   type fetchResult struct{ fetched []Item; next time.Time; err error }
 
-.code advconc/reader/reader.go /STARTFETCHDONE /,/STOPFETCHDONE / HLfetch
-.code advconc/reader/reader.go /STARTFETCHIF /,/STOPFETCHIF / HLfetch
-.code advconc/reader/reader.go /STARTFETCHASYNC /,/STOPFETCHASYNC / HLfetch
+.code advconc/fakemain/fakemain.go /STARTFETCHDONE /,/STOPFETCHDONE / HLfetch
+.code advconc/fakemain/fakemain.go /STARTFETCHIF /,/STOPFETCHIF / HLfetch
+.code advconc/fakemain/fakemain.go /STARTFETCHASYNC /,/STOPFETCHASYNC / HLfetch
 
 * Implemented Subscribe
 
@@ -379,6 +376,3 @@
 Go Tour (learn Go in your browser)
 
 .link http://tour.golang.org
-
-*Go*Team*Fireside*Chat*
-5:20pm Room 2
diff --git a/2013/advconc/buffer.go b/2013/advconc/buffer/buffer.go
similarity index 100%
rename from 2013/advconc/buffer.go
rename to 2013/advconc/buffer/buffer.go
diff --git a/2013/advconc/dedupermain.go b/2013/advconc/dedupermain.go
deleted file mode 100644
index d613f93..0000000
--- a/2013/advconc/dedupermain.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"math/rand"
-	"time"
-
-	. "code.google.com/p/go.talks/2013/advconc/reader"
-)
-
-func init() {
-	rand.Seed(time.Now().UnixNano())
-	FakeFetch = true
-}
-
-// STARTMAIN OMIT
-func main() {
-	// STARTMERGECALL OMIT
-	// Subscribe to some feeds, and create a merged update stream.
-	merged := Dedupe(Merge(
-		Subscribe(Fetch("blog.golang.org")),
-		Subscribe(Fetch("blog.golang.org")),
-		Subscribe(Fetch("blog.golang.org")),
-		Subscribe(Fetch("googleblog.blogspot.com")),
-		Subscribe(Fetch("googledevelopers.blogspot.com"))))
-	// STOPMERGECALL OMIT
-
-	// Close the subscriptions after some time.
-	time.AfterFunc(3*time.Second, func() {
-		fmt.Println("closed:", merged.Close())
-	})
-
-	// Print the stream.
-	for it := range merged.Updates() {
-		fmt.Println(it.Channel, it.Title)
-	}
-
-	panic("show me the stacks")
-}
-
-// STOPMAIN OMIT
diff --git a/2013/advconc/reader/reader.go b/2013/advconc/dedupermain/dedupermain.go
similarity index 87%
copy from 2013/advconc/reader/reader.go
copy to 2013/advconc/dedupermain/dedupermain.go
index 980f2bd..20867ac 100644
--- a/2013/advconc/reader/reader.go
+++ b/2013/advconc/dedupermain/dedupermain.go
@@ -1,11 +1,11 @@
-package reader
+// dedupermain runs the Subscribe example with several duplicate
+// subscriptions to demonstrate deduping.
+package main
 
 import (
 	"fmt"
 	"math/rand"
 	"time"
-
-	rss "github.com/jteeuwen/go-pkg-rss"
 )
 
 // STARTITEM OMIT
@@ -483,16 +483,9 @@
 	return d.updates
 }
 
-// FakeFetch causes Fetch to use a fake fetcher instead of the real
-// one.
-var FakeFetch bool
-
 // Fetch returns a Fetcher for Items from domain.
 func Fetch(domain string) Fetcher {
-	if FakeFetch {
-		return fakeFetch(domain)
-	}
-	return realFetch(domain)
+	return fakeFetch(domain)
 }
 
 func fakeFetch(domain string) Fetcher {
@@ -524,49 +517,33 @@
 	return
 }
 
-// realFetch returns a fetcher for the specified blogger domain.
-func realFetch(domain string) Fetcher {
-	return NewFetcher(fmt.Sprintf("http://%s/feeds/posts/default?alt=rss", domain))
+func init() {
+	rand.Seed(time.Now().UnixNano())
 }
 
-type fetcher struct {
-	uri   string
-	feed  *rss.Feed
-	items []Item
-}
+// STARTMAIN OMIT
+func main() {
+	// STARTMERGECALL OMIT
+	// Subscribe to some feeds, and create a merged update stream.
+	merged := Dedupe(Merge(
+		Subscribe(Fetch("blog.golang.org")),
+		Subscribe(Fetch("blog.golang.org")),
+		Subscribe(Fetch("blog.golang.org")),
+		Subscribe(Fetch("googleblog.blogspot.com")),
+		Subscribe(Fetch("googledevelopers.blogspot.com"))))
+	// STOPMERGECALL OMIT
 
-// NewFetcher returns a Fetcher for uri.
-func NewFetcher(uri string) Fetcher {
-	f := &fetcher{
-		uri: uri,
+	// Close the subscriptions after some time.
+	time.AfterFunc(3*time.Second, func() {
+		fmt.Println("closed:", merged.Close())
+	})
+
+	// Print the stream.
+	for it := range merged.Updates() {
+		fmt.Println(it.Channel, it.Title)
 	}
-	newChans := func(feed *rss.Feed, chans []*rss.Channel) {}
-	newItems := func(feed *rss.Feed, ch *rss.Channel, items []*rss.Item) {
-		for _, it := range items {
-			f.items = append(f.items, Item{
-				Channel: ch.Title,
-				GUID:    it.Guid,
-				Title:   it.Title,
-			})
-		}
-	}
-	f.feed = rss.New(1 /*minimum interval in minutes*/, true /*respect limit*/, newChans, newItems)
-	return f
+
+	panic("show me the stacks")
 }
 
-func (f *fetcher) Fetch() (items []Item, next time.Time, err error) {
-	fmt.Println("fetching", f.uri)
-	if err = f.feed.Fetch(f.uri, nil); err != nil {
-		return
-	}
-	items = f.items
-	f.items = nil
-	next = time.Now().Add(time.Duration(f.feed.SecondsTillUpdate()) * time.Second)
-	return
-}
-
-// TODO: in a longer talk: move the Subscribe function onto a Reader type, to
-// support dynamically adding and removing Subscriptions.  Reader should dedupe.
-
-// TODO: in a longer talk: make successive Subscribe calls for the same uri
-// share the same underlying Subscription, but provide duplicate streams.
+// STOPMAIN OMIT
diff --git a/2013/advconc/fakemain.go b/2013/advconc/fakemain.go
deleted file mode 100644
index 5c2d08c..0000000
--- a/2013/advconc/fakemain.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"math/rand"
-	"time"
-
-	. "code.google.com/p/go.talks/2013/advconc/reader"
-)
-
-func init() {
-	rand.Seed(time.Now().UnixNano())
-	FakeFetch = true
-}
-
-// STARTMAIN OMIT
-func main() {
-	// STARTMERGECALL OMIT
-	// Subscribe to some feeds, and create a merged update stream.
-	merged := Merge(
-		Subscribe(Fetch("blog.golang.org")),
-		Subscribe(Fetch("googleblog.blogspot.com")),
-		Subscribe(Fetch("googledevelopers.blogspot.com")))
-	// STOPMERGECALL OMIT
-
-	// Close the subscriptions after some time.
-	time.AfterFunc(3*time.Second, func() {
-		fmt.Println("closed:", merged.Close())
-	})
-
-	// Print the stream.
-	for it := range merged.Updates() {
-		fmt.Println(it.Channel, it.Title)
-	}
-
-	panic("show me the stacks")
-}
-
-// STOPMAIN OMIT
diff --git a/2013/advconc/reader/reader.go b/2013/advconc/fakemain/fakemain.go
similarity index 87%
copy from 2013/advconc/reader/reader.go
copy to 2013/advconc/fakemain/fakemain.go
index 980f2bd..52e0091 100644
--- a/2013/advconc/reader/reader.go
+++ b/2013/advconc/fakemain/fakemain.go
@@ -1,11 +1,10 @@
-package reader
+// fakemain runs the Subscribe example with a fake RSS fetcher.
+package main
 
 import (
 	"fmt"
 	"math/rand"
 	"time"
-
-	rss "github.com/jteeuwen/go-pkg-rss"
 )
 
 // STARTITEM OMIT
@@ -483,16 +482,9 @@
 	return d.updates
 }
 
-// FakeFetch causes Fetch to use a fake fetcher instead of the real
-// one.
-var FakeFetch bool
-
 // Fetch returns a Fetcher for Items from domain.
 func Fetch(domain string) Fetcher {
-	if FakeFetch {
-		return fakeFetch(domain)
-	}
-	return realFetch(domain)
+	return fakeFetch(domain)
 }
 
 func fakeFetch(domain string) Fetcher {
@@ -524,49 +516,31 @@
 	return
 }
 
-// realFetch returns a fetcher for the specified blogger domain.
-func realFetch(domain string) Fetcher {
-	return NewFetcher(fmt.Sprintf("http://%s/feeds/posts/default?alt=rss", domain))
+func init() {
+	rand.Seed(time.Now().UnixNano())
 }
 
-type fetcher struct {
-	uri   string
-	feed  *rss.Feed
-	items []Item
-}
+// STARTMAIN OMIT
+func main() {
+	// STARTMERGECALL OMIT
+	// Subscribe to some feeds, and create a merged update stream.
+	merged := Merge(
+		Subscribe(Fetch("blog.golang.org")),
+		Subscribe(Fetch("googleblog.blogspot.com")),
+		Subscribe(Fetch("googledevelopers.blogspot.com")))
+	// STOPMERGECALL OMIT
 
-// NewFetcher returns a Fetcher for uri.
-func NewFetcher(uri string) Fetcher {
-	f := &fetcher{
-		uri: uri,
+	// Close the subscriptions after some time.
+	time.AfterFunc(3*time.Second, func() {
+		fmt.Println("closed:", merged.Close())
+	})
+
+	// Print the stream.
+	for it := range merged.Updates() {
+		fmt.Println(it.Channel, it.Title)
 	}
-	newChans := func(feed *rss.Feed, chans []*rss.Channel) {}
-	newItems := func(feed *rss.Feed, ch *rss.Channel, items []*rss.Item) {
-		for _, it := range items {
-			f.items = append(f.items, Item{
-				Channel: ch.Title,
-				GUID:    it.Guid,
-				Title:   it.Title,
-			})
-		}
-	}
-	f.feed = rss.New(1 /*minimum interval in minutes*/, true /*respect limit*/, newChans, newItems)
-	return f
+
+	panic("show me the stacks")
 }
 
-func (f *fetcher) Fetch() (items []Item, next time.Time, err error) {
-	fmt.Println("fetching", f.uri)
-	if err = f.feed.Fetch(f.uri, nil); err != nil {
-		return
-	}
-	items = f.items
-	f.items = nil
-	next = time.Now().Add(time.Duration(f.feed.SecondsTillUpdate()) * time.Second)
-	return
-}
-
-// TODO: in a longer talk: move the Subscribe function onto a Reader type, to
-// support dynamically adding and removing Subscriptions.  Reader should dedupe.
-
-// TODO: in a longer talk: make successive Subscribe calls for the same uri
-// share the same underlying Subscription, but provide duplicate streams.
+// STOPMAIN OMIT
diff --git a/2013/advconc/fakemainnomerge.go b/2013/advconc/fakemainnomerge.go
deleted file mode 100644
index 8ab960d..0000000
--- a/2013/advconc/fakemainnomerge.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"math/rand"
-	"time"
-
-	. "code.google.com/p/go.talks/2013/advconc/reader"
-)
-
-func init() {
-	rand.Seed(time.Now().UnixNano())
-	FakeFetch = true
-}
-
-// STARTMAIN OMIT
-func main() {
-	sub := Subscribe(Fetch("blog.golang.org"))
-
-	// Close the subscription after some time.
-	time.AfterFunc(3*time.Second, func() {
-		fmt.Println("closed:", sub.Close())
-	})
-
-	// Print the stream.
-	for it := range sub.Updates() {
-		fmt.Println(it.Channel, it.Title)
-	}
-
-	panic("show me the stacks")
-}
-
-// STOPMAIN OMIT
diff --git a/2013/advconc/naivemain.go b/2013/advconc/naivemain.go
deleted file mode 100644
index a5a5155..0000000
--- a/2013/advconc/naivemain.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"math/rand"
-	"time"
-
-	. "code.google.com/p/go.talks/2013/advconc/reader"
-)
-
-func NaiveSubscribe(fetcher Fetcher) Subscription {
-	s := &naiveSub{
-		fetcher: fetcher,
-		updates: make(chan Item),
-	}
-	go s.loop()
-	return s
-}
-
-type naiveSub struct {
-	fetcher Fetcher
-	updates chan Item
-	closed  bool
-	err     error
-}
-
-func (s *naiveSub) Updates() <-chan Item {
-	return s.updates
-}
-
-func (s *naiveSub) loop() {
-	// STARTNAIVE OMIT
-	for {
-		if s.closed { // HLsync
-			close(s.updates)
-			return
-		}
-		items, next, err := s.fetcher.Fetch()
-		if err != nil {
-			s.err = err                  // HLsync
-			time.Sleep(10 * time.Second) // HLsleep
-			continue
-		}
-		for _, item := range items {
-			s.updates <- item // HLsend
-		}
-		if now := time.Now(); next.After(now) {
-			time.Sleep(next.Sub(now)) // HLsleep
-		}
-	}
-	// STOPNAIVE OMIT
-}
-
-func (s *naiveSub) Close() error {
-	s.closed = true // HLsync
-	return s.err    // HLsync
-}
-
-func init() {
-	rand.Seed(time.Now().UnixNano())
-	FakeFetch = true
-}
-
-func main() {
-	// Subscribe to some feeds, and create a merged update stream.
-	merged := Merge(
-		NaiveSubscribe(Fetch("blog.golang.org")),
-		NaiveSubscribe(Fetch("googleblog.blogspot.com")),
-		NaiveSubscribe(Fetch("googledevelopers.blogspot.com")))
-
-	// Close the subscriptions after some time.
-	time.AfterFunc(3*time.Second, func() {
-		fmt.Println("closed:", merged.Close())
-	})
-
-	// Print the stream.
-	for it := range merged.Updates() {
-		fmt.Println(it.Channel, it.Title)
-	}
-
-	// The loops are still running.  Let the race detector notice.
-	time.Sleep(1 * time.Second)
-
-	panic("show me the stacks")
-}
diff --git a/2013/advconc/reader/reader.go b/2013/advconc/naivemain/naivemain.go
similarity index 87%
copy from 2013/advconc/reader/reader.go
copy to 2013/advconc/naivemain/naivemain.go
index 980f2bd..de4c11b 100644
--- a/2013/advconc/reader/reader.go
+++ b/2013/advconc/naivemain/naivemain.go
@@ -1,11 +1,11 @@
-package reader
+// naivemain runs the Subscribe example with the naive Subscribe
+// implementation and a fake RSS fetcher.
+package main
 
 import (
 	"fmt"
 	"math/rand"
 	"time"
-
-	rss "github.com/jteeuwen/go-pkg-rss"
 )
 
 // STARTITEM OMIT
@@ -483,16 +483,9 @@
 	return d.updates
 }
 
-// FakeFetch causes Fetch to use a fake fetcher instead of the real
-// one.
-var FakeFetch bool
-
 // Fetch returns a Fetcher for Items from domain.
 func Fetch(domain string) Fetcher {
-	if FakeFetch {
-		return fakeFetch(domain)
-	}
-	return realFetch(domain)
+	return fakeFetch(domain)
 }
 
 func fakeFetch(domain string) Fetcher {
@@ -524,49 +517,77 @@
 	return
 }
 
-// realFetch returns a fetcher for the specified blogger domain.
-func realFetch(domain string) Fetcher {
-	return NewFetcher(fmt.Sprintf("http://%s/feeds/posts/default?alt=rss", domain))
-}
-
-type fetcher struct {
-	uri   string
-	feed  *rss.Feed
-	items []Item
-}
-
-// NewFetcher returns a Fetcher for uri.
-func NewFetcher(uri string) Fetcher {
-	f := &fetcher{
-		uri: uri,
+func NaiveSubscribe(fetcher Fetcher) Subscription {
+	s := &naiveSub{
+		fetcher: fetcher,
+		updates: make(chan Item),
 	}
-	newChans := func(feed *rss.Feed, chans []*rss.Channel) {}
-	newItems := func(feed *rss.Feed, ch *rss.Channel, items []*rss.Item) {
-		for _, it := range items {
-			f.items = append(f.items, Item{
-				Channel: ch.Title,
-				GUID:    it.Guid,
-				Title:   it.Title,
-			})
+	go s.loop()
+	return s
+}
+
+type naiveSub struct {
+	fetcher Fetcher
+	updates chan Item
+	closed  bool
+	err     error
+}
+
+func (s *naiveSub) Updates() <-chan Item {
+	return s.updates
+}
+
+func (s *naiveSub) loop() {
+	// STARTNAIVE OMIT
+	for {
+		if s.closed { // HLsync
+			close(s.updates)
+			return
+		}
+		items, next, err := s.fetcher.Fetch()
+		if err != nil {
+			s.err = err                  // HLsync
+			time.Sleep(10 * time.Second) // HLsleep
+			continue
+		}
+		for _, item := range items {
+			s.updates <- item // HLsend
+		}
+		if now := time.Now(); next.After(now) {
+			time.Sleep(next.Sub(now)) // HLsleep
 		}
 	}
-	f.feed = rss.New(1 /*minimum interval in minutes*/, true /*respect limit*/, newChans, newItems)
-	return f
+	// STOPNAIVE OMIT
 }
 
-func (f *fetcher) Fetch() (items []Item, next time.Time, err error) {
-	fmt.Println("fetching", f.uri)
-	if err = f.feed.Fetch(f.uri, nil); err != nil {
-		return
+func (s *naiveSub) Close() error {
+	s.closed = true // HLsync
+	return s.err    // HLsync
+}
+
+func init() {
+	rand.Seed(time.Now().UnixNano())
+}
+
+func main() {
+	// Subscribe to some feeds, and create a merged update stream.
+	merged := Merge(
+		NaiveSubscribe(Fetch("blog.golang.org")),
+		NaiveSubscribe(Fetch("googleblog.blogspot.com")),
+		NaiveSubscribe(Fetch("googledevelopers.blogspot.com")))
+
+	// Close the subscriptions after some time.
+	time.AfterFunc(3*time.Second, func() {
+		fmt.Println("closed:", merged.Close())
+	})
+
+	// Print the stream.
+	for it := range merged.Updates() {
+		fmt.Println(it.Channel, it.Title)
 	}
-	items = f.items
-	f.items = nil
-	next = time.Now().Add(time.Duration(f.feed.SecondsTillUpdate()) * time.Second)
-	return
+
+	// The loops are still running.  Let the race detector notice.
+	time.Sleep(1 * time.Second)
+
+	panic("show me the stacks")
 }
-
-// TODO: in a longer talk: move the Subscribe function onto a Reader type, to
-// support dynamically adding and removing Subscriptions.  Reader should dedupe.
-
-// TODO: in a longer talk: make successive Subscribe calls for the same uri
-// share the same underlying Subscription, but provide duplicate streams.
diff --git a/2013/advconc/naivemainnomerge.go b/2013/advconc/naivemainnomerge.go
deleted file mode 100644
index f4a94ad..0000000
--- a/2013/advconc/naivemainnomerge.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"math/rand"
-	"time"
-
-	. "code.google.com/p/go.talks/2013/advconc/reader"
-)
-
-func init() {
-	rand.Seed(time.Now().UnixNano())
-	FakeFetch = true
-}
-
-func main() {
-	sub := NaiveSubscribe(Fetch("blog.golang.org"))
-
-	// Close the subscription after some time.
-	time.AfterFunc(3*time.Second, func() {
-		fmt.Println("closed:", sub.Close())
-	})
-
-	// Print the stream.
-	for it := range sub.Updates() {
-		fmt.Println(it.Channel, it.Title)
-	}
-
-	panic("show me the stacks")
-}
diff --git a/2013/advconc/nilselect.go b/2013/advconc/nilselect/nilselect.go
similarity index 100%
rename from 2013/advconc/nilselect.go
rename to 2013/advconc/nilselect/nilselect.go
diff --git a/2013/advconc/pingpong/pingpong.go b/2013/advconc/pingpong/pingpong.go
new file mode 100644
index 0000000..80acfff
--- /dev/null
+++ b/2013/advconc/pingpong/pingpong.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+	"fmt"
+	"time"
+)
+
+// STARTMAIN1 OMIT
+type Ball struct{ hits int }
+
+func main() {
+	table := make(chan *Ball)
+	go player("ping", table)
+	go player("pong", table)
+
+	table <- new(Ball) // game on; toss the ball
+	time.Sleep(1 * time.Second)
+	<-table // game over; grab the ball
+}
+
+func player(name string, table chan *Ball) {
+	for {
+		ball := <-table
+		ball.hits++
+		fmt.Println(name, ball.hits)
+		time.Sleep(100 * time.Millisecond)
+		table <- ball
+	}
+}
+
+// STOPMAIN1 OMIT
diff --git a/2013/advconc/pingpong2.go b/2013/advconc/pingpong2.go
deleted file mode 100644
index 0aca88f..0000000
--- a/2013/advconc/pingpong2.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"time"
-)
-
-// STARTMAIN1 OMIT
-type Ball struct{}
-
-func main() {
-	table := make(chan Ball)
-	stop1, stop2 := make(chan int), make(chan int)
-	go player("ping", table, stop1)
-	go player("pong", table, stop2)
-
-	table <- Ball{}
-	time.Sleep(1 * time.Second)
-	stop1 <- 1
-	stop2 <- 1
-	<-stop1
-	<-stop2
-}
-
-// STOPMAIN1 OMIT
-
-// STARTPLAYER1 OMIT
-func player(name string, table chan Ball, stop chan int) {
-	for {
-		select {
-		case ball := <-table:
-			fmt.Println(name)
-			time.Sleep(100 * time.Millisecond)
-			table <- ball
-		case <-stop:
-			fmt.Println(name, "done")
-			stop <- 1
-		}
-	}
-}
-
-// STOPPLAYER1 OMIT
diff --git a/2013/advconc/pingpongdeadlock.go b/2013/advconc/pingpongdeadlock/pingpongdeadlock.go
similarity index 100%
rename from 2013/advconc/pingpongdeadlock.go
rename to 2013/advconc/pingpongdeadlock/pingpongdeadlock.go
diff --git a/2013/advconc/pingpongpanic.go b/2013/advconc/pingpongpanic/pingpongpanic.go
similarity index 100%
rename from 2013/advconc/pingpongpanic.go
rename to 2013/advconc/pingpongpanic/pingpongpanic.go
diff --git a/2013/advconc/reader/reader.go b/2013/advconc/realmain/realmain.go
similarity index 92%
rename from 2013/advconc/reader/reader.go
rename to 2013/advconc/realmain/realmain.go
index 980f2bd..6c340e7 100644
--- a/2013/advconc/reader/reader.go
+++ b/2013/advconc/realmain/realmain.go
@@ -1,4 +1,5 @@
-package reader
+// realmain runs the Subscribe example with a real RSS fetcher.
+package main
 
 import (
 	"fmt"
@@ -570,3 +571,38 @@
 
 // TODO: in a longer talk: make successive Subscribe calls for the same uri
 // share the same underlying Subscription, but provide duplicate streams.
+
+func init() {
+	rand.Seed(time.Now().UnixNano())
+}
+
+// STARTMAIN OMIT
+func main() {
+	// STARTMERGECALL OMIT
+	// Subscribe to some feeds, and create a merged update stream.
+	merged := Merge(
+		Subscribe(Fetch("blog.golang.org")),
+		Subscribe(Fetch("googleblog.blogspot.com")),
+		Subscribe(Fetch("googledevelopers.blogspot.com")))
+	// STOPMERGECALL OMIT
+
+	// Close the subscriptions after some time.
+	time.AfterFunc(3*time.Second, func() {
+		fmt.Println("closed:", merged.Close())
+	})
+
+	// Print the stream.
+	for it := range merged.Updates() {
+		fmt.Println(it.Channel, it.Title)
+	}
+
+	// Uncomment the panic below to dump the stack traces.  This
+	// will show several stacks for persistent HTTP connections
+	// created by the real RSS client.  To clean these up, we'll
+	// need to extend Fetcher with a Close method and plumb this
+	// through the RSS client implementation.
+	//
+	// panic("show me the stacks")
+}
+
+// STOPMAIN OMIT