Merge "Fix flaky TestNoServersAvailable"
diff --git a/lib/modules/examples_test.go b/lib/modules/examples_test.go
new file mode 100644
index 0000000..2247b7a
--- /dev/null
+++ b/lib/modules/examples_test.go
@@ -0,0 +1,52 @@
+package modules_test
+
+import (
+	"fmt"
+	"io"
+	"os"
+
+	"veyron.io/veyron/veyron/lib/modules"
+)
+
+func init() {
+	modules.RegisterChild("echo", "<args>...", echo)
+}
+
+func echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	for i, a := range args {
+		fmt.Fprintf(stdout, "%d: %s\n", i, a)
+	}
+	return nil
+}
+
+func ExampleDispatch() {
+	if modules.IsModulesProcess() {
+		// Child process. Dispatch will invoke the 'echo' command
+		if err := modules.Dispatch(); err != nil {
+			panic(fmt.Sprintf("unexpected error: %s", err))
+		}
+		return
+	}
+	// Parent process.
+	sh, _ := modules.NewShell(nil)
+	defer sh.Cleanup(nil, nil)
+	h, _ := sh.Start("echo", nil, "a", "b")
+	h.Shutdown(os.Stdout, os.Stderr)
+	// Output:
+	// 0: echo
+	// 1: a
+	// 2: b
+}
+
+func ExampleDispatchAndExit() {
+	// DispatchAndExit will call os.Exit(0) when executed within the child.
+	modules.DispatchAndExit()
+	sh, _ := modules.NewShell(nil)
+	defer sh.Cleanup(nil, nil)
+	h, _ := sh.Start("echo", nil, "c", "d")
+	h.Shutdown(os.Stdout, os.Stderr)
+	// Output:
+	// 0: echo
+	// 1: c
+	// 2: d
+}
diff --git a/lib/modules/registry.go b/lib/modules/registry.go
index 61325d2..721e45a 100644
--- a/lib/modules/registry.go
+++ b/lib/modules/registry.go
@@ -115,6 +115,16 @@
 // a subprocess that is not a unit test. It will return without an error
 // if it is executed by a process that does not specify an entry point
 // in its environment.
+//
+// func main() {
+//     if modules.IsModulesProcess() {
+//         if err := modules.Dispatch(); err != nil {
+//             panic("error")
+//          }
+//          return
+//      }
+//      parent code...
+//
 func Dispatch() error {
 	if !IsModulesProcess() {
 		return nil
@@ -125,6 +135,27 @@
 	return registry.dispatch()
 }
 
+// DispatchAndExit is like Dispatch except that it will call os.Exit(0)
+// when executed within a child process and the command succeeds, or panic
+// on encountering an error.
+//
+// func main() {
+//     modules.DispatchAndExit()
+//     parent code...
+//
+func DispatchAndExit() {
+	if !IsModulesProcess() {
+		return
+	}
+	if IsTestHelperProcess() {
+		panic("use DispatchInTest in unittests")
+	}
+	if err := registry.dispatch(); err != nil {
+		panic(fmt.Sprintf("unexpected error: %s", err))
+	}
+	os.Exit(0)
+}
+
 func (r *cmdRegistry) dispatch() error {
 	ch, err := vexec.GetChildHandle()
 	if err != nil {