(root)/
glibc-2.38/
sysdeps/
pthread/
tst-cancel20.c
       1  /* Copyright (C) 2003-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <pthread.h>
      20  #include <signal.h>
      21  #include <stdio.h>
      22  #include <stdlib.h>
      23  #include <unistd.h>
      24  
      25  
      26  static int fd[4];
      27  static pthread_barrier_t b;
      28  volatile int in_sh_body;
      29  unsigned long cleanups;
      30  
      31  static void
      32  cl (void *arg)
      33  {
      34    cleanups = (cleanups << 4) | (long) arg;
      35  }
      36  
      37  
      38  static void __attribute__((noinline))
      39  sh_body (void)
      40  {
      41    char c;
      42  
      43    pthread_cleanup_push (cl, (void *) 1L);
      44  
      45    in_sh_body = 1;
      46    if (read (fd[2], &c, 1) == 1)
      47      {
      48        puts ("read succeeded");
      49        exit (1);
      50      }
      51  
      52    pthread_cleanup_pop (0);
      53  }
      54  
      55  
      56  static void
      57  sh (int sig)
      58  {
      59    pthread_cleanup_push (cl, (void *) 2L);
      60    sh_body ();
      61    in_sh_body = 0;
      62  
      63    pthread_cleanup_pop (0);
      64  }
      65  
      66  
      67  static void __attribute__((noinline))
      68  tf_body (void)
      69  {
      70    char c;
      71  
      72    pthread_cleanup_push (cl, (void *) 3L);
      73  
      74    int r = pthread_barrier_wait (&b);
      75    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
      76      {
      77        puts ("child thread: barrier_wait failed");
      78        exit (1);
      79      }
      80  
      81    if (read (fd[0], &c, 1) == 1)
      82      {
      83        puts ("read succeeded");
      84        exit (1);
      85      }
      86  
      87    pthread_cleanup_pop (0);
      88  }
      89  
      90  
      91  static void *
      92  tf (void *arg)
      93  {
      94    pthread_cleanup_push (cl, (void *) 4L);
      95    tf_body ();
      96    pthread_cleanup_pop (0);
      97    return NULL;
      98  }
      99  
     100  
     101  static int
     102  do_one_test (void)
     103  {
     104    in_sh_body = 0;
     105    cleanups = 0;
     106    if (pipe (fd) != 0 || pipe (fd + 2) != 0)
     107      {
     108        puts ("pipe failed");
     109        return 1;
     110      }
     111  
     112    pthread_t th;
     113    if (pthread_create (&th, NULL, tf, NULL) != 0)
     114      {
     115        puts ("create failed");
     116        return 1;
     117      }
     118  
     119    int r = pthread_barrier_wait (&b);
     120    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
     121      {
     122        puts ("parent thread: barrier_wait failed");
     123        return 1;
     124      }
     125  
     126    sleep (1);
     127  
     128    r = pthread_kill (th, SIGHUP);
     129    if (r)
     130      {
     131        errno = r;
     132        printf ("pthread_kill failed %m\n");
     133        return 1;
     134      }
     135  
     136    while (in_sh_body == 0)
     137      sleep (1);
     138  
     139    if (pthread_cancel (th) != 0)
     140      {
     141        puts ("cancel failed");
     142        return 1;
     143      }
     144  
     145    void *ret;
     146    if (pthread_join (th, &ret) != 0)
     147      {
     148        puts ("join failed");
     149        return 1;
     150      }
     151  
     152    if (ret != PTHREAD_CANCELED)
     153      {
     154        puts ("result is wrong");
     155        return 1;
     156      }
     157  
     158    if (cleanups != 0x1234L)
     159      {
     160        printf ("called cleanups %lx\n", cleanups);
     161        return 1;
     162      }
     163  
     164    /* The pipe closing must be issued after the cancellation handling to avoid
     165       a race condition where the cancellation runs after both pipe ends are
     166       closed.  In this case the read syscall returns EOF and the cancellation
     167       must not act.  */
     168    close (fd[0]);
     169    close (fd[1]);
     170    close (fd[2]);
     171    close (fd[3]);
     172  
     173    return 0;
     174  }
     175  
     176  
     177  static int
     178  do_test (void)
     179  {
     180    stack_t ss;
     181    ss.ss_sp = malloc (2 * SIGSTKSZ);
     182    if (ss.ss_sp == NULL)
     183      {
     184        puts ("failed to allocate alternate stack");
     185        return 1;
     186      }
     187    ss.ss_flags = 0;
     188    ss.ss_size = 2 * SIGSTKSZ;
     189    if (sigaltstack (&ss, NULL) < 0)
     190      {
     191        printf ("sigaltstack failed %m\n");
     192        return 1;
     193      }
     194  
     195    if (pthread_barrier_init (&b, NULL, 2) != 0)
     196      {
     197        puts ("barrier_init failed");
     198        return 1;
     199      }
     200  
     201    struct sigaction sa;
     202    sa.sa_handler = sh;
     203    sigemptyset (&sa.sa_mask);
     204    sa.sa_flags = 0;
     205  
     206    if (sigaction (SIGHUP, &sa, NULL) != 0)
     207      {
     208        puts ("sigaction failed");
     209        return 1;
     210      }
     211  
     212    puts ("sa_flags = 0 test");
     213    if (do_one_test ())
     214      return 1;
     215  
     216    sa.sa_handler = sh;
     217    sigemptyset (&sa.sa_mask);
     218    sa.sa_flags = SA_ONSTACK;
     219  
     220    if (sigaction (SIGHUP, &sa, NULL) != 0)
     221      {
     222        puts ("sigaction failed");
     223        return 1;
     224      }
     225  
     226    puts ("sa_flags = SA_ONSTACK test");
     227    if (do_one_test ())
     228      return 1;
     229  
     230  #ifdef SA_SIGINFO
     231    sa.sa_sigaction = (void (*)(int, siginfo_t *, void *)) sh;
     232    sigemptyset (&sa.sa_mask);
     233    sa.sa_flags = SA_SIGINFO;
     234  
     235    if (sigaction (SIGHUP, &sa, NULL) != 0)
     236      {
     237        puts ("sigaction failed");
     238        return 1;
     239      }
     240  
     241    puts ("sa_flags = SA_SIGINFO test");
     242    if (do_one_test ())
     243      return 1;
     244  
     245    sa.sa_sigaction = (void (*)(int, siginfo_t *, void *)) sh;
     246    sigemptyset (&sa.sa_mask);
     247    sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
     248  
     249    if (sigaction (SIGHUP, &sa, NULL) != 0)
     250      {
     251        puts ("sigaction failed");
     252        return 1;
     253      }
     254  
     255    puts ("sa_flags = SA_SIGINFO|SA_ONSTACK test");
     256    if (do_one_test ())
     257      return 1;
     258  #endif
     259  
     260    return 0;
     261  }
     262  
     263  #define TIMEOUT 40
     264  #define TEST_FUNCTION do_test ()
     265  #include "../test-skeleton.c"