(root)/
glibc-2.38/
nptl/
tst-cancel17.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 <aio.h>
      19  #include <errno.h>
      20  #include <pthread.h>
      21  #include <stdio.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include <unistd.h>
      25  
      26  
      27  static pthread_barrier_t b;
      28  
      29  
      30  /* Cleanup handling test.  */
      31  static int cl_called;
      32  
      33  static void
      34  cl (void *arg)
      35  {
      36    ++cl_called;
      37  }
      38  
      39  
      40  static void *
      41  tf (void *arg)
      42  {
      43    int r = pthread_barrier_wait (&b);
      44    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
      45      {
      46        puts ("tf: barrier_wait failed");
      47        exit (1);
      48      }
      49  
      50    pthread_cleanup_push (cl, NULL);
      51  
      52    const struct aiocb *l[1] = { arg };
      53  
      54    TEMP_FAILURE_RETRY (aio_suspend (l, 1, NULL));
      55  
      56    pthread_cleanup_pop (0);
      57  
      58    puts ("tf: aio_suspend returned");
      59  
      60    exit (1);
      61  }
      62  
      63  
      64  static void *
      65  tf2 (void *arg)
      66  {
      67    int r = pthread_barrier_wait (&b);
      68    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
      69      {
      70        puts ("tf2: barrier_wait failed");
      71        exit (1);
      72      }
      73  
      74    pthread_cleanup_push (cl, NULL);
      75  
      76    const struct aiocb *l[1] = { arg };
      77    struct timespec ts = { .tv_sec = 1000, .tv_nsec = 0 };
      78  
      79    TEMP_FAILURE_RETRY (aio_suspend (l, 1, &ts));
      80  
      81    pthread_cleanup_pop (0);
      82  
      83    puts ("tf2: aio_suspend returned");
      84  
      85    exit (1);
      86  }
      87  
      88  
      89  static int
      90  do_test (void)
      91  {
      92    int fds[2];
      93    if (pipe (fds) != 0)
      94      {
      95        puts ("pipe failed");
      96        return 1;
      97      }
      98  
      99    struct aiocb a, a2, *ap;
     100    char mem[1];
     101    memset (&a, '\0', sizeof (a));
     102    a.aio_fildes = fds[0];
     103    a.aio_buf = mem;
     104    a.aio_nbytes = sizeof (mem);
     105    if (aio_read (&a) != 0)
     106      {
     107        puts ("aio_read failed");
     108        return 1;
     109      }
     110  
     111    if (pthread_barrier_init (&b, NULL, 2) != 0)
     112      {
     113        puts ("barrier_init failed");
     114        return 1;
     115      }
     116  
     117    pthread_t th;
     118    if (pthread_create (&th, NULL, tf, &a) != 0)
     119      {
     120        puts ("1st create failed");
     121        return 1;
     122      }
     123  
     124    int r = pthread_barrier_wait (&b);
     125    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
     126      {
     127        puts ("barrier_wait failed");
     128        exit (1);
     129      }
     130  
     131    struct timespec  ts = { .tv_sec = 0, .tv_nsec = 100000000 };
     132    while (nanosleep (&ts, &ts) != 0)
     133      continue;
     134  
     135    puts ("going to cancel tf in-time");
     136    if (pthread_cancel (th) != 0)
     137      {
     138        puts ("1st cancel failed");
     139        return 1;
     140      }
     141  
     142    void *status;
     143    if (pthread_join (th, &status) != 0)
     144      {
     145        puts ("1st join failed");
     146        return 1;
     147      }
     148    if (status != PTHREAD_CANCELED)
     149      {
     150        puts ("1st thread not canceled");
     151        return 1;
     152      }
     153  
     154    if (cl_called == 0)
     155      {
     156        puts ("tf cleanup handler not called");
     157        return 1;
     158      }
     159    if (cl_called > 1)
     160      {
     161        puts ("tf cleanup handler called more than once");
     162        return 1;
     163      }
     164  
     165    cl_called = 0;
     166  
     167    if (pthread_create (&th, NULL, tf2, &a) != 0)
     168      {
     169        puts ("2nd create failed");
     170        return 1;
     171      }
     172  
     173    r = pthread_barrier_wait (&b);
     174    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
     175      {
     176        puts ("2nd barrier_wait failed");
     177        exit (1);
     178      }
     179  
     180    ts.tv_sec = 0;
     181    ts.tv_nsec = 100000000;
     182    while (nanosleep (&ts, &ts) != 0)
     183      continue;
     184  
     185    puts ("going to cancel tf2 in-time");
     186    if (pthread_cancel (th) != 0)
     187      {
     188        puts ("2nd cancel failed");
     189        return 1;
     190      }
     191  
     192    if (pthread_join (th, &status) != 0)
     193      {
     194        puts ("2nd join failed");
     195        return 1;
     196      }
     197    if (status != PTHREAD_CANCELED)
     198      {
     199        puts ("2nd thread not canceled");
     200        return 1;
     201      }
     202  
     203    if (cl_called == 0)
     204      {
     205        puts ("tf2 cleanup handler not called");
     206        return 1;
     207      }
     208    if (cl_called > 1)
     209      {
     210        puts ("tf2 cleanup handler called more than once");
     211        return 1;
     212      }
     213  
     214    puts ("in-time cancellation succeeded");
     215  
     216    ap = &a;
     217    if (aio_cancel (fds[0], &a) != AIO_CANCELED)
     218      {
     219        puts ("aio_cancel failed");
     220        /* If aio_cancel failed, we cannot reuse aiocb a.  */
     221        ap = &a2;
     222      }
     223  
     224  
     225    cl_called = 0;
     226  
     227    size_t len2 = fpathconf (fds[1], _PC_PIPE_BUF);
     228    size_t page_size = sysconf (_SC_PAGESIZE);
     229    len2 = 20 * (len2 < page_size ? page_size : len2) + sizeof (mem) + 1;
     230    char *mem2 = malloc (len2);
     231    if (mem2 == NULL)
     232      {
     233        puts ("could not allocate memory for pipe write");
     234        return 1;
     235      }
     236  
     237    memset (ap, '\0', sizeof (*ap));
     238    ap->aio_fildes = fds[1];
     239    ap->aio_buf = mem2;
     240    ap->aio_nbytes = len2;
     241    if (aio_write (ap) != 0)
     242      {
     243        puts ("aio_write failed");
     244        return 1;
     245      }
     246  
     247    if (pthread_create (&th, NULL, tf, ap) != 0)
     248      {
     249        puts ("3rd create failed");
     250        return 1;
     251      }
     252  
     253    puts ("going to cancel tf early");
     254    if (pthread_cancel (th) != 0)
     255      {
     256        puts ("3rd cancel failed");
     257        return 1;
     258      }
     259  
     260    r = pthread_barrier_wait (&b);
     261    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
     262      {
     263        puts ("3rd barrier_wait failed");
     264        exit (1);
     265      }
     266  
     267    if (pthread_join (th, &status) != 0)
     268      {
     269        puts ("3rd join failed");
     270        return 1;
     271      }
     272    if (status != PTHREAD_CANCELED)
     273      {
     274        puts ("3rd thread not canceled");
     275        return 1;
     276      }
     277  
     278    if (cl_called == 0)
     279      {
     280        puts ("tf cleanup handler not called");
     281        return 1;
     282      }
     283    if (cl_called > 1)
     284      {
     285        puts ("tf cleanup handler called more than once");
     286        return 1;
     287      }
     288  
     289    cl_called = 0;
     290  
     291    if (pthread_create (&th, NULL, tf2, ap) != 0)
     292      {
     293        puts ("4th create failed");
     294        return 1;
     295      }
     296  
     297    puts ("going to cancel tf2 early");
     298    if (pthread_cancel (th) != 0)
     299      {
     300        puts ("4th cancel failed");
     301        return 1;
     302      }
     303  
     304    r = pthread_barrier_wait (&b);
     305    if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
     306      {
     307        puts ("4th barrier_wait failed");
     308        exit (1);
     309      }
     310  
     311    if (pthread_join (th, &status) != 0)
     312      {
     313        puts ("4th join failed");
     314        return 1;
     315      }
     316    if (status != PTHREAD_CANCELED)
     317      {
     318        puts ("4th thread not canceled");
     319        return 1;
     320      }
     321  
     322    if (cl_called == 0)
     323      {
     324        puts ("tf2 cleanup handler not called");
     325        return 1;
     326      }
     327    if (cl_called > 1)
     328      {
     329        puts ("tf2 cleanup handler called more than once");
     330        return 1;
     331      }
     332  
     333    puts ("early cancellation succeeded");
     334  
     335    if (ap == &a2)
     336      {
     337        /* The aio_read(&a) was not canceled because the read request was
     338  	 already in progress. In the meanwhile aio_write(ap) wrote something
     339  	 to the pipe and the read request either has already been finished or
     340  	 is able to read the requested byte.
     341  	 Wait for the read request before returning from this function because
     342  	 the return value and error code from the read syscall will be written
     343  	 to the struct aiocb a, which lies on the stack of this function.
     344  	 Otherwise the stack from subsequent function calls - e.g. _dl_fini -
     345  	 will be corrupted, which can lead to undefined behaviour like a
     346  	 segmentation fault.  */
     347        const struct aiocb *l[1] = { &a };
     348        TEMP_FAILURE_RETRY (aio_suspend(l, 1, NULL));
     349      }
     350  
     351    return 0;
     352  }
     353  
     354  #define TEST_FUNCTION do_test ()
     355  #include "../test-skeleton.c"