github.com/DARA-Project/GoDist-Scheduler@v0.0.0-20201030134746-668de4acea0d/examples/diningphilosopher_test/diningphilosopher.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "log" 7 "math/rand" 8 "net" 9 "os" 10 "time" 11 "runtime" 12 logging "github.com/op/go-logging" 13 14 "bitbucket.org/bestchai/dinv/dinvRT" 15 ) 16 17 const ( 18 Ack = 0xFF 19 RequestStick = 1 20 ReleaseStick = 2 21 ExcuseMe = 3 22 SIZEOFINT = 4 23 n = 50 24 BUFF_SIZE = 1024 25 SLEEP_MAX = 1e8 26 ) 27 28 type Msg struct { 29 Type int 30 } 31 32 //global state variables 33 var ( 34 Eating bool 35 Thinking bool 36 LeftChopstick bool 37 RightChopstick bool 38 Excused bool 39 Chopsticks int 40 Eaten int 41 ID string 42 43 logger *logging.Logger 44 loglevel = 5 45 ) 46 47 //Transition into the eating state 48 func EatingState() { 49 Eating = true 50 Thinking = false 51 LeftChopstick = true 52 RightChopstick = true 53 Eaten++ 54 runtime.DaraLog("EatingState","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 55 logger.Noticef("Eaten [%d/%d]", Eaten, n) 56 57 } 58 59 //transition into the thinking state 60 func ThinkingState() { 61 Eating = false 62 Thinking = true 63 LeftChopstick = false 64 RightChopstick = false 65 Chopsticks = 0 66 runtime.DaraLog("ThinkingState","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 67 } 68 69 //obtain the left chopstick 70 func LeftChopstickState() { 71 Eating = false 72 Thinking = true 73 LeftChopstick = true 74 Chopsticks++ 75 runtime.DaraLog("LeftChopstickState","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 76 77 } 78 79 //obtain the right chopstick 80 func RightChopstickState() { 81 Eating = false 82 Thinking = true 83 RightChopstick = true 84 Chopsticks++ 85 runtime.DaraLog("RightChopstickState","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 86 } 87 88 //structure defining a philosopher 89 type Philosopher struct { 90 id, neighbourId int 91 chopstick chan bool // the left chopstick // inspired by the wikipedia page, left chopsticks should be used first 92 neighbour *net.UDPConn 93 } 94 95 func makePhilosopher(port, neighbourPort int) *Philosopher { 96 logger = SetupLogger(loglevel, "Phil-"+fmt.Sprintf("%d", port)) 97 logger.Debugf("Setting up listing connection on %d\n", port) 98 conn, err := net.ListenPacket("udp", ":"+fmt.Sprintf("%d", port)) 99 if err != nil { 100 log.Fatalf("Unable to set up connection Dying right away: %s", err.Error()) 101 } 102 103 //for general testing 104 if port == 4000 { 105 ID = fmt.Sprintf("%d", port) 106 } else { 107 ID = "ALL" 108 } 109 110 logger.Debugf("listening on %d\n", port) 111 var neighbour *net.UDPConn 112 var errDial error 113 connected := false 114 //Continuously try to connect via udp 115 for !connected { 116 117 time.Sleep(time.Millisecond) 118 neighbourAddr, errR := net.ResolveUDPAddr("udp4", ":"+fmt.Sprintf("%d", neighbourPort)) 119 PrintErr(errR) 120 listenAddr, errL := net.ResolveUDPAddr("udp4", ":"+fmt.Sprintf("%d", port+1000)) 121 PrintErr(errL) 122 neighbour, errDial = net.DialUDP("udp4", listenAddr, neighbourAddr) 123 PrintErr(errDial) 124 connected = (errR == nil) && (errL == nil) && (errDial == nil) 125 } 126 127 //setup chopstick channel triggered when chopsticks are given or 128 //received 129 chopstick := make(chan bool, 1) 130 chopstick <- true 131 logger.Debugf("launching chopstick server\n") 132 133 //sleep while the others start 134 time.Sleep(time.Millisecond) 135 136 go ListenForChopsticks(port, chopstick, conn) 137 return &Philosopher{port, neighbourPort, chopstick, neighbour} 138 } 139 140 func ListenForChopsticks(port int, chopstick chan bool, conn net.PacketConn) { 141 defer logger.Debugf("Chopstick #%d\n is down", port) //attempt to show when the chopsticks are no longer available 142 //Incomming request handler 143 for true { 144 time.Sleep(time.Millisecond) 145 req, addr := getRequest(conn) 146 go func(request int) { 147 switch request { 148 case ReleaseStick: 149 150 runtime.DaraLog("ReleaseStick","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 151 logger.Debugf("Receiving left stick on %d\n", port) 152 chopstick <- true 153 case RequestStick: 154 runtime.DaraLog("RequestStick","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 155 logger.Debugf("Stick request receved on %d\n", port) //TODO this is where I cant just give it away for fairness 156 select { 157 case <-chopstick: 158 runtime.DaraLog("GiveStick","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 159 logger.Debugf("Giving stick on %d\n", port) 160 resp := dinvRT.PackM(Msg{Type: Ack}, fmt.Sprintf("Giving stick on %d\n", port)) 161 conn.WriteTo(resp, addr) 162 case <-time.After(time.Duration(SLEEP_MAX * 1.3)): 163 runtime.DaraLog("TimeoutStick","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 164 logger.Criticalf("Exiting timed out wait on %d", port) 165 } 166 case ExcuseMe: 167 runtime.DaraLog("Excuse","Eating,Thinking,LeftChopstick,RightChopstick,Eaten",Eating,Thinking,LeftChopstick,RightChopstick,Eaten) 168 if !Excused { 169 logger.Debugf("%d has been excused from the table\n", port) 170 } 171 Excused = true 172 } 173 }(req) 174 } 175 } 176 177 //Read incomming udp messages and return the command code and sender address 178 func getRequest(conn net.PacketConn) (int, net.Addr) { 179 var msg Msg 180 buf := make([]byte, BUFF_SIZE) 181 _, addr, err := conn.ReadFrom(buf[0:]) 182 if err != nil { 183 log.Fatalf("Unable to read while getting a request Dying for a reason %s", err.Error()) 184 } 185 186 dinvRT.UnpackM(buf, &msg, "reading request") 187 return msg.Type, addr 188 } 189 190 //Transition and print state, then sleep for a random amount of time 191 func (phil *Philosopher) think() { 192 ThinkingState() 193 logger.Debugf("%d is thinking.\n", phil.id) 194 time.Sleep(time.Duration(rand.Int63n(SLEEP_MAX))) 195 } 196 197 //Eat and then wait for a random amount of time 198 func (phil *Philosopher) eat() { 199 EatingState() 200 logger.Debugf("%d is eating.\n", phil.id) 201 time.Sleep(time.Duration(rand.Int63n(SLEEP_MAX))) 202 } 203 204 //Attempt to gain a chopstic from a neighbouring philosopher 205 func (phil *Philosopher) getChopsticks() { 206 logger.Debugf("request chopstick %d -> %d\n", phil.id, phil.neighbourId) 207 timeout := make(chan bool, 1) 208 neighbourChopstick := make(chan bool, 1) 209 go func() { time.Sleep(time.Duration(SLEEP_MAX * 2)); timeout <- true }() 210 211 //obtain left chopstick, the one owned by this philosopher 212 <-phil.chopstick 213 LeftChopstickState() 214 //timeout function 215 logger.Debugf("%v got his chopstick.\n", phil.id) 216 217 //request chopstick function left should be owned 218 go func() { 219 time.Sleep(time.Millisecond) 220 //Send Request to Neighbour 221 var msg Msg 222 msg.Type = RequestStick 223 buf := make([]byte, BUFF_SIZE) 224 225 req := dinvRT.PackM(msg, "requesting chopsticks") 226 conn := phil.neighbour 227 conn.Write(req) 228 229 //Read response from Neighbour 230 _, err := conn.Read(buf[0:]) 231 if err != nil { 232 log.Println("Is this the one that keeps killing me?") 233 //Connection most likely timed out, chopstick unatainable 234 log.Println(err.Error()) 235 return 236 } 237 238 dinvRT.UnpackM(buf, &msg, "received a chopstick") 239 resp := msg.Type 240 if resp == Ack { 241 logger.Debugf("Received chopstick %d <- %d\n", phil.id, phil.neighbourId) 242 neighbourChopstick <- true 243 RightChopstickState() 244 } 245 }() 246 247 select { 248 case <-neighbourChopstick: 249 //get ready to eat 250 return 251 case <-timeout: 252 logger.Debugf("Timed out on %d\n", phil.id) 253 //return left chopstick and try again 254 phil.chopstick <- true 255 phil.think() 256 phil.getChopsticks() 257 } 258 } 259 260 func (phil *Philosopher) returnChopsticks() { 261 req := dinvRT.PackM(Msg{Type: ReleaseStick}, "returning stick") 262 263 logger.Debugf("Returning stick %d -> %d\n", phil.id, phil.neighbourId) 264 conn := phil.neighbour 265 conn.Write(req) 266 phil.chopstick <- true 267 ThinkingState() 268 } 269 270 func (phil *Philosopher) dine() { 271 phil.think() 272 phil.getChopsticks() 273 phil.eat() 274 phil.returnChopsticks() 275 } 276 277 //ask to be excused untill someone says you can 278 func (phil *Philosopher) leaveTable() { 279 for true { 280 req := dinvRT.PackM(Msg{Type: ExcuseMe}, "excuse me I want to leave") 281 conn := phil.neighbour 282 conn.Write(req) 283 284 if Excused == true { 285 break 286 } 287 time.Sleep(time.Duration(SLEEP_MAX)) 288 } 289 } 290 291 //main should take as an argument the port number of the philosoper 292 //and that of its neighbour 293 func main() { 294 var ( 295 myPort, neighbourPort int 296 ) 297 298 runtime.SchedulerPrint("\n\n\nThis is a mega test VAASTAV & Amy\n\n\n\n\n\n") 299 flag.IntVar(&myPort, "mP", 8080, "The port number this host will listen on") 300 flag.IntVar(&neighbourPort, "nP", 8081, "The port this host neighbour will listen on") 301 flag.Parse() 302 log.SetFlags(log.Lshortfile) 303 philosopher := makePhilosopher(myPort, neighbourPort) 304 for i := 0; i < n; i++ { 305 306 time.Sleep(time.Millisecond) 307 philosopher.dine() 308 } 309 logger.Noticef("%v is done dining\n", philosopher.id) 310 philosopher.leaveTable() 311 logger.Noticef("%d left the table\n", philosopher.id) 312 } 313 314 func PrintErr(err error) { 315 if err != nil { 316 log.Println("Printing Death Error") 317 log.Println(err) 318 os.Exit(1) 319 } 320 } 321 322 func SetupLogger(loglevel int, name string) *logging.Logger { 323 324 logger := logging.MustGetLogger(name) 325 format := logging.MustStringFormatter( 326 `%{color}%{time:15:04:05.000} %{shortfile} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`, 327 ) 328 // For demo purposes, create two backend for os.Stderr. 329 backend := logging.NewLogBackend(os.Stderr, "", 0) 330 331 // For messages written to backend2 we want to add some additional 332 // information to the output, including the used log level and the name of 333 // the function. 334 backendFormatter := logging.NewBackendFormatter(backend, format) 335 // Only errors and more severe messages should be sent to backend1 336 backendlevel := logging.AddModuleLevel(backendFormatter) 337 backendlevel.SetLevel(logging.Level(loglevel), "") 338 // Set the backends to be used. 339 logging.SetBackend(backendlevel) 340 return logger 341 } 342