CS 208 s22 — Concurrency

1 Why Concurrency?

  • concurrent computing: when the execution of multiple computations (or processes) overlap
    • we need to correctly control access to shared resources

We've seen that the kernel can overlap the execution of processes. But user applications can also make use of concurreny. Benefits of application-level concurrency:

  • Accessing slow I/O devices
  • Interacting with humans
  • Reducing latency by deferring work
  • Servicing multiple network clients
  • Computing in parallel on a multi-core machine

2 Process-based Concurrency

iterative-echo.png

int main(int argc, char **argv)
{
    int listenfd, connfd;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;


    listenfd = Open_listenfd(argv[1]);
    while (1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
        echo(connfd); 
        Close(connfd); 
     }
     exit(0);
}

process-echo.png

#include "csapp.h"

void sigchld_handler(int sig)
{ 
    while (waitpid(-1, 0, WNOHANG) > 0)
        ;
    return;
}

int main(int argc, char **argv)
{
    int listenfd, connfd;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;

    Signal(SIGCHLD, sigchld_handler);
    listenfd = Open_listenfd(argv[1]);
    while (1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
        if (Fork() == 0) {
            Close(listenfd); /* Child closes its listening socket */
            echo(connfd);    /* Child services client */
            Close(connfd);   /* Child closes connection with client */
            exit(0);         /* Child exits */
        }
        Close(connfd); /* Parent closes connected socket (important!) */
    }
}
  • When fork creates a child process, the child gets a copy of the parent's file descriptor table.
  • This means the child has both connfd and listenfd.
  • The child has no need of the listening socket, so it should close it (it remains open in the parent).
  • Parent process must close its copy of connfd
    • Kernel keeps reference count for each socket/open file
    • After fork, the reference count for connfd is 2
    • Connection will not be closed until refcnt(connfd) = 0
  • Listening server process must reap zombie children
    • When a child process stops or terminates, the parent is sent a SIGCHLD signal
    • The default response is to ignore it
    • But this could result in these zombie child processes hanging around, taking up memory, waiting to communicate their exit status to the parent
    • So to avoid fatal memory leak, the parent should immediately call wait (or an equivalent) to reap (deallocate) the child

process-echo-accept.png

2.1 Pros and Cons of Process-based Servers

  • Pros
    • Handle multiple connections concurrently.
    • Clean sharing model.
      • descriptors (no)
      • file tables (yes)
      • global variables (no)
    • Simple and straightforward.
  • Cons
    • Additional overhead for process control.
    • Nontrivial to share data between processes. (This example too simple to demonstrate)

3 Threads

  • previously, we defined a process as an instance of a running program
  • a thread is a single execution sequence that represents the minimal unit of scheduling
    • one process may contain multiple threads

process-traditional.png

process-alternate.png

process-threads.png

  • Multiple threads can be associated with a process
    • Each thread has its own logical control flow
    • Each thread shares the same code, data, and kernel context
    • Each thread has its own stack for local variables but not protected from other threads
    • Each thread has its own thread id (TID)

3.1 Process vs Threads

  • How threads and processes are similar
    • Each has its own logical control flow
    • Each can run concurrently with others (possibly on different cores)
    • Each is context switched
  • How threads and processes are different
    • Threads share all code and data (except local stacks)
    • Processes (typically) do not
    • Threads are somewhat less expensive than processes
    • Process control (creating and reaping) twice as expensive as thread control
    • Linux numbers:
      • ~20K cycles to create and reap a process
      • ~10K cycles (or less) to create and reap a thread

3.2 Threading Models

  • POSIX Threads (pthread)
    • #include <pthread.h>, low-level interface giving fine-grained control
    • Standard interface for ~60 functions that manipulate threads from C programs
      • Creating and reaping threads
        • pthread_create()
        • pthread_join()
      • Determining your thread ID
        • pthread_self()
      • Terminating threads
        • pthread_cancel()
        • pthread_exit()
        • exit() [terminates all threads]
        • return [terminates current thread]
  • Fork-Join model (OpenMP)
    • #include <omp.h>, higher-level interface for fork-join approach
    • pthreads can do fork-join manually

fork-join.png

3.3 Threaded Echo Server

#include "csapp.h"

/* Thread routine */
void *thread(void *vargp)
{
    int connfd = *((int *)vargp);
    Pthread_detach(pthread_self()); 
    Free(vargp);                    
    echo(connfd);
    Close(connfd);
    return NULL;
}

int main(int argc, char **argv)
{
    int listenfd, *connfdp;
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;
    pthread_t tid;

    listenfd = Open_listenfd(argv[1]);
    while (1) {
        clientlen=sizeof(struct sockaddr_storage);
        connfdp = Malloc(sizeof(int)); 
        *connfdp = Accept(listenfd, (SA *) &clientaddr, &clientlen); 
        Pthread_create(&tid, NULL, thread, connfdp);
    }
    return 0;
}
  • Run thread in detached mode.
    • Runs independently of other threads
    • Reaped automatically (by kernel) when it terminates
  • Free storage allocated to hold connfd.
  • Close connfd (important!)
  • Must be careful to avoid unintended sharing
    • For example, passing pointer to main thread’s stack
      • Pthread_create(&tid, NULL, thread, (void *)&connfd)

connfd-sharing.png