1 /* Multithread-safety test for setlocale_null_r (LC_ALL, ...).
2    Copyright (C) 2019-2021 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program 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
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by Bruno Haible <bruno@clisp.org>, 2019.  */
18 
19 #include <config.h>
20 
21 /* Work around GCC bug 44511.  */
22 #if 4 < __GNUC__ + (3 <= __GNUC_MINOR__)
23 # pragma GCC diagnostic ignored "-Wreturn-type"
24 #endif
25 
26 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
27 
28 /* Specification.  */
29 #include <locale.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 
36 #include "glthread/thread.h"
37 
38 /* We want to use the system's setlocale() function here, not the gnulib
39    override.  */
40 #undef setlocale
41 
42 
43 /* Some common locale names.  */
44 
45 #if defined _WIN32 && !defined __CYGWIN__
46 # define ENGLISH "English_United States"
47 # define GERMAN  "German_Germany"
48 # define FRENCH  "French_France"
49 # define ENCODING ".1252"
50 #else
51 # define ENGLISH "en_US"
52 # define GERMAN  "de_DE"
53 # define FRENCH  "fr_FR"
54 # if defined __sgi
55 #  define ENCODING ".ISO8859-15"
56 # elif defined __hpux
57 #  define ENCODING ".utf8"
58 # else
59 #  define ENCODING ".UTF-8"
60 # endif
61 #endif
62 
63 static const char LOCALE1[] = ENGLISH ENCODING;
64 static const char LOCALE2[] = GERMAN ENCODING;
65 static const char LOCALE3[] = FRENCH ENCODING;
66 
67 static char *expected;
68 
69 static void *
thread1_func(void * arg)70 thread1_func (void *arg)
71 {
72   for (;;)
73     {
74       char buf[SETLOCALE_NULL_ALL_MAX];
75 
76       if (setlocale_null_r (LC_ALL, buf, sizeof (buf)))
77         abort ();
78       if (strcmp (expected, buf) != 0)
79         {
80           fprintf (stderr, "thread1 disturbed by thread2!\n"); fflush (stderr);
81           abort ();
82         }
83     }
84 
85   /*NOTREACHED*/
86 }
87 
88 static void *
thread2_func(void * arg)89 thread2_func (void *arg)
90 {
91   for (;;)
92     {
93       char buf[SETLOCALE_NULL_ALL_MAX];
94 
95       setlocale_null_r (LC_NUMERIC, buf, sizeof (buf));
96       setlocale_null_r (LC_ALL, buf, sizeof (buf));
97     }
98 
99   /*NOTREACHED*/
100 }
101 
102 int
main(int argc,char * argv[])103 main (int argc, char *argv[])
104 {
105   if (setlocale (LC_ALL, LOCALE1) == NULL)
106     {
107       fprintf (stderr, "Skipping test: LOCALE1 not recognized\n");
108       return 77;
109     }
110   if (setlocale (LC_NUMERIC, LOCALE2) == NULL)
111     {
112       fprintf (stderr, "Skipping test: LOCALE2 not recognized\n");
113       return 77;
114     }
115   if (setlocale (LC_TIME, LOCALE3) == NULL)
116     {
117       fprintf (stderr, "Skipping test: LOCALE3 not recognized\n");
118       return 77;
119     }
120 
121   expected = strdup (setlocale (LC_ALL, NULL));
122 
123   /* Create the two threads.  */
124   gl_thread_create (thread1_func, NULL);
125   gl_thread_create (thread2_func, NULL);
126 
127   /* Let them run for 5 seconds.  */
128   {
129     struct timespec duration;
130     duration.tv_sec = 5;
131     duration.tv_nsec = 0;
132 
133     nanosleep (&duration, NULL);
134   }
135 
136   return 0;
137 }
138 
139 #else
140 
141 /* No multithreading available.  */
142 
143 #include <stdio.h>
144 
145 int
main()146 main ()
147 {
148   fputs ("Skipping test: multithreading not enabled\n", stderr);
149   return 77;
150 }
151 
152 #endif
153 
154 /* Without locking, the results of this test would be:
155 glibc                OK
156 musl libc            crash < 10 sec
157 macOS                crash < 1 sec
158 FreeBSD              crash < 1 sec
159 NetBSD               crash < 2 sec
160 OpenBSD              crash < 1 sec
161 AIX                  crash < 2 sec
162 HP-UX                OK
163 IRIX                 OK
164 Solaris 10           OK
165 Solaris 11.0         OK
166 Solaris 11.4         OK
167 Solaris OpenIndiana  OK
168 Haiku                crash < 1 sec
169 Cygwin               crash < 1 sec
170 mingw                OK
171 MSVC                 OK (assuming compiler option /MD !)
172 */
173