(runtime|services): Implement new AllGlobberX and ChildrenGlobberX

This change adds support for the new AllGlobberX and ChildrenGlobberX to
the rpc Server, and convers most of the code to use them.

I'm not super happy with the wspr change, but it works. Added TODO to
revisit.

Depends on v.io/c/13589

Partly addresses v.io/i/532 and v.io/i/533

MultiPart: 2/3
Change-Id: I541587e5d376a65f2b5ba4a3aab8f4b7063ad042
diff --git a/cmd/mounttable/impl_test.go b/cmd/mounttable/impl_test.go
index 1aaf18f..09875f3 100644
--- a/cmd/mounttable/impl_test.go
+++ b/cmd/mounttable/impl_test.go
@@ -15,6 +15,7 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -42,13 +43,12 @@
 	suffix string
 }
 
-func (s *server) Glob__(ctx *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
-	ctx.VI(2).Infof("Glob() was called. suffix=%v pattern=%q", s.suffix, pattern)
-	ch := make(chan naming.GlobReply, 2)
-	ch <- naming.GlobReplyEntry{naming.MountEntry{"name1", []naming.MountedServer{{"server1", deadline(1)}}, false, false}}
-	ch <- naming.GlobReplyEntry{naming.MountEntry{"name2", []naming.MountedServer{{"server2", deadline(2)}, {"server3", deadline(3)}}, false, false}}
-	close(ch)
-	return ch, nil
+func (s *server) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+	ctx.VI(2).Infof("Glob() was called. suffix=%v pattern=%q", s.suffix, g.String())
+	sender := call.SendStream()
+	sender.Send(naming.GlobReplyEntry{naming.MountEntry{"name1", []naming.MountedServer{{"server1", deadline(1)}}, false, false}})
+	sender.Send(naming.GlobReplyEntry{naming.MountEntry{"name2", []naming.MountedServer{{"server2", deadline(2)}, {"server3", deadline(3)}}, false, false}})
+	return nil
 }
 
 func (s *server) Mount(ctx *context.T, _ rpc.ServerCall, server string, ttl uint32, flags naming.MountFlag) error {
diff --git a/runtime/internal/naming/namespace/all_test.go b/runtime/internal/naming/namespace/all_test.go
index 89e3552..0844c4f 100644
--- a/runtime/internal/naming/namespace/all_test.go
+++ b/runtime/internal/naming/namespace/all_test.go
@@ -14,6 +14,7 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/namespace"
 	"v.io/v23/naming"
 	"v.io/v23/options"
@@ -132,18 +133,20 @@
 
 // testServer has the following namespace:
 // "" -> {level1} -> {level2}
-func (t *testServer) GlobChildren__(*context.T, rpc.ServerCall) (<-chan string, error) {
-	ch := make(chan string, 1)
+func (t *testServer) GlobChildren__(_ *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
 	switch t.suffix {
 	case "":
-		ch <- "level1"
+		if n := "level1"; m.Match(n) {
+			call.SendStream().Send(naming.GlobChildrenReplyName{n})
+		}
 	case "level1":
-		ch <- "level2"
+		if n := "level2"; m.Match(n) {
+			call.SendStream().Send(naming.GlobChildrenReplyName{n})
+		}
 	default:
-		return nil, nil
+		return nil
 	}
-	close(ch)
-	return ch, nil
+	return nil
 }
 
 type dispatcher struct{}
@@ -496,11 +499,11 @@
 	mu        sync.Mutex
 }
 
-func (g *GlobbableServer) Glob__(*context.T, rpc.ServerCall, string) (<-chan naming.GlobReply, error) {
+func (g *GlobbableServer) Glob__(*context.T, rpc.GlobServerCall, *glob.Glob) error {
 	g.mu.Lock()
 	defer g.mu.Unlock()
 	g.callCount++
-	return nil, nil
+	return nil
 }
 
 func (g *GlobbableServer) GetAndResetCount() int {
diff --git a/runtime/internal/rpc/benchmark/glob/README.txt b/runtime/internal/rpc/benchmark/glob/README.txt
deleted file mode 100644
index 0103cd2..0000000
--- a/runtime/internal/rpc/benchmark/glob/README.txt
+++ /dev/null
@@ -1,120 +0,0 @@
-Glob Benchmarks
-
-The benchmarks in this directory attempt to provide some guidance for the amount
-of buffering to use with the channels returned by Glob__ and GlobChildren__.
-
-The first set of benchmarks (BenchmarkChanN) shows the relationship between the
-buffer size and the latency of a very simple channel with one writer and one
-reader doing nothing else.
-
-The second set of benchmarks (BenchmarkGlobN) does the same thing but with a
-Glob__ server and a Glob client. The third set (BenchmarkGlobChildrenN) uses
-GlobChildren__.
-
-As of 2014-12-03, the conclusion is that the queue size has very little impact
-on performance.
-
-The BenchmarkChanN set shows that increasing the queue size improves latency for
-the very simple case, but not for Glob__ or GlobChildren__.
-
-An interesting observation is that all the benchmarks get slower as the number
-of cpus increases.
-
-Below are the test results for 1, 2, and 4 cpus on a HP Z420 workstation with
-2 × 6 cpu cores (Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz).
-
-$ ./glob.test -test.bench=. -test.benchtime=5s -test.cpu=1
-BenchmarkChan0	20000000	       464 ns/op
-BenchmarkChan1	20000000	       585 ns/op
-BenchmarkChan2	20000000	       484 ns/op
-BenchmarkChan4	20000000	       425 ns/op
-BenchmarkChan8	50000000	       396 ns/op
-BenchmarkChan16	50000000	       381 ns/op
-BenchmarkChan32	50000000	       371 ns/op
-BenchmarkChan64	50000000	       365 ns/op
-BenchmarkChan128	50000000	       363 ns/op
-BenchmarkChan256	50000000	       362 ns/op
-BenchmarkGlob0	  500000	     35029 ns/op
-BenchmarkGlob1	  500000	     63536 ns/op
-BenchmarkGlob2	  500000	     34753 ns/op
-BenchmarkGlob4	  500000	     26379 ns/op
-BenchmarkGlob8	  500000	     19293 ns/op
-BenchmarkGlob16	 1000000	     18149 ns/op
-BenchmarkGlob32	  500000	     52364 ns/op
-BenchmarkGlob64	  500000	     83879 ns/op
-BenchmarkGlob128	  100000	     88448 ns/op
-BenchmarkGlob256	  100000	     57922 ns/op
-BenchmarkGlobChildren0	  100000	    118448 ns/op
-BenchmarkGlobChildren1	  100000	    123274 ns/op
-BenchmarkGlobChildren2	  100000	    116110 ns/op
-BenchmarkGlobChildren4	  100000	    134175 ns/op
-BenchmarkGlobChildren8	  100000	    118776 ns/op
-BenchmarkGlobChildren16	  100000	    123191 ns/op
-BenchmarkGlobChildren32	  100000	    132195 ns/op
-BenchmarkGlobChildren64	  100000	    126004 ns/op
-BenchmarkGlobChildren128	  100000	    135072 ns/op
-BenchmarkGlobChildren256	  100000	    127399 ns/op
-
-$ ./glob.test -test.bench=. -test.benchtime=5s -test.cpu=2
-BenchmarkChan0-2	 5000000	      1595 ns/op
-BenchmarkChan1-2	 5000000	      1649 ns/op
-BenchmarkChan2-2	10000000	      1245 ns/op
-BenchmarkChan4-2	10000000	      1299 ns/op
-BenchmarkChan8-2	10000000	       982 ns/op
-BenchmarkChan16-2	10000000	       929 ns/op
-BenchmarkChan32-2	10000000	       916 ns/op
-BenchmarkChan64-2	10000000	       903 ns/op
-BenchmarkChan128-2	10000000	       907 ns/op
-BenchmarkChan256-2	10000000	       914 ns/op
-BenchmarkGlob0-2	  500000	     61455 ns/op
-BenchmarkGlob1-2	  500000	     46890 ns/op
-BenchmarkGlob2-2	  200000	     56462 ns/op
-BenchmarkGlob4-2	  500000	     22783 ns/op
-BenchmarkGlob8-2	  200000	     64783 ns/op
-BenchmarkGlob16-2	 1000000	     68119 ns/op
-BenchmarkGlob32-2	  200000	     78611 ns/op
-BenchmarkGlob64-2	  500000	     82180 ns/op
-BenchmarkGlob128-2	 1000000	     81548 ns/op
-BenchmarkGlob256-2	  100000	     88278 ns/op
-BenchmarkGlobChildren0-2	  100000	     83188 ns/op
-BenchmarkGlobChildren1-2	  100000	     81751 ns/op
-BenchmarkGlobChildren2-2	  100000	     81896 ns/op
-BenchmarkGlobChildren4-2	  100000	     81857 ns/op
-BenchmarkGlobChildren8-2	  100000	     81531 ns/op
-BenchmarkGlobChildren16-2	  100000	     89915 ns/op
-BenchmarkGlobChildren32-2	  100000	     81112 ns/op
-BenchmarkGlobChildren64-2	  100000	     80997 ns/op
-BenchmarkGlobChildren128-2	  100000	     81350 ns/op
-BenchmarkGlobChildren256-2	  100000	     81344 ns/op
-
-$ ./glob.test -test.bench=. -test.benchtime=5s -test.cpu=4
-BenchmarkChan0-4	 5000000	      2012 ns/op
-BenchmarkChan1-4	 5000000	      3149 ns/op
-BenchmarkChan2-4	 5000000	      1839 ns/op
-BenchmarkChan4-4	10000000	       957 ns/op
-BenchmarkChan8-4	20000000	       660 ns/op
-BenchmarkChan16-4	20000000	       523 ns/op
-BenchmarkChan32-4	20000000	       507 ns/op
-BenchmarkChan64-4	20000000	       509 ns/op
-BenchmarkChan128-4	20000000	       507 ns/op
-BenchmarkChan256-4	20000000	       511 ns/op
-BenchmarkGlob0-4	  100000	    103269 ns/op
-BenchmarkGlob1-4	  100000	    101222 ns/op
-BenchmarkGlob2-4	  100000	    102049 ns/op
-BenchmarkGlob4-4	  100000	    102763 ns/op
-BenchmarkGlob8-4	  100000	    101939 ns/op
-BenchmarkGlob16-4	  100000	    102989 ns/op
-BenchmarkGlob32-4	  100000	    103898 ns/op
-BenchmarkGlob64-4	  100000	    102838 ns/op
-BenchmarkGlob128-4	  100000	    101532 ns/op
-BenchmarkGlob256-4	  100000	    101059 ns/op
-BenchmarkGlobChildren0-4	  100000	    106617 ns/op
-BenchmarkGlobChildren1-4	  100000	    102576 ns/op
-BenchmarkGlobChildren2-4	  100000	    106313 ns/op
-BenchmarkGlobChildren4-4	  100000	    102774 ns/op
-BenchmarkGlobChildren8-4	  100000	    102886 ns/op
-BenchmarkGlobChildren16-4	  100000	    106771 ns/op
-BenchmarkGlobChildren32-4	  100000	    103309 ns/op
-BenchmarkGlobChildren64-4	  100000	    105112 ns/op
-BenchmarkGlobChildren128-4	  100000	    102295 ns/op
-BenchmarkGlobChildren256-4	  100000	    102951 ns/op
diff --git a/runtime/internal/rpc/benchmark/glob/doc.go b/runtime/internal/rpc/benchmark/glob/doc.go
deleted file mode 100644
index ce0aad5..0000000
--- a/runtime/internal/rpc/benchmark/glob/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2015 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 glob
-
-// This file exists only to prevent build failures from having a test-only
-// package.
diff --git a/runtime/internal/rpc/benchmark/glob/glob_test.go b/runtime/internal/rpc/benchmark/glob/glob_test.go
deleted file mode 100644
index a45053c..0000000
--- a/runtime/internal/rpc/benchmark/glob/glob_test.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2015 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 glob_test
-
-import (
-	"fmt"
-	"testing"
-
-	"v.io/v23"
-	"v.io/v23/context"
-	"v.io/v23/naming"
-	"v.io/v23/rpc"
-	"v.io/v23/security"
-
-	"v.io/x/ref/lib/xrpc"
-	_ "v.io/x/ref/runtime/factories/generic"
-	"v.io/x/ref/test"
-)
-
-func TestNothing(t *testing.T) {
-}
-
-func RunBenchmarkChan(b *testing.B, bufferSize int) {
-	ch := make(chan string, bufferSize)
-	go func() {
-		for i := 0; i < b.N; i++ {
-			ch <- fmt.Sprintf("%09d", i)
-		}
-		close(ch)
-	}()
-	for _ = range ch {
-		continue
-	}
-}
-
-func BenchmarkChan0(b *testing.B) {
-	RunBenchmarkChan(b, 0)
-}
-
-func BenchmarkChan1(b *testing.B) {
-	RunBenchmarkChan(b, 1)
-}
-
-func BenchmarkChan2(b *testing.B) {
-	RunBenchmarkChan(b, 2)
-}
-
-func BenchmarkChan4(b *testing.B) {
-	RunBenchmarkChan(b, 4)
-}
-
-func BenchmarkChan8(b *testing.B) {
-	RunBenchmarkChan(b, 8)
-}
-
-func BenchmarkChan16(b *testing.B) {
-	RunBenchmarkChan(b, 16)
-}
-
-func BenchmarkChan32(b *testing.B) {
-	RunBenchmarkChan(b, 32)
-}
-
-func BenchmarkChan64(b *testing.B) {
-	RunBenchmarkChan(b, 64)
-}
-
-func BenchmarkChan128(b *testing.B) {
-	RunBenchmarkChan(b, 128)
-}
-
-func BenchmarkChan256(b *testing.B) {
-	RunBenchmarkChan(b, 256)
-}
-
-type disp struct {
-	obj interface{}
-}
-
-func (d *disp) Lookup(_ *context.T, suffix string) (interface{}, security.Authorizer, error) {
-	return d.obj, nil, nil
-}
-
-type globObject struct {
-	b          *testing.B
-	bufferSize int
-}
-
-func (o *globObject) Glob__(_ *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
-	if pattern != "*" {
-		panic("this benchmark only works with pattern='*'")
-	}
-	ch := make(chan naming.GlobReply, o.bufferSize)
-	go func() {
-		for i := 0; i < o.b.N; i++ {
-			name := fmt.Sprintf("%09d", i)
-			ch <- naming.GlobReplyEntry{naming.MountEntry{Name: name}}
-		}
-		close(ch)
-	}()
-	return ch, nil
-}
-
-type globChildrenObject struct {
-	b          *testing.B
-	bufferSize int
-}
-
-func (o *globChildrenObject) GlobChildren__(_ *context.T, call rpc.ServerCall) (<-chan string, error) {
-	if call.Suffix() != "" {
-		return nil, nil
-	}
-	ch := make(chan string, o.bufferSize)
-	go func() {
-		for i := 0; i < o.b.N; i++ {
-			ch <- fmt.Sprintf("%09d", i)
-		}
-		close(ch)
-	}()
-	return ch, nil
-}
-
-func globClient(b *testing.B, ctx *context.T, name string) (int, error) {
-	client := v23.GetClient(ctx)
-	call, err := client.StartCall(ctx, name, rpc.GlobMethod, []interface{}{"*"})
-	if err != nil {
-		return 0, err
-	}
-	var me naming.MountEntry
-	b.ResetTimer()
-	count := 0
-	for {
-		if err := call.Recv(&me); err != nil {
-			break
-		}
-		count++
-	}
-	b.StopTimer()
-	if err := call.Finish(); err != nil {
-		return 0, err
-	}
-	return count, nil
-}
-
-func RunBenchmarkGlob(b *testing.B, obj interface{}) {
-	ctx, shutdown := test.V23Init()
-	defer shutdown()
-
-	server, err := xrpc.NewDispatchingServer(ctx, "", &disp{obj})
-	if err != nil {
-		b.Fatalf("failed to start server: %v", err)
-	}
-	addr := server.Status().Endpoints[0].Name()
-
-	count, err := globClient(b, ctx, addr)
-	if err != nil {
-		b.Fatalf("globClient failed: %v", err)
-	}
-	if count != b.N {
-		b.Fatalf("unexpected number of results: got %d, expected %d", count, b.N)
-	}
-}
-
-func BenchmarkGlob0(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 0})
-}
-
-func BenchmarkGlob1(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 1})
-}
-
-func BenchmarkGlob2(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 2})
-}
-
-func BenchmarkGlob4(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 4})
-}
-
-func BenchmarkGlob8(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 8})
-}
-
-func BenchmarkGlob16(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 16})
-}
-
-func BenchmarkGlob32(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 32})
-}
-
-func BenchmarkGlob64(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 64})
-}
-
-func BenchmarkGlob128(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 128})
-}
-
-func BenchmarkGlob256(b *testing.B) {
-	RunBenchmarkGlob(b, &globObject{b, 256})
-}
-
-func BenchmarkGlobChildren0(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 0})
-}
-
-func BenchmarkGlobChildren1(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 1})
-}
-
-func BenchmarkGlobChildren2(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 2})
-}
-
-func BenchmarkGlobChildren4(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 4})
-}
-
-func BenchmarkGlobChildren8(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 8})
-}
-
-func BenchmarkGlobChildren16(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 16})
-}
-
-func BenchmarkGlobChildren32(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 32})
-}
-
-func BenchmarkGlobChildren64(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 64})
-}
-
-func BenchmarkGlobChildren128(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 128})
-}
-
-func BenchmarkGlobChildren256(b *testing.B) {
-	RunBenchmarkGlob(b, &globChildrenObject{b, 256})
-}
diff --git a/runtime/internal/rpc/reserved.go b/runtime/internal/rpc/reserved.go
index 3e8a0ca..2f63127 100644
--- a/runtime/internal/rpc/reserved.go
+++ b/runtime/internal/rpc/reserved.go
@@ -5,6 +5,7 @@
 package rpc
 
 import (
+	"fmt"
 	"strings"
 
 	"v.io/v23/context"
@@ -188,6 +189,12 @@
 // levels.
 const maxRecursiveGlobDepth = 10
 
+type gState struct {
+	name  string
+	glob  *glob.Glob
+	depth int
+}
+
 func (i *globInternal) Glob(ctx *context.T, call rpc.StreamServerCall, pattern string) error {
 	ctx.VI(3).Infof("rpc Glob: Incoming request: %q.Glob(%q)", i.receiver, pattern)
 	g, err := glob.Parse(pattern)
@@ -205,11 +212,6 @@
 	}
 	call = callWithMethodTags(ctx, call, tags)
 
-	type gState struct {
-		name  string
-		glob  *glob.Glob
-		depth int
-	}
 	queue := []gState{gState{glob: g}}
 
 	someMatchesOmitted := false
@@ -227,7 +229,7 @@
 		suffix := subcall.Suffix()
 		if state.depth > maxRecursiveGlobDepth {
 			ctx.Errorf("rpc Glob: exceeded recursion limit (%d): %q", maxRecursiveGlobDepth, suffix)
-			call.Send(naming.GlobReplyError{
+			subcall.Send(naming.GlobReplyError{
 				naming.GlobError{Name: state.name, Error: reserved.NewErrGlobMaxRecursionReached(ctx)},
 			})
 			continue
@@ -235,21 +237,21 @@
 		obj, auth, err := disp.Lookup(ctx, suffix)
 		if err != nil {
 			ctx.VI(3).Infof("rpc Glob: Lookup failed for %q: %v", suffix, err)
-			call.Send(naming.GlobReplyError{
+			subcall.Send(naming.GlobReplyError{
 				naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrNoExist, ctx, err)},
 			})
 			continue
 		}
 		if obj == nil {
 			ctx.VI(3).Infof("rpc Glob: object not found for %q", suffix)
-			call.Send(naming.GlobReplyError{
+			subcall.Send(naming.GlobReplyError{
 				naming.GlobError{Name: state.name, Error: verror.New(verror.ErrNoExist, ctx, "nil object")},
 			})
 			continue
 		}
 
 		// Verify that that requester is authorized for the current object.
-		if err := authorize(ctx, call.Security(), auth); err != nil {
+		if err := authorize(ctx, subcall.Security(), auth); err != nil {
 			someMatchesOmitted = true
 			ctx.VI(3).Infof("rpc Glob: client is not authorized for %q: %v", suffix, err)
 			continue
@@ -260,13 +262,13 @@
 		invoker, err := objectToInvoker(obj)
 		if err != nil {
 			ctx.VI(3).Infof("rpc Glob: object for %q cannot be converted to invoker: %v", suffix, err)
-			call.Send(naming.GlobReplyError{
+			subcall.Send(naming.GlobReplyError{
 				naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)},
 			})
 			continue
 		}
 		gs := invoker.Globber()
-		if gs == nil || (gs.AllGlobber == nil && gs.ChildrenGlobber == nil) {
+		if gs == nil || (gs.AllGlobber == nil && gs.ChildrenGlobber == nil && gs.AllGlobberX == nil && gs.ChildrenGlobberX == nil) {
 			if state.glob.Len() == 0 {
 				subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name, IsLeaf: true}})
 			} else {
@@ -299,36 +301,106 @@
 			}
 			continue
 		}
-		ctx.VI(3).Infof("rpc Glob: %q implements ChildrenGlobber", suffix)
-		children, err := gs.ChildrenGlobber.GlobChildren__(ctx, subcall)
-		// The requested object doesn't exist.
-		if err != nil {
-			subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
-			continue
-		}
-		// The glob pattern matches the current object.
-		if state.glob.Len() == 0 {
-			subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name}})
-		}
-		// The current object has no children.
-		if children == nil {
-			continue
-		}
-		depth := state.depth
-		// This is a recursive pattern. Make sure we don't recurse forever.
-		if state.glob.Len() == 0 {
-			depth++
-		}
-		matcher, left := state.glob.Head(), state.glob.Tail()
-		for child := range children {
-			if len(child) == 0 || strings.Contains(child, "/") {
-				ctx.Errorf("rpc Glob: %q.GlobChildren__() sent an invalid child name: %q", suffix, child)
+		if gs.ChildrenGlobber != nil {
+			ctx.VI(3).Infof("rpc Glob: %q implements ChildrenGlobber", suffix)
+			children, err := gs.ChildrenGlobber.GlobChildren__(ctx, subcall)
+			// The requested object doesn't exist.
+			if err != nil {
+				subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
 				continue
 			}
-			if matcher.Match(child) {
-				next := naming.Join(state.name, child)
-				queue = append(queue, gState{next, left, depth})
+			// The glob pattern matches the current object.
+			if state.glob.Len() == 0 {
+				subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name}})
 			}
+			// The current object has no children.
+			if children == nil {
+				continue
+			}
+			depth := state.depth
+			// This is a recursive pattern. Make sure we don't recurse forever.
+			if state.glob.Len() == 0 {
+				depth++
+			}
+			matcher, left := state.glob.Head(), state.glob.Tail()
+			for child := range children {
+				if len(child) == 0 || strings.Contains(child, "/") {
+					ctx.Errorf("rpc Glob: %q.GlobChildren__() sent an invalid child name: %q", suffix, child)
+					continue
+				}
+				if matcher.Match(child) {
+					next := naming.Join(state.name, child)
+					queue = append(queue, gState{next, left, depth})
+				}
+			}
+			continue
+		}
+		if gs.AllGlobberX != nil {
+			ctx.VI(3).Infof("rpc Glob: %q implements AllGlobberX", suffix)
+			send := func(reply naming.GlobReply) error {
+				select {
+				case <-ctx.Done():
+					return verror.New(verror.ErrAborted, ctx)
+				default:
+				}
+				switch v := reply.(type) {
+				case naming.GlobReplyEntry:
+					v.Value.Name = naming.Join(state.name, v.Value.Name)
+					return subcall.Send(v)
+				case naming.GlobReplyError:
+					v.Value.Name = naming.Join(state.name, v.Value.Name)
+					return subcall.Send(v)
+				}
+				return nil
+			}
+			if err := gs.AllGlobberX.Glob__(ctx, &globServerCall{subcall, send}, state.glob); err != nil {
+				ctx.VI(3).Infof("rpc Glob: %q.Glob(%q) failed: %v", suffix, state.glob, err)
+				subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
+			}
+			continue
+		}
+		if gs.ChildrenGlobberX != nil {
+			ctx.VI(3).Infof("rpc Glob: %q implements ChildrenGlobberX", suffix)
+			depth := state.depth
+			if state.glob.Len() == 0 {
+				// The glob pattern matches the current object.
+				subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name}})
+				if state.glob.Recursive() {
+					// This is a recursive pattern. Make sure we don't recurse forever.
+					depth++
+				} else {
+					// The pattern can't possibly match any children of this node.
+					continue
+				}
+			}
+			matcher, tail := state.glob.Head(), state.glob.Tail()
+			send := func(reply naming.GlobChildrenReply) error {
+				select {
+				case <-ctx.Done():
+					return verror.New(verror.ErrAborted, ctx)
+				default:
+				}
+				switch v := reply.(type) {
+				case naming.GlobChildrenReplyName:
+					child := v.Value
+					if len(child) == 0 || strings.Contains(child, "/") {
+						return verror.New(verror.ErrBadArg, ctx, fmt.Sprintf("invalid child name: %q", child))
+					}
+					if !matcher.Match(child) {
+						return verror.New(verror.ErrBadArg, ctx, fmt.Sprintf("child name does not match: %q", child))
+					}
+					next := naming.Join(state.name, child)
+					queue = append(queue, gState{next, tail, depth})
+				case naming.GlobChildrenReplyError:
+					v.Value.Name = naming.Join(state.name, v.Value.Name)
+					return subcall.Send(naming.GlobReplyError{v.Value})
+				}
+				return nil
+			}
+			if err := gs.ChildrenGlobberX.GlobChildren__(ctx, &globChildrenServerCall{subcall, send}, matcher); err != nil {
+				subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
+			}
+			continue
 		}
 	}
 	if someMatchesOmitted {
@@ -337,6 +409,36 @@
 	return nil
 }
 
