3

I am facing a sync issue with pthread. threadWaitFunction1, is a thread wait function. I expect line no. 247 flag = 1 to be executed only after 243-246 has finished. But i find it strange that sometimes, it jumps directly to 247 before 243-246 has finished.

Please help me.

Thanks in advance.

236   struct timespec timeToWait;
237   static void* threadWaitFunction1(void *timeToWaitPtr)
238   {
239       cout << "Setting flag =0 inside threadWaitFunction1\n";
240       
241       cout << "Inside threadWaitFunction\n";
242       struct timespec *ptr = (struct timespec*) timeToWaitPtr;
243       pthread_mutex_lock(&timerMutex);
          flag = 0;
244       pthread_cond_timedwait(&timerCond, &timerMutex, ptr);
          flag=1;
245       pthread_mutex_unlock(&timerMutex);
246       cout << "Setting flag =1 inside threadWaitFunction1\n";
247       
248
249    }

The thread which creates and calls the above thread is:

263  static void timer_trackStartTime ()
264  {
265       struct timeval now;
266       pthread_t thread;
267       
268       printf("Inside trackStartTime: flag = %d\n",flag);
269 
270      /* Setting timer expiration */
271       timeToWait.tv_sec = lt_leak_start_sec;;  // First expiry after 1 sec
272       timeToWait.tv_nsec = lt_leak_start_nsec;
273       pthread_create(&thread, NULL, threadWaitFunction1, &timeToWait);
274       pthread_join(thread, NULL);
275       //pthread_kill(thread, SIGKILL); // Destroying the thread to ensure no leaks
276 
.
.
283       }

If i protect the whole function using pthread_mutex_lock, but still the same problem persists. How to ensure orderly execution? Can anyone help?

EDIT: now.tv_sec and now.tv_nsec removed from the code. *EDIT: Changed the flags inside the mutex (still does not work)*

RajSanpui
  • 11,556
  • 32
  • 79
  • 146
  • 2
    This question is unanswerable in its current form because you don't say how you're measuring where/when `flag` changes. Note that changing `flag` while you don't hold a mutex is almost surely bogus, unless this is the only thread with access to `flag`, in which case your question makes no sense. – R.. GitHub STOP HELPING ICE Apr 28 '11 at 12:41
  • @R: The flag is changed, only when the start time: current time + lt_leak_start_sec. I think you have missed the argument passed to the thread function. – RajSanpui Apr 28 '11 at 12:45
  • @R: The conditional mutex is held, till the time specified in the struct is reached. – RajSanpui Apr 28 '11 at 12:46
  • @kingsmasher1: there is nothing preventing the `flag` assignments from spilling into the mutex protected region. – ninjalj Apr 28 '11 at 18:42
  • 4
    Make sure (now.tv_usec * 1000) + lt_leak_start_nsec; doesn't overflow. You can only set tv_nsec to max 999999999, if the expression is larger than that, you should subtract 999999999 from tv_nsec, and increment tv_sec by 1. If your `timeToWaitPtr` contains an invalid tv_nsec (larger than 999999999), pthread_cond_timedwait will fail (you should check its return value too.) – nos Apr 28 '11 at 19:04
  • 1
    @ninjalj: Yes there is. XBD 4.11 (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11) reads: "The following functions synchronize memory with respect to other threads" followed by a list that includes `pthread_mutex_lock` and `pthread_mutex_unlock`. – R.. GitHub STOP HELPING ICE Apr 28 '11 at 20:07

5 Answers5

5

So it is not really execution ordering (which is most probably correct) but timing that makes you unhappy. And under "it jumps directly to 247 before 243-246 has finished" you mean "I observed it executing 247 before the time it should wait in 244 has passed". Right?

Then, I suspect this is the problem of spurious wakeup: a thread might get woken up even though no other thread signalled the condition variable. The specification of pthread_cond_timedwait() says that "Spurious wakeups from the pthread_cond_timedwait() or pthread_cond_wait() functions may occur."

