├── README.md ├── cpu-api ├── Makefile ├── README.md ├── p1.c ├── p2.c ├── p3.c └── p4.c ├── cpu-sched-lottery ├── README.md └── lottery.c ├── dist-intro ├── Makefile ├── README.md ├── client.c ├── server.c ├── udp.c └── udp.h ├── file-intro └── pstack.c ├── include ├── common.h └── common_threads.h ├── intro ├── Makefile ├── README.md ├── common.h ├── common_threads.h ├── cpu.c ├── io.c ├── mem.c └── threads.c ├── threads-api ├── Makefile ├── README.md ├── thread_create.c ├── thread_create_simple_args.c └── thread_create_with_return_args.c ├── threads-bugs ├── Makefile ├── README.md ├── atomicity.c ├── atomicity_fixed.c ├── deadlock.c ├── deadlock_run.sh ├── ordering.c └── ordering_fixed.c ├── threads-cv ├── Makefile ├── README.md ├── join.c ├── join_modular.c ├── join_no_lock.c ├── join_no_state_var.c ├── join_spin.c ├── pc.c └── pc_single_cv.c ├── threads-intro ├── Makefile ├── README.md ├── t0.c └── t1.c ├── threads-locks ├── README.md └── compare-and-swap.c ├── threads-sema ├── Makefile ├── README.md ├── binary.c ├── dining_philosophers_deadlock.c ├── dining_philosophers_deadlock_print.c ├── dining_philosophers_no_deadlock.c ├── dining_philosophers_no_deadlock_print.c ├── join.c ├── producer_consumer_works.c ├── rwlock.c ├── throttle.c ├── zemaphore.c └── zemaphore.h └── vm-intro ├── Makefile ├── README.md └── va.c /README.md: -------------------------------------------------------------------------------- 1 | # ostep-code 2 | Code from various chapters in OSTEP (http://www.ostep.org) 3 | 4 | * [Introduction](intro) 5 | 6 | ## Virtualization 7 | 8 | CPU Virtualization Chapters: 9 | * Processes 10 | * [Process API](cpu-api) 11 | * Direct Execution 12 | * CPU Scheduling 13 | * Multi-level Feedback 14 | * [Lottery Scheduling](cpu-sched-lottery) 15 | * Multi-CPU Scheduling 16 | 17 | Memory Virtualization Chapters: 18 | * [Address Spaces](vm-intro) 19 | * Memory API 20 | * Address Translation 21 | * Segmentation 22 | * Free Space Management 23 | * Introduction to Paging 24 | * Translation Lookaside Buffers 25 | * Advanced Page Tables 26 | * Swapping: Mechanisms 27 | * Swapping: Policies 28 | * Complete VM Systems 29 | 30 | ## Concurrency 31 | 32 | Concurrency Chapters: 33 | * [Concurrency and Threads](threads-intro) 34 | * [Threads API](threads-api) 35 | * [Locks](threads-locks) 36 | * Locked Data Structures 37 | * [Condition Variables](threads-cv) 38 | * [Semaphores](threads-sema) 39 | * [Concurrency Bugs](threads-bugs) 40 | * Event-based Concurrency 41 | 42 | ## Persistence 43 | 44 | Persistence Chapters: 45 | * I/O Devices 46 | * Hard Disk Drives 47 | * Redundant Disk Arrays (RAID) 48 | * Files and Directories 49 | * File System Implementation 50 | * Fast File System (FFS) 51 | * FSCK and Journaling 52 | * Log-structured File System (LFS) 53 | * Flash-based SSDs 54 | * Data Integrity and Protection 55 | * [Distributed Systems](dist-intro) 56 | * Network File System (NFS) 57 | * Andrew File System (AFS) 58 | 59 | -------------------------------------------------------------------------------- /cpu-api/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: p1 p2 p3 p4 3 | 4 | clean: 5 | rm -f p1 p2 p3 p4 6 | 7 | p1: p1.c 8 | gcc -o p1 p1.c -Wall 9 | 10 | p2: p2.c 11 | gcc -o p2 p2.c -Wall 12 | 13 | p3: p3.c 14 | gcc -o p3 p3.c -Wall 15 | 16 | p4: p4.c 17 | gcc -o p4 p4.c -Wall 18 | 19 | -------------------------------------------------------------------------------- /cpu-api/README.md: -------------------------------------------------------------------------------- 1 | ## CPU API 2 | 3 | Code from OSTEP chapter [Interlude: Process API](http://pages.cs.wisc.edu/~remzi/OSTEP/cpu-api.pdf). 4 | 5 | To compile, just type: 6 | ``` 7 | prompt> make 8 | ``` 9 | 10 | See the highly primitive `Makefile` for details. 11 | 12 | Then run `p1`, `p2`, `p3`, or `p4`, as need be. Examples: 13 | 14 | ``` 15 | prompt> ./p1 16 | ``` 17 | 18 | ``` 19 | prompt> ./p2 20 | ``` 21 | 22 | ``` 23 | prompt> ./p3 24 | ``` 25 | 26 | ``` 27 | prompt> ./p4 28 | ``` 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /cpu-api/p1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | main(int argc, char *argv[]) 7 | { 8 | printf("hello world (pid:%d)\n", (int) getpid()); 9 | int rc = fork(); 10 | if (rc < 0) { 11 | // fork failed; exit 12 | fprintf(stderr, "fork failed\n"); 13 | exit(1); 14 | } else if (rc == 0) { 15 | // child (new process) 16 | printf("hello, I am child (pid:%d)\n", (int) getpid()); 17 | } else { 18 | // parent goes down this path (original process) 19 | printf("hello, I am parent of %d (pid:%d)\n", 20 | rc, (int) getpid()); 21 | } 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /cpu-api/p2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int 7 | main(int argc, char *argv[]) 8 | { 9 | printf("hello world (pid:%d)\n", (int) getpid()); 10 | int rc = fork(); 11 | if (rc < 0) { 12 | // fork failed; exit 13 | fprintf(stderr, "fork failed\n"); 14 | exit(1); 15 | } else if (rc == 0) { 16 | // child (new process) 17 | printf("hello, I am child (pid:%d)\n", (int) getpid()); 18 | sleep(1); 19 | } else { 20 | // parent goes down this path (original process) 21 | int wc = wait(NULL); 22 | printf("hello, I am parent of %d (wc:%d) (pid:%d)\n", 23 | rc, wc, (int) getpid()); 24 | } 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /cpu-api/p3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main(int argc, char *argv[]) 9 | { 10 | printf("hello world (pid:%d)\n", (int) getpid()); 11 | int rc = fork(); 12 | if (rc < 0) { 13 | // fork failed; exit 14 | fprintf(stderr, "fork failed\n"); 15 | exit(1); 16 | } else if (rc == 0) { 17 | // child (new process) 18 | printf("hello, I am child (pid:%d)\n", (int) getpid()); 19 | char *myargs[3]; 20 | myargs[0] = strdup("wc"); // program: "wc" (word count) 21 | myargs[1] = strdup("p3.c"); // argument: file to count 22 | myargs[2] = NULL; // marks end of array 23 | execvp(myargs[0], myargs); // runs word count 24 | printf("this shouldn't print out"); 25 | } else { 26 | // parent goes down this path (original process) 27 | int wc = wait(NULL); 28 | printf("hello, I am parent of %d (wc:%d) (pid:%d)\n", 29 | rc, wc, (int) getpid()); 30 | } 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /cpu-api/p4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int 10 | main(int argc, char *argv[]) 11 | { 12 | int rc = fork(); 13 | if (rc < 0) { 14 | // fork failed; exit 15 | fprintf(stderr, "fork failed\n"); 16 | exit(1); 17 | } else if (rc == 0) { 18 | // child: redirect standard output to a file 19 | close(STDOUT_FILENO); 20 | open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU); 21 | 22 | // now exec "wc"... 23 | char *myargs[3]; 24 | myargs[0] = strdup("wc"); // program: "wc" (word count) 25 | myargs[1] = strdup("p4.c"); // argument: file to count 26 | myargs[2] = NULL; // marks end of array 27 | execvp(myargs[0], myargs); // runs word count 28 | } else { 29 | // parent goes down this path (original process) 30 | int wc = wait(NULL); 31 | assert(wc >= 0); 32 | } 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /cpu-sched-lottery/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Proportional Share Scheduling 3 | 4 | Code from OSTEP chapter [Scheduling: Proportional Share](http://pages.cs.wisc.edu/~remzi/OSTEP/cpu-sched-lottery.pdf). 5 | 6 | Compile with: 7 | 8 | ``` 9 | prompt> gcc -o lottery lottery.c -Wall 10 | ``` 11 | 12 | Run like this: 13 | 14 | ``` 15 | ./lottery 1 100 16 | ``` 17 | 18 | which uses random seed '1' to run a little lottery 100 different times. 19 | 20 | Read the source code for details. 21 | 22 | -------------------------------------------------------------------------------- /cpu-sched-lottery/lottery.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // global ticket count 7 | int gtickets = 0; 8 | 9 | struct node_t { 10 | int tickets; 11 | struct node_t *next; 12 | }; 13 | 14 | struct node_t *head = NULL; 15 | 16 | void insert(int tickets) { 17 | struct node_t *tmp = malloc(sizeof(struct node_t)); 18 | assert(tmp != NULL); 19 | tmp->tickets = tickets; 20 | tmp->next = head; 21 | head = tmp; 22 | gtickets += tickets; 23 | } 24 | 25 | void print_list() { 26 | struct node_t *curr = head; 27 | printf("List: "); 28 | while (curr) { 29 | printf("[%d] ", curr->tickets); 30 | curr = curr->next; 31 | } 32 | printf("\n"); 33 | } 34 | 35 | int 36 | main(int argc, char *argv[]) 37 | { 38 | if (argc != 3) { 39 | fprintf(stderr, "usage: lottery \n"); 40 | exit(1); 41 | } 42 | int seed = atoi(argv[1]); 43 | int loops = atoi(argv[2]); 44 | srandom(seed); 45 | 46 | // populate list with some number of jobs, each 47 | // with some number of tickets 48 | insert(50); 49 | insert(100); 50 | insert(25); 51 | 52 | print_list(); 53 | 54 | int i; 55 | for (i = 0; i < loops; i++) { 56 | int counter = 0; 57 | int winner = random() % gtickets; // get winner 58 | struct node_t *current = head; 59 | 60 | // loop until the sum of ticket values is > the winner 61 | while (current) { 62 | counter = counter + current->tickets; 63 | if (counter > winner) 64 | break; // found the winner 65 | current = current->next; 66 | } 67 | // current is the winner: schedule it... 68 | print_list(); 69 | printf("winner: %d %d\n\n", winner, current->tickets); 70 | 71 | } 72 | return 0; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /dist-intro/Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -Wall -Werror 3 | 4 | SRCS := client.c \ 5 | server.c 6 | 7 | OBJS := ${SRCS:c=o} 8 | PROGS := ${SRCS:.c=} 9 | 10 | .PHONY: all 11 | all: ${PROGS} 12 | 13 | ${PROGS} : % : %.o Makefile 14 | ${CC} $< -o $@ udp.c 15 | 16 | clean: 17 | rm -f ${PROGS} ${OBJS} 18 | 19 | %.o: %.c Makefile 20 | ${CC} ${CFLAGS} -c $< 21 | -------------------------------------------------------------------------------- /dist-intro/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Distributed Systems: Introduction 3 | 4 | A simple UDP client and server: 5 | - `client.c`: example client code, sends a message to the server and waits for a reply 6 | - `server.c`: example server code, waits for messages indefinitely and replies 7 | 8 | Both use `udp.c` as a simple UDP communication library. 9 | 10 | The `Makefile` builds `client` and `server` executables. Type `make` to do this. 11 | 12 | To run: type `server &` to run the server in the background; then type `client` to 13 | run the client. You will likely then want to kill the server if you are done. 14 | 15 | If you want to run these on different machines, you'll have to change the client 16 | to send messages to the machine the server is running upon, instead of `localhost`. 17 | 18 | -------------------------------------------------------------------------------- /dist-intro/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "udp.h" 3 | 4 | #define BUFFER_SIZE (1000) 5 | 6 | // client code 7 | int main(int argc, char *argv[]) { 8 | struct sockaddr_in addrSnd, addrRcv; 9 | 10 | int sd = UDP_Open(20000); 11 | int rc = UDP_FillSockAddr(&addrSnd, "localhost", 10000); 12 | 13 | char message[BUFFER_SIZE]; 14 | sprintf(message, "hello world"); 15 | 16 | printf("client:: send message [%s]\n", message); 17 | rc = UDP_Write(sd, &addrSnd, message, BUFFER_SIZE); 18 | if (rc < 0) { 19 | printf("client:: failed to send\n"); 20 | exit(1); 21 | } 22 | 23 | printf("client:: wait for reply...\n"); 24 | rc = UDP_Read(sd, &addrRcv, message, BUFFER_SIZE); 25 | printf("client:: got reply [size:%d contents:(%s)\n", rc, message); 26 | return 0; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /dist-intro/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "udp.h" 3 | 4 | #define BUFFER_SIZE (1000) 5 | 6 | // server code 7 | int main(int argc, char *argv[]) { 8 | int sd = UDP_Open(10000); 9 | assert(sd > -1); 10 | while (1) { 11 | struct sockaddr_in addr; 12 | char message[BUFFER_SIZE]; 13 | printf("server:: waiting...\n"); 14 | int rc = UDP_Read(sd, &addr, message, BUFFER_SIZE); 15 | printf("server:: read message [size:%d contents:(%s)]\n", rc, message); 16 | if (rc > 0) { 17 | char reply[BUFFER_SIZE]; 18 | sprintf(reply, "goodbye world"); 19 | rc = UDP_Write(sd, &addr, reply, BUFFER_SIZE); 20 | printf("server:: reply\n"); 21 | } 22 | } 23 | return 0; 24 | } 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /dist-intro/udp.c: -------------------------------------------------------------------------------- 1 | #include "udp.h" 2 | 3 | // create a socket and bind it to a port on the current machine 4 | // used to listen for incoming packets 5 | int UDP_Open(int port) { 6 | int fd; 7 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 8 | perror("socket"); 9 | return 0; 10 | } 11 | 12 | // set up the bind 13 | struct sockaddr_in my_addr; 14 | bzero(&my_addr, sizeof(my_addr)); 15 | 16 | my_addr.sin_family = AF_INET; 17 | my_addr.sin_port = htons(port); 18 | my_addr.sin_addr.s_addr = INADDR_ANY; 19 | 20 | if (bind(fd, (struct sockaddr *) &my_addr, sizeof(my_addr)) == -1) { 21 | perror("bind"); 22 | close(fd); 23 | return -1; 24 | } 25 | 26 | return fd; 27 | } 28 | 29 | // fill sockaddr_in struct with proper goodies 30 | int UDP_FillSockAddr(struct sockaddr_in *addr, char *hostname, int port) { 31 | bzero(addr, sizeof(struct sockaddr_in)); 32 | if (hostname == NULL) { 33 | return 0; // it's OK just to clear the address 34 | } 35 | 36 | addr->sin_family = AF_INET; // host byte order 37 | addr->sin_port = htons(port); // short, network byte order 38 | 39 | struct in_addr *in_addr; 40 | struct hostent *host_entry; 41 | if ((host_entry = gethostbyname(hostname)) == NULL) { 42 | perror("gethostbyname"); 43 | return -1; 44 | } 45 | in_addr = (struct in_addr *) host_entry->h_addr; 46 | addr->sin_addr = *in_addr; 47 | 48 | return 0; 49 | } 50 | 51 | int UDP_Write(int fd, struct sockaddr_in *addr, char *buffer, int n) { 52 | int addr_len = sizeof(struct sockaddr_in); 53 | int rc = sendto(fd, buffer, n, 0, (struct sockaddr *) addr, addr_len); 54 | return rc; 55 | } 56 | 57 | int UDP_Read(int fd, struct sockaddr_in *addr, char *buffer, int n) { 58 | int len = sizeof(struct sockaddr_in); 59 | int rc = recvfrom(fd, buffer, n, 0, (struct sockaddr *) addr, (socklen_t *) &len); 60 | // assert(len == sizeof(struct sockaddr_in)); 61 | return rc; 62 | } 63 | 64 | int UDP_Close(int fd) { 65 | return close(fd); 66 | } 67 | 68 | -------------------------------------------------------------------------------- /dist-intro/udp.h: -------------------------------------------------------------------------------- 1 | #ifndef __UDP_h__ 2 | #define __UDP_h__ 3 | 4 | // 5 | // includes 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | // 27 | // prototypes 28 | // 29 | 30 | int UDP_Open(int port); 31 | int UDP_Close(int fd); 32 | 33 | int UDP_Read(int fd, struct sockaddr_in *addr, char *buffer, int n); 34 | int UDP_Write(int fd, struct sockaddr_in *addr, char *buffer, int n); 35 | 36 | int UDP_FillSockAddr(struct sockaddr_in *addr, char *hostName, int port); 37 | 38 | #endif // __UDP_h__ 39 | 40 | -------------------------------------------------------------------------------- /file-intro/pstack.c: -------------------------------------------------------------------------------- 1 | // Persistent stack example of how mmap() naturally creates a software abstraction of persistent memory. 2 | // Author: Terence Kelly 3 | // tpkelly @ { acm.org, cs.princeton.edu, eecs.umich.edu } 4 | 5 | // Note: Use 'truncate' to make the initial (empty) backing file, 6 | // whose size should be a multiple of the system page size: 7 | // 8 | // prompt> getconf PAGESIZE 9 | // 4096 10 | // prompt> truncate -s 4096 ps.img 11 | // 12 | // makes a 4096-byte empty file. The first sizeof(size_t) bytes will 13 | // be interprted as the number of items on the stack; the remainder 14 | // of the file will contain the integers contained in the stack. 15 | // Now you can run the program: 16 | // 17 | // prompt> ./pstack 7 13 47 pop 18 | // 47 19 | // prompt> ./pstack pop pop 99 20 | // 13 21 | // 7 22 | // prompt> ./pstack pop 23 | // 99 24 | // 25 | // Notice that items push'd in one invocation of the program persist 26 | // until the next invocation: The stack is persistent. 27 | // 28 | // You can use hexdump to examine the contents of the backing file: 29 | // 30 | // prompt> hexdump ps.img 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | typedef struct { 43 | size_t n; 44 | int stack[]; // zero-length per C99 45 | } pstack_t; 46 | 47 | int main(int argc, char *argv[]) { 48 | int fd, rc; 49 | struct stat s; 50 | size_t file_size; 51 | pstack_t *p; 52 | 53 | fd = open("ps.img", O_RDWR); 54 | assert(fd > -1); 55 | rc = fstat(fd, &s); 56 | assert(rc == 0); 57 | 58 | file_size = (size_t) s.st_size; 59 | assert(file_size >= sizeof(pstack_t) && file_size % sizeof(int) == 0); 60 | 61 | p = (pstack_t *) mmap(NULL, file_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 62 | assert(p != MAP_FAILED); 63 | 64 | for (int i = 1; i < argc; i++) 65 | if (strcmp(argv[i], "pop") == 0) { 66 | if (p->n > 0) // stack not empty 67 | printf("%d\n", p->stack[--p->n]); 68 | } else { // push 69 | if (sizeof(pstack_t) + (1 + p->n) * sizeof(int) <= file_size) // stack not full 70 | p->stack[p->n++] = atoi(argv[i]); 71 | } 72 | (void) close(fd); 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __common_h__ 2 | #define __common_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | double GetTime() { 9 | struct timeval t; 10 | int rc = gettimeofday(&t, NULL); 11 | assert(rc == 0); 12 | return (double) t.tv_sec + (double) t.tv_usec/1e6; 13 | } 14 | 15 | void Spin(int howlong) { 16 | double t = GetTime(); 17 | while ((GetTime() - t) < (double) howlong) 18 | ; // do nothing in loop 19 | } 20 | 21 | #endif // __common_h__ 22 | -------------------------------------------------------------------------------- /include/common_threads.h: -------------------------------------------------------------------------------- 1 | #ifndef __common_threads_h__ 2 | #define __common_threads_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __linux__ 9 | #include 10 | #endif 11 | 12 | #define Pthread_create(thread, attr, start_routine, arg) assert(pthread_create(thread, attr, start_routine, arg) == 0); 13 | #define Pthread_join(thread, value_ptr) assert(pthread_join(thread, value_ptr) == 0); 14 | 15 | #define Pthread_mutex_lock(m) assert(pthread_mutex_lock(m) == 0); 16 | #define Pthread_mutex_unlock(m) assert(pthread_mutex_unlock(m) == 0); 17 | #define Pthread_cond_signal(cond) assert(pthread_cond_signal(cond) == 0); 18 | #define Pthread_cond_wait(cond, mutex) assert(pthread_cond_wait(cond, mutex) == 0); 19 | 20 | #define Mutex_init(m) assert(pthread_mutex_init(m, NULL) == 0); 21 | #define Mutex_lock(m) assert(pthread_mutex_lock(m) == 0); 22 | #define Mutex_unlock(m) assert(pthread_mutex_unlock(m) == 0); 23 | #define Cond_init(cond) assert(pthread_cond_init(cond, NULL) == 0); 24 | #define Cond_signal(cond) assert(pthread_cond_signal(cond) == 0); 25 | #define Cond_wait(cond, mutex) assert(pthread_cond_wait(cond, mutex) == 0); 26 | 27 | #ifdef __linux__ 28 | #define Sem_init(sem, value) assert(sem_init(sem, 0, value) == 0); 29 | #define Sem_wait(sem) assert(sem_wait(sem) == 0); 30 | #define Sem_post(sem) assert(sem_post(sem) == 0); 31 | #endif // __linux__ 32 | 33 | #endif // __common_threads_h__ 34 | -------------------------------------------------------------------------------- /intro/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: cpu mem threads io 3 | 4 | clean: 5 | rm -f cpu mem threads io 6 | 7 | cpu: cpu.c common.h 8 | gcc -o cpu cpu.c -Wall 9 | 10 | mem: mem.c common.h 11 | gcc -o mem mem.c -Wall 12 | 13 | threads: threads.c common.h common_threads.h 14 | gcc -o threads threads.c -Wall -pthread 15 | 16 | io: io.c common.h 17 | gcc -o io io.c -Wall 18 | 19 | -------------------------------------------------------------------------------- /intro/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Code from OSTEP chapter [Introduction](http://pages.cs.wisc.edu/~remzi/OSTEP/intro.pdf). 4 | 5 | To compile, just type: 6 | ``` 7 | prompt> make 8 | ``` 9 | 10 | See the highly primitive `Makefile` for details. 11 | 12 | Then, run them! Examples: 13 | 14 | ``` 15 | prompt> ./cpu A 16 | ``` 17 | 18 | ``` 19 | prompt> ./mem 1 20 | ``` 21 | 22 | ``` 23 | prompt> ./threads 10000 24 | ``` 25 | 26 | ``` 27 | prompt> ./io 28 | ``` 29 | 30 | 31 | ## Details 32 | 33 | One issue with mem.c is that address space randomization is usually on by 34 | default. To turn it off: 35 | 36 | ### macOS 37 | From [stackoverflow](http://stackoverflow.com/questions/23897963/documented-way-to-disable-aslr-on-os-x) 38 | 39 | Just compile/link as follows: 40 | gcc -o mem mem.c -Wall -Wl,-no_pie 41 | 42 | ### Linux 43 | 44 | From Giovanni Lagorio: 45 | 46 | Under Linux you can disable ASLR, without using a debugger, in (at least) two ways: 47 | * Use the command setarch to run a process with ASLR disabled; I typically run 48 | bash, with which I can execute examples, like this: 49 | `setarch $(uname --machine) --addr-no-randomize /bin/bash` 50 | * Writing 0 into `/proc/sys/kernel/randomize_va_space`; you need to be 51 | root to do this and this change has (a non-permanent) effect on the 52 | whole system, which is something you probably don't want. I use this 53 | one only inside VMs. 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /intro/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __common_h__ 2 | #define __common_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | double GetTime() { 9 | struct timeval t; 10 | int rc = gettimeofday(&t, NULL); 11 | assert(rc == 0); 12 | return (double) t.tv_sec + (double) t.tv_usec/1e6; 13 | } 14 | 15 | void Spin(int howlong) { 16 | double t = GetTime(); 17 | while ((GetTime() - t) < (double) howlong) 18 | ; // do nothing in loop 19 | } 20 | 21 | #endif // __common_h__ 22 | -------------------------------------------------------------------------------- /intro/common_threads.h: -------------------------------------------------------------------------------- 1 | #ifndef __common_threads_h__ 2 | #define __common_threads_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __linux__ 9 | #include 10 | #endif 11 | 12 | #define Pthread_create(thread, attr, start_routine, arg) assert(pthread_create(thread, attr, start_routine, arg) == 0); 13 | #define Pthread_join(thread, value_ptr) assert(pthread_join(thread, value_ptr) == 0); 14 | 15 | #define Pthread_mutex_lock(m) assert(pthread_mutex_lock(m) == 0); 16 | #define Pthread_mutex_unlock(m) assert(pthread_mutex_unlock(m) == 0); 17 | #define Pthread_cond_signal(cond) assert(pthread_cond_signal(cond) == 0); 18 | #define Pthread_cond_wait(cond, mutex) assert(pthread_cond_wait(cond, mutex) == 0); 19 | 20 | #define Mutex_init(m) assert(pthread_mutex_init(m, NULL) == 0); 21 | #define Mutex_lock(m) assert(pthread_mutex_lock(m) == 0); 22 | #define Mutex_unlock(m) assert(pthread_mutex_unlock(m) == 0); 23 | #define Cond_init(cond) assert(pthread_cond_init(cond, NULL) == 0); 24 | #define Cond_signal(cond) assert(pthread_cond_signal(cond) == 0); 25 | #define Cond_wait(cond, mutex) assert(pthread_cond_wait(cond, mutex) == 0); 26 | 27 | #ifdef __linux__ 28 | #define Sem_init(sem, value) assert(sem_init(sem, 0, value) == 0); 29 | #define Sem_wait(sem) assert(sem_wait(sem) == 0); 30 | #define Sem_post(sem) assert(sem_post(sem) == 0); 31 | #endif // __linux__ 32 | 33 | #endif // __common_threads_h__ 34 | -------------------------------------------------------------------------------- /intro/cpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | if (argc != 2) { 8 | fprintf(stderr, "usage: cpu \n"); 9 | exit(1); 10 | } 11 | char *str = argv[1]; 12 | 13 | while (1) { 14 | printf("%s\n", str); 15 | Spin(1); 16 | } 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /intro/io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]) { 10 | int fd = open("/tmp/file", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); 11 | assert(fd >= 0); 12 | char buffer[20]; 13 | sprintf(buffer, "hello world\n"); 14 | int rc = write(fd, buffer, strlen(buffer)); 15 | assert(rc == (strlen(buffer))); 16 | fsync(fd); 17 | close(fd); 18 | return 0; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /intro/mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | 6 | int main(int argc, char *argv[]) { 7 | if (argc != 2) { 8 | fprintf(stderr, "usage: mem \n"); 9 | exit(1); 10 | } 11 | int *p; 12 | p = malloc(sizeof(int)); 13 | assert(p != NULL); 14 | printf("(%d) addr pointed to by p: %p\n", (int) getpid(), p); 15 | *p = atoi(argv[1]); // assign value to addr stored in p 16 | while (1) { 17 | Spin(1); 18 | *p = *p + 1; 19 | printf("(%d) value of p: %d\n", getpid(), *p); 20 | } 21 | return 0; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /intro/threads.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common.h" 4 | #include "common_threads.h" 5 | 6 | volatile int counter = 0; 7 | int loops; 8 | 9 | void *worker(void *arg) { 10 | int i; 11 | for (i = 0; i < loops; i++) { 12 | counter++; 13 | } 14 | return NULL; 15 | } 16 | 17 | int main(int argc, char *argv[]) { 18 | if (argc != 2) { 19 | fprintf(stderr, "usage: threads \n"); 20 | exit(1); 21 | } 22 | loops = atoi(argv[1]); 23 | pthread_t p1, p2; 24 | printf("Initial value : %d\n", counter); 25 | Pthread_create(&p1, NULL, worker, NULL); 26 | Pthread_create(&p2, NULL, worker, NULL); 27 | Pthread_join(p1, NULL); 28 | Pthread_join(p2, NULL); 29 | printf("Final value : %d\n", counter); 30 | return 0; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /threads-api/Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -Wall -Werror -I../include 3 | 4 | SRCS := thread_create.c \ 5 | thread_create_simple_args.c \ 6 | thread_create_with_return_args.c 7 | 8 | OBJS := ${SRCS:c=o} 9 | PROGS := ${SRCS:.c=} 10 | 11 | .PHONY: all 12 | all: ${PROGS} 13 | 14 | ${PROGS} : % : %.o Makefile 15 | ${CC} $< -o $@ -pthread 16 | 17 | clean: 18 | rm -f ${PROGS} ${OBJS} 19 | 20 | %.o: %.c Makefile 21 | ${CC} ${CFLAGS} -c $< 22 | -------------------------------------------------------------------------------- /threads-api/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Threads API 3 | 4 | Some simple examples of how to use POSIX thread APIs. 5 | 6 | Relevant files: 7 | - `thread_create.c`: A simple thread creation program, with args passed to the 8 | thread. 9 | - `thread_create_with_return_args.c`: Return values from the thread to the 10 | parent. 11 | - `thread_create_simple_args.c`: Simpler argument/return value passing for the 12 | lazy. 13 | 14 | Type `make` to build; each file `foo.c` generates an executable `foo` which 15 | you can then run, e.g.: 16 | 17 | ```sh 18 | prompt> ./thread_create 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /threads-api/thread_create.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef struct { 6 | int a; 7 | int b; 8 | } myarg_t; 9 | 10 | void *mythread(void *arg) { 11 | myarg_t *args = (myarg_t *) arg; 12 | printf("%d %d\n", args->a, args->b); 13 | return NULL; 14 | } 15 | 16 | int main(int argc, char *argv[]) { 17 | pthread_t p; 18 | myarg_t args = { 10, 20 }; 19 | 20 | int rc = pthread_create(&p, NULL, mythread, &args); 21 | assert(rc == 0); 22 | (void) pthread_join(p, NULL); 23 | printf("done\n"); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /threads-api/thread_create_simple_args.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common_threads.h" 4 | 5 | void *mythread(void *arg) { 6 | long long int value = (long long int) arg; 7 | printf("%lld\n", value); 8 | return (void *) (value + 1); 9 | } 10 | 11 | int main(int argc, char *argv[]) { 12 | pthread_t p; 13 | long long int rvalue; 14 | Pthread_create(&p, NULL, mythread, (void *) 100); 15 | Pthread_join(p, (void **) &rvalue); 16 | printf("returned %lld\n", rvalue); 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /threads-api/thread_create_with_return_args.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common_threads.h" 5 | 6 | typedef struct { 7 | int a; 8 | int b; 9 | } myarg_t; 10 | 11 | typedef struct { 12 | int x; 13 | int y; 14 | } myret_t; 15 | 16 | void *mythread(void *arg) { 17 | myarg_t *args = (myarg_t *) arg; 18 | printf("args %d %d\n", args->a, args->b); 19 | myret_t *rvals = malloc(sizeof(myret_t)); 20 | assert(rvals != NULL); 21 | rvals->x = 1; 22 | rvals->y = 2; 23 | return (void *) rvals; 24 | } 25 | 26 | int main(int argc, char *argv[]) { 27 | pthread_t p; 28 | myret_t *rvals; 29 | myarg_t args = { 10, 20 }; 30 | Pthread_create(&p, NULL, mythread, &args); 31 | Pthread_join(p, (void **) &rvals); 32 | printf("returned %d %d\n", rvals->x, rvals->y); 33 | free(rvals); 34 | return 0; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /threads-bugs/Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -Wall -Werror -I../include -pthread 3 | 4 | OS := $(shell uname -s) 5 | LIBS := 6 | ifeq ($(OS),Linux) 7 | LIBS += -pthread 8 | endif 9 | 10 | SRCS := atomicity.c \ 11 | atomicity_fixed.c \ 12 | ordering.c \ 13 | ordering_fixed.c \ 14 | deadlock.c 15 | 16 | OBJS := ${SRCS:c=o} 17 | PROGS := ${SRCS:.c=} 18 | 19 | .PHONY: all 20 | all: ${PROGS} 21 | 22 | ${PROGS} : % : %.o Makefile 23 | ${CC} $< -o $@ ${LIBS} 24 | 25 | clean: 26 | rm -f ${PROGS} ${OBJS} 27 | 28 | %.o: %.c Makefile 29 | ${CC} ${CFLAGS} -c $< 30 | -------------------------------------------------------------------------------- /threads-bugs/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Common Concurrency Problems 3 | 4 | Code examples from the chapter about concurrency problems. 5 | Type `make` to build all examples. 6 | 7 | ## Atomicity Failure 8 | 9 | - `atomicity.c`: Shows how uncareful check-then-use can crash code 10 | - `atomicity_fixed.c`: Shows how to fix the problem with a lock 11 | 12 | ## Ordering Violation 13 | 14 | - `ordering.c`: Shows the ordering problem from the book chapter 15 | - `ordering_fixed.c`: Shows how to fix the problem with a condition variable 16 | 17 | ## Deadlock 18 | 19 | - `deadlock.c`: Shows simple two-cycle deadlock 20 | - `deadlock_run.sh`: Script to run the above program many times, until you hit a deadlock and are convinced deadlock can occur 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /threads-bugs/atomicity.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | typedef struct { 10 | int pid; 11 | } proc_t; 12 | 13 | typedef struct { 14 | proc_t *proc_info; 15 | } thread_info_t; 16 | 17 | proc_t p; 18 | thread_info_t th; 19 | thread_info_t *thd; 20 | 21 | void *thread1(void *arg) { 22 | printf("t1: before check\n"); 23 | if (thd->proc_info) { 24 | printf("t1: after check\n"); 25 | sleep(2); 26 | printf("t1: use!\n"); 27 | printf("%d\n", thd->proc_info->pid); 28 | } 29 | return NULL; 30 | } 31 | 32 | void *thread2(void *arg) { 33 | printf(" t2: begin\n"); 34 | sleep(1); // change to 5 to make the code "work"... 35 | printf(" t2: set to NULL\n"); 36 | thd->proc_info = NULL; 37 | return NULL; 38 | } 39 | 40 | int main(int argc, char *argv[]) { 41 | if (argc != 1) { 42 | fprintf(stderr, "usage: main\n"); 43 | exit(1); 44 | } 45 | thread_info_t t; 46 | p.pid = 100; 47 | t.proc_info = &p; 48 | thd = &t; 49 | 50 | pthread_t p1, p2; 51 | printf("main: begin\n"); 52 | Pthread_create(&p1, NULL, thread1, NULL); 53 | Pthread_create(&p2, NULL, thread2, NULL); 54 | // join waits for the threads to finish 55 | Pthread_join(p1, NULL); 56 | Pthread_join(p2, NULL); 57 | printf("main: end\n"); 58 | return 0; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /threads-bugs/atomicity_fixed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | pthread_mutex_t proc_info_lock = PTHREAD_MUTEX_INITIALIZER; 10 | 11 | typedef struct { 12 | int pid; 13 | } proc_t; 14 | 15 | typedef struct { 16 | proc_t *proc_info; 17 | } thread_info_t; 18 | 19 | proc_t p; 20 | thread_info_t th; 21 | thread_info_t *thd; 22 | 23 | void *thread1(void *arg) { 24 | printf("t1: before check\n"); 25 | Pthread_mutex_lock(&proc_info_lock); 26 | if (thd->proc_info) { 27 | printf("t1: after check\n"); 28 | sleep(2); 29 | printf("t1: use!\n"); 30 | printf("%d\n", thd->proc_info->pid); 31 | } 32 | Pthread_mutex_unlock(&proc_info_lock); 33 | return NULL; 34 | } 35 | 36 | void *thread2(void *arg) { 37 | printf(" t2: begin\n"); 38 | sleep(1); // change to 5 to make the code "work"... 39 | Pthread_mutex_lock(&proc_info_lock); 40 | printf(" t2: set to NULL\n"); 41 | thd->proc_info = NULL; 42 | Pthread_mutex_unlock(&proc_info_lock); 43 | return NULL; 44 | } 45 | 46 | int main(int argc, char *argv[]) { 47 | if (argc != 1) { 48 | fprintf(stderr, "usage: main\n"); 49 | exit(1); 50 | } 51 | thread_info_t t; 52 | p.pid = 100; 53 | t.proc_info = &p; 54 | thd = &t; 55 | 56 | pthread_t p1, p2; 57 | printf("main: begin\n"); 58 | Pthread_create(&p1, NULL, thread1, NULL); 59 | Pthread_create(&p2, NULL, thread2, NULL); 60 | // join waits for the threads to finish 61 | Pthread_join(p1, NULL); 62 | Pthread_join(p2, NULL); 63 | printf("main: end\n"); 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /threads-bugs/deadlock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | pthread_mutex_t L1 = PTHREAD_MUTEX_INITIALIZER; 10 | pthread_mutex_t L2 = PTHREAD_MUTEX_INITIALIZER; 11 | 12 | void *thread1(void *arg) { 13 | printf("t1: begin\n"); 14 | printf("t1: try to acquire L1...\n"); 15 | Pthread_mutex_lock(&L1); 16 | printf("t1: L1 acquired\n"); 17 | printf("t1: try to acquire L2...\n"); 18 | Pthread_mutex_lock(&L2); 19 | printf("t1: L2 acquired\n"); 20 | Pthread_mutex_unlock(&L1); 21 | Pthread_mutex_unlock(&L2); 22 | return NULL; 23 | } 24 | 25 | void *thread2(void *arg) { 26 | printf(" t2: begin\n"); 27 | printf(" t2: try to acquire L2...\n"); 28 | Pthread_mutex_lock(&L2); 29 | printf(" t2: L2 acquired\n"); 30 | printf(" t2: try to acquire L1...\n"); 31 | Pthread_mutex_lock(&L1); 32 | printf(" t2: L1 acquired\n"); 33 | Pthread_mutex_unlock(&L1); 34 | Pthread_mutex_unlock(&L2); 35 | 36 | return NULL; 37 | } 38 | 39 | int main(int argc, char *argv[]) { 40 | if (argc != 1) { 41 | fprintf(stderr, "usage: main\n"); 42 | exit(1); 43 | } 44 | pthread_t p1, p2; 45 | printf("main: begin\n"); 46 | Pthread_create(&p1, NULL, thread1, NULL); 47 | Pthread_create(&p2, NULL, thread2, NULL); 48 | // join waits for the threads to finish 49 | Pthread_join(p1, NULL); 50 | Pthread_join(p2, NULL); 51 | printf("main: end\n"); 52 | return 0; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /threads-bugs/deadlock_run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | while true; do 4 | ./deadlock 5 | done 6 | 7 | 8 | -------------------------------------------------------------------------------- /threads-bugs/ordering.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | #define PR_STATE_INIT (0) 10 | 11 | typedef struct { 12 | pthread_t Tid; 13 | int State; 14 | } pr_thread_t; 15 | 16 | pr_thread_t *PR_CreateThread(void *(*start_routine)(void *)) { 17 | pr_thread_t *p = malloc(sizeof(pr_thread_t)); 18 | if (p == NULL) 19 | return NULL; 20 | p->State = PR_STATE_INIT; 21 | Pthread_create(&p->Tid, NULL, start_routine, NULL); 22 | // turn the sleep off to avoid the fault, sometimes... 23 | sleep(1); 24 | return p; 25 | } 26 | 27 | void PR_WaitThread(pr_thread_t *p) { 28 | Pthread_join(p->Tid, NULL); 29 | } 30 | 31 | pr_thread_t *mThread; 32 | 33 | void *mMain(void *arg) { 34 | printf("mMain: begin\n"); 35 | int mState = mThread->State; 36 | printf("mMain: state is %d\n", mState); 37 | return NULL; 38 | } 39 | 40 | 41 | int main(int argc, char *argv[]) { 42 | printf("ordering: begin\n"); 43 | mThread = PR_CreateThread(mMain); 44 | PR_WaitThread(mThread); 45 | printf("ordering: end\n"); 46 | return 0; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /threads-bugs/ordering_fixed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | #define PR_STATE_INIT (0) 10 | 11 | typedef struct { 12 | pthread_t Tid; 13 | int State; 14 | } pr_thread_t; 15 | 16 | pr_thread_t *PR_CreateThread(void *(*start_routine)(void *)) { 17 | pr_thread_t *p = malloc(sizeof(pr_thread_t)); 18 | if (p == NULL) 19 | return NULL; 20 | p->State = PR_STATE_INIT; 21 | Pthread_create(&p->Tid, NULL, start_routine, NULL); 22 | // turn the sleep off to avoid the fault, sometimes... 23 | sleep(1); 24 | return p; 25 | } 26 | 27 | void PR_WaitThread(pr_thread_t *p) { 28 | Pthread_join(p->Tid, NULL); 29 | } 30 | 31 | pr_thread_t *mThread; 32 | 33 | pthread_mutex_t mtLock = PTHREAD_MUTEX_INITIALIZER; 34 | pthread_cond_t mtCond = PTHREAD_COND_INITIALIZER; 35 | int mtInit = 0; 36 | 37 | void *mMain(void *arg) { 38 | printf("mMain: begin\n"); 39 | 40 | // wait for thread structure to be initialized 41 | Pthread_mutex_lock(&mtLock); 42 | while (mtInit == 0) 43 | Pthread_cond_wait(&mtCond, &mtLock); 44 | Pthread_mutex_unlock(&mtLock); 45 | 46 | int mState = mThread->State; 47 | printf("mMain: state is %d\n", mState); 48 | return NULL; 49 | } 50 | 51 | 52 | int main(int argc, char *argv[]) { 53 | printf("ordering: begin\n"); 54 | mThread = PR_CreateThread(mMain); 55 | 56 | // signal: thread has been created, and mThread initialized 57 | Pthread_mutex_lock(&mtLock); 58 | mtInit = 1; 59 | Pthread_cond_signal(&mtCond); 60 | Pthread_mutex_unlock(&mtLock); 61 | 62 | PR_WaitThread(mThread); 63 | printf("ordering: end\n"); 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /threads-cv/Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -Wall -Werror -I../include -pthread 3 | 4 | OS := $(shell uname -s) 5 | LIBS := 6 | ifeq ($(OS),Linux) 7 | LIBS += -pthread 8 | endif 9 | 10 | SRCS := join.c \ 11 | join_spin.c \ 12 | join_no_lock.c \ 13 | join_no_state_var.c \ 14 | join_modular.c \ 15 | pc_single_cv.c \ 16 | pc.c 17 | 18 | OBJS := ${SRCS:c=o} 19 | PROGS := ${SRCS:.c=} 20 | 21 | .PHONY: all 22 | all: ${PROGS} 23 | 24 | ${PROGS} : % : %.o Makefile 25 | ${CC} $< -o $@ ${LIBS} 26 | 27 | clean: 28 | rm -f ${PROGS} ${OBJS} 29 | 30 | %.o: %.c Makefile 31 | ${CC} ${CFLAGS} -c $< 32 | -------------------------------------------------------------------------------- /threads-cv/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Condition Variables 3 | 4 | Code examples from condition variables chapter. Build by typing `make`; 5 | run the resulting executable to see how it works. Insert `sleep()` calls 6 | of various lengths to control timing and force bad things to happen. 7 | 8 | ## Fork/Join Problem 9 | 10 | - `join_spin.c`: Working solution but wastes CPU. 11 | - `join_no_lock.c`: What happens when you don't put a lock around the state change and signal 12 | - `join_no_state_var.c`: What happens if you don't have a state variable 13 | - `join.c`: A working solution 14 | - `join_modular.c`: A modularized version 15 | 16 | ## Producer/Consumer Problem 17 | - `pc_single_cv.c`: What happens if you only use one condition variable 18 | - `pc.c`: A working solution 19 | 20 | -------------------------------------------------------------------------------- /threads-cv/join.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "common_threads.h" 6 | 7 | pthread_cond_t c = PTHREAD_COND_INITIALIZER; 8 | pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 9 | int done = 0; 10 | 11 | void *child(void *arg) { 12 | printf("child\n"); 13 | sleep(1); 14 | Mutex_lock(&m); 15 | done = 1; 16 | Cond_signal(&c); 17 | Mutex_unlock(&m); 18 | return NULL; 19 | } 20 | int main(int argc, char *argv[]) { 21 | pthread_t p; 22 | printf("parent: begin\n"); 23 | Pthread_create(&p, NULL, child, NULL); 24 | Mutex_lock(&m); 25 | while (done == 0) 26 | Cond_wait(&c, &m); // releases lock when going to sleep 27 | Mutex_unlock(&m); 28 | printf("parent: end\n"); 29 | return 0; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /threads-cv/join_modular.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "common_threads.h" 6 | 7 | // 8 | // Simple sync "object" 9 | // 10 | 11 | typedef struct { 12 | pthread_cond_t c; 13 | pthread_mutex_t m; 14 | int done; 15 | } synchronizer_t; 16 | 17 | synchronizer_t s; 18 | 19 | void sync_init(synchronizer_t *s) { 20 | s->done = 0; 21 | Mutex_init(&s->m); 22 | Cond_init(&s->c); 23 | } 24 | 25 | void sync_signal(synchronizer_t *s) { 26 | Mutex_lock(&s->m); 27 | s->done = 1; 28 | Cond_signal(&s->c); 29 | Mutex_unlock(&s->m); 30 | } 31 | 32 | void sync_wait(synchronizer_t *s) { 33 | Mutex_lock(&s->m); 34 | while (s->done == 0) 35 | Cond_wait(&s->c, &s->m); 36 | s->done = 0; // reset for next use 37 | Mutex_unlock(&s->m); 38 | } 39 | 40 | 41 | // 42 | // Main threads 43 | // 44 | 45 | void *child(void *arg) { 46 | printf("child\n"); 47 | sleep(1); 48 | sync_signal(&s); 49 | return NULL; 50 | } 51 | int main(int argc, char *argv[]) { 52 | pthread_t p; 53 | printf("parent: begin\n"); 54 | sync_init(&s); 55 | Pthread_create(&p, NULL, child, NULL); 56 | sync_wait(&s); 57 | printf("parent: end\n"); 58 | return 0; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /threads-cv/join_no_lock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "common_threads.h" 6 | 7 | pthread_cond_t c = PTHREAD_COND_INITIALIZER; 8 | pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 9 | int done = 0; 10 | 11 | void *child(void *arg) { 12 | printf("child: begin\n"); 13 | sleep(1); 14 | done = 1; 15 | printf("child: signal\n"); 16 | Cond_signal(&c); 17 | return NULL; 18 | } 19 | int main(int argc, char *argv[]) { 20 | pthread_t p; 21 | printf("parent: begin\n"); 22 | Pthread_create(&p, NULL, child, NULL); 23 | Mutex_lock(&m); 24 | printf("parent: check condition\n"); 25 | while (done == 0) { 26 | sleep(2); 27 | printf("parent: wait to be signalled...\n"); 28 | Cond_wait(&c, &m); 29 | } 30 | Mutex_unlock(&m); 31 | printf("parent: end\n"); 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /threads-cv/join_no_state_var.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "common_threads.h" 6 | 7 | pthread_cond_t c = PTHREAD_COND_INITIALIZER; 8 | pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 9 | 10 | void *child(void *arg) { 11 | printf("child: begin\n"); 12 | Mutex_lock(&m); 13 | printf("child: signal\n"); 14 | Cond_signal(&c); 15 | Mutex_unlock(&m); 16 | return NULL; 17 | } 18 | int main(int argc, char *argv[]) { 19 | pthread_t p; 20 | printf("parent: begin\n"); 21 | Pthread_create(&p, NULL, child, NULL); 22 | sleep(2); 23 | printf("parent: wait to be signalled...\n"); 24 | Mutex_lock(&m); 25 | Cond_wait(&c, &m); 26 | Mutex_unlock(&m); 27 | printf("parent: end\n"); 28 | return 0; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /threads-cv/join_spin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "common_threads.h" 6 | 7 | volatile int done = 0; 8 | 9 | void *child(void *arg) { 10 | printf("child\n"); 11 | sleep(5); 12 | done = 1; 13 | return NULL; 14 | } 15 | 16 | int main(int argc, char *argv[]) { 17 | pthread_t p; 18 | printf("parent: begin\n"); 19 | Pthread_create(&p, NULL, child, NULL); 20 | while (done == 0) 21 | ; // spin 22 | printf("parent: end\n"); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /threads-cv/pc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "common.h" 8 | #include "common_threads.h" 9 | 10 | int max; 11 | int loops; 12 | int *buffer; 13 | 14 | int use_ptr = 0; 15 | int fill_ptr = 0; 16 | int num_full = 0; 17 | 18 | pthread_cond_t empty = PTHREAD_COND_INITIALIZER; 19 | pthread_cond_t fill = PTHREAD_COND_INITIALIZER; 20 | pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 21 | 22 | int consumers = 1; 23 | int verbose = 1; 24 | 25 | 26 | void do_fill(int value) { 27 | buffer[fill_ptr] = value; 28 | fill_ptr = (fill_ptr + 1) % max; 29 | num_full++; 30 | } 31 | 32 | int do_get() { 33 | int tmp = buffer[use_ptr]; 34 | use_ptr = (use_ptr + 1) % max; 35 | num_full--; 36 | return tmp; 37 | } 38 | 39 | void *producer(void *arg) { 40 | int i; 41 | for (i = 0; i < loops; i++) { 42 | Mutex_lock(&m); // p1 43 | while (num_full == max) // p2 44 | Cond_wait(&empty, &m); // p3 45 | do_fill(i); // p4 46 | Cond_signal(&fill); // p5 47 | Mutex_unlock(&m); // p6 48 | } 49 | 50 | // end case: put an end-of-production marker (-1) 51 | // into shared buffer, one per consumer 52 | for (i = 0; i < consumers; i++) { 53 | Mutex_lock(&m); 54 | while (num_full == max) 55 | Cond_wait(&empty, &m); 56 | do_fill(-1); 57 | Cond_signal(&fill); 58 | Mutex_unlock(&m); 59 | } 60 | 61 | return NULL; 62 | } 63 | 64 | void *consumer(void *arg) { 65 | int tmp = 0; 66 | // consumer: keep pulling data out of shared buffer 67 | // until you receive a -1 (end-of-production marker) 68 | while (tmp != -1) { 69 | Mutex_lock(&m); // c1 70 | while (num_full == 0) // c2 71 | Cond_wait(&fill, &m); // c3 72 | tmp = do_get(); // c4 73 | Cond_signal(&empty); // c5 74 | Mutex_unlock(&m); // c6 75 | } 76 | return NULL; 77 | } 78 | 79 | int 80 | main(int argc, char *argv[]) 81 | { 82 | if (argc != 4) { 83 | fprintf(stderr, "usage: %s \n", argv[0]); 84 | exit(1); 85 | } 86 | max = atoi(argv[1]); 87 | loops = atoi(argv[2]); 88 | consumers = atoi(argv[3]); 89 | 90 | buffer = (int *) malloc(max * sizeof(int)); 91 | assert(buffer != NULL); 92 | 93 | int i; 94 | for (i = 0; i < max; i++) { 95 | buffer[i] = 0; 96 | } 97 | 98 | pthread_t pid, cid[consumers]; 99 | Pthread_create(&pid, NULL, producer, NULL); 100 | for (i = 0; i < consumers; i++) { 101 | Pthread_create(&cid[i], NULL, consumer, (void *) (long long int) i); 102 | } 103 | Pthread_join(pid, NULL); 104 | for (i = 0; i < consumers; i++) { 105 | Pthread_join(cid[i], NULL); 106 | } 107 | return 0; 108 | } 109 | 110 | -------------------------------------------------------------------------------- /threads-cv/pc_single_cv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "common.h" 8 | #include "common_threads.h" 9 | 10 | int max; 11 | int loops; 12 | int *buffer; 13 | 14 | int use_ptr = 0; 15 | int fill_ptr = 0; 16 | int num_full = 0; 17 | 18 | pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 19 | pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 20 | 21 | int consumers = 1; 22 | int verbose = 1; 23 | 24 | 25 | void do_fill(int value) { 26 | buffer[fill_ptr] = value; 27 | fill_ptr = (fill_ptr + 1) % max; 28 | num_full++; 29 | } 30 | 31 | int do_get() { 32 | int tmp = buffer[use_ptr]; 33 | use_ptr = (use_ptr + 1) % max; 34 | num_full--; 35 | return tmp; 36 | } 37 | 38 | void *producer(void *arg) { 39 | int i; 40 | for (i = 0; i < loops; i++) { 41 | Mutex_lock(&m); // p1 42 | while (num_full == max) // p2 43 | Cond_wait(&cv, &m); // p3 44 | do_fill(i); // p4 45 | Cond_signal(&cv); // p5 46 | Mutex_unlock(&m); // p6 47 | } 48 | 49 | // end case: put an end-of-production marker (-1) 50 | // into shared buffer, one per consumer 51 | for (i = 0; i < consumers; i++) { 52 | Mutex_lock(&m); 53 | while (num_full == max) 54 | Cond_wait(&cv, &m); 55 | do_fill(-1); 56 | Cond_signal(&cv); 57 | Mutex_unlock(&m); 58 | } 59 | 60 | return NULL; 61 | } 62 | 63 | void *consumer(void *arg) { 64 | int tmp = 0; 65 | // consumer: keep pulling data out of shared buffer 66 | // until you receive a -1 (end-of-production marker) 67 | while (tmp != -1) { 68 | Mutex_lock(&m); // c1 69 | while (num_full == 0) // c2 70 | Cond_wait(&cv, &m); // c3 71 | tmp = do_get(); // c4 72 | Cond_signal(&cv); // c5 73 | Mutex_unlock(&m); // c6 74 | } 75 | return NULL; 76 | } 77 | 78 | int 79 | main(int argc, char *argv[]) 80 | { 81 | if (argc != 4) { 82 | fprintf(stderr, "usage: %s \n", argv[0]); 83 | exit(1); 84 | } 85 | max = atoi(argv[1]); 86 | loops = atoi(argv[2]); 87 | consumers = atoi(argv[3]); 88 | 89 | buffer = (int *) malloc(max * sizeof(int)); 90 | assert(buffer != NULL); 91 | 92 | int i; 93 | for (i = 0; i < max; i++) { 94 | buffer[i] = 0; 95 | } 96 | 97 | pthread_t pid, cid[consumers]; 98 | Pthread_create(&pid, NULL, producer, NULL); 99 | for (i = 0; i < consumers; i++) { 100 | Pthread_create(&cid[i], NULL, consumer, (void *) (long long int) i); 101 | } 102 | Pthread_join(pid, NULL); 103 | for (i = 0; i < consumers; i++) { 104 | Pthread_join(cid[i], NULL); 105 | } 106 | return 0; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /threads-intro/Makefile: -------------------------------------------------------------------------------- 1 | 2 | FLAGS = -Wall -pthread 3 | INCLUDES = ../include 4 | 5 | all: t0 t1 6 | 7 | clean: 8 | rm -f t0 t1 9 | 10 | t0: t0.c 11 | gcc -I $(INCLUDES) -o t0 t0.c $(FLAGS) 12 | 13 | t1: t1.c 14 | gcc -I $(INCLUDES) -o t1 t1.c $(FLAGS) 15 | -------------------------------------------------------------------------------- /threads-intro/README.md: -------------------------------------------------------------------------------- 1 | 2 | A simple set of programs that use threads: 3 | - `t0.c` 4 | - `t1.c` 5 | 6 | Each one depends on header files found in `../include` 7 | 8 | The Makefile is simplistic but will do. 9 | 10 | 11 | -------------------------------------------------------------------------------- /threads-intro/t0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "common_threads.h" 7 | 8 | void *mythread(void *arg) { 9 | printf("%s\n", (char *) arg); 10 | return NULL; 11 | } 12 | 13 | int main(int argc, char *argv[]) { 14 | if (argc != 1) { 15 | fprintf(stderr, "usage: main\n"); 16 | exit(1); 17 | } 18 | 19 | pthread_t p1, p2; 20 | printf("main: begin\n"); 21 | Pthread_create(&p1, NULL, mythread, "A"); 22 | Pthread_create(&p2, NULL, mythread, "B"); 23 | // join waits for the threads to finish 24 | Pthread_join(p1, NULL); 25 | Pthread_join(p2, NULL); 26 | printf("main: end\n"); 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /threads-intro/t1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "common_threads.h" 7 | 8 | int max; 9 | volatile int counter = 0; // shared global variable 10 | 11 | void *mythread(void *arg) { 12 | char *letter = arg; 13 | int i; // stack (private per thread) 14 | printf("%s: begin [addr of i: %p]\n", letter, &i); 15 | for (i = 0; i < max; i++) { 16 | counter = counter + 1; // shared: only one 17 | } 18 | printf("%s: done\n", letter); 19 | return NULL; 20 | } 21 | 22 | int main(int argc, char *argv[]) { 23 | if (argc != 2) { 24 | fprintf(stderr, "usage: main-first \n"); 25 | exit(1); 26 | } 27 | max = atoi(argv[1]); 28 | 29 | pthread_t p1, p2; 30 | printf("main: begin [counter = %d] [%x]\n", counter, 31 | (unsigned int) &counter); 32 | Pthread_create(&p1, NULL, mythread, "A"); 33 | Pthread_create(&p2, NULL, mythread, "B"); 34 | // join waits for the threads to finish 35 | Pthread_join(p1, NULL); 36 | Pthread_join(p2, NULL); 37 | printf("main: done\n [counter: %d]\n [should: %d]\n", 38 | counter, max*2); 39 | return 0; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /threads-locks/README.md: -------------------------------------------------------------------------------- 1 | 2 | A simple example of compare-and-swap shown in actual C code (which calls into 3 | assembly). 4 | 5 | The code, in entirety, is shown here: 6 | 7 | ```c 8 | #include 9 | 10 | int global = 0; 11 | 12 | char compare_and_swap(int *ptr, int old, int new) { 13 | unsigned char ret; 14 | // Note that sete sets a ’byte’ not the word 15 | __asm__ __volatile__ ( 16 | " lock\n" 17 | " cmpxchgl %2,%1\n" 18 | " sete %0\n" 19 | : "=q" (ret), "=m" (*ptr) 20 | : "r" (new), "m" (*ptr), "a" (old) 21 | : "memory"); 22 | return ret; 23 | } 24 | 25 | int main(int argc, char *argv[]) { 26 | printf("before successful cas: %d\n", global); 27 | int success = compare_and_swap(&global, 0, 100); 28 | printf("after successful cas: %d (success: %d)\n", global, success); 29 | 30 | printf("before failing cas: %d\n", global); 31 | success = compare_and_swap(&global, 0, 200); 32 | printf("after failing cas: %d (old: %d)\n", global, success); 33 | 34 | return 0; 35 | } 36 | ``` 37 | 38 | The first call to `compare_and_swap()` succeeds because the old value is 39 | correct; the second call does not because the old value is wrong. 40 | 41 | To compile and run: 42 | ```sh 43 | prompt> gcc -o compare-and-swap compare-and-swap.c -Wall 44 | prompt> ./compare-and-swap 45 | ``` 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /threads-locks/compare-and-swap.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int global = 0; 4 | 5 | char compare_and_swap(int *ptr, int old, int new) { 6 | unsigned char ret; 7 | // Note that sete sets a ’byte’ not the word 8 | __asm__ __volatile__ ( 9 | " lock\n" 10 | " cmpxchgl %2,%1\n" 11 | " sete %0\n" 12 | : "=q" (ret), "=m" (*ptr) 13 | : "r" (new), "m" (*ptr), "a" (old) 14 | : "memory"); 15 | return ret; 16 | } 17 | 18 | int main(int argc, char *argv[]) { 19 | printf("before successful cas: %d\n", global); 20 | int success = compare_and_swap(&global, 0, 100); 21 | printf("after successful cas: %d (success: %d)\n", global, success); 22 | 23 | printf("before failing cas: %d\n", global); 24 | success = compare_and_swap(&global, 0, 200); 25 | printf("after failing cas: %d (old: %d)\n", global, success); 26 | 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /threads-sema/Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -Wall -Werror -I../include -pthread 3 | 4 | OS := $(shell uname -s) 5 | LIBS := 6 | ifeq ($(OS),Linux) 7 | LIBS += -pthread 8 | endif 9 | 10 | SRCS := dining_philosophers_deadlock.c \ 11 | dining_philosophers_deadlock_print.c \ 12 | dining_philosophers_no_deadlock.c \ 13 | dining_philosophers_no_deadlock_print.c \ 14 | join.c \ 15 | binary.c \ 16 | producer_consumer_works.c \ 17 | rwlock.c \ 18 | zemaphore.c \ 19 | throttle.c 20 | 21 | OBJS := ${SRCS:c=o} 22 | PROGS := ${SRCS:.c=} 23 | 24 | .PHONY: all 25 | all: ${PROGS} 26 | 27 | ${PROGS} : % : %.o Makefile 28 | ${CC} $< -o $@ ${LIBS} 29 | 30 | clean: 31 | rm -f ${PROGS} ${OBJS} 32 | 33 | %.o: %.c Makefile 34 | ${CC} ${CFLAGS} -c $< 35 | -------------------------------------------------------------------------------- /threads-sema/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Fork/Join 3 | 4 | Simple example of the fork/join (i.e., waiting for a child) problem 5 | using semaphores, found in `join.c`. 6 | 7 | Run `make` to build the code; run `join` to test it. Fun! 8 | 9 | ```sh 10 | prompt> make 11 | prompt> ./join 12 | ``` 13 | 14 | 15 | # Binary Semaphores (Locks) 16 | 17 | Simple example of semaphores as locks (binary semaphores). 18 | Code in `binary.c`. 19 | 20 | Run `make` to build the code; run `binary` to test it. Fun! 21 | 22 | ```sh 23 | prompt> make 24 | prompt> ./binary 25 | ``` 26 | 27 | # Producer/Consumer 28 | 29 | Code for the working producer/consumer solution from the text, 30 | found in `producer_consumer.c`. 31 | 32 | Run `make` to build, and `producer_consumer` to test it. 33 | The program takes a few different arguments: 34 | - The number of buffers between the producer/consumer 35 | - The number of times a producer should produce something 36 | - The number of consumer threads 37 | 38 | ```sh 39 | prompt> make 40 | prompt> ./producer_consumer 1 1000 1 41 | ``` 42 | 43 | The output should print each produced item once, and show which 44 | consumer consumed each produced item. 45 | 46 | # Reader/Writer Locks 47 | 48 | Code in `rwlock.c`. Build via `make`, run via `rwlock`. 49 | 50 | # Dining Philosophers 51 | 52 | The dining philosophers example from the text is found herein, in a few 53 | different forms: 54 | - `dining_philosophers_deadlock.c`: code with deadlock 55 | - `dining_philosophers_deadlock_print.c`: code with deadlock, and some useful printing 56 | - `dining_philosophers_no_deadlock.c`: code without deadlock 57 | - `dining_philosophers_no_deadlock_print.c`: code without deadlock, and some useful printing 58 | 59 | Run `make` to build all of them with the highly primitive `Makefile`. 60 | 61 | 62 | # Zemaphores 63 | 64 | Code in `zemaphore.c`. We bet you can figure out the rest. This is just 65 | a small test of the Zemaphore with the fork/join problem. 66 | 67 | 68 | # Throttle 69 | 70 | Bonus code that shows how semaphores can be used to throttle how 71 | many different threads run through a certain bit of code at a time. 72 | Code in `throttle.c`. 73 | 74 | -------------------------------------------------------------------------------- /threads-sema/binary.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | #ifdef linux 10 | #include 11 | #elif __APPLE__ 12 | #include "zemaphore.h" 13 | #endif 14 | 15 | sem_t mutex; 16 | volatile int counter = 0; 17 | 18 | void *child(void *arg) { 19 | int i; 20 | for (i = 0; i < 10000000; i++) { 21 | Sem_wait(&mutex); 22 | counter++; 23 | Sem_post(&mutex); 24 | } 25 | return NULL; 26 | } 27 | 28 | int main(int argc, char *argv[]) { 29 | Sem_init(&mutex, 1); 30 | pthread_t c1, c2; 31 | Pthread_create(&c1, NULL, child, NULL); 32 | Pthread_create(&c2, NULL, child, NULL); 33 | Pthread_join(c1, NULL); 34 | Pthread_join(c2, NULL); 35 | printf("result: %d (should be 20000000)\n", counter); 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /threads-sema/dining_philosophers_deadlock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "common_threads.h" 7 | 8 | #ifdef linux 9 | #include 10 | #elif __APPLE__ 11 | #include "zemaphore.h" 12 | #endif 13 | 14 | typedef struct { 15 | int num_loops; 16 | int thread_id; 17 | } arg_t; 18 | 19 | sem_t forks[5]; 20 | 21 | int left(int p) { 22 | return p; 23 | } 24 | 25 | int right(int p) { 26 | return (p + 1) % 5; 27 | } 28 | 29 | void get_forks(int p) { 30 | Sem_wait(&forks[left(p)]); 31 | Sem_wait(&forks[right(p)]); 32 | } 33 | 34 | void put_forks(int p) { 35 | Sem_post(&forks[left(p)]); 36 | Sem_post(&forks[right(p)]); 37 | } 38 | 39 | void think() { 40 | return; 41 | } 42 | 43 | void eat() { 44 | return; 45 | } 46 | 47 | void *philosopher(void *arg) { 48 | arg_t *args = (arg_t *) arg; 49 | int p = args->thread_id; 50 | 51 | int i; 52 | for (i = 0; i < args->num_loops; i++) { 53 | think(); 54 | get_forks(p); 55 | eat(); 56 | put_forks(p); 57 | } 58 | return NULL; 59 | } 60 | 61 | int main(int argc, char *argv[]) { 62 | if (argc != 2) { 63 | fprintf(stderr, "usage: dining_philosophers_deadlock \n"); 64 | exit(1); 65 | } 66 | printf("dining: started\n"); 67 | 68 | int i; 69 | for (i = 0; i < 5; i++) 70 | Sem_init(&forks[i], 1); 71 | 72 | pthread_t p[5]; 73 | arg_t a[5]; 74 | for (i = 0; i < 5; i++) { 75 | a[i].num_loops = atoi(argv[1]); 76 | a[i].thread_id = i; 77 | Pthread_create(&p[i], NULL, philosopher, &a[i]); 78 | } 79 | 80 | for (i = 0; i < 5; i++) 81 | Pthread_join(p[i], NULL); 82 | 83 | printf("dining: finished\n"); 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /threads-sema/dining_philosophers_deadlock_print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "common_threads.h" 7 | 8 | #ifdef linux 9 | #include 10 | #elif __APPLE__ 11 | #include "zemaphore.h" 12 | #endif 13 | 14 | typedef struct { 15 | int num_loops; 16 | int thread_id; 17 | } arg_t; 18 | 19 | sem_t forks[5]; 20 | sem_t print_lock; 21 | 22 | void space(int s) { 23 | Sem_wait(&print_lock); 24 | int i; 25 | for (i = 0; i < s * 10; i++) 26 | printf(" "); 27 | } 28 | 29 | void space_end() { 30 | Sem_post(&print_lock); 31 | } 32 | 33 | int left(int p) { 34 | return p; 35 | } 36 | 37 | int right(int p) { 38 | return (p + 1) % 5; 39 | } 40 | 41 | void get_forks(int p) { 42 | space(p); printf("%d: try %d\n", p, left(p)); space_end(); 43 | Sem_wait(&forks[left(p)]); 44 | space(p); printf("%d: try %d\n", p, right(p)); space_end(); 45 | Sem_wait(&forks[right(p)]); 46 | } 47 | 48 | void put_forks(int p) { 49 | Sem_post(&forks[left(p)]); 50 | Sem_post(&forks[right(p)]); 51 | } 52 | 53 | void think() { 54 | return; 55 | } 56 | 57 | void eat() { 58 | return; 59 | } 60 | 61 | void *philosopher(void *arg) { 62 | arg_t *args = (arg_t *) arg; 63 | 64 | space(args->thread_id); printf("%d: start\n", args->thread_id); space_end(); 65 | 66 | int i; 67 | for (i = 0; i < args->num_loops; i++) { 68 | space(args->thread_id); printf("%d: think\n", args->thread_id); space_end(); 69 | think(); 70 | get_forks(args->thread_id); 71 | space(args->thread_id); printf("%d: eat\n", args->thread_id); space_end(); 72 | eat(); 73 | put_forks(args->thread_id); 74 | space(args->thread_id); printf("%d: done\n", args->thread_id); space_end(); 75 | } 76 | return NULL; 77 | } 78 | 79 | int main(int argc, char *argv[]) { 80 | if (argc != 2) { 81 | fprintf(stderr, "usage: dining_philosophers \n"); 82 | exit(1); 83 | } 84 | printf("dining: started\n"); 85 | 86 | int i; 87 | for (i = 0; i < 5; i++) 88 | Sem_init(&forks[i], 1); 89 | Sem_init(&print_lock, 1); 90 | 91 | pthread_t p[5]; 92 | arg_t a[5]; 93 | for (i = 0; i < 5; i++) { 94 | a[i].num_loops = atoi(argv[1]); 95 | a[i].thread_id = i; 96 | Pthread_create(&p[i], NULL, philosopher, &a[i]); 97 | } 98 | 99 | for (i = 0; i < 5; i++) 100 | Pthread_join(p[i], NULL); 101 | 102 | printf("dining: finished\n"); 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /threads-sema/dining_philosophers_no_deadlock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "common_threads.h" 7 | 8 | #ifdef linux 9 | #include 10 | #elif __APPLE__ 11 | #include "zemaphore.h" 12 | #endif 13 | 14 | typedef struct { 15 | int num_loops; 16 | int thread_id; 17 | } arg_t; 18 | 19 | sem_t forks[5]; 20 | 21 | int left(int p) { 22 | return p; 23 | } 24 | 25 | int right(int p) { 26 | return (p + 1) % 5; 27 | } 28 | 29 | void get_forks(int p) { 30 | if (p == 4) { 31 | Sem_wait(&forks[right(p)]); 32 | Sem_wait(&forks[left(p)]); 33 | } else { 34 | Sem_wait(&forks[left(p)]); 35 | Sem_wait(&forks[right(p)]); 36 | } 37 | } 38 | 39 | void put_forks(int p) { 40 | Sem_post(&forks[left(p)]); 41 | Sem_post(&forks[right(p)]); 42 | } 43 | 44 | void think() { 45 | return; 46 | } 47 | 48 | void eat() { 49 | return; 50 | } 51 | 52 | void *philosopher(void *arg) { 53 | arg_t *args = (arg_t *) arg; 54 | int p = args->thread_id; 55 | 56 | int i; 57 | for (i = 0; i < args->num_loops; i++) { 58 | think(); 59 | get_forks(p); 60 | eat(); 61 | put_forks(p); 62 | } 63 | return NULL; 64 | } 65 | 66 | int main(int argc, char *argv[]) { 67 | if (argc != 2) { 68 | fprintf(stderr, "usage: dining_philosophers \n"); 69 | exit(1); 70 | } 71 | printf("dining: started\n"); 72 | 73 | int i; 74 | for (i = 0; i < 5; i++) 75 | Sem_init(&forks[i], 1); 76 | 77 | pthread_t p[5]; 78 | arg_t a[5]; 79 | for (i = 0; i < 5; i++) { 80 | a[i].num_loops = atoi(argv[1]); 81 | a[i].thread_id = i; 82 | Pthread_create(&p[i], NULL, philosopher, &a[i]); 83 | } 84 | 85 | for (i = 0; i < 5; i++) 86 | Pthread_join(p[i], NULL); 87 | 88 | printf("dining: finished\n"); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /threads-sema/dining_philosophers_no_deadlock_print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common.h" 6 | #include "common_threads.h" 7 | 8 | #ifdef linux 9 | #include 10 | #elif __APPLE__ 11 | #include "zemaphore.h" 12 | #endif 13 | 14 | typedef struct { 15 | int num_loops; 16 | int thread_id; 17 | } arg_t; 18 | 19 | sem_t forks[5]; 20 | sem_t print_lock; 21 | 22 | void space(int s) { 23 | Sem_wait(&print_lock); 24 | int i; 25 | for (i = 0; i < s * 10; i++) 26 | printf(" "); 27 | } 28 | 29 | void space_end() { 30 | Sem_post(&print_lock); 31 | } 32 | 33 | int left(int p) { 34 | return p; 35 | } 36 | 37 | int right(int p) { 38 | return (p + 1) % 5; 39 | } 40 | 41 | void get_forks(int p) { 42 | if (p == 4) { 43 | space(p); printf("4 try %d\n", right(p)); space_end(); 44 | Sem_wait(&forks[right(p)]); 45 | space(p); printf("4 try %d\n", left(p)); space_end(); 46 | Sem_wait(&forks[left(p)]); 47 | } else { 48 | space(p); printf("try %d\n", left(p)); space_end(); 49 | Sem_wait(&forks[left(p)]); 50 | space(p); printf("try %d\n", right(p)); space_end(); 51 | Sem_wait(&forks[right(p)]); 52 | } 53 | } 54 | 55 | void put_forks(int p) { 56 | Sem_post(&forks[left(p)]); 57 | Sem_post(&forks[right(p)]); 58 | } 59 | 60 | void think() { 61 | return; 62 | } 63 | 64 | void eat() { 65 | return; 66 | } 67 | 68 | void *philosopher(void *arg) { 69 | arg_t *args = (arg_t *) arg; 70 | 71 | space(args->thread_id); printf("%d: start\n", args->thread_id); space_end(); 72 | 73 | int i; 74 | for (i = 0; i < args->num_loops; i++) { 75 | space(args->thread_id); printf("%d: think\n", args->thread_id); space_end(); 76 | think(); 77 | get_forks(args->thread_id); 78 | space(args->thread_id); printf("%d: eat\n", args->thread_id); space_end(); 79 | eat(); 80 | put_forks(args->thread_id); 81 | space(args->thread_id); printf("%d: done\n", args->thread_id); space_end(); 82 | } 83 | return NULL; 84 | } 85 | 86 | int main(int argc, char *argv[]) { 87 | if (argc != 2) { 88 | fprintf(stderr, "usage: dining_philosophers \n"); 89 | exit(1); 90 | } 91 | printf("dining: started\n"); 92 | 93 | int i; 94 | for (i = 0; i < 5; i++) 95 | Sem_init(&forks[i], 1); 96 | Sem_init(&print_lock, 1); 97 | 98 | pthread_t p[5]; 99 | arg_t a[5]; 100 | for (i = 0; i < 5; i++) { 101 | a[i].num_loops = atoi(argv[1]); 102 | a[i].thread_id = i; 103 | Pthread_create(&p[i], NULL, philosopher, &a[i]); 104 | } 105 | 106 | for (i = 0; i < 5; i++) 107 | Pthread_join(p[i], NULL); 108 | 109 | printf("dining: finished\n"); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /threads-sema/join.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | #ifdef linux 10 | #include 11 | #elif __APPLE__ 12 | #include "zemaphore.h" 13 | #endif 14 | 15 | sem_t s; 16 | 17 | void *child(void *arg) { 18 | sleep(2); 19 | printf("child\n"); 20 | Sem_post(&s); // signal here: child is done 21 | return NULL; 22 | } 23 | 24 | int main(int argc, char *argv[]) { 25 | Sem_init(&s, 0); 26 | printf("parent: begin\n"); 27 | pthread_t c; 28 | Pthread_create(&c, NULL, child, NULL); 29 | Sem_wait(&s); // wait here for child 30 | printf("parent: end\n"); 31 | return 0; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /threads-sema/producer_consumer_works.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common.h" 8 | #include "common_threads.h" 9 | 10 | #ifdef linux 11 | #include 12 | #elif __APPLE__ 13 | #include "zemaphore.h" 14 | #endif 15 | 16 | int max; 17 | int loops; 18 | int *buffer; 19 | 20 | int use = 0; 21 | int fill = 0; 22 | 23 | sem_t empty; 24 | sem_t full; 25 | sem_t mutex; 26 | 27 | #define CMAX (10) 28 | int consumers = 1; 29 | 30 | void do_fill(int value) { 31 | buffer[fill] = value; 32 | fill++; 33 | if (fill == max) 34 | fill = 0; 35 | } 36 | 37 | int do_get() { 38 | int tmp = buffer[use]; 39 | use++; 40 | if (use == max) 41 | use = 0; 42 | return tmp; 43 | } 44 | 45 | void *producer(void *arg) { 46 | int i; 47 | for (i = 0; i < loops; i++) { 48 | Sem_wait(&empty); 49 | Sem_wait(&mutex); 50 | do_fill(i); 51 | Sem_post(&mutex); 52 | Sem_post(&full); 53 | } 54 | 55 | // end case 56 | for (i = 0; i < consumers; i++) { 57 | Sem_wait(&empty); 58 | Sem_wait(&mutex); 59 | do_fill(-1); 60 | Sem_post(&mutex); 61 | Sem_post(&full); 62 | } 63 | 64 | return NULL; 65 | } 66 | 67 | void *consumer(void *arg) { 68 | int tmp = 0; 69 | while (tmp != -1) { 70 | Sem_wait(&full); 71 | Sem_wait(&mutex); 72 | tmp = do_get(); 73 | Sem_post(&mutex); 74 | Sem_post(&empty); 75 | printf("%lld %d\n", (long long int) arg, tmp); 76 | } 77 | return NULL; 78 | } 79 | 80 | int main(int argc, char *argv[]) { 81 | if (argc != 4) { 82 | fprintf(stderr, "usage: %s \n", argv[0]); 83 | exit(1); 84 | } 85 | max = atoi(argv[1]); 86 | loops = atoi(argv[2]); 87 | consumers = atoi(argv[3]); 88 | assert(consumers <= CMAX); 89 | 90 | buffer = (int *) malloc(max * sizeof(int)); 91 | assert(buffer != NULL); 92 | int i; 93 | for (i = 0; i < max; i++) { 94 | buffer[i] = 0; 95 | } 96 | 97 | Sem_init(&empty, max); // max are empty 98 | Sem_init(&full, 0); // 0 are full 99 | Sem_init(&mutex, 1); // mutex 100 | 101 | pthread_t pid, cid[CMAX]; 102 | Pthread_create(&pid, NULL, producer, NULL); 103 | for (i = 0; i < consumers; i++) { 104 | Pthread_create(&cid[i], NULL, consumer, (void *) (long long int) i); 105 | } 106 | Pthread_join(pid, NULL); 107 | for (i = 0; i < consumers; i++) { 108 | Pthread_join(cid[i], NULL); 109 | } 110 | return 0; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /threads-sema/rwlock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | #ifdef linux 10 | #include 11 | #elif __APPLE__ 12 | #include "zemaphore.h" 13 | #endif 14 | 15 | typedef struct _rwlock_t { 16 | sem_t writelock; 17 | sem_t lock; 18 | int readers; 19 | } rwlock_t; 20 | 21 | void rwlock_init(rwlock_t *lock) { 22 | lock->readers = 0; 23 | Sem_init(&lock->lock, 1); 24 | Sem_init(&lock->writelock, 1); 25 | } 26 | 27 | void rwlock_acquire_readlock(rwlock_t *lock) { 28 | Sem_wait(&lock->lock); 29 | lock->readers++; 30 | if (lock->readers == 1) 31 | Sem_wait(&lock->writelock); 32 | Sem_post(&lock->lock); 33 | } 34 | 35 | void rwlock_release_readlock(rwlock_t *lock) { 36 | Sem_wait(&lock->lock); 37 | lock->readers--; 38 | if (lock->readers == 0) 39 | Sem_post(&lock->writelock); 40 | Sem_post(&lock->lock); 41 | } 42 | 43 | void rwlock_acquire_writelock(rwlock_t *lock) { 44 | Sem_wait(&lock->writelock); 45 | } 46 | 47 | void rwlock_release_writelock(rwlock_t *lock) { 48 | Sem_post(&lock->writelock); 49 | } 50 | 51 | int read_loops; 52 | int write_loops; 53 | int counter = 0; 54 | 55 | rwlock_t mutex; 56 | 57 | void *reader(void *arg) { 58 | int i; 59 | int local = 0; 60 | for (i = 0; i < read_loops; i++) { 61 | rwlock_acquire_readlock(&mutex); 62 | local = counter; 63 | rwlock_release_readlock(&mutex); 64 | printf("read %d\n", local); 65 | } 66 | printf("read done: %d\n", local); 67 | return NULL; 68 | } 69 | 70 | void *writer(void *arg) { 71 | int i; 72 | for (i = 0; i < write_loops; i++) { 73 | rwlock_acquire_writelock(&mutex); 74 | counter++; 75 | rwlock_release_writelock(&mutex); 76 | } 77 | printf("write done\n"); 78 | return NULL; 79 | } 80 | 81 | int main(int argc, char *argv[]) { 82 | if (argc != 3) { 83 | fprintf(stderr, "usage: rwlock readloops writeloops\n"); 84 | exit(1); 85 | } 86 | read_loops = atoi(argv[1]); 87 | write_loops = atoi(argv[2]); 88 | 89 | rwlock_init(&mutex); 90 | pthread_t c1, c2; 91 | Pthread_create(&c1, NULL, reader, NULL); 92 | Pthread_create(&c2, NULL, writer, NULL); 93 | Pthread_join(c1, NULL); 94 | Pthread_join(c2, NULL); 95 | printf("all done\n"); 96 | return 0; 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /threads-sema/throttle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | 9 | #ifdef linux 10 | #include 11 | #elif __APPLE__ 12 | #include "zemaphore.h" 13 | #endif 14 | 15 | sem_t s; 16 | 17 | void *child(void *arg) { 18 | Sem_wait(&s); 19 | printf("child %lld\n", (long long int) arg); 20 | sleep(1); 21 | Sem_post(&s); 22 | return NULL; 23 | } 24 | 25 | int main(int argc, char *argv[]) { 26 | if (argc != 3) { 27 | fprintf(stderr, "usage: throttle \n"); 28 | exit(1); 29 | } 30 | int num_threads = atoi(argv[1]); 31 | int sem_value = atoi(argv[2]); 32 | 33 | Sem_init(&s, sem_value); 34 | 35 | printf("parent: begin\n"); 36 | pthread_t c[num_threads]; 37 | 38 | int i; 39 | for (i = 0; i < num_threads; i++) 40 | Pthread_create(&c[i], NULL, child, (void *) (long long int)i); 41 | 42 | for (i = 0; i < num_threads; i++) 43 | Pthread_join(c[i], NULL); 44 | 45 | printf("parent: end\n"); 46 | return 0; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /threads-sema/zemaphore.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "common_threads.h" 8 | #include "zemaphore.h" 9 | 10 | Zem_t s; 11 | 12 | void *child(void *arg) { 13 | sleep(4); 14 | printf("child\n"); 15 | Zem_post(&s); // signal here: child is done 16 | return NULL; 17 | } 18 | 19 | int main(int argc, char *argv[]) { 20 | Zem_init(&s, 0); 21 | printf("parent: begin\n"); 22 | pthread_t c; 23 | Pthread_create(&c, NULL, child, NULL); 24 | Zem_wait(&s); // wait here for child 25 | printf("parent: end\n"); 26 | return 0; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /threads-sema/zemaphore.h: -------------------------------------------------------------------------------- 1 | #ifndef __zemaphore_h__ 2 | #define __zemaphore_h__ 3 | 4 | typedef struct __Zem_t { 5 | int value; 6 | pthread_cond_t cond; 7 | pthread_mutex_t lock; 8 | } Zem_t; 9 | 10 | void Zem_init(Zem_t *z, int value) { 11 | z->value = value; 12 | Cond_init(&z->cond); 13 | Mutex_init(&z->lock); 14 | } 15 | 16 | void Zem_wait(Zem_t *z) { 17 | Mutex_lock(&z->lock); 18 | while (z->value <= 0) 19 | Cond_wait(&z->cond, &z->lock); 20 | z->value--; 21 | Mutex_unlock(&z->lock); 22 | } 23 | 24 | void Zem_post(Zem_t *z) { 25 | Mutex_lock(&z->lock); 26 | z->value++; 27 | Cond_signal(&z->cond); 28 | Mutex_unlock(&z->lock); 29 | } 30 | 31 | #ifdef __APPLE__ 32 | typedef Zem_t sem_t; 33 | 34 | #define Sem_wait(s) Zem_wait(s) 35 | #define Sem_post(s) Zem_post(s) 36 | #define Sem_init(s, v) Zem_init(s, v) 37 | #endif 38 | 39 | #endif // __zemaphore_h__ 40 | -------------------------------------------------------------------------------- /vm-intro/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: va 3 | 4 | clean: 5 | rm -f va 6 | 7 | va: va.c 8 | gcc -o va va.c -Wall 9 | 10 | -------------------------------------------------------------------------------- /vm-intro/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Code from OSTEP chapter [The Abstraction: Address Spaces](http://pages.cs.wisc.edu/~remzi/OSTEP/vm-intro.pdf). 4 | 5 | To compile, just type: 6 | ``` 7 | prompt> make 8 | ``` 9 | 10 | See the highly primitive `Makefile` for details. 11 | 12 | Then, run it: 13 | 14 | ``` 15 | prompt> ./virtual-addresses 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /vm-intro/va.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) { 5 | printf("location of code : %p\n", main); 6 | printf("location of heap : %p\n", malloc(100e6)); 7 | int x = 3; 8 | printf("location of stack: %p\n", &x); 9 | return 0; 10 | } 11 | 12 | --------------------------------------------------------------------------------