1

I'm trying to fix my code, I've solved the deadlock an the mutual exclusion problem, but I don't know how I can avoid the starvation, because in promela (PML) there aren't the monitor. Can someone help me? Thanks in advance

bool forchetta0 = false,forchetta1 = false, forchetta2 = false, 
     forchetta3 = false, forchetta4 = false;

bool want0 = false, want1 = false, want2 = false, want3 = false, want4 = false;

active[5] proctype filosofo(){
printf("Filosofo %d -> sto pensando\n",_pid);

ciclo:
if
::true -> printf("Filosofo %d -> PENSO \n",_pid);
::true -> printf("Fisolofo %d -> voglio mangiare\n",_pid); 

                          if
                            ::_pid == 0 -> atomic{(!want4 &&!want1);want0 = true;}
                                forchetta0 = true; forchetta4 = true;
                                printf("Filosofo 0 -> STO MANGIANDO\n");
                                forchetta0 = false; forchetta4 = false;
                                want0 = false;
                                printf("Filosofo 0 -> HO FINITO DI MANGIARE\n");

                            ::_pid == 1 -> atomic{(!want0 &&!want2);want1 = true;}
                                forchetta0 = true; forchetta1 = true;
                                printf("Filosofo 1 -> STO MANGIANDO\n");
                                forchetta0 = false; forchetta1 = false;
                                want1 = false;
                                printf("Filosofo 1 -> HO FINITO DI MANGIARE\n");

                            ::_pid == 2 -> atomic{(!want1 &&!want3);want2 = true;}
                                forchetta1 = true; forchetta2 = true;
                                printf("Filosofo 2 -> STO MANGIANDO\n");
                                forchetta1 = false; forchetta2 = false;
                                want2 = false;
                                printf("Filosofo 2 -> HO FINITO DI MANGIARE\n");

                            ::_pid == 3 -> atomic{(!want2&&!want4);want3= true;}
                                forchetta3 = true; forchetta2= true;
                                printf("Filosofo 3 -> STO MANGIANDO\n");
                                forchetta3 = false; forchetta2= false;
                                want3 = false;
                                printf("Filosofo 3 -> HO FINITO DI MANGIARE\n");

                            ::_pid == 4 -> atomic{(!want0 &&!want3);want4= true;}
                                forchetta4 = true; forchetta3= true;
                                printf("Filosofo 4 -> STO MANGIANDO\n");
                                forchetta4 = false; forchetta3= false;
                                want4 = false;
                                printf("Filosofo 4 -> HO FINITO DI MANGIARE\n");
                        fi;
fi;

goto ciclo;
}
Patrick Trentin
  • 7,126
  • 3
  • 23
  • 40
macco
  • 21
  • 1
  • 11
  • Presumably the 'starvation' also occurs when you only have two or three, not five, pids? – GoZoner Sep 17 '15 at 14:49
  • if I recall, you can avoid starvation with timeouts. if everyone has the exact same timeout, however, you can end up with them timing out in synch. if you slightly randomize their timeouts, you can avoid it. – Dave Cousineau Jul 13 '16 at 21:58
  • 1
    Not the right language, but here is a paper on this problem that compares the performance of several algorithms to solve this problem: http://howardhinnant.github.io/dining_philosophers.html It includes several variants of "fork dropping" behavior mentioned in Patrick Trentin's answer. – Howard Hinnant Jul 20 '16 at 17:33
  • @HowardHinnant I saw your link only by chance after one whole year, thanks, it's an interesting reading – Patrick Trentin Sep 20 '17 at 19:41

1 Answers1

0

The pitfall in your code is that every philosopher refrains from eating if one of his neighbors are already trying to eat, in a sense they are acting too politely with each other and thus it is the scheduler that decides who is going to starve.

It is possible that you designed the code in this way after realizing that if every philosopher is selfish and simply grabs as many forks as he can, then you might end up in a deadlock situation.

The right solution lies among the two extremes: you need to relax the strong commitment to eat of each philosopher just enough to avoid deadlock but not too much to end up in starvation.


A possible approach for avoiding deadlock without incurring in starvation is to introduce the concept of altruistic philosopher, that is a philosopher which drops the fork that it holds when it realizes that no one can eat because there is a deadlock situation.

Having just one altruistic philosopher is sufficient in order to avoid deadlock, and thus all other philosophers can continue being greedy and attempt to grab as many forks as possible.

However, if you never change who is the altruistic philosopher during your execution, then you might end up with that guy starving himself to death. Thus, the solution is to change the altruistic philosopher infinitely often. A fair strategy is to change it in a round robin fashion each time that one philosopher acts altruistically in a deadlock situation. In that way, no philosopher is penalized and you can actually verify that no philosopher starves under the fairness condition that the scheduler gives a chance to every philosopher to execute infinitely often.


In order to help you model this approach in Promela, I'll drop you the following NuSMV code, which solves the same problem, taken from the teaching material of the laboratory of Formal Methods at the University of Trento:

MODULE philosopher(ID, left_chopstick, right_chopstick, ALTRUIST, DEADLOCK)

  VAR
    state : {think, pickup, eat, putdown};

  ASSIGN
    init(state) := think;

    next(state) := case
      (DEADLOCK) & (ALTRUIST = ID): think;
      (state = think): pickup;
      (state = pickup) & (left_chopstick = 1) & (right_chopstick = 2): eat;
      (state = eat): putdown;
      (state = putdown) & !(left_chopstick = 1) & !(right_chopstick = 2): think;
      TRUE : state;
    esac;

    next(left_chopstick) := case
      (DEADLOCK) & (ALTRUIST = ID): 2;
      (state = pickup) & (left_chopstick = 0): 1;
      (state = putdown) & (left_chopstick = 1) & !(right_chopstick = 2): 0;
      TRUE : left_chopstick;
    esac;

    next(right_chopstick) := case
      (state = pickup) & (left_chopstick = 1) & (right_chopstick = 0): 2;
      (state = putdown) & (right_chopstick = 2): 0;
      TRUE : right_chopstick;
    esac;

    next(ALTRUIST) := case
      (DEADLOCK) & (ALTRUIST = ID): (ALTRUIST mod 5) + 1;
      TRUE : ALTRUIST;
    esac;

FAIRNESS  running

The code, even for someone who doesn't know NuSMV, should be pretty self-explanatory.

The rest of the model is presented here:

MODULE main

  VAR
    chopstick1 : 0..2;
    chopstick2 : 0..2;
    chopstick3 : 0..2;
    chopstick4 : 0..2;
    chopstick5 : 0..2;

    ph1 : process philosopher(1, chopstick1, chopstick2, ALTRUIST, DEADLOCK);
    ph2 : process philosopher(2, chopstick2, chopstick3, ALTRUIST, DEADLOCK);
    ph3 : process philosopher(3, chopstick3, chopstick4, ALTRUIST, DEADLOCK);
    ph4 : process philosopher(4, chopstick4, chopstick5, ALTRUIST, DEADLOCK);
    ph5 : process philosopher(5, chopstick5, chopstick1, ALTRUIST, DEADLOCK);

    ALTRUIST : 1..5;

  ASSIGN
    init(chopstick1) := 0;
    init(chopstick2) := 0;
    init(chopstick3) := 0;
    init(chopstick4) := 0;
    init(chopstick5) := 0;

  DEFINE
    DEADLOCK :=
      chopstick1 = 1 &
      chopstick2 = 1 &
      chopstick3 = 1 &
      chopstick4 = 1 &
      chopstick5 = 1;

    some_eating :=
      ph1.state = eat |
      ph2.state = eat |
      ph3.state = eat |
      ph4.state = eat |
      ph5.state = eat ;

    adjacent_eating :=
      (ph1.state = eat & ph2.state = eat) |
      (ph2.state = eat & ph3.state = eat) |
      (ph3.state = eat & ph4.state = eat) |
      (ph4.state = eat & ph5.state = eat) |
      (ph5.state = eat & ph1.state = eat) ;

    all_pickup :=
      ph1.state = pickup &
      ph2.state = pickup &
      ph3.state = pickup &
      ph4.state = pickup &
      ph5.state = pickup ;

    number_eating :=
      toint(ph1.state = eat) +
      toint(ph2.state = eat) +
      toint(ph3.state = eat) +
      toint(ph4.state = eat) +
      toint(ph5.state = eat) ;


-- Never two neighboring philosophers eat at the same time.
LTLSPEC G !adjacent_eating

-- No more than two philosophers can eat at the same time.
LTLSPEC G (number_eating <=2)

-- Somebody eats infinitely often.
LTLSPEC G F some_eating

-- If every philosopher holds his left fork, sooner or later somebody will get the opportunity to eat.
LTLSPEC G (all_pickup -> F some_eating)
LTLSPEC  ! ( F G all_pickup )
Patrick Trentin
  • 7,126
  • 3
  • 23
  • 40
  • Can you provide a reference for "Having just one altruistic philosopher is sufficient in order to avoid deadlock"? I've never heard of this before in the context of the dining philospher's problem, [it isn't listed on Wikipedia as a recognised solution](https://en.wikipedia.org/wiki/Dining_philosophers_problem), and Google isn't helping either. – Akshat Mahajan Jul 13 '16 at 22:25
  • 2
    Well, it's a pretty naive solution I would say, since deadlock arises when every philopher holds one fork and no one can grab the other fork to eat, having just one philopher giving up his own fork unlocks another philopher which will be able to eat, put down both of his forks and thus unblock another philosopher and so on. If you look at the teaching material of the fifth lesson in NuSMV at the following [link](http://www.patricktrentin.com/pages/fm2016.html), you will find that the proposed solution asks to formally verify a property equi-satisfiable to the absence of deadlock. – Patrick Trentin Jul 13 '16 at 22:38
  • Note: *"equi-satisfiable"* is a too-strong (and improper) wording here, let's say that the last property that is formally verified is a sufficient condition to guarantee absence of deadlock. Notice that the [following link](http://www.patricktrentin.com/teaching/fm2016/lesson11/exercises11.tar.gz) contains the full source code of the above solution, and the aforementioned property can be formally proved to be satisfied by the proposed model. – Patrick Trentin Jul 14 '16 at 09:57