+type globServerCall struct {
+	rpc.ServerCall
+	send func(reply naming.GlobReply) error
+}
+
+func (g *globServerCall) SendStream() interface {
+	Send(naming.GlobReply) error
+} {
+	return g
+}
+
+func (g *globServerCall) Send(reply naming.GlobReply) error {
+	return g.send(reply)
+}
+
+type globChildrenServerCall struct {
+	rpc.ServerCall
+	send func(reply naming.GlobChildrenReply) error
+}
+
+func (g *globChildrenServerCall) SendStream() interface {
+	Send(naming.GlobChildrenReply) error
+} {
+	return g
+}
+
+func (g *globChildrenServerCall) Send(reply naming.GlobChildrenReply) error {
+	return g.send(reply)
+}
+
 // derivedServerCall allows us to derive calls with slightly different properties,
 // useful for our various special-cased reserved methods.
 type derivedServerCall struct {
diff --git a/runtime/internal/rpc/test/glob_test.go b/runtime/internal/rpc/test/glob_test.go
index 8a53bd7..d16e4bd 100644
--- a/runtime/internal/rpc/test/glob_test.go
+++ b/runtime/internal/rpc/test/glob_test.go
@@ -35,6 +35,7 @@
 		"a/b/c2/d1",
 		"a/b/c2/d2",
 		"a/x/y/z",
+		"a/X/y/z",
 		"leaf",
 	}
 	tree := newNode()
