# pthread_cancel 與解構子行為實驗

## **Manual**

[pthread\_cancel(3) - Linux manual page](https://man7.org/linux/man-pages/man3/pthread_cancel.3.html)

根據 linux manual 所寫，pthread\_cancel 基本上可以在一些可中斷的地方（POSIX 給了一個清單，大概是 IO 操作的時候）中斷 thread。

這時就好奇了：C++ 的 Destructor 會不會被觸發？這並沒有寫在文件上。

## **Code**

```cpp
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>

struct Obj {
    Obj() {
        puts("constructor");
    }

    ~Obj() {
        puts("destructor");
    }
};

void* try_thread(void*) {
    Obj obj;
    puts("try_thread: start");
    int i;
    for (i = 0; i < 100; ++i) {
        printf("try_thread: %d\n", i);
        sleep(1);
    }
    puts("try_thread: end");
    return 0;
}

int main() {
    pthread_t t;
    pthread_create(&t, NULL, try_thread, NULL);
    sleep(1);
    sleep(1);

    pthread_cancel(t);

    pthread_join(t, NULL);
    puts("joined");
    return 0;
}
```

## **Result**

### gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)

```plaintext
constructor
try_thread: start
try_thread: 0
try_thread: 1
destructor
joined
```

### Apple clang version 15.0.0 (clang-1500.0.40.1)

```plaintext
constructor
try_thread: start
try_thread: 0
try_thread: 1
joined
```

## **glibc Code Tracing**

從結果上來看，glibc 的 pthread\_cancel implement 可以 trigger Destructor ？

[pthread\_cancel.c - nptl/pthread\_cancel.c - Glibc source code (glibc-2.38.9000) - Bootlin](https://elixir.bootlin.com/glibc/latest/source/nptl/pthread_cancel.c#L71)

裡面的機制是註冊一個 signal handler，然後對要 cancel 的 thread 扔 signal ，讓特定 thread 觸發 signal handler 呼叫 `__do_cancel`。

[pthreadP.h - sysdeps/nptl/pthreadP.h - Glibc source code (glibc-2.38.9000) - Bootlin](https://elixir.bootlin.com/glibc/latest/source/sysdeps/nptl/pthreadP.h#L264)

```c
/* Called when a thread reacts on a cancellation request.  */
void __do_cancel () {
  // ...
  __pthread_unwind ((__pthread_unwind_buf_t *)
    THREAD_GETMEM (self, cleanup_jmp_buf));
}
```

裡面呼叫 `__pthread_unwind`

[unwind.c - nptl/unwind.c - Glibc source code (glibc-2.38.9000) - Bootlin](https://elixir.bootlin.com/glibc/latest/source/nptl/unwind.c#L120)

看起來裡面是扔 Exception？所以可以觸發 Destructor。

```c
void __pthread_unwind (__pthread_unwind_buf_t *buf) {
  struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
  struct pthread *self = THREAD_SELF;

  /* This is not a catchable exception, so don't provide any details about
     the exception type.  We do need to initialize the field though.  */
  THREAD_SETMEM (self, exc.exception_class, 0);
  THREAD_SETMEM (self, exc.exception_cleanup, &unwind_cleanup);

  _Unwind_ForcedUnwind (&self->exc, unwind_stop, ibuf);
  /* NOTREACHED */

  /* We better do not get here.  */
  abort ();
}
```

## **Reference**

[pthread\_cancel(3) - Linux manual page](https://man7.org/linux/man-pages/man3/pthread_cancel.3.html)

[Cancelling a thread using pthread\_cancel : good practice or bad](https://stackoverflow.com/questions/4760687/cancelling-a-thread-using-pthread-cancel-good-practice-or-bad)

[pthread\_cancel considered harmful](https://skaark.wordpress.com/2010/08/26/pthread_cancel-considered-harmful/)
