Created
August 19, 2014 22:09
-
-
Save agam/fe8983acccd455fd9510 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"time" | |
) | |
const ( | |
NameCmd int = iota | |
PingCmd int = iota | |
SetDepthCmd int = iota | |
GetDepthCmd int = iota | |
) | |
type Command struct { | |
cmdtype int | |
cmdchan chan int | |
cmdarg int | |
} | |
type State struct { | |
depth int | |
} | |
type Node struct { | |
neighbors map[string]chan Command | |
name string | |
comm chan Command | |
state State | |
} | |
type Graph struct { | |
nodes map[string]*Node | |
edges int | |
} | |
func GetNode(g *Graph, s string, numnodes int) *Node { | |
n, ok := g.nodes[s] | |
if !ok { | |
newnode := &Node{ | |
neighbors: make(map[string]chan Command), | |
name: s, | |
comm: make(chan Command, numnodes), | |
state: State{0}, | |
} | |
g.nodes[s] = newnode | |
go node_agent(newnode) | |
return newnode | |
} | |
return n | |
} | |
func node_agent(node *Node) { | |
for { | |
cmd := <-node.comm | |
switch cmd.cmdtype { | |
case NameCmd: | |
fmt.Println("I am ", node.name) | |
case PingCmd: | |
cmd.cmdchan <- PingCmd | |
case SetDepthCmd: | |
// Ask all neighbors to modify depth, and pass | |
// along the notification channel to them | |
depth := cmd.cmdarg | |
if node.state.depth > 0 { | |
break | |
} | |
node.state.depth = depth | |
for _, ch := range node.neighbors { | |
ch <- Command{SetDepthCmd, cmd.cmdchan, depth + 1} | |
} | |
cmd.cmdchan <- 1 | |
case GetDepthCmd: | |
cmd.cmdchan <- node.state.depth | |
} | |
} | |
} | |
func calculateDepths(g *Graph) { | |
// Recursively trigger computation from root node | |
notification := make(chan int) | |
g.nodes["root"].comm <- Command{SetDepthCmd, notification, 1} | |
for i := 0; i < len(g.nodes); i++ { | |
// Wait for every node to receive this atleast once | |
<-notification | |
} | |
// Now check all the depths | |
depthchan := make(chan int) | |
for name, node := range g.nodes { | |
node.comm <- Command{GetDepthCmd, depthchan, 0} | |
fmt.Println(name, " => ", <-depthchan) | |
} | |
} | |
func IsAlive(node *Node) bool { | |
// Ping the node with a 10ms timeout | |
pingchan := make(chan int) | |
node.comm <- Command{PingCmd, pingchan, 0} | |
select { | |
case <-pingchan: | |
return true | |
case <-time.After(10 * time.Millisecond): | |
return false | |
} | |
} | |
func MakeGraph(nodemap map[string][]string) *Graph { | |
g := &Graph{ | |
nodes: make(map[string]*Node), | |
edges: 0, | |
} | |
numnodes := len(nodemap) | |
for k, v := range nodemap { | |
node := GetNode(g, k, numnodes) | |
for _, neighbor := range v { | |
g.edges++ | |
neighbor_node := GetNode(g, neighbor, numnodes) | |
node.neighbors[neighbor] = neighbor_node.comm | |
neighbor_node.neighbors[k] = node.comm | |
} | |
} | |
return g | |
} | |
func printGraph(g *Graph) { | |
fmt.Println("Graph :") | |
for _, node := range g.nodes { | |
if IsAlive(node) { | |
node.comm <- Command{NameCmd, nil, 0} | |
} | |
} | |
} | |
func main() { | |
fmt.Println("------\nWoah!\n------\n") | |
nodemap := make(map[string][]string) | |
nodemap["root"] = []string{"abc", "def", "xyz"} | |
nodemap["abc"] = []string{"def", "ghi", "jkl"} | |
nodemap["jkl"] = []string{"pqr", "mno", "rst"} | |
nodemap["def"] = []string{"rst"} | |
nodemap["xyz"] = []string{"123", "456"} | |
nodemap["ghi"] = []string{"456"} | |
nodemap["pqr"] = []string{"987", "000"} | |
nodemap["mno"] = []string{"456"} | |
nodemap["rst"] = []string{"111", "222"} | |
nodemap["123"] = []string{"222", "456"} | |
fmt.Println("Nodes: ") | |
for k, v := range nodemap { | |
fmt.Println(k, " => ", v) | |
} | |
fmt.Println() | |
mygraph := MakeGraph(nodemap) | |
printGraph(mygraph) | |
calculateDepths(mygraph) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment