2

I have two threads - threadA & threadB. If B is waiting for mutex, which is owned by A, Will it get the ownership immediately after A unlocks it, assuming it has higher priority than A ?
This not a question on who gets the lock when multiple threads are waiting, but if a single waiting thread becomes runnable & gets the processor or not.

From the test example below it doesn't seem to happen always. Can someone please clarify ?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//global variables
/* START = THREAD A runs, STOP = THREAD A Stops & Waits */
volatile enum { START, STOP } state = START;
pthread_cond_t      condA  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;

void *threadA()
{
int i = 0;
int j = 0;

struct sched_param p;
p.sched_priority                        =       16;
pthread_setschedparam(pthread_self(), SCHED_FIFO, &p);

printf("threadA created\n");
while(1)
{
    printf("threadA lock requested  \n");
    pthread_mutex_lock(&mutex);
    while (state == STOP)
    {
        printf("Waiting in STOP state, until some one sends START again\n");
        pthread_cond_wait(&condA, &mutex);
    }
    printf("threadA locked mutex  \n");
    //do stuff - ~a few ms of work, simulated with dummy for loop
    for(j=0; j<=100000000; j++)
        ;
    i++;
    printf("threadA loop cntr %d\n",i);
    //printf("threadA   unlock requested  \n");
    pthread_mutex_unlock(&mutex);
    printf("threadA unlocked mutex  \n");
}
fflush(stdout);
return 0;
}

void *threadB()
{

struct sched_param p;
p.sched_priority                        =       17;
pthread_setschedparam(pthread_self(), SCHED_FIFO, &p);

printf("threadB created\n");
do
{
    /* Time to stop threadA */
    printf("threadB lock requested  \n");
    pthread_mutex_lock(&mutex);
    printf("threadB locked mutex  \n");
    state = STOP;
    pthread_cond_signal(&condA);
    //printf("threadB unlock requested  \n");
    pthread_mutex_unlock(&mutex);
    //printf("threadB unlocked mutex  \n");
}while(0);
fflush(stdout);
return 0;
}

int main(int argc, char *argv[])
{
int j = 0;

//create our threads
pthread_t a, b;

pthread_create(&a, NULL, threadA, NULL);
/* Wait for a while to make sure A is 
   up & running before stopping it */
for(j=0; j<=100000; j++)
    ;
// Now stop A
pthread_create(&b, NULL, threadB, NULL);

fflush(stdout);


pthread_join(a, NULL);
pthread_join(b,NULL);
}

Typical output I see is as below..
threadA created
threadA lock requested
threadA locked mutex
threadB created
threadB lock requested
threadA loop cntr 1
threadA unlocked mutex << A unlocked it, so a waiting B should receive it here??
threadA lock requested
threadA locked mutex
threadA loop cntr 2
threadB locked mutex << B granted ownership finally !!
threadA unlocked mutex
threadA lock requested
Waiting in STOP state, until some one sends START again

Hari
  • 153
  • 8
  • Possible duplicate of [What is the \`pthread\_mutex\_lock()\` wake order with multiple threads waiting?](http://stackoverflow.com/questions/14947191/what-is-the-pthread-mutex-lock-wake-order-with-multiple-threads-waiting) – kaylum Feb 23 '17 at 23:26
  • "After" isn't really a well-defined notion. – Kerrek SB Feb 23 '17 at 23:31
  • Have you checked that your `pthread_setschedparam()` calls are not returning an error? – caf Feb 24 '17 at 00:07
  • Yes, pthread_setschedparam() calls are successfull. I am running it as sudo – Hari Feb 24 '17 at 00:21
  • Running a program via `sudo` does not guarantee that the calls are successful. You can only check that they're successful by capturing the return value and handling non-zero values as errors. – Jonathan Leffler Feb 24 '17 at 00:46
  • I do see your point. But I have verified by printing that return values are zero, so the schedparam() calls are successfull. – Hari Feb 24 '17 at 00:48

1 Answers1

5

Simply put: no, you can not rely in any way on which waiting thread will get a mutex, or even that a waiting thread will be woken up before another thread that wasn't waiting for the mutex happens to request and get the lock.

In fact, the OS scheduler might even be more likely to allow an already-running thread to continue to run and obtain the lock.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • I would've expected after mutex_unlock(), B might be in list of runnable threads & as it has higher priority than A, A should be preempted to make way for B. If this is not the guaranteed behaviour, what is a good way to achieve the same ? – Hari Feb 23 '17 at 23:33
  • @Hari Even if B is at a higher priority than A, A is already running, so the scheduler may very well elect to avoid the overhead of two context switches to preempt A and start running B. As you've seen. – Andrew Henle Feb 23 '17 at 23:41
  • I thought the whole point of priority is to preempt the low priority process ! If minimizing context switch is the goal, how sched_priority is useful or relevant ? – Hari Feb 23 '17 at 23:45
  • From the man page, "The pthread_mutex_unlock() function shall release the mutex object referenced by mutex. The manner in which a mutex is released is dependent upon the mutex's type attribute. If there are threads blocked on the mutex object referenced by mutex when pthread_mutex_unlock() is called, resulting in the mutex becoming available, **the scheduling policy shall determine which thread shall acquire the mutex.**." So B should get the mutex in above example ? – Hari Feb 24 '17 at 00:02
  • 1
    "the scheduling policy shall determine". How did you get "B should get the mutex" from that. The man page clearly says it is up to the scheduling policy to decide. Linux does not do a context switch upon unlock. So it stays with the current thread which then successfully reacquires the lock. You are trying to interpret the behaviour based on an incorrect understanding of how Linux scheduling works. – kaylum Feb 24 '17 at 01:16
  • Because only thread B is waiting for mutex at this point, it shoud've received the ownership. Even if there is no context switch, threadA later tries to lock mutex, gets blocked, yielding the processor to threadB - or at least that's my understanding. – Hari Feb 24 '17 at 01:33
  • @Hari The output you posted isn't even sufficient to establish that thread B actually attempted to lock the mutex before thread A. All you know is the output from the B's `printf()` statement appeared on your terminal before the output from A's `printf()` statements. As another poster commented earlier: *"After" isn't really a well-defined notion.* If you want an explicit order of thread processing, you can either provide code that guarantees the order you want through explicit synchronization objects such as mutexes, condition variables, or semaphores; or you can continue to be surprised. – Andrew Henle Feb 24 '17 at 03:21
  • if i'm yielding from thread expicitly, can i give guaranties, that lock will not come back to the thread.? – Volodymyr Boiko Jun 18 '17 at 09:10