5 printf("servproc cancelled, thread id %ld\n", pr_thread_id(NULL));
6 }
7 void
8 servproc(void *cookie, char *dataptr, size_t datasize,
9 door_desc_t *descptr, size_t ndesc)
10 {
11 int oldstate, junk;
12 long arg, result;
13 Pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, oldstate);
14 pthread_cleanup_push(servproc_cleanup, NULL);
15 sleep(6);
16 arg = *((long*)dataptr);
17 result = arg * arg;
18 pthread_cleanup_pop(0);
19 Pthread_setcancelstate(oldstate, junk);
20 Door_return((char*)result, sizeof(result), NULL, 0);
21 }
Вспомните, что мы говорили об отмене выполнения потока в разделе 8.5 и в связи с листингом 15.18. Когда система обнаруживает завершение клиента в процессе выполнения серверной процедуры, потоку, обрабатывающему запрос этого клиента, отправляется запрос на отмену:
■ если поток отключил возможность отмены, ничего не происходит и поток выполняется до завершения (вызов door_return), а результаты сбрасываются;
■ если возможность отмены включена, вызываются обработчики отмены потока, а затем он завершает работу.
В тексте процедуры сервера мы сначала вызвали pthread_setcancelstate для включения возможности отмены потока, потому что по умолчанию при создании новых потоков библиотекой возможность их отмены отключается. Эта функция сохраняет текущее состояние потока в переменной oldstate, и мы восстанавливаем его в конце функции. Затем мы вызываем pthread_cleanup_push для регистрации нашего обработчика отмены servproc_cleanup. Эта функция только выводит идентификатор отмененного потока, но вообще она может выполнять все необходимое для корректного завершения процедуры сервера (разблокировать исключения и т. п.). После возвращения из обработчика поток завершается.
В текст процедуры сервера мы добавляем 6-секундную паузу, чтобы клиент мог успешно завершить работу в вызове door_call.
Запустив клиент дважды, мы увидим сообщение интерпретатора Alarm clock при завершении процесса сигналом SIGALRM:
solaris % clientintr4 /tmp/door4 44
Alarm Clock
solaris % clientintr4 /tmp/door4 44
Alarm Clock
Посмотрим, что при этом выводит сервер. Каждый раз при досрочном завершении клиента поток процедуры сервера действительно отменяется и вызывается обработчик отмены потока:
solaris % serverintr4 /tmp/door4
servproc canceled, thread id 4
servproc canceled, thread id 5
Цель, с которой мы вызываем программу-клиент дважды, — показать, что после завершения потока с идентификатором 4 библиотека создает новый поток (с идентификатором 5) для обработки второго запроса клиента.
Интерфейс дверей позволяет вызывать процедуры в других процессах на том же узле. В следующей главе мы обсудим возможность удаленного вызова процедур в процессах на других узлах.
Основные функции этого интерфейса просты в работе и использовании. Сервер вызывает door_create для создания двери и связывания ее с процедурой сервера, а затем вызывает fattach для сопоставления этой двери и имени файла в файловой системе. Клиент вызывает open для этого имени файла и затем может вызвать door_call для вызова процедуры сервера. Возврат из процедуры сервера осуществляется вызовом door_return.
Обычно разрешения для двери проверяются только один раз — при ее открытии вызовом open. Проверяются идентификаторы пользователя и группы клиента (и полного имени файла). Одной из полезных функций дверей (по сравнению с другими средствами IPC) является возможность получения информации о клиенте в процессе работы (его действующего и реального идентификаторов). Это может использоваться сервером для принятия решения о предоставлении услуг данному клиенту.
Двери предоставляют возможность передачи дескрипторов от клиента серверу и обратно. Это достаточно мощное средство, поскольку дескрипторы в Unix дают возможность обращаться ко множеству объектов (файлам, сокетам или XTI, дверям).
При вызове процедур в другом процессе следует учесть возможность досрочного завершения клиента или сервера. Клиент получает уведомление о досрочном завершении сервера с помощью сообщения об ошибке EINTR. Сервер получает уведомление о досрочном завершении клиента в процессе обработки процедуры посредством запроса на отмену выполнения потока данной процедуры. Сервер может обработать этот запрос или проигнорировать его.