ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы....

62
Лекция 3. POSIX Threads. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение потоков Пазников Алексей Александрович Кафедра вычислительных систем СибГУТИ Сайт курса: http://cpct.sibsutis.ru/~apaznikov/teaching/ Q/A: https://piazza.com/sibsutis.ru/spring2015/pct2015spring Параллельные вычислительные технологии Весна 2015 (Parallel Computing Technologies, PCT 15)

Transcript of ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы....

Page 1: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Лекция 3. POSIX Threads. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение потоков

Пазников Алексей АлександровичКафедра вычислительных систем СибГУТИ

Сайт курса: http://cpct.sibsutis.ru/~apaznikov/teaching/Q/A: https://piazza.com/sibsutis.ru/spring2015/pct2015spring

Параллельные вычислительные технологииВесна 2015 (Parallel Computing Technologies, PCT 15)

Page 2: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Реентерабельность

2

Page 3: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Реентерабельность

▪ Одну функцию может вызывать (re-enter) несколько потоков.

▪ Функции, которые могут безопасно вызываться из нескольких потоков, называются безопасными в многопоточной среде или потокобезопасными (thread-safe)

▪ Все функции, определённые стандартом Single UNIX Specification, являтся потокобезопасными, за исключением ...

3

Page 4: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Непотокобезопасные функции

asctimebasenamecatgetscryptctimedbm_clearerrdbm_closedbm_deletedbm_errordbm_fetchdbm_firstkeydbm_nextkeydbm_opendbm_storedirnamedlerrordrand48

ecvtencryptendgrentendpwentendutxentfcvtftwgcvtgetc_unlockedgetchar_unlockedgetdategetenvgetgrentgetgrgidgetgrnamgethostbyaddrgethostbyname

getutxlinegmtimehcreatehdestroyhsearchinet_ntoa164algammalgammaflgammallocaleconvlocaltimelrand48mrand48nftwnl_langinfoptsname

gethostentgetlogingetnetbyaddrgetnetbynamegetnetentgetoptgetprotobynamegetprotobynumbergetprotoentgetpwentgetpwnamgetpwuidgetservbynamegetservbyportgetserventgetutxentgetutxid

putc_unlockedputchar_unlockedputenvpututxlinerandreaddirsetenvsetgrentsetkeysetpwentsetutxentstrerrorstrtokttynameunsetenvwcstombswctomb

4

Page 5: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Реентерабельность

▪ У некоторых функций есть альтернативные потокобезопасные версии

▪ Например, изменяется интерфейс: результат не возвращается буфере, размещенном статически

acstime__rctime_rgetgrgid.rgetgrnam_rgetlogin_rgetpwnam_rgetpwuid_r

gmtime_rlocaltime_rrand_rreaddir_rstrerror_rstrtok_rttyname_r

5

Page 6: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Посимвольный ввод-вывод без блокировки:

Безопасный способ управления объектами FILE

void flockfile(FILE *filehandle);

int ftrylockfile(FILE *filehandle);

void funlockfile(FILE *filehandle);

int getc_unlocked(FILE *stream);

int getchar_unlocked(void);

int putc_unlocked(int c, FILE *stream);

int putchar_unlocked(int c);

Функции должны быть окружены вызовамиflockfile и funlockfile

6

Page 7: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Нереентерабельная версия функции getenv

static char envbuf[ARG_MAX];extern char **environ;

char *getenv(const char *name){ int i, len; len = strlen(name);

for (i = 0; environ[i] != NULL; i++) { if ((strncmp(name, environ[i], len) == 0) && (environ[i][len] == '=')) { strcpy(envbuf, &environ[i][len + 1]); return envbuf; } } return NULL;}

7

Page 8: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потокобезопасная версия функции getenv

int getenv_r(const char *name, char *buf, int buflen) { int i, olen, len = strlen(name); pthread_once(&init_done, thread_init);

pthread_mutex_lock(&env_mutex); for (i = 0; environ[i] != NULL; i++) { if ((strncmp(name, environ[i], len) == 0) && (environ[i][len] == '=')) { olen = strlen(&environ[i][len+1]);

if (olen >= buflen) { pthread_mutex_unlock(&env_mutex); return ENOSPC; } strcpy(buf, &environ[i][len+1]); pthread_mutex_unlock(&env_mutex); return O; } } pthread_mutex_unlock(&env_mutex); return ENOENT; } 8

Page 9: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

