/* * CDDL HEADER START * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the "License"). You may not use this file except * in compliance with the License. * * You can obtain a copy of the license at * src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * HEADER in each file and include the License file at * usr/src/OPENSOLARIS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your * own identifying information: Portions Copyright [yyyy] * [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * The "cascade" test case is a multiprocess/multithread batten-passing model * using lock primitives alone for synchronisation. Threads are arranged in a * ring. Each thread has two locks of its own on which it blocks, and is able * to manipulate the two locks belonging to the thread which follows it in the * ring. * * The number of threads (nthreads) is specified by the generic libMicro -P/-T * options. With nthreads == 1 (the default) the uncontended case can be timed. * * The main logic is generic and allows any simple blocking API to be tested. * The API-specific component is clearly indicated. */ #include #include #include #include #include #include "libmicro.h" typedef struct { int ts_once; int ts_id; int ts_us0; /* our lock indices */ int ts_us1; int ts_them0; /* their lock indices */ int ts_them1; } tsd_t; static int nthreads; /* * API-specific code BEGINS here */ static int opto = 0; static int opts = 0; static int nlocks; static pthread_mutex_t *mxs; static pthread_cond_t *cvs; static int *conds; int benchmark_init() { lm_tsdsize = sizeof (tsd_t); (void) sprintf(lm_optstr, "os"); lm_defN = "cscd_cond"; (void) sprintf(lm_usage, " [-o] (do signal outside mutex)\n" " [-s] (force PTHREAD_PROCESS_SHARED)\n" "notes: thread cascade using pthread_conds\n"); return (0); } /*ARGSUSED*/ int benchmark_optswitch(int opt, char *optarg) { switch (opt) { case 'o': opto = 1; break; case 's': opts = 1; break; default: return (-1); } return (0); } int benchmark_initrun() { int i; int e = 0; pthread_mutexattr_t ma; pthread_condattr_t ca; nthreads = lm_optP * lm_optT; nlocks = nthreads * 2; /*LINTED*/ mxs = (pthread_mutex_t *)mmap(NULL, nlocks * sizeof (pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0L); if (mxs == MAP_FAILED) { return (1); } /*LINTED*/ cvs = (pthread_cond_t *)mmap(NULL, nlocks * sizeof (pthread_cond_t), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0L); if (cvs == MAP_FAILED) { return (1); } /*LINTED*/ conds = (int *)mmap(NULL, nlocks * sizeof (pthread_cond_t), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0L); if (conds == MAP_FAILED) { return (1); } (void) pthread_mutexattr_init(&ma); (void) pthread_condattr_init(&ca); if (lm_optP > 1 || opts) { (void) pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); (void) pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED); } else { (void) pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_PRIVATE); (void) pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_PRIVATE); } for (i = 0; i < nlocks; i++) { (void) pthread_mutex_init(&mxs[i], &ma); (void) pthread_cond_init(&cvs[i], &ca); conds[i] = 0; } return (e); } int block(int index) { (void) pthread_mutex_lock(&mxs[index]); while (conds[index] != 0) { (void) pthread_cond_wait(&cvs[index], &mxs[index]); } conds[index] = 1; (void) pthread_mutex_unlock(&mxs[index]); return (0); } int unblock(int index) { (void) pthread_mutex_lock(&mxs[index]); conds[index] = 0; if (opto) { (void) pthread_mutex_unlock(&mxs[index]); (void) pthread_cond_signal(&cvs[index]); } else { (void) pthread_cond_signal(&cvs[index]); (void) pthread_mutex_unlock(&mxs[index]); } return (0); } /* * API-specific code ENDS here */ int benchmark_initbatch(void *tsd) { tsd_t *ts = (tsd_t *)tsd; int e = 0; if (ts->ts_once == 0) { int us, them; #if !defined(__APPLE__) us = (getpindex() * lm_optT) + gettindex(); #else us = gettsdindex(tsd); #endif /* __APPLE__ */ them = (us + 1) % (lm_optP * lm_optT); ts->ts_id = us; /* lock index asignment for us and them */ ts->ts_us0 = (us * 2); ts->ts_us1 = (us * 2) + 1; if (us < nthreads - 1) { /* straight-thru connection to them */ ts->ts_them0 = (them * 2); ts->ts_them1 = (them * 2) + 1; } else { /* cross-over connection to them */ ts->ts_them0 = (them * 2) + 1; ts->ts_them1 = (them * 2); } ts->ts_once = 1; } /* block their first move */ e += block(ts->ts_them0); return (e); } int benchmark(void *tsd, result_t *res) { tsd_t *ts = (tsd_t *)tsd; int i; int e = 0; /* wait to be unblocked (id == 0 will not block) */ e += block(ts->ts_us0); for (i = 0; i < lm_optB; i += 2) { /* allow them to block us again */ e += unblock(ts->ts_us0); /* block their next + 1 move */ e += block(ts->ts_them1); /* unblock their next move */ e += unblock(ts->ts_them0); /* wait for them to unblock us */ e += block(ts->ts_us1); /* repeat with locks reversed */ e += unblock(ts->ts_us1); e += block(ts->ts_them0); e += unblock(ts->ts_them1); e += block(ts->ts_us0); } /* finish batch with nothing blocked */ e += unblock(ts->ts_them0); e += unblock(ts->ts_us0); res->re_count = i; res->re_errors = e; return (0); }