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