@@ -62,6 +63,9 @@
 		{"", "...", []string{
 			"",
 			"a",
+			"a/X",
+			"a/X/y",
+			"a/X/y/z",
 			"a/b",
 			"a/b/c1",
 			"a/b/c1/d1",
@@ -76,6 +80,9 @@
 		}, nil},
 		{"a", "...", []string{
 			"",
+			"X",
+			"X/y",
+			"X/y/z",
 			"b",
 			"b/c1",
 			"b/c1/d1",
@@ -116,19 +123,28 @@
 		{"a/x/y/z", "...", []string{
 			"",
 		}, nil},
+		{"a/X", "...", []string{
+			"",
+			"y",
+			"y/z",
+		}, nil},
 		{"", "", []string{""}, nil},
 		{"", "*", []string{"a", "leaf"}, nil},
 		{"a", "", []string{""}, nil},
-		{"a", "*", []string{"b", "x"}, nil},
+		{"a", "*", []string{"X", "b", "x"}, nil},
 		{"a/b", "", []string{""}, nil},
 		{"a/b", "*", []string{"c1", "c2"}, nil},
 		{"a/b/c1", "", []string{""}, nil},
 		{"a/b/c1", "*", []string{"d1", "d2"}, nil},
 		{"a/b/c1/d1", "*", []string{}, nil},
 		{"a/b/c1/d1", "", []string{""}, nil},
+		{"a/b/c2", "", []string{""}, nil},
+		{"a/b/c2", "*", []string{"d1", "d2"}, nil},
+		{"a/b/c2/d1", "*", []string{}, nil},
+		{"a/b/c2/d1", "", []string{""}, nil},
 		{"a", "*/c?", []string{"b/c1", "b/c2"}, nil},
-		{"a", "*/*", []string{"b/c1", "b/c2", "x/y"}, nil},
-		{"a", "*/*/*", []string{"b/c1/d1", "b/c1/d2", "b/c2/d1", "b/c2/d2", "x/y/z"}, nil},
+		{"a", "*/*", []string{"X/y", "b/c1", "b/c2", "x/y"}, nil},
+		{"a", "*/*/*", []string{"X/y/z", "b/c1/d1", "b/c1/d2", "b/c2/d1", "b/c2/d2", "x/y/z"}, nil},
 		{"a/x", "*/*", []string{"y/z"}, nil},
 		{"bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
 		{"bad/foo", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
@@ -270,6 +286,12 @@
 	if len(elems) < 2 || (elems[0] == "a" && elems[1] == "x") {
 		return &vChildrenObject{d.tree, elems}, auth, nil
 	}
+	if len(elems) < 2 || (elems[0] == "a" && elems[1] == "X") {
+		return &vChildrenXObject{d.tree, elems}, auth, nil
+	}
+	if len(elems) >= 3 && elems[0] == "a" && elems[1] == "b" && elems[2] == "c2" {
+		return &globXObject{d.tree, elems}, auth, nil
+	}
 	return &globObject{d.tree, elems}, auth, nil
 }
 
@@ -334,6 +356,54 @@
 	return ch, nil
 }
 
+type globXObject struct {
+	n      *node
+	suffix []string
+}
+
+func (o *globXObject) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+	n := o.n.find(o.suffix, false)
+	if n == nil {
+		return verror.New(verror.ErrNoExist, ctx, o.suffix)
+	}
+	o.globLoop(call, "", g, n)
+	return nil
+}
+
+func (o *globXObject) globLoop(call rpc.GlobServerCall, name string, g *glob.Glob, n *node) {
+	if g.Len() == 0 {
+		call.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: name}})
+	}
+	if g.Empty() {
+		return
+	}
+	matcher, left := g.Head(), g.Tail()
+	for leaf, child := range n.children {
+		if matcher.Match(leaf) {
+			o.globLoop(call, naming.Join(name, leaf), left, child)
+		}
+	}
+}
+
+type vChildrenXObject struct {
+	n      *node
+	suffix []string
+}
+
+func (o *vChildrenXObject) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
+	n := o.n.find(o.suffix, false)
+	if n == nil {
+		return verror.New(verror.ErrNoExist, ctx, o.suffix)
+	}
+	sender := call.SendStream()
+	for child, _ := range n.children {
+		if m.Match(child) {
+			sender.Send(naming.GlobChildrenReplyName{child})
+		}
+	}
+	return nil
+}
+
 type node struct {
 	children map[string]*node
 }
