0 users browsing Programming. | 1 guest | 1 bot  
Main » Programming » Semaphore timing issues in Linux
Pages: 1
Posted on 20-02-19, 14:38 (revision 1)
Post: #120 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
O.k. so I create a program that spawns a thread, takes a lock, sleep for five and then release. The thread, meanwhile, waits for 1 second before waiting 3 seconds for the lock to disappear.

This is apparently impossible to do with CLOCK_MONOTONIC, and it frustrates me to no end that I cannot do this, because this means I cannot sync my systems time clock and have a reliable delay (consider: DST, I put a delay on 0.5s, but since DST just changed that delay becomes 3600.5s instead...)

Here is my code so far, does anyone know how to change this?

Expected output is that main thread create child thread, takes the lock and waits for 5 seconds before giving it back.

Meanwhile the child thread should be delayed for 3 seconds if mutex is taken. If you comment out the mutex-taking, it should return after 1s. In both instances it sticks with 3 seconds.


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

pthread_mutex_t lock;
struct timespec first;
struct timespec current;

void timeoutThread(void* args) {
int err;
pthread_condattr_t attr;
pthread_cond_t cond;
struct timespec timeout;

clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec += 3;

pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&cond, &attr);

usleep(1000000);
err = pthread_cond_timedwait(&cond, &lock, &time);
clock_gettime(CLOCK_MONOTONIC, ¤t);
printf("[%d.%d] ", (int)(current.tv_sec - start.tv_sec), (int)(current.tv_nsec - start.tv_nsec));
switch(err) {
case ETIMEDOUT: printf("Timeout expired\n"); break;
case EINVAL: perror("Error: "); break;
default: printf("Took it in time\n"); break;
}
}

int main(void) {
pthread_t handle;

pthread_mutex_init(&lock, NULL);

clock_gettime(CLOCK_MONOTONIC, &start);
clock_gettime(CLOCK_MONOTONIC, ¤t);

printf("[%d.%d] ", (int)(current.tv_sec - start.tv_sec), (int)(current.tv_nsec - start.tv_nsec));
printf("Start\n");

pthread_create(&handle, NULL, (void*)timedThread, NULL);

pthread_mutex_lock(&lock);
usleep(5000000);
pthread_mutex_unlock(&lock);

clock_gettime(CLOCK_MONOTONIC, ¤t);
printf("[%d.%d] ", (int)(current.tv_sec - start.tv_sec), (int)(current.tv_nsec - start.tv_nsec));
printf("Done\n");

return 0;
}
Posted on 20-02-20, 04:15 (revision 1)
Post: #129 of 147
Since: 11-01-18

Last post: 1 day
Last view: 8 hours
Well, first off, why are you writing your own program to sync your clocks?

Secondly... its smells like a classic race condition: its releasing after 3 seconds cause the child thread got there first. Maybe grab the lock before creating the thread?
Posted on 20-02-20, 14:35 (revision 1)
Post: #121 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
No, I want to take a lock OR wait three seconds, and if the lock is not successfully taken within these three seconds, exit the program with catastrophic failure.

sem_timedwait() allows me to do this. So does pthread_muted_timedlock().

But pthread_cond_timedwait() always wait three seconds regardless if the lock is available, or not.

And cond_timedwait() is the only way I can use CLOCK_MONOTONIC, instead of CLOCK_REALTIME. I wish to avoid CLOCK_REALTIME because it can be altered in this way:


clock_gettime(CLOCK_REALTIME, &start);
clock_gettime(CLOCK_REALTIME, &timeout);
printf("%d HELLO\n",get_seconds_elapsed(timeout,start));
timeout.tv_sec += 1;
usleep(timeout);
clock_gettime(CLOCK_REALTIME, &timeout);
printf("%d WORLD\n",get_seconds_elapsed(timeout,start));


The above code should print:


0 HELLO
1 WORLD


Now let us imagine a daylight savings time take place just as the code is run, you instead get


0 HELLO
3601 WORLD


I want this code to be reliable, not prone to the settings of the REALTIME clock changing.
Posted on 20-02-20, 19:58 (revision 2)
Post: #130 of 147
Since: 11-01-18

Last post: 1 day
Last view: 8 hours
if you are worried about DST, use a timezone that doesn't have them, or don't use timezones at all.

Maybe you should review the ptheads documentation, according to this:
The pthread_cond_timedwait() and pthread_cond_wait() functions shall block on a condition variable. The application shall ensure that these functions are called with mutex locked by the calling thread; otherwise, an error (for PTHREAD_MUTEX_ERRORCHECK and robust mutexes) or undefined behavior (for other mutexes) results.


my impression is that pthread_cond_timedwait() unlocks the mutex and then waits.
Posted on 20-02-24, 08:22 (revision 2)
Post: #122 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
Regarding timezones, that is one thing that might screw things up - another one is NTP, or PTP, or a whole slew of different timesync protocols. Not using timesync is unfortunately not an option for this application, we need it.

Regarding pthread_cond_timedwait, yes, you are correct, after some testing and further research I've confirmed it too.

