netconfig: Provide a way to signal when the watching has stopped.

This commit makes it possible for clients of the NetConfigWatcher to
learn when the watching has really stopped.

One of the motivations for this was to enable the shutdown function
returned by v23.Init to ensure that all spawned goroutines (such as the
net config watcher) have been stopped.

Change-Id: I138e508d7dcaa81d367111075f5e9e186a6d4cfb
diff --git a/netconfig/.api b/netconfig/.api
index c6b4471..b7db259 100644
--- a/netconfig/.api
+++ b/netconfig/.api
@@ -10,5 +10,5 @@
 pkg netconfig, type IPRoute struct, Net net.IPNet
 pkg netconfig, type IPRoute struct, PreferredSource net.IP
 pkg netconfig, type NetConfigWatcher interface { Channel, Stop }
-pkg netconfig, type NetConfigWatcher interface, Channel() chan struct{}
+pkg netconfig, type NetConfigWatcher interface, Channel() <-chan struct{}
 pkg netconfig, type NetConfigWatcher interface, Stop()
diff --git a/netconfig/ipaux_bsd.go b/netconfig/ipaux_bsd.go
index 63e0078..d8bae90 100644
--- a/netconfig/ipaux_bsd.go
+++ b/netconfig/ipaux_bsd.go
@@ -42,10 +42,14 @@
 		return
 	}
 	w.stopped = true
+	if w.t != nil {
+		w.t.Stop()
+		w.t = nil
+	}
 	syscall.Close(w.s)
 }
 
-func (w *bsdNetConfigWatcher) Channel() chan struct{} {
+func (w *bsdNetConfigWatcher) Channel() <-chan struct{} {
 	return w.c
 }
 
@@ -80,7 +84,10 @@
 }
 
 func (w *bsdNetConfigWatcher) watcher() {
-	defer w.Stop()
+	defer func() {
+		w.Stop()
+		close(w.c)
+	}()
 
 	// Loop waiting for messages.
 	for {
@@ -111,7 +118,7 @@
 			// NOTE(p): I chose 3 seconds because that covers all the
 			// events involved in moving from one wifi network to another.
 			w.Lock()
-			if w.t == nil {
+			if w.t == nil && !w.stopped {
 				w.t = time.AfterFunc(3*time.Second, w.ding)
 			}
 			w.Unlock()
diff --git a/netconfig/ipaux_linux.go b/netconfig/ipaux_linux.go
index 15ec84e..c2c3d5a 100644
--- a/netconfig/ipaux_linux.go
+++ b/netconfig/ipaux_linux.go
@@ -269,11 +269,15 @@
 	if w.stopped {
 		return
 	}
+	if w.t != nil {
+		w.t.Stop()
+		w.t = nil
+	}
 	w.stopped = true
 	syscall.Close(w.s)
 }
 
-func (w *rtnetlinkWatcher) Channel() chan struct{} {
+func (w *rtnetlinkWatcher) Channel() <-chan struct{} {
 	return w.c
 }
 
@@ -319,6 +323,10 @@
 }
 
 func (w *rtnetlinkWatcher) watcher() {
+	defer func() {
+		w.Stop()
+		close(w.c)
+	}()
 	var newAddrs []net.IP
 	for {
 		rb := make([]byte, 4096)
@@ -361,21 +369,14 @@
 			// NOTE(p): I chose 3 seconds because that covers all the
 			// events involved in moving from one wifi network to another.
 			w.Lock()
-			if w.t == nil {
+			if w.t == nil && !w.stopped {
 				w.t = time.AfterFunc(3*time.Second, w.ding)
 			}
 			w.Unlock()
 		}
 	}
-
-	w.Stop()
-	w.Lock()
-	close(w.c)
-	if w.t != nil {
-		w.t.Stop()
-	}
-	w.Unlock()
 }
+
 func toIP(a []byte) (net.IP, error) {
 	switch len(a) {
 	case 4:
diff --git a/netconfig/ipaux_other.go b/netconfig/ipaux_other.go
index 81501d0..2d549e5 100644
--- a/netconfig/ipaux_other.go
+++ b/netconfig/ipaux_other.go
@@ -20,12 +20,11 @@
 	stop chan struct{} // channel to tell the watcher to stop
 }
 
-// Stop any waiter
 func (w *timerNetConfigWatcher) Stop() {
 	w.stop <- struct{}{}
 }
 
-func (w *timerNetConfigWatcher) Channel() chan struct{} {
+func (w *timerNetConfigWatcher) Channel() <-chan struct{} {
 	return w.c
 }
 
diff --git a/netconfig/ipaux_test.go b/netconfig/ipaux_test.go
new file mode 100644
index 0000000..e57cacd
--- /dev/null
+++ b/netconfig/ipaux_test.go
@@ -0,0 +1,21 @@
+// Copyright 2016 The Vanadium 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 netconfig
+
+import (
+	"testing"
+)
+
+func TestNetConfigWatcherStop(t *testing.T) {
+	w, err := NewNetConfigWatcher()
+	if err != nil {
+		t.Fatal(err)
+	}
+	w.Stop()
+	// The channel should eventually be closed when the watcher exits.
+	// If it doesn't close, then this test will run into a timeout.
+	for range w.Channel() {
+	}
+}
diff --git a/netconfig/model.go b/netconfig/model.go
index b0568db..cbd0f8f 100644
--- a/netconfig/model.go
+++ b/netconfig/model.go
@@ -21,7 +21,9 @@
 	// A channel that returns an item whenever the network addresses or
 	// interfaces have changed. It is up to the caller to reread the
 	// network configuration in such cases.
-	Channel() chan struct{}
+	//
+	// The channel will be closed when the watcher exits.
+	Channel() <-chan struct{}
 }
 
 // IPRoute represents a route in the kernel's routing table.