diff --git a/services/application/applicationd/service.go b/services/application/applicationd/service.go
index e11f26d..c4e833e 100644
--- a/services/application/applicationd/service.go
+++ b/services/application/applicationd/service.go
@@ -9,6 +9,7 @@
 	"strings"
 
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -208,7 +209,7 @@
 	return i.allAppVersionsForProfiles(appName, profiles)
 }
 
-func (i *appRepoService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+func (i *appRepoService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
 	ctx.VI(0).Infof("%v.GlobChildren__()", i.suffix)
 	i.store.Lock()
 	defer i.store.Unlock()
@@ -224,34 +225,34 @@
 	case 0:
 		results, err = i.allApplications()
 		if err != nil {
-			return nil, err
+			return err
 		}
 	case 1:
 		results, err = i.allAppVersions(elems[0])
 		if err != nil {
-			return nil, err
+			return err
 		}
 	case 2:
 		versions, err := i.allAppVersions(elems[0])
 		if err != nil {
-			return nil, err
+			return err
 		}
 		for _, v := range versions {
 			if v == elems[1] {
-				return nil, nil
+				return nil
 			}
 		}
-		return nil, verror.New(verror.ErrNoExist, nil)
+		return verror.New(verror.ErrNoExist, nil)
 	default:
-		return nil, verror.New(verror.ErrNoExist, nil)
+		return verror.New(verror.ErrNoExist, nil)
 	}
 
-	ch := make(chan string, len(results))
 	for _, r := range results {
-		ch <- r
+		if m.Match(r) {
+			call.SendStream().Send(naming.GlobChildrenReplyName{r})
+		}
 	}
-	close(ch)
-	return ch, nil
+	return nil
 }
 
 func (i *appRepoService) GetPermissions(ctx *context.T, call rpc.ServerCall) (perms access.Permissions, version string, err error) {
diff --git a/services/device/device/devicemanager_mock_test.go b/services/device/device/devicemanager_mock_test.go
index d683d0b..6d1e486 100644
--- a/services/device/device/devicemanager_mock_test.go
+++ b/services/device/device/devicemanager_mock_test.go
@@ -14,6 +14,7 @@
 	"time"
 
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -291,15 +292,13 @@
 	err     error
 }
 