int getenv_r(const char *name, char *buf, int buflen) { int i, olen, len = strlen(name); pthread_once(&init_done, thread_init);

pthread_mutex_lock(&env_mutex); for (i = 0; environ[i] != NULL; i++) { if ((strncmp(name, environ[i], len) == 0) && (environ[i][len] == '=')) { olen = strlen(&environ[i][len+1]);

if (olen >= buflen) { pthread_mutex_unlock(&env_mutex); return ENOSPC; } strcpy(buf, &environ[i][len+1]); pthread_mutex_unlock(&env_mutex); return O; } } pthread_mutex_unlock(&env_mutex); return ENOENT; }

Потокобезопасная версия функции getenv

1

2

9

Page 10: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

int getenv_r(const char *name, char *buf, int buflen) { int i, olen, len = strlen(name); pthread_once(&init_done, thread_init);

pthread_mutex_lock(&env_mutex); for (i = 0; environ[i] != NULL; i++) { if ((strncmp(name, environ[i], len) == 0) && (environ[i][len] == '=')) { olen = strlen(&environ[i][len+1]);

if (olen >= buflen) { pthread_mutex_unlock(&env_mutex); return ENOSPC; } strcpy(buf, &environ[i][len+1]); pthread_mutex_unlock(&env_mutex); return O; } } pthread_mutex_unlock(&env_mutex); return ENOENT; }

Потокобезопасная версия функции getenv

R/W-мьютексы?

10

Page 11: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потокобезопасная версия функции getenv

extern char *environ;pthread_mutex_t envjnutex;

static pthread_once_t initjtone = PTHREAD_ONCE_INIT;static void thread_init(void){ pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_nutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&env_mutex, &attг); pthread_mutexattr_destroy(&attг);}

11

Page 12: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потокобезопасная версия функции getenv

extern char *environ;pthread_mutex_t envjnutex;

static pthread_once_t initjtone = PTHREAD_ONCE_INIT;static void thread_init(void){ pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_nutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&env_mutex, &attг); pthread_mutexattr_destroy(&attг);}

Зачем нужен рекурсивный мьютекс?

12

Page 13: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков

13

Page 14: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков

▪ Необходимость сохранять данные, специфичные для конкретного потока.

▪ Данные одного потока не должны быть изменены другим потоком.

▪ “Таблица” данных не годится: идентификаторы потоков не целые, не известно заранее, сколько будет потоков.

▪ Необходим способ для адаптации к многопоточной среде (например, так было в случае с errno)

▪ Каждый поток может использовать локальные данные, не прибегая к синхронизации.

14

Page 15: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков

T1 T2foo 0

bar 1

buz 2

Keys

0 fun0()

1 NULL

2 fun2()

Destructor

0 x0f98a

1 ...

2 ...

0 NULL

1 ...

2 ...

15

Page 16: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков

T1 T2foo 0

bar 1

buz 2

Keys

0 fun0()

1 NULL

2 fun2()

Destructor

0 x0f98a

1 ...

2 ...

0 x10df3

1 ...

2 ...

16

Page 17: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Создание ключа локальных данных

/* Создать ключ, ассоциированный с лок. данными */int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

key - ключ (идентификатор локальных данных)destructor - деструктор локальных данных

▪ Ключ может использоваться разными потоками.▪ Каждый поток ассоциирует с ключом отдельный набор

локальных данных.▪ С ключом можно связать фунукцию-деструктор,

которая вызывается в случае штатного (не exit, abort) завершения потока.

▪ Поток может создать несколько ключей. По завершении потока запускаются деструктор для всех ненулевых ключей. 17

Page 18: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Создание и удаление ключа локальных данных

/* Разорвать связь ключа с локальными данными */int pthread_key_delete(pthread_key_t *key);

▪ Вызов не приводит к деструктору ключа.

/* Создать ключ, ассоциированный * с локальными данными */int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

key - ключ (идентификатор локальных данных)destructor - деструктор локальных данных

18

Page 19: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Создание ключа локальных данных

Ключ должен инициироваться атомарно и один раз!

void destructor(void *);pthread_key_t key;int init_done = 0;

int threadfunc(void *arg){ if (!init_done) { init.done = 1; err = pthread_key_create(&key, destructor); }}

19

Page 20: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

void destructor(void *);pthread_key_t key;int init_done = 0;

int threadfunc(void *arg){ if (!init_done) { init.done = 1; err = pthread_key_create(&key, destructor); }}

Создание ключа локальных данных

Ключ должен инициироваться атомарно и один раз!

20

Page 21: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Создание и удаление ключа локальных данных

