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