C++ condition_variable wait_for not always working - c++

My application start the logThread thread and then cyclically calls lookForSms wich makes a curl call and wait for logThread to read a certain string, but this string may never come so i used a condition_variable wait_for so that eventually (after 5 minutes) it can continue the test.
The problem is at times it waits a lot longer, sometimes 20 min sometimes forever.
How can I make it reliably work?
I'm using Visual Studio Community 2017, here the code:
void lookForSms(CURL *curl) {
sms_found = false;
int i;
CURLcode res;
cout << "\n" << timestamp() << "\t";
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
unique_lock<mutex> lk(F);
analysis << timestampMil() << "\t";
while (cv.wait_for(lk, TIMEOUT_SMS * 1000ms) != cv_status::timeout && !sms_found);
if (sms_found) {
analysis << timestampMil() << "\tSMS RECEIVED\n";
cout << "\tOK";
}
else {
analysis << timestampMil() << "\tSMS LOST\n";
cout << "\tKO";
}
}
}
void logThread() {
string line;
int n, i, count;
unsigned char buf[BUFFER];
while (!close) {
N.lock();
M.lock();
N.unlock();
n = RS232_PollComport(comport, buf, BUFFER);
if (n > 0 && n < BUFFER) {
buf[n] = '\0';
for (i = 0; i < n; i++) {
if (buf[i] == '\n') {
all << timestampMil() << "\t" << line << "\n";
handleLine(line);
line = string();
}
else if (buf[i] != '\r' && buf[i] >= 32) {
line += buf[i];
}
}
}
M.unlock();
}
}
void handleLine(string line) {
if (searchString(line, RCV_SMS)) {
unique_lock<mutex> lk(F);
sms_found = true;
lk.unlock();
cv.notify_all();
}
}
[EDIT] This should have everything it needs to recreate the problem.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#define TIMEOUT_SMS 300
#define DELAY_SMS 10
mutex L, M, N, F;
condition_variable cv;
void logThread() {
while (!close) {
N.lock();
M.lock();
N.unlock();
handleLine("");
Sleep(500);
M.unlock();
}
}
void lookForSms() {
unique_lock<mutex> lk(F);
sms_found = false;
unlocked = false;
//while (cv.wait_for(lk, TIMEOUT_SMS * 1000ms) != cv_status::timeout && !unlocked);
cv.wait_for(lk, 10 * TIMEOUT_SMS * 1000ms, []() {return unlocked; });
if (sms_found) {
cout << "\tOK";
}
else {
cout << "\tKO";
}
}
}
void handleLine(string line) {
if (false) {// the problem is when cv remains locked
unique_lock<mutex> lk(F);
unlocked = true;
sms_found = true;
lk.unlock();
cv.notify_all();
}
}
void stopThreads() {
N.lock();
M.lock();
//stop log thread
close = true;
N.unlock();
M.unlock();
}
int main(int argc, const char * argv[]) {
thread logger(logThread);
for (i = 0; i < repeat; i++) {
lookForSms();
Sleep(DELAY_SMS * 1000);
}
//aquire mutex and set close to true, then join
stopThreads();
//wait for the thread to end
logger.join();
return 0;
}

Related

Writing in file from shared buffer missing data and program crash without cout

