😄
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
CC=gcc
|
CC=gcc
|
||||||
CFLAGS=-g -Wall -Wextra -pedantic -std=gnu99 -pthread
|
#CFLAGS=-g -Wall -Wextra -pedantic -std=gnu99 -pthread
|
||||||
|
CFLAGS=-g -pthread
|
||||||
EXAMPLES=fibs fauxgrep fauxgrep-mt fhistogram fhistogram-mt
|
EXAMPLES=fibs fauxgrep fauxgrep-mt fhistogram fhistogram-mt
|
||||||
|
|
||||||
.PHONY: all test clean ../src.zip
|
.PHONY: all test clean ../src.zip
|
||||||
|
106
A2/fauxgrep-mt.c
106
A2/fauxgrep-mt.c
@ -20,68 +20,68 @@
|
|||||||
#include "job_queue.h"
|
#include "job_queue.h"
|
||||||
|
|
||||||
int main(int argc, char * const *argv) {
|
int main(int argc, char * const *argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
err(1, "usage: [-n INT] STRING paths...");
|
err(1, "usage: [-n INT] STRING paths...");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
int num_threads = 1;
|
|
||||||
char const *needle = argv[1];
|
|
||||||
char * const *paths = &argv[2];
|
|
||||||
|
|
||||||
|
|
||||||
if (argc > 3 && strcmp(argv[1], "-n") == 0) {
|
|
||||||
// Since atoi() simply returns zero on syntax errors, we cannot
|
|
||||||
// distinguish between the user entering a zero, or some
|
|
||||||
// non-numeric garbage. In fact, we cannot even tell whether the
|
|
||||||
// given option is suffixed by garbage, i.e. '123foo' returns
|
|
||||||
// '123'. A more robust solution would use strtol(), but its
|
|
||||||
// interface is more complicated, so here we are.
|
|
||||||
num_threads = atoi(argv[2]);
|
|
||||||
|
|
||||||
if (num_threads < 1) {
|
|
||||||
err(1, "invalid thread count: %s", argv[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
needle = argv[3];
|
int num_threads = 1;
|
||||||
paths = &argv[4];
|
char const *needle = argv[1];
|
||||||
|
char * const *paths = &argv[2];
|
||||||
|
|
||||||
} else {
|
|
||||||
needle = argv[1];
|
|
||||||
paths = &argv[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(0); // Initialise the job queue and some worker threads here.
|
if (argc > 3 && strcmp(argv[1], "-n") == 0) {
|
||||||
|
// Since atoi() simply returns zero on syntax errors, we cannot
|
||||||
|
// distinguish between the user entering a zero, or some
|
||||||
|
// non-numeric garbage. In fact, we cannot even tell whether the
|
||||||
|
// given option is suffixed by garbage, i.e. '123foo' returns
|
||||||
|
// '123'. A more robust solution would use strtol(), but its
|
||||||
|
// interface is more complicated, so here we are.
|
||||||
|
num_threads = atoi(argv[2]);
|
||||||
|
|
||||||
// FTS_LOGICAL = follow symbolic links
|
if (num_threads < 1) {
|
||||||
// FTS_NOCHDIR = do not change the working directory of the process
|
err(1, "invalid thread count: %s", argv[2]);
|
||||||
//
|
}
|
||||||
// (These are not particularly important distinctions for our simple
|
|
||||||
// uses.)
|
|
||||||
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
|
||||||
|
|
||||||
FTS *ftsp;
|
needle = argv[3];
|
||||||
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
paths = &argv[4];
|
||||||
err(1, "fts_open() failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FTSENT *p;
|
} else {
|
||||||
while ((p = fts_read(ftsp)) != NULL) {
|
needle = argv[1];
|
||||||
switch (p->fts_info) {
|
paths = &argv[2];
|
||||||
case FTS_D:
|
|
||||||
break;
|
|
||||||
case FTS_F:
|
|
||||||
assert(0); // Process the file p->fts_path, somehow.
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fts_close(ftsp);
|
assert(0); // Initialise the job queue and some worker threads here.
|
||||||
|
|
||||||
assert(0); // Shut down the job queue and the worker threads here.
|
// FTS_LOGICAL = follow symbolic links
|
||||||
|
// FTS_NOCHDIR = do not change the working directory of the process
|
||||||
|
//
|
||||||
|
// (These are not particularly important distinctions for our simple
|
||||||
|
// uses.)
|
||||||
|
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
||||||
|
|
||||||
return 0;
|
FTS *ftsp;
|
||||||
|
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
||||||
|
err(1, "fts_open() failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FTSENT *p;
|
||||||
|
while ((p = fts_read(ftsp)) != NULL) {
|
||||||
|
switch (p->fts_info) {
|
||||||
|
case FTS_D:
|
||||||
|
break;
|
||||||
|
case FTS_F:
|
||||||
|
assert(0); // Process the file p->fts_path, somehow.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fts_close(ftsp);
|
||||||
|
|
||||||
|
assert(0); // Shut down the job queue and the worker threads here.
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
104
A2/fauxgrep.c
104
A2/fauxgrep.c
@ -16,67 +16,67 @@
|
|||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
int fauxgrep_file(char const *needle, char const *path) {
|
int fauxgrep_file(char const *needle, char const *path) {
|
||||||
FILE *f = fopen(path, "r");
|
FILE *f = fopen(path, "r");
|
||||||
|
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
warn("failed to open %s", path);
|
warn("failed to open %s", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
char *line = NULL;
|
|
||||||
size_t linelen = 0;
|
|
||||||
int lineno = 1;
|
|
||||||
|
|
||||||
while (getline(&line, &linelen, f) != -1) {
|
|
||||||
if (strstr(line, needle) != NULL) {
|
|
||||||
printf("%s:%d: %s", path, lineno, line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lineno++;
|
char *line = NULL;
|
||||||
}
|
size_t linelen = 0;
|
||||||
|
int lineno = 1;
|
||||||
|
|
||||||
free(line);
|
while (getline(&line, &linelen, f) != -1) {
|
||||||
fclose(f);
|
if (strstr(line, needle) != NULL) {
|
||||||
|
printf("%s:%d: %s", path, lineno, line);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
lineno++;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char * const *argv) {
|
int main(int argc, char * const *argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
err(1, "usage: STRING paths...");
|
err(1, "usage: STRING paths...");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
char const *needle = argv[1];
|
|
||||||
char * const *paths = &argv[2];
|
|
||||||
|
|
||||||
// FTS_LOGICAL = follow symbolic links
|
|
||||||
// FTS_NOCHDIR = do not change the working directory of the process
|
|
||||||
//
|
|
||||||
// (These are not particularly important distinctions for our simple
|
|
||||||
// uses.)
|
|
||||||
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
|
||||||
|
|
||||||
FTS *ftsp;
|
|
||||||
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
|
||||||
err(1, "fts_open() failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FTSENT *p;
|
|
||||||
while ((p = fts_read(ftsp)) != NULL) {
|
|
||||||
switch (p->fts_info) {
|
|
||||||
case FTS_D:
|
|
||||||
break;
|
|
||||||
case FTS_F:
|
|
||||||
fauxgrep_file(needle, p->fts_path);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fts_close(ftsp);
|
char const *needle = argv[1];
|
||||||
|
char * const *paths = &argv[2];
|
||||||
|
|
||||||
return 0;
|
// FTS_LOGICAL = follow symbolic links
|
||||||
|
// FTS_NOCHDIR = do not change the working directory of the process
|
||||||
|
//
|
||||||
|
// (These are not particularly important distinctions for our simple
|
||||||
|
// uses.)
|
||||||
|
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
||||||
|
|
||||||
|
FTS *ftsp;
|
||||||
|
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
||||||
|
err(1, "fts_open() failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FTSENT *p;
|
||||||
|
while ((p = fts_read(ftsp)) != NULL) {
|
||||||
|
switch (p->fts_info) {
|
||||||
|
case FTS_D:
|
||||||
|
break;
|
||||||
|
case FTS_F:
|
||||||
|
fauxgrep_file(needle, p->fts_path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fts_close(ftsp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -23,65 +23,65 @@ pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|||||||
#include "histogram.h"
|
#include "histogram.h"
|
||||||
|
|
||||||
int main(int argc, char * const *argv) {
|
int main(int argc, char * const *argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
err(1, "usage: paths...");
|
err(1, "usage: paths...");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
int num_threads = 1;
|
|
||||||
char * const *paths = &argv[1];
|
|
||||||
|
|
||||||
if (argc > 3 && strcmp(argv[1], "-n") == 0) {
|
|
||||||
// Since atoi() simply returns zero on syntax errors, we cannot
|
|
||||||
// distinguish between the user entering a zero, or some
|
|
||||||
// non-numeric garbage. In fact, we cannot even tell whether the
|
|
||||||
// given option is suffixed by garbage, i.e. '123foo' returns
|
|
||||||
// '123'. A more robust solution would use strtol(), but its
|
|
||||||
// interface is more complicated, so here we are.
|
|
||||||
num_threads = atoi(argv[2]);
|
|
||||||
|
|
||||||
if (num_threads < 1) {
|
|
||||||
err(1, "invalid thread count: %s", argv[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paths = &argv[3];
|
int num_threads = 1;
|
||||||
} else {
|
char * const *paths = &argv[1];
|
||||||
paths = &argv[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(0); // Initialise the job queue and some worker threads here.
|
if (argc > 3 && strcmp(argv[1], "-n") == 0) {
|
||||||
|
// Since atoi() simply returns zero on syntax errors, we cannot
|
||||||
|
// distinguish between the user entering a zero, or some
|
||||||
|
// non-numeric garbage. In fact, we cannot even tell whether the
|
||||||
|
// given option is suffixed by garbage, i.e. '123foo' returns
|
||||||
|
// '123'. A more robust solution would use strtol(), but its
|
||||||
|
// interface is more complicated, so here we are.
|
||||||
|
num_threads = atoi(argv[2]);
|
||||||
|
|
||||||
// FTS_LOGICAL = follow symbolic links
|
if (num_threads < 1) {
|
||||||
// FTS_NOCHDIR = do not change the working directory of the process
|
err(1, "invalid thread count: %s", argv[2]);
|
||||||
//
|
}
|
||||||
// (These are not particularly important distinctions for our simple
|
|
||||||
// uses.)
|
|
||||||
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
|
||||||
|
|
||||||
FTS *ftsp;
|
paths = &argv[3];
|
||||||
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
} else {
|
||||||
err(1, "fts_open() failed");
|
paths = &argv[1];
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FTSENT *p;
|
|
||||||
while ((p = fts_read(ftsp)) != NULL) {
|
|
||||||
switch (p->fts_info) {
|
|
||||||
case FTS_D:
|
|
||||||
break;
|
|
||||||
case FTS_F:
|
|
||||||
assert(0); // Process the file p->fts_path, somehow.
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fts_close(ftsp);
|
assert(0); // Initialise the job queue and some worker threads here.
|
||||||
|
|
||||||
assert(0); // Shut down the job queue and the worker threads here.
|
// FTS_LOGICAL = follow symbolic links
|
||||||
|
// FTS_NOCHDIR = do not change the working directory of the process
|
||||||
|
//
|
||||||
|
// (These are not particularly important distinctions for our simple
|
||||||
|
// uses.)
|
||||||
|
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
||||||
|
|
||||||
move_lines(9);
|
FTS *ftsp;
|
||||||
|
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
||||||
|
err(1, "fts_open() failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
FTSENT *p;
|
||||||
|
while ((p = fts_read(ftsp)) != NULL) {
|
||||||
|
switch (p->fts_info) {
|
||||||
|
case FTS_D:
|
||||||
|
break;
|
||||||
|
case FTS_F:
|
||||||
|
assert(0); // Process the file p->fts_path, somehow.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fts_close(ftsp);
|
||||||
|
|
||||||
|
assert(0); // Shut down the job queue and the worker threads here.
|
||||||
|
|
||||||
|
move_lines(9);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
112
A2/fhistogram.c
112
A2/fhistogram.c
@ -21,73 +21,73 @@
|
|||||||
int global_histogram[8] = { 0 };
|
int global_histogram[8] = { 0 };
|
||||||
|
|
||||||
int fhistogram(char const *path) {
|
int fhistogram(char const *path) {
|
||||||
FILE *f = fopen(path, "r");
|
FILE *f = fopen(path, "r");
|
||||||
|
|
||||||
int local_histogram[8] = { 0 };
|
int local_histogram[8] = { 0 };
|
||||||
|
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
warn("failed to open %s", path);
|
warn("failed to open %s", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
char c;
|
|
||||||
while (fread(&c, sizeof(c), 1, f) == 1) {
|
|
||||||
i++;
|
|
||||||
update_histogram(local_histogram, c);
|
|
||||||
if ((i % 100000) == 0) {
|
|
||||||
merge_histogram(local_histogram, global_histogram);
|
|
||||||
print_histogram(global_histogram);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
int i = 0;
|
||||||
|
|
||||||
merge_histogram(local_histogram, global_histogram);
|
char c;
|
||||||
print_histogram(global_histogram);
|
while (fread(&c, sizeof(c), 1, f) == 1) {
|
||||||
|
i++;
|
||||||
|
update_histogram(local_histogram, c);
|
||||||
|
if ((i % 100000) == 0) {
|
||||||
|
merge_histogram(local_histogram, global_histogram);
|
||||||
|
print_histogram(global_histogram);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
fclose(f);
|
||||||
|
|
||||||
|
merge_histogram(local_histogram, global_histogram);
|
||||||
|
print_histogram(global_histogram);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char * const *argv) {
|
int main(int argc, char * const *argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
err(1, "usage: paths...");
|
err(1, "usage: paths...");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
char * const *paths = &argv[1];
|
|
||||||
|
|
||||||
// FTS_LOGICAL = follow symbolic links
|
|
||||||
// FTS_NOCHDIR = do not change the working directory of the process
|
|
||||||
//
|
|
||||||
// (These are not particularly important distinctions for our simple
|
|
||||||
// uses.)
|
|
||||||
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
|
||||||
|
|
||||||
FTS *ftsp;
|
|
||||||
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
|
||||||
err(1, "fts_open() failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FTSENT *p;
|
|
||||||
while ((p = fts_read(ftsp)) != NULL) {
|
|
||||||
switch (p->fts_info) {
|
|
||||||
case FTS_D:
|
|
||||||
break;
|
|
||||||
case FTS_F:
|
|
||||||
fhistogram(p->fts_path);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fts_close(ftsp);
|
char * const *paths = &argv[1];
|
||||||
|
|
||||||
move_lines(9);
|
// FTS_LOGICAL = follow symbolic links
|
||||||
|
// FTS_NOCHDIR = do not change the working directory of the process
|
||||||
|
//
|
||||||
|
// (These are not particularly important distinctions for our simple
|
||||||
|
// uses.)
|
||||||
|
int fts_options = FTS_LOGICAL | FTS_NOCHDIR;
|
||||||
|
|
||||||
return 0;
|
FTS *ftsp;
|
||||||
|
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
||||||
|
err(1, "fts_open() failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FTSENT *p;
|
||||||
|
while ((p = fts_read(ftsp)) != NULL) {
|
||||||
|
switch (p->fts_info) {
|
||||||
|
case FTS_D:
|
||||||
|
break;
|
||||||
|
case FTS_F:
|
||||||
|
fhistogram(p->fts_path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fts_close(ftsp);
|
||||||
|
|
||||||
|
move_lines(9);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
130
A2/fibs.c
130
A2/fibs.c
@ -1,6 +1,6 @@
|
|||||||
// This program reads a newline-separated sequence of integers from
|
// This program reads a newline-separated sequence of integers from
|
||||||
// standard input. For each such integer, the corresponding Fibonacci
|
// standard input. For each such integer, the corresponding Fibonacci
|
||||||
// number is printed. This is similar to the programs we saw at the
|
// number is printed. This is similar to the programs we saw at the
|
||||||
// November 20 lecture.
|
// November 20 lecture.
|
||||||
|
|
||||||
// Setting _DEFAULT_SOURCE is necessary to activate visibility of
|
// Setting _DEFAULT_SOURCE is necessary to activate visibility of
|
||||||
@ -31,92 +31,92 @@ pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|||||||
// A simple recursive (inefficient) implementation of the Fibonacci
|
// A simple recursive (inefficient) implementation of the Fibonacci
|
||||||
// function.
|
// function.
|
||||||
int fib (int n) {
|
int fib (int n) {
|
||||||
if (n < 2) {
|
if (n < 2) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return fib(n-1) + fib(n-2);
|
return fib(n-1) + fib(n-2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function converts a line to an integer, computes the
|
// This function converts a line to an integer, computes the
|
||||||
// corresponding Fibonacci number, then prints the result to the
|
// corresponding Fibonacci number, then prints the result to the
|
||||||
// screen.
|
// screen.
|
||||||
void fib_line(const char *line) {
|
void fib_line(const char *line) {
|
||||||
int n = atoi(line);
|
int n = atoi(line);
|
||||||
int fibn = fib(n);
|
int fibn = fib(n);
|
||||||
assert(pthread_mutex_lock(&stdout_mutex) == 0);
|
assert(pthread_mutex_lock(&stdout_mutex) == 0);
|
||||||
printf("fib(%d) = %d\n", n, fibn);
|
printf("fib(%d) = %d\n", n, fibn);
|
||||||
assert(pthread_mutex_unlock(&stdout_mutex) == 0);
|
assert(pthread_mutex_unlock(&stdout_mutex) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each thread will run this function. The thread argument is a
|
// Each thread will run this function. The thread argument is a
|
||||||
// pointer to a job queue.
|
// pointer to a job queue.
|
||||||
void* worker(void *arg) {
|
void* worker(void *arg) {
|
||||||
struct job_queue *jq = arg;
|
struct job_queue *jq = arg;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
char *line;
|
char *line;
|
||||||
if (job_queue_pop(jq, (void**)&line) == 0) {
|
if (job_queue_pop(jq, (void**)&line) == 0) {
|
||||||
fib_line(line);
|
fib_line(line);
|
||||||
free(line);
|
free(line);
|
||||||
} else {
|
} else {
|
||||||
// If job_queue_pop() returned non-zero, that means the queue is
|
// If job_queue_pop() returned non-zero, that means the queue is
|
||||||
// being killed (or some other error occured). In any case,
|
// being killed (or some other error occured). In any case,
|
||||||
// that means it's time for this thread to die.
|
// that means it's time for this thread to die.
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char * const *argv) {
|
int main(int argc, char * const *argv) {
|
||||||
int num_threads = 1;
|
int num_threads = 1;
|
||||||
|
|
||||||
if (argc == 3 && strcmp(argv[1], "-n") == 0) {
|
if (argc == 3 && strcmp(argv[1], "-n") == 0) {
|
||||||
// Since atoi() simply returns zero on syntax errors, we cannot
|
// Since atoi() simply returns zero on syntax errors, we cannot
|
||||||
// distinguish between the user entering a zero, or some
|
// distinguish between the user entering a zero, or some
|
||||||
// non-numeric garbage. In fact, we cannot even tell whether the
|
// non-numeric garbage. In fact, we cannot even tell whether the
|
||||||
// given option is suffixed by garbage, i.e. '123foo' returns
|
// given option is suffixed by garbage, i.e. '123foo' returns
|
||||||
// '123'. A more robust solution would use strtol(), but its
|
// '123'. A more robust solution would use strtol(), but its
|
||||||
// interface is more complicated, so here we are.
|
// interface is more complicated, so here we are.
|
||||||
num_threads = atoi(argv[2]);
|
num_threads = atoi(argv[2]);
|
||||||
|
|
||||||
if (num_threads < 1) {
|
if (num_threads < 1) {
|
||||||
err(1, "invalid thread count: %s", argv[2]);
|
err(1, "invalid thread count: %s", argv[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Create job queue.
|
// Create job queue.
|
||||||
struct job_queue jq;
|
struct job_queue jq;
|
||||||
job_queue_init(&jq, 64);
|
job_queue_init(&jq, 64);
|
||||||
|
|
||||||
// Start up the worker threads.
|
// Start up the worker threads.
|
||||||
pthread_t *threads = calloc(num_threads, sizeof(pthread_t));
|
pthread_t *threads = calloc(num_threads, sizeof(pthread_t));
|
||||||
for (int i = 0; i < num_threads; i++) {
|
for (int i = 0; i < num_threads; i++) {
|
||||||
if (pthread_create(&threads[i], NULL, &worker, &jq) != 0) {
|
if (pthread_create(&threads[i], NULL, &worker, &jq) != 0) {
|
||||||
err(1, "pthread_create() failed");
|
err(1, "pthread_create() failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Now read lines from stdin until EOF.
|
// Now read lines from stdin until EOF.
|
||||||
char *line = NULL;
|
char *line = NULL;
|
||||||
ssize_t line_len;
|
ssize_t line_len;
|
||||||
size_t buf_len = 0;
|
size_t buf_len = 0;
|
||||||
while ((line_len = getline(&line, &buf_len, stdin)) != -1) {
|
while ((line_len = getline(&line, &buf_len, stdin)) != -1) {
|
||||||
job_queue_push(&jq, (void*)strdup(line));
|
job_queue_push(&jq, (void*)strdup(line));
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
|
|
||||||
// Destroy the queue.
|
// Destroy the queue.
|
||||||
job_queue_destroy(&jq);
|
job_queue_destroy(&jq);
|
||||||
|
|
||||||
// Wait for all threads to finish. This is important, at some may
|
// Wait for all threads to finish. This is important, at some may
|
||||||
// still be working on their job.
|
// still be working on their job.
|
||||||
for (int i = 0; i < num_threads; i++) {
|
for (int i = 0; i < num_threads; i++) {
|
||||||
if (pthread_join(threads[i], NULL) != 0) {
|
if (pthread_join(threads[i], NULL) != 0) {
|
||||||
err(1, "pthread_join() failed");
|
err(1, "pthread_join() failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// This header file contains not just function prototypes, but also
|
// This header file contains not just function prototypes, but also
|
||||||
// the definitions. This means it does not need to be compiled
|
// the definitions. This means it does not need to be compiled
|
||||||
// separately.
|
// separately.
|
||||||
//
|
//
|
||||||
// You should not need to modify this file.
|
// You should not need to modify this file.
|
||||||
@ -7,65 +7,65 @@
|
|||||||
#ifndef HISTOGRAM_H
|
#ifndef HISTOGRAM_H
|
||||||
#define HISTOGRAM_H
|
#define HISTOGRAM_H
|
||||||
|
|
||||||
// Move the cursor down 'n' lines. Negative 'n' supported.
|
// Move the cursor down 'n' lines. Negative 'n' supported.
|
||||||
static void move_lines(int n) {
|
static void move_lines(int n) {
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
printf("\033[%dA", -n);
|
printf("\033[%dA", -n);
|
||||||
} else {
|
} else {
|
||||||
printf("\033[%dB", n);
|
printf("\033[%dB", n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear from cursor to end of line.
|
// Clear from cursor to end of line.
|
||||||
static void clear_line() {
|
static void clear_line() {
|
||||||
printf("\033[K");
|
printf("\033[K");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print a visual representation of a histogram to the screen. After
|
// Print a visual representation of a histogram to the screen. After
|
||||||
// printing, the cursor is moved back to the beginning of the output.
|
// printing, the cursor is moved back to the beginning of the output.
|
||||||
// This means that next time print_histogram() is called, the previous
|
// This means that next time print_histogram() is called, the previous
|
||||||
// output will be overwritten.
|
// output will be overwritten.
|
||||||
static void print_histogram(int histogram[8]) {
|
static void print_histogram(int histogram[8]) {
|
||||||
int64_t bits_seen = 0;
|
int64_t bits_seen = 0;
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
bits_seen += histogram[i];
|
bits_seen += histogram[i];
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
clear_line();
|
|
||||||
printf("Bit %d: ", i);
|
|
||||||
|
|
||||||
double proportion = histogram[i] / ((double)bits_seen);
|
|
||||||
for (int i = 0; i < 60*proportion; i++) {
|
|
||||||
printf("*");
|
|
||||||
}
|
}
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_line();
|
for (int i = 0; i < 8; i++) {
|
||||||
printf("%ld bits processed.\n", (long)bits_seen);
|
clear_line();
|
||||||
move_lines(-9);
|
printf("Bit %d: ", i);
|
||||||
|
|
||||||
|
double proportion = histogram[i] / ((double)bits_seen);
|
||||||
|
for (int i = 0; i < 60*proportion; i++) {
|
||||||
|
printf("*");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_line();
|
||||||
|
printf("%ld bits processed.\n", (long)bits_seen);
|
||||||
|
move_lines(-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge the former histogram into the latter, setting the former to
|
// Merge the former histogram into the latter, setting the former to
|
||||||
// zero in the process.
|
// zero in the process.
|
||||||
static void merge_histogram(int from[8], int to[8]) {
|
static void merge_histogram(int from[8], int to[8]) {
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
to[i] += from[i];
|
to[i] += from[i];
|
||||||
from[i] = 0;
|
from[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the histogram with the bits of a byte.
|
// Update the histogram with the bits of a byte.
|
||||||
static void update_histogram(int histogram[8], unsigned char byte) {
|
static void update_histogram(int histogram[8], unsigned char byte) {
|
||||||
// For all bits in a byte...
|
// For all bits in a byte...
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
// count if bit 'i' is set.
|
// count if bit 'i' is set.
|
||||||
if (byte & (1<<i)) {
|
if (byte & (1<<i)) {
|
||||||
histogram[i]++;
|
histogram[i]++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,18 +4,90 @@
|
|||||||
|
|
||||||
#include "job_queue.h"
|
#include "job_queue.h"
|
||||||
|
|
||||||
|
pthread_mutex_t queue_operation = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_mutex_t queue_push = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_mutex_t queue_pop = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_mutex_t queue_destroy = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
int job_queue_init(struct job_queue *job_queue, int capacity) {
|
int job_queue_init(struct job_queue *job_queue, int capacity) {
|
||||||
assert(0);
|
pthread_mutex_lock(&queue_operation);
|
||||||
|
pthread_mutex_lock(&queue_push);
|
||||||
|
pthread_mutex_lock(&queue_pop);
|
||||||
|
pthread_mutex_lock(&queue_destroy);
|
||||||
|
|
||||||
|
job_queue->capacity = capacity;
|
||||||
|
job_queue->size = 0;
|
||||||
|
job_queue->jobs = malloc(capacity);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&queue_operation);
|
||||||
|
pthread_mutex_unlock(&queue_push);
|
||||||
|
pthread_mutex_unlock(&queue_destroy);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int job_queue_destroy(struct job_queue *job_queue) {
|
int job_queue_destroy(struct job_queue *job_queue) {
|
||||||
assert(0);
|
printf("esauc\n");
|
||||||
|
pthread_mutex_lock(&queue_destroy);
|
||||||
|
pthread_mutex_lock(&queue_operation);
|
||||||
|
|
||||||
|
printf("esauc\n");
|
||||||
|
//free(job_queue->jobs);
|
||||||
|
printf("esauc\n");
|
||||||
|
//free(job_queue);
|
||||||
|
printf("esauc\n");
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&queue_push);
|
||||||
|
pthread_mutex_unlock(&queue_pop);
|
||||||
|
pthread_mutex_unlock(&queue_destroy);
|
||||||
|
pthread_mutex_unlock(&queue_operation);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int job_queue_push(struct job_queue *job_queue, void *data) {
|
int job_queue_push(struct job_queue *job_queue, void *data) {
|
||||||
assert(0);
|
pthread_mutex_lock(&queue_push);
|
||||||
|
pthread_mutex_lock(&queue_operation);
|
||||||
|
|
||||||
|
(&job_queue->jobs)[job_queue->size] = data;
|
||||||
|
job_queue->size = job_queue->size + 1;
|
||||||
|
|
||||||
|
if (job_queue->size != job_queue->capacity) {
|
||||||
|
pthread_mutex_unlock(&queue_push);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job_queue->size == 1) {
|
||||||
|
pthread_mutex_unlock(&queue_pop);
|
||||||
|
pthread_mutex_lock(&queue_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&queue_operation);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int job_queue_pop(struct job_queue *job_queue, void **data) {
|
int job_queue_pop(struct job_queue *job_queue, void **data) {
|
||||||
assert(0);
|
pthread_mutex_lock(&queue_pop);
|
||||||
|
pthread_mutex_lock(&queue_operation);
|
||||||
|
|
||||||
|
if (job_queue == NULL) {
|
||||||
|
pthread_mutex_unlock(&queue_pop);
|
||||||
|
pthread_mutex_unlock(&queue_operation);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
job_queue->size = job_queue->size - 1;
|
||||||
|
*data = (&job_queue->jobs)[job_queue->size];
|
||||||
|
|
||||||
|
if (job_queue->size == 0) {
|
||||||
|
pthread_mutex_unlock(&queue_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job_queue->size != 0) {
|
||||||
|
pthread_mutex_unlock(&queue_pop);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job_queue->size == job_queue->capacity - 1) {
|
||||||
|
pthread_mutex_unlock(&queue_pop);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&queue_operation);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4,25 +4,27 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
struct job_queue {
|
struct job_queue {
|
||||||
int dummy;
|
int capacity;
|
||||||
|
int size;
|
||||||
|
void* jobs;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialise a job queue with the given capacity. The queue starts out
|
// Initialise a job queue with the given capacity. The queue starts out
|
||||||
// empty. Returns non-zero on error.
|
// empty. Returns non-zero on error.
|
||||||
int job_queue_init(struct job_queue *job_queue, int capacity);
|
int job_queue_init(struct job_queue *job_queue, int capacity);
|
||||||
|
|
||||||
// Destroy the job queue. Blocks until the queue is empty before it
|
// Destroy the job queue. Blocks until the queue is empty before it
|
||||||
// is destroyed.
|
// is destroyed.
|
||||||
int job_queue_destroy(struct job_queue *job_queue);
|
int job_queue_destroy(struct job_queue *job_queue);
|
||||||
|
|
||||||
// Push an element onto the end of the job queue. Blocks if the
|
// Push an element onto the end of the job queue. Blocks if the
|
||||||
// job_queue is full (its size is equal to its capacity). Returns
|
// job_queue is full (its size is equal to its capacity). Returns
|
||||||
// non-zero on error. It is an error to push a job onto a queue that
|
// non-zero on error. It is an error to push a job onto a queue that
|
||||||
// has been destroyed.
|
// has been destroyed.
|
||||||
int job_queue_push(struct job_queue *job_queue, void *data);
|
int job_queue_push(struct job_queue *job_queue, void *data);
|
||||||
|
|
||||||
// Pop an element from the front of the job queue. Blocks if the
|
// Pop an element from the front of the job queue. Blocks if the
|
||||||
// job_queue contains zero elements. Returns non-zero on error. If
|
// job_queue contains zero elements. Returns non-zero on error. If
|
||||||
// job_queue_destroy() has been called (possibly after the call to
|
// job_queue_destroy() has been called (possibly after the call to
|
||||||
// job_queue_pop() blocked), this function will return -1.
|
// job_queue_pop() blocked), this function will return -1.
|
||||||
int job_queue_pop(struct job_queue *job_queue, void **data);
|
int job_queue_pop(struct job_queue *job_queue, void **data);
|
||||||
|
BIN
A2/testfile
Executable file
BIN
A2/testfile
Executable file
Binary file not shown.
16
A2/testfile.c
Normal file
16
A2/testfile.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "job_queue.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct job_queue q;
|
||||||
|
int b = 5;
|
||||||
|
job_queue_init(&q, 64);
|
||||||
|
job_queue_push(&q, &b);
|
||||||
|
void* a = malloc(1);
|
||||||
|
job_queue_pop(&q, &a);
|
||||||
|
printf("%i\n",*((int *) a));
|
||||||
|
printf("esauc\n");
|
||||||
|
job_queue_destroy(&q);
|
||||||
|
}
|
Reference in New Issue
Block a user