So this is my implementation of a Logical Ring Buffer with Semaphore Synchronization - it is an assignment and most everything here is as described in the book, concerning the implementation of the buffer and semaphores.
Funny thing is this will throw some odd errors most notably Microsoft C++ exception: std::system_error at memory location 0x... for which I can't really find anything online as to what the cause of this is. I assume it might be the accessing of a global variable, but 9/10 times the program will run successfully (assuming I tell VS to continue), the times it does not it is only the last comparison of integers at index 99 of the 2 int arrays.
This is the main method and producer / consumer functions. There is some global variable declaration and initialization, then the Pro/Con threads are created, started, and waited on, finally the results are compared.
#include <iostream> // cout, cin, ignore
#include <thread> // thread, join
#include <random> // rand
/* the number of 'messages' to pass between processes */
const size_t NUMBER_OF_MESSAGES = 100;
/* integer arrays for checking messages after passing */
int PRODUCED_MESSAGES[NUMBER_OF_MESSAGES];
int CONSUMED_MESSAGES[NUMBER_OF_MESSAGES];
/* the logical ring buffer for thread message passing */
LogicalRingBuffer BUFF; // not initiaslized yet ...
void producer_process() {
for (size_t i = 0; i < NUMBER_OF_MESSAGES; i++) {
PRODUCED_MESSAGES[i] = rand();
BUFF.insert(PRODUCED_MESSAGES[i]);
}
}
void consumer_process() {
for (size_t i = 0; i < NUMBER_OF_MESSAGES; i++) {
CONSUMED_MESSAGES[i] = BUFF.remove();
}
}
int main(int agrc, char* argv[]) {
BUFF = LogicalRingBuffer(); /* initializes the buffer */
/* creating the producer and consumer process threads */
std::thread t1(producer_process), t2(consumer_process);
/* wait for both threads to complete before comparisons */
t1.join();
t2.join();
/* iterating through the contents of both integer arrays */
for (size_t i = 0; i < NUMBER_OF_MESSAGES; i++) {
/* printing the contents of the arrays to terminal */
std::cout << "[" << i << "] " << PRODUCED_MESSAGES[i]
<< " <-> " << CONSUMED_MESSAGES[i] << std::endl;
/* inform user and exit program if comparison fails */
if (PRODUCED_MESSAGES[i] != CONSUMED_MESSAGES[i]) {
std::cout << "SYNCHRONIZATION FAILURE!" << std::endl;
std::cin.ignore(); return -1;
}
}
/* inform user of succesful message passing results */
std::cout << "ACTION COMPLETED SUCCESFULLY" << std::endl;
std::cin.ignore(); return 0;
}
And this is the Buffer/Semaphore implementation. I tried to follow the book to a T, and given that almost every time this runs it is successful (other than throwing errors during runtime) I think the synchronicity is stable in this.
struct LogicalRingBuffer{
private:
/* the standard size of the ring buffer */
const static size_t SIZEOF_RING_BUFFER = 10;
/* buffer array and pointers for iteration */
int BUFFER[SIZEOF_RING_BUFFER], *head, *tail;
/* inserts data into the buffer, and recycles the tail pointer */
void push(int data) {
/* insert data into the buffer, increment tail pointer */
*tail = data;
++tail;
/* if tail pointing at end of BUFFER, reset to the front */
if (tail == BUFFER + (SIZEOF_RING_BUFFER - 1)) tail = BUFFER;
}
/* removes data from the buffer, and recycles the head pointer */
int pull() {
/* remove data from the buffer, increment head pointer */
int R = *head;
++head;
/* if head pointing at end of BUFFER, reset to the front */
if (head == BUFFER + (SIZEOF_RING_BUFFER - 1)) head = BUFFER;
/* return the integer data value */
return R;
}
struct Semaphore {
/* the counting value, number of resources */
int count{ NULL };
/* examines resources, holds until ready */
void wait() {
while (count <= 0); //busy wait
--count;
}
/* releases aquired resource (increment) */
void signal() {
++count;
}
} empty, full, mutex; /* Semaphores for Synchronization */
public:
/* initializer for LogicalRingBuffer struct */
LogicalRingBuffer() {
head = tail = BUFFER; // all pointers at BUFFER[0]
empty.count = SIZEOF_RING_BUFFER; // number of open positions
mutex.count = 1; // a binary semaphore, mutex
full.count = 0; // number of used positions
}
/* semaphore synchronized insertion of data */
void insert(int data) {
empty.wait(); // decrements available positions in buff
mutex.wait(); // waits to gain mutual exlusion lock
push(data); // pushes the data into the ring buff
mutex.signal(); // releases the mutual exclusion lock
full.signal(); // increments count of buffered datums
}
/* semaphore synchronized removal of data */
int remove() {
int data{ NULL }; // initialize return data
full.wait(); // decrements count of buffered items
mutex.wait(); // waits to gain mutual exlusion lock
data = pull(); // pulls the data from the ring buff
mutex.signal(); // releases the mutual exlusion lock
empty.signal(); // increments avilable positions in buff
return data; // return integer data
}
};
I guess I just want this to run without any hiccups, so am I missing something here? Because I'm fairly certain the logic is correct and this is something that maybe Visual Studio does, or who knows...
The two threads you create both attempt to lock the mutex by calling Semaphore::wait(). However, there is nothing preventing the threads from reading and writing to the mutex's count variable at the same time. This results in undefined behavior which can cause the system errors you are seeing. You are on the right track in your implementation, but at some point the program has to go to the OS for actual thread-safety. You need to use either the standard library's thread-safety mechanisms to protect concurrent access to this variable or your OS's low-level mechanisms, like Windows's critical sections.
C++11's standard library implements std::mutex which can be used to protect concurrent access to variables which are shared between threads. However, it's implementation is likely as high-level as the one you are trying to implement yourself, so it would make more sense to discard your own implementation in favor of the standard library's.
This was my solution to the problem, compiling in Cygwin GCC with: g++ *.cpp -std=c++11 -w and rewriting the Semaphore::wait() method as follows:
void wait() {
/* the next line was added to avoid count being accessed simultaneously, vvv */
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); // <---
while (count <= 0); /* BUSY WAIT */
--count;
}
I plan on running more tests, but so far I have had exactly 0 conflicts when using the thread sleep.
Related
I'm trying to write a thread pool in c++ that fulfills the following criteria:
a single writer occasionally writes a new input value, and once it does, many threads concurrently access this same value, and each spit out a random floating point number.
each worker thread uses the same function, so there's no reason to build a thread-safe queue for all the different functions. I store the common function inside the thread_pool class.
these functions are by far the most computationally-intensive aspect of the program. Any locks that prevent these functions from doing their work is the primary thing I'm trying to avoid.
the floating point output from all these functions is simply averaged.
the user has a single function called thread_pool::start_work that changes this shared input, and tells all the workers to work for a fixed number of tasks.
thread_pool::start_work returns std::future
Below is what I have so far. It can be built and run with g++ test_tp.cpp -std=c++17 -lpthread; ./a.out Unfortunately it either deadlocks or does the work too many (or sometimes too few) times. I am thinking that it's because m_num_comps_done is not thread-safe. There are chances that all the threads skip over the last count, and then they all end up yielding. But isn't this variable atomic?
#include <vector>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <queue>
#include <atomic>
#include <future>
#include <iostream>
#include <numeric>
/**
* #class join_threads
* #brief RAII thread killer
*/
class join_threads
{
std::vector<std::thread>& m_threads;
public:
explicit join_threads(std::vector<std::thread>& threads_)
: m_threads(threads_) {}
~join_threads() {
for(unsigned long i=0; i < m_threads.size(); ++i) {
if(m_threads[i].joinable())
m_threads[i].join();
}
}
};
// how remove the first two template parameters ?
template<typename func_input_t, typename F>
class thread_pool
{
using func_output_t = typename std::result_of<F(func_input_t)>::type;
static_assert( std::is_floating_point<func_output_t>::value,
"function output type must be floating point");
unsigned m_num_comps;
std::atomic_bool m_done;
std::atomic_bool m_has_an_input;
std::atomic<int> m_num_comps_done; // need to be atomic? why?
F m_f; // same function always used
func_input_t m_param; // changed occasionally by a single writer
func_output_t m_working_output; // many reader threads average all their output to get this
std::promise<func_output_t> m_out;
mutable std::shared_mutex m_mut;
mutable std::mutex m_output_mut;
std::vector<std::thread> m_threads;
join_threads m_joiner;
void worker_thread() {
while(!m_done)
{
if(m_has_an_input){
if( m_num_comps_done.load() < m_num_comps - 1 ) {
std::shared_lock<std::shared_mutex> lk(m_mut);
func_output_t tmp = m_f(m_param); // long time
m_num_comps_done++;
// quick
std::lock_guard<std::mutex> lk2(m_output_mut);
m_working_output += tmp / m_num_comps;
}else if(m_num_comps_done.load() == m_num_comps - 1){
std::shared_lock<std::shared_mutex> lk(m_mut);
func_output_t tmp = m_f(m_param); // long time
m_num_comps_done++;
std::lock_guard<std::mutex> lk2(m_output_mut);
m_working_output += tmp / m_num_comps;
m_num_comps_done++;
try{
m_out.set_value(m_working_output);
}catch(std::future_error& e){
std::cout << "future_error caught: " << e.what() << "\n";
}
}else{
std::this_thread::yield();
}
}else{
std::this_thread::yield();
}
}
}
public:
/**
* #brief ctor spawns working threads
*/
thread_pool(F f, unsigned num_comps)
: m_num_comps(num_comps)
, m_done(false)
, m_has_an_input(false)
, m_joiner(m_threads)
, m_f(f)
{
unsigned const thread_count=std::thread::hardware_concurrency(); // should I subtract one?
try {
for(unsigned i=0; i<thread_count; ++i) {
m_threads.push_back( std::thread(&thread_pool::worker_thread, this));
}
} catch(...) {
m_done=true;
throw;
}
}
~thread_pool() {
m_done=true;
}
/**
* #brief changes the shared data member,
* resets the num_comps_left variable,
* resets the accumulator thing to 0, and
* resets the promise object
*/
std::future<func_output_t> start_work(func_input_t new_param) {
std::unique_lock<std::shared_mutex> lk(m_mut);
m_param = new_param;
m_num_comps_done = 0;
m_working_output = 0.0;
m_out = std::promise<func_output_t>();
m_has_an_input = true; // only really matters just after initialization
return m_out.get_future();
}
};
double slowSum(std::vector<double> nums) {
// std::this_thread::sleep_for(std::chrono::milliseconds(200));
return std::accumulate(nums.begin(), nums.end(), 0.0);
}
int main(){
// construct
thread_pool<std::vector<double>, std::function<double(std::vector<double>)>>
le_pool(slowSum, 1000);
// add work
auto ans = le_pool.start_work(std::vector<double>{1.2, 3.2, 4213.1});
std::cout << "final answer is: " << ans.get() << "\n";
std::cout << "it should be 4217.5\n";
return 1;
}
You check the "done" count, then get the lock. This allows multiple threads to be waiting for the lock. In particular, there might not be a thread that enters the second if body.
The other side of that is because you have all threads running all the time, the "last" thread may not get access to its exclusive section early (before enough threads have run) or even late (because additional threads are waiting at the mutex in the first loop).
To fix the first issue, since the second if block has all of the same code that is in the first if block, you can have just one block that checks the count to see if you've reached the end and should set the out value.
The second issue requires you to check m_num_comps_done a second time after acquiring the mutex.
I am trying to implement an array-based ring buffer that is thread-safe for multiple producers and a single consumer. The main idea is to have atomic head and tail indices. When pushing an element to the queue, the head is increased atomically to reserve a slot in the buffer:
#include <atomic>
#include <chrono>
#include <iostream>
#include <stdexcept>
#include <thread>
#include <vector>
template <class T> class MPSC {
private:
int MAX_SIZE;
std::atomic<int> head{0}; ///< index of first free slot
std::atomic<int> tail{0}; ///< index of first occupied slot
std::unique_ptr<T[]> data;
std::unique_ptr<std::atomic<bool>[]> valid; ///< indicates whether data at an
///< index has been fully written
/// Compute next index modulo size.
inline int advance(int x) { return (x + 1) % MAX_SIZE; }
public:
explicit MPSC(int size) {
if (size <= 0)
throw std::invalid_argument("size must be greater than 0");
MAX_SIZE = size + 1;
data = std::make_unique<T[]>(MAX_SIZE);
valid = std::make_unique<std::atomic<bool>[]>(MAX_SIZE);
}
/// Add an element to the queue.
///
/// If the queue is full, this method blocks until a slot is available for
/// writing. This method is not starvation-free, i.e. it is possible that one
/// thread always fills up the queue and prevents others from pushing.
void push(const T &msg) {
int idx;
int next_idx;
int k = 100;
do {
idx = head;
next_idx = advance(idx);
while (next_idx == tail) { // queue is full
k = k >= 100000 ? k : k * 2; // exponential backoff
std::this_thread::sleep_for(std::chrono::nanoseconds(k));
} // spin
} while (!head.compare_exchange_weak(idx, next_idx));
if (valid[idx])
// this throws, suggesting that two threads are writing to the same index. I have no idea how this is possible.
throw std::runtime_error("message slot already written");
data[idx] = msg;
valid[idx] = true; // this was set to false by the reader,
// set it to true to indicate completed data write
}
/// Read an element from the queue.
///
/// If the queue is empty, this method blocks until a message is available.
/// This method is only safe to be called from one single reader thread.
T pop() {
int k = 100;
while (is_empty() || !valid[tail]) {
k = k >= 100000 ? k : k * 2;
std::this_thread::sleep_for(std::chrono::nanoseconds(k));
} // spin
T res = data[tail];
valid[tail] = false;
tail = advance(tail);
return res;
}
bool is_full() { return (head + 1) % MAX_SIZE == tail; }
bool is_empty() { return head == tail; }
};
When there is a lot of congestion, some messages get overwritten by other threads. Hence there must be something fundamentally wrong with what I'm doing here.
What seems to be happening is that two threads are acquiring the same index to write their data to. Why could that be?
Even if a producer were to pause just before writing it's data, the tail could not increase past this threads idx and hence no other thread should be able to overtake and claim that same idx.
EDIT
At the risk of posting too much code, here is a simple program that reproduces the problem. It sends some incrementing numbers from many threads and checks whether all numbers are received by the consumer:
#include "mpsc.hpp" // or whatever; the above queue
#include <thread>
#include <iostream>
int main() {
static constexpr int N_THREADS = 10; ///< number of threads
static constexpr int N_MSG = 1E+5; ///< number of messages per thread
struct msg {
int t_id;
int i;
};
MPSC<msg> q(N_THREADS / 2);
std::thread threads[N_THREADS];
// consumer
threads[0] = std::thread([&q] {
int expected[N_THREADS] {};
for (int i = 0; i < N_MSG * (N_THREADS - 1); ++i) {
msg m = q.pop();
std::cout << "Got message from T-" << m.t_id << ": " << m.i << std::endl;
if (expected[m.t_id] != m.i) {
std::cout << "T-" << m.t_id << " unexpected msg " << m.i << "; expected " << expected[m.t_id] << std::endl;
return -1;
}
expected[m.t_id] = m.i + 1;
}
});
// producers
for (int id = 1; id < N_THREADS; ++id) {
threads[id] = std::thread([id, &q] {
for (int i = 0; i < N_MSG; ++i) {
q.push(msg{id, i});
}
});
}
for (auto &t : threads)
t.join();
}
I am trying to implement an array-based ring buffer that is thread-safe for multiple producers and a single consumer.
I assume you are doing this as a learning exercise. Implementing a lock-free queue yourself is most probably the wrong thing to do if you want to solve a real problem.
What seems to be happening is that two threads are acquiring the same index to write their data to. Why could that be?
The combination of that producer spinlock with the outer CAS loop does not work in the intended way:
do {
idx = head;
next_idx = advance(idx);
while (next_idx == tail) { // queue is full
k = k >= 100000 ? k : k * 2; // exponential backoff
std::this_thread::sleep_for(std::chrono::nanoseconds(k));
} // spin
//
// ...
//
// All other threads (producers and consumers) can progress.
//
// ...
//
} while (!head.compare_exchange_weak(idx, next_idx));
The queue may be full when the CAS happens because those checks are performed independently. In addition, the CAS may succeed because the other threads may have advanced head to exactly match idx.
I have a big file, and i have to read it by chunk. Each time when i read a chunk, i have to do some time consuming operation, so i think multithread reading might help, each thread reads a chunk one by one and does its operation. here is my code in c++11
#include<iostream>
#include<fstream>
#include <condition_variable>
#include <mutex>
#include <thread>
using namespace std;
const int CHAR_PER_FILE = 1e8;
const int NUM_THREAD = 2;
int order = -1;
bool is_reading = false;
mutex mtx;
condition_variable file_not_reading;
void partition(ifstream& is)
{
while (is.peek() != EOF)
{
unique_lock<mutex> lock(mtx);
while (is_reading)
file_not_reading.wait(lock);
is_reading = true;
char *c = new char[CHAR_PER_FILE];
is.read(c, CHAR_PER_FILE);
order++;
is_reading = false;
file_not_reading.notify_all();
lock.unlock();
char oc[3];
sprintf(oc, "%d", order);
this_thread::sleep_for(chrono::milliseconds(2000));//some operations that take long time
ofstream os(oc, ios::binary);
os.write(c, CHAR_PER_FILE);
delete[] c;
os.close();
}
}
int main()
{
ifstream is("bigfile.txt",ios::binary);
thread threads[NUM_THREAD];
for (int i = 0; i < NUM_THREAD; i++)
threads[i] = thread(partition, ref(is));
for (int i = 0; i < NUM_THREAD; i++)
threads[i].join();
is.close();
system("pause");
return 0;
}
But my code didn't work, it only created 4 files instead of `bigfilesize/CHAR_PER_FILE, and threads seem got stuck, how can i make it work?
Is there any c++11 multithread reading file implementation or example?
Thanks.
My advice:
Use one thread to read chunks from the file. Every time a chunk is read, post it to a request queue. It is not worth reading multithreaded as there will be internal locks/blocking reading a common resource.
Use a pool of threads. Each of them read from the queue, retrieves a chunk, execute the expensive operation and go back to wait for a new request.
The queue must be mutex protected.
Don't use more threads than the number of processing units (CPU/Cores/HyperThreads) you have.
The main caveat of the above is that it will not guarantee the processing order. You will probably need to post the results to a central place that can reorder (again central place -> must be mutex protected).
You could use task-based parallelism with std::async:
class result; // result of expensive operation
result expensive_operation(std::vector<char> const& data)
{
result r = // long computation
return r;
}
std::vector<char>::size_type BLOCK_SIZE = 4096;
std::vector<std::future<result>> partition(ifstream& in)
{
std::vector<std::future<result>> tasks;
while (!in.eof() && !in.fail())
{
std::vector<char> c(BLOCK_SIZE);
is.read(c.data(), BLOCK_SIZE);
c.resize(in.gcount());
tasks.push_back( std::async( [](std::vector<char> data)
{
return expensive_operation(data);
},
std::move(c) ));
}
return tasks;
}
int main()
{
ifstream is("bigfile.txt",ios::binary);
auto results = partition(is);
// iterate over results and do something with it
}
Does the file have to be read in "sequential" order, i.e. do the chunks have to be "operated" on in a special order? Otherwise you could e.g. make 4 threads and let each thread read 1/4 of the file (you could do this by using tellg and saving the position in e.g. a vector or variable). That way you wouldn't have to use locks.
Maybe you could tell us how the data you read in has to be evaluated.
Perhaps...
void partition(ifstream& is)
{
unique_lock<mutex> lock(mtx);
std::vector<char> c(CHAR_PER_FILE);
is.read(c.data(), CHAR_PER_FILE);
lock.unlock();
if (is.fail() && !is.eof()) return;
size_t num_bytes_read = is.gcount();
std::ostringstream oc;
oc << order;
this_thread::sleep_for(chrono::milliseconds(2000)); //take long time
if (std::ofstream os(oc, ios::binary))
os.write(c.data(), CHAR_PER_FILE);
}
Notes:
The mutex serialises the operations already - no need for a condition variable.
I've added a little input error and bytes-read handling - you should check after os.write() too, add an else for failed ofstream creation etc.
I designed one sample multithreaded application in which I read one file in one thread using circular buffer with checking isfull or not, also write same buffer to output file with checking buffer isEmpty or not.
My problem is that first thread complete its execution first so that second thread gets remaining data in buffer, so output is wrong.
Code:
/* Circular Queues */
//#include<mutex.h>
#include <iostream>
using namespace std;
#include<windows.h>
const int chunk = 512; //buffer read data
const int MAX = 2*chunk ; //queue size
unsigned int Size ;
FILE *fpOut;
FILE *fp=fopen("Test_1K.txt","rb");
//class queue
class cqueue
{
public :
static int front,rear;
static char *a;
cqueue()
{
front=rear=-1;
a=new char[MAX];
}
~cqueue()
{
delete[] a;
}
};
int cqueue::front;
int cqueue::rear;
char* cqueue::a;
DWORD WINAPI Thread1(LPVOID param)
{
int i;
fseek(fp,0,SEEK_END);
Size=ftell(fp); //Read file size
cout<<"\nsize is"<<Size;
rewind(fp);
for(i=0;i<Size;i+=chunk) //read data in chunk as buffer half size
{
while((cqueue::rear==MAX-1)); //wait until buffer is full?
if((cqueue::rear>MAX-1))
cqueue::rear=0;
else{
fread(cqueue::a,1,chunk,fp); //read data from in buffer
cqueue::rear+=chunk; //increment rear pointer of queue to indicate buffer is filled up with data
if((cqueue::front==-1))
cqueue::front=0; //update front pointer value to read data from buffer in Thread2
}
}
fclose(fp);
cout<<"\nQueue write completed\n";
return 0;
}
DWORD WINAPI Thread2(LPVOID param)
{
for(int j=0;j<Size;j+=chunk)
{
while((cqueue::front==-1)); //wait until buffer is empty
cqueue::front+=chunk; //update front pointer after read data from queue
fwrite(cqueue::a,1,chunk,fpOut); //write data file
if((cqueue::front==MAX-1))
cqueue::front=0; //update queue front pointer when it reads upto queue Max size
}
fclose(fpOut);
cout<<"\nQueue read completed\n";
return 0;
}
void startThreads()
{
DWORD threadIDs[2];
HANDLE threads[2];
fpOut=fopen("V-1.7OutFile.txt","wb");
threads[0] = CreateThread(NULL,0,Thread1,NULL,0,&threadIDs[0]);
threads[1] = CreateThread(NULL,0,Thread2,NULL,0,&threadIDs[1]);
if(threads[0] && threads[1])
{
printf("Threads Created.(IDs %d and %d)",threadIDs[0],threadIDs[1]);
}
}
void main()
{
cqueue c1;
startThreads();
system("pause");
}
Please suggest any solution for shared buffer accessing.
A major problem here is that you use static member variable, but never initialize them except in the constructor, and as you never actually construct an instance of the class the constructor will not be called.
That means when you use the static member variables they contain indeterminate values (i.e. their values will be seemingly random) you invoke undefined behavior.
I've written my own version of thread safe queue. However, when I run this program, it hangs/deadlocks itself.
Wondering, why is this locks/hangs forever.
void concurrentqueue::addtoQueue(const int number)
{
locker currentlock(lock_for_queue);
numberlist.push(number);
pthread_cond_signal(&queue_availability_condition);
}
int concurrentqueue::getFromQueue()
{
int number = 0;
locker currentlock(lock_for_queue);
if ( empty() )
{
pthread_cond_wait(&queue_availability_condition,&lock_for_queue);
}
number = numberlist.front();
numberlist.pop();
return number;
}
bool concurrentqueue::empty()
{
return numberlist.empty();
}
I've written, the class locker as RAII.
class locker
{
public:
locker(pthread_mutex_t& lockee): target(lockee)
{
pthread_mutex_lock(&target);
}
~locker()
{
pthread_mutex_unlock(&target);
}
private:
pthread_mutex_t target;
};
My writer/reader thread code is very simple. Writer thread, adds to the queue and reader thread, reads from the queue.
void * writeintoqueue(void* myqueue)
{
void *t = 0;
concurrentqueue *localqueue = (concurrentqueue *) myqueue;
for ( int i = 0; i < 10 ; ++i)
{
localqueue->addtoQueue(i*10);
}
pthread_exit(t);
}
void * readfromqueue(void* myqueue)
{
void *t = 0;
concurrentqueue *localqueue = (concurrentqueue *) myqueue;
int number = 0;
for ( int i = 0 ; i < 10 ; ++i)
{
number = localqueue->getFromQueue();
std::cout << "The number from the queue is " << number << std::endl;
}
pthread_exit(t);
}
This is definitely not safe:
if ( empty() )
{
pthread_cond_wait(&queue_availability_condition,&lock_for_queue);
}
If another thread that was not previously waiting calls getFromQueue() after addtoQueue() has signalled the condition variable and exited but before the waiting thread has aquired the lock then this thread could exit and expect the queue to have values in it. You must recheck that the queue is not empty.
Change the if into a while:
while ( empty() )
{
pthread_cond_wait(&queue_availability_condition,&lock_for_queue);
}
Reformulating spong's comment as an answer: your locker class should NOT be copying the pthread_mutex_t by value. You should use a reference or a pointer instead, e.g.:
class locker
{
public:
locker(pthread_mutex_t& lockee): target(lockee)
{
pthread_mutex_lock(&target);
}
~locker()
{
pthread_mutex_unlock(&target);
}
private:
pthread_mutex_t& target; // <-- this is a reference
};
The reason for this is that all pthreads data types should be treated as opaque types -- you don't know what's in them and should not copy them. The library does things like looking at a particular memory address to determine if a lock is held, so if there are two copies of a variable that indicates if the lock is held, odd things could happen, such as multiple threads appearing to succeed in locking the same mutex.
I tested your code, and it also deadlocked for me. I then ran it through Valgrind, and although it did not deadlock in that case (due to different timings, or maybe Valgrind only simulates one thread at a time), Valgrind reported numerous errors. After fixing locker to use a reference instead, it ran without deadlocking and without generating any errors in Valgrind.
See also Debugging with pthreads.