-func (mdi *mockDeviceInvoker) Glob__(_ *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
-	gs := GlobStimulus{pattern}
+func (mdi *mockDeviceInvoker) Glob__(_ *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+	gs := GlobStimulus{g.String()}
 	gr := mdi.tape.Record(gs).(GlobResponse)
-	ch := make(chan naming.GlobReply, len(gr.results))
-	defer close(ch)
 	for _, r := range gr.results {
-		ch <- naming.GlobReplyEntry{naming.MountEntry{Name: r}}
+		call.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: r}})
 	}
-	return ch, gr.err
+	return gr.err
 }
 
 type dispatcher struct {
diff --git a/services/device/deviced/internal/impl/app_service.go b/services/device/deviced/internal/impl/app_service.go
index 2646685..576ffa3 100644
--- a/services/device/deviced/internal/impl/app_service.go
+++ b/services/device/deviced/internal/impl/app_service.go
@@ -135,6 +135,7 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -1429,7 +1430,7 @@
 	}
 }
 
-func (i *appService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+func (i *appService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
 	tree := newTreeNode()
 	switch len(i.suffix) {
 	case 0:
@@ -1446,20 +1447,18 @@
 		}
 		i.scanInstance(ctx, tree, i.suffix[0], dir)
 	default:
-		return nil, verror.New(verror.ErrNoExist, nil, i.suffix)
+		return verror.New(verror.ErrNoExist, nil, i.suffix)
 	}
 	n := tree.find(i.suffix, false)
 	if n == nil {
-		return nil, verror.New(errors.ErrInvalidSuffix, nil)
+		return verror.New(errors.ErrInvalidSuffix, nil)
 	}
-	ch := make(chan string)
-	go func() {
-		for child, _ := range n.children {
-			ch <- child
+	for child, _ := range n.children {
+		if m.Match(child) {
+			call.SendStream().Send(naming.GlobChildrenReplyName{child})
 		}
-		close(ch)
-	}()
-	return ch, nil
+	}
+	return nil
 }
 
 // TODO(rjkroege): Refactor to eliminate redundancy with newAppSpecificAuthorizer.
diff --git a/services/device/deviced/internal/impl/proxy_invoker.go b/services/device/deviced/internal/impl/proxy_invoker.go
index 5eb1d08..c395de6 100644
--- a/services/device/deviced/internal/impl/proxy_invoker.go
+++ b/services/device/deviced/internal/impl/proxy_invoker.go
@@ -10,6 +10,7 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security/access"
@@ -213,12 +214,11 @@
 }
 
 func (p *proxyInvoker) Globber() *rpc.GlobState {
-	return &rpc.GlobState{AllGlobber: p}
+	return &rpc.GlobState{AllGlobberX: p}
 }
 
 type call struct {
-	rpc.ServerCall
-	ch chan<- naming.GlobReply
+	rpc.GlobServerCall
 }
 
 func (c *call) Recv(v interface{}) error {
@@ -226,17 +226,13 @@
 }
 
 func (c *call) Send(v interface{}) error {
-	c.ch <- v.(naming.GlobReply)
-	return nil
+	return c.SendStream().Send(v.(naming.GlobReply))
 }
 
