1

I have a sample code of futex. But i couldnt understand the code flow....

#include <stdio.h>
#include <pthread.h>
#include <linux/futex.h>
#include <syscall.h>
#include <unistd.h>

#define NUM 50

int futex_addr;

int futex_wait(void* addr, int val1){
  return syscall(SYS_futex,&futex_addr,val1, NULL, NULL, 0);
}
int futex_wake(void* addr, int n){
  return syscall(SYS_futex, addr, FUTEX_WAKE, n, NULL, NULL, 0);
}

void* thread_f(void* par){
        int id = (int) par;

    /*go to sleep*/
    futex_addr = 0;
    futex_wait(&futex_addr,0);

        printf("Thread %d starting to work!\n",id);
        return NULL;
}

int main(){
        pthread_t threads[NUM];
        int i;

        for (i=0;i<NUM;i++){
                pthread_create(&threads[i],NULL,thread_f,(void *)i);
        }

        printf("Everyone wait...\n");
        sleep(1);
        printf("Now go!\n");
    /*wake threads*/
    futex_wake(&futex_addr,50);

    /*give the threads time to complete their tasks*/
        sleep(1);


    printf("Main is quitting...\n");
        return 0;
}

the output comes like this :

Everyone wait...
Now go!
Thread 0 starting to work!
Thread 1 starting to work!
Thread 2 starting to work!
Thread 3 starting to work!
Thread 4 starting to work!
Thread 5 starting to work!
Thread 6 starting to work!
Thread 7 starting to work
Thread 8 starting to work!
Thread 9 starting to work!
.
.
Main is quitting

How does actually this code is behaving??

what is the trigger for thread_f functions??

How does wait & wake working here??

Prashanth Cm
  • 151
  • 2
  • 4
  • 8

1 Answers1

4
  1. You create 50 threads and put the main thread to sleep.
  2. Within each thread, you set the value of futex_addr to zero (redundantly).
  3. You call futex_wait with the address of that value, and a value parameter of zero. That means "block, if the value I point to is (still) really zero".
  4. sys_futex checks that the value at &futex_addr is really zero, which is the condition for blocking your thread (that is important for the correct operability of the syscall, otherwise futex_wake would have to block alike NtReleaseKeyedEvent under Windows). Of course the value is zero, that's all any thread has ever written to it, so your thread blocks.
  5. The main thread eventually returns from sleep and calls futex_wake with a parameter of 50, which means "wake (up to) 50 threads that are waiting on &futex_addr". So all your 50 threads wake up in one big thundering herd.
  6. Each thread writes a message to stdout (without synchronization, so you might see garbled output) and exits.
  7. The main thread exits as well, without joining threads or synchronizing, but due to the second call to sleep, chances are that it will "work fine" without evil things happening, anyway (not that it's a good thing to write code like this!).

Note that this method of waking N threads works, but is discouraged (much like using futex in the first place is discouraged, too). Typically you want to wake exactly one thread (use 1), or all threads (use INT_MAX).

Damon
  • 67,688
  • 20
  • 135
  • 185
  • I mean to say first "Everyone wait..." gets printed later "Thread %d starting to work!\n" gets printed. Surprisingly thread_f call is before "Everyone wait..." print.... – Prashanth Cm Jul 01 '14 at 05:05
  • The trigger for `thread_f` is `pthread_create`, which is subject to a) the main thread being scheduled b) a dozen launching threads competing with the main thread for CPU time c) "random stuff" and d) non-synchronized use of stdio functions, which can give unpredictable output. It is not specified which thread is scheduled first after `clone` (which is what happens inside `pthread_create`). Most likely they'll start in the order the main thread created them, but something different _could_ happen. Similar is true for `printf` and `write` (which is internally called). They're not atomic. – Damon Jul 01 '14 at 08:43
  • 1
    So, the output that you see is not very well-defined. You could see thread 4 output "Everyone wait" before thread 1 outputs "Starting", and you could even _see_ the output of thread 6 first, even though thread 1 is scheduled first (and interrupted again for some reason). Or, you could see half-complete messages, or complete garbage, even that is possible. – Damon Jul 01 '14 at 08:45