Skip to content
Permalink
ce0c5b8f36
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
593 lines (464 sloc) 11.6 KB
#define _GNU_SOURCE
#include <argp.h>
#include <complex.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <gd.h>
#include <inttypes.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#define size_x 320
#define size_y 240
#define PATH "/tmp/s.sockperf"
struct thread_param
{
unsigned int from;
unsigned int to;
unsigned int nserv;
};
struct coord
{
unsigned int x;
unsigned int y;
complex double z;
};
/* We use 64bit values for the times. */
typedef unsigned long long int hp_timing_t;
static unsigned int nclients = 2;
static unsigned int nservers = 2;
static bool timing;
static int points;
static complex double top_left = -0.7 + 0.2i;
static complex double bottom_right = -0.5 - 0.0i;
static int colors[256];
static gdImagePtr image;
static pthread_mutex_t image_lock;
static int sock;
static void *
client (void *arg)
{
struct thread_param *param = arg;
unsigned int cnt;
unsigned int nserv = param->nserv;
struct pollfd servpoll[nserv];
struct sockaddr_un servaddr;
socklen_t servlen;
struct coord c;
bool new_coord (void)
{
if (cnt >= param->to)
return false;
unsigned int row = cnt / size_x;
unsigned int col = cnt % size_x;
c.x = col;
c.y = row;
c.z = (top_left
+ ((col
* (creal (bottom_right) - creal (top_left))) / size_x)
+ (_Complex_I * (row * (cimag (bottom_right) - cimag (top_left)))
/ size_y));
++cnt;
return true;
}
for (cnt = 0; cnt < nserv; ++cnt)
{
servpoll[cnt].fd = socket (AF_UNIX, SOCK_STREAM, 0);
if (servpoll[cnt].fd < 0)
{
puts ("cannot create socket in client");
return NULL;
}
memset (&servaddr, '\0', sizeof (servaddr));
servaddr.sun_family = AF_UNIX;
strncpy (servaddr.sun_path, PATH, sizeof (servaddr.sun_path));
servlen = offsetof (struct sockaddr_un, sun_path) + strlen (PATH) + 1;
int err;
while (1)
{
err = TEMP_FAILURE_RETRY (connect (servpoll[cnt].fd, &servaddr,
servlen));
if (err != -1 || errno != ECONNREFUSED)
break;
pthread_yield ();
}
if (err == -1)
{
printf ("cannot connect: %m (%d)\n", errno);
exit (1);
}
servpoll[cnt].events = POLLOUT;
servpoll[cnt].revents = 0;
}
cnt = param->from;
new_coord ();
bool z_valid = true;
while (1)
{
int i;
int n = poll (servpoll, nserv, -1);
if (n == -1)
{
puts ("poll returned error");
break;
}
bool cont = false;
for (i = 0; i < nserv && n > 0; ++i)
if (servpoll[i].revents != 0)
{
if (servpoll[i].revents == POLLIN)
{
unsigned int vals[3];
if (TEMP_FAILURE_RETRY (read (servpoll[i].fd, &vals,
sizeof (vals)))
!= sizeof (vals))
{
puts ("read error in client");
return NULL;
}
pthread_mutex_lock (&image_lock);
gdImageSetPixel (image, vals[0], vals[1], vals[2]);
++points;
pthread_mutex_unlock (&image_lock);
servpoll[i].events = POLLOUT;
}
else
{
if (servpoll[i].revents != POLLOUT)
printf ("revents: %hd != POLLOUT ???\n",
servpoll[i].revents);
if (z_valid)
{
if (TEMP_FAILURE_RETRY (write (servpoll[i].fd, &c,
sizeof (c))) != sizeof (c))
{
puts ("write error in client");
return NULL;
}
cont = true;
servpoll[i].events = POLLIN;
z_valid = new_coord ();
if (! z_valid)
/* No more to do. Clear the event fields. */
for (i = 0; i < nserv; ++i)
if (servpoll[i].events == POLLOUT)
servpoll[i].events = servpoll[i].revents = 0;
}
else
servpoll[i].events = servpoll[i].revents = 0;
}
--n;
}
else if (servpoll[i].events != 0)
cont = true;
if (! cont && ! z_valid)
break;
}
c.x = 0xffffffff;
c.y = 0xffffffff;
for (cnt = 0; cnt < nserv; ++cnt)
{
TEMP_FAILURE_RETRY (write (servpoll[cnt].fd, &c, sizeof (c)));
close (servpoll[cnt].fd);
}
return NULL;
}
static void *
server (void *arg)
{
struct sockaddr_un cliaddr;
socklen_t clilen;
int clisock = TEMP_FAILURE_RETRY (accept (sock, &cliaddr, &clilen));
if (clisock == -1)
{
puts ("accept failed");
return NULL;
}
while (1)
{
struct coord c;
if (TEMP_FAILURE_RETRY (read (clisock, &c, sizeof (c))) != sizeof (c))
{
printf ("server read failed: %m (%d)\n", errno);
break;
}
if (c.x == 0xffffffff && c.y == 0xffffffff)
break;
unsigned int rnds = 0;
complex double z = c.z;
while (cabs (z) < 4.0)
{
z = z * z - 1;
if (++rnds == 255)
break;
}
unsigned int vals[3] = { c.x, c.y, rnds };
if (TEMP_FAILURE_RETRY (write (clisock, vals, sizeof (vals)))
!= sizeof (vals))
{
puts ("server write error");
return NULL;
}
}
close (clisock);
return NULL;
}
static const char *outfilename = "test.png";
static const struct argp_option options[] =
{
{ "clients", 'c', "NUMBER", 0, "Number of client threads" },
{ "servers", 's', "NUMBER", 0, "Number of server threads per client" },
{ "timing", 'T', NULL, 0,
"Measure time from startup to the last thread finishing" },
{ NULL, 0, NULL, 0, NULL }
};
/* Prototype for option handler. */
static error_t parse_opt (int key, char *arg, struct argp_state *state);
/* Data structure to communicate with argp functions. */
static struct argp argp =
{
options, parse_opt
};
int
main (int argc, char *argv[])
{
int cnt;
FILE *outfile;
struct sockaddr_un servaddr;
socklen_t servlen;
int remaining;
/* Parse and process arguments. */
argp_parse (&argp, argc, argv, 0, &remaining, NULL);
pthread_t servth[nservers * nclients];
pthread_t clntth[nclients];
struct thread_param clntparam[nclients];
image = gdImageCreate (size_x, size_y);
if (image == NULL)
{
puts ("gdImageCreate failed");
return 1;
}
for (cnt = 0; cnt < 255; ++cnt)
colors[cnt] = gdImageColorAllocate (image, 256 - cnt, 256 - cnt,
256 - cnt);
/* Black. */
colors[cnt] = gdImageColorAllocate (image, 0, 0, 0);
sock = socket (AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
error (EXIT_FAILURE, errno, "cannot create socket");
memset (&servaddr, '\0', sizeof (servaddr));
servaddr.sun_family = AF_UNIX;
strncpy (servaddr.sun_path, PATH, sizeof (servaddr.sun_path));
servlen = offsetof (struct sockaddr_un, sun_path) + strlen (PATH) + 1;
if (bind (sock, &servaddr, servlen) == -1)
error (EXIT_FAILURE, errno, "bind failed");
listen (sock, SOMAXCONN);
pthread_mutex_init (&image_lock, NULL);
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
clockid_t cl;
struct timespec start_time;
if (timing)
{
if (clock_getcpuclockid (0, &cl) != 0
|| clock_gettime (cl, &start_time) != 0)
timing = false;
}
/* Start the servers. */
for (cnt = 0; cnt < nservers * nclients; ++cnt)
{
if (pthread_create (&servth[cnt], NULL, server, NULL) != 0)
{
puts ("pthread_create for server failed");
exit (1);
}
}
for (cnt = 0; cnt < nclients; ++cnt)
{
clntparam[cnt].from = cnt * (size_x * size_y) / nclients;
clntparam[cnt].to = MIN ((cnt + 1) * (size_x * size_y) / nclients,
size_x * size_y);
clntparam[cnt].nserv = nservers;
if (pthread_create (&clntth[cnt], NULL, client, &clntparam[cnt]) != 0)
{
puts ("pthread_create for client failed");
exit (1);
}
}
/* Wait for the clients. */
for (cnt = 0; cnt < nclients; ++cnt)
if (pthread_join (clntth[cnt], NULL) != 0)
{
puts ("client pthread_join failed");
exit (1);
}
/* Wait for the servers. */
for (cnt = 0; cnt < nclients * nservers; ++cnt)
if (pthread_join (servth[cnt], NULL) != 0)
{
puts ("server pthread_join failed");
exit (1);
}
if (timing)
{
struct timespec end_time;
if (clock_gettime (cl, &end_time) == 0)
{
end_time.tv_sec -= start_time.tv_sec;
end_time.tv_nsec -= start_time.tv_nsec;
if (end_time.tv_nsec < 0)
{
end_time.tv_nsec += 1000000000;
--end_time.tv_sec;
}
printf ("\nRuntime: %lu.%09lu seconds\n%d points computed\n",
(unsigned long int) end_time.tv_sec,
(unsigned long int) end_time.tv_nsec,
points);
}
}
outfile = fopen (outfilename, "w");
if (outfile == NULL)
error (EXIT_FAILURE, errno, "cannot open output file '%s'", outfilename);
gdImagePng (image, outfile);
fclose (outfile);
unlink (PATH);
return 0;
}
/* Handle program arguments. */
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
switch (key)
{
case 'c':
nclients = strtoul (arg, NULL, 0);
break;
case 's':
nservers = strtoul (arg, NULL, 0);
break;
case 'T':
timing = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static hp_timing_t
get_clockfreq (void)
{
/* We read the information from the /proc filesystem. It contains at
least one line like
cpu MHz : 497.840237
or also
cpu MHz : 497.841
We search for this line and convert the number in an integer. */
static hp_timing_t result;
int fd;
/* If this function was called before, we know the result. */
if (result != 0)
return result;
fd = open ("/proc/cpuinfo", O_RDONLY);
if (__glibc_likely (fd != -1))
{
/* XXX AFAIK the /proc filesystem can generate "files" only up
to a size of 4096 bytes. */
char buf[4096];
ssize_t n;
n = read (fd, buf, sizeof buf);
if (__builtin_expect (n, 1) > 0)
{
char *mhz = memmem (buf, n, "cpu MHz", 7);
if (__glibc_likely (mhz != NULL))
{
char *endp = buf + n;
int seen_decpoint = 0;
int ndigits = 0;
/* Search for the beginning of the string. */
while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n')
++mhz;
while (mhz < endp && *mhz != '\n')
{
if (*mhz >= '0' && *mhz <= '9')
{
result *= 10;
result += *mhz - '0';
if (seen_decpoint)
++ndigits;
}
else if (*mhz == '.')
seen_decpoint = 1;
++mhz;
}
/* Compensate for missing digits at the end. */
while (ndigits++ < 6)
result *= 10;
}
}
close (fd);
}
return result;
}
int
clock_getcpuclockid (pid_t pid, clockid_t *clock_id)
{
/* We don't allow any process ID but our own. */
if (pid != 0 && pid != getpid ())
return EPERM;
#ifdef CLOCK_PROCESS_CPUTIME_ID
/* Store the number. */
*clock_id = CLOCK_PROCESS_CPUTIME_ID;
return 0;
#else
/* We don't have a timer for that. */
return ENOENT;
#endif
}
#define HP_TIMING_NOW(Var) __asm__ __volatile__ ("rdtsc" : "=A" (Var))
/* Get current value of CLOCK and store it in TP. */
int
clock_gettime (clockid_t clock_id, struct timespec *tp)
{
int retval = -1;
switch (clock_id)
{
case CLOCK_PROCESS_CPUTIME_ID:
{
static hp_timing_t freq;
hp_timing_t tsc;
/* Get the current counter. */
HP_TIMING_NOW (tsc);
if (freq == 0)
{
freq = get_clockfreq ();
if (freq == 0)
return EINVAL;
}
/* Compute the seconds. */
tp->tv_sec = tsc / freq;
/* And the nanoseconds. This computation should be stable until
we get machines with about 16GHz frequency. */
tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq;
retval = 0;
}
break;
default:
errno = EINVAL;
break;
}
return retval;
}