-func (p *proxyInvoker) Glob__(ctx *context.T, serverCall rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
-	ch := make(chan naming.GlobReply)
-	go func() {
-		p.Invoke(ctx, &call{serverCall, ch}, rpc.GlobMethod, []interface{}{&pattern})
-		close(ch)
-	}()
-	return ch, nil
+func (p *proxyInvoker) Glob__(ctx *context.T, serverCall rpc.GlobServerCall, g *glob.Glob) error {
+	pattern := g.String()
+	p.Invoke(ctx, &call{serverCall}, rpc.GlobMethod, []interface{}{&pattern})
+	return nil
 }
 
 // numResults returns the number of result values for the given method.
diff --git a/services/internal/binarylib/service.go b/services/internal/binarylib/service.go
index e9bf201..8a706e9 100644
--- a/services/internal/binarylib/service.go
+++ b/services/internal/binarylib/service.go
@@ -41,6 +41,8 @@
 	"syscall"
 
 	"v.io/v23/context"
+	"v.io/v23/glob"
+	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/security/access"
@@ -351,23 +353,21 @@
 	return nil
 }
 
-func (i *binaryService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+func (i *binaryService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
 	elems := strings.Split(i.suffix, "/")
 	if len(elems) == 1 && elems[0] == "" {
 		elems = nil
 	}
 	n := i.createObjectNameTree().find(elems, false)
 	if n == nil {
-		return nil, verror.New(ErrOperationFailed, ctx)
+		return verror.New(ErrOperationFailed, ctx)
 	}
-	ch := make(chan string)
-	go func() {
-		for k, _ := range n.children {
-			ch <- k
+	for k, _ := range n.children {
+		if m.Match(k) {
+			call.SendStream().Send(naming.GlobChildrenReplyName{k})
 		}
-		close(ch)
-	}()
-	return ch, nil
+	}
+	return nil
 }
 
 func (i *binaryService) GetPermissions(ctx *context.T, call rpc.ServerCall) (perms access.Permissions, version string, err error) {
diff --git a/services/internal/logreaderlib/logfile.go b/services/internal/logreaderlib/logfile.go
index 3f24a21..5a4109d 100644
--- a/services/internal/logreaderlib/logfile.go
+++ b/services/internal/logreaderlib/logfile.go
@@ -16,6 +16,8 @@
 	"strings"
 
 	"v.io/v23/context"
+	"v.io/v23/glob"
+	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/services/logreader"
 	"v.io/v23/verror"
@@ -112,42 +114,44 @@
 	return reader.tell(), nil
 }
 
-// GlobChildren__ returns the list of files in a directory streamed on a
-// channel. The list is empty if the object is a file.
-func (i *logfileService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+// GlobChildren__ returns the list of files in a directory on a stream.
+// The list is empty if the object is a file.
+func (i *logfileService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
 	ctx.VI(1).Infof("%v.GlobChildren__()", i.suffix)
 	dirName, err := translateNameToFilename(i.root, i.suffix)
 	if err != nil {
-		return nil, err
+		return err
 	}
 	stat, err := os.Stat(dirName)
 	if err != nil {
 		if os.IsNotExist(err) {
-			return nil, verror.New(verror.ErrNoExist, ctx, dirName)
+			return verror.New(verror.ErrNoExist, ctx, dirName)
 		}
-		return nil, verror.New(errOperationFailed, ctx, dirName)
+		return verror.New(errOperationFailed, ctx, dirName)
 	}
 	if !stat.IsDir() {
-		return nil, nil
+		return nil
 	}
 
-	ch := make(chan string)
-	go func() {
-		defer close(ch)
-		f, err := os.Open(dirName)
+	f, err := os.Open(dirName)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	for {
+		fi, err := f.Readdir(100)
+		if err == io.EOF {
+			break
+		}
 		if err != nil {
-			return
+			return err
 		}
-		defer f.Close()
-		for {
-			fi, err := f.Readdir(100)
-			if err != nil {
-				return
-			}
-			for _, file := range fi {
-				ch <- file.Name()
+		for _, file := range fi {
+			name := file.Name()
+			if m.Match(name) {
+				call.SendStream().Send(naming.GlobChildrenReplyName{name})
 			}
 		}
-	}()
-	return ch, nil
+	}
+	return nil
 }
diff --git a/services/internal/statslib/stats.go b/services/internal/statslib/stats.go
index 6fc9b49..ddc2735 100644
--- a/services/internal/statslib/stats.go
+++ b/services/internal/statslib/stats.go
@@ -11,6 +11,7 @@
 	"time"
 
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/services/stats"
@@ -38,21 +39,17 @@
 }
 
 // Glob__ returns the name of all objects that match pattern.
-func (i *statsService) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
-	ctx.VI(1).Infof("%v.Glob__(%q)", i.suffix, pattern)
-
-	ch := make(chan naming.GlobReply)
-	go func() {
-		defer close(ch)
-		it := libstats.Glob(i.suffix, pattern, time.Time{}, false)
-		for it.Advance() {
-			ch <- naming.GlobReplyEntry{naming.MountEntry{Name: it.Value().Key}}
-		}
-		if err := it.Err(); err != nil {
-			ctx.VI(1).Infof("libstats.Glob(%q, %q) failed: %v", i.suffix, pattern, err)
-		}
-	}()
-	return ch, nil
+func (i *statsService) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+	ctx.VI(1).Infof("%v.Glob__(%q)", i.suffix, g.String())
+	sender := call.SendStream()
+	it := libstats.Glob(i.suffix, g.String(), time.Time{}, false)
+	for it.Advance() {
+		sender.Send(naming.GlobReplyEntry{naming.MountEntry{Name: it.Value().Key}})
+	}
+	if err := it.Err(); err != nil {
+		ctx.VI(1).Infof("libstats.Glob(%q, %q) failed: %v", i.suffix, g.String(), err)
+	}
+	return nil
 }
 
 // WatchGlob returns the name and value of the objects that match the request,
diff --git a/services/mounttable/mounttablelib/mounttable.go b/services/mounttable/mounttablelib/mounttable.go
index 666a355..c391c89 100644
--- a/services/mounttable/mounttablelib/mounttable.go
+++ b/services/mounttable/mounttablelib/mounttable.go
@@ -645,7 +645,7 @@
 }
 
 // globStep is called with n and n.parent locked.  Returns with both unlocked.
-func (mt *mountTable) globStep(ctx *context.T, call security.Call, n *node, name string, pattern *glob.Glob, ch chan<- naming.GlobReply) {
+func (mt *mountTable) globStep(ctx *context.T, call security.Call, n *node, name string, pattern *glob.Glob, gCall rpc.GlobServerCall) {
 	if shouldAbort(ctx) {
 		n.parent.Unlock()
 		n.Unlock()
@@ -679,7 +679,7 @@
 		}
 		// Hold no locks while we are sending on the channel to avoid livelock.
 		n.Unlock()
-		ch <- naming.GlobReplyEntry{me}
+		gCall.SendStream().Send(naming.GlobReplyEntry{me})
 		return
 	}
 
@@ -721,7 +721,7 @@
 					c.Unlock()
 					continue
 				}
-				mt.globStep(ctx, call, c, naming.Join(name, k), suffix, ch)
+				mt.globStep(ctx, call, c, naming.Join(name, k), suffix, gCall)
 				n.Lock()
 			}
 		}
