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