\documentclass[10pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}

\usepackage{enumerate}
\usepackage{listings}
\usepackage{url}
\usepackage{float}
\usepackage[toc,page]{appendix}

\usepackage[dutch]{babel}
\usepackage{listings}

\lstset{
language=C,
numbers=left,
numberstyle=\footnotesize,
stepnumber=1,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=4,
captionpos=t,
breaklines=false
}

% dot graphs
\usepackage{dot2texi}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}

\title{Operating systems opdracht 4: POSIX threads}
\author{Sander van Veen \& Tadde\"us Kroes \\
        6167969 \& 6054129 \\
		\url{sandervv@gmail.com} \& \url{taddeuskroes@hotmail.com} }

\begin{document}

\definecolor{darkgray}{rgb}{0.95,0.95,0.95}

\maketitle

\tableofcontents

\pagebreak

\section{Inleiding tot threads}

Steeds meer computers (en servers al helemaal) hebben meer dan \'e\'en processor
aan boord. Meerdere processoren maken het mogelijk om taken parallel uit te
voeren, maar hiervoor dient men wel meerdere taken te hebben (anders valt er
weinig parallel uit te voeren). Het gebruik van threads maakt het mogelijk om
bijvoorbeeld:

\begin{enumerate}
\item bepaalde problemen helderder te programmeren, doordat diverse onderdelen
      van een programma die afwisselend moeten worden uitgevoerd, in hoge mate
      onafhankelijk van elkaar kunnen worden beschreven.
\item deeltaken, zoals het afhandelen van service-aanvragen in een server
      programma, eenvoudig af te splitsen.
\item multi-processorsystemen beter te benutten door I/O operaties in een
      gescheide thread te laten draaien en meerdere taken tegelijkertijd te
      laten draaien.
\end{enumerate}

\subsection{Threads en performance}
Threads hebben een shared state (zoals een fork van een process) en shared
memory en zijn daarom de meest voor de hand liggende oplossing om een hoge
\emph{concurrency}\footnote{Programma's die in staat zijn om een hoge
concurrency af te handelen, kunnen snel/goed een groot aantal gelijktijdige
requests/users aan. Concurrency is belangrijk voor servers.} aan te kunnen.

Threads zijn in feite subprogramma's die voor een groot deel onafhankelijk van
elkaar functioneren. Merk op dat het mogelijk is om threads niet meer
onafhankelijk van elkaar te maken door \texttt{join()} als host\footnote{In dit
geval is een \emph{host} de thread die andere threads aanmaakt.} op een of meer
threads uit te voeren. Dan voegen de threads samen en wordt de code na de
\texttt{join()} weer sequentieel uitgevoerd. Soms kan geen \texttt{join()} worden
uitgevoerd op een thread, want een thread kan als \texttt{detached} worden
ingesteld.

Niet in alle gevallen zijn taken in threads op te delen. Als de taak niet op te
delen is in stukken die onafhankelijk van elkaar kunnen worden uitgevoerd,
levert een multi-processor systeem geen performance winst op. Het kan ook zijn
dat het opdelen van een process in meerdere onafhankelijke stukken geen winst
oplevert voor de throughput op netwerk niveau:

