Lab 5: Solution with one mutex?

In the solution we present in class, we use two mutex variables. Many of the solutions submitted use one mutex only. A question raised during discussion with a student whether any solution with one mutex is wrong.

I will post some of the solutions submitted by you, and let you take a look and discuss. I think debugging threads is not easy and is a skill that comes with experience. So hopefully this little exercise will serve as a practice for you.

First, I will start off with the correct solution (labelled A) using two mutex variables. The mutex s ensures that only one savage waits at one time.

VERSION A

mutex s, m
cond filled, empty

savage:
  while (true)
    lock(s)
    lock(m)
    if (pot is empty)
      signal(empty)
      wait(filled, m)
    eat()
    unlock(m)
    unlock(s)

cook:
  while (true)
    lock(m)
    if (pot is not empty)
       wait(empty, m)
    cook()
    signal(filled)
    unlock(m)

Before we move on to implementations that uses one mutex, here is a slight variable of the above. Here, locking and unlocking of m is done inside the if statement. Is it correct?

VERSION B

mutex s, m
cond filled, empty

savage:
  while (true)
    lock(s)
    if (pot is empty)
      lock(m)  < --
      signal(empty)
      wait(filled, m)
      unlock(m) <--
    eat()
    unlock(s)

cook:
  while (true)
    lock(m)
    if (pot is not empty)
       wait(empty, m)
    cook()
    signal(filled)
    unlock(m)

Now, here are the four versions of solutions that uses only one mutex.

Version C removes the use of mutex s.

VERSION C

mutex m
cond filled, empty

savage:
  while (true)
    lock(m)
    if (pot is empty)
      signal(empty)
      wait(filled, m)
    eat()
    unlock(m)

cook:
  while (true)
    lock(m)
    if (pot is not empty)
       wait(empty, m)
    cook()
    signal(filled)
    unlock(m)

Version D replaces signal(filled) with broadcast(filled) in the cook thread.

VERSION D

mutex m
cond filled, empty

savage:
  while (true)
    lock(m)
    if (pot is empty)
      signal(empty)
      wait(filled, m)
    eat()
    unlock(m)

cook:
  while (true)
    lock(m)
    if (pot is not empty)
       wait(empty, m)
    cook()
    broadcast(filled) < --
    unlock(m)

Version E added a signal(filled) after eat() to VERSION C.

VERSION E

mutex m
cond filled, empty

savage:
  while (true)
    lock(m)
    if (pot is empty)
      signal(empty)
      wait(filled, m)
    eat()
    signal(filled) < --
    unlock(m)

cook:
  while (true)
    lock(m)
    if (pot is not empty)
       wait(empty, m)
    cook()
    signal(filled)
    unlock(m)

Version F changes the if statement to while statement.

VERSION F

mutex m
cond filled, empty

savage:
  while (true)
    lock(m)
    while  (pot is empty)   < --
      signal(empty)
      wait(filled, m)
    eat()
    signal(filled)
    unlock(m)

cook:
  while (true)
    lock(m)
    if (pot is not empty)
       wait(empty, m)
    cook()
    signal(filled)
    unlock(m)