Usually, a condition variable is associated with a certain event in the application, and a thread waiting on a condition variable in fact waits for a signal by another thread that the event of interest has happened. If you have no event and just want to wait for a certain amount of time, indeed other ways, such as usleep() or timers, are more appropriate, except if you also need a pthread cancellation point.

ADDED: Since you seem satisfied with usleep() and only asked why pthread_cond_timedwait() did not work to your expectations, I decided not to post the code. If you need it, you may use the answer of @Hasturkun.


ADDED-2: The output in comments below (obtained after the solution of Hasturkun was applied) suggests that the waiting thread does not exit the loop, which likely means that pthread_cond_timedwait() returns something different than ETIMEDOUT. Have you seen the comment by @nos to your post (I fixed the amount of nanosecs to subtract):

Make sure (now.tv_usec * 1000) + lt_leak_start_nsec; doesn't overflow. You can only set tv_nsec to max 999999999, if the expression is larger than that, you should subtract 1000000000 from tv_nsec, and increment tv_sec by 1. If your timeToWaitPtr contains an invalid tv_nsec (larger than 999999999), pthread_cond_timedwait will fail (you should check its return value too.) – nos Apr 28 at 19:04

In this case, pthread_cond_timedwait() will repeatedly return EINVAL and will never get out of the loop. It is better to adjust the timeout before entering the wait loop, though it can also be done in response to EINVAL.


ADDED-3: Now after you changed the code in your question to pass the timeout without adding to current time, it has another problem. As stated in the spec, the timeout for pthread_cond_timedwait() is absolute time, not relative; so when you pass something like 3 sec as the timeout, it is interpreted as "3 seconds since the reference point for the system time". That moment is almost certainly passed for a while, and so pthread_cond_timedwait() returns immediately.
I would recommend you to read the specification thoroughly (including rationale) to build better understanding of how this function is supposed to be used.

Alexey Kukanov
  • 12,479
  • 2
  • 36
  • 55
  • @Alexey: I am sorry, using the `ETIMEDOUT` too does not work. One out of 5 executions, still gives incorrect output. – RajSanpui May 02 '11 at 06:15
  • `/tmp # ./a.out Constructor called # Measurement starts: 3.45 sec # Measurement ends at end of execution Inside Timer func lt_start_sec = 3 lt_start_nsec = 450000000 Inside trackStartTime: flag = 0 Inside threadWaitFunction new: registerAlloc won't be called as flag=0 new: registerAlloc won't be called as flag=0 new: registerAlloc won't be called as flag=0 new: Leaktracer registerAlloc won't be called as flag=0` (P.S: After 3.45 secs flag should be 1) but as you see all are "0" – RajSanpui May 02 '11 at 06:35
  • This problem happens once or twice in 5 consecutive executions. – RajSanpui May 02 '11 at 06:38
  • @kingsmasher: I updated my answer; supposedly the problem is in invalid timeout value. – Alexey Kukanov May 02 '11 at 07:23
  • @Alexey: Thanks Alexey, there is no chance of overflow. I am using only: lt_leak_start_sec and _nsec. Checking for overflow using `strtol` as these are entered using env variables. – RajSanpui May 02 '11 at 08:37
  • @Alexey: Actually, `now.tv_sec` and `now.tv_usec` is not required for putting the delay. DIrectly we can put the timings. – RajSanpui May 02 '11 at 08:39
  • I am not putting now.tv_sec or nsec. – RajSanpui May 04 '11 at 09:30
  • 1
    @kingsmasher: I updated my answer in response. Basically, you must use absolute time for the timeout parameter of `pthread_cond_timedwait()`. – Alexey Kukanov May 04 '11 at 10:23
3

Paul E. McKenney has written a book titled "Is Parallel Programming Hard, And, If So, What Can You Do About It?", which has really good information (and some nice pictures) on memory barriers.

