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