Как использовать pthread_atfork () и pthread_once () для переинициализации взаимных исключений в дочерних процессах

У нас есть совместно использованная библиотека C++, которая пользуется библиотекой Ice ZeroC для RPC и если мы не закрываем время выполнения Льда, мы наблюдали дочерние процессы, зависающие относительно случайных взаимных исключений. Ледяное время выполнения запускает потоки, имеет много внутренних взаимных исключений и сохраняет открытые дескрипторы файлов к серверам.

Кроме того, у нас есть несколько собственных взаимных исключений для защиты нашего внутреннего состояния.

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

Чтение стандарта POSIX на pthread_atfork () при обработке взаимных исключений и внутреннего состояния:

С другой стороны, некоторые библиотеки, возможно, смогли предоставить просто дочернюю стандартную программу, которая повторно инициализирует взаимные исключения в библиотеке и всех ассоциированных странах к некоторому известному значению (например, чем это было, когда изображение первоначально выполнялось). Этот подход не возможен, тем не менее, потому что реализациям позволяют перестать работать * _init (), и * _destroy () призывает к взаимным исключениям и блокировкам, если взаимное исключение или блокировка все еще заблокированы. В этом случае дочерняя стандартная программа не может повторно инициализировать взаимные исключения и блокировки.

На Linux этот тест C программа возвращает EPERM из pthread_mutex_unlock () в дочернем pthread_atfork () обработчик. Linux требует, чтобы добавление _NP к макросу PTHREAD_MUTEX_ERRORCHECK для него скомпилировало.

Эта программа связана от этого хорошего потока.

Учитывая, что это технически не безопасно или законно разблокировать или уничтожить взаимное исключение в ребенке, я думаю, что лучше иметь указатели на взаимные исключения и затем сделать, чтобы ребенок сделал новый pthread_mutex_t на "куче" и оставил взаимные исключения родителя в покое, таким образом, имея маленькую утечку памяти.

Единственная проблема - то, как повторно инициализировать состояние библиотеки, и я думаю о сбросе pthread_once_t. Возможно, потому что POSIX имеет инициализатор для pthread_once_t, что он может быть сброшен к его начальному состоянию.

#include 
#include 
#include 

static pthread_once_t once_control = PTHREAD_ONCE_INIT;

static pthread_mutex_t *mutex_ptr = 0;

static void
setup_new_mutex()
{
    mutex_ptr = malloc(sizeof(*mutex_ptr));
    pthread_mutex_init(mutex_ptr, 0);
}

static void
prepare()
{
    pthread_mutex_lock(mutex_ptr);
}

static void
parent()
{
    pthread_mutex_unlock(mutex_ptr);
}

static void
child()
{
    // Reset the once control.
    pthread_once_t once = PTHREAD_ONCE_INIT;
    memcpy(&once_control, &once, sizeof(once_control));
}

static void
init()
{
    setup_new_mutex();
    pthread_atfork(&prepare, &parent, &child);
}

int
my_library_call(int arg)
{
    pthread_once(&once_control, &init);

    pthread_mutex_lock(mutex_ptr);
    // Do something here that requires the lock.
    int result = 2*arg;
    pthread_mutex_unlock(mutex_ptr);

    return result;
}

В вышеупомянутом образце в ребенке () я только сбросил pthread_once_t путем создания копии нового pthread_once_t инициализированной с PTHREAD_ONCE_INIT. Новый pthread_mutex_t только создается, когда библиотечная функция вызывается в дочернем процессе.

Это - hacky, но возможно лучший способ иметь дело с этим краем стандартов. Если pthread_once_t содержит взаимное исключение затем, система должна иметь способ инициализировать его от его состояния PTHREAD_ONCE_INIT. Если это содержит указатель на взаимное исключение, выделенное на "куче", чем это будет вынуждено выделить новое и установить адрес в pthread_once_t. Я надеюсь, что это не использует адрес pthread_once_t ни для чего специального, которое победило бы это.

Поиск comp.programming.threads группа для pthread_atfork () показывает большое хорошее обсуждение и как мало стандарты POSIX действительно обеспечивают для решения этой проблемы.

Существует также проблема, что нужно только вызвать функции async-signal-safe от pthread_atfork () обработчики, и кажется, что самый важный является дочерним обработчиком, где только memcpy () сделан.

Это работает? Существует ли лучший способ иметь дело с требованиями нашей общей библиотеки?

20
задан Blair Zajac 12 April 2010 в 19:09
поделиться