2010-08-24 9 views
5

Pracuję nad biblioteką, w której pracuję nad różnymi zadaniami dla bibliotek innych firm, które wykonują względnie szkicową lub niebezpieczną pracę związaną z platformą. (W szczególności, piszę parser funkcji matematycznych, który wywołuje kompilatory JIT, takie jak LLVM lub libjit, w celu zbudowania kodu maszynowego.) W praktyce te biblioteki stron trzecich mają tendencję do roztrzaskać się (część z nich to moja wina , oczywiście, ale nadal chcę trochę ubezpieczenia).Jak izolować zadanie/wątek przed awarią?

Chciałbym więc móc bardzo zgrabnie radzić sobie z pracą umierającą okropnie - SIGSEGV, SIGILL, itd. - bez niszczenia reszty mojego kodu (lub kodu użytkowników nazywających mnie funkcje biblioteczne). Żeby było jasne, nie obchodzi mnie, czy ta konkretna praca może być kontynuowana (nie będę próbowała naprawiać stanu awaryjnego), ani też nie dbam o stan obiektów po takiej awarii (odrzucę je natychmiast, gdy nastąpi awaria). Chcę tylko być w stanie wykryć awarię, zatrzymać awarię przed usunięciem całego procesu, przestać wywoływać awarie i wznowić wykonywanie.

(Dla nieco większego kontekstu, kod w tym momencie jest pętlą for, testowanie każdego z dostępnych kompilatorów JIT.Niektóre z tych kompilatorów może ulec awarii.Jeśli tak, chcę po prostu wykonać continue; i dalej z testowaniem innego kompilatora.)

Obecnie mam implementację opartą na signal(), która nie udaje się całkiem koszmarnie; Oczywiście, jest to niezdefiniowane zachowanie dla longjmp() z obsługi sygnału, a obsługa sygnałów oczekuje się, że zakończy się na exit() lub terminate(). Rzucanie kodu w inny wątek nie pomaga samo w sobie, przynajmniej tak jak dotąd testowałem. Nie mogę również zhackować sposobu, aby to działało przy użyciu wyjątków C++.

Jaki jest najlepszy sposób izolowania określonego zestawu instrukcji/wątku/zadania przed awarią?

Odpowiedz

10

Spawn nowy proces.

+1

To jedyny sposób na zrobienie tego. Wątek może uszkodzić pamięć w dowolnym miejscu procesu, więc po SEGV nie można zagwarantować, że pamięć nie ulegnie zmianie. – KeithB

+0

Dzięki za heads-up. Prawie na pewno właściwa odpowiedź tutaj. Mam zamiar poczytać na fork() i towarzystwie. –

5

Jakie dane wyjściowe są gromadzone, gdy zadanie powiedzie się?

Pytam, ponieważ jeśli wyjście ma niską przepustowość, byłbym skłonny uruchomić każde zadanie w jego własnym procesie.

Każda z tych krytycznych zadań, które uruchamiasz, ma dużą szansę na uszkodzenie pamięci używanej w innym miejscu procesu.

Procesy zapewniają najlepszą ochronę.

1

Procesy zapewniają najlepszą ochronę, ale możliwe jest, że nie można tego zrobić.

Jeśli punktami początkowymi wątków są funkcje, które napisałeś (na przykład ThreadProc w świecie Windows), możesz je opakować w bloki try{...}catch(...). Jeśli chcesz poinformować, że wystąpił wyjątek, możesz przekazać określone kody błędów z powrotem do głównego wątku lub użyć innego mechanizmu. Jeśli chcesz zarejestrować nie tylko, że wystąpił wyjątek, ale także ten wyjątek, musisz przechwycić określone typy wyjątków i wyodrębnić z nich informacje diagnostyczne, aby nawiązać połączenie z głównym wątkiem. A'la:

int my_tempermental_thread() 
{ 
    try 
    { 
    // ... magic happens ... 
    return 0; 
    } 
    catch(const std::exception& ex) 
    { 
    // ... or maybe it doesn't ... 
    string reason = ex.what(); 
    tell_main_thread_what_went_wong(reason); 
    return 1; 
    } 
    catch(...) 
    { 
    // ... definitely not magical happenings here ... 
    tell_main_thread_what_went_wrong("uh, something bad and undefined"); 
    return 2; 
    } 
} 

Należy pamiętać, że jeśli się w ten sposób uruchomieniu ryzyko niwecząc proces hosta, gdy zdarzają się wyjątki. Mówisz, że nie próbujesz rozwiązać problemu, ale skąd wiesz, że złośliwa nić nie jadła twojego stacka? Catch-and-ignore to świetny sposób na stworzenie strasznie kłopotliwych błędów.

0

W systemie Windows może być możliwe użycie VirtualProtect(YourMemory, PAGE_READONLY) podczas wywoływania niezaufanego kodu. Każda próba zmodyfikowania tej pamięci spowodowałaby wyjątek strukturalny. Możesz bezpiecznie złapać to i kontynuować egzekucję. Jednak pamięć przydzielona przez tę bibliotekę oczywiście wycieknie, podobnie jak inne zasoby. Odpowiednikiem linuksowym jest mprotect(YorMemory, PROT_READ), co powoduje SEGV.