1 /* Copyright (C) 2002-2022 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 <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/mman.h>
26 #include <sys/wait.h>
27 
28 
29 static int
do_test(void)30 do_test (void)
31 {
32   size_t ps = sysconf (_SC_PAGESIZE);
33   char tmpfname[] = "/tmp/tst-spin2.XXXXXX";
34   char data[ps];
35   void *mem;
36   int fd;
37   pthread_spinlock_t *s;
38   pid_t pid;
39   char *p;
40   int err;
41 
42   fd = mkstemp (tmpfname);
43   if (fd == -1)
44     {
45       printf ("cannot open temporary file: %m\n");
46       return 1;
47     }
48 
49   /* Make sure it is always removed.  */
50   unlink (tmpfname);
51 
52   /* Create one page of data.  */
53   memset (data, '\0', ps);
54 
55   /* Write the data to the file.  */
56   if (write (fd, data, ps) != (ssize_t) ps)
57     {
58       puts ("short write");
59       return 1;
60     }
61 
62   mem = mmap (NULL, ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
63   if (mem == MAP_FAILED)
64     {
65       printf ("mmap failed: %m\n");
66       return 1;
67     }
68 
69   s = (pthread_spinlock_t *) (((uintptr_t) mem
70 			       + __alignof (pthread_spinlock_t))
71 			      & ~(__alignof (pthread_spinlock_t) - 1));
72   p = (char *) (s + 1);
73 
74   if (pthread_spin_init (s, PTHREAD_PROCESS_SHARED) != 0)
75     {
76       puts ("spin_init failed");
77       return 1;
78     }
79 
80   if (pthread_spin_lock (s) != 0)
81     {
82       puts ("spin_lock failed");
83       return 1;
84     }
85 
86   err = pthread_spin_trylock (s);
87   if (err == 0)
88     {
89       puts ("1st spin_trylock succeeded");
90       return 1;
91     }
92   else if (err != EBUSY)
93     {
94       puts ("1st spin_trylock didn't return EBUSY");
95       return 1;
96     }
97 
98   err = pthread_spin_unlock (s);
99   if (err != 0)
100     {
101       puts ("parent: spin_unlock failed");
102       return 1;
103     }
104 
105   err = pthread_spin_trylock (s);
106   if (err != 0)
107     {
108       puts ("2nd spin_trylock failed");
109       return 1;
110     }
111 
112   *p = 0;
113 
114   puts ("going to fork now");
115   pid = fork ();
116   if (pid == -1)
117     {
118       puts ("fork failed");
119       return 1;
120     }
121   else if (pid == 0)
122     {
123       /* Play some lock ping-pong.  It's our turn to unlock first.  */
124       if ((*p)++ != 0)
125 	{
126 	  puts ("child: *p != 0");
127 	  return 1;
128 	}
129 
130       if (pthread_spin_unlock (s) != 0)
131 	{
132 	  puts ("child: 1st spin_unlock failed");
133 	  return 1;
134 	}
135 
136       puts ("child done");
137     }
138   else
139     {
140       if (pthread_spin_lock (s) != 0)
141 	{
142 	  puts ("parent: 2nd spin_lock failed");
143 	  return 1;
144 	}
145 
146       puts ("waiting for child");
147 
148       waitpid (pid, NULL, 0);
149 
150       puts ("parent done");
151     }
152 
153   return 0;
154 }
155 
156 #define TEST_FUNCTION do_test ()
157 #include "../test-skeleton.c"
158