Back to your question, flag isn't protected by anything. While you may think that pthread_mutex_lock() and pthread_mutex_unlock provides some strong ordering and visibility guarantees, the only guarantees it provides are for accesses inside the critical region and for the mutex itself.

What's more, on some architectures pthread_mutex_lock() uses an acquire barrier and pthread_mutex_unlock() uses a release barrier, which means that accesses before and after the mutex protected region can spill into the mutex protected region. Strong ordering guarantees are provided between a CPU releasing a mutex and another CPU acquiring the same mutex, but pretty much everything else doesn't need (and maybe doesn't get) such strong guarantees.

Edit:

Apparently I was wrong with respect to pthreads, they seem to require full memory barriers (if you interpret synchronize memory with respect to other threads as requiring that). More about this, and some info on the guarantees provided in real-world implementations at Reordering Constraints for Pthread-Style Locks by Hans Boehm.

I'm also still wondering about NPTL on IA64 1, 2.

ninjalj
  • 42,493
  • 9
  • 106
  • 148
  • This is not true. POSIX specifies both `pthread_mutex_lock` and `pthread_mutex_unlock`, among all other synchronization-related functions, as **full memory barriers**. See XBD section 4.11 (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11) for details. If your code can work with just memory barriers and no locks, you are welcome to implement a plain memory barrier as a wrapper around `pthread_mutex_lock` and `pthread_mutex_unlock` on a dummy, local-variable mutex that will never be contended. – R.. GitHub STOP HELPING ICE Apr 28 '11 at 20:09
1

As stated by Alexey Kukanov, the problem is likely spurious wakeup. your code may be corrected to loop until timeout occurs. Note that I also moved the flag setting to be under the mutex.

static void* threadWaitFunction1(void *timeToWaitPtr)
{
    struct timespec *ptr = (struct timespec*) timeToWaitPtr;
    int ret;

    pthread_mutex_lock(&timerMutex);
    cout << "Setting flag =0 inside threadWaitFunction1\n";
    flag=0;
    cout << "Inside threadWaitFunction\n";
    while (pthread_cond_timedwait(&timerCond, &timerMutex, ptr) != ETIMEDOUT)
        ;
    cout << "Setting flag =1 inside threadWaitFunction1\n";
    flag=1;
    pthread_mutex_unlock(&timerMutex);
}

To be on the safe side, you should check the flag under the same mutex to establish ordering

Hasturkun
  • 35,395
  • 6
  • 71
  • 104
  • Unfortunately, it is not that easy. You wait with the same timeout each time, making it possible that the overall waiting period exceeds the timeout. Instead, at each wakeup you need to check how much time has already passed, and adjust the timeout appropriately. And this is much more code :) – Alexey Kukanov May 01 '11 at 17:23
  • No I am incorrect, since `pthread_cond_timedwait()` takes absolute time. Upvoting your answer. – Alexey Kukanov May 01 '11 at 18:53
  • @Hasturkun: Moving the flag inside the `pthread_mutex_lock` has no effect, i had mentioned this in my post, may be u missed :) – RajSanpui May 02 '11 at 05:54
  • @kingsmasher: Why downvoting this and mine answers? It does not seem fair. The key thing is not the flag but the `while` loop around the call to `pthread_cond_timedwait()`, with the result being checked. – Alexey Kukanov May 02 '11 at 06:01
  • @Husturkun: One out of 5 executions, still gives wrong result. – RajSanpui May 02 '11 at 06:13
  • @kingsmasher: are you saying that the desired time still does not pass even if you use the loop till ETIMEDOUT? It's completely unclear why would it not work (assuming that all the rest is correct, i.e. the mutex is correctly initialized etc). FYI I "edited" my answer. – Alexey Kukanov May 02 '11 at 06:15
  • @Hasturkun: Please edit your answer, so that i can remove the down vote, although your solution does not work (see execution results i gave at Alexey), but still the loop may be used for some future ideas, so downvoting isn't fair i guess (if not upvoting atleast). – RajSanpui May 02 '11 at 07:04