/* функция initfn вызывается один раз при первом * обращении к pthrad_once */pthread_once_t initflag = PTHREAD_ONCE_INIT;int pthread_once(pthread_once_t *initflag, void (*initfn)(void));

void destructor(void *);pthread_key_t key;pthread_once_t init_done = PTHREAD_ONCEINIT;

void thread_init(void) { err = pthread_key_create(&key, destructor);}

int threadfunc(void *arg) { pthread_once(&init_done, thread_init);}

21

Page 22: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Ассоциация ключа с локальными данными

/* вернуть указатель на область памяти с локальными * данными (или NULL) */void *pthread_getspecific(pthread_key_t key);

/* связать ключ с локальными данными */int pthread_setspecific(pthread_key_t key, const void *value);

22

Page 23: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков - пример

pthread_key_t key;

void *foo(void *arg) {

int *newdata = (int *) pthread_getspecific(key);

if (newdata == NULL) {

newdata = (int *) malloc(sizeof(int));

int *argval = (int *) arg;

*newdata = *argval;

pthread_setspecific(key, newdata);

}

int *data = (int *) pthread_getspecific(key);

printf("data = %d\n", *data);

return NULL;

}23

Page 24: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков - пример

int main(int argc, const char *argv[]) {

pthread_t tid1, tid2;

int arr[2] = {1234, 5678};

pthread_key_create(&key, NULL);

pthread_create(&tid1, NULL, foo, &arr[0]);

pthread_create(&tid2, NULL, foo, &arr[1]);

pthread_join(tid1, NULL);

pthread_join(tid2, NULL);

return 0;

}24

Page 25: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков - пример 2

static pthread_key_t key;

static pthread_once_t init_done = PTHREAD_ONCE_INIT;

pthread_mutex_t env_mutex =

PTHREADMUTEX_INITIALIZER;

extern char **environ;

static void thread_init(void)

{

pthread_key_create(&key, free);

}

char *getenv(const char *name) {

int i, len;

char *envbuf;

25

Page 26: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков - пример 2

pthread_once(&init_done, thread_init);

pthread_mutex_lock(&env_mutex);

envbuf = (char *) pthread_getspecific(key);

if (envbuf == NULL) {

envbuf = malloc(ARG_MAX);

if (envbuf == NULL) {

pthread_mutex_unlock(&env_mutex);

return NULL;

}

pthread_setspecific(key, envbuf);

}

26

Page 27: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Локальные данные потоков - пример 2

len = strlen(name);

for (i = 0; environ[i] != NULL; i++) {

if ((strncmp(name, environ[i], len) == 0) &&

(environ[i][len] == '=')) {

strcpy(envbuf, &environ[i][len+1]);

pthread_mutex_unlock(&env_mutex);

return envbuf;

}

}

pthread_mnutex_unlock(&env_mutex);

return NULL;

}

27

Page 28: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потоки и сигналы

28

Page 29: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потоки и сигналы

▪ Каждый поток имеет собственную маску потоков.

▪ Отдельно взятый поток может заблокировать доставку сигнала.

▪ Когда поток назначает обработчик сигнала, он становится общим для всех потоков. То есть сигналы могут “противоречить” друг другу.

▪ Сигнал доставляется только одному потоку. Если сигнал связан с аппаратной ошибкой или таймером, то доставляется потоку, который стал причиной. Остальные сигналы могут доставляться произвольному потоку.

29

Page 30: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Задать маску сигналов для потока

/* задать маску обработчиков сигналов */int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);

▪ если oldset != NULL, в него возвращается текущая маска сигналов

▪ если в set передается непустой указатель, то значение how определяет, как должна измениться маска сигналов

▪ how == SIG_BLOCK - новая маска - объединение текущей маски и с набором, указанным в set

▪ how == SIG_UNBLOCK - новая маска - пересечение текущей маски с набором, на который указывает set

▪ how == SIG_SETMASK - новая маска set замещает текущую маску

30

Page 31: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Ожидания поступления сигнала потоком

/* приостановиться в ожидании хотя бы одного * сигнала из set, сохранить поступивший сигнал * в sig */int sigwait(const sigset_t *set, int *sig);

▪ Сигнал удаляется из набора сигналов, ожидающих обработки.

▪ Во избежании ошибочной обработки, поток должен заблокировать (pthread_sigmask) сигналы перед вызовом sigwait. Иначе сигнал может быть доставлен

▪ sigwait позволяет синхронно обрабатывать асинхронные сигналы