So, to sum it up:
pthread_mutex_timedlock()
does the same thing as
sem_timedwait()
which does a completely different thing from
pthread_cond_timedwait()


And both of the former calls support CLOCK_REALTIME and only CLOCK_REALTIME, while only the last call supports CLOCK_MONOTONIC (in addition to CLOCK_REALTIME).

Why is there no pthread_mutex_timedlock_monotonic()? T_T

It might be just me, but I strongly suspect this API come from the same guys that developed PHP...
Posted on 20-02-24, 20:41 (revision 1)
Post: #131 of 147
Since: 11-01-18

Last post: 1 day
Last view: 8 hours
Ok, running UTC eliminates DST, how does NTP screw things up, when you can choose when to run it?
Posted on 20-02-28, 10:07 (revision 2)
Post: #123 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
Ah, now I understand the problem. Consider the following code:


struct timespec next_wait;
clock_gettime(CLOCK_REALTIME, &next_wait);
int res;

while (1) {
next_wait.tv_nsec += 1000000; /* Wait one millisecond */
next_wait.tv_sec += next_wait.tv_nsec / 1000000000; /* Increase seconds if applicable */
next_wait.tv_nsec %= 1000000000; /* Reset nsec below 1B nsec */
if (pthread_mutex_timedlock(&sem, &next_wait) == 0) {
doSomeStuff();
pthread_cond_timedwait(&sem, &next_wait);
}
}


Now type this in terminal:

sudo ntpdate


Why would this cause period instability, and how can I guarantee period stability? The answer is pretty obvious if you think about it. :)
Posted on 20-02-28, 17:43
Post: #132 of 147
Since: 11-01-18

Last post: 1 day
Last view: 8 hours
by not using outdated time syncing software? ntpdate was replaced with ntpd.
Posted on 20-03-02, 13:58
Post: #124 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
Posted by funkyass
by not using outdated time syncing software? ntpdate was replaced with ntpd.


Doesn't matter what time sync software I use, ntpdate was just an example. In theory, I should have a period of 100ms in the above code.

In practice, if I get timesynced and add/remove +/- 50ms to the realtime clock every 500 ms, I will have timing periods like this (in ms):

100,100,100,130,100
100,100,100,80,100
100,100,100,60,100
100,100,100,120,100
100,100,100,150,100

This is not what I want.
Posted on 20-03-05, 02:14

Post: #62 of 73
Since: 11-13-19

Last post: 3 days
Last view: 8 hours
And you should never expect process yielding to return to your process *EXACTLY* in context switching intervals of the kernel.
Posted on 20-03-05, 08:17 (revision 1)
Post: #126 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
Yes, I am aware of this. It is not a problem that a jitter happens or that it comes in a bit late, as long as it is within the period.

The problem is that the period gets displaced by x ms every 500 ms, so in absolute numbers and in ideal conditions I would get these numbers:

100,200,300,400,500
600,700,800,900,1000
1100,1200,1300,1400,1500
1600,1700,1800,1900,2000
2100,2200,2300,2400,2500

With a monotonic clock and jitter introduced something like this might be more likely:

102,205,301,410,541
601,702,804,903,1002
1108,1207,1306,1418,1523
1608,1706,1810,1917,2033
2119,2202,2301,2412,2507

Still fine, no deadline misses, might want to do some slight tweaking though. But with REALTIME clock I get these "periods" instead:

102, 205, 301, 440, 571
631, 732, 834, 913, 1012
1118, 1217, 1316, 1388, 1493
1578, 1676, 1780, 1907, 2023
2109, 2192, 2291, 2452, 2547


Which, when compiled into bar charts, provide this kind of periodic accuracy:

https://imgshare.io/image/timing-problems.t7tqY

You should never be too early, yet clearly here we are...
Posted on 20-03-13, 00:26

Post: #66 of 73
Since: 11-13-19

Last post: 3 days
Last view: 8 hours
Yes, jitter stacks. What, you thought they'd make up for it by returning to you *earlier* the next switch?
Posted on 20-03-13, 09:18
Post: #128 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
Posted by kode54
Yes, jitter stacks. What, you thought they'd make up for it by returning to you *earlier* the next switch?


Depends on the loop.

If you say "sleep 100 ms" at the end of the period, then yes, it will stack.

If you say "sleep until time x" where x is the time the thread started plus y*100ms, where y is the number of iterations run, then no, it does not stack.

Unless you tamper with the clock, which you do when running time sync protocols together with this concept. But that is clock changes stacking, not jitter.
Posted on 20-03-13, 18:49
Post: #134 of 147
Since: 11-01-18

Last post: 1 day
Last view: 8 hours
Is all this running on a hard real time os?
Posted on 20-03-16, 10:18 (revision 1)
Post: #129 of 135
Since: 11-24-18

Last post: 22 days
Last view: 7 hours
Posted by funkyass
Is all this running on a hard real time os?


It has soft realtime requirements, yes. Hard realtime no - then we'd use FreeRTOS instead (as we do on a few different products in the same family).
Pages: 1
Main » Programming » Semaphore timing issues in Linux
Yes, it's an ad.