sleep() в C
Многие знают, что sleep() обладает плохой точностью. Многие так же знают, что sleep(), как и любой блокирующий системный вызов нужно вызывать в цикле, так как он попросту может не выждать требуемое от него количество времени.
Но в таком случае, после обработки сигнала, когда управляющий поток вернётся в место, где его прервали, sleep() будет вызван с теми же параметрами, что и первый раз.
Возможно, Вы думаете, что можно положиться на то, что sleep() возвращает время, которое ему нужно "доспать" и просто итерировать цикл, до тех пор, пока возвращаемое значение не будет равно 0. Давайте обратимся к документации библиотеки libc.
Потому, что жизненный путь программы может пролегать через ситуации, когда процессу будет приходить любое количество сигналов, любых типов, в любой последовательности и в ходе прерывания слипа однажды, может совершенно спокойно пропасть или добавиться секунда или больше (или меньше). Особенно это касается сигнала SIGALRM. На некоторых системах, даже если Вы перехватили, заблокировали этот сигнал, sleep() может закончиться раньше. И это не полный список проблем.
Все эти проблемы можно решить разными способами, но есть один вариант, который покрывает их всех вместе. Используйте nanosleep(). Если механизм обработки сигналов прервёт выполнение этого вызова -- он вернёт -1 и errno будет установлена в значение EINTR. А факт того, что, она запишет оставшееся время ожидания в наносекундах в структуру timespec, указатель на которую можно передать вторым аргументом, позволит корректно продолжить "сон".
Но в таком случае, после обработки сигнала, когда управляющий поток вернётся в место, где его прервали, sleep() будет вызван с теми же параметрами, что и первый раз.
Возможно, Вы думаете, что можно положиться на то, что sleep() возвращает время, которое ему нужно "доспать" и просто итерировать цикл, до тех пор, пока возвращаемое значение не будет равно 0. Давайте обратимся к документации библиотеки libc.
Resist the temptation to implement a sleep for a fixed amount of time by using the return value of sleep, when nonzero, to call sleep again. This will work with a certain amount of accuracy as long as signals arrive infrequently. But each signal can cause the eventual wakeup time to be off by an additional second or so. Suppose a few signals happen to arrive in rapid succession by bad luck—there is no limit on how much this could shorten or lengthen the wait.Тоесть, разработчики стандартной библиотеки C настоятельно рекомендуют воздерживаться от такого кода:
- int n = 1;
- while (n>0) {
- n = sleep(n);
- }
Все эти проблемы можно решить разными способами, но есть один вариант, который покрывает их всех вместе. Используйте nanosleep(). Если механизм обработки сигналов прервёт выполнение этого вызова -- он вернёт -1 и errno будет установлена в значение EINTR. А факт того, что, она запишет оставшееся время ожидания в наносекундах в структуру timespec, указатель на которую можно передать вторым аргументом, позволит корректно продолжить "сон".
- #include <errno.h>
- #include <time.h>
- int sleep_ex( unsigned long sleep_time_sec )
- {
- struct timespec tv;
- /* Construct the timespec from the number of whole seconds... */
- tv.tv_sec = sleep_time_sec;
- /* ... and the remainder in nanoseconds. */
- tv.tv_nsec = static_cast < long > ( ( sleep_time_sec - tv.tv_sec ) * 1000000000 );
- int rval = 0;
- // repeat nanosleep() call only if rval == -1 and errno set to EINTR
- do {
- /* Sleep for the time specified in tv. If interrupted by a
- signal, place the remaining time left to sleep back into tv. */
- rval = nanosleep ( &tv, &tv );
- } while ( rval == -1 && errno == EINTR );
- return rval;
- }