doc/codewalk: Share Memory By Communicating

R=r, rsc
CC=golang-dev
https://golang.org/cl/1727043
diff --git a/doc/codewalk/urlpoll.go b/doc/codewalk/urlpoll.go
new file mode 100644
index 0000000..2629f2b
--- /dev/null
+++ b/doc/codewalk/urlpoll.go
@@ -0,0 +1,117 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"http"
+	"log"
+	"time"
+)
+
+const (
+	numPollers     = 2           // number of Poller goroutines to launch
+	second         = 1e9         // one second is 1e9 nanoseconds
+	pollInterval   = 60 * second // how often to poll each URL
+	statusInterval = 10 * second // how often to log status to stdout
+	errTimeout     = 10 * second // back-off timeout on error
+)
+
+var urls = []string{
+	"http://www.google.com/",
+	"http://golang.org/",
+	"http://blog.golang.org/",
+}
+
+// State represents the last-known state of a URL.
+type State struct {
+	url    string
+	status string
+}
+
+// StateMonitor maintains a map that stores the state of the URLs being
+// polled, and prints the current state every updateInterval nanoseconds.
+// It returns a chan State to which resource state should be sent.
+func StateMonitor(updateInterval int64) chan<- State {
+	updates := make(chan State)
+	urlStatus := make(map[string]string)
+	ticker := time.NewTicker(updateInterval)
+	go func() {
+		for {
+			select {
+			case <-ticker.C:
+				logState(urlStatus)
+			case s := <-updates:
+				urlStatus[s.url] = s.status
+			}
+		}
+	}()
+	return updates
+}
+
+// logState prints a state map.
+func logState(s map[string]string) {
+	log.Stdout("Current state:")
+	for k, v := range s {
+		log.Stdoutf(" %s %s", k, v)
+	}
+}
+
+// Resource represents an HTTP URL to be polled by this program.
+type Resource struct {
+	url      string
+	errCount int64
+}
+
+// Poll executes an HTTP HEAD request for url
+// and returns the HTTP status string or an error string.
+func (r *Resource) Poll() string {
+	resp, err := http.Head(r.url)
+	if err != nil {
+		log.Stderr("Error", r.url, err)
+		r.errCount++
+		return err.String()
+	}
+	r.errCount = 0
+	return resp.Status
+}
+
+// Sleep sleeps for an appropriate interval (dependant on error state)
+// before sending the Resource to done.
+func (r *Resource) Sleep(done chan *Resource) {
+	time.Sleep(pollInterval + errTimeout*r.errCount)
+	done <- r
+}
+
+func Poller(in <-chan *Resource, out chan<- *Resource, status chan<- State) {
+	for r := range in {
+		s := r.Poll()
+		status <- State{r.url, s}
+		out <- r
+	}
+}
+
+func main() {
+	// create our input and output channels
+	pending, complete := make(chan *Resource), make(chan *Resource)
+
+	// launch the StateMonitor
+	status := StateMonitor(statusInterval)
+
+	// launch some Poller goroutines
+	for i := 0; i < numPollers; i++ {
+		go Poller(pending, complete, status)
+	}
+
+	// send some Resources to the pending queue
+	go func() {
+		for _, url := range urls {
+			pending <- &Resource{url: url}
+		}
+	}()
+
+	for r := range complete {
+		go r.Sleep(pending)
+	}
+}