▪ Можно выделить отдельные потоки, которые будут заниматься только обработкой сигналов

31

Page 32: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Передача сигнала потоку

/* приостановиться в ожидании хотя бы одного * сигнала из set, сохранить поступивший сигнал * в sig */int sigwait(const sigset_t *set, int *sig);

/* передать сигнал потоку */int pthread_kill(pthread_t thread, int sig);

▪ Можно проверить существование потока, передав sig = 0▪ Если действие по умолчанию для сигнала - завершение

процесса, то передача сигнала потоку приведёт к завершению всего процесса

▪ n.b. таймеры являются ресурсами процесса32

Page 33: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потоки и сигналы - пример

int quitflag; /* поток записывает ненулевое значение */sigsetjt mask;

pthread_mutex_t: lock = PTHREAD_MUTEX_ITIALIZER;

pthread_cond_t wait = PTHREAD_COND_INITIALIZER;

void *thr_fn(void *arg) {

int err, signo;

for (;;) {

err = sigwait(&mask, &signo);

if (err != 0) {

fprintf(stderr, "sigwait failed"); exit(1); }

33

Page 34: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потоки и сигналы - пример

switch (signo) {

case SIGINT:

printf("interruption");

break;

case SIGQUIT:

pthread_mutex_lock(&lock);

quitflag = 1;

pthread_mutex_unlock(&lock);

pthread_cond_signal(&wait);

return O;

default:

fprintf(stderr, "undef. signal %d\n", signo);

exit(1)

}

}}34

Page 35: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потоки и сигналы - пример

int main(void) {

int err; sigset_t oldmask; pthread_t tid;

sigemptyset(&mask);

sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT);

if ((err = pthread_sigmask(SIG_BLOCK, &mask,

&oldmask)) != 0) {

fprintf(stderr, "SIG_BL0CK failed"); exit(1); }

err = pthread_create(&tid, NULL, thr_fn, 0);

if (err != 0) {

fprintf(stderr, "can’t create thread"); exit(1); }

pthread_mutex_lock(&lock);

while (quitflag == 0)

pthread_cond_wait(&wait, &lock);

pthread_mutex_unlock(&lock);35

Page 36: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Потоки и сигналы - пример

/* Сигнал SIGQUIT был перехвачен и сейчас * опять заблокирован. */ quitflag = 0;

/* Восстановить маску сигналов, в которой * SIGQUIT разблокирован. */ if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {

fprintf(stderr, "SIG_SETMASK failed");

exit(O);

}

}

36

Page 37: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Принудительное завершение потоков

37

Page 38: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Принудительное завершение потоков

/* установить атрибут принудительного завершения * oldstate - старое значение атрибута */int pthread_setcancelstate(int state, int *oldstate);

PTHREAD_CANCEL_ENABLE - вызов pthread_cancel приводит к тому, что поток продолжает работу до точки выхода. Точки выхода определены стандартом.PTHREAD_CANCEL_DISABLE - вызов pthread_cancel не приводит к завершению потока.

/* задать точку выхода из потока: если есть * ожидающий запрос, то поток завершит работу */void pthread_testcancel(void);

38

Page 39: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Тип завершения потока

/* задать тип выхода */int pthread_setcanceltype(int type, int *oldtype);

PTHREAD_CANCEL_DEFERRED - отложенный выход (до точки выхода)

PTHREAD_CANCEL_ASYNCHRONOUS - немедленный выход из потока при поступлении запроса

39

Page 40: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Тип завершения потока

/* задать тип выхода */int pthread_setcanceltype(int type, int *oldtype);

PTHREAD_CANCEL_DEFERRED - отложенный выход (до точки выхода)

PTHREAD_CANCEL_ASYNCHRONOUS - немедленный выход из потока при поступлении запроса

40

Page 41: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Действия потоков при принудительном завершении

При завершении потока необходимо убедиться:

▪ Поток должен освободить все блокировки, которые он удерживает.

▪ Поток должен освободить выделенную память.

▪ Поток должен завершиться в корректном состоянии.

Асинхронная отмена может привести к нежелательным последствиям!

41

Page 42: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Обработчки отмены потока (cancellation cleanup handlers)

function 3 arg 3

function 2 arg 2

function 1 arg 1

Cleanup Stack

pthread_cleanup_push() pthread_cleanup_pop()

▪ Обработчики позволяют очистить состояние потока: выполнить какие-то действия при отмене.

▪ Функции 3-1 запускаются при отмене потока или срабатывания pthread_exit.

42

