I'm currently programming a concurrent queue while learning how to use the multithreading features of C++11.
When the consumer calls the dequeue() function and the queue has no entries, the function should wait until another thread calls enqueue(). I'm using a condition_variable for that.
My tests worked fine with a few entries and threads, but when I'm using more (up to 100000 elements, 20 producers, only 1 consumer), I get an Access Violation inside the condition_variable::wait function:
Unbehandelte Ausnahme bei 0x5A2C7EEC (msvcr110d.dll) in Tests.exe:
0xC0000005: Zugriffsverletzung beim Lesen an Position 0xFEEEFEF6
I've been stuck with this for hours. I hope you can help me. Thank you.
The Code:
// --------------------------------------------------------------------------------------
// Concurrent Queue
// --------------------------------------------------------------------------------------
#pragma once
#include <atomic> // Atomic operations for lock-free operations
#include <mutex>
#include <condition_variable>
using namespace std;
// --------------------------------------------------------------------------------------
// Declarations
// --------------------------------------------------------------------------------------
template<typename T>
class ConcurrentQueue;
template<typename T>
class ConcurrentQueueEntry;
// --------------------------------------------------------------------------------------
// Queue
// --------------------------------------------------------------------------------------
template<typename T>
class ConcurrentQueue {
public:
ConcurrentQueue();
~ConcurrentQueue();
void enqueue(const T value);
T try_dequeue();
T dequeue();
unsigned long count() const;
private:
atomic<ConcurrentQueueEntry<T>*> front;
atomic<ConcurrentQueueEntry<T>*> rear;
atomic_ulong i_count;
mutex dequeueWaitMutex;
condition_variable dequeueWaitCV;
};
// --------------------------------------------------------------------------------------
// Entry
// --------------------------------------------------------------------------------------
template<typename T>
class ConcurrentQueueEntry {
public:
ConcurrentQueueEntry(T _value);
T value;
atomic<ConcurrentQueueEntry<T>*> next;
};
// --------------------------------------------------------------------------------------
// Exception: Queue is empty
// --------------------------------------------------------------------------------------
class EmptyQueueException {};
// --------------------------------------------------------------------------------------
// Constructors and Destructor
// --------------------------------------------------------------------------------------
// Create Queue
template<typename T>
ConcurrentQueue<T>::ConcurrentQueue()
: front(), rear(), i_count(), dequeueWaitMutex(), dequeueWaitCV()
{
i_count.store(0);
}
// Delete Queue
template<typename T>
ConcurrentQueue<T>::~ConcurrentQueue()
{
ConcurrentQueueEntry<T>* previous = this->front.load();
while(previous != NULL) {
ConcurrentQueueEntry<T>* next = previous->next.load();
delete previous;
previous = next;
}
}
// Create Entry
template<typename T>
ConcurrentQueueEntry<T>::ConcurrentQueueEntry
(T _value)
: value(_value), next(NULL)
{ }
// --------------------------------------------------------------------------------------
// Public Methods
// --------------------------------------------------------------------------------------
// Enqueue
template<typename T>
void ConcurrentQueue<T>::enqueue
(const T value)
{
// create, append
ConcurrentQueueEntry<T>* entry = new ConcurrentQueueEntry<T>(value);
ConcurrentQueueEntry<T>* former_rear = this->rear.exchange(entry);
// connect
if(former_rear == NULL) {
this->front.store(entry);
}
else {
former_rear->next.store(entry);
}
// Add
++i_count;
dequeueWaitCV.notify_one();
}
// Dequeue (aborts if queue is empty)
template<typename T>
T ConcurrentQueue<T>::try_dequeue()
{
ConcurrentQueueEntry<T>* front = this->front.load();
while(front != NULL &&
!this->front.compare_exchange_weak(front, front->next.load()));
if(front == NULL)
throw EmptyQueueException();
--i_count;
T value = front->value;
delete front;
return value;
}
// Dequeue (waits if queue is empty)
template<typename T>
T ConcurrentQueue<T>::dequeue() {
while(true) {
try {
return this->try_dequeue();
}
catch(EmptyQueueException) {
unique_lock<mutex> lock(dequeueWaitMutex);
dequeueWaitCV.wait(lock, [&] { return this->count() == 0; });
}
}
}
// Count entries
template<typename T>
unsigned long ConcurrentQueue<T>::count() const {
return this->i_count.load();
}
The Call Stack:
msvcr110d.dll!Concurrency::details::LockQueueNode::IsTicketValid() Zeile 924 C++
msvcr110d.dll!Concurrency::details::LockQueueNode::UpdateQueuePosition(Concurrency::details::LockQueueNode * pPreviousNode) Zeile 811 C++
msvcr110d.dll!Concurrency::critical_section::_Acquire_lock(void * _PLockingNode, bool _FHasExternalNode) Zeile 1193 C++
msvcr110d.dll!Concurrency::critical_section::lock() Zeile 1028 C++
msvcr110d.dll!Concurrency::details::_Condition_variable::wait(Concurrency::critical_section & _Lck) Zeile 576 C++
msvcp110d.dll!do_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx, const xtime * target) Zeile 47 C++
msvcp110d.dll!_Cnd_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx) Zeile 73 C++
Tests.exe!std::_Cnd_waitX(_Cnd_internal_imp_t * * _Cnd, _Mtx_internal_imp_t * * _Mtx) Zeile 93 C++
Tests.exe!std::condition_variable::wait(std::unique_lock<std::mutex> & _Lck) Zeile 60 C++
Tests.exe!std::condition_variable::wait<<lambda_61c2d1dffb87d02ed418fe62879bb063> >(std::unique_lock<std::mutex> & _Lck, ConcurrentQueue<long>::dequeue::__l7::<lambda_61c2d1dffb87d02ed418fe62879bb063> _Pred) Zeile 67 C++
Tests.exe!ConcurrentQueue<long>::dequeue() Zeile 156 C++
Tests.exe!<lambda_c8c79a4136723f6fef9d0a0557ed768b>::operator()() Zeile 38 C++
Tests.exe!std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()() Zeile 1152 C++
Tests.exe!std::_LaunchPad<std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >::_Run(std::_LaunchPad<std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> > * _Ln) Zeile 196 C++
Tests.exe!std::_LaunchPad<std::_Bind<0,void,<lambda_c8c79a4136723f6fef9d0a0557ed768b>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >::_Go() Zeile 187 C++
msvcp110d.dll!_Call_func(void * _Data) Zeile 52 C++
msvcr110d.dll!_callthreadstartex() Zeile 354 C
msvcr110d.dll!_threadstartex(void * ptd) Zeile 337 C
kernel32.dll!747f850d() Unbekannt
[Unten angegebene Rahmen sind möglicherweise nicht korrekt und/oder fehlen, keine Symbole geladen für kernel32.dll]
ntdll.dll!7719bf39() Unbekannt
ntdll.dll!7719bf0c() Unbekannt
Two notes up front for debugging:
0xFEEEFEF6 is 0xFEEEFEEE + 8. The 0xFEEEFEEE is a signal value which the debug runtime stores in some variables. I think that this value indicates that the owning object's destructor was already called. In contrast, before initialization, the value is 0xCDCDCDCD I think.
You can configure VS to turn off internationalization. That way you get English error messages, which are more suitable for the forum here.
Now, concerning your code, the first thing is that this is not "the code" but rather just part thereof. Distil a minimal example, please. However, in the meantime, you could make your queue class uncopyable and unassignable (using ".. = delete;"). If I'm guessing right, you will then get compile errors which come from copying the queue, probably during thread startup.
Related
I am new to C++ and multithreading applications. I want to process a long list of data (potentially several thousands of entries) by dividing its entries among a few threads. I have retrieved a ThreadPool class and a Queue class from the web (it is my first time tackling the subject). I construct the threads and populate the queue in the following way (definitions at the end of the post):
ThreadPool *pool = new ThreadPool(8);
std::vector<std::function<void(int)>> *caller =
new std::vector<std::function<void(int)>>;
for (size_t i = 0; i < Nentries; ++i)
{
caller->push_back(
[=](int j){func(entries[i], j);});
pool->PushTask((*caller)[i]);
}
delete pool;
The problem is that only a number of entries equaling the number of created threads are processed, as if the program does not wait for the queue to be empty. Indeed, if I put
while (pool->GetWorkQueueLength()) {}
just before the pool destructor, the whole list is correctly processed. However, I am afraid I am consuming too many resources by using a while loop. Moreover, I have not found anyone doing anything like it, so I think this is the wrong approach and the classes I use have some error. Can anyone find the error (if present) or suggest another solution?
Here are the classes I use. I suppose the problem is in the implementation of the destructor, but I am not sure.
SynchronizeQueue.hh
#ifndef SYNCQUEUE_H
#define SYNCQUEUE_H
#include <list>
#include <mutex>
#include <condition_variable>
template<typename T>
class SynchronizedQueue
{
public:
SynchronizedQueue();
void Put(T const & data);
T Get();
size_t Size();
private:
SynchronizedQueue(SynchronizedQueue const &)=delete;
SynchronizedQueue & operator=(SynchronizedQueue const &)=delete;
std::list<T> queue;
std::mutex mut;
std::condition_variable condvar;
};
template<typename T>
SynchronizedQueue<T>::SynchronizedQueue()
{}
template<typename T>
void SynchronizedQueue<T>::Put(T const & data)
{
std::unique_lock<std::mutex> lck(mut);
queue.push_back(data);
condvar.notify_one();
}
template<typename T>
T SynchronizedQueue<T>::Get()
{
std::unique_lock<std::mutex> lck(mut);
while (queue.empty())
{
condvar.wait(lck);
}
T result = queue.front();
queue.pop_front();
return result;
}
template<typename T>
size_t SynchronizedQueue<T>::Size()
{
std::unique_lock<std::mutex> lck(mut);
return queue.size();
}
#endif
ThreadPool.hh
#ifndef THREADPOOL_H
#define THREADPOOL_H
#include "SynchronizedQueue.hh"
#include <atomic>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
class ThreadPool
{
public:
ThreadPool(int nThreads = 0);
virtual ~ThreadPool();
void PushTask(std::function<void(int)> func);
size_t GetWorkQueueLength();
private:
void WorkerThread(int i);
std::atomic<bool> done;
unsigned int threadCount;
SynchronizedQueue<std::function<void(int)>> workQueue;
std::vector<std::thread> threads;
};
#endif
ThreadPool.cc
#include "ThreadPool.hh"
#include "SynchronizedQueue.hh"
void doNothing(int i)
{}
ThreadPool::ThreadPool(int nThreads)
: done(false)
{
if (nThreads <= 0)
{
threadCount = std::thread::hardware_concurrency();
}
else
{
threadCount = nThreads;
}
for (unsigned int i = 0; i < threadCount; ++i)
{
threads.push_back(std::thread(&ThreadPool::WorkerThread, this, i));
}
}
ThreadPool::~ThreadPool()
{
done = true;
for (unsigned int i = 0; i < threadCount; ++i)
{
PushTask(&doNothing);
}
for (auto& th : threads)
{
if (th.joinable())
{
th.join();
}
}
}
void ThreadPool::PushTask(std::function<void(int)> func)
{
workQueue.Put(func);
}
void ThreadPool::WorkerThread(int i)
{
while (!done)
{
workQueue.Get()(i);
}
}
size_t ThreadPool::GetWorkQueueLength()
{
return workQueue.Size();
}
You can push tasks saying "done" instead of setting "done" via atomic variable.
So that each thread will exit by itself when seeing "done" task, and no earlier. In destructor you only need to push these tasks and join threads. This is called "poison pill".
Alternatively, if you insist on your current design with done variable, you can wait on the same condition you already have:
std::unique_lock<std::mutex> lck(mut);
while (!queue.empty())
{
condvar.wait(lck);
}
But then you'll need to change your notify_one to notify_all, and this may be sub-optimal.
I want to process a long list of data (potentially several thousands of entries) by dividing its entries among a few threads.
You can do that with parallel algorithms, like tbb::parallel_for:
#include <tbb/parallel_for.h>
#include <vector>
void func(int entry);
int main () {
std::vector<int> entries(1000000);
tbb::parallel_for(size_t{0}, entries.size(), [&](size_t i) { func(entries[i]); });
}
If you need sequential thread ids, you can do:
void func(int element, int thread_id);
template<class C>
inline auto make_range(C& c) -> decltype(tbb::blocked_range<decltype(c.begin())>(c.begin(), c.end())) {
return tbb::blocked_range<decltype(c.begin())>(c.begin(), c.end());
}
int main () {
std::vector<int> entries(1000000);
std::atomic<int> thread_counter{0};
tbb::parallel_for(make_range(entries), [&](auto sub_range) {
static thread_local int const thread_id = thread_counter.fetch_add(1, std::memory_order_relaxed);
for(auto& element : sub_range)
func(element, thread_id);
});
}
Alternatively, there is std::this_thread::get_id.
In a single thread, I have this beautiful class that redirects all cout output to a QTextEdit
#include <iostream>
#include <streambuf>
#include <string>
#include <QScrollBar>
#include "QTextEdit"
#include "QDateTime"
class ThreadLogStream : public std::basic_streambuf<char>, QObject
{
Q_OBJECT
public:
ThreadLogStream(std::ostream &stream) : m_stream(stream)
{
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~ThreadLogStream()
{
// output anything that is left
if (!m_string.empty())
{
log_window->append(m_string.c_str());
}
m_stream.rdbuf(m_old_buf);
}
protected:
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
log_window->append(m_string.c_str());
m_string.erase(m_string.begin(), m_string.end());
}
else
m_string += v;
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
m_string.append(p, p + n);
long pos = 0;
while (pos != static_cast<long>(std::string::npos))
{
pos = m_string.find('\n');
if (pos != static_cast<long>(std::string::npos))
{
std::string tmp(m_string.begin(), m_string.begin() + pos);
log_window->append(tmp.c_str());
m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
}
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
std::string m_string;
QTextEdit* log_window;
};
However, this doesn't work if ANY thread (QThread) is initiated with a cout. This is because all pointers are messed up, and one has to use signals and slots for allowing transfer of data between the sub-thread and the main thread.
I would like to modify this class to emit a signal rather than write to a text file. This requires that this class becomes a Q_OBJECT and be inherited from one. I tried to inherit from QObject in addition to std::basic_streambuf<char> and added Q_OBJECT macro in the body but it didn't compile.
Could you please help me to achieve this? What should I do to get this class to emit signals that I can connect to and that are thread safe?
For those who need the full "working" answer, here it's. I just copied it because #GraemeRock asked for it.
#ifndef ThreadLogStream_H
#define ThreadLogStream_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QScrollBar>
#include "QTextEdit"
#include "QDateTime"
class ThreadLogStream : public QObject, public std::basic_streambuf<char>
{
Q_OBJECT
public:
ThreadLogStream(std::ostream &stream) : m_stream(stream)
{
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~ThreadLogStream()
{
// output anything that is left
if (!m_string.empty())
{
emit sendLogString(QString::fromStdString(m_string));
}
m_stream.rdbuf(m_old_buf);
}
protected:
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
emit sendLogString(QString::fromStdString(m_string));
m_string.erase(m_string.begin(), m_string.end());
}
else
m_string += v;
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
m_string.append(p, p + n);
long pos = 0;
while (pos != static_cast<long>(std::string::npos))
{
pos = static_cast<long>(m_string.find('\n'));
if (pos != static_cast<long>(std::string::npos))
{
std::string tmp(m_string.begin(), m_string.begin() + pos);
emit sendLogString(QString::fromStdString(tmp));
m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
}
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
std::string m_string;
signals:
void sendLogString(const QString& str);
};
#endif // ThreadLogStream_H
The derivation needs to happen QObject-first:
class LogStream : public QObject, std::basic_streambuf<char> {
Q_OBJECT
...
};
...
If the goal was to minimally modify your code, there's a simpler way. You don't need to inherit QObject to emit signals iff you know exactly what slots the signals are going to. All you need to do is to invoke the slot in a thread safe way:
QMetaObject::invokeMethod(log_window, "append", Qt::QueuedConnection,
Q_ARG(QString, tmp.c_str()));
To speed things up, you can cache the method so that it doesn't have to be looked up every time:
class LogStream ... {
QPointer<QTextEdit> m_logWindow;
QMetaMethod m_append;
LogStream::LogStream(...) :
m_logWindow(...),
m_append(m_logWindow->metaObject()->method(
m_logWindow->metaObject()->indexOfSlot("append(QString)") )) {
...
}
};
You can then invoke it more efficiently:
m_append.invoke(m_logWindow, Qt::QueuedConnection, Q_ARG(QString, tmp.c_str()));
Finally, whenever you're holding pointers to objects whose lifetimes are not under your control, it's helpful to use QPointer since it never dangles. A QPointer resets itself to 0 when the pointed-to object gets destructed. It will at least prevent you from dereferencing a dangling pointer, since it never dangles.
System: Linux
Compiler: gcc version 4.4.6
Program is for a college level class in which the instructor specified the functions to be used in a provided class. I can only make changes to the two files here and not to any files provided from the instructor. The program appears to be operating correctly except for my implementation of the dequeue function. I need access to the front, rear, item, and count. The compiler says they are out of scope. I was able to access them from the other functions in the cpp file, but because of the different method this one uses I am stumped. From what I've found this is a function pointer. I've never worked with these before and the only documentation I could find on them was how to use them, but nothing about how to access members from outside the function that are not sent in. Any help on this would be greatly appreciated. I've been working on this for days and cannot decide how to approach the problem.
////////////////////////////////////////////////////////////////////////////////////////////
//My.h
#ifndef __LINKEDQUEUE_H__
#define __LINKEDQUEUE_H__
#include <ostream>
#include <stdint.h>
#include "task.h"
#include "queueExceptions.h"
#include <string>
#include <new>
#include "queue.h"
/*class QueueEmpty
{};
class QueueFull
{};
*/
typedef Task* ItemType;
struct NodeType {
ItemType info;
NodeType* next;
};
class LinkedQueue: public Queue
{
public:
int count;
NodeType* front;
NodeType* rear;
LinkedQueue();
~LinkedQueue();
/**
* Enqueue a task onto the queue
* #param tsk The task to enqueue
* #throws QueueFull if there is not room on the queue to place the item.
*/
void enqueue(Task *tsk) throw (QueueFull);
/**
* Dequeue an element from the queue.
* #return the front of the queue.
* #throws QueueEmpty if there are no elements in the queue.
*/
Task *dequeue() throw (QueueEmpty);
/**
* Retrieve the current number of items on the queue.
* #return the current number of items on the queue.
*/
size_t depth() const;
};
#endif // __LINKEDQUEUE_H__
///////////////////////////////////////////////////////////////////////////////////////////
//my.cpp
#include <iostream>
#include <string>
#include "linkedQueue.h"
using namespace std;
LinkedQueue::LinkedQueue()
{
front = NULL;
rear = NULL;
count = 0;
}
/**
* Enqueue a task onto the queue
* #param tsk The task to enqueue
* #throws QueueFull if there is not room on the queue to place the item.
*/
void LinkedQueue::enqueue(Task *tsk) throw (QueueFull)
{
NodeType* newNode;
newNode = new NodeType;
if (newNode == NULL)
{
throw QueueFull();
}
else
{
newNode->info = tsk;
newNode->next = NULL;
if (rear == NULL)
front = newNode;
else
rear->next = newNode;
rear = newNode;
count++;
}
}
/**
* Dequeue an element from the queue.
* #return the front of the queue.
* #throws QueueEmpty if there are no elements in the queue.
*/
Task LinkedQueue::*dequeue() throw (QueueEmpty)
{
if (front == NULL) {throw QueueEmpty();}
else
{
NodeType* tempPtr;
tempPtr = front;
item = front->info;
front = front->next;
if (front == NULL)
rear = NULL;
delete tempPtr;
LinkedQueue::count--;
return item;
}
}
/**
* Retrieve the current number of items on the queue.
* #return the current number of items on the queue.
*/
size_t LinkedQueue::depth() const
{
return count;
}
LinkedQueue::~LinkedQueue()
{
NodeType* tempPtr;
while (front != NULL)
{
tempPtr = front;
front = front->next;
delete tempPtr;
}
rear = NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////
compile errors:
g++ -g -o linkedQueue.o -Wall -Werror -c linkedQueue.cpp
linkedQueue.cpp: In function ‘Task LinkedQueue::* dequeue()’:
linkedQueue.cpp:46: error: ‘front’ was not declared in this scope
linkedQueue.cpp:51: error: ‘item’ was not declared in this scope
linkedQueue.cpp:54: error: ‘rear’ was not declared in this scope
linkedQueue.h:31: error: invalid use of non-static data member ‘LinkedQueue::count’
linkedQueue.cpp:56: error: from this location
make: *** [linkedQueue.o] Error 1
You've got a typo. This...
Task LinkedQueue::*dequeue()
Should be
Task* LinkedQueue::dequeue()
This has confused the compiler since the implementation doesn't match what you have in your header and it doesn't know it's part of your class.
EDIT: In addition item isn't declared anywhere, but I reckon it is meant to be a local variable of type Task*
I have these three.h files that are giving me so many mind-boggling errors, that there must be some fundamental issue that I'm overlooking. My objective is to create an efficient linked list. I realize that there are some logic issues, but right now, I'm more interested in getting it to compile.
Basic Node class:
#pragma once
#include "stdafx.h"
#include <iostream>
#include <deque>
#include <climits>
#include "FreeList.h"
using namespace std;
using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
namespace ListTestProgram
{
generic <typename E> ref class LinkNode
{
private:
LinkNode<E>^ ptr;
FreeList<E>^ freelist;
public:
E element;
LinkNode^ next;
// constructors
LinkNode( E elemval, LinkNode<E>^ nextval = nullptr)
{
element = elemval; next = nextval;
freelist = gcnew FreeList<E>;
}
LinkNode(LinkNode<E>^ nextval = nullptr)
{
next = nextval;
freelist = gcnew FreeList<E>;
}
LinkNode<E>^ newNode()
{
freelist->nextNode();
}
void deleteNode(LinkNode<E>^ ptr)
{
freelist->add((LinkNode<E>^)ptr)->next;
}
};
}
Link class:
// LinkList.h provides for implementation of a linked list
#pragma once
#include "stdafx.h"
#include "LinkNode.h"
#include <iostream>
#include <deque>
#include <climits>
using namespace std;
using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
namespace ListTestProgram
{
generic <typename E> ref class LinkList
{
private:
LinkNode<E>^ head; // pointer to list header
LinkNode<E>^ tail; // pointer to last element
LinkNode<E>^ curr; //access to current element
int cnt; // size of list
static int defaultSize = 100;
void init() // Initializer
{
curr = tail = head = gcnew LinkNode<E>();
cnt = 0;
}
void removal() // Return link nodes to free store
{
while (head != nullptr)
{
curr = head;
head = head->next;
delete curr;
}
}
public:
LinkList<E>(){}
LinkList<E>(int size=defaultSize) {init(); } // Constructor
~LinkList() { removal(); } // Destructor
void print(); // print list contents
void clear() { removal(); init(); } // Clear list
// Insert "it" at current position
void insert (E it)
{
curr->next = gcnew LinkNode<E>(it, curr->next);
if(tail == curr) tail = curr->next; // new tail
cnt++;
}
void append(E it)
{
tail = tail->next = gcnew LinkNode<E> (it, nullptr);
cnt++;
}
// Remove and return current element
E remove()
{
assert(curr->next != nullptr, "No element");
E it = curr->next->element; // Remember value
LinkNode<E>^ ltemp = curr->next; // Rember link node
if(tail == curr->next) tail = curr; // reset tail
curr->next = curr->next->next; // remove from list
delete ltemp;
cnt--;
return it;
}
void moveToNext() // Place curr to head of list
{
curr = head;
}
void moveToEnd() // Place curr at end of list
{
curr = tail;
}
// Move curr one step left; no change if already at front
void prev()
{
if (curr == head) return;
LinkNode<E>^ temp = head;
// March down the list until we find the previous element
while (temp->next!=curr) temp= temp->next;
curr = temp;
}
// Move curr one step right; no change if already at end
void next()
{
if (curr != tail) curr = curr->next;
}
// Return the position of the current element
int currPos()
{
LinkNode<E>^ temp = head;
int i;
for (i = 0; curr != temp; i++)
temp = temp->next;
return i;
}
// Move down list to "pos" position
void moveToPos(int pos)
{
assert((pos>=0)&&(pos<=cnt), "Position out of range.");
curr = head;
for (int i=0; i<pos; i++) curr = curr->next;
}
E getValue()
{
assert(curr->next != nullptr, "No value");
return curr->next->element;
}
};
}
And the FreeList class, which I want to hold nodes that are deleted from the linked list so that they can be reused, avoiding repeated calls to delete and new.
// FreeList.h provides a stack of nodes from which new nodes from the LinkNode class can be
// derived. This is to limit the number of calls to gcnew, to minimize proceccor time. Items in the
// FreeList stack are returned to LinkNode calls when a new node is called for.
#pragma once
#include "stdafx.h"
#include <iostream>
#include <deque>
#include <climits>
#include "LinkNode.h"
using namespace std;
using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
namespace ListTestProgram
{
generic <typename E> ref class FreeList
{
/**** members ****/
public: Stack^ freelist;
/**** constructors / Destructors ****/
FreeList() { freelist = gcnew Stack; }
~FreeList(){}
/**** methods ****/
// return an instance of LinkNode
LinkNode<E>^ nextNode ()
{
if (freelist->Count != 0)
return freelist->Pop();
else
{
repopulateStack();
nextNode(node);
}
}
// repopulate an empty stack
void repopulateStack()
{
for (int i = 0; i < 100; i++)
freelist->Push(gcnew LinkNode<E>());
}
// add a deleted node to the freelist for later use
void add(LinkNode<E>^ trash)
{
freelist->Push(trash);
}
};
}
A few of my errors are:
Error 13 error C2039: 'nextNode' : is not a member of 'ListTestProgram::FreeList<E>' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\LinkNode.h 43 1 TestingLists
Error 6 error C2061: syntax error : identifier 'LinkNode' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 52 1 TestingLists
Error 10 error C2065: 'trash' : undeclared identifier c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 54 1 TestingLists
Error 3 error C2143: syntax error : missing ';' before '<' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 33 1 TestingLists
Error 1 error C2872: 'Stack' : ambiguous symbol c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 23 1 TestingLists
Error 6 error C2061: syntax error : identifier 'LinkNode' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 52 1 TestingLists
Your headers have circular dependencies: FreeList.h depends on LinkNode.h which depends on FreeList.h. Header file dependencies must form a directed, acyclic graph.
I wrote some multithreading code using Boost thread library. I initialized two threads in the constructor using the placeholder _1 as the argument required by member function fillSample(int num). But this doesn't compile in my Visual Studio 2010. Following is the code:
#include<boost/thread.hpp>
#include<boost/thread/condition.hpp>
#include<boost/bind/placeholders.hpp>
#define SAMPLING_FREQ 250
#define MAX_NUM_SAMPLES 5*60*SAMPLING_FREQ
#define BUFFER_SIZE 8
class ECG
{
private:
int sample[BUFFER_SIZE];
int sampleIdx;
int readIdx, writeIdx;
boost::thread m_ThreadWrite;
boost::thread m_ThreadRead;
boost::mutex m_Mutex;
boost::condition bufferNotFull, bufferNotEmpty;
public:
ECG();
void fillSample(int num); //get sample from the data stream
void processSample(); //process ECG sample, return the last processed
};
ECG::ECG() : readyFlag(false), sampleIdx(0), readIdx(0), writeIdx(0)
{
m_ThreadWrite=boost::thread((boost::bind(&ECG::fillSample, this, _1)));
m_ThreadRead=boost::thread((boost::bind(&ECG::processSample, this)));
}
void ECG::fillSample(int num)
{
boost::mutex::scoped_lock lock(m_Mutex);
while( (writeIdx-readIdx)%BUFFER_SIZE == BUFFER_SIZE-1 )
{
bufferNotFull.wait(lock);
}
sample[writeIdx] = num;
writeIdx = (writeIdx+1) % BUFFER_SIZE;
bufferNotEmpty.notify_one();
}
void ECG::processSample()
{
boost::mutex::scoped_lock lock(m_Mutex);
while( readIdx == writeIdx )
{
bufferNotEmpty.wait(lock);
}
sample[readIdx] *= 2;
readIdx = (readIdx+1) % BUFFER_SIZE;
++sampleIdx;
bufferNotFull.notify_one();
}
I already included the placeholders.hpp header file but it still doesn't compile. If I replace the _1 with 0, then it will work. But this will initialize the thread function with 0, which is not what I want. Any ideas on how to make this work?
Move the creation to the initialization list:
m_ThreadWrite(boost::bind(&ECG::fillSample, this, _1)), ...
thread object is not copyable, and your compiler doesn't support its move constructor.