aboutsummaryrefslogtreecommitdiff
path: root/testcases/open_posix_testsuite/conformance/interfaces/mq_timedsend/12-1.c
blob: 276aee65dce4cd60534c552d6b1f5c9bbaa37cca (plain)
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
/*
 * Copyright (c) 2003, Intel Corporation. All rights reserved.
 * Created by:  julie.n.fleischer REMOVE-THIS AT intel DOT com
 * This file is licensed under the GPL license.  For the full content
 * of this license, see the COPYING file at the top level of this
 * source tree.
 */

/*
 * Test mq_timedsend() will set errno == EINTR if it is interrupted by a signal.
 *
 * Steps:
 * 1. Create a thread and set up a signal handler for SIGUSR1
 * 2. Thread indicates to main that it is ready to start calling mq_timedsend
 *    until it blocks for a timeout of 10 seconds.
 * 3. In main, send the thread the SIGUSR1 signal while mq_timedsend is
 *    blocking.
 * 4. Check to make sure that mq_timedsend blocked, and that it returned
 *    EINTR when it was interrupted by SIGUSR1.
 */

#include <stdio.h>
#include <pthread.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include "posixtest.h"

#define NAMESIZE 50
#define MSGSTR "0123456789"
#define BUFFER 40
#define MAXMSG 10
#define TIMEOUT 10		/* seconds mq_timedsend will block */
#define SIGNAL_DELAY_MS 50	/* delay in ms between 2 signals */

#define error_and_exit(en, msg) \
	do { errno = en; perror(msg); exit(PTS_UNRESOLVED); } while (0)

/* variable to indicate how many times signal handler was called */
static volatile sig_atomic_t in_handler;

/* errno returned by mq_timedsend() */
static int mq_timedsend_errno = -1;

pthread_barrier_t barrier;

/*
 * This handler is just used to catch the signal and stop sleep (so the
 * parent knows the child is still busy sending signals).
 */
void justreturn_handler(int signo)
{
	in_handler++;
}

void *a_thread_func(void *arg)
{
	int i, ret;
	struct sigaction act;
	char gqname[NAMESIZE];
	mqd_t gqueue;
	const char *msgptr = MSGSTR;
	struct mq_attr attr;
	struct timespec ts;

	/* Set up handler for SIGUSR1 */
	act.sa_handler = justreturn_handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGUSR1, &act, 0);

	/* Set up mq */
	sprintf(gqname, "/mq_timedsend_12-1_%d", getpid());

	attr.mq_maxmsg = MAXMSG;
	attr.mq_msgsize = BUFFER;
	gqueue = mq_open(gqname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, &attr);
	if (gqueue == (mqd_t) -1)
		error_and_exit(errno, "mq_open");

	/* mq_timedsend will block for TIMEOUT seconds when it waits */
	ts.tv_sec = time(NULL) + TIMEOUT;
	ts.tv_nsec = 0;

	/* main can now start sending SIGUSR1 signal */
	ret = pthread_barrier_wait(&barrier);
	if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
		error_and_exit(ret, "pthread_barrier_wait start");

	for (i = 0; i < MAXMSG + 1; i++) {
		ret = mq_timedsend(gqueue, msgptr, strlen(msgptr), 1, &ts);
		if (ret == -1) {
			mq_timedsend_errno = errno;
			break;
		}
	}

	if (mq_unlink(gqname) != 0)
		error_and_exit(errno, "mq_unlink");

	switch (mq_timedsend_errno) {
	case -1:
		mq_timedsend_errno = 0;
		printf("Error: mq_timedsend wasn't interrupted\n");
		break;
	case EINTR:
		printf("thread: mq_timedsend interrupted by signal"
			" and correctly set errno to EINTR\n");
		break;
	default:
		printf("mq_timedsend not interrupted by signal or"
			" set errno to incorrect code: %d\n",
			mq_timedsend_errno);
		break;
	}

	/* wait until main stops sending signals */
	ret = pthread_barrier_wait(&barrier);
	if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
		error_and_exit(ret, "pthread_barrier_wait end");
	pthread_exit(NULL);
}

int main(void)
{
	pthread_t new_th;
	int i = 0, ret;

	ret = pthread_barrier_init(&barrier, NULL, 2);
	if (ret != 0)
		error_and_exit(ret, "pthread_barrier_init");

	ret = pthread_create(&new_th, NULL, a_thread_func, NULL);
	if (ret != 0)
		error_and_exit(ret, "pthread_create");

	/* wait for thread to start */
	ret = pthread_barrier_wait(&barrier);
	if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
		error_and_exit(ret, "pthread_barrier_wait start");

	struct timespec completion_wait_ts = {0, SIGNAL_DELAY_MS*1000000};
	while (i < TIMEOUT*1000 && mq_timedsend_errno < 0) {
		/* signal thread while it's in mq_timedsend */
		ret = pthread_kill(new_th, SIGUSR1);
		if (ret != 0)
			error_and_exit(ret, "pthread_kill");
		nanosleep(&completion_wait_ts, NULL);
		i += SIGNAL_DELAY_MS;
	}

	/* thread can now safely exit */
	ret = pthread_barrier_wait(&barrier);
	if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
		error_and_exit(ret, "pthread_barrier_wait end");

	ret = pthread_join(new_th, NULL);
	if (ret != 0)
		error_and_exit(ret, "pthread_join");

	/* Test to see if the thread blocked correctly in mq_timedsend,
	 * and if it returned EINTR when it caught the signal */
	if (mq_timedsend_errno != EINTR) {
		printf("Error: mq_timedsend was NOT interrupted\n");
		printf(" signal handler was called %d times\n", in_handler);
		printf(" SIGUSR1 signals sent: %d\n", i);
		printf(" last mq_timedsend errno: %d %s\n",
			mq_timedsend_errno, strerror(mq_timedsend_errno));
		if (in_handler == 0) {
			printf("Error: SIGUSR1 was never received\n");
			return PTS_UNRESOLVED;
		}
		return PTS_FAIL;
	}

	ret = pthread_barrier_destroy(&barrier);
	if (ret != 0)
		error_and_exit(ret, "pthread_barrier_destroy");

	printf("Test PASSED\n");
	return PTS_PASS;
}