Junikin
Junikin

Reputation: 301

Semaphores as a diamond

My homework was to perform waits and signals on an array of semaphores in a diamond pattern.

Here is the pattern:

    08
  06  07  
03  04  05
  01  02
    00

Thread #08 cannot join the diamond until threads #06 and #07 are both in position.
Thread #07 cannot join until threads #04 and #05 are both in position.
Thread #06 cannot join until threads #03 and #04 are both in position. and so on...

So far my thinking has resulted in the following ugly algorithm:

   if (me==0) {
       wait_sem (tCount[1]) ;
       wait_sem (tCount[2]) ;
       signal_sem (tCount[0]) ;
   }
   if (me==1) {
       wait_sem (tCount[3]) ;
       wait_sem (tCount[4]) ;
   }
   if (me==2) {
       wait_sem (tCount[4]) ;
       wait_sem (tCount[5]) ;
   }
   if (me==3) {
       wait_sem (tCount[6]) ;
   }
   if (me==4) {
       wait_sem (tCount[6]) ;
       wait_sem (tCount[7]) ;
   }
   if (me==5) {
       wait_sem (tCount[7]) ;
   }
   if (me==6) {
       wait_sem (tCount[8]) ;
   }
   if (me==7) {
       wait_sem (tCount[8]) ;
   }

Is there a cleaner method of doing this? I heard switch but I have never used it before so if anyone suggests it please provide me with an explanation and or example. Thanks a lot for all input.

Upvotes: 3

Views: 121

Answers (2)

cpatricio
cpatricio

Reputation: 457

This solution is only to show how it could be done with case, there are better implementations for this problem!

Switch is better than if's/else because it works as a jump, when if's will need to test all conditions until its satisfied (if0, if1, if2...), the switch will directly to the right case.

switch(me)
{
    case 0:
        wait_sem(tCount[1]);
        wait_sem(tCount[2]);
        signal_sem(tCount[me]);
        break;
    case 1:
        wait_sem(tCount[3]) ;
        wait_sem(tCount[4]) ;
        signal_sem(tCount[me]);
        break;
    case 2:
        wait_sem(tCount[4]);
        wait_sem(tCount[5]);
        signal_sem(tCount[me]);
        break;
    case 3:
        wait_sem(tCount[6]);
        signal_sem(tCount[me]);
        break;
    case 4:
       wait_sem(tCount[6]);
       wait_sem(tCount[7]);
       signal_sem(tCount[me]);
       break;
    case 5:
       wait_sem(tCount[7]);
       signal_sem(tCount[me]);
       break;
    case 6:
       wait_sem(tCount[8]);
       signal_sem(tCount[me]);
       break;
    case 7:
       wait_sem(tCount[8]);
       signal_sem(tCount[me]);
       break;
    case 8:
       signal_sem(tCount[me]);
       break;
}

Upvotes: 1

paddy
paddy

Reputation: 63481

Let's just take a really simple approach, where you keep your semaphores in a global array (or simply just accessible to your threads). You can set up a list of dependencies like this:

std::vector<std::vector<int>> thread_depends = {
    { },      // 0
    { 0 },    // 1
    { 0 },    // 2
    { 1 },    // 3
    { 1, 2 }, // 4
    { 2 },    // 5
    { 3, 4 }, // 6
    { 4, 5 }, // 7
    { 6, 7 }, // 8
};

Now, each thread just needs to wait on everything in thread_depends[me]:

const auto & dep = thread_depends[me];
std::for_each( dep.begin(), dep.end(), [&tCount](int who){ wait_sem( tCount[who] ); } );
signal_sem( tCount[me] );

The benefit of this approach is that you don't duplicate the logic of what to do for each case. Instead, you're just representing the dependencies, and you only have one piece of code that does the actual work. This means less chance of making a copy-paste mistake.

Upvotes: 3

Related Questions