@@ -749,7 +749,7 @@
 	// Hold no locks while we are sending on the channel to avoid livelock.
 	n.Unlock()
 	// Intermediate nodes are marked as serving a mounttable since they answer the mounttable methods.
-	ch <- naming.GlobReplyEntry{naming.MountEntry{Name: name, ServesMountTable: true}}
+	gCall.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: name, ServesMountTable: true}})
 }
 
 // Glob finds matches in the namespace.  If we reach a mount point before matching the
@@ -763,38 +763,29 @@
 // a state that never existed in the mounttable.  For example, if someone removes c/d and later
 // adds a/b while a Glob is in progress, the Glob may return a set of nodes that includes both
 // c/d and a/b.
-func (ms *mountContext) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
+func (ms *mountContext) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
 	ctx.VI(2).Infof("mt.Glob %v", ms.elems)
 	scall := call.Security()
 
-	g, err := glob.Parse(pattern)
-	if err != nil {
-		return nil, err
-	}
-
 	mt := ms.mt
-	ch := make(chan naming.GlobReply)
-	go func() {
-		defer close(ch)
-		// If there was an access error, just ignore the entry, i.e., make it invisible.
-		n, err := mt.findNode(ctx, scall, ms.elems, false, nil, nil)
-		if err != nil {
-			return
-		}
-		// If the current name is not fully resolvable on this nameserver we
-		// don't need to evaluate the glob expression. Send a partially resolved
-		// name back to the client.
-		if n == nil {
-			ms.linkToLeaf(ctx, scall, ch)
-			return
-		}
-		mt.globStep(ctx, scall, n, "", g, ch)
-	}()
-	return ch, nil
+	// If there was an access error, just ignore the entry, i.e., make it invisible.
+	n, err := mt.findNode(ctx, scall, ms.elems, false, nil, nil)
+	if err != nil {
+		return nil
+	}
+	// If the current name is not fully resolvable on this nameserver we
+	// don't need to evaluate the glob expression. Send a partially resolved
+	// name back to the client.
+	if n == nil {
+		ms.linkToLeaf(ctx, scall, call)
+		return nil
+	}
+	mt.globStep(ctx, scall, n, "", g, call)
+	return nil
 }
 
-func (ms *mountContext) linkToLeaf(ctx *context.T, call security.Call, ch chan<- naming.GlobReply) {
-	n, elems, err := ms.mt.findMountPoint(ctx, call, ms.elems)
+func (ms *mountContext) linkToLeaf(ctx *context.T, sCall security.Call, gCall rpc.GlobServerCall) {
+	n, elems, err := ms.mt.findMountPoint(ctx, sCall, ms.elems)
 	if err != nil || n == nil {
 		return
 	}
@@ -804,7 +795,7 @@
 		servers[i].Server = naming.Join(s.Server, strings.Join(elems, "/"))
 	}
 	n.Unlock()
-	ch <- naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: servers}}
+	gCall.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: servers}})
 }
 
 func (ms *mountContext) SetPermissions(ctx *context.T, call rpc.ServerCall, perms access.Permissions, version string) error {
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index 981bc7a..879943b 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -18,6 +18,7 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/conventions"
+	"v.io/v23/glob"
 	"v.io/v23/naming"
 	"v.io/v23/options"
 	"v.io/v23/rpc"
@@ -436,7 +437,9 @@
 	}
 }
 
-type fakeServerCall struct{}
+type fakeServerCall struct {
+	sendCount int
+}
 
 func (fakeServerCall) Security() security.Call              { return security.NewCall(&security.CallParams{}) }
 func (fakeServerCall) Suffix() string                       { return "" }
@@ -444,6 +447,15 @@
 func (fakeServerCall) RemoteEndpoint() naming.Endpoint      { return nil }
 func (fakeServerCall) GrantedBlessings() security.Blessings { return security.Blessings{} }
 func (fakeServerCall) Server() rpc.Server                   { return nil }
