Lock Challenge Step 4
Syncmultiplethreadssharingaresource
main.cpp
Go to the documentation of this file.
1 
14 
15 
16 #include <iostream> // std::cout
17 #include <thread> // std::thread
18 #include <mutex> // std::mutex, std::adopt_lock, std::unique_lock
19 #include <chrono> // std::chrono
20 #include <condition_variable> // std::condition_variable
21 #include <random> // std::default_random_generator
22 
23 #include "LockGuard.h" // chal::LockGuard
24 
25 #define NUM_THDS 3 // number of threads to execute
26 
27 
28 // Globals
29 std::mutex mtx; // shared mutex to signal threads
30 std::mutex prnt_mtx; // shared mutex to print from threads
31 std::condition_variable cond; // shared condition variable
32 bool DEBUG = false; // turn on debug messages
33 
34 
35 
45 void debugPrinter(int id, std::string msg) {
46 
47  std::cerr << "***\tDEBUG from" << id+1 << ": "
48  << msg << "\t***" << std::endl;
49 }
50 
59 void debugPrinter(int id, int next_thd) {
60 
61  std::cerr << "***\tDEBUG from" << id+1 << ": "
62  << " next: " << next_thd << "\t***" << std::endl;
63 }
64 
74 void debugPrinter(int id, std::string msg, int next_thd) {
75 
76  std::cerr << "***\tDEBUG from" << id+1 << ": "
77  << msg << " next: " << next_thd << "\t***" << std::endl;
78 }
79 
80 
81 
91 void thd_printer(int id, std::string msg) {
92  //bool DEBUG = true;
93 
94  if(DEBUG)
95  debugPrinter(id, "made it to thd_printer");
96 
97  prnt_mtx.lock();
98  if(DEBUG)
99  debugPrinter(id, "made it to mtx.lock");
100 
101  chal::LockGuard<std::mutex> lck (prnt_mtx, std::adopt_lock);
102  if(DEBUG)
103  debugPrinter(id, "made it to LockGuard");
104 
105  std::cout << "thread" << id+1 << ": " << msg << std::endl;
106 }
107 
108 
120 void thd_worker (const int id, int &next_thd, std::default_random_engine &rand_e) {
121  //bool DEBUG = true;
122 
123  if(DEBUG)
124  debugPrinter(id, next_thd);
125 
126  int wait_tm; // time to randomize
127 
128  thd_printer(id, "starting, waiting.");
129 
130  // repeat forever
131  while(1) {
132 
133  // lock mutex
134  if(DEBUG)
135  debugPrinter(id, "ABOUT TO LOCK", next_thd);
136  mtx.lock();
137  std::unique_lock<std::mutex> locker (mtx, std::adopt_lock);
138 
139  // wait for condition signal
140  // Upon condition signal, check if current thread is next
141  // if yes continue, if not keep waiting
142  // lambda function creates condition predicate
143  if(DEBUG)
144  debugPrinter(id, "made it to cond.wait");
145  cond.wait(locker, [&]() { return id == next_thd; });
146  if(DEBUG)
147  debugPrinter(id, "made it past cond.wait");
148 
149  thd_printer(id, "signal received, doing work ....");
150 
151  // generate pseudo-random value between 1 and 5 seconds
152  wait_tm = 1 + rand_e() % 5;
153 
154  // sleep to simulate work
155  if(DEBUG)
156  debugPrinter(id, "sleeping for:", wait_tm);
157  std::this_thread::sleep_for(std::chrono::seconds(wait_tm));
158 
159  thd_printer(id, "done with work, signal next thread");
160  if(DEBUG)
161  debugPrinter(id, "returned from thd_printer");
162 
163  // if topmost thread, reset next_thd
164  if(next_thd == NUM_THDS-1) {
165  next_thd = 0;
166  }
167  // otherwise, just increment
168  else {
169  ++next_thd;
170  }
171 
172  if(DEBUG)
173  debugPrinter(id, "ABOUT TO NOTIFY", next_thd);
174  cond.notify_all(); // restart sequence
175  }
176 }
177 
178 
179 
180 int main () {
181  //bool DEBUG = true;
182  int id = -1; // identifier to pass to Debug Printer
183 
184  std::thread threads[NUM_THDS];
185  int next_thd = -1; // prevent a spurious wake from prematurely activating thread 0
186 
187  std::default_random_engine rand_e;
188 
189  std::cout << "main: starting all threads" << std::endl;
190 
191  // spawn NUM_THDS threads:
192  for (int i=0; i<NUM_THDS; ++i) {
193 
194  if(DEBUG)
195  debugPrinter(id, "main for loop #");
196 
197  // populate the array of thread objects
198  // pass in: * their unique ID by value
199  // * an integer to keep track of thread order by reference
200  // * a shared psuedo-random number generator by reference
201  threads[i] = std::thread(thd_worker, i, std::ref(next_thd), std::ref(rand_e));
202  }
203 
205  std::this_thread::sleep_for(std::chrono::seconds(3));
206 
207  if(DEBUG)
208  debugPrinter(id, "waited three seconds");
209 
210  cond.notify_all(); // start sequence
211  next_thd = 0; // allow thread 0 to be activated
212 
213  if(DEBUG)
214  debugPrinter(id, "main join for starting");
215 
216  // clean up
217  for ( auto& th : threads ) {
218  th.join();
219 
220  if(DEBUG) {
221  debugPrinter(id, "an instance of main for join loop");
222  }
223  }
224 
225  return 0;
226 }
std::mutex mtx
Definition: main.cpp:29
void thd_worker(const int id, int &next_thd, std::default_random_engine &rand_e)
thread worker function
Definition: main.cpp:120
A movable scoped lock type.
Definition: LockGuard.h:34
std::mutex prnt_mtx
Definition: main.cpp:30
void thd_printer(int id, std::string msg)
thread print function
Definition: main.cpp:91
int main()
Definition: main.cpp:180
std::condition_variable cond
Definition: main.cpp:31
bool DEBUG
Definition: main.cpp:32
#define NUM_THDS
Definition: main.cpp:25
void debugPrinter(int id, std::string msg)
DEBUG PRINTER function.
Definition: main.cpp:45
contains namespace chal and class LockGuard