Skip to content

Instantly share code, notes, and snippets.

@agam
Created August 19, 2014 22:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agam/fe8983acccd455fd9510 to your computer and use it in GitHub Desktop.
Save agam/fe8983acccd455fd9510 to your computer and use it in GitHub Desktop.
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