3
\$\begingroup\$

I want to implement simplest mutex without built-in functions or the <pthread.h> module.

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h> // strcpy
#include <unistd.h> // usleep

#define MICROS (1)
#define THREADS (300)
#define ACCESSES (1000000)

char * names[] = {"alpha",   "bravo",    "charlie", "delta", 
                  "echo",    "foxtrot",  "golf",    "hotel", 
                  "india",   "juliet",   "kilo",    "lima",
                  "mike",    "november", "oscar",   "papa", 
                  "quebec",  "romeo",    "sierra",  "tango", 
                  "uniform", "victor",   "whiskey", "x-ray", 
                  "yankee",  "zulu"};

void mutex_lock();
void mutex_unlock();

int mx = 0;
int counter = 0;
char pool[128] = {0};
pthread_t threads[THREADS];

void fill_pool(int number)
{
    int i;
    counter++;
    for (i = 0; i < 10000; i++)
        strcpy(pool, names[number % 26]);
}

void show_pool(int number)
{
    printf("%04d - %04d - %s\n", counter, number, pool);
    if (strcmp(pool, names[number % 26]))
    {
        printf("Thread race detected!\n");
        exit(1);
    }
}

void * thread_fnc(void * index)
{
    int number = (int)index;
    int i;
    for (i = 0; i < ACCESSES; i++)
    {
        usleep(random() % 1000);
        mutex_lock();
        {
            fill_pool(number);
            show_pool(number);
        }
        mutex_unlock();
    }    
}

int atomic_xchg(int * ptr, int val)
{
    unsigned int  tmp = val;
    __asm__(
        "xchgl %0, %1;\n"       
        : "=r"(tmp), "+m"(*ptr)
        : "0"(tmp)              
        :"memory");             
    return tmp;
}

int test_and_set(void)
{
    return atomic_xchg(&mx, 1);
}

void mutex_lock()
{
    while (test_and_set()) // Comment this line
                           // to test race
    {
        usleep(MICROS);
    }
}

void mutex_unlock()
{
    mx = 0;
}

int main()
{
    int i;    

    printf(" ##  - THRD - name\n");    

    for (i = 0; i < THREADS; i++)
        pthread_create(&threads[i], NULL, thread_fnc, (void *)i);

    for (i = 0; i < THREADS; i++) 
        pthread_join(threads[i], NULL);

    return 0;
}

This code works and does not fail after ~1 000 000 iterations. When I use simple while (mx) ... mx = 1 it fails after ~17 000 iterations.

Is this code totally thread-safe? I worry about passing pointer to atomic_xchg, since it needs dereferencing.

To compile:

gcc -pthread ./lock.c -o lock.s -lpthread && ./lock.elf`
\$\endgroup\$
  • \$\begingroup\$ Uh-oh, sorry, it was in my source code originally, looks like I've overwritten it when I was entering 4 spaces in the beginning of each line. \$\endgroup\$ – Alex Tiger Feb 13 '17 at 19:30
  • \$\begingroup\$ Allright. Unfortunately, I don't know inline assembler, which is like the bread and butter of this review. \$\endgroup\$ – Zeta Feb 13 '17 at 19:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.