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