# Synchronization Examples - Apply the tools to several classic synchronization problems ## Classic Problems of Synchronization ### Bounded-Buffer Problem The structure of the **producer** process: ```c // 假設有 n 個 buffers, 每個都有資料 // 三個 semaphore 預設值為 mutex = 1, full = 0, empty = n while (true) { ... /* produce an item in next produced */ ... wait(empty); wait(mutex); ... /* add next produced to the buffer */ ... signal(mutex); signal(full); } ``` The structure of the **consumer** process: ```c while (true) { wait(full); wait(mutex); ... /* remove an item from buffer to next consumed */ ... signal(mutex); signal(empty); ... /* consume the item in next consumed */ ... } ``` ### Readers–Writers Problem - Reader 只讀資料所以可以同時 access 同一份 data - Writer 會修改資料,所以一次只會有 writer 一個人在存取 data The reader processes share the following data structures: ```c semaphore rw_mutex = 1; // mutual exclusion semaphore for the writers semaphore mutex = 1; // ensure mutual exclusion when the variable read_count is updated int read_count = 0; // how many processes are currently reading the object ``` The structure of a **writer** process: ```c while (true) { wait(rw_mutex); ... /* writing is performed */ ... signal(rw_mutex); } ``` The structure of a **reader** process: ```c while (true) { wait(mutex); // 表示我想要修改 read_count read_count++; if (read_count == 1) // 表示現在是 reader 要讀資料了 wait(rw_mutex); // 讓 writer 無法修改資料 signal(mutex); // 結束修改 read_count ... /* reading is performed */ ... wait(mutex); // 表示我想要修改 read_count read_count--; if (read_count == 0) // 表示 reader 結束了 signal(rw_mutex); // writer 可以修改資料了 signal(mutex); // 結束修改 read_count } ``` ### Dining-Philosophers Problem ![image](https://hackmd.io/_uploads/Bki7MZ3Ba.png) #### Semaphore Solution Data structure: ``` semaphore chopstick[5]; ``` The structure of philosopher $i$: ```c while (true) { wait(chopstick[i]); wait(chopstick[(i + 1) % 5]); ... /* eat for a while */ ... signal(chopstick[i]); signal(chopstick[(i + 1) % 5]); ... /* think for awhile */ ... } ``` #### Monitor Solution Data structure: ``` enum {THINKING, HUNGRY, EATING} state[5]; condition self[5]; ``` ```c monitor DiningPhilosophers { enum {THINKING, HUNGRY, EATING} state[5]; condition self[5]; void pickup(int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self[i].wait(); } void putdown(int i) { state[i] = THINKING; test((i + 4) % 5); test((i + 1) % 5); } void test(int i) { if ((state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING)) { state[i] = EATING; self[i].signal(); } } initialization_code() { for (int i = 0; i < 5; i++) state[i] = THINKING; } } ``` ``` DiningPhilosophers.pickup(i); ... eat ... DiningPhilosophers.putdown(i); ``` ## Alternative Approaches ### Transactional Memory - A function `update()` that modifies shared data - This function would be written using mutex locks (or semaphore) - ```c void update() { aquire(); /* modify shared data */ release(); } ``` - A memory transaction is a sequence of read-write operations that are atomic - If all operations in a transaction are completed, the memory transaction is committed - Otherwise, the operations must be aborted and rolled back - ```c void update() { atomic { /* modify share data */ } } ```