I'm trying to write a program, that uses threads. The thread should sleep and wait until head pointer of Stack changes, then do some stuff and sleep again. However, my thread keeps hanging on wait function, and my program hangs with it, waiting for statement to change. But it won't, because whole program waiting to thread complete. So.. Here is my code, it's working, when I put the thread join into destructor, but I want it to run before the pushes happening, so it can notice changes during run, and then when int main() completed, safely terminated.
#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <condition_variable>
struct Node {
int data;
Node* next;
};
class Stack {
private:
Node* head;
std::mutex mtx;
std::thread writeThread;
std::ofstream outFile;
Node* prevHead;
std::condition_variable cv;
public:
Stack() {
head = nullptr;
prevHead = nullptr;
outFile.open("C:\\Users\\mayday\\Desktop\\stack.txt");
writeThread = std::thread(&Stack::WriteStack, this);
}
~Stack() {
writeThread.join();
}
void Push(int data) {
std::unique_lock<std::mutex> lock(mtx);
Node* newNode = new Node();
newNode->data = data;
newNode->next = head;
head = newNode;
lock.unlock();
cv.notify_one();
}
void WriteStack() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return head != prevHead; });
Node* temp = head;
while (temp != nullptr) {
outFile << temp->data << " ";
temp = temp->next;
}
outFile << std::endl;
outFile.close();
prevHead = head;
lock.unlock();
}
}
};
int main() {
Stack s;
s.Push(1);
s.Push(2);
s.Push(3);
return 0;
}
I tried creating new void method for thread join() to execute it from main, but if I implement it before Push(), then it will sleep forever and do nothing. Even with calls from destructor, it's just doing stuff and keeps freezing waiting on condition changes. Of course I can provide it a way out by checking head == prevHead in the end, and then breaking it, but I want it just to sleep in background, until it will be notified for changes. How can I overcome this freeze?
Sorry for my bad english.
A few things:
As I called out the comments, there's no reason to explicitly invoke lock.unlock() since the destructor of unique_lock will do that for you.
Also, I tend to favor notify_all() instead of notify_one() just so I don't have to think about what additional edge cases that might create.
But neither of the above issues are the reason for your hang.
Your code is hanging because there's nothing to signal to the thread to exit. Your main thread just hangs on a call to writeThread.join() in the Stack destructor waiting for the worker thread to exit. And your worker thread is just hanging out waiting to be notified again.
Add a new bool member to your class called needToExit initialized to false in the constructor.
In the destructor, set this variable to true (while under a mutex lock) and then signal the cv.
In your thread, check this variable as an exit condition.
Update. Based on your comments, I also added a change to Push such that it will wait for the worker thread to finish processing a previous head pointer change. A cv.notify_all call is made from the thread when to signal back to the main thread each time it finishes processing a head change.
class Stack {
private:
Node* head;
std::mutex mtx;
std::thread writeThread;
std::ofstream outFile;
Node* prevHead;
std::condition_variable cv;
bool needToExit; // DECLARE THIS
public:
Stack() {
head = nullptr;
prevHead = nullptr;
needToExit = false; // INIT TO FALSE
outFile.open("C:\\Users\\jselbie\\Desktop\\stack.txt");
writeThread = std::thread(&Stack::WriteStack, this);
}
~Stack() {
{
std::unique_lock<std::mutex> lock(mtx);
needToExit = true; // SET UNDER LOCK IN DESTRUCTOR
}
cv.notify_all(); // signal cv to wake up thread to check condition
writeThread.join();
}
void Push(int data) {
std::cout << "push(" << data << ")\n";
// wait for any previous head change to
// be picked up and processed by the thread
cv.wait(lock, [this]() {
return (head == prevHead)
});
std::unique_lock<std::mutex> lock(mtx);
Node* newNode = new Node();
newNode->data = data;
newNode->next = head;
head = newNode;
cv.notify_all();
}
void WriteStack() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() {
// CHECK needToExit in addition to pointer change
return ((head != prevHead) || needToExit);
});
if (head != prevHead) {
Node* temp = head;
while (temp != nullptr) {
outFile << temp->data << " ";
temp = temp->next;
}
outFile << std::endl;
outFile.close();
prevHead = head;
cv.notify_all(); // notify main thread
}
if (needToExit)
break; // EXIT THREAD WHEN needtoExit==true
}
}
}
};
Related
In the chapter 6 of C++ Concurrency in Action, it implements a thread-safe queue. Here is the complete code. But I find there may be something wrong with its use of condition variable.
std::unique_lock<std::mutex> wait_for_data()
{
std::unique_lock<std::mutex> head_lock(head_mutex);
data_cond.wait(head_lock, [&] {return head.get() != queue::get_tail(); });
return std::move(head_lock);
}
void push(T new_value)
{
std::shared_ptr<T> new_data(
std::make_shared<T>(std::move(new_value)));
std::unique_ptr<node> p(new node);
{
std::lock_guard<std::mutex> tail_lock(tail_mutex);
tail->data = new_data;
node *const new_tail = p.get();
tail->next = std::move(p);
tail = new_tail;
}
data_cond.notify_one();
}
The consuming part locks head_mutex, but the producing part locks tail_mutex, possibly causing the consumer to miss notifications. Am I rihgt?
I need to write a void push(const T& val) implementation for lock free stack.
The problem is that compare_exchange_weak expects non atomic node* but I must use std::atomic<node*> next field instead of regular node* next.
I tried to solve this problem by doing this
void push(const T& val) {
node* new_node = new node(val);
node* local_next = new_node->next.load();
while (!head.compare_exchange_weak(local_next, new_node));
}
But creating if local_next makes things even worse. I tested 2 variants of code. The first one has non-atomic field node* next and I lost about 20-30 elements in the test code below. And using the second variant I got a deadlock.
Test code:
#include <iostream>
#include <thread>
#include <atomic>
#include "lock_free_stack.h"
using namespace std;
void test(lock_free_stack<int>& st, atomic<int>& sum) {
st.push(1);
shared_ptr<int> val(st.pop());
while (val == nullptr) { }
sum.store(sum.load() + *val);
}
int main(int argc, const char * argv[]) {
atomic<int> sum;
sum.store(0);
for (int i = 0; i < 100; ++i) {
lock_free_stack<int> st;
thread t1(test, ref(st), ref(sum));
thread t2(test, ref(st), ref(sum));
thread t3(test, ref(st), ref(sum));
thread t4(test, ref(st), ref(sum));
thread t5(test, ref(st), ref(sum));
thread t6(test, ref(st), ref(sum));
thread t7(test, ref(st), ref(sum));
thread t8(test, ref(st), ref(sum));
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
}
if (sum.load() == 800) {
cout << "CORRECT" << endl;
} else {
cout << "TIME TO REWRITE STACK " << sum << endl;
}
return 0;
}
And the code of my lock free stack (first variant):
#ifndef lock_free_stack_hard_lock_free_stack_h
#define lock_free_stack_hard_lock_free_stack_h
template <typename T>
class lock_free_stack {
private:
struct node {
node* next;
std::shared_ptr<T> value;
node (const T& val) : value(std::make_shared<T>(val)) { }
};
std::atomic<node*> head;
std::shared_ptr<T> default_value;
public:
lock_free_stack() : head(nullptr), default_value(std::make_shared<T>()) { }
void push(const T& val) {
node* new_node = new node(val);
new_node->next = head.load();
while (!head.compare_exchange_weak(new_node->next, new_node));
}
std::shared_ptr<T> pop() {
node* old_head = head.load();
while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
if (old_head) {
return old_head->value;
} else {
return std::shared_ptr<T>();
}
}
};
#endif
And the second variant:
#ifndef lock_free_stack_hard_lock_free_stack_h
#define lock_free_stack_hard_lock_free_stack_h
template <typename T>
class lock_free_stack {
private:
struct node {
std::atomic<node*> next;
std::shared_ptr<T> value;
node (const T& val) : value(std::make_shared<T>(val)) { }
};
std::atomic<node*> head;
std::shared_ptr<T> default_value;
public:
lock_free_stack() : head(nullptr), default_value(std::make_shared<T>()) { }
void push(const T& val) {
node* new_node = new node(val);
new_node->next = head.load();
node* local_next = new_node->next.load();
while (!head.compare_exchange_weak(local_next, new_node));
}
std::shared_ptr<T> pop() {
node* old_head = head.load();
while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
if (old_head) {
return old_head->value;
} else {
return std::shared_ptr<T>();
}
}
};
#endif
So the final question is how to create that local_next correctly?
Thank you.
One problem with the test is the line sum.store(sum.load() + *val);
Use atomic ops, such as sum += *val;
The first variant is garbage, because you can't guarantee atomicity on stores to node::next pointer. It would possible to use memory fence/barrier with non-atomic next-pointers, but I wouldn't trust such implementation.
Your second variant is more close to correct implementation.
However you forgot the most important thing from the push() CAS-loop:
void push(const T& val) {
node* new_node = new node(val);
new_node->next = head.load();
node* local_next = new_node->next.load();
while (!head.compare_exchange_weak(local_next, new_node));
}
Here the code allocates new node, loads and stores head pointer to new_node->next. Next, code saves already known stack head pointer value to local_next. (unnecessary step) Then code tries update stack head to new_node without updating new_node->next. This would be fine you were on singe core machine running single thread without pre-emption and the CAS would succeed 100% of time. ;)
When the CAS fails, it loads current fresh value of head into local_next and the code is stuck in infinite loop, because new_node will never be equal to local_next. So you got the last part wrong.
To make a functional CAS-loop the failing thread must reload and recompute what ever data it was trying to update. This means you must update new_node->next from head before re-trying the CAS.
This does not solve the ABA problem of CAS-loops, but I leave it out from my answer. I suggest reading more about CAS-operation and its pitfalls.
Because CAS operation does a load operation the fix is very simple, just store local_next to new_node->next after failing CAS.
Here is more valid (untested) version:
node* new_node = new node(val);
node* local_head = head.load();
new_node->next.store(local_head);
while(!head.compare_exchange_weak(local_head, new_node) {
new_node->next.store(local_head);
}
You'll to do similiar thing to your pop() implementation.
I am told that the code in insert() isn't thread-safe because an interwoven thread can set head to node after another thread does, effectively loosing a link to one node. But no matter how many times I run this program I keep getting 2 as the number of nodes instead of 1. Why is that?
#include <functional>
#include <iostream>
#include <thread>
#include <chrono>
struct List
{
struct Node
{
int data{0};
Node* next{0};
};
Node* head{0};
void insert(int n)
{
Node* node = new Node{n};
node->next = head;
head = node;
}
};
int Count(List& list)
{
int count = 0;
for (List::Node* head = list.head; head != nullptr; head = head->next)
count++;
return count;
}
int main()
{
List i;
std::thread t1(&List::insert, &i, 5);
std::thread t2(&List::insert, &i, 3);
t1.join();
t2.join();
std::cout << Count(i);
}
Demo
I've produced a simple implementation of the lockless (lockfree) queue using the new std::atomic in C++11. I can't see what I'm doing wrong here.
#include <atomic>
template<typename T>
class lockless_queue
{
public:
template<typename DataType>
struct node
{
node(const DataType& data)
: data(data), next(nullptr) {}
DataType data;
node* next;
};
lockless_queue()
: head_(nullptr) {}
void produce(const T &data)
{
node<T>* new_node = new node<T>(data);
// put the current value of head into new_node->next
new_node->next = head_.load(std::memory_order_relaxed);
// now make new_node the new head, but if the head
// is no longer what's stored in new_node->next
// (some other thread must have inserted a node just now)
// then put that new head into new_node->next and try again
while(!std::atomic_compare_exchange_weak_explicit(
&head_,
&new_node->next,
new_node,
std::memory_order_release,
std::memory_order_relaxed)) {}
}
node<T>* consume_all()
{
// Reset queue and return head atomically
return head_.exchange(nullptr, std::memory_order_consume);
}
private:
std::atomic<node<T>*> head_;
};
// main.cpp
#include <iostream>
int main()
{
lockless_queue<int> s;
s.produce(1);
s.produce(2);
s.produce(3);
auto head = s.consume_all();
while (head)
{
auto tmp = head->next;
std::cout << tmp->data << std::endl;
delete head;
head = tmp;
}
}
And my output:
2
1
Segmentation fault (core dumped)
Can I have some pointers where to look or an indication what I could be doing wrong?
Thanks!
You are dereferencing tmp instead of head:
while (head)
{
auto tmp = head->next;
std::cout << tmp->data << std::endl;
delete head;
head = tmp;
}
should be:
while (head)
{
std::cout << head->data << std::endl;
auto tmp = head->next;
delete head;
head = tmp;
}
This is why 3 doesn't appear in your output and Segmentation fault does.
You have another error in your code that won't show up until you start trying to perform concurrent enqueues. If your compare_exchange_weak_explicit fails, that implies that another thread managed to change the head pointer, and as such before you can try your CAS again, you need to re-load the new value of the head pointer into your new_node->next. The following will do the trick:
while(!std::atomic_compare_exchange_weak_explicit(
&head_,
&new_node->next,
new_node,
std::memory_order_release,
std::memory_order_relaxed)) {
new_node->next = head_.load(std::memory_order_relaxed);
}
So I have the below snippet of code...
for(int k=0; k<10; k++){
State newState;
newState = queue.dequeue();
//...do stuff with `newState`...
}
The problem I'm having is that after hours of testing, I realized that newState dequeues the exact same instance of State every time the code gets to newState = queue.dequeue(), rather than popping a new element out in a FIFO fashion... I can't seem to figure out what exactly I'm doing wrong :/
Below is my Queue class .h and .cpp code...
//queue.h
#include <iostream>
#include <stdio.h>
#include "state.h"
using namespace std;
// Node class
class Node {
public:
State elem;
Node* next;
Node() {}
State Elem() { return elem; }
Node* Next() { return next; }
};
// Queue class
class Queue {
Node *head;
Node *tail;
public:
Queue();
~Queue();
bool isEmpty();
int size();
void enqueue(State);
State dequeue();
};
and my .cpp file...
//queue.cpp
#include "queue.h"
#include <iostream>
Queue::Queue(){
head = NULL;
tail = NULL;
}
Queue::~Queue(){
if(!isEmpty()){
while(head){
Node *del = head;
head = head->next;
delete del;
}
}
}
bool Queue::isEmpty(){
return head == NULL;
}
int Queue::size(){
int count = 0;
if(isEmpty()){
return count;
} else {
Node *temp = head;
while(temp){
temp = temp->next;
count++;
}
delete temp;
return count;
}
}
//insert s to back of queue
void Queue::enqueue(State s){
Node* newNode = new Node();
newNode->elem = s;
newNode->next = NULL;
if(s.checkZ()){
cout << "Puzzle solved!" << endl << endl;
exit(0);
}
if(head==NULL){
head = tail = newNode;
} else {
tail->next = newNode;
tail = newNode;
}
}
//remove State from front of queue
State Queue::dequeue(){
if(isEmpty()){
cout << "This puzzle has no solution." << endl;
cout << "Exiting program..." << endl << endl;
exit(0);
} else {
State ret = head->Elem();
Node *del = head;
head = head->next;
delete del;
return ret;
}
}
Thanks in advance for the help, and I'd be glad to show any other snippets of my code per request
EDIT: here's my copy constructor for State...
State::State(const State &rhs){
pieces = rhs.pieces;
pieceCount = rhs.pieceCount;
rows = rhs.rows; cols = rhs.cols;
currentState = rhs.currentState;
prevStates = rhs.prevStates;
prevStateCount = rhs.prevStateCount;
moves = rhs.moves;
moveCount = rhs.moveCount;
}
In size()
delete temp;
while not dangerous, because temp is null at this point, it looks suspicious. You can also remove the if/else because count is not incremented when head is null.
Otherwise I cannot see why it should return the same element again and again. The only reason could be some code around, which inserts the same element more than once.