Параллельное и распределенное программирование на С++ (Хьюз, Хьюз) - страница 404

>* Обратите внимание на то, что функция 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 ().