}
pthread_mutex_unlock (&l->lock);
}
void
lock_for_write (rwlock *1) {
pthread_mutex_lock (&l->lock),-l->waiting_writers++;
pthread_cleanup_push (waiting_writer_cleanup, 1); while (l->lock_count ! = О) pthread_cond_wait (&l->wcond, &l->lock); l->lock_count = -1; /*
* Обратите внимание на то, что функция pthread_cleanup_pop()
* выполняет здесь функцию waiting_writer_cleanup(). */
pthread_cleanup_pop (1);
}
void
release_write_lock (rwlock *1) {
pthread_mutex_lock (&l->lock);
l->lock_count = 0;
if (l->waiting_writers == О)
pthread_cond_broadcast (&l->rcond)
else
pthread_cond_signal (&l->wcond); pthread_mutex_unlock (&l->lock);
}
/*
* Эта функция вызывается для инициализации блокировки
* чтения-записи. */
void
initialize_rwlock (rwlock *1) {
pthread_mutex_init (&l->lock, pthread_mutexattr_default); pthread_cond_init (&l->wcond, pthread_condattr_default); pthread_cond_init (&l->rcond, pthread_condattr_default); l->lock_count = О; l->waiting_writers = О;
\
Приложение Б 559
}
reader_thread() {
lock_for_read (&lock);
pthread_cleanup_push (release_read_lock, &lock); /*
* Поток устанавливает блокировку для чтения. */
pthread_cleanup_pop (1);
}
writer_thread() {
lock_for_write (&lock);
pthread_cleanup_push (release_write_lock, &lock); /*
* Поток устанавливает блокировку для записи. */
pthread_cleanup_pop (1) ;
}
Замечания по использованию
Две описываемые здесь функции, pthread_cleanup_push() и pthread_cleanup_pop (), которые помещают и извлекают из стека обработчики запроса на отмену потока, можно сравнить с левой и правой круглыми скобками. Их нужно всегда использовать «в паре».
Логическое обоснование
Ограничение, налагае м ое на две функции, pthread_cleanup_push() и pthread_cleanup_pop(), которые помещают и извлекают из стека обработчики запроса на отмену потока, и состоящее в том, что они должны использоваться попарно в пределах одного и того же лексического контекста, позволяет создавать эффективные макросы (или компиляторные реализации) и эффективно управлять памятью. Вариант реализации этих функций в виде макросов может выглядеть следующим образом,
#define pthread_cleanup_push (rtn, arg) { \
struct _pthread_handler_rec _cleanup_handler, **_head; \
_cleanup_handler.rtn = rtn; \
_cleanup_handler.arg = arg; \
(void) pthread_getspecific (_pthread_handler_key, &_head);
\
_cleanup_handler.next = *_head; \
*_head = &_cleanup_handler;
#define pthread_cleanup_pop (ex) \
*_head = _cleanup_handler.next; \
if (ex) (*_cleanup_handler.rtn) (_cleanup_handler.arg); \
}
Возможна даже более «смелая» реализация этих функций, которая позволит компилятору «считать» обработчик запроса на отмену константой, значение которой можно «встраивать» в код. В данном томе стандарта IEEE Std 1003.1-2001 пока оставлен неопределенным результат вызова функции longjmp () из обработчика сигнала, выполняемого в функции библиотеки POSIX System Interfaces. Если в какой-то реализации потребуется разрешить этот вызов и придать ему надлежащее поведение, функция longjmp () должна в этом случае вызвать все обработчики запроса на отмену, которые были помещены в стек (но еще не извлечены из него) с момента вызова функции setjmp ().
Рассмотрим многопоточную функцию, вызываемую одним потоком, который использует сигналы. Если бы сигнал был выдан обработчику сигналов во время операции qsort(), и этому обработчику пришлось бы вызвать функцию longjmp() (которая в свою очередь не вызывала бы обработчики запроса на отмену), то вспомогательные потоки, создаваемые функцией qsort (), не были бы аннулированы. Они бы продолжали выполняться и осуществляли запись в массив аргументов даже в том случае, если этот массив был к тому времени извлечен из стека.
Обратите внимание на то, что такой механизм обработки запросов на отмену особенно тесно связан с языком С, и, несмотря на требование независимости языка, предъявляемое к любому унифицированному механизму выполнения «очистительно-восстановительных работ», подобный механизм, выраженный в других языках, может быть совершенно иным. Кроме того, необходимость этого механизма в действительности связана только с отсутствием реального механизма обработки исключительных ситуаций в языке С, который был бы идеальным решением.
Здесь отсутствуют замечания о функции безопасной отмены потока. Если приложение в своих обработчиках сигналов не имеет точек отмены, блокирует любой сигнал, обработчик которого может иметь точки отмены (несмотря на вызов асинхронно-опасных функций), или запрещает отмену (несмотря на вызов асинхронно-опасных функций), все функции можно безопасно вызывать из функций обработки запросов на отмену потоков.
Читать дальше