Peterson's Algorithm

后端 未结 1 1782
甜味超标
甜味超标 2021-01-07 09:40

In the classic Peterson\'s algorithm, you check for 2 flags flag1 and flag2 and the turn variable before entering a critical section.Will this work if I check for turn first

1条回答
  •  不知归路
    2021-01-07 10:34

    Yes, it will work, if you first check turn and then check flag[0] or flag[1] inside the condition in while().

    The reason is that busy waiting is performed only when both conditions are true.

    As a proof I've written a small C program simulating two processes with random switches between them.

    For the critical section I use this piece of code in process 0:

    global ^= 0x5555;
    global ^= 0x5555;
    global++;
    

    and this in process 1:

    global ^= 0xAAAA;
    global ^= 0xAAAA;
    global++;
    

    Both processes execute this section 1000 times each. If there's a race condition between the critical sections of the two, global will likely be different from 2000 at the end of the simulation.

    Code:

    #include 
    #include 
    #include 
    
    typedef enum
    {
      InstrNop,
      InstrHalt,
      InstrSetVarNum,
      InstrJumpVarZero,
      InstrJumpVarNonzero,
      InstrJump,
      InstrIncVar,
      InstrDecVar,
      InstrXorVarNum,
    } tInstr;
    
    int ExecuteInstruction(unsigned* Vars, const unsigned* Program, unsigned* Position)
    {
      switch (Program[*Position])
      {
      default:
      case InstrHalt:
        return 0;
    
      case InstrNop:
        (*Position)++;
        break;
    
      case InstrSetVarNum:
        Vars[Program[*Position + 1]] = Program[*Position + 2];
        (*Position) += 3;
        break;
    
      case InstrXorVarNum:
        Vars[Program[*Position + 1]] ^= Program[*Position + 2];
        (*Position) += 3;
        break;
    
      case InstrJumpVarZero:
        if (Vars[Program[*Position + 1]] == 0)
          (*Position) = Program[*Position + 2];
        else
          (*Position) += 3;
        break;
    
      case InstrJumpVarNonzero:
        if (Vars[Program[*Position + 1]] != 0)
          (*Position) = Program[*Position + 2];
        else
          (*Position) += 3;
        break;
    
      case InstrJump:
        (*Position) = Program[*Position + 1];
        break;
    
      case InstrIncVar:
        Vars[Program[*Position + 1]]++;
        (*Position) += 2;
        break;
    
      case InstrDecVar:
        Vars[Program[*Position + 1]]--;
        (*Position) += 2;
        break;
      }
    
      return 1;
    }
    
    typedef enum
    {
      VarGlobal,
    
      VarCnt0,
      VarCnt1,
    
      VarFlag0,
      VarFlag1,
      VarTurn,
    
      VarIdxMax
    } tVarIdx;
    
    unsigned Vars[VarIdxMax];
    
    #define USE_PETERSON 01
    #define SWAP_CHECKS 01
    
    const unsigned Program0[] =
    {
      // cnt0 = 1000;
      InstrSetVarNum, VarCnt0, 1000,
    
    // 3:
    #if USE_PETERSON
      // flag[0] = 1;
      InstrSetVarNum, VarFlag0, 1,
      // turn = 1;
      InstrSetVarNum, VarTurn, 1,
    // 9:
      // while (flag[1] == 1 && turn == 1) {}
    #if SWAP_CHECKS
      InstrJumpVarZero, VarTurn, 17,
      InstrJumpVarZero, VarFlag1, 17,
    #else
      InstrJumpVarZero, VarFlag1, 17,
      InstrJumpVarZero, VarTurn, 17,
    #endif
      InstrJump, 9,
    // 17:
    #endif
    
    // Critical section starts
      // global ^= 0x5555;
      // global ^= 0x5555;
      // global++;
      InstrXorVarNum, VarGlobal, 0x5555,
      InstrXorVarNum, VarGlobal, 0x5555,
      InstrIncVar, VarGlobal,
    // Critical section ends
    
    #if USE_PETERSON
      // flag[0] = 0;
      InstrSetVarNum, VarFlag0, 0,
    #endif
    
      // cnt0--;
      InstrDecVar, VarCnt0,
      // if (cnt0 != 0) goto 3;
      InstrJumpVarNonzero, VarCnt0, 3,
    
      // end
      InstrHalt
    };
    
    const unsigned Program1[] =
    {
      // cnt1 = 1000;
      InstrSetVarNum, VarCnt1, 1000,
    
    // 3:
    #if USE_PETERSON
      // flag[1] = 1;
      InstrSetVarNum, VarFlag1, 1,
      // turn = 0;
      InstrSetVarNum, VarTurn, 0,
    // 9:
      // while (flag[0] == 1 && turn == 0) {}
    #if SWAP_CHECKS
      InstrJumpVarNonzero, VarTurn, 17,
      InstrJumpVarZero, VarFlag0, 17,
    #else
      InstrJumpVarZero, VarFlag0, 17,
      InstrJumpVarNonzero, VarTurn, 17,
    #endif
      InstrJump, 9,
    // 17:
    #endif
    
    // Critical section starts
      // global ^= 0xAAAA;
      // global ^= 0xAAAA;
      // global++;
      InstrXorVarNum, VarGlobal, 0xAAAA,
      InstrXorVarNum, VarGlobal, 0xAAAA,
      InstrIncVar, VarGlobal,
    // Critical section ends
    
    #if USE_PETERSON
      // flag[1] = 0;
      InstrSetVarNum, VarFlag1, 0,
    #endif
    
      // cnt1--;
      InstrDecVar, VarCnt1,
      // if (cnt1 != 0) goto 3;
      InstrJumpVarNonzero, VarCnt1, 3,
    
      // end
      InstrHalt
    };
    
    void Simulate(void)
    {
      unsigned pos0 = 0, pos1 = 0;
    
      while (Program0[pos0] != InstrHalt ||
             Program1[pos1] != InstrHalt)
      {
        int cnt;
    
        cnt = rand() % 50;
        while (cnt--)
          if (!ExecuteInstruction(Vars, Program0, &pos0))
            break;
    
        cnt = rand() % 50;
        while (cnt--)
          if (!ExecuteInstruction(Vars, Program1, &pos1))
            break;
      }
    }
    
    int main(void)
    {
      srand(time(NULL));
      Simulate();
      printf("VarGlobal = %u\n", Vars[VarGlobal]);
      return 0;
    }
    

    Output (ideone):

    VarGlobal = 2000
    

    Now, the same program with the order of the checks as in Wikipedia, for which I define SWAP_CHECKSas 0: output (ideone):

    VarGlobal = 2000
    

    Finally, to show that there's a race condition when Peterson's algorithm is disabled, I define USE_PETERSON as 0: output (ideone):

    VarGlobal = 1610
    

    0 讨论(0)
提交回复
热议问题