Page 43: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Обработчки отмены потока (cancellation cleanup handlers)

▪ pthread_cleanup_push и pthread_cleanup_pop должны вызываться на одном уровне.

pthread_cleanup_push(free, pointer);

...

pthread_cleanup_pop(1);

pthread_cleanup_push(free, pointer);

...

}

pthread_cleanup_pop(1);

43

Page 44: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Обработчки отмены потока (cancellation cleanup handlers)

▪ pthread_cleanup_push и pthread_cleanup_pop должны вызываться на одном уровне.

pthread_cleanup_push(free, pointer);

...

pthread_cleanup_pop(1);

pthread_cleanup_push(free, pointer);

...

}

pthread_cleanup_pop(1);

44

Page 45: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Обработчики отмены потока - пример 2

pthread_cleanup_push(pthread_mutex_unlock,

(void *) &mut);

pthread_mutex_lock(&mut);

/* do some work */

pthread_mutex_unlock(&mut);

pthread_cleanup_pop(0);

void unlock(void *arg) {

pthread_mutex_unlock( &lock );

}

void *func(void *arg) {

pthread_mutex_lock(&lock);

pthread_cleanup_push( &unlock, NULL );

/* ... */

pthread_cleanup_pop(1);45

Page 46: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

enum { NTHREADS = 8 };

pthread_mutex_t file_lock = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t nalive_lock =

PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t alldead_cond =

PTHREAD_COND_INITIALIZER;

pthread_once_t init_done = PTHREAD_ONCE_INIT;

pthread_key_t key_name;

int nalive = 0;

FILE *file;

sigset_t mask;

pthread_t tid[NTHREADS];

#define FILENAME "play" 46

Page 47: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

int main(int argc, const char *argv[]) { sigemptyset(&mask); sigaddset(&mask, SIGQUIT); sigset_t oldmask; int rc = pthread_sigmask(SIG_BLOCK, &mask, &oldmask); file = fopen(FILENAME, "w"); fclose(file); pthread_t thr_reaper; pthread_create(&thr_reaper, NULL, grim_reaper, NULL); pthread_create(&tid[0], NULL, character, "Hamlet"); pthread_create(&tid[1], NULL, character, "Ophelia"); pthread_create(&tid[2], NULL, character, "Gertrude"); pthread_create(&tid[3], NULL, character, "Claudius"); pthread_create(&tid[4], NULL, character, "Polonius"); pthread_create(&tid[5], NULL, character, "Laertes"); pthread_create(&tid[6], NULL, character, "Rosencrantz"); pthread_create(&tid[7], NULL, character, "Guildenstern");

47

Page 48: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

pthread_mutex_lock(&nalive_lock); while (nalive > 0) { pthread_cond_wait(&alldead_cond, &nalive_lock); } pthread_mutex_unlock(&nalive_lock);

for (int tid_i = 0; tid_i < NTHREADS; tid_i++) { pthread_join(tid[tid_i], NULL); }

file = fopen(FILENAME, "a"); fprintf(file, "The rest is silence.\n"); fclose(file);

pthread_cancel(thr_reaper); pthread_join(thr_reaper, NULL);

return 0;}

48

Page 49: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

void _sem_wait(sem_t *sem) { while (sem_wait(sem) != 0) { }}

void key_destructor(void *arg) { printf("key destructor\n"); char *name = (char *) arg; free(name);}

void thread_init(void) { pthread_key_create(&key_name, key_destructor);}

49

Page 50: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

void cleanup_lock(void *arg) { printf("cleanup_lock\n"); pthread_mutex_t *lock = (pthread_mutex_t *) arg; pthread_mutex_unlock(lock);}

void cleanup_file(void *arg) { printf("cleanup_file\n"); fclose(file);}

50

Page 51: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

void cleanup_death(void *arg) {

printf("cleanup_death\n"); pthread_mutex_lock(&file_lock); file = fopen(FILENAME, "a"); fprintf(file, "%s is dead\n", (char *) pthread_getspecific(key_name)); pthread_mutex_unlock(&file_lock);

pthread_mutex_lock(&nalive_lock); if (--nalive == 0) { pthread_mutex_unlock(&nalive_lock); printf("The last character is dead.\n"); pthread_cond_signal(&alldead_cond); } pthread_mutex_unlock(&nalive_lock);}

51

Page 52: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

void *character(void *arg) { pthread_once(&init_done, thread_init);

pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

char *name = (char *) malloc(strlen(arg)); strcpy(name, arg);

pthread_setspecific(key_name, name); pthread_mutex_lock(&nalive_lock); nalive++; pthread_mutex_unlock(&nalive_lock);

52

Page 53: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

pthread_cleanup_push(cleanup_death, NULL); for (;;) { fprintf(stderr, "I am %s\n", name); pthread_mutex_lock(&file_lock); pthread_cleanup_push(cleanup_lock, NULL); file = fopen(FILENAME, "a"); pthread_cleanup_push(cleanup_file, NULL); fprintf(file, "%s was here\n", (char *) pthread_getspecific(key_name)); pthread_cleanup_pop(0); fclose(file); pthread_cleanup_pop(0); pthread_mutex_unlock(&file_lock); pthread_testcancel(); sleep(1); } pthread_cleanup_pop(0); return NULL; }

53

Page 54: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

void *grim_reaper(void *arg){ for (;;) { int signo; int rc = sigwait(&mask, &signo);

if (rc != 0) { fprintf(stderr, "sigwait() failed"); exit(1); }

54

Page 55: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

switch (signo) { case SIGQUIT: printf("SIGQUIT received\n"); if (nalive > 0) { struct drand48_data rand48_buffer; srand48_r(time(NULL), &rand48_buffer); int rc; do { long int char_id; lrand48_r(&rand48_buffer, &char_id); char_id %= NTHREADS; rc = pthread_cancel(tid[char_id]); } while (rc == ESRCH); } break; default: printf("Unspec. signal\n"); exit(1); } } } /* end of the function */

55

Page 56: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

$ ./signal_cleanupI am Rosencrantz and I am writing to the fileI am Guildenstern and I am writing to the fileI am Polonius and I am writing to the fileI am Claudius and I am writing to the fileI am Gertrude and I am writing to the fileI am Ophelia and I am writing to the fileI am Hamlet and I am writing to the fileI am Laertes and I am writing to the fileI am Rosencrantz and I am writing to the fileI am Guildenstern and I am writing to the fileI am Polonius and I am writing to the fileI am Claudius and I am writing to the fileI am Gertrude and I am writing to the fileI am Ophelia and I am writing to the fileI am Hamlet and I am writing to the file

56

Page 57: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

SIGQUIT receivedcleanup_deathkey destructorI am Gertrude and I am writing to the fileI am Guildenstern and I am writing to the fileI am Ophelia and I am writing to the fileI am Laertes and I am writing to the fileI am Polonius and I am writing to the fileI am Hamlet and I am writing to the fileI am Rosencrantz and I am writing to the fileI am Gertrude and I am writing to the fileI am Guildenstern and I am writing to the fileI am Ophelia and I am writing to the fileI am Laertes and I am writing to the fileI am Hamlet and I am writing to the fileI am Polonius and I am writing to the file

pkill -SIGQUIT signal_cleanup

57

Page 58: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

SIGQUIT receivedcleanup_deathkey destructorI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Ophelia and I am writing to the fileI am Rosencrantz and I am writing to the fileI am Hamlet and I am writing to the fileI am Polonius and I am writing to the fileI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Ophelia and I am writing to the fileI am Rosencrantz and I am writing to the fileI am Hamlet and I am writing to the fileI am Polonius and I am writing to the fileI am Rosencrantz and I am writing to the file

pkill -SIGQUIT signal_cleanup

58

Page 59: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

SIGQUIT receivedcleanup_deathkey destructorI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Gertrude and I am writing to the fileI am Laertes and I am writing to the fileI am Gertrude and I am writing to the file

pkill -SIGQUIT signal_cleanup

59

Page 60: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

SIGQUIT receivedcleanup_deathkey destructorI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileI am Gertrude and I am writing to the fileSIGQUIT receivedcleanup_deathThe last character is dead.key destructor

pkill -SIGQUIT signal_cleanup

pkill -SIGQUIT signal_cleanup

60

Page 61: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

$ cat playLaertes was hereRosencrantz was hereGuildenstern was herePolonius was hereClaudius was hereGertrude was herePolonius is deadHamlet was hereLaertes was here…Laertes was hereGertrude was hereHamlet was hereOphelia is deadRosencrantz was here

61

Page 62: ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные потоков. Принудительное завершение

Пример (сигналы, отмена потоков)

Laertes was hereGertrude was hereLaertes was hereGertrude was hereLaertes was hereGertrude was hereLaertes was hereLaertes is deadGertrude was hereGertrude was hereGertrude was hereGertrude was hereGertrude was hereGertrude was hereGertrude is deadThe rest is silence.

62