summary refs log tree commit diff
path: root/nptl/tst-pthread-attr-sigmask.c
blob: 8f854d8d11cbe7ce53b6243ebc5fcabc74e11e68 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/* Tests for pthread_attr_setsigmask_np, pthread_attr_getsigmask_np.
   Copyright (C) 2020 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

/* This thread uses different masked status for SIGUSR1, SIGUSR2,
   SIGHUP to determine if signal masks are applied to new threads as
   expected.  */

#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <support/check.h>
#include <support/xsignal.h>
#include <support/xthread.h>
#include <threads.h>

typedef bool signals[_NSIG];

static const char *
masked_or_unmasked (bool masked)
{
  if (masked)
    return "masked";
  else
    return "unmasked";
}

/* Report an error if ACTUAL_MASK does not match EXPECTED_MASK.
   CONTEXT is used in error messages.  */
static void
check_sigmask (const char *context, signals expected_mask,
               const sigset_t *actual_mask)
{
  for (int sig = 1; sig < _NSIG; ++sig)
    if (sigismember (actual_mask, sig) != expected_mask[sig])
      {
        support_record_failure ();
        printf ("error: %s: signal %d should be %s, but is %s\n",
                context, sig,
                masked_or_unmasked (sigismember (actual_mask, sig)),
                masked_or_unmasked (expected_mask[sig]));
      }
}

/* Report an error if the current thread signal mask does not match
   EXPECTED_MASK.  CONTEXT is used in error messages.  */
static void
check_current_sigmask (const char *context, signals expected_mask)
{
  sigset_t actual_mask;
  xpthread_sigmask (SIG_SETMASK, NULL, &actual_mask);
  check_sigmask (context, expected_mask, &actual_mask);
}

/* Thread start routine which checks the current thread signal mask
   against CLOSURE.  */
static void *
check_sigmask_thread_function (void *closure)
{
  check_current_sigmask ("on thread", closure);
  return NULL;
}

/* Same for C11 threads.  */
static int
check_sigmask_thread_function_c11 (void *closure)
{
  check_current_sigmask ("on C11 thread", closure);
  return 0;
}

/* Launch a POSIX thread with ATTR (which can be NULL) and check that
   it has the expected signal mask.  */
static void
check_posix_thread (pthread_attr_t *attr, signals expected_mask)
{
  xpthread_join (xpthread_create (attr, check_sigmask_thread_function,
                                  expected_mask));
}

/* Launch a C11 thread and check that it has the expected signal
   mask.  */
static void
check_c11_thread (signals expected_mask)
{
  thrd_t thr;
  TEST_VERIFY_EXIT (thrd_create (&thr, check_sigmask_thread_function_c11,
                                 expected_mask) == thrd_success);
  TEST_VERIFY_EXIT (thrd_join (thr, NULL) == thrd_success);
}

static int
do_test (void)
{
  check_current_sigmask ("initial mask", (signals) { false, });
  check_posix_thread (NULL, (signals) { false, });
  check_c11_thread ((signals) { false, });

  sigset_t set;
  sigemptyset (&set);
  sigaddset (&set, SIGUSR1);
  xpthread_sigmask (SIG_SETMASK, &set, NULL);
  check_current_sigmask ("SIGUSR1 masked", (signals) { [SIGUSR1] = true, });
  /* The signal mask is inherited by the new thread.  */
  check_posix_thread (NULL, (signals) { [SIGUSR1] = true, });
  check_c11_thread ((signals) { [SIGUSR1] = true, });

  pthread_attr_t attr;
  xpthread_attr_init (&attr);
  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set),
                PTHREAD_ATTR_NO_SIGMASK_NP);
  /* By default, the signal mask is inherited (even with an explicit
     thread attribute).  */
  check_posix_thread (&attr, (signals) { [SIGUSR1] = true, });

  /* Check that pthread_attr_getsigmask_np can obtain the signal
     mask.  */
  sigemptyset (&set);
  sigaddset (&set, SIGUSR2);
  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, &set), 0);
  sigemptyset (&set);
  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set), 0);
  check_sigmask ("pthread_attr_getsigmask_np", (signals) { [SIGUSR2] = true, },
                 &set);

  /* Check that a thread is launched with the configured signal
     mask.  */
  check_current_sigmask ("SIGUSR1 masked", (signals) { [SIGUSR1] = true, });
  check_posix_thread (&attr, (signals) { [SIGUSR2] = true, });
  check_current_sigmask ("SIGUSR1 masked", (signals) { [SIGUSR1] = true, });

  /* But C11 threads remain at inheritance.  */
  check_c11_thread ((signals) { [SIGUSR1] = true, });

  /* Check that filling the original signal set does not affect thread
     creation.  */
  sigfillset (&set);
  check_posix_thread (&attr, (signals) { [SIGUSR2] = true, });

  /* Check that clearing the signal in the attribute restores
     inheritance.  */
  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, NULL), 0);
  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set),
                PTHREAD_ATTR_NO_SIGMASK_NP);
  check_posix_thread (&attr, (signals) { [SIGUSR1] = true, });

  /* Mask SIGHUP via the default thread attribute.  */
  sigemptyset (&set);
  sigaddset (&set, SIGHUP);
  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, &set), 0);
  TEST_COMPARE (pthread_setattr_default_np (&attr), 0);

  /* Check that the mask was applied to the default attribute.  */
  xpthread_attr_destroy (&attr);
  TEST_COMPARE (pthread_getattr_default_np (&attr), 0);
  sigaddset (&set, SIGHUP);
  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set), 0);
  check_sigmask ("default attribute", (signals) { [SIGHUP] = true, }, &set);
  xpthread_attr_destroy (&attr);

  /* Check that the default attribute is applied.  */
  check_posix_thread (NULL, (signals) { [SIGHUP] = true, });
  check_c11_thread ((signals) { [SIGHUP] = true, });

  /* An explicit attribute with no signal mask triggers inheritance
     even if the default has been changed.  */
  xpthread_attr_init (&attr);
  check_posix_thread (&attr, (signals) { [SIGUSR1] = true, });

  /* Explicitly setting the signal mask affects the new thread even
     with a default attribute.  */
  sigemptyset (&set);
  sigaddset (&set, SIGUSR2);
  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, &set), 0);
  check_posix_thread (&attr, (signals) { [SIGUSR2] = true, });

  /* Resetting the default attribute brings back the old inheritance
     behavior.  */
  xpthread_attr_destroy (&attr);
  xpthread_attr_init (&attr);
  TEST_COMPARE (pthread_setattr_default_np (&attr), 0);
  xpthread_attr_destroy (&attr);
  check_posix_thread (NULL, (signals) { [SIGUSR1] = true, });
  check_c11_thread ((signals) { [SIGUSR1] = true, });

  return 0;
}

#include <support/test-driver.c>