1/*
2 * Copyright (c) 2002, Intel Corporation. All rights reserved.
3 * This file is licensed under the GPL license.  For the full content
4 * of this license, see the COPYING file at the top level of this
5 * source tree.
6 *
7 *	Test pthread_spin_init(pthread_spinlock_t *lock, int pshared)
8 *
9 *	If the Thread Process-Shared Synchronization option is supported and
10 *	the value of pshared is PTHREAD_PROCESS_SHARED, the implementation shall
11 *	permit the spin lock to be opreated upon by any thread that has access
12 *	to the memory where the spin lock is allocated, even if it is allocated
13 *	in memory that is shared by multiple processes.
14 *
15 * steps:
16 *	1. Create a piece of shared memory object, create a spin lock 'spinlock' and
17 *	   set the PTHREAD_PROCESS_SHARED attribute.
18 *	2. Parent map the shared memory to its memory space, put 'spinlock' into it;
19 *	3. Parent get the spin lock;
20 *	4. Fork to create child
21 *	5. Child map the shared memory to its memory space;
22 *	6. Child call pthread_spin_trylock(), should fail with EBUSY
23 */
24
25
26#define _XOPEN_SOURCE 600
27#include <pthread.h>
28#include <stdio.h>
29#include <unistd.h>
30#include <errno.h>
31#include <sys/mman.h>
32#include <fcntl.h>
33#include <sys/wait.h>
34#include "posixtest.h"
35
36struct shmstruct{
37	pthread_spinlock_t spinlock;
38	int data;
39} *spinlock_data;
40
41int main()
42{
43
44	/* Make sure there is process-shared capability. */
45	#ifndef PTHREAD_PROCESS_SHARED
46	  fprintf(stderr,"process-shared attribute is not available for testing\n");
47	  return PTS_UNSUPPORTED;
48	#endif
49
50	int pshared = PTHREAD_PROCESS_SHARED;
51
52	char shm_name[] = "tmp_pthread_spinlock_getpshared";
53	int shm_fd;
54	int pid;
55
56	/* Create shared object */
57	shm_unlink(shm_name);
58	shm_fd = shm_open(shm_name, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
59	if(shm_fd == -1)
60	{
61		perror("Error at shm_open()");
62		return PTS_UNRESOLVED;
63	}
64
65        if(ftruncate(shm_fd, sizeof(struct shmstruct)) != 0) {
66                perror("Error at ftruncate()");
67                shm_unlink(shm_name);
68                return PTS_UNRESOLVED;
69        }
70
71	/* Map the shared memory object to parent's memory */
72	spinlock_data = mmap(NULL, sizeof(struct shmstruct), PROT_READ|PROT_WRITE,
73				MAP_SHARED, shm_fd, 0);
74
75	if(spinlock_data == MAP_FAILED)
76	{
77		perror("Error at first mmap()");
78                shm_unlink(shm_name);
79		return PTS_UNRESOLVED;
80	}
81
82	/* Initialize spinlock */
83	if((pthread_spin_init(&(spinlock_data->spinlock), pshared)) != 0)
84	{
85		printf("Test FAILED: Error at pthread_rwlock_init()\n");
86		return PTS_FAIL;
87	}
88
89	printf("main: attempt spin lock\n");
90	if((pthread_spin_lock(&(spinlock_data->spinlock))) != 0)
91	{
92		printf("Error at pthread_spin_lock()\n");
93		return PTS_UNRESOLVED;
94	}
95	printf("main: acquired spin lock\n");
96
97	/* Initialize spinlock data */
98	spinlock_data->data = 0;
99
100	/* Fork a child process */
101	pid = fork();
102	if(pid == -1)
103	{
104		perror("Error at fork()");
105		return PTS_UNRESOLVED;
106	}
107	else if(pid > 0)
108	{
109		/* Parent */
110		/* wait until child writes to spinlock data */
111		while(spinlock_data->data != 1)
112			sleep(1);
113
114		printf("main: unlock spin lock\n");
115		if(pthread_spin_unlock(&(spinlock_data->spinlock)) != 0)
116		{
117			printf("Parent: error at pthread_spin_unlock()\n");
118			return PTS_UNRESOLVED;
119		}
120
121		/* Tell child that parent unlocked the spin lock */
122		spinlock_data->data = 2;
123
124		/* Wait until child ends */
125		wait(NULL);
126
127		if((shm_unlink(shm_name)) != 0)
128		{
129			perror("Error at shm_unlink()");
130			return PTS_UNRESOLVED;
131		}
132
133		printf("Test PASSED\n");
134		return PTS_PASS;
135	}
136	else
137	{
138		/* Child */
139		/* Map the shared object to child's memory */
140		spinlock_data = mmap(NULL, sizeof(struct shmstruct), PROT_READ|PROT_WRITE,
141				MAP_SHARED, shm_fd, 0);
142
143		if(spinlock_data == MAP_FAILED)
144		{
145			perror("child : Error at mmap()");
146			return PTS_UNRESOLVED;
147		}
148
149		printf("child: attempt spin lock\n");
150		if((pthread_spin_trylock(&(spinlock_data->spinlock))) != EBUSY)
151		{
152			printf("Test FAILED: Child expects EBUSY\n");
153			return PTS_FAIL;
154		}
155		printf("child: correctly got EBUSY\n");
156
157		/* Tell parent it can unlock now */
158		spinlock_data->data = 1;
159
160		/* Wait for parent to unlock spinlock */
161		while(spinlock_data->data != 2)
162			sleep(1);
163
164		/* Child tries to get spin lock after parent unlock,
165		 * it should get the lock. */
166		printf("child: attempt spin lock\n");
167		if((pthread_spin_trylock(&(spinlock_data->spinlock))) != 0)
168		{
169			printf("Test FAILED: Child should get the lock\n");
170			return PTS_FAIL;
171		}
172		printf("child: acquired spin lock\n");
173
174		printf("child: unlock spin lock\n");
175		if(pthread_spin_unlock(&(spinlock_data->spinlock)) != 0)
176		{
177			printf("Child: error at pthread_spin_unlock()\n");
178			return PTS_UNRESOLVED;
179		}
180
181		if(pthread_spin_destroy(&(spinlock_data->spinlock)) != 0)
182		{
183			printf("Child: error at pthread_spin_destroy()\n");
184			return PTS_UNRESOLVED;
185		}
186	}
187}
188