I am making a program using threads and a shared buffer. The two threads run indefinitely in the background, one thread will fill a shared buffer with data and the other thread will write the content of the shared buffer into a file.
The user can start or stop the data filling which is resulting in the thread entering into a waiting state until the user starts the thread again. Each loop the buffer is filled with 50 floats.
This is the code :
#include <iostream>
#include <vector>
#include <iterator>
#include <utility>
#include <fstream>
#include <condition_variable>
#include <mutex>
#include <thread>
using namespace std;
std::mutex m;
std::condition_variable cv;
std::vector<std::vector<float>> datas;
bool keep_running = true, start_running = false;
void writing_thread()
{
ofstream myfile;
bool opn = false;
while(1)
{
while(keep_running)
{
// Open the file only once
if(!opn)
{
myfile.open("IQ_Datas.txt");
opn = true;
}
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return !datas.empty();});
auto d = std::move(datas);
lk.unlock();
for(auto &entry : d)
{
for(auto &e : entry)
myfile << e << endl;
}
}
if(opn)
{
myfile.close();
opn = false;
}
}
}
void sending_thread()
{
std::vector<float> m_buffer;
int cpt=0;
//Fill the buffer with 50 floats
for(float i=0; i<50; i++)
m_buffer.push_back(i);
while(1)
{
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return keep_running && start_running;});
}
while(keep_running)
{
//Each loop d is containing 50 floats
std::vector<float> d = m_buffer;
cout << "in3" << endl; //Commenting this line makes the program crash
{
std::lock_guard<std::mutex> lk(m);
if (!keep_running)break;
datas.push_back(std::move(d));
}
cv.notify_one();
cpt++;
}
cout << "Total data: " << cpt*50 << endl;
cpt = 0;
}
}
void start()
{
{
std::unique_lock<std::mutex> lk(m);
start_running = true;
}
cv.notify_all();
}
void stop()
{
{
std::unique_lock<std::mutex> lk(m);
start_running = false;
}
cv.notify_all();
}
int main()
{
int go = 0;
thread t1(sending_thread);
thread t2(writing_thread);
t1.detach();
t2.detach();
while(1)
{
std::cin >> go;
if(go == 1)
{
start();
keep_running = true;
}
else if(go == 0)
{
stop();
keep_running = false;
}
}
return 0;
}
I have 2 issues with this code :
When commenting the line cout << "in3" << endl; the program will crash after ~20-40 seconds with the error message : terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc. If i let the cout, the program will run without problems.
When the program is working, after stoping sending_thread i display the total amount of data that has been copied with cout << "Total data: " << cpt*50 << endl;. For small amount of datas, all of it is written correctly into the file but when the amount is big, there is missing data. Missing/Correct data (Total number of lines in the file does not match total data)
Why with the cout the program is running correctly ? And what is causing the missing data ? Is it because sending_thread is filling the buffer too fast while writing_threadtakes too much time to write into the file?
EDIT: Some precisions, adding more cout into sending_threadseems to fix all the issues. First thread produced 21 million floats and second thread successfully wrote in the file 21 million floats. It seems like without the cout, producer threads works too fast for the consumer thread to keep retrieving data from the shared buffer while writing it into a file.
To avoid:
Moved-from object 'datas' of type 'std::vector' is moved:
auto d = std::move(datas);
^~~~~~~~~~~~~~~~
Replace this:
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return !datas.empty();});
auto d = std::move(datas);
lk.unlock();
With this:
// Wait until main() sends data
std::vector<std::vector<float>> d;
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] { return !datas.empty(); });
datas.swap(d);
}
Also replace your bool variables that are accessed from multiple threads with std::atomic_bool or std::atomic_flag.
The bad_alloc comes from sending_thread being much faster than writing_thread so it will run out of memory. When you slow down sending_thread enough (with printing), the problem is less visible, but you should have some synchronization to do it properly. You could make a wrapper class around it and provide insert and extraction methods to make sure all access is synchronized properly and also give it a max number of elements. An example:
template<typename T>
class atomic2dvector {
public:
atomic2dvector(size_t max_elements) : m_max_elements(max_elements) {}
atomic2dvector(const atomic2dvector&) = delete;
atomic2dvector(atomic2dvector&&) = delete;
atomic2dvector& operator=(const atomic2dvector&) = delete;
atomic2dvector& operator=(atomic2dvector&&) = delete;
~atomic2dvector() { shutdown(); }
bool insert_one(std::vector<T>&& other) {
std::unique_lock<std::mutex> lock(m_mtx);
while(m_current_elements + m_data.size() > m_max_elements && m_shutdown == false)
m_cv.wait(lock);
if(m_shutdown) return false;
m_current_elements += other.size();
m_data.emplace_back(std::forward<std::vector<T>>(other));
m_cv.notify_one();
return true;
}
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
if(m_shutdown == false) {
m_current_elements = 0;
return_value.swap(m_data);
} else {
// return an empty vector if we should shutdown
}
m_cv.notify_one();
return return_value;
}
bool is_active() const { return m_shutdown == false; }
void shutdown() {
m_shutdown = true;
m_cv.notify_all();
}
private:
size_t m_max_elements;
size_t m_current_elements = 0;
std::atomic<bool> m_shutdown = false;
std::condition_variable m_cv{};
std::mutex m_mtx{};
std::vector<std::vector<T>> m_data{};
};
If you'd like to keep extracting data even after shutdown, you can change extract_all() to this:
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
m_current_elements = 0;
return_value.swap(m_data);
m_cv.notify_one();
return return_value;
}
A full example could look like this:
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <iterator>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
using namespace std;
template<typename T>
class atomic2dvector {
public:
atomic2dvector(size_t max_elements) : m_max_elements(max_elements) {}
atomic2dvector(const atomic2dvector&) = delete;
atomic2dvector(atomic2dvector&&) = delete;
atomic2dvector& operator=(const atomic2dvector&) = delete;
atomic2dvector& operator=(atomic2dvector&&) = delete;
~atomic2dvector() { shutdown(); }
bool insert_one(std::vector<T>&& other) {
std::unique_lock<std::mutex> lock(m_mtx);
while(m_current_elements + m_data.size() > m_max_elements &&
m_shutdown == false)
m_cv.wait(lock);
if(m_shutdown) return false;
m_current_elements += other.size();
m_data.emplace_back(std::forward<std::vector<T>>(other));
m_cv.notify_one();
return true;
}
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
m_current_elements = 0;
return_value.swap(m_data);
m_cv.notify_one();
return return_value;
}
bool is_active() const { return m_shutdown == false; }
void shutdown() {
m_shutdown = true;
m_cv.notify_all();
}
private:
size_t m_max_elements;
size_t m_current_elements = 0;
std::atomic<bool> m_shutdown = false;
std::condition_variable m_cv{};
std::mutex m_mtx{};
std::vector<std::vector<T>> m_data{};
};
std::mutex m;
std::condition_variable cv;
atomic2dvector<float> datas(256 * 1024 * 1024 / sizeof(float)); // 0.25 GiB limit
std::atomic_bool start_running = false;
void writing_thread() {
std::ofstream myfile("IQ_Datas.txt");
if(myfile) {
std::cout << "writing_thread waiting\n";
std::vector<std::vector<float>> d;
while((d = datas.extract_all()).empty() == false) {
std::cout << "got " << d.size() << "\n";
for(auto& entry : d) {
for(auto& e : entry) myfile << e << "\n";
}
std::cout << "wrote " << d.size() << "\n\n";
}
}
std::cout << "writing_thread shutting down\n";
}
void sending_thread() {
std::vector<float> m_buffer;
std::uintmax_t cpt = 0;
// Fill the buffer with 50 floats
for(float i = 0; i < 50; i++) m_buffer.push_back(i);
while(true) {
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {
return start_running == true || datas.is_active() == false;
});
}
if(datas.is_active() == false) break;
std::cout << "sending...\n";
while(start_running == true) {
// Each loop d is containing 50 floats
std::vector<float> d = m_buffer;
if(datas.insert_one(std::move(d)) == false) break;
cpt++;
}
cout << "Total data: " << cpt * 50 << endl;
cpt = 0;
}
std::cout << "sending_thread shutting down\n";
}
void start() {
std::unique_lock<std::mutex> lk(m);
start_running = true;
cv.notify_all();
}
void stop() {
std::unique_lock<std::mutex> lk(m);
start_running = false;
cv.notify_all();
}
void quit() {
datas.shutdown();
cv.notify_all();
}
int main() {
int go = 0;
thread t1(sending_thread);
thread t2(writing_thread);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Enter 1 to make the sending thread send and 0 to make it stop "
"sending. Enter a non-integer to shutdown.\n";
while(std::cin >> go) {
if(go == 1) {
start();
} else if(go == 0) {
stop();
}
}
std::cout << "--- shutting down ---\n";
quit();
std::cout << "joining threads\n";
t1.join();
std::cout << "t1 joined\n";
t2.join();
std::cout << "t2 joined\n";
}

