HW4 Solutions 1. Synchronization: warmup int i = 0; scond_t cond; smutex_t mutex; void foo(void *) { smutex_lock(&mutex); printf("I am foo!!!\n"); i = 1; scond_signal(&cond, &mutex); smutex_unlock(&mutex); } void boo(void *) { smutex_lock(&mutex); while (!i) { scond_wait(&cond, &mutex); } printf("I am boo!!!!\n"); smutex_unlock(&mutex); } int main(int argc, char**argv) { smutex_init(&mutex); scond_init(&cond); thread* t1 = create_thread(foo); thread* t2 = create_thread(boo); join_thread(t1); join_thread(t2); smutex_destroy(&mutex); scond_destroy(&cond); exit(0); } 2.1. The outer "if" is not protected by a mutex. So the interleaving can be: T1: check Bob's balance T2: decrease Bob's balance T1: acquire mutex T1: cause balance to be negative... 2.2 Place the lock() around the if check. Depending on the implementation, there may need to be two instances of unlock() in the code. 3. Smokers class Table { public: void matchSmokerUseTable(); void paperSmokerUseTable(); void tobaccoSmokerUseTable(); void agentUseTable(); Table(); int getPaper(); int getTobacco(); int getMatch(); ~Table(); private: scond_t tableEmptyCond; // contract: when table is empty, signal this scond_t tableFullCond; // when table is full, signal this smutex_t tableMutex; int paper, tobacco, match; // shared state, encapsulated // table is small, so can at most have two items at once bool tableIsFull(); bool tableIsEmpty(); }; Table:: Table() { paper = 0; tobacco = 0; match = 0; scond_init(&tableFullCond); scond_init(&tableEmptyCond); smutex_init(&tableMutex); } Table:: ~Table() { scond_destroy(&tableEmptyCond); scond_destroy(&tableFullCond); smutex_destroy(&tableMutex); } // warm up example int Table::getPaper() { smutex_lock(&tableMutex); int r = paper; smutex_unlock(&tableMutex); return r; } // get tobacco and get match is similar // private method, why we don't need mutex here? bool Table::tableIsFull() { return paper + tobacco + match >= 2; } bool Table::tableIsEmpty() { return !tableIsFull(); } void Table::agentUseTable() { smutex_lock(&tableMutex); while (tableIsFull()) { scond_wait(&tableEmptyCond, &tableMutex); } chooseIngredients(&paper, &tobacco, &match); // why broadcast instead of signal? scond_broadcast(&tableFullCond, &tableMutex); smutex_unlock(&tableMutex); } void Table::paperSmokerUseTable() { smutex_lock(&tableMutex); while(tableIsEmpty() || (match < 1 || tobacco < 1)) { scond_wait(&tableFullCond, &tableMutex); } match--; tobacco--; scond_signal(&tableEmptyCond, &tableMutex); smutex_unlock(&tableMutex); } 4. class Auction { public: Auction(); ~Auction(); void RegisterBid(uint32_t amount); uint32_t SelectWinner(); private: uint32_t bids[NUM_BIDS]; // bids[i] == 0 means that slot i is free mutex m; cond cv_auctionready; cond cv_roomforbid; int get_avail_slot(); uint32_t find_max(); }; Auction::Auction() { memset(bids, 0, sizeof(bids)); // FILL THIS IN mutex_init(&m); cond_init(&cv_auctionready); cond_init(&cv_roomforbid); } void Auction:RegisterBid(uint32_t amount) { // We use 0 to mean "slot free" assert(amount > 0); // FILL THIS IN int idx; m.acquire(); while ((idx = get_avail_slot()) < 0) { cond_wait(&m, &cv_roomforbid); } bids[idx] = amount; if (idx == NUM_BIDS-1) cond_signal(&m, &cv_auctionready); m.release(); } uint32_t Auction:SelectWinner() { // FILL THIS IN uint32_t winning_bid; m.acquire(); while (get_avail_slot() >= 0) cond_wait(&m, &cv_auctionready); winning_bid = find_max(); memset(bids, 0, sizeof(bids)); cond_broadcast(&m, &cv_roomforbid); m.release(); return winning_bid; } // helper functions int Auction::get_avail_slot() { int i; for (int i = 0; i < NUM_BIDS; i++) if (!bids[i]) return i; return -1; } uint32_t Auction::find_max() { uint32_t max = bids[0]; for (int i = 0; i < NUM_BIDS; i++) if (bids[i] > max) max = bids[i]; return max; }