\begin{quote}First, the more threads handling a parallelized request, the larger
the overall response time. Often all parallel tasks must finish before a request
is completed, and thus the overall response time becomes the maximum response
time of any subtask, and more subtasks will push further into the long tail of
subtask response times. With 10 subtasks, a one-in-a-thousand chance of
suboptimal process scheduling will affect 1 percent of requests (recall that the
request time is the maximum of all subrequests), but with 1,000 subtasks it will
affect virtually all requests.\footnote{Tekst afkomstig uit
\url{http://research.google.com/pubs/archive/36448.pdf}, geschreven door Urs
Hölzle (senior vice president of operations van Google).}\end{quote}

Het kan dus zijn dat het opdelen van processen in deeltaken een negatieve impact
heeft op de totale throughput. Het opdelen van een aantal onafhankelijke
requests over meerdere servers zorgt voor lagere gemiddelde response time. Maar
het opdelen van een individuele request over meerdere servers kan gemiddeld
langer duren. Dus het niveau waarop taken worden verdeeld is belangrijk.

\subsection{Waarom zijn threads zo moeilijk?}

De implementatie van een goed werkende multithreaded applicatie is moeilijk. Dit
komt doordat threads te maken hebben met \emph{pre-emptive scheduling}, meerdere
\emph{independent execution streams} en synchronisatieproblemen.

Debuggers zijn in veel gevallen een stuk minder geschikt om te gebruiken bij een
multithreaded applicatie. Doordat code tegelijkertijd kan worden uitgevoerd in
plaats van sequentieel, is het debuggen van een stuk code lastiger. Het geheugen
kan namelijk in de tussentijd zijn aangepast door een andere thread, terwijl dat
bij een sequentieel programma niet kan.

Omdat threads door middel van \emph{pre-emptive scheduling} parallel worden
uitgevoerd, is het aanmaken van een logbestand met alle acties die de threads
uitvoeren lastig. De uitvoer van het logbestand zal afhankelijk zijn van de
manier waarop de \emph{process scheduler} de threads heeft ingedeeld.

Tot slot is er ook nog een probleem met het gebruik van shared memory tussen de
threads: synchronisatie. Als twee threads met hetzelfde stukje geheugen werken,
dan kan het probleem optreden dat een van de twee threads de waarde van de
andere thread overschrijft, zonder dat de threads dit van elkaar weten.

Een van de oplossingen voor het synchronisatieprobleem is het gebruik van
\emph{locks} en/of \emph{conditions}. Door middel van locks is het mogelijk om
een stuk geheugen tijdelijk af te schermen voor een van de threads en weer
beschikbaar te stellen als de thread de waarde heeft kunnen bijwerken. Als de
lock wordt opgeheven (waarde is bijgewerkt), dan kunnen de andere threads pas
dat stuk geheugen veranderen.

De implementatie van locks is moeilijk, doordat een enkele keer dat een lock
niet (goed) wordt gebruikt de data corrupt is. Alle threads moeten dus hetzelfde
lock mechanisme hanteren, anders is er geen zekerheid dat de data niet in de
tussentijd door een andere thread is overschreven. Het tweede probleem met locks
is dat er een \emph{deadlock}situatie kan optreden. Dit houdt in dat twee
threads eindeloos op elkaar wachten: thread 1 beschikt over lock 1 en heeft lock
2 nodig, en thread 2 verkeert in de omgekeerde situatie.

Naast de bovenstaande deadlocksituatie, kan het ook voorkomen dat een thread
nooit de mogelijkheid krijgt om de tweede lock te krijgen. Als een thread
oneindig lang op zijn tweede lock moet wachten, spreekt men van
\emph{starvation}. Dit is anders dan een deadlocksituatie omdat bij starvation
maar een deel van de processen nooit zijn werk kan doen (bij deadlock kan geen
enkel proces zijn werk doen).

\subsection{Events versus threads}

Een van de alternatieven voor threads is het gebruik van \emph{events}. Veel
applicaties plaatsen de graphical user interface in een aparte thread om het
programma optimaal zijn werk te kunnen laten doen, terwijl de gebruiker
bijvoorbeeld met zijn muis over het venster beweegt. Events zouden hier een
goede oplossing zijn omdat events veel minder tijd kost om te ontwikkelen voor
een ontwikkelaar:

\begin{enumerate}
\item \emph{Timing dependencies} treden alleen op n\'a het plaatsvinden van een
      event en zijn dus niet afhankelijk van proces scheduling. Een logbestand
      is hierdoor veel gemakkelijker bij te houden.
\item Geen synchronisatieprobleem doordat de events sequentieel worden
      afgehandeld in plaats van parallel.
\item Er vinden geen deadlock- en/of starvationsituaties meer plaats, doordat
      er geen locks worden gebruikt die de threads in goede banen leiden.
\item De ontwikkelaar kan gemakkelijker met een debugger werken, want
      synchronisatie en parallelisme vormen geen problemen meer.
\end{enumerate}

\noindent
Daarnaast werkt een \emph{event-based} applicatie veel effici\"enter dan een
multithreaded applicatie als alleen een enkele CPU in het systeem aanwezig is.
Dit komt doordat er geen \emph{locking overhead} en \emph{context switches}
(tussen de threads) meer nodig zijn. In veel applicaties is het dan ook niet
noodzakelijk om alles parallel te laten lopen. Daar moet wel een kanttekening
bij worden geplaatst: als er meerdere CPU's aanwezig zijn en de taak in
onafhankelijke subtaken is op te delen, dan geeft een multithreaded applicatie
wel degelijk een voordeel. Dit gaat echter wel te kostte van veel ontwikkeltijd.

Het blijft dus de vraag of de kosten voor de ontwikkeling (loon) opwegen tegen
de kosten voor meer hardware. Als er geen duidelijke voorkeur is, zou ik
persoonlijk kiezen voor het bouwen een prototype dat is gebaseerd op events.
Als blijkt dat het systeem der mate slecht presteert, kan er altijd nog een deel
van de applicatie herschreven worden tot een multithreaded applicatie.

\pagebreak

\section{De opgave}

Deze opgave gaat over het ``Dinerende Filosofen Probleem''\footnote{
\url{http://en.wikipedia.org/wiki/Dining\_philosophers\_problem}}. De
probleemstelling is simpel: Er is een tafel, met een $n$ filosofen, en $n$
aantal eetstokjes. De filosofen willen afwisselend eten en nadenken. Elke
filosoof heeft twee eetstokjes nodig om te eten. Voor deze opgave dient men een
simulator te maken die dit probleem nabootst, gebruik makend van POSIX threads
(\texttt{pthreads}). De eetstokjes dienen te worden gemodelleerd als mutexen
(\texttt{pthread\_mutex}).

\subsection{De host van het diner}

De oorspronkelijke thread (die wordt gestart vanuit \texttt{main()}), wordt de
``host'' genoemd. De host voert de volgende taken uit:

\begin{enumerate}
\item De host bepaalt hoeveel filosofen er gesimuleerd moeten worden (minimaal
      2, maar er mag geen ingeprogrammeerde bovengrens zijn). Vanaf de
      commandline kan het aantal te simuleren filosofen worden aangepast:
      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
host_start( argc > 1 ? atoi(argv[1]) : 2 );
      \end{lstlisting}
      In \texttt{host\_start()} wordt dan nog gecontroleerd of de gegeven
      integer ook daadwerkelijk groter of gelijk aan 2 is. Mocht iemand anders
      de functie \texttt{host\_start()} aanroepen, dan is het goed dat alle
      voorwaardes worden gecontroleerd (defensief programmeren).
\item De host initialiseert de diverse benodigde variabelen, zoals de mutexen
      voor de eetstokjes. Dit wordt opgelost in het volgende codefragment:

      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray},xrightmargin=-1.2in]
pthread_attr_t attr;
pthread_t *threads = malloc(philos * sizeof(pthread_t));
forks = malloc(philos * sizeof(pthread_mutex_t));

// POSIX standard specifies that threads are joinable by default, 
// but unfortunately, not all pthread implementations set threads
// as joinable by default. Therefore, set the thread's detach state.
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

// Let all philosophers wait before they can eat diner.
pthread_barrier_init(&wait_barrier, NULL, philos);

// Create the cutlery (mutexes) and initialise the statistics.
for(int i = 0; i < philos; i++) {
    pthread_mutex_init(&forks[i], NULL);
    stats[i].meals = 0;
    stats[i].forks = 0;
    stats[i].id = i;
    stats[i].locked = 0;
}
      \end{lstlisting}
      
      Merk op dat er een \emph{barrier} wordt aangemaakt, want die zal er voor
      zorgen dat elke filosoof netjes wacht totdat alle filosofen aan tafel
      zitten.\footnote{ The \emph{count} argument specifies the number of
      threads that must call \texttt{pthread\_barrier\_wait()} before any of
      them successfully return from the call. From: \url{http://manpages.
      ubuntu.com/manpages/maverick/man3/pthread_barrier_init.3posix.html}} De
      derde parameter is een getal die het aantal ``wachtende'' threads telt en
      als dat in dit geval gelijk is aan alle filosofen, dan wordt deze
      barri\`ere opgeheven.

      In de struct \texttt{diner\_stats} staat ook \texttt{id}, wat het
      stoelnummer voorstelt van de filosoof. Dit stoelnummer bepaalt de te
      gebruiken vorken van de filosoof. Er is bewust gekozen om deze taak aan de
      host van het diner te geven. Een nummertje trek je bij de slager, niet als
      je uitgenodigd wordt voor een diner. De host bepaalt zo (zonder
      voorbedachte raden) hoe de tafelschikking is.

\item De host maakt de threads waarmee de filosofen zullen worden gesimuleerd,
      en kiest het gedrag van elke individuele filosoof (daarover later meer).
      De threads worden als volgt aangemaakt:
    
      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray},xrightmargin=-.9in]
for(int i = 0; i < philos; i++) {
    printf("Host: inviting philo #%d\n", i);
   
    rc = pthread_create(threads+i, &attr, philo_start,
                        (void*)&stats[i]);

    if(rc) {
        fprintf(stderr, "pthread_create() returned: %d\n", rc);
        exit(-1);
    }
}
      \end{lstlisting}
    
      Merk op dat hier bewust geen gedrag aan een filosoof wordt toegewezen. Dit
      is een eigenschap van de filosoof en is daarom verplaatst naar de functie
      van een filosoof, die aangeroepen wordt als de filosoof ``binnenkomt''
      (in deze opdracht is dat \texttt{philo\_start()}).

\item De host controleert minimaal 100 maal per seconde of er misschien deadlock
      is ontstaan. Als dat mocht zijn gebeurd, zorgt hij ervoor dat de simulatie
      netjes wordt beëindigd. Tussen de controles door mag de host geen CPU-tijd
      gebruiken.

      Deze eis was lastig te implementeren omdat een timer ook tijd gebruikt en
      daardoor niet precies over $x$ tijdseenheden kan stoppen. Omdat de context
      switches een rol kunnen spelen bij een hoge load-factor van het systeem,
      is er voor gekozen om een dynamische timer te maken. Dit houdt in dat elke
      keer dat de deadlock-detectie wordt uitgevoerd de timer wordt bijgewerkt.
      Ondanks dat het updaten van de timer enkele CPU cycles kost, levert dit
      wel een betrouwbaarder resultaat op.
      
      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray},xrightmargin=-.9in]
struct timeval wct;
gettimeofday(&wct, NULL);
double start_time = wct.tv_sec + wct.tv_usec / 1e6;

do {
    usleep(10000);
    
    // Deadlock detection
    int p;
    for(p = 0; p < philos && (stats[p].locked == 1 
                              || stats[p].locked == 2); p++);

    if( p == philos ) {
        puts("Host: deadlock occured.");
        diner_finished = 1;

        break;
    }

    // Update timer
    gettimeofday(&wct, NULL);
} while(wct.tv_sec+wct.tv_usec / 1e6 - start_time < TIME_LIMIT);
      \end{lstlisting}

      Gedurende de ontwikkeling van deze simulator is veelvoudig gebruikt
      gemaakt van het commando \texttt{time} om de runtime van het programma te
      meten, buiten het programma om. De simulator benadert de 10 seconden (voor
      zo ver dat mogelijk was om met \texttt{time} te meten) zeer goed. Zo kan
      het zijn dat er $10.002$ wordt genoteerd, waarbij de 2 millisecondes te
      verklaren zijn door het opruimen/aanmaken van de gebruikte threads en
      mutexes. Het aanmaken/opruimen van deze benodigdheden wordt namelijk niet
      meegerekend met de ge\"eiste 10 seconden grens van het diner.

      Op het moment dat een deadlock (alle filosofen wachten op een linker- dan
      wel rechtervork) wordt waargenomen door de host, is het over met de pret.
      De host stopt het diner en laat elke filosoof betalen voor het aantal
      hapjes dat gegeten is of het aantal keren dat een vork is gepakt. Uit de
      opdracht is niet op te maken of de host na een deadlock het diner direct
      laat be\"eindigen of dat hij er voor kiest om iedereen eerst te laten
      betalen en dan weg te sturen. Er is voor het tweede gekozen, omdat het
      niet vanzelfsprekend is dat er na een ruzie tussen de klanten iedereen
      gratis naar huis kan.

\item De host signaleert alle filosofen na 10 seconden dat het feest over is en
      dat ze moeten gaan afrekenen. Hij voert een reeks
      \texttt{pthread\_join()}s uit voor de simulatie-threads.

      De oplossing om alle filosofen na 10 seconden niet meer te laten eten, is
      in de vorige eis besproken. Ook het betalen van de filosofen is in
      voorgaande eis besproken. De reeks \texttt{pthread\_join()}s worden als
      volgt uitgevoerd:

      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray},xrightmargin=-.9in]
for(int i = 0; i < philos; i++) {
    // Reclaim all cutlery (mutexes) in reverse order.
    for(int p = philos; p >= i; p--)
        pthread_mutex_unlock(&forks[p]);
    
    // Wait on the other philosophers.
    if( (rc = pthread_join(threads[i], &status)) ) {
        fprintf(stderr, 
                "return code from pthread_join() is %d\n", rc);
        exit(EXIT_FAILURE);
    }

    printf("Philo #%d ate %d meal(s) and grabbed %d fork(s).\n",
           i, stats[i].meals, stats[i].forks);
    
    pthread_mutex_destroy(&forks[i]);
}
      \end{lstlisting}

      De vorken worden afgepakt van de filosofen, de filosofen verlaten het
      feest en de vorken worden netjes ``opgeruimd''.

\end{enumerate}

\subsection{De filosofen van het diner}

Elke filosoof voert de volgende acties uit:

\begin{enumerate}
\item Zij trekt een nummertje, waarmee de plaats aan tafel wordt bepaald en dus
      ook de nummers van de te gebruiken eetstokjes (nb een apart af te schermen
      kritieke sectie).

      In de eisen van de host van het diner is genoemd waarom er geen nummertje
      wordt getrokken. Als dit wel een belangrijk onderdeel was van de opdracht,
      dan zou het als volgt zijn opgelost:

      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
pthread_mutex_lock(&seat_lock);
stats->seat = get_seat();
pthread_mutex_unlock(&seat_lock);
      \end{lstlisting}

      In dit geval is de \texttt{seat} hetzelfde als \texttt{id} van de
      \texttt{diner\_stats} (\texttt{seat} is dit voorbeeld enkel een betere
      benaming). De mutex voorkomt dat er tegelijkertijd een nummer kan worden
      getrokken. Door het trekken van een nummer door de host te laten doen,
      verdwijnt een kritieke sectie (omdat het zonder race conditions kan worden
      gedaan door de host). Merk op dat het aantal locks zo laag mogelijk dient
      te zijn, om het programma goed te kunnen onderhouden (meerdere locks maken
      het programma immers onnodig complex).

\item Zij meldt de volgende informatie (naar stdout, via printf; dit is weer
      een aparte kritieke sectie): stoelnummer, soort gedrag.
    
      Het is onduidelijk waarom dit een kritieke sectie is. Data die dient te
      worden geprint, wordt niet zowel gelezen, dan wel geschreven door de andere
      threads en heeft dus geen mutex nodig. Mocht het wel een eis van de
      opdracht zijn dan zou het op de volgende manier zijn opgelost:
      
      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
pthread_mutex_lock(&introduction_lock);
printf("Hey, my seat is %d and i'm %s\n",
       stats->id, stats->behaviour);
pthread_mutex_unlock(&introduction_lock);
      \end{lstlisting}

\item Zij probeert vervolgens twee eetstokjes te pakken (afhankelijk van
      het gekozen gedrag kan dat mislukken, zie onder punt 4). 
\item Als het gelukt is die twee stokjes te pakken gaat ze eten en legt
      vervolgens de twee stokjes weer netjes terug.  Als er geen signaal is dat
      er nu moet worden afgerekend, probeert ze weer opnieuw twee eetstokjes te
      pakken (terug naar stap 3).

      \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
// Leg twee stokjes neer
pthread_mutex_unlock(&forks[f]);
pthread_mutex_unlock(&forks[(f+1) % philos]);

// En neem weer twee stokjes aan
pthread_mutex_lock(&forks[f]);
pthread_mutex_lock(&forks[(f+1) % philos]);
      \end{lstlisting}

      Het signaal wordt gecontroleerd doordat er eerst een loop condition wordt
      controleert, voordat de stokjes weer worden opgepakt door de filosoof.

\item Afrekenen bestaat er uit te melden hoe vaak deze filosoof heeft geprobeerd
      stokjes en pakken, en hoe vaak ze feitelijk heeft gegeten. Deze eis is bij
      de eisen van de host besproken.
\item Na het afrekenen verlaat ze de zaak middels een \texttt{pthread\_exit}.

\end{enumerate}

\subsection{Verschillend gedrag}

Er zijn meerdere soorten gedrag voor de filosofen geimplementeerd. De
gedragsvormen worden letterlijk in de opdracht vermeld en daarom niet verder in
dit verslag behandeld. Om \emph{random behaviour} te implementeren is er gebruik
gemaakt van de function \texttt{rand()} en dan wordt er een pseudo-random keuze
gemaakt uit de andere vier soorten gedragsvormen. Daarnaast is er gebruik
gemaakt van function pointers om de gedragsvormen eenvoudig aan te kunnen
roepen. Dit maakte het makkelijk om de gedragsvorm \emph{random behaviour} toe
te voegen.

\pagebreak

\section{Analyse van de gedragsvormen}

\begin{verbatim}
Host: inviting philo #0
Host: inviting philo #1
P #0: hello everybody, i'm optimistic!
Host: inviting philo #2
P #1: hello everybody, i'm shy!
Host: inviting philo #3
P #2: hello everybody, i'm shy!
Host: inviting philo #4
P #3: hello everybody, i'm right!
P #4: hello everybody, i'm right!
Host: invitations done.
Host: deadlock occured.
P #0: let's eating!
P #1: let's eating!
P #2: let's eating!
P #4: let's eating!
P #3: let's eating!
Host: diner is finished.
P #0: i'm leaving, after paying the bill.
P #1: i'm leaving, after paying the bill.
P #4: i'm leaving, after paying the bill.
Philo #0 ate 0 meal(s) and grabbed 12677 fork(s).
P #2: i'm leaving, after paying the bill.
P #3: i'm leaving, after paying the bill.
Philo #1 ate 46281 meal(s) and grabbed 47562 fork(s).
Philo #2 ate 41127 meal(s) and grabbed 43236 fork(s).
Philo #3 ate 38098 meal(s) and grabbed 76198 fork(s).
Philo #4 ate 8555 meal(s) and grabbed 17112 fork(s).
Host: philosophers are done.
\end{verbatim}

In bovenstaande output treedt ``vrijwel direct'' een deadlock op. 

\begin{verbatim}
Host: inviting philo #0
Host: inviting philo #1
P #0: hello everybody, i'm random!
Host: inviting philo #2
P #1: hello everybody, i'm optimistic!
Host: inviting philo #3
P #2: hello everybody, i'm right!
Host: inviting philo #4
P #3: hello everybody, i'm left!
P #4: hello everybody, i'm optimistic!
Host: invitations done.
P #4: let's eating!
P #1: let's eating!
P #0: let's eating!
P #2: let's eating!
P #3: let's eating!
Host: diner is finished.
P #4: i'm leaving, after paying the bill.
P #1: i'm leaving, after paying the bill.
P #2: i'm leaving, after paying the bill.
P #3: i'm leaving, after paying the bill.
P #0: i'm leaving, after paying the bill.
Philo #0 ate 16930743 meal(s) and grabbed 21233771 fork(s).
Philo #1 ate 15252154 meal(s) and grabbed 963192 fork(s).
Philo #2 ate 19851122 meal(s) and grabbed 39702244 fork(s).
Philo #3 ate 16646568 meal(s) and grabbed 33293136 fork(s).
Philo #4 ate 20589769 meal(s) and grabbed 1254557 fork(s).
Host: philosophers are done.
\end{verbatim}

\pagebreak

\begin{appendices}

\section{main.h}

\lstinputlisting[xleftmargin=-0.5in,language=c]{../main.h}

\section{main.c}

\lstinputlisting[xleftmargin=-0.5in,language=c]{../main.c}

\end{appendices}

\end{document}
