Проблема с Boehm g c и многопоточной программой - PullRequest
1 голос
/ 03 марта 2020

У меня проблема при использовании сборщика мусора Boehm в многопоточной программе.
Моя основная функция спит, пока поток выполняет некоторые выделения и освобождения с использованием сборщика мусора.

Когда сборщик мусора вызывает collect(), сон основного потока прерывается, и программа продолжается, как будто ничего не произошло.

Следующий исходный код завершается через 1 секунду, в то время как он должен спать не менее 100 секунд:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define GC_THREADS
#include <gc/gc.h>

void foo () {
    sleep (1);
    GC_gcollect (); // or multiple allocation, that will trigger a collect at some point
}

void * thread_func (void* data) {
    foo ();
}

int main () {
    // GC_init (); ordinarily useless, and does not change anything 
    pthread_t id;
    GC_pthread_create (&id, NULL, &thread_func, NULL);
    sleep (100);
    printf ("End \n");
}

Та же проблема возникает, когда это поток, который спит, и основная функция. который выполняет распределение. Я использую последнюю стабильную версию bohem gc (а именно 8.0.4), на ubuntu-18.04.

Кто-нибудь имеет представление о том, что происходит?

1 Ответ

2 голосов
/ 03 марта 2020

Сборщик мусора использует несколько сигналов (Согласно документации по отладке , SIGSEGV, SIGBUS, а также SIGPWR и SIGXCPU в многопоточных linux установках, подобных вам " используется внутренне) и устанавливает для них функции обработчика сигнала.

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

Итак, если вы хотите смешать sleep() с сборщиком мусора, вам придется использовать al oop, например:

int timeout = 100;
int time_remaining;
while ((time_remaining = sleep(timeout)) > 0) {
  timeout = time_remaining;
}

Более надежная реализация использует nanosleep() напрямую (это то, как sleep() реализовано в Linux + Glib c) для лучшей обработки ошибок:

struct timespec req = { .tv_sec = 100, .tv_nsec = 0 };
struct timespec rem;
while (nanosleep(&req, &rem) < 0) {
  if (errno == EINTR) {
    // Interrupted by a signal handler
    req = rem;
  } else {
    // Some other error happened; handle appropriately for your application
    perror("nanosleep");
    exit(EXIT_FAILURE);
  }
}

Еще более надежная версия, которая не будет спать дольше 100 секунд из-за времени, используемого сборщиком мусора (если только время, проведенное до этого времени в спящем режиме + g c превышает это время), использует clock_nanosleep() спать до истечения заданной отметки времени:

struct timespec req;
if (clock_gettime(CLOCK_MONOTONIC, &req) < 0) {
  perror("clock_gettime");
  exit(EXIT_FAILURE);
}
req.tv_sec += 100;
int rc;
while ((rc = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &req, NULL)) != 0) {
  if (rc != EINTR) {
     fprintf(stderr, "clock_nanosleep: %s\n", strerror(rc));
     exit(EXIT_FAILURE);
  }
}
...