C++ Error when threading, std::invoke:

Okay, so these are my errors.
std::invoke: no matching overloaded function found
// and
Failed to specialize function template 'unkown-type std::invoke("Callable &&,_Types &&...) noexcept()'
I really need your guys help. Im pretty new to C++, so I would like if you gave me examples on how to do it. Not only explain.
And this is my code:
#include <iostream>
#include <thread>
#include <stdio.h>
#include <Windows.h>
#include <string>
using namespace std;
void SetColor(int ForgC);
void Navigation();
void Switch(int index);
void UpdateMenu();
const int IWAL = 1;
const int ITRI = 0;
int M_Index = 0;
int Changes = 0;
bool Name1 = false;
bool Name2 = false;
string bools[2] = { "[OFF]", "[ON]" };
void Navigation()
{
for (;;)
{
for (int i = 2; i < 180; i++)
{
if (GetAsyncKeyState(i) & 0x8000)
{
switch (i)
{
case 38:
if (M_Index < 2)
M_Index++;
Changes++;
break;
case 40:
if (M_Index > 0)
M_Index--;
Changes++;
break;
case 37:
Switch(M_Index);
Changes++;
break;
case 39:
Switch(M_Index);
Changes++;
break;
}
Sleep(200);
}
}
}
}
void Switch(int index)
{
if (index == IWAL)
{
Name1 = !Name1;
}
else if (index == ITRI)
{
Name2 = !Name2;
}
}
void UpdateMenu()
{
int temp = -1;
for (;;)
{
if (temp != Changes)
{
temp = Changes;
system("cls");
SetColor(15);
cout << ">> Krizzo's Menu <<" << endl;
cout << "___________________" << endl << endl;
if (M_Index == IWAL)
{
SetColor(10);
cout << " Name1\t=\t" << bools[Name1] << endl;
}
else
{
SetColor(15);
cout << " Name1\t=\t" << bools[Name1] << endl;
}
if (M_Index == ITRI)
{
SetColor(10);
cout << " Name2\t=\t" << bools[Name2] << endl;
}
else
{
SetColor(15);
cout << " Name2\t=\t" << bools[Name2] << endl;
}
}
}
}
void SetColor(int ForgC)
{
WORD wColor;
//We will need this handle to get the current background attribute
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
//We use csbi for the wAttributes word.
if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
{
//Mask out all but the background attribute, and add in the foreground color
wColor = (csbi.wAttributes & 0xF0) + (ForgC & 0x0F);
SetConsoleTextAttribute(hStdOut, wColor);
}
return;
}
int main(int argc, char** argv) {
std::thread t1(Navigation);
std::thread t2(Switch);
std::thread t3(UpdateMenu);
std::thread t4(SetColor);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
Your Switch and SetColor functions take a parameter. You have to pass this parameter to the thread constructor. Example:
int addOne(int x)
{
return x + 1;
}
int main()
{
std::thread t1(addOne, 5);
t1.join();
}
When dealing with reference parameters, you have to wrap the parameter in a std::ref() call:
void addOne(int& x)
{
x += 1;
}
int main()
{
int a = 42;
std::thread t1(addOne, std::ref(a));
t1.join();
}
Lastly, when you want to run a member function of a class on a seperate thread, you have to pass the this pointer, as the thread has to know which instance the member function is called on.
class X
{
void doSomething()
{
std::thread t1(X::expensiveCalculations, this);
//do something else
t1.join();
}
void expensiveCalculations()
{
}
};
int main()
{
X x;
x.doSomething();
}

Trying to switch between threads using condition_variables

I have two threads. Both get initialized, but the first thread runs first, while the second thread waits for the first thread (call_once(flagEven1, [&](){cond2.wait(lock, [&](){return zeroEvenOddFlag == 1; }); });). After a certain condition is met, the first thread wakes up the second thread and goes to sleep.
zeroEvenOddFlag = 0;
cond1.notify_all();
cond2.wait(lock, [&](){return zeroEvenOddFlag == 1; });
This process alternates until the task is finished. Right now the threads just get locked up and I don't know what's happening. The threads together should process the input string and produce two strings of zeros and non-zero numbers. I corrected for spurious wakeups by adding an integer that is shared between the threads (zeroEvenOddFlag).
#include <string>
#include <thread>
#include <future>
#include <cstdio>
#include <iostream>
#include <queue>
#include <condition_variable>
using namespace std;
mutex mu;
condition_variable cond1;
condition_variable cond2;
once_flag flagZero;
once_flag flagEven;
once_flag flagEven1;
int zeroEvenOddFlag = 0;
string printerFunc(queue<char>& input, int zeroEvenOdd, once_flag& flag){
string output = "";
function<void(string&)> f;
unique_lock<mutex> lock(mu);
call_once(flag, [&](){
if (zeroEvenOdd == 1){
f = [&](string& output){
int i = input.front() - '0';
if (i == 0){
output += to_string(i);
input.pop();
}
else {
zeroEvenOddFlag = 1;
cond2.notify_all();
cond1.wait(lock, [&](){return zeroEvenOddFlag == 0; });
}
};
}
else if (zeroEvenOdd == 2){
call_once(flagEven1, [&](){cond2.wait(lock, [&](){return zeroEvenOddFlag == 1; }); });
f = [&](string& output){
int i = input.front() - '0';
if (i != 0){
output += to_string(i);
input.pop();
}
else {
zeroEvenOddFlag = 0;
cond1.notify_all();
cond2.wait(lock, [&](){return zeroEvenOddFlag == 1; });
}
};
}
});
while(input.size()!=0){
f(output);
}
return output;
}
int _tmain(int argc, _TCHAR* argv[])
{
string input = "0102030";
queue<char> inputQueue;
for(char c : input){
inputQueue.push(c);
}
auto zeros = async(launch::async, printerFunc, ref(inputQueue), 1, ref(flagZero));
auto evens = async(launch::async, printerFunc, ref(inputQueue), 2, ref(flagEven));
string zerosString = zeros.get();
string evensString = evens.get();
cout << "these are the zeros " << zerosString << endl;
cout << "these are the evens " << evensString << endl;
this_thread::sleep_for(chrono::seconds(10));
return 0;
}
You need to always check for spurious wake-ups conditions. The use of cond2.wait(lock, [&](){return zeroEvenOddFlag == 1; }); and the like help check. Also, you reach an issue at the end of your loop. Which ever thread finally owns the mutex has to release it when it consumes the last bit of the queue, so that the other threads can be freed up to finish. The final code should look like this:
#include <string>
#include <thread>
#include <future>
#include <cstdio>
#include <iostream>
#include <queue>
#include <condition_variable>
using namespace std;
mutex mu;
condition_variable cond1;
condition_variable cond2;
once_flag flagZero;
once_flag flagEven;
once_flag flagEven1;
int zeroEvenOddFlag = 0;
string printerFunc(queue<char>& input, int zeroEvenOdd, once_flag& flag){
string output = "";
function<void(string&)> f;
unique_lock<mutex> lock(mu);
call_once(flag, [&](){
if (zeroEvenOdd == 1){
f = [&](string& output){
int i = input.front() - '0';
if (i == 0){
output += to_string(i);
input.pop();
if (input.size() == 0){
zeroEvenOddFlag = 1;
cond2.notify_all();
}
}
else {
zeroEvenOddFlag = 1;
cond2.notify_all();
cond1.wait(lock, [&](){return zeroEvenOddFlag == 0; });
}
};
}
else if (zeroEvenOdd == 2){
call_once(flagEven1, [&](){cond2.wait(lock, [&](){return zeroEvenOddFlag == 1; }); });
f = [&](string& output){
int i = input.front() - '0';
if (i != 0){
output += to_string(i);
input.pop();
if (input.size() == 0){
zeroEvenOddFlag = 0;
cond1.notify_all();
}
}
else {
zeroEvenOddFlag = 0;
cond1.notify_all();
cond2.wait(lock, [&](){return zeroEvenOddFlag == 1; });
}
};
}
});
while(input.size()!=0){
f(output);
}
return output;
}
int _tmain(int argc, _TCHAR* argv[])
{
string input = "0102030";
queue<char> inputQueue;
for(char c : input){
inputQueue.push(c);
}
auto zeros = async(launch::async, printerFunc, ref(inputQueue), 1, ref(flagZero));
auto evens = async(launch::async, printerFunc, ref(inputQueue), 2, ref(flagEven));
string zerosString = zeros.get();
string evensString = evens.get();
cout << "these are the zeros " << zerosString << endl;
cout << "these are the evens " << evensString << endl;
this_thread::sleep_for(chrono::seconds(10));
return 0;
}

synchronization issue with 3 threads and 2 shared resources in c++11

i have 3 thread and 2 shared resources, which need some locking...i tried to illustrate the resources with 2 buffers...
- thread 1 can only access resource 1
- thread 2 can access resource 1 and 2
- thread 3 can access resource 1 and 2
can someone tell me why the following locking fails? since thread2 and thread3 will access resource 1 and 2...i thought i could use try_lock? ...it seems the issue pops up, when thread2 and thread3 is only able to lock 1 mutex at a time...any idea?
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;
class SynchronizationTest {
private:
mutex lock_r1;
mutex lock_r2;
vector<pair<string, int>> buffer_r1;
vector<pair<string, int>> buffer_r2;
unsigned int buffer_r1_max_size;
unsigned int buffer_r2_max_size;
bool buffer_r1_inc_element(const string &thread_id) {
if (buffer_r1.size() == buffer_r1_max_size) {
return true;
}
if (buffer_r1.size() == 0) {
buffer_r1.push_back(make_pair(thread_id, 0));
}
else {
int last_val = buffer_r1.back().second;
buffer_r1.push_back(make_pair(thread_id, ++last_val));
}
return false;
}
bool buffer_r2_inc_element(const string &thread_id) {
if (buffer_r2.size() == buffer_r2_max_size) {
return true;
}
if (buffer_r2.size() == 0) {
buffer_r2.push_back(make_pair(thread_id, 0));
}
else {
int last_val = buffer_r2.back().second;
buffer_r2.push_back(make_pair(thread_id, ++last_val));
}
return false;
}
public:
SynchronizationTest(int buff_r1_size, int buff_r2_size) : buffer_r1_max_size(buff_r1_size),
buffer_r2_max_size(buff_r2_size) {}
void thread1() {
bool buffer_r1_full = false;
while (!buffer_r1_full) {
{
unique_lock<mutex> l(lock_r1, std::defer_lock);
if (l.try_lock()) {
buffer_r1_full = buffer_r1_inc_element("thread1");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void thread2() {
bool buffer_r1_full = false;
bool buffer_r2_full = false;
while (!buffer_r1_full || !buffer_r2_full) {
{
unique_lock<mutex> lock1(lock_r1, defer_lock);
unique_lock<mutex> lock2(lock_r2, defer_lock);
int result = try_lock(lock1, lock2);
if(result == -1) {
buffer_r1_full = buffer_r1_inc_element("thread2");
buffer_r2_full = buffer_r2_inc_element("thread2");
}
else if(result != 0) {
buffer_r1_full = buffer_r1_inc_element("thread2");
}
else if(result != 1) {
buffer_r2_full = buffer_r2_inc_element("thread2");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void thread3() {
bool buffer_r1_full = false;
bool buffer_r2_full = false;
while (!buffer_r1_full || !buffer_r2_full) {
{
unique_lock<mutex> lock1(lock_r1, defer_lock);
unique_lock<mutex> lock2(lock_r2, defer_lock);
int result = try_lock(lock1, lock2);
if(result == -1) {
buffer_r1_full = buffer_r1_inc_element("thread3");
buffer_r2_full = buffer_r2_inc_element("thread3");
}
else if(result != 0) {
buffer_r1_full = buffer_r1_inc_element("thread3");
}
else if(result != 1) {
buffer_r2_full = buffer_r2_inc_element("thread3");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void print_buffer() {
for_each(buffer_r1.begin(), buffer_r1.end(), [](pair<string, int> p) { cout << p.first.c_str() << " " << p.second << endl; });
cout << '\n';
for_each(buffer_r2.begin(), buffer_r2.end(), [](pair<string, int> p) { cout << p.first.c_str() << " " << p.second << endl; });
}
};
int main() {
// your code goes here
SynchronizationTest st(20, 20);
thread t1(&SynchronizationTest::thread1, &st);
thread t2(&SynchronizationTest::thread2, &st);
thread t3(&SynchronizationTest::thread3, &st);
t1.join();
t2.join();
t3.join();
st.print_buffer();
return 0;
}
std::try_lock does not work that way. If it returns -1, all locks are held. If it returns a non-negative integer, no locks are held. The returned value tells which lock failed, but any locks that were locked successfully are released before try_lock returns.
problem solved:
unique_lock<mutex> lock1(lock_r1, defer_lock);
unique_lock<mutex> lock2(lock_r2, defer_lock);
bool result1 = lock1.try_lock();
bool result2 = lock2.try_lock();
if(result1 && result2) {
buffer_r1_full = buffer_r1_inc_element("thread2");
buffer_r2_full = buffer_r2_inc_element("thread2");
}
else if(result1) {
buffer_r1_full = buffer_r1_inc_element("thread2");
}
else if(result2) {
buffer_r2_full = buffer_r2_inc_element("thread2");
}

the magic cout that keep my program alive

I wrote a code to implement spin lock and mutex lock.
There is an interesting but. A magic cout can keep my program alive. If I remove the cout, my program will be sleeping forever. (This only happens in Linux. Windows is doing fine)
Any one have a clue?
#include <pthread.h>
#include <iostream>
#include <queue>
#include <sys/time.h>
#include <stdexcept>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define Tcount 10
#define TheLock MutexLock
static inline int TAS(volatile int * ptr) {
unsigned long result;
asm volatile("lock;"
"xchgl %0, %1;"
: "=r"(result), "=m"(*ptr)
: "0"(1), "m"(*ptr)
: "memory");
return result;
}
class SpinLock {
private:
int lock;
pthread_t owner;
public:
SpinLock() {
lock = 0;
}
void getLock() {
while (TAS(&lock) == 1) {
}
owner = pthread_self();
}
void releaseLock() {
if (lock == 0) {
cout << "Spin no lock" << endl;
return;
} else if (owner == pthread_self()) {
owner = NULL;
lock = 0;
} else {
throw runtime_error("Spin can't release");
}
}
};
class MutexLock {
private:
int lock;
pthread_t owner;
queue<pthread_t> q;
SpinLock qLock;
public:
MutexLock() {
lock = 0;
}
void getLock(int id) {
pthread_t self = pthread_self();
cout<<"a"<<endl;// magic cout
if (TAS(&lock) == 0) {
owner = self;
return;
}
qLock.getLock();
q.push(self);
qLock.releaseLock();
while (owner != self) {
}
}
void releaseLock(int id) {
if (lock == 0) {
cout << "Mutex no lock" << endl;
return;
} else if (owner == pthread_self()) {
qLock.getLock();
if (q.empty()) {
owner = NULL;
lock = 0;
} else {
owner = q.front();
q.pop();
}
qLock.releaseLock();
} else {
throw runtime_error("Mutex can't release");
}
}
};
TheLock lock;
int g = 0;
void* run(void* pt) {
int id = (int) pt;
for (int i = 0; i < 10000; i++) {
lock.getLock(id);
//cout<<"Thread "<<id<<" get lock, g="<<g<<endl;
int next = g + 1;
g = next;
//cout<<"Thread "<<id<<" release lock, g="<<g<<endl;
lock.releaseLock(id);
}
return NULL;
}
int main() {
pthread_t th[Tcount];
long mtime, seconds, useconds;
struct timeval start, end;
gettimeofday(&start, NULL);
for (int i = 0; i < Tcount; i++) {
pthread_create(&th[i], NULL, run, (void*) (i+10));
}
for (int i = 0; i < Tcount; i++) {
pthread_join(th[i], 0);
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
mtime = ((seconds) * 1000000 + useconds);
cout << "g=" << g << endl;
cout << "time=" << mtime << endl;
return 0;
}
You cannot implement a mutex by using the volatile keyword as the operations may not be atomic. This means that the OS might switch to a different thread before the operation has completed.
For mutex you have to use the OS. It is the only thing that knows when threads are being switched.