2015-12-23 00:50:04 +00:00

1226 lines
23 KiB
C

/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* psort - parallel sort.
*
* This is loosely based on some real parallel sort benchmarks, but
* because of various limitations of OS/161 it is massively
* inefficient. But that's ok; the goal is to stress the VM and buffer
* cache.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#ifndef RANDOM_MAX
/* Note: this is correct for OS/161 but not for some Unix C libraries */
#define RANDOM_MAX RAND_MAX
#endif
#define PATH_KEYS "sortkeys"
#define PATH_SORTED "output"
#define PATH_TESTDIR "psortdir"
#define PATH_RANDOM "rand:"
/*
* Workload sizing.
*
* We fork numprocs processes. Each one works on WORKNUM integers at a
* time, so the total VM load is WORKNUM * sizeof(int) * numprocs. For
* the best stress testing, this should be substantially larger than
* your available RAM.
*
* Meanwhile we generate and process numkeys integers, so the total
* filesystem load is numkeys * sizeof(int). For the best stress
* testing this should be substantially larger than your buffer cache.
*
* This test is supposed to adjust to arbitrary settings of WORKNUM,
* numprocs, and numkeys. For a small test try these settings:
* WORKNUM (16*1024)
* numprocs 4, or 2
* numkeys 10000
*
* This should fit in memory (memory footprint is 256K) and generate
* only small files (~40K) which will work on the pre-largefiles SFS.
*
* The default settings are:
* WORKNUM (96*1024)
* numprocs 4
* numkeys 128*1024
*
* so the memory footprint is 1.5M (comparable to the VM tests) and
* the base file size is 512K. Note that because there are a lot of
* temporary files, it pumps a good deal more than 512K of data. For
* the record, the solution set takes about 7.5 minutes of virtual
* time to run it, in either 1M or 2M of RAM and with 4 CPUs; this
* currently corresponds to about 13-14 minutes of real time.
*
* Note that the parent psort process serves as a director and doesn't
* itself compute; it has a workspace (because it's a static buffer)
* but doesn't use it. A VM system that doesn't do zerofill
* optimization will be a lot slower because it has to copy this space
* for every batch of forks.
*
* Also note that you can set numprocs and numkeys on the command
* line, but not WORKNUM.
*
* FUTURE: maybe make a build option to malloc the work space instead
* of using a static buffer, which would allow choosing WORKNUM on the
* command line too, at the cost of depending on malloc working.
*/
/* Set the workload size. */
#define WORKNUM (96*1024)
static int numprocs = 4;
static int numkeys = 128*1024;
/* Per-process work buffer */
static int workspace[WORKNUM];
/* Random seed for generating the data */
static long randomseed = 15432753;
/* other state */
static off_t correctsize;
static unsigned long checksum;
#define NOBODY (-1)
static int me = NOBODY;
static const char *progname;
////////////////////////////////////////////////////////////
static
void
sortints(int *v, int num)
{
int pivotval, pivotpoint, pivotcount;
int frontpos, readpos, endpos, i, j;
int tmp;
if (num < 2) {
return;
}
pivotpoint = num/2;
pivotval = v[pivotpoint];
pivotcount = 0;
frontpos = 0;
readpos = 0;
endpos = num;
while (readpos < endpos) {
if (v[readpos] < pivotval) {
v[frontpos++] = v[readpos++];
}
else if (v[readpos] == pivotval) {
readpos++;
pivotcount++;
}
else {
tmp = v[--endpos];
v[endpos] = v[readpos];
v[readpos] = tmp;
}
}
assert(readpos == endpos);
assert(frontpos + pivotcount == readpos);
for (i=frontpos; i<endpos; i++) {
v[i] = pivotval;
}
for (i=endpos, j=num-1; i<j; i++,j--) {
tmp = v[i];
v[i] = v[j];
v[j] = tmp;
}
sortints(v, frontpos);
sortints(&v[endpos], num-endpos);
}
////////////////////////////////////////////////////////////
static
void
initprogname(const char *av0)
{
if (av0) {
progname = strrchr(av0, '/');
if (progname) {
progname++;
}
else {
progname = av0;
}
}
else {
progname = "psort";
}
}
static
void
vscomplain(char *buf, size_t len, const char *fmt, va_list ap, int err)
{
size_t pos;
if (me >= 0) {
snprintf(buf, len, "%s: proc %d: ", progname, me);
}
else {
snprintf(buf, len, "%s: ", progname);
}
pos = strlen(buf);
vsnprintf(buf+pos, len-pos, fmt, ap);
pos = strlen(buf);
if (err >= 0) {
snprintf(buf+pos, len-pos, ": %s\n", strerror(err));
}
else {
snprintf(buf+pos, len-pos, "\n");
}
}
static
void
complainx(const char *fmt, ...)
{
char buf[256];
va_list ap;
ssize_t junk;
va_start(ap, fmt);
vscomplain(buf, sizeof(buf), fmt, ap, -1);
va_end(ap);
/* Write the message in one go so it's atomic */
junk = write(STDERR_FILENO, buf, strlen(buf));
/*
* This variable must be assigned and then ignored with some
* (broken) Linux C libraries. Ah, Linux...
*/
(void)junk;
}
static
void
complain(const char *fmt, ...)
{
char buf[256];
va_list ap;
ssize_t junk;
int err = errno;
va_start(ap, fmt);
vscomplain(buf, sizeof(buf), fmt, ap, err);
va_end(ap);
/* Write the message in one go so it's atomic */
junk = write(STDERR_FILENO, buf, strlen(buf));
/*
* This variable must be assigned and then ignored with some
* (broken) Linux C libraries. Ah, Linux...
*/
(void)junk;
}
////////////////////////////////////////////////////////////
static
int
doopen(const char *path, int flags, int mode)
{
int fd;
fd = open(path, flags, mode);
if (fd<0) {
complain("%s", path);
exit(1);
}
return fd;
}
static
void
doclose(const char *path, int fd)
{
if (close(fd)) {
complain("%s: close", path);
exit(1);
}
}
static
void
docreate(const char *path)
{
int fd;
fd = doopen(path, O_WRONLY|O_CREAT|O_TRUNC, 0664);
doclose(path, fd);
}
static
void
doremove(const char *path)
{
static int noremove;
if (noremove) {
return;
}
if (remove(path) < 0) {
if (errno == ENOSYS) {
/* Complain (and try) only once. */
noremove = 1;
}
complain("%s: remove", path);
}
}
static
off_t
getsize(const char *path)
{
struct stat buf;
int fd;
static int no_stat, no_fstat;
if (!no_stat) {
if (stat(path, &buf) == 0) {
return buf.st_size;
}
if (errno != ENOSYS) {
complain("%s: stat", path);
exit(1);
}
/* Avoid further "Unknown syscall 81" messages */
no_stat = 1;
}
fd = doopen(path, O_RDONLY, 0);
if (!no_fstat) {
if (fstat(fd, &buf) == 0) {
close(fd);
return buf.st_size;
}
if (errno != ENOSYS) {
complain("%s: stat", path);
exit(1);
}
/* Avoid further "Unknown syscall 82" messages */
no_fstat = 1;
}
/* otherwise, lseek */
if (lseek(fd, 0, SEEK_END) >= 0) {
buf.st_size = lseek(fd, 0, SEEK_CUR);
if (buf.st_size >= 0) {
return buf.st_size;
}
}
complain("%s: getting file size with lseek", path);
close(fd);
exit(1);
}
static
size_t
doread(const char *path, int fd, void *buf, size_t len)
{
int result;
result = read(fd, buf, len);
if (result < 0) {
complain("%s: read", path);
exit(1);
}
return (size_t) result;
}
static
void
doexactread(const char *path, int fd, void *buf, size_t len)
{
size_t result;
result = doread(path, fd, buf, len);
if (result != len) {
complainx("%s: read: short count", path);
exit(1);
}
}
static
void
dowrite(const char *path, int fd, const void *buf, size_t len)
{
int result;
result = write(fd, buf, len);
if (result < 0) {
complain("%s: write", path);
exit(1);
}
if ((size_t) result != len) {
complainx("%s: write: short count", path);
exit(1);
}
}
static
void
dolseek(const char *name, int fd, off_t offset, int whence)
{
if (lseek(fd, offset, whence) < 0) {
complain("%s: lseek", name);
exit(1);
}
}
#if 0 /* let's not require subdirs */
static
void
dochdir(const char *path)
{
if (chdir(path) < 0) {
complain("%s: chdir", path);
exit(1);
}
}
static
void
domkdir(const char *path, int mode)
{
if (mkdir(path, mode) < 0) {
complain("%s: mkdir", path);
exit(1);
}
}
#endif /* 0 */
static
pid_t
dofork(void)
{
pid_t pid;
pid = fork();
if (pid < 0) {
complain("fork");
/* but don't exit */
}
return pid;
}
////////////////////////////////////////////////////////////
static
int
dowait(int guy, pid_t pid)
{
int status, result;
result = waitpid(pid, &status, 0);
if (result < 0) {
complain("waitpid");
return -1;
}
if (WIFSIGNALED(status)) {
complainx("proc %d: signal %d", guy, WTERMSIG(status));
return -1;
}
assert(WIFEXITED(status));
status = WEXITSTATUS(status);
if (status) {
complainx("proc %d: exit %d", guy, status);
return -1;
}
return 0;
}
static
void
doforkall(const char *phasename, void (*func)(void))
{
int i, bad = 0;
pid_t pids[numprocs];
for (i=0; i<numprocs; i++) {
pids[i] = dofork();
if (pids[i] < 0) {
bad = 1;
}
else if (pids[i] == 0) {
/* child */
me = i;
func();
exit(0);
}
}
for (i=0; i<numprocs; i++) {
if (pids[i] > 0 && dowait(i, pids[i])) {
bad = 1;
}
}
if (bad) {
complainx("%s failed.", phasename);
exit(1);
}
}
static
void
seekmyplace(const char *name, int fd)
{
int keys_per, myfirst;
off_t offset;
keys_per = numkeys / numprocs;
myfirst = me*keys_per;
offset = myfirst * sizeof(int);
dolseek(name, fd, offset, SEEK_SET);
}
static
int
getmykeys(void)
{
int keys_per, myfirst, mykeys;
keys_per = numkeys / numprocs;
myfirst = me*keys_per;
mykeys = (me < numprocs-1) ? keys_per : numkeys - myfirst;
return mykeys;
}
////////////////////////////////////////////////////////////
static
unsigned long
checksum_file(const char *path)
{
int fd;
char buf[512];
size_t count, i;
unsigned long sum = 0;
fd = doopen(path, O_RDONLY, 0);
while ((count = doread(path, fd, buf, sizeof(buf))) > 0) {
for (i=0; i<count; i++) {
sum += (unsigned char) buf[i];
}
}
doclose(path, fd);
return sum;
}
////////////////////////////////////////////////////////////
static long *seeds;
static
void
genkeys_sub(void)
{
int fd, i, mykeys, keys_done, keys_to_do, value;
fd = doopen(PATH_KEYS, O_WRONLY, 0);
mykeys = getmykeys();
seekmyplace(PATH_KEYS, fd);
srandom(seeds[me]);
keys_done = 0;
while (keys_done < mykeys) {
keys_to_do = mykeys - keys_done;
if (keys_to_do > WORKNUM) {
keys_to_do = WORKNUM;
}
for (i=0; i<keys_to_do; i++) {
value = random();
// check bounds of value
assert(value >= 0);
assert(value <= RANDOM_MAX);
// do not allow the value to be zero or RANDOM_MAX
while (value == 0 || value == RANDOM_MAX) {
value = random();
}
workspace[i] = value;
}
dowrite(PATH_KEYS, fd, workspace, keys_to_do*sizeof(int));
keys_done += keys_to_do;
}
doclose(PATH_KEYS, fd);
}
static
void
genkeys(void)
{
long seedspace[numprocs];
int i;
/* Create the file. */
docreate(PATH_KEYS);
/* Generate random seeds for each subprocess. */
srandom(randomseed);
for (i=0; i<numprocs; i++) {
seedspace[i] = random();
}
/* Do it. */
complainx("Generating %d integers using %d procs", numkeys, numprocs);
seeds = seedspace;
doforkall("Initialization", genkeys_sub);
seeds = NULL;
/* Cross-check the size of the output. */
if (getsize(PATH_KEYS) != correctsize) {
complainx("%s: file is wrong size", PATH_KEYS);
exit(1);
}
/* Checksum the output. */
complainx("Checksumming the data (using one proc)");
checksum = checksum_file(PATH_KEYS);
complainx("Checksum of unsorted keys: %ld", checksum);
}
////////////////////////////////////////////////////////////
static
const char *
binname(int a, int b)
{
static char rv[32];
snprintf(rv, sizeof(rv), "bin-%d-%d", a, b);
return rv;
}
static
const char *
mergedname(int a)
{
static char rv[32];
snprintf(rv, sizeof(rv), "merged-%d", a);
return rv;
}
static
void
bin(void)
{
int infd, outfds[numprocs];
const char *name;
int i, mykeys, keys_done, keys_to_do;
int key, pivot, binnum;
infd = doopen(PATH_KEYS, O_RDONLY, 0);
mykeys = getmykeys();
seekmyplace(PATH_KEYS, infd);
for (i=0; i<numprocs; i++) {
name = binname(me, i);
outfds[i] = doopen(name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
}
pivot = (RANDOM_MAX / numprocs);
keys_done = 0;
while (keys_done < mykeys) {
keys_to_do = mykeys - keys_done;
if (keys_to_do > WORKNUM) {
keys_to_do = WORKNUM;
}
doexactread(PATH_KEYS, infd, workspace,
keys_to_do * sizeof(int));
for (i=0; i<keys_to_do; i++) {
key = workspace[i];
binnum = key / pivot;
if (key <= 0) {
complainx("proc %d: garbage key %d", me, key);
key = 0;
}
assert(binnum >= 0);
assert(binnum < numprocs);
dowrite("bin", outfds[binnum], &key, sizeof(key));
}
keys_done += keys_to_do;
}
doclose(PATH_KEYS, infd);
for (i=0; i<numprocs; i++) {
doclose(binname(me, i), outfds[i]);
}
}
static
void
sortbins(void)
{
const char *name;
int i, fd;
off_t binsize;
for (i=0; i<numprocs; i++) {
name = binname(me, i);
binsize = getsize(name);
if (binsize % sizeof(int) != 0) {
complainx("%s: bin size %ld no good", name,
(long) binsize);
exit(1);
}
if (binsize > (off_t) sizeof(workspace)) {
complainx("proc %d: %s: bin too large", me, name);
exit(1);
}
fd = doopen(name, O_RDWR, 0);
doexactread(name, fd, workspace, binsize);
sortints(workspace, binsize/sizeof(int));
dolseek(name, fd, 0, SEEK_SET);
dowrite(name, fd, workspace, binsize);
doclose(name, fd);
}
}
static
void
mergebins(void)
{
int infds[numprocs], outfd;
int values[numprocs], ready[numprocs];
const char *name, *outname;
int i, result;
int numready, place, val, worknum;
outname = mergedname(me);
outfd = doopen(outname, O_WRONLY|O_CREAT|O_TRUNC, 0664);
for (i=0; i<numprocs; i++) {
name = binname(i, me);
infds[i] = doopen(name, O_RDONLY, 0);
values[i] = 0;
ready[i] = 0;
}
worknum = 0;
while (1) {
numready = 0;
for (i=0; i<numprocs; i++) {
if (infds[i] < 0) {
continue;
}
if (!ready[i]) {
result = doread("bin", infds[i],
&val, sizeof(int));
if (result == 0) {
doclose("bin", infds[i]);
infds[i] = -1;
continue;
}
if ((size_t) result != sizeof(int)) {
complainx("%s: read: short count",
binname(i, me));
exit(1);
}
values[i] = val;
ready[i] = 1;
}
numready++;
}
if (numready == 0) {
break;
}
/* find the smallest */
place = -1;
for (i=0; i<numprocs; i++) {
if (!ready[i]) {
continue;
}
if (place < 0 || values[i] < val) {
val = values[i];
place = i;
}
}
assert(place >= 0);
workspace[worknum++] = val;
if (worknum >= WORKNUM) {
assert(worknum == WORKNUM);
dowrite(outname, outfd, workspace,
worknum * sizeof(int));
worknum = 0;
}
ready[place] = 0;
}
dowrite(outname, outfd, workspace, worknum * sizeof(int));
doclose(outname, outfd);
for (i=0; i<numprocs; i++) {
assert(infds[i] < 0);
}
}
static
void
assemble(void)
{
off_t mypos;
int i, fd;
const char *args[3];
mypos = 0;
for (i=0; i<me; i++) {
mypos += getsize(mergedname(i));
}
fd = doopen(PATH_SORTED, O_WRONLY, 0);
dolseek(PATH_SORTED, fd, mypos, SEEK_SET);
if (dup2(fd, STDOUT_FILENO) < 0) {
complain("dup2");
exit(1);
}
doclose(PATH_SORTED, fd);
args[0] = "cat";
args[1] = mergedname(me);
args[2] = NULL;
execv("/bin/cat", (char **) args);
complain("/bin/cat: exec");
exit(1);
}
static
void
checksize_bins(void)
{
off_t totsize;
int i, j;
totsize = 0;
for (i=0; i<numprocs; i++) {
for (j=0; j<numprocs; j++) {
totsize += getsize(binname(i, j));
}
}
if (totsize != correctsize) {
complain("Sum of bin sizes is wrong (%ld, should be %ld)",
(long) totsize, (long) correctsize);
exit(1);
}
}
static
void
checksize_merge(void)
{
off_t totsize;
int i;
totsize = 0;
for (i=0; i<numprocs; i++) {
totsize += getsize(mergedname(i));
}
if (totsize != correctsize) {
complain("Sum of merged sizes is wrong (%ld, should be %ld)",
(long) totsize, (long) correctsize);
exit(1);
}
}
static
void
sort(void)
{
unsigned long sortedsum;
int i, j;
/* Step 1. Toss into bins. */
complainx("Tossing into %d bins using %d procs",
numprocs*numprocs, numprocs);
doforkall("Tossing", bin);
checksize_bins();
complainx("Done tossing into bins.");
/* Step 2: Sort the bins. */
complainx("Sorting %d bins using %d procs",
numprocs*numprocs, numprocs);
doforkall("Sorting", sortbins);
checksize_bins();
complainx("Done sorting the bins.");
/* Step 3: Merge corresponding bins. */
complainx("Merging %d bins using %d procs",
numprocs*numprocs, numprocs);
doforkall("Merging", mergebins);
checksize_merge();
complainx("Done merging the bins.");
/* Step 3a: delete the bins */
for (i=0; i<numprocs; i++) {
for (j=0; j<numprocs; j++) {
doremove(binname(i, j));
}
}
/* Step 4: assemble output file */
complainx("Assembling output file using %d procs", numprocs);
docreate(PATH_SORTED);
doforkall("Final assembly", assemble);
if (getsize(PATH_SORTED) != correctsize) {
complainx("%s: file is wrong size", PATH_SORTED);
exit(1);
}
/* Step 4a: delete the merged bins */
for (i=0; i<numprocs; i++) {
doremove(mergedname(i));
}
/* Step 5: Checksum the result. */
complainx("Checksumming the output (using one proc)");
sortedsum = checksum_file(PATH_SORTED);
complainx("Checksum of sorted keys: %ld", sortedsum);
if (sortedsum != checksum) {
complainx("Sums do not match");
exit(1);
}
}
////////////////////////////////////////////////////////////
static
const char *
validname(int a)
{
static char rv[32];
snprintf(rv, sizeof(rv), "valid-%d", a);
return rv;
}
static
void
checksize_valid(void)
{
off_t totvsize, correctvsize;
int i;
correctvsize = (off_t) numprocs*2*sizeof(int);
totvsize = 0;
for (i=0; i<numprocs; i++) {
totvsize += getsize(validname(i));
}
if (totvsize != correctvsize) {
complainx("Sum of validation sizes is wrong "
"(%ld, should be %ld)",
(long) totvsize, (long) correctvsize);
exit(1);
}
}
static
void
dovalidate(void)
{
const char *name;
int fd, i, mykeys, keys_done, keys_to_do;
int key, smallest, largest;
name = PATH_SORTED;
fd = doopen(name, O_RDONLY, 0);
mykeys = getmykeys();
seekmyplace(name, fd);
smallest = RANDOM_MAX;
largest = 0;
keys_done = 0;
while (keys_done < mykeys) {
keys_to_do = mykeys - keys_done;
if (keys_to_do > WORKNUM) {
keys_to_do = WORKNUM;
}
doexactread(name, fd, workspace, keys_to_do * sizeof(int));
for (i=0; i<keys_to_do; i++) {
key = workspace[i];
if (key < 0) {
complain("%s: found negative key", name);
exit(1);
}
if (key == 0) {
complain("%s: found zero key", name);
exit(1);
}
if (key >= RANDOM_MAX) {
complain("%s: found too-large key", name);
exit(1);
}
if (key < smallest) {
smallest = key;
}
if (key > largest) {
largest = key;
}
}
keys_done += keys_to_do;
}
doclose(name, fd);
name = validname(me);
fd = doopen(name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
dowrite(name, fd, &smallest, sizeof(smallest));
dowrite(name, fd, &largest, sizeof(largest));
doclose(name, fd);
}
static
void
validate(void)
{
int smallest, largest, prev_largest;
int i, fd;
const char *name;
complainx("Validating the sorted data using %d procs", numprocs);
doforkall("Validation", dovalidate);
checksize_valid();
prev_largest = 1;
for (i=0; i<numprocs; i++) {
name = validname(i);
fd = doopen(name, O_RDONLY, 0);
doexactread(name, fd, &smallest, sizeof(int));
doexactread(name, fd, &largest, sizeof(int));
if (smallest < 1) {
complainx("Validation: block %d: bad SMALLEST", i);
exit(1);
}
if (largest >= RANDOM_MAX) {
complainx("Validation: block %d: bad LARGEST", i);
exit(1);
}
if (smallest > largest) {
complainx("Validation: block %d: SMALLEST > LARGEST",
i);
exit(1);
}
if (smallest < prev_largest) {
complain("Validation: block %d smallest key %d",
i, smallest);
complain("Validation: previous block largest key %d",
prev_largest);
complain("Validation failed");
exit(1);
}
}
for (i=0; i<numprocs; i++) {
doremove(validname(i));
}
}
////////////////////////////////////////////////////////////
static
void
setdir(void)
{
#if 0 /* let's not require subdirs */
domkdir(PATH_TESTDIR, 0775);
dochdir(PATH_TESTDIR);
#endif /* 0 */
}
static
void
unsetdir(void)
{
doremove(PATH_KEYS);
doremove(PATH_SORTED);
#if 0 /* let's not require subdirs */
dochdir("..");
if (rmdir(PATH_TESTDIR) < 0) {
complain("%s: rmdir", PATH_TESTDIR);
/* but don't exit */
}
#endif /* 0 */
}
////////////////////////////////////////////////////////////
static
void
randomize(void)
{
int fd;
fd = doopen(PATH_RANDOM, O_RDONLY, 0);
doexactread(PATH_RANDOM, fd, &randomseed, sizeof(randomseed));
doclose(PATH_RANDOM, fd);
}
static
void
usage(void)
{
complain("Usage: %s [-p procs] [-k keys] [-s seed] [-r]", progname);
exit(1);
}
static
void
doargs(int argc, char *argv[])
{
int i, ch, val, arg;
for (i=1; i<argc; i++) {
if (argv[i][0] != '-') {
usage();
return;
}
ch = argv[i][1];
switch (ch) {
case 'p': arg = 1; break;
case 'k': arg = 1; break;
case 's': arg = 1; break;
case 'r': arg = 0; break;
default: usage(); return;
}
if (arg) {
if (argv[i][2]) {
val = atoi(argv[i]+2);
}
else {
i++;
if (!argv[i]) {
complain("Option -%c requires an "
"argument", ch);
exit(1);
}
val = atoi(argv[i]);
}
switch (ch) {
case 'p': numprocs = val; break;
case 'k': numkeys = val; break;
case 's': randomseed = val; break;
default: assert(0); break;
}
}
else {
switch (ch) {
case 'r': randomize(); break;
default: assert(0); break;
}
}
}
}
int
main(int argc, char *argv[])
{
initprogname(argc > 0 ? argv[0] : NULL);
doargs(argc, argv);
correctsize = (off_t) (numkeys*sizeof(int));
setdir();
genkeys();
sort();
validate();
complainx("Succeeded.");
unsetdir();
return 0;
}