xref: /freebsd/tools/test/stress2/misc/pthread2.sh (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
1#!/bin/sh
2
3#
4# Copyright (c) 2014 EMC Corp.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28
29# Threaded producer-consumer test.
30
31. ../default.cfg
32
33export LANG=C
34here=`pwd`
35cd /tmp
36sed '1,/^EOF/d' < $here/$0 > pthread2.c
37mycc -o pthread2 -Wall -Wextra -O2 -g pthread2.c -lpthread || exit 1
38rm -f pthread2.c /tmp/pthread2.core
39
40log=/tmp/pthread2.`date '+%Y%m%d-%H%M'`
41for i in `jot 5`; do
42	[ $i -eq 1 ] && echo "# `uname -v`"
43	time sh -c '
44	for i in `jot 8`; do
45		/tmp/pthread2 &
46	done
47	wait
48	'
49done > $log 2>&1
50rm -f /tmp/pthread2
51
52if [ -n "$bench" ]; then
53	pair=`ls /tmp/pthread2* | egrep "pthread2\.[0-9]{8}-" | sort |
54	    tail -2 | tr '\n' ' '`
55	ministat -w 72 $pair
56else
57	rm -f $log
58fi
59
60#  __thr_umutex_lock() may call abort(3) under VM pressure.
61[ -r /tmp/pthread2.core ] && echo FAIL
62exit 0
63EOF
64/*
65 * Threaded producer-consumer test.
66 * Loosly based on work by
67 * Andrey Zonov (c) 2012
68 */
69
70#include <sys/types.h>
71#include <sys/stat.h>
72#include <sys/time.h>
73#include <sys/queue.h>
74#include <err.h>
75#include <errno.h>
76#include <fcntl.h>
77#include <pthread.h>
78#ifdef __FreeBSD__
79#include <pthread_np.h>
80#define	__NP__
81#endif
82#include <sched.h>
83#include <signal.h>
84#include <stdio.h>
85#include <stdlib.h>
86#include <string.h>
87#include <time.h>
88#include <unistd.h>
89
90#define	LOCK(x)		plock(&x.mtx)
91#define	UNLOCK(x)	punlock(&x.mtx)
92#define	SIGNAL(x)	psig(&x.wait)
93#define	WAIT(x)		pwait(&x.wait, &x.mtx)
94
95long ncreate, nrename, nunlink;
96int bench, max;
97char *dirname1;
98char *dirname2;
99
100struct file {
101	char *name;
102	STAILQ_ENTRY(file) next;
103};
104
105struct files {
106	pthread_mutex_t mtx;
107	pthread_cond_t wait;
108	STAILQ_HEAD(, file) list;
109};
110
111static struct files newfiles;
112static struct files renamedfiles;
113
114#define MAXQ 100000		/* Max create queue length */
115#define MESSAGES 10000000;
116
117static void
118hand(int i __unused) {	/* handler */
119	fprintf(stderr, "max = %d, ncreate = %ld, nrename = %ld, nunlink = %ld\n",
120			max, ncreate, nrename, nunlink);
121}
122
123static void
124ahand(int i __unused) {	/* handler */
125	fprintf(stderr, "FAIL\n");
126	hand(0);
127	_exit(0);
128}
129
130void
131plock(pthread_mutex_t *l)
132{
133	int rc;
134
135	if ((rc = pthread_mutex_lock(l)) != 0)
136		errc(1, rc, "pthread_mutex_lock");
137}
138
139void
140punlock(pthread_mutex_t *l)
141{
142	int rc;
143
144	if ((rc = pthread_mutex_unlock(l)) != 0)
145		errc(1, rc, "pthread_mutex_unlock");
146}
147
148void
149psig(pthread_cond_t *c)
150{
151	int rc;
152
153	if ((rc = pthread_cond_signal(c)) != 0)
154		errc(1, rc, "pthread_cond_signal");
155}
156
157void
158pwait(pthread_cond_t *c, pthread_mutex_t *l)
159{
160	int rc;
161
162	if ((rc = pthread_cond_wait(c, l)) != 0)
163		errc(1, rc, "pthread_cond_wait");
164}
165
166void *
167loop_create(void *arg __unused)
168{
169	int i;
170	struct file *file;
171
172#ifdef __NP__
173	pthread_set_name_np(pthread_self(), __func__);
174#endif
175
176	for (i = 0; i < max; i++) {
177		file = malloc(sizeof(*file));
178		asprintf(&file->name, "%s/filename_too-long:%d", dirname1, i);
179		LOCK(newfiles);
180		STAILQ_INSERT_TAIL(&newfiles.list, file, next);
181		ncreate++;
182		UNLOCK(newfiles);
183		SIGNAL(newfiles);
184		if (ncreate - nrename > MAXQ)
185			usleep(400);
186	}
187	return (NULL);
188}
189
190void *
191loop_rename(void *arg __unused)
192{
193	char *filename, *newname;
194	struct file *file;
195
196#ifdef	__NP__
197	pthread_set_name_np(pthread_self(), __func__);
198#endif
199
200	while (nrename < max) {
201		LOCK(newfiles);
202		while (STAILQ_EMPTY(&newfiles.list)) {
203			WAIT(newfiles);
204		}
205		file = STAILQ_FIRST(&newfiles.list);
206		STAILQ_REMOVE_HEAD(&newfiles.list, next);
207		UNLOCK(newfiles);
208		filename = strrchr(file->name, '/');
209		asprintf(&newname, "%s/%s", dirname2, filename);
210		nrename++;
211		free(file->name);
212		file->name = newname;
213		LOCK(renamedfiles);
214		STAILQ_INSERT_TAIL(&renamedfiles.list, file, next);
215		UNLOCK(renamedfiles);
216		SIGNAL(renamedfiles);
217	}
218	return (NULL);
219}
220
221void *
222loop_unlink(void *arg __unused)
223{
224	struct file *file;
225
226#ifdef	__NP__
227	pthread_set_name_np(pthread_self(), __func__);
228#endif
229
230	while (nunlink < max) {
231		LOCK(renamedfiles);
232		while (STAILQ_EMPTY(&renamedfiles.list)) {
233			WAIT(renamedfiles);
234		}
235		file = STAILQ_FIRST(&renamedfiles.list);
236		STAILQ_REMOVE_HEAD(&renamedfiles.list, next);
237		nunlink++;
238		UNLOCK(renamedfiles);
239		free(file->name);
240		free(file);
241	}
242	return (NULL);
243}
244
245int
246main(void)
247{
248	int i;
249	int rc;
250	pthread_t tid[3];
251
252	bench = getenv("bench") != NULL;
253	asprintf(&dirname1, "%s.1", "f1");
254	asprintf(&dirname2, "%s.2", "f2");
255	max = MESSAGES;
256
257	STAILQ_INIT(&newfiles.list);
258	STAILQ_INIT(&renamedfiles.list);
259
260	if ((rc = pthread_mutex_init(&newfiles.mtx, NULL)) != 0)
261		errc(1, rc, "pthread_mutex_init()");
262	if ((rc = pthread_cond_init(&newfiles.wait, NULL)) != 0)
263		errc(1, rc, "pthread_cond_init()");
264	if ((rc = pthread_mutex_init(&renamedfiles.mtx, NULL)) != 0)
265		errc(1, rc, "pthread_mutex_init()");
266	if ((rc = pthread_cond_init(&renamedfiles.wait, NULL)) != 0)
267		errc(1, rc, "pthread_cond_init()");
268
269	signal(SIGINFO, hand);
270	signal(SIGALRM, ahand);
271	alarm(300);
272	if ((rc = pthread_create(&tid[0], NULL, loop_create, NULL)) != 0)
273		errc(1, rc, "pthread_create()");
274	if ((rc = pthread_create(&tid[1], NULL, loop_rename, NULL)) != 0)
275		errc(1, rc, "pthread_create()");
276	if ((rc = pthread_create(&tid[2], NULL, loop_unlink, NULL)) != 0)
277		errc(1, rc, "pthread_create()");
278
279	for (i = 0; i < 3; i++) {
280		if ((rc = pthread_join(tid[i], NULL)) != 0)
281			errc(1, rc, "pthread_join(%d)", i);
282	}
283
284	if ((rc = pthread_mutex_destroy(&newfiles.mtx)) != 0)
285		errc(1, rc, "pthread_mutex_destroy(newfiles)");
286	if ((rc = pthread_cond_destroy(&newfiles.wait)) != 0)
287		errc(1, rc, "pthread_cond_destroy(newfiles)");
288	if ((rc = pthread_mutex_destroy(&renamedfiles.mtx)) != 0)
289		errc(1, rc, "pthread_mutex_destroy(renamedfiles)");
290	if ((rc = pthread_cond_destroy(&renamedfiles.wait)) != 0)
291		errc(1, rc, "pthread_cond_destroy(renamedfiles)");
292	free(dirname1);
293	free(dirname2);
294
295	return (0);
296}
297