why the implementatoin of Bakery algorithm in ANSI C does not work in ANSI C

I follow the description of wiki (Lamport's bakery algorithm - Wikipedia, the free encyclopedia), then implement that algorithm in C, but it doesn't work, Starving is still here, is the implementation worry?

Only print out:

Thread ID: 0 START!
Thread ID: 0 END!
Thread ID: 0 START!
Thread ID: 0 END!
.....
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define COUNT 2                                 /* Number of thread. */
static int Entering[ COUNT ], Number[ COUNT ];  /* Global variables for Lamport's bakery algorithm. */

int max ( int* arr ) {
    int i, max = arr[ 0 ];
    for ( i = 1 ; i < COUNT ; i++ )
        if ( Number[ i ] > max )
            return Number[ i ];
    return Number[ 0 ];
}


static void* T1 ( void* args ) {
    pthread_detach( pthread_self() );                   /* Guarantees that thread resources are deallocated upon return. */
    int i = 0, j;
    while ( 1 ) {
        /* Request to enter critical section. */
        Entering[ i ] = 1;
        Number[ i ] = 1 + max( Number );
        Entering[ i ] = 0;
        for ( j = 0 ; j < COUNT ; j++ ) {
            while ( Entering[ j ] ) {}
            while ( Number[ j ] != 0 && ( Number[ j ] <  Number[ i ] || ( Number[ j ] == Number[ i ] && j < i ) ) ) {}
        }

        /* Critical section. */
        printf( "Thread ID: %d START!\n", i );
        for ( j = 0 ; j < 0xFFFFFF ; j++ ) {}
        printf( "Thread ID: %d END!\n", i );

        /* End of critical section. */
        Number[ i ] = 0;
    }
    return NULL;
}

static void* T2 ( void* args ) {
    pthread_detach( pthread_self() );                   /* Guarantees that thread resources are deallocated upon return. */
    int i = 1, j;
    while ( 1 ) {
        /* Request to enter critical section. */
        Entering[ i ] = 1;
        Number[ i ] = 1 + max( Number );
        Entering[ i ] = 0;
        for ( j = 0 ; j < COUNT ; j++ ) {
            while ( Entering[ j ] ) {}
            while ( Number[ j ] != 0 && ( Number[ j ] <  Number[ i ] || ( Number[ j ] == Number[ i ] && j < i ) ) ) {}
        }

        /* Critical section. */
        printf( "Thread ID: %d START!\n", i );
        for ( j = 0 ; j < 0xFFFFFF ; j++ ) {}
        printf( "Thread ID: %d END!\n", i );

        /* End of critical section. */
        Number[ i ] = 0;
    }
    return NULL;
}

int main () {
    int i = 0;
    pthread_t t1;

    /* Initial Bakery algorithm. */
    for ( i = 0 ; i < COUNT ; i++ )
        Number[ i ] = Entering[ i ] = 0;

    /* Thread Creation. */
    if ( pthread_create( &t1, NULL, T1, NULL ) )
        exit( -1 );
    if ( pthread_create( &t1, NULL, T2, NULL ) )
        exit( -1 );

    /* Loop forever. */
    while ( 1 ) {}
    return EXIT_SUCCESS;
}

The pseudocode of Lamport's bakery algorithm

    // declaration and initial values of global variables
    Entering: array [1..NUM_THREADS] of bool = {false};
    Number: array [1..NUM_THREADS] of integer = {0};
    
 1  lock(integer i) {
 2      Entering = true;
 3      Number = 1 + max(Number[1], ..., Number[NUM_THREADS]);
 4      Entering = false;
 5      for (j = 1; j <= NUM_THREADS; j++) {
 6          // Wait until thread j receives its number:
 7          while (Entering[j]) { /* nothing */ }
 8          // Wait until all threads with smaller numbers or with the same
 9          // number, but with higher priority, finish their work:
10          while ((Number[j] != 0) && ((Number[j], j) < (Number, i))) {
11              /* nothing */
12          }
13      }
14  }
15
16  unlock(integer i) {
17      Number = 0;
18  }
19
20  Thread(integer i) {
21      while (true) {
22          lock(i);
23          // The critical section goes here...
24          unlock(i);
25          // non-critical section...
26      }
27  }

and

(a, b) < (c, d)

which is equivalent to:

(a < c) or ((a == c) and (b < d)) 

Thanks:)

A few problems with writing it in C.

1) You're assuming variables are atomic. Sometimes they're not.

2) You're assuming different threads have access to the same memory/cache. In multicore systems, this isn't always true, "memory barriers" are needed to force cores to be consistent with each other when necessary.

3) The compiler makes certain assumptions about memory. Unless you tell it otherwise, it will assume a variable is not "magic" and won't change mysteriously when its not looking (which is precisely what you're doing with threads). You have to make a variable volatile(i.e. "volatile int i") if you're going to have threads competing over it this way.

So, writing the algorithm in raw ANSI C isn't recommended. You might be able to make this work in raw C on a single-core system, maybe, if you make the shared variables volatile. I think there's also some extensions in GNU gcc for explicit atomic operations.

1 Like

Thanks, I have solved my problem and I am reading the Atomic Builtins of GCC, I just post these links here, for those people who may have same problem.

Atomic Builtins - Using the GNU Compiler Collection (GCC)
Techie Stuff Atomic Operations