Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lock only one of two possible mutexes

I have a multi threaded C++ program which simulates a car workshop. Basically a car is a thread here and a station is a resource. It works like this: Car enters a workshop and it has a list of stations (just ints), that it has to visit, to get repaired. There are 3 types of stations:

  • 1x2 station - 1 station can repair 2 cars at once
  • 1x1 station - 1 station can repair 1 car at once
  • 2x1 station - there are 2 stations needed to get the job done

The last two types would be easy for me, cuz in the 1x1 type I just lock the mutex on station and other threads have to wait. On the 2x1 type I just go for std::lock on two stations to avoid deadlock etc.

The problem is with the first type. Lets imagine that repairing two cars at once means that one car is on the left hand side of the station and the other one on the right hand side (I will have to draw this situation with ncurses as well). So I thought about implementing a station for 1x2 type like this:

class station1x2 {
public:
    std::mutex r_mutex;
    std::mutex l_mutex;
}

So I would like to lock either the r_mutex or the l_mutex, so there are 4 possible scenarios:

  • both of them are not locked, I lock whichever of them
  • right one is locked, I lock the left one
  • left one is locked, I lock the right one
  • both of them are locked, I wait

The problem here is: Is there in C++ a mechanism to lock just one of given mutexes? (like I give some function my r_mutex and l_mutex and it chooses the not locked one and locks it for me).

like image 478
minecraftplayer1234 Avatar asked Jun 06 '18 07:06

minecraftplayer1234


2 Answers

A mutex is not the correct synchronisation primitive here. This could be done with a semaphore (basically a 0-n primitive where a mutex is 0-1), but there is no semaphore in the standard library.

There is a condition variable, however, which you can use here. You will need:

  • A condition variable to signal "there's free space in the station"
  • A counter (or other means, such as two booleans) representing free spaces in the station
  • A mutex to protect these

When entering the station, lock the mutex and see if there's any free space. If there is, occupy one, release the mutex, and get repairs. If both are full, wait on the condition variable (this will release the mutex).

When done with repairs, lock the mutex, mark your space as free, release the mutex and notify the condition variable (since there's free space now).

In code:

class station1x2 {
public:
    std::mutex mutex;
    std::condition_variable cond;
    int freeSpaces;

    void enter() {
      std::unique_lock<std::mutex> l(mutex);
      cond.wait(l, [&]() { return freeSpaces > 0; }
      --freeSpaces;
    }

    void exit() {
      {
        std::unique_lock<std::mutex> l(mutex);
        ++freeSpaces;
      }
      cond.notify_one();
    }
}
like image 149
Angew is no longer proud of SO Avatar answered Nov 07 '22 18:11

Angew is no longer proud of SO


In your case, I would use the try_lock method. This method returns true (and locks the mutex) if the lock is possible, false otherwise (already locked mutex).

if (!r_mutex.try_lock() && !l_mutex.try_lock())
  std::cout << "All mutexes already locked" << std::endl;
like image 36
SegFault Avatar answered Nov 07 '22 19:11

SegFault