Изменить тип отмены pthread другого потока?

Что я хочу сделать, так это то, что основной поток сначала пытается выполнить обычную отложенную отмену в рабочем потоке (выполняя код, который для моих целей является черным ящиком), а затем, если поток все еще работает после тайм-аута (pthread_timedjoin_np()), я хочу сделать асинхронную отмену. У меня проблема в том, что pthread_setcanceltype() предназначен только для вызывающего потока. Есть ли обходной путь или хак, который позволит мне это сделать? Я хочу избежать использования сигналов, поскольку, по крайней мере, в Linux кажется, что асинхронная отмена по-прежнему будет выполнять деструкторы C++ объектов потока, что важно для меня.


person Display Name    schedule 06.07.2011    source источник


Ответы (1)


Есть несколько случаев, когда pthread_setcanceltype() действительно должен выполнить отмену (см. исходный код ниже). Итак, это причина, почему нет pthread_setcanceltype_for_thread(). Фактический тип отмены — это поле в структуре pthread, которое должно быть изменено атомарно.

ftp://sources.redhat.com/pub/glibc/snapshots/glibc-latest .tar.bz2/glibc-20090518/nptl/pthread_setcanceltype.c

__pthread_setcanceltype (type, oldtype)
     int type;
     int *oldtype;
{
  volatile struct pthread *self;

  self = THREAD_SELF;

  int oldval = THREAD_GETMEM (self, cancelhandling);
  while (1)
    {
      int newval = (type == PTHREAD_CANCEL_ASYNCHRONOUS
                    ? oldval | CANCELTYPE_BITMASK
                    : oldval & ~CANCELTYPE_BITMASK);

      /* Store the old value.  */
      if (oldtype != NULL)
        *oldtype = ((oldval & CANCELTYPE_BITMASK)
                    ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED);

      /* Update the cancel handling word.  This has to be done
         atomically since other bits could be modified as well.  */
      int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval,
                                              oldval);
      if (__builtin_expect (curval == oldval, 1))
        {
          if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
            {
              THREAD_SETMEM (self, result, PTHREAD_CANCELED);
              __do_cancel ();  // HERE THE CANCELLING
            }

          break;
        }

      /* Prepare for the next round.  */
      oldval = curval;
    }

  return 0;
}
strong_alias (__pthread_setcanceltype, pthread_setcanceltype)

Если у вас есть большая потребность во внешнем изменении типа отмены, вы можете взломать библиотеку и установить поле напрямую.

PS: для NPTL (текущая реализация pthreads в glibc в Linux) самый простой способ увидеть, как получить struct pthread из int pthread_t, это... pthread_join:

 pthread_join (pthread_t threadid, thread_return) 
 { 
     struct pthread *pd = (struct pthread *) threadid;
person osgx    schedule 06.07.2011
comment
Проблема в том, что поле отмены находится не в pthread_t, которое определено как typedef unsigned long int pthread_t;, а в поле struct pthread, которое находится в descr.h. - person Display Name; 07.07.2011
comment
[упс, я редактировал, но время истекло] pthread_t определяется как uint в pthreadtypes.h. Поле отмены находится в struct pthread, которое находится в descr.h. Я не понимаю, как я могу получить это из pthread_t. Код для pthread_cancel(), который вы опубликовали, использует макрос THREAD_SELF, который читает из регистра (по-видимому, потому что он быстрее, чем обычное чтение из локального хранилища потока), но этот регистр будет указывать на конкретный поток, поэтому я не могу получить к нему доступ из другого . В любом случае я могу получить доступ к TLS другого потока или к другому обходному пути? - person Display Name; 07.07.2011
comment
Подсказка 1: память одинакова для обоих потоков. Подсказка 2: отладчик может получить доступ ко всем структурам потоков. - person osgx; 07.07.2011
comment
Самый простой способ увидеть, как pthread_t преобразуется в struct pthread, это ... pthread_join: pthread_join (pthread_t threadid, thread_return) { struct pthread *pd = (struct pthread *) threadid;. Возьми? %) - person osgx; 07.07.2011
comment
Что ж, это упрощает дело. :) Спасибо за помощь. - person Display Name; 07.07.2011
comment
Единственное, что нужно добавить на случай, если кто-то еще попытается это сделать: __do_cancel() — это функция, которая вызывается отменяемым потоком (она также использует THREAD_SELF), поэтому ее также следует изменить, чтобы установить бит выхода и вызвать раскрутку. функция для другого потока. - person Display Name; 07.07.2011
comment
do_cancel не может вызвать раскрутку другого потока. Таким образом, вы не можете изменить тип отмены другого потока на PTHREAD_CANCEL_ASYNCHRONOUS, если ожидается внешний запрос на отмену. (функция __pthread_unwind использует THREAD_SELF). Кроме того, вызов __pthread_unwind (объявленный с помощью __attribute ((noreturn)) ) не может вернуться к вызывающей стороне: /* NOTREACHED / / Нам лучше сюда не попадать. */ прервать (); - person osgx; 07.07.2011
comment
CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS требует установки CANCELED_BITMASK, а pthread_cancel устанавливает ее после отправки SIGCANCEL потоку. Если я выполню pthread_cancel, когда режим отложен, то pthread_timedjoin_np вернет ETIMEDOUT, что можно сказать о том, будет ли установлен CANCELED_BITMASK? - person Display Name; 07.07.2011
comment
@DisplayName, если вам удалось изменить тип другой темы, не могли бы вы рассказать мне, как это сделать? Я пытаюсь сделать то же самое, но если мне придется сделать это без изменения библиотеки, я не могу понять, как получить pthread-›cancelhanling, поскольку структура pthread взята из descr.h, а не из pthread.h. Поэтому, даже если у меня есть указатель на структуру, я не могу изменить ее атрибуты (обработка отмены) - person mathworker; 31.12.2018