The goal of this task is to create two concurrent activities ("Threads" or "Tasks", not processes.) that share data synchronously. Your language may provide syntax or libraries to perform concurrency. Different languages provide different implementations of concurrency, often with different names. Some languages use the term threads, others use the term tasks, while others use co-processes. This task should not be implemented using fork, spawn, or the Linux/UNIX/Win32 pipe command, as communication should be between threads, not processes.

One of the concurrent units will read from a file named "input.txt" and send the contents of that file, one line at a time, to the other concurrent unit, which will print the line it receives to standard output. The printing unit must count the number of lines it prints. After the concurrent unit reading the file sends its last line to the printing unit, the reading unit will request the number of lines printed by the printing unit. The reading unit will then print the number of lines printed by the printing unit.

This task requires two-way communication between the concurrent units. All concurrent units must cleanly terminate at the end of the program.

#include <stdlib.h>	/* malloc(), realloc(), free() */
#include <stdio.h>	/* fopen(), fgetc(), fwrite(), printf() */
 
#include <libco.h>	/* co_create(), co_switch() */
 
void
fail(const char *message) {
	perror(message);
	exit(1);
}
 
/*
 * These are global variables of this process. All cothreads of this
 * process will share these global variables.
 */
cothread_t reader;
cothread_t printer;
struct {
	char	*buf;	/* Not a C string. No terminating '\0'. */
	size_t	len;	/* Length of line in buffer. */
	size_t	cap;	/* Maximum capacity of buffer. */
} line;
size_t count;		/* Number of lines printed. */
 
/*
 * The reader cothread reads every line of an input file, passes each
 * line to the printer cothread, and reports the number of lines.
 */
void
reader_entry(void)
{
	FILE *input;
	size_t newcap;
	int c, eof, eol;
	char *newbuf;
 
	input = fopen("input.txt", "r");
	if (input == NULL)
		fail("fopen");
 
	line.buf = malloc(line.cap = 4096);  /* New buffer. */
	if (line.buf == NULL)
		fail("malloc");
	line.len = 0;  /* Start with an empty line. */
 
	do {
		c = fgetc(input);  /* Read next character. */
		if (ferror(input))
			fail("fgetc");
 
		eof = (c == EOF);
		if (eof) {
			/*
			 * End of file is also end of line,
		`	 * unless the line would be empty.
			 */
			eol = (line.len > 0);
		} else {
			/* Append c to the buffer. */
			if (line.len == line.cap) {
				/* Need a bigger buffer! */
				newcap = line.cap * 2;
				newbuf = realloc(line.buf, newcap);
				if (newbuf == NULL)
					fail("realloc");
				line.buf = newbuf;
				line.cap = newcap;
			}
			line.buf[line.len++] = c;
 
			/* '\n' is end of line. */
			eol = (c == '\n');
		}
 
		if (eol) {
			/* Pass our complete line to the printer. */
			co_switch(printer);
			line.len = 0;  /* Destroy our line. */
		}
	} while (!eof);
 
	free(line.buf);
	line.buf = NULL;  /* Stops a loop in the printer. */
 
	printf("Printed %zu lines.\n", count);
	co_switch(printer);
}
 
/*
 * The printer cothread starts the reader cothread, prints every line
 * line from the reader cothread, and counts the number of lines.
 */
int
main()
{
	reader = co_create(4096, reader_entry);
	printer = co_active();
	count = 0;
 
	for (;;) {
		co_switch(reader);
		if (line.buf == NULL)
			break;
 
		/* Print this line. Count it. */
		fwrite(line.buf, 1, line.len, stdout);
		count++;
	}
 
	co_delete(reader);
	return 0;
}