😄
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
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
|
||||
|
||||
.PHONY: all test clean ../src.zip
|
||||
|
106
A2/fauxgrep-mt.c
106
A2/fauxgrep-mt.c
@ -20,68 +20,68 @@
|
||||
#include "job_queue.h"
|
||||
|
||||
int main(int argc, char * const *argv) {
|
||||
if (argc < 2) {
|
||||
err(1, "usage: [-n INT] STRING paths...");
|
||||
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]);
|
||||
if (argc < 2) {
|
||||
err(1, "usage: [-n INT] STRING paths...");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
needle = argv[3];
|
||||
paths = &argv[4];
|
||||
int num_threads = 1;
|
||||
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
|
||||
// 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;
|
||||
if (num_threads < 1) {
|
||||
err(1, "invalid thread count: %s", argv[2]);
|
||||
}
|
||||
|
||||
FTS *ftsp;
|
||||
if ((ftsp = fts_open(paths, fts_options, NULL)) == NULL) {
|
||||
err(1, "fts_open() failed");
|
||||
return -1;
|
||||
}
|
||||
needle = argv[3];
|
||||
paths = &argv[4];
|
||||
|
||||
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;
|
||||
} else {
|
||||
needle = argv[1];
|
||||
paths = &argv[2];
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
int fauxgrep_file(char const *needle, char const *path) {
|
||||
FILE *f = fopen(path, "r");
|
||||
FILE *f = fopen(path, "r");
|
||||
|
||||
if (f == NULL) {
|
||||
warn("failed to open %s", path);
|
||||
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);
|
||||
if (f == NULL) {
|
||||
warn("failed to open %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lineno++;
|
||||
}
|
||||
char *line = NULL;
|
||||
size_t linelen = 0;
|
||||
int lineno = 1;
|
||||
|
||||
free(line);
|
||||
fclose(f);
|
||||
while (getline(&line, &linelen, f) != -1) {
|
||||
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) {
|
||||
if (argc < 2) {
|
||||
err(1, "usage: STRING paths...");
|
||||
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;
|
||||
if (argc < 2) {
|
||||
err(1, "usage: STRING paths...");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
int main(int argc, char * const *argv) {
|
||||
if (argc < 2) {
|
||||
err(1, "usage: paths...");
|
||||
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]);
|
||||
if (argc < 2) {
|
||||
err(1, "usage: paths...");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
paths = &argv[3];
|
||||
} else {
|
||||
paths = &argv[1];
|
||||
}
|
||||
int num_threads = 1;
|
||||
char * const *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
|
||||
// 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;
|
||||
if (num_threads < 1) {
|
||||
err(1, "invalid thread count: %s", argv[2]);
|
||||
}
|
||||
|
||||
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;
|
||||
paths = &argv[3];
|
||||
} else {
|
||||
paths = &argv[1];
|
||||
}
|
||||
}
|
||||
|
||||
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 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) {
|
||||
fflush(stdout);
|
||||
warn("failed to open %s", path);
|
||||
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);
|
||||
if (f == NULL) {
|
||||
fflush(stdout);
|
||||
warn("failed to open %s", path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
int i = 0;
|
||||
|
||||
merge_histogram(local_histogram, global_histogram);
|
||||
print_histogram(global_histogram);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
fclose(f);
|
||||
|
||||
merge_histogram(local_histogram, global_histogram);
|
||||
print_histogram(global_histogram);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char * const *argv) {
|
||||
if (argc < 2) {
|
||||
err(1, "usage: paths...");
|
||||
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;
|
||||
if (argc < 2) {
|
||||
err(1, "usage: paths...");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// standard input. For each such integer, the corresponding Fibonacci
|
||||
// number is printed. This is similar to the programs we saw at the
|
||||
// standard input. For each such integer, the corresponding Fibonacci
|
||||
// number is printed. This is similar to the programs we saw at the
|
||||
// November 20 lecture.
|
||||
|
||||
// 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
|
||||
// function.
|
||||
int fib (int n) {
|
||||
if (n < 2) {
|
||||
return 1;
|
||||
} else {
|
||||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
if (n < 2) {
|
||||
return 1;
|
||||
} else {
|
||||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
}
|
||||
|
||||
// This function converts a line to an integer, computes the
|
||||
// corresponding Fibonacci number, then prints the result to the
|
||||
// screen.
|
||||
void fib_line(const char *line) {
|
||||
int n = atoi(line);
|
||||
int fibn = fib(n);
|
||||
assert(pthread_mutex_lock(&stdout_mutex) == 0);
|
||||
printf("fib(%d) = %d\n", n, fibn);
|
||||
assert(pthread_mutex_unlock(&stdout_mutex) == 0);
|
||||
int n = atoi(line);
|
||||
int fibn = fib(n);
|
||||
assert(pthread_mutex_lock(&stdout_mutex) == 0);
|
||||
printf("fib(%d) = %d\n", n, fibn);
|
||||
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.
|
||||
void* worker(void *arg) {
|
||||
struct job_queue *jq = arg;
|
||||
struct job_queue *jq = arg;
|
||||
|
||||
while (1) {
|
||||
char *line;
|
||||
if (job_queue_pop(jq, (void**)&line) == 0) {
|
||||
fib_line(line);
|
||||
free(line);
|
||||
} else {
|
||||
// If job_queue_pop() returned non-zero, that means the queue is
|
||||
// being killed (or some other error occured). In any case,
|
||||
// that means it's time for this thread to die.
|
||||
break;
|
||||
while (1) {
|
||||
char *line;
|
||||
if (job_queue_pop(jq, (void**)&line) == 0) {
|
||||
fib_line(line);
|
||||
free(line);
|
||||
} else {
|
||||
// If job_queue_pop() returned non-zero, that means the queue is
|
||||
// being killed (or some other error occured). In any case,
|
||||
// that means it's time for this thread to die.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char * const *argv) {
|
||||
int num_threads = 1;
|
||||
int num_threads = 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 (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]);
|
||||
if (num_threads < 1) {
|
||||
err(1, "invalid thread count: %s", argv[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create job queue.
|
||||
struct job_queue jq;
|
||||
job_queue_init(&jq, 64);
|
||||
// Create job queue.
|
||||
struct job_queue jq;
|
||||
job_queue_init(&jq, 64);
|
||||
|
||||
// Start up the worker threads.
|
||||
pthread_t *threads = calloc(num_threads, sizeof(pthread_t));
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
if (pthread_create(&threads[i], NULL, &worker, &jq) != 0) {
|
||||
err(1, "pthread_create() failed");
|
||||
// Start up the worker threads.
|
||||
pthread_t *threads = calloc(num_threads, sizeof(pthread_t));
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
if (pthread_create(&threads[i], NULL, &worker, &jq) != 0) {
|
||||
err(1, "pthread_create() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now read lines from stdin until EOF.
|
||||
char *line = NULL;
|
||||
ssize_t line_len;
|
||||
size_t buf_len = 0;
|
||||
while ((line_len = getline(&line, &buf_len, stdin)) != -1) {
|
||||
job_queue_push(&jq, (void*)strdup(line));
|
||||
}
|
||||
free(line);
|
||||
|
||||
// Destroy the queue.
|
||||
job_queue_destroy(&jq);
|
||||
|
||||
// Wait for all threads to finish. This is important, at some may
|
||||
// still be working on their job.
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
if (pthread_join(threads[i], NULL) != 0) {
|
||||
err(1, "pthread_join() failed");
|
||||
// Now read lines from stdin until EOF.
|
||||
char *line = NULL;
|
||||
ssize_t line_len;
|
||||
size_t buf_len = 0;
|
||||
while ((line_len = getline(&line, &buf_len, stdin)) != -1) {
|
||||
job_queue_push(&jq, (void*)strdup(line));
|
||||
}
|
||||
free(line);
|
||||
|
||||
// Destroy the queue.
|
||||
job_queue_destroy(&jq);
|
||||
|
||||
// Wait for all threads to finish. This is important, at some may
|
||||
// still be working on their job.
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
if (pthread_join(threads[i], NULL) != 0) {
|
||||
err(1, "pthread_join() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
//
|
||||
// You should not need to modify this file.
|
||||
@ -7,65 +7,65 @@
|
||||
#ifndef 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) {
|
||||
if (n < 0) {
|
||||
printf("\033[%dA", -n);
|
||||
} else {
|
||||
printf("\033[%dB", n);
|
||||
}
|
||||
if (n < 0) {
|
||||
printf("\033[%dA", -n);
|
||||
} else {
|
||||
printf("\033[%dB", n);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear from cursor to end of 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.
|
||||
// This means that next time print_histogram() is called, the previous
|
||||
// output will be overwritten.
|
||||
static void print_histogram(int histogram[8]) {
|
||||
int64_t bits_seen = 0;
|
||||
int64_t bits_seen = 0;
|
||||
|
||||
for (int i = 0; i < 8; 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("*");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bits_seen += histogram[i];
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
clear_line();
|
||||
printf("%ld bits processed.\n", (long)bits_seen);
|
||||
move_lines(-9);
|
||||
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();
|
||||
printf("%ld bits processed.\n", (long)bits_seen);
|
||||
move_lines(-9);
|
||||
}
|
||||
|
||||
// Merge the former histogram into the latter, setting the former to
|
||||
// zero in the process.
|
||||
static void merge_histogram(int from[8], int to[8]) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
to[i] += from[i];
|
||||
from[i] = 0;
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
to[i] += from[i];
|
||||
from[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the histogram with the bits of a byte.
|
||||
static void update_histogram(int histogram[8], unsigned char byte) {
|
||||
// For all bits in a byte...
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// count if bit 'i' is set.
|
||||
if (byte & (1<<i)) {
|
||||
histogram[i]++;
|
||||
// For all bits in a byte...
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// count if bit 'i' is set.
|
||||
if (byte & (1<<i)) {
|
||||
histogram[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -4,18 +4,90 @@
|
||||
|
||||
#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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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>
|
||||
|
||||
struct job_queue {
|
||||
int dummy;
|
||||
int capacity;
|
||||
int size;
|
||||
void* jobs;
|
||||
};
|
||||
|
||||
// Initialise a job queue with the given capacity. The queue starts out
|
||||
// empty. Returns non-zero on error.
|
||||
// Initialise a job queue with the given capacity. The queue starts out
|
||||
// empty. Returns non-zero on error.
|
||||
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.
|
||||
int job_queue_destroy(struct job_queue *job_queue);
|
||||
|
||||
// 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
|
||||
// non-zero on error. It is an error to push a job onto a queue that
|
||||
// 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
|
||||
// non-zero on error. It is an error to push a job onto a queue that
|
||||
// has been destroyed.
|
||||
int job_queue_push(struct job_queue *job_queue, void *data);
|
||||
|
||||
// Pop an element from the front of the job queue. Blocks if the
|
||||
// job_queue contains zero elements. Returns non-zero on error. If
|
||||
// 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_destroy() has been called (possibly after the call to
|
||||
// job_queue_pop() blocked), this function will return -1.
|
||||
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