blob: 475db5b78b21937785a39b484387908531ebaaa5 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package ipc
2
3import (
4 "strings"
5 "sync"
6
7 "veyron2/ipc"
8 "veyron2/verror"
9)
10
11var (
12 errNilDispatcher = verror.BadArgf("ipc: can't register nil dispatcher")
13 errRegisterOnStoppedDisptrie = verror.Abortedf("ipc: Register called on stopped dispatch trie")
14)
15
16// disptrie represents the dispatch trie, where each trie node holds a slice of
17// dispatchers for that node, and a mapping of children keyed by slash-delimited
18// veyron name fragment. The trie performs longest-prefix name lookup, and is
19// safe for concurrent access.
20type disptrie struct {
21 sync.RWMutex
22 root *trienode
23}
24
25func newDisptrie() *disptrie {
26 return &disptrie{root: new(trienode)}
27}
28
29// splitName splits the veyron name into its slash-separated components.
30func splitName(name string) []string {
31 name = strings.TrimLeft(name, "/")
32 if name == "" {
33 return nil
34 }
35 return strings.Split(name, "/")
36}
37
38// Register disp in the trie node represented by prefix. Multiple dispatchers
39// may be registered in the same node, and are stored in order of registration.
40func (d *disptrie) Register(prefix string, disp ipc.Dispatcher) error {
41 if disp == nil {
42 return errNilDispatcher
43 }
44 name := splitName(prefix)
45 var err error
46 d.Lock()
47 if d.root != nil {
48 d.root.insert(name, disp)
49 } else {
50 err = errRegisterOnStoppedDisptrie
51 }
52 d.Unlock()
53 return err
54}
55
56// Lookup returns the dispatchers registered under the trie node representing
57// the longest match against the prefix. Also returns the remaining unmatched
58// suffix.
59func (d *disptrie) Lookup(prefix string) (disps []ipc.Dispatcher, suffix string) {
60 name := splitName(prefix)
61 d.RLock()
62 if d.root != nil {
63 disps, suffix = d.root.lookup(name, nil)
64 }
65 d.RUnlock()
66 return
67}
68
69// Stop dispatching on all registered dispatchers. After this call it's
70// guaranteed that no new lookups will be issued on the previously registered
71// dispatchers.
72func (d *disptrie) Stop() {
73 // Unlink the root, which ensures all dispatchers are unregistered
74 // atomically, and will not be consulted for future lookups.
75 d.Lock()
76 d.root = nil
77 d.Unlock()
78}
79
80// trienode represents a single node in the dispatch trie.
81type trienode struct {
82 disps []ipc.Dispatcher
83 children map[string]*trienode
84}
85
86func (t *trienode) insert(name []string, disp ipc.Dispatcher) {
87 if len(name) == 0 {
88 t.disps = append(t.disps, disp)
89 return
90 }
91 var child *trienode
92 if t.children == nil {
93 // No children, create new children and add new child node.
94 child = new(trienode)
95 t.children = map[string]*trienode{name[0]: child}
96 } else if c, ok := t.children[name[0]]; ok {
97 // Existing child, set child node.
98 child = c
99 } else {
100 // Non-existing child, add new child node.
101 child = new(trienode)
102 t.children[name[0]] = child
103 }
104 child.insert(name[1:], disp)
105}
106
107func (t *trienode) lookup(name []string, prev []ipc.Dispatcher) ([]ipc.Dispatcher, string) {
108 if len(t.disps) > 0 {
109 prev = t.disps
110 }
111 if len(name) > 0 && t.children != nil {
112 if child, ok := t.children[name[0]]; ok {
113 return child.lookup(name[1:], prev)
114 }
115 }
116 ret := strings.Join(name, "/")
117 if strings.HasPrefix(ret, "/") && !strings.HasPrefix(ret, "//") {
118 ret = "/" + ret
119 }
120 return prev, ret
121}
122
123func (t *trienode) walk(f func(ipc.Dispatcher)) {
124 for _, disp := range t.disps {
125 f(disp)
126 }
127 for _, child := range t.children {
128 child.walk(f)
129 }
130}