
2 changed files with 169 additions and 0 deletions
@ -0,0 +1,59 @@ |
|||||
|
// This file is under GNU General Public License 3.0
|
||||
|
// see LICENSE.txt
|
||||
|
|
||||
|
#ifndef LIBPEPADAPTER_SEMAPHORE_HH |
||||
|
#define LIBPEPADAPTER_SEMAPHORE_HH |
||||
|
|
||||
|
#include <mutex> |
||||
|
#include <condition_variable> |
||||
|
#include <atomic> |
||||
|
|
||||
|
namespace pEp { |
||||
|
class CountingSemaphore { |
||||
|
std::mutex mtx; |
||||
|
std::condition_variable cv; |
||||
|
|
||||
|
// To synchronize threads, ALWAYS use <atomic> types
|
||||
|
std::atomic_uint _count; |
||||
|
|
||||
|
public: |
||||
|
CountingSemaphore(unsigned count = 0) : _count(count) {} |
||||
|
|
||||
|
CountingSemaphore& operator=(unsigned count) |
||||
|
{ |
||||
|
std::unique_lock<std::mutex> lock(mtx); |
||||
|
_count.store (count); |
||||
|
if (count != 0) |
||||
|
cv.notify_one (); |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
unsigned load() |
||||
|
{ |
||||
|
return _count.load (); |
||||
|
} |
||||
|
|
||||
|
void p() |
||||
|
{ |
||||
|
std::unique_lock<std::mutex> lock(mtx); |
||||
|
// FIXME: is the loop even needed? Any received notification will
|
||||
|
// wake up ony one thread, which will see the count as non-zero...
|
||||
|
while (_count.load() == 0) { |
||||
|
//std::cout << "p: waiting...\n";
|
||||
|
cv.wait(lock); |
||||
|
} |
||||
|
_count --; |
||||
|
//std::cout << "after p: " << _count.load () << "\n";
|
||||
|
} |
||||
|
|
||||
|
void v() |
||||
|
{ |
||||
|
std::unique_lock<std::mutex> lock(mtx); |
||||
|
_count ++; |
||||
|
//std::cout << "after v: " << _count.load () << "\n";
|
||||
|
cv.notify_one(); |
||||
|
} |
||||
|
}; |
||||
|
} // namespace pEp
|
||||
|
|
||||
|
#endif // LIBPEPADAPTER_SEMAPHORE_HH
|
@ -0,0 +1,110 @@ |
|||||
|
// This file is under GNU General Public License 3.0
|
||||
|
// see LICENSE.txt
|
||||
|
|
||||
|
#include <iostream> |
||||
|
#include <thread> |
||||
|
#include <cassert> |
||||
|
#include "../src/std_utils.hh" |
||||
|
#include "../src/CountingSemaphore.hh" |
||||
|
|
||||
|
using namespace std; |
||||
|
using namespace pEp; |
||||
|
|
||||
|
thread make_two_threads_for_many_threads() |
||||
|
{ |
||||
|
thread res([&]() { |
||||
|
CountingSemaphore semaphore; |
||||
|
semaphore = 3; |
||||
|
|
||||
|
thread thread1([&]() { |
||||
|
semaphore.p (); |
||||
|
semaphore.p (); |
||||
|
semaphore.p (); |
||||
|
// Now the count is at 0, assuming the other thread has not
|
||||
|
// started yet; otherwise it should be at 1.
|
||||
|
|
||||
|
semaphore.p (); |
||||
|
//cout << "1: done\n";
|
||||
|
}); |
||||
|
|
||||
|
Utils::sleep_millis(1000); |
||||
|
|
||||
|
thread thread2([&]() { |
||||
|
semaphore.v(); |
||||
|
}); |
||||
|
|
||||
|
thread1.join(); |
||||
|
thread2.join(); |
||||
|
|
||||
|
assert (semaphore.load() == 0); |
||||
|
}); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
void many_threads() |
||||
|
{ |
||||
|
int i; |
||||
|
|
||||
|
#define N 1000 |
||||
|
thread threads[N]; |
||||
|
std::cout << "starting " << N |
||||
|
<< " threads, each starting and joining a thread pair...\n"; |
||||
|
for (i = 0; i < N; i ++) |
||||
|
threads [i] = make_two_threads_for_many_threads (); |
||||
|
std::cout << "joining threads...\n"; |
||||
|
for (i = 0; i < N; i ++) |
||||
|
threads [i].join(); |
||||
|
#undef N |
||||
|
std::cout << "many_threads done.\n\n"; |
||||
|
} |
||||
|
|
||||
|
void v_times (CountingSemaphore &semaphore, |
||||
|
int times) |
||||
|
{ |
||||
|
std::cout << "V'ing " << times << " times...\n"; |
||||
|
for (int i = 0; i < times; i ++) |
||||
|
semaphore.v (); |
||||
|
std::cout << "... I V'd " << times << " times.\n"; |
||||
|
} |
||||
|
|
||||
|
void p_times (CountingSemaphore &semaphore, |
||||
|
int times) |
||||
|
{ |
||||
|
std::cout << "P'ing " << times << " times...\n"; |
||||
|
for (int i = 0; i < times; i ++) |
||||
|
semaphore.p (); |
||||
|
std::cout << "... I P'd " << times << " times.\n"; |
||||
|
} |
||||
|
|
||||
|
void few_threads() |
||||
|
{ |
||||
|
#define HALF_N (1000 * 1000 * 10) |
||||
|
#define N (HALF_N * 2) |
||||
|
CountingSemaphore semaphore; |
||||
|
|
||||
|
thread thread_p1([&]() { |
||||
|
p_times (semaphore, HALF_N); |
||||
|
}); |
||||
|
thread thread_v([&]() { |
||||
|
v_times (semaphore, N); |
||||
|
}); |
||||
|
thread thread_p2([&]() { |
||||
|
p_times (semaphore, HALF_N); |
||||
|
}); |
||||
|
#undef HALF_N |
||||
|
#undef N |
||||
|
thread_v.join(); |
||||
|
thread_p1.join(); |
||||
|
thread_p2.join(); |
||||
|
|
||||
|
assert (semaphore.load() == 0); |
||||
|
|
||||
|
std::cout << "few_thrads done.\n\n"; |
||||
|
} |
||||
|
|
||||
|
int main() |
||||
|
{ |
||||
|
many_threads (); |
||||
|
few_threads (); |
||||
|
return 0; |
||||
|
} |
Loading…
Reference in new issue