pthread_sleepon_timedwait()

Заставить поток бездействовать на время ожидания

Прототип:

#include <pthread.h>
int pthread_sleepon_timedwait( const volatile void *addr,
uint64_t nsec );

Аргументы:

addr
Дескриптор, который поток должен ожидать. Значение addr обычно представляет собой структуру данных, управляющую ресурсом.
nsec
Ограничение времени ожидания в наносекундах.

Библиотека:

libc

Описание:

Функция pthread_sleepon_timedwait() использует мьютекс и условные переменные, чтобы "заснуть" на дескрипторе addr.

Если nsec не равно нулю, то pthread_sleepon_timedwait() вызовет pthread_cond_timedwait(). Если pthread_cond_timedwait() истекает, то pthread_sleepon_timedwait() возвращает ETIMEDOUT. Если nsec равно нулю, то pthread_sleepon_timedwait() вызывает pthread_cond_wait().

Функции pthread_sleepon_*() обеспечивают простой и унифицированный способ ожидания различных ресурсов в многопоточном приложении. Например, многопоточная файловая система может захотеть ожидать таких разнообразных ресурсов, как кэш-блок, блокировка файла, завершение операции и многие другие. Например, для ожидания ресурса:

pthread_sleepon_lock();
while ( (ptr = cachelist->free) == NULL )
{
pthread_sleepon_timedwait( cachelist, 1000 );
}
cachelist->free = ptr->free;
pthread_sleepon_unlock();

Чтобы начать операцию и дождаться ее завершения:

/* Line up for access to the driver */
pthread_sleepon_lock();
if ( driver->busy )
{
pthread_sleepon_timedwait( &driver->busy, 1000 );
}
/* We now have exclusive use of the driver */
driver->busy = 1;
driver_start(driver); /* This should be relatively fast */
/* Wait for something to signal driver complete */
pthread_sleepon_timedwait(&driver->complete, 1000);
pthread_sleepon_unlock();
/* Get the status/data */
driver_complete(driver);
/* Release control of the driver and signal anyone waiting */
pthread_sleepon_lock();
driver->busy = 0;
pthread_sleepon_signal(&driver->busy);
pthread_sleepon_unlock();

Использование цикла while вместо if обрабатывает случай, когда ожидание на addr прерывается с помощью pthread_sleepon_broadcast().

Необходимо вызвать pthread_sleepon_lock(), который захватывает управляющий мьютекс для условной переменной и гарантирует, что другой поток не войдет в критическую секцию между проверкой, блокировкой и использованием ресурса. Поскольку pthread_sleepon_timedwait() вызывает pthread_cond_timedwait(), она освобождает управляющий мьютекс при блокировке. Она повторно захватывает мьютекс перед "пробуждением".

"Пробуждение" выполняется, когда другой поток вызывает функцию pthread_sleepon_signal(), которая "пробуждает" единственный поток, или pthread_sleepon_broadcast(), которая "пробуждает" все потоки, заблокированные на addr. Потоки разблокируются в порядке приоритета. Если имеется более одного потока с одинаковым наивысшим приоритетом, первым "пробуждается" тот, который ждал дольше всех.

Используется один мьютекс и одна условная переменная для каждого уникального адреса, который в настоящее время заблокирован. Таким образом, общее количество условных переменных равно количеству уникальных addr, ожидающих потока. Это также означает, что максимальное количество условных переменных никогда не превышает количество потоков. Для этого условные переменные создаются динамически по мере необходимости и помещаются во внутренний список для повторного использования, когда они не требуются.

Можно отметить, что функции pthread_sleepon_*() являются более простыми в использовании и понимании, чем условные переменные. Они также напоминают традиционные функции sleepon() и wakeup(), имеющиеся в ядрах Unix. Они могут быть реализованы следующим образом:

int _sleepon( void *addr )
{
int ret;
if ( (ret = pthread_sleepon_lock()) == EOK )
{
ret = pthread_sleepon_timedwait( addr, 1000 );
pthread_sleepon_unlock();
}
return (ret);
}
void _wakeup( void *addr )
{
if ( pthread_sleepon_lock() == EOK )
{
pthread_sleepon_broadcast( addr, 1000 );
pthread_sleepon_unlock();
}
}

Обратите внимание, что в большинстве ядер Unix поток выполняется до тех пор, пока не заблокируется, и поэтому ему не нужно беспокоиться о защите того условия, которое он проверяет с помощью мьютекса. Точно так же, когда вызывается Unix-овская wakeup(), немедленного переключения потока не происходит. Таким образом, можно использовать только описанные выше простые процедуры (_wakeup() и _sleepon()), если все потоки выполняются с планированием SCHED_FIFO и с одинаковым приоритетом, что более точно имитирует планирование ядра Unix.

Возвращаемое значение:

EDEADLK
Вызывающий поток уже владеет управляющим мьютексом.
ETIMEDOUT
Истекло время, указанное в nsec.
EOK
Успешное завершение.

Классификация:

ЗОСРВ «Нейтрино»

Безопасность использования
Точка остановки потока
Да
Обработчик прерываний
Нет
Обработчик сигналов
Да
В потоке
Да

Тематические ссылки:

pthread_cond_wait(), pthread_cond_wait_interruptible(), pthread_mutex_lock(), pthread_mutex_unlock(), pthread_sleepon_broadcast(), pthread_sleepon_lock(), pthread_sleepon_signal(), pthread_sleepon_unlock(), pthread_sleepon_wait(), sched_setscheduler()




Предыдущий раздел: Описание API системной библиотеки