0

This could be because the compiler has optimized things and put your assignment to your flag before the thread mutex. If you want to guarantee order of execution, (something which is not normally guaranteed, on the only condition that the visible behaviour of your program does not change due to optimizations), you use a memory barrier to make sure that the instructions you want to be executed in the order you write them, are executed only in that order.

Here is a very interesting, though rather technical and long, article on how memory barriers work and what they do and don't do. It's written for Linux, but the basic principles remain the same.

EDIT:

The lock is an implicit memory barrier, by the link I gave earlier, so no memory barrier is needed.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • @Tony: memory barrier? Can you please put a bit more detail? – RajSanpui Apr 28 '11 at 12:05
  • @ issssssssssssssshhhh access problem at the article at my office. :( I will check it at home, and revert to you. In between if you can please add some code snippets to my current code, it will be a huge favor. – RajSanpui Apr 28 '11 at 12:10
  • 2
    you should really really read up on things like this, because it's not an easy subject and only real understanding will get you anywhere... – Tony The Lion Apr 28 '11 at 12:17
  • @Tony: Definitely, but now unable to access the link here. That's why i asked, if you are comfortable with the same. – RajSanpui Apr 28 '11 at 12:33
  • `pthread_mutex_lock` **is a memory barrier**. This is specified by POSIX. You do not need to do anything else to create one. – R.. GitHub STOP HELPING ICE Apr 28 '11 at 12:38
  • @R: So, you mean i will protect the whole function using pthread_mutex_lock? But lock is already there..then, why the flag is not properly working on every execution? I mean sometimes executed before and sometimes after. – RajSanpui Apr 28 '11 at 12:42
  • @R yea I saw that while reading that document, I will make an edit – Tony The Lion Apr 28 '11 at 12:43
  • @R: Please check my comments below the question. Where you have specified it is unclear – RajSanpui Apr 28 '11 at 12:47
  • @R..: No, `pthread_mutex_lock` is not a memory barrier, but it usually implies a full memory barrier or an acquire memory barrier. – ninjalj Apr 28 '11 at 18:44
  • I'm not sure what your distinction between "is" versus "implies" is. – R.. GitHub STOP HELPING ICE Apr 28 '11 at 19:57
  • @R..: A mutex has a memory barrier (if the memory model of the underlying architecture needs memory barriers) and other things. – ninjalj Apr 28 '11 at 22:24
  • 2
    I think we're just using words differently. According to your usage, a memory barrier is a special cpu instruction to synchronize memory. To me, a memory barrier is part of the formal language/machine specification which, on architectures like x86 where no special opcode is needed to ensure that writes are seen by others threads, might simply be implemented as nothing at all. – R.. GitHub STOP HELPING ICE Apr 28 '11 at 22:27
  • @R..: I think that's a **fence** on C1x and C++0x, if I'm understanding you correctly (and it may imply memory and compiler barriers). – ninjalj Apr 28 '11 at 23:20
  • @all: Unfortunately no one has been able to give me a clear solution, this is sad :-( – RajSanpui Apr 29 '11 at 05:40
0

Just for everyone's info:

What i could not achieve using pthread_cond_timedwait(&timerCond, &timerMutex, ptr); i have achieved using usleep( ), usleep takes timespec structure where we can specify the wait period using seconds and nanoseconds, and my purpose is solved.

So what does the pthread_cond_timedwait(&timerCond, &timerMutex, ptr); make sense for?? I am surprised, as this API is expected to make the calling thread wait, fo that condition to satisfy, but it seems that processor jumps to next instruction as an optimisation measure, and does not wait foer the condition to satisfy.

But still my problem remains the same, as to why, pthread_cond_timedwait(&timerCond, &timerMutex, ptr); should not make the calling thread wait?

It seems i wasted a day behind this API: pthread_cond_timedwait( )

RajSanpui
  • 11,556
  • 32
  • 79
  • 146