+func (c *fakeServerCall) SendStream() interface {
+	Send(naming.GlobReply) error
+} {
+	return c
+}
+func (c *fakeServerCall) Send(reply naming.GlobReply) error {
+	c.sendCount++
+	return nil
+}
 
 func TestGlobAborts(t *testing.T) {
 	ctx, shutdown := test.V23Init()
@@ -470,15 +482,10 @@
 
 	glob := func(ctx *context.T) (int, error) {
 		root, _, _ := mt.Lookup(ctx, "")
-		ch, err := root.(rpc.Globber).Globber().AllGlobber.Glob__(ctx, fakeServerCall{}, "...")
-		if err != nil {
-			return 0, err
-		}
-		num := 0
-		for range ch {
-			num++
-		}
-		return num, nil
+		g, _ := glob.Parse("...")
+		fCall := &fakeServerCall{}
+		root.(rpc.Globber).Globber().AllGlobberX.Glob__(ctx, fCall, g)
+		return fCall.sendCount, nil
 	}
 
 	got, err := glob(ctx)
diff --git a/services/mounttable/mounttablelib/neighborhood.go b/services/mounttable/mounttablelib/neighborhood.go
index 44a2cdb..75488c2 100644
--- a/services/mounttable/mounttablelib/neighborhood.go
+++ b/services/mounttable/mounttablelib/neighborhood.go
@@ -268,39 +268,29 @@
 }
 
 // Glob__ implements rpc.AllGlobber
-func (ns *neighborhoodService) Glob__(ctx *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
-	g, err := glob.Parse(pattern)
-	if err != nil {
-		return nil, err
-	}
-
+func (ns *neighborhoodService) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
 	// return all neighbors that match the first element of the pattern.
 	nh := ns.nh
 
+	sender := call.SendStream()
 	switch len(ns.elems) {
 	case 0:
-		ch := make(chan naming.GlobReply)
-		go func() {
-			defer close(ch)
-			matcher := g.Head()
-			for k, n := range nh.neighbors() {
-				if matcher.Match(k) {
-					ch <- naming.GlobReplyEntry{naming.MountEntry{Name: k, Servers: n, ServesMountTable: true}}
-				}
+		matcher := g.Head()
+		for k, n := range nh.neighbors() {
+			if matcher.Match(k) {
+				sender.Send(naming.GlobReplyEntry{naming.MountEntry{Name: k, Servers: n, ServesMountTable: true}})
 			}
-		}()
-		return ch, nil
+		}
+		return nil
 	case 1:
 		neighbor := nh.neighbor(ns.elems[0])
 		if neighbor == nil {
-			return nil, verror.New(naming.ErrNoSuchName, ctx, ns.elems[0])
+			return verror.New(naming.ErrNoSuchName, ctx, ns.elems[0])
 		}
-		ch := make(chan naming.GlobReply, 1)
-		ch <- naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: neighbor, ServesMountTable: true}}
-		close(ch)
-		return ch, nil
+		sender.Send(naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: neighbor, ServesMountTable: true}})
+		return nil
 	default:
-		return nil, verror.New(naming.ErrNoSuchName, ctx, ns.elems)
+		return verror.New(naming.ErrNoSuchName, ctx, ns.elems)
 	}
 }
 
diff --git a/services/role/roled/internal/discharger.go b/services/role/roled/internal/discharger.go
index d6db009..f0321ee 100644
--- a/services/role/roled/internal/discharger.go
+++ b/services/role/roled/internal/discharger.go
@@ -9,6 +9,7 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
@@ -59,6 +60,6 @@
 	return discharge, nil
 }
 
-func (d *dischargerImpl) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
-	return globChildren(ctx, call.Security(), d.serverConfig)
+func (d *dischargerImpl) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
+	return globChildren(ctx, call, d.serverConfig, m)
 }
diff --git a/services/role/roled/internal/glob.go b/services/role/roled/internal/glob.go
index bb854f2..88b0478 100644
--- a/services/role/roled/internal/glob.go
+++ b/services/role/roled/internal/glob.go
@@ -10,25 +10,29 @@
 	"strings"
 
 	"v.io/v23/context"
+	"v.io/v23/glob"
+	"v.io/v23/naming"
+	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
 )
 
-func globChildren(ctx *context.T, call security.Call, serverConfig *serverConfig) (<-chan string, error) {
-	n := findRoles(ctx, call, serverConfig.root)
-	suffix := call.Suffix()
+func globChildren(ctx *context.T, call rpc.GlobChildrenServerCall, serverConfig *serverConfig, m *glob.Element) error {
+	sCall := call.Security()
+	n := findRoles(ctx, sCall, serverConfig.root)
+	suffix := sCall.Suffix()
 	if len(suffix) > 0 {
 		n = n.find(strings.Split(suffix, "/"), false)
 	}
 	if n == nil {
-		return nil, verror.New(verror.ErrNoExistOrNoAccess, ctx)
+		return verror.New(verror.ErrNoExistOrNoAccess, ctx)
 	}
-	ch := make(chan string, len(n.children))
 	for c := range n.children {
-		ch <- c
+		if m.Match(c) {
+			call.SendStream().Send(naming.GlobChildrenReplyName{c})
+		}
 	}
-	close(ch)
-	return ch, nil
+	return nil
 }
 
 // findRoles finds all the roles to which the caller has access.
diff --git a/services/role/roled/internal/role.go b/services/role/roled/internal/role.go
index a46a31d..5e4aadc 100644
--- a/services/role/roled/internal/role.go
+++ b/services/role/roled/internal/role.go
@@ -10,6 +10,7 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/glob"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
@@ -46,8 +47,8 @@
 	return createBlessings(ctx, call.Security(), i.roleConfig, v23.GetPrincipal(ctx), extensions, caveats, i.serverConfig.dischargerLocation)
 }
 
-func (i *roleService) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
-	return globChildren(ctx, call.Security(), i.serverConfig)
+func (i *roleService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
+	return globChildren(ctx, call, i.serverConfig, m)
 }
 
 // filterNonMembers returns only the blessing names that are authorized members
diff --git a/services/wspr/internal/rpc/server/invoker.go b/services/wspr/internal/rpc/server/invoker.go
index f10c43c..9e6e40c 100644
--- a/services/wspr/internal/rpc/server/invoker.go
+++ b/services/wspr/internal/rpc/server/invoker.go
@@ -6,7 +6,7 @@
 
 import (
 	"v.io/v23/context"
-	"v.io/v23/naming"
+	"v.io/v23/glob"
 	"v.io/v23/rpc"
 	"v.io/v23/vdl"
 	"v.io/v23/vdlroot/signature"
@@ -80,16 +80,23 @@
 	return results, nil
 }
 
-// TODO(bjornick,rthellend): Find a reasonable way to implement this for JS.
 func (i *invoker) Globber() *rpc.GlobState {
 	if i.globFunc == nil {
 		return nil
 	}
-	return &rpc.GlobState{AllGlobber: i}
+	return &rpc.GlobState{AllGlobberX: i}
 }
 
-func (i *invoker) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
-	return i.globFunc(ctx, call, pattern)
+func (i *invoker) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+	// TODO(rthellend,bjornick): Should we convert globFunc to match the
+	// new Glob__ interface?
+	ch, err := i.globFunc(ctx, call, g.String())
+	if ch != nil {
+		for reply := range ch {
+			call.SendStream().Send(reply)
+		}
+	}
+	return err
 }
 
 func (i *invoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) {