I'm developing a LRU-cache in C++, using boost mutexes and locks, in a multi-threaded environment.
The architecture is based on a boost::unordered_map + a lock-free-queue
Insertions work in non-blocking mode (try_lock), but removals should lock the map and proceed.
The problem is that very rarely, the cache-access deadlocks in the removal.
.h
typedef boost::function<std::string ( const std::string &key )> LoaderFunction;
class ListNode;
struct CacheEntry {
CacheEntry(std::string key="", std::string value="");
ListNode * createLruListNode() const;
ListNode * getLruListNode() const;
virtual ~CacheEntry();
const std::string key;
const std::string value;
private:
ListNode ** listNodePP;
};
struct ListNode {
ListNode(const CacheEntry* entry = NULL);
~ListNode();
void setCacheEntry(const CacheEntry* entry);
const CacheEntry * getCacheEntry();
void setDirty();
private:
const CacheEntry * cacheEntry;
bool dirty;
};
typedef LockFreeQueue<ListNode*> List;
typedef boost::unordered_map
< const string , const CacheEntry * >
Cache;
typedef Cache::iterator CacheIter;
.cpp
#include "LockFreeQueue.h"
#include <unistd.h>
using namespace std;
/* ... */
ListNode::ListNode(const CacheEntry* e2) : cacheEntry(e2) {
dirty=false;
}
void ListNode::setCacheEntry(const CacheEntry* entry) {
cacheEntry=entry;
}
const CacheEntry* ListNode::getCacheEntry() {
if(dirty) {
return NULL;
}
return cacheEntry;
}
void ListNode::setDirty() {
dirty=true;
}
std::string PeachCachePartition::get(const string key) {
CacheIter iter=cache->find(key);
string value;
if(iter!=cache->end()) {
__sync_fetch_and_add(_hits,1);
const CacheEntry* entry=iter->second;
value=(entry->value);
lruList->enqueue(entry->getLruListNode());
if(size() > max) { // removes some
int howMany = (int) ceil((*_misses)/(*_hits))+1;
int k=0;
ListNode removedListNode=ListNode();
ListNode * p=&removedListNode;
ListNode ** pp=&p;
while(size() > max && k<howMany) {
if(lruList->dequeue(pp)) {
const CacheEntry * toBeRemoved=p->getCacheEntry();
if(toBeRemoved) {
remove(toBeRemoved->key);
k++;
}
}
}
}
} else {
__sync_fetch_and_add(_misses,1);
value=loader(key);
if(value.size()>0) {
put(key,value);
}
}
return value;
}
void PeachCachePartition::remove(const std::string &key) {
try {
boost::lock_guard<boost::mutex> mapLockGuard(mapMutex);
CacheIter iter = cache->find(key);
if(iter!=cache->end()) {
const CacheEntry * toBeRemoved=iter->second;
if(toBeRemoved->getLruListNode()) {
toBeRemoved->getLruListNode()->setDirty();
}
delete(toBeRemoved);
cache->erase(iter);
__sync_sub_and_fetch(_size,1);
}
} catch (std::exception &e) {
Logger::err(e.what());
}
}
void PeachCachePartition::put(const std::string &key, std::string &value) {
try {
boost::unique_lock<boost::mutex> mapLockGuard(mapMutex,boost::try_to_lock);
if(mapLockGuard.owns_lock()) {
CacheIter iter=cache->find(key);
const CacheEntry * entry;
if(iter!=cache->end()) {
entry=iter->second;
entry->getLruListNode()->setDirty();
} else {
entry = new CacheEntry(key,value);
__sync_add_and_fetch(_size,1);
(*cache)[key] = entry;
}
entry->createLruListNode()->setCacheEntry(entry);
lruList->enqueue(entry->getLruListNode());
}
} catch (std::exception &e) {
Logger::err(e.what());
}
}
Can you explain me what's wrong? I'm almost sure it deadlocks in the removal as it is the only lock it must acquire.
Thanks everybody
edit: I'm using this cache in an apache module which runs mpm_prefork_module: could this be the problem? Should I use boost::interprocess instead of boost::thread?
Related
I have a working implementation for the classic "Queue using two stacks" data structure in C++ and am wondering what the best software engineering practice would be for handling const correctness.
The peek() function returns the value at the front of the queue, which may mean that we need to pop everything from one stack and copy it to the other. One way to use the copy function is to cast the constness away for this as shewn below. Is there a better way?
class MyQueue {
stack <int> s1;
stack <int> s2;
public:
MyQueue() {
}
void push(int x) {
s1.push(x);
}
void copy()
{
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
int pop() {
int res;
if (s2.empty()) copy();
if (! s2.empty()) {
res = s2.top();
s2.pop();
}
return res;
}
int peek() const {
if (s2.empty()) ((MyQueue*) (this))->copy();
return s2.top();
}
bool empty() const {
return s1.empty() && s2.empty();
}
};
It's better to adjust the design a bit, so that s2 is never empty unless the queue is empty. Then the difficult question goes away, and the code gets simpler, too:
class MyQueue {
stack <int> s1;
stack <int> s2;
public:
MyQueue() {
}
void push(int x) {
if (s2.empty()) {
// queue is empty
s2.push(x);
} else {
s1.push(x);
}
}
int pop() {
int res = s2.pop();
if (s2.empty())
copy();
return res;
}
int peek() const {
return s2.top();
}
bool empty() const {
return s2.empty();
}
private:
void copy()
{
while (!s1.empty()) {
s2.push(s1.pop());
}
}
};
I'm following this tutorial to create a simple iterator, although they are iterating primitive int, I'm iterating an object type SpamValue.
I have class called SpamValue and another called SpamValueStackIter and they are tightly coupled, because I didn't want to expose a lot of getters, so I made one class SpamValueStackIter a "friend class" in SpamValue header.
#ifndef SPAMSTACK_H
#define SPAMSTACK_H
#include <iostream>
#include "SpamValue.h"
using namespace std;
class SpamStack
{
public:
friend class SpamValueStackIter;
SpamStack(SpamValue** SpamValueItems, int size)
{
_position =-1;
_total_size = size;
_SpamValueItems = new SpamValue*[_total_size];
int i=0;
for (; i<_total_size; i++)
{
this->_SpamValueItems[i] = SpamValueItems[i];
}
}
~SpamStack()
{
if (NULL!=_SpamValueItems)
{
/*delete each*/
int i =0;
for (; i<_total_size; i++)
{
if (NULL!=_SpamValueItems[i])
{
delete _SpamValueItems[i];
}
}
/*delete the array*/
delete [] _SpamValueItems;
}
}
/*push*/
void push(SpamValue* SpamValue)
{
_SpamValueItems[++_position];
}
/*pop*/
SpamValue* pop()
{
return _SpamValueItems[_position--];
}
/*isEmpty*/
bool isEmpty()
{
return (_position == -1);
}
/*getters*/
SpamValue** getSpamValueItems()
{
return this->_SpamValueItems;
}
int getTotalSize()
{
return _total_size;
}
SpamValueStackIter* createIterator()const;
private:
SpamValue** _SpamValueItems;
int _total_size;
int _position;
};
class SpamValueStackIter
{
const SpamStack* _stack;
int _index;
public:
SpamValueStackIter(const SpamStack *s)
{
_stack = s;
}
/*set index position to first item*/
void first()
{
_index = 0;
}
/*set index position to the next item in the iterator*/
void next()
{
_index++;
}
/*is the iteration completed */
bool isDone()
{
return _index == _stack->_position + 1;
}
/* return the current item */
SpamValue* currentItem()
{
return _stack->_SpamValueItems[index];
}
/*create a new iterator*/
SpamValueStackIter* SpamStack::createIterator()const
{
return new SpamValueStackIter(this);
}
};
#endif /* SPAMSTACK_H*/
In the SpamStack.h:, Im getting this error:
SpamStack.h:77:6: error: ‘SpamValueStackIter’ does not name a type
SpamValueStackIter* createIterator()const;
And also:
SpamStack.h:121:52: error: cannot define member function ‘SpamStack::createIterator tor’ within ‘SpamValueStackIter’
SpamValueStackIter* SpamStack::createIterator()const
Why can't SpamStack resolve the "friend class" that's defined in the same header?
After forward declaration, as suggested by others:
#ifndef SPAMSTACK_H
#define SPAMSTACK_H
#include <iostream>
#include "SpamValue.h"
using namespace std;
/*forward declare*/
class SpamValueStackIter;
class SpamStack
{
public:
friend class SpamValueStackIter;
SpamStack(SpamValue** SpamValueItems, int size)
{
_position =-1;
_total_size = size;
_SpamValueItems = new SpamValue*[_total_size];
int i=0;
for (; i<_total_size; i++)
{
this->_SpamValueItems[i] = SpamValueItems[i];
}
}
~SpamStack()
{
if (NULL!=_SpamValueItems)
{
/*delete each*/
int i =0;
for (; i<_total_size; i++)
{
if (NULL!=_SpamValueItems[i])
{
delete _SpamValueItems[i];
}
}
/*delete the array*/
delete [] _SpamValueItems;
}
}
/*push*/
void push(SpamValue* SpamValue)
{
_SpamValueItems[++_position];
}
/*pop*/
SpamValue* pop()
{
return _SpamValueItems[_position--];
}
/*isEmpty*/
bool isEmpty()
{
return (_position == -1);
}
/*getters*/
SpamValue** getSpamValueItems()
{
return this->_SpamValueItems;
}
int getTotalSize()
{
return _total_size;
}
SpamValueStackIter* createIterator()const;
private:
SpamValue** _SpamValueItems;
int _total_size;
int _position;
};
class SpamValueStackIter
{
public:
SpamValueStackIter(const SpamStack *s)
{
_stack = s;
}
/*set index position to first item*/
void first()
{
_index = 0;
}
/*set index position to the next item in the iterator*/
void next()
{
_index++;
}
/*is the iteration completed */
bool isDone()
{
return _index == _stack->_position + 1;
}
/* return the current item */
SpamValue* currentItem()
{
return _stack->_SpamValueItems[index];
}
private:
const SpamStack* _stack;
int _index;
};
/create a new iterator/
SpamValueStackIter* SpamStack::createIterator()const
{
return new SpamValueStackIter(this);
}
#endif /* SPAMSTACK_H */
In getting this error now:
SpamStack.h:117:45: error: invalid types ‘SpamValue** const[<unresolved overloaded function type>]’ for array subscript
return _stack->_SpamValueItems[index];
I have implemented a serializer to send data over network. And I have implemented a system that can deserialize primitive data, string, map(string, string), map(string, float), but the error happens with map(string, int) when the deserialized map is used to fetch the value from key. In the debugger I can see that map receive correct value but when I'm trying to get data, I get an error "std::out_of_range at memory location".
Here is my code
#include <stdint.h>
#include <memory>
#include <string>
#include <map>
#include <algorithm>
#define STREAM_ENDIANNESS 0
#define PLATFORM_ENDIANNESS 0
using namespace std;
class OutputMemoryStream
{
void ReallocBuffer(uint32_t inNewLength)
{
mBuffer = static_cast<char*>(std::realloc(mBuffer, inNewLength));
mCapacity = inNewLength;
}
char* mBuffer = nullptr;
uint32_t mHead;
uint32_t mCapacity;
public:
OutputMemoryStream() : mHead(0) { ReallocBuffer(32); }
~OutputMemoryStream()
{
if (mBuffer) { mBuffer = nullptr; }
}
char* GetBufferPtr() const { return mBuffer; }
uint32_t GetLength() const { return mHead; }
void Write(const void* inData, size_t inByteCount)
{
//make sure we have space...
uint32_t resultHead = mHead + static_cast<uint32_t>(inByteCount);
if (resultHead > mCapacity)
{
ReallocBuffer(std::max(mCapacity * 2, resultHead));
}
//copy into buffer at head
std::memcpy(mBuffer + mHead, inData, inByteCount);
//increment head for next write
mHead = resultHead;
}
template< typename T > void Write(T inData)
{
static_assert(std::is_arithmetic< T >::value || std::is_enum< T >::value, "Generic Write only supports primitive data types");
if (STREAM_ENDIANNESS == PLATFORM_ENDIANNESS)
{
Write(&inData, sizeof(inData));
}
else { }
}
template< typename T >
void Write(const std::map< string, T >& inMap)
{
uint32_t elementCount = inMap.size();
Write(elementCount);
for (std::pair<string, T> element : inMap)
{
Write(element.first);
Write(element.second);
}
}
void Write(const std::string& inString)
{
size_t elementCount = inString.size();
Write(elementCount + 1);
Write(inString.data(), (elementCount + 1) * sizeof(char));
}
};
class InputMemoryStream
{
private:
char* mBuffer;
uint32_t mHead;
uint32_t mCapacity;
public:
InputMemoryStream() {}
InputMemoryStream(char* inBuffer, uint32_t inByteCount) : mBuffer(inBuffer), mCapacity(inByteCount), mHead(0) { }
~InputMemoryStream()
{
if (mBuffer) { mBuffer = nullptr; }
}
uint32_t GetRemainingDataSize() const
{
return mCapacity - mHead;
}
void Read(void* outData, uint32_t inByteCount)
{
uint32_t resultHead = mHead + inByteCount;
if (resultHead > mCapacity)
{
//handle error, no data to read!
//...
}
std::memcpy(outData, mBuffer + mHead, inByteCount);
mHead = resultHead;
}
template< typename T > void Read(T& outData)
{
static_assert(std::is_arithmetic< T >::value || std::is_enum< T >::value, "Generic Read only supports primitive data types");
Read(&outData, sizeof(outData));
}
template<typename T1>
void Read(std::map<string, T1> &mapP)
{
size_t elemenCount;
Read(elemenCount);
for (int i = 0; i < elemenCount; i++)
{
string key; T1 value;
Read(key);
Read(value);
std::pair<string, T1> pair(key, value);
mapP.insert(pair);
}
}
void Read(string &outString)
{
size_t strSize;
Read(strSize);
outString.resize(strSize);
for (int i = 0; i < strSize; i++)
{
Read(&outString[i], 1);
}
}
};
class ServerObject
{
OutputMemoryStream outStream;
InputMemoryStream inStream;
map<std::string, int> mapInt;
public:
ServerObject() {};
ServerObject(char* byteArray, int byteCount)
{
InputMemoryStream inStream(byteArray, byteCount);
Deserialize(inStream);
}
~ServerObject() {};
void Serialize()
{
outStream.Write(mapInt);
}
void Deserialize(InputMemoryStream inStream)
{
inStream.Read(mapInt);
}
OutputMemoryStream GetOutStream()
{
return outStream;
}
int GetInt(string key)
{
return mapInt.at(key);
}
void PutInt(string key, int value)
{
mapInt.insert(std::pair<string, int>(key, value));
}
};
int main()
{
ServerObject * so = new ServerObject();
so->PutInt("test", 10);
so->Serialize();
ServerObject * so1 = new ServerObject(so->GetOutStream().GetBufferPtr(), so->GetOutStream().GetLength());
int i = so1->GetInt("test");
system("pause>NULL");
return 0;
}
Your void Write(const std::string& inString) function of OutputMemoryStream should not store additional byte of buffer for null terminator because std::string will not contain null terminator but if you use c_str(), a null terminator will be included in the return from this method. Don't get confused with the internal structure of the memory. std::string stores the length of the string in its member variable so there is no need of null terminator. The function should be as shown below.
void Write(const std::string& inString)
{
size_t elementCount = inString.size();
Write(elementCount);
Write(inString.data(), elementCount * sizeof(char));
}
Im writing a program that simulates a vacuum cleaning a room. There is an initial state of dirty spots and I want to use various AI algorithms to find the best paths to clean the room. By making the algorithms separate from the actual problem I think my solution will be very modular.
Each algorithm only knows about States. Each state can be expanded to children states. Here is my first algorithm, UniformCost:
#include<iostream>
#include<set>
class State {
public:
State(){}
bool operator< (const State& s) const;
bool isGoal();
std::set<State> expand();
};
class UniformCost {
private:
State startState;
std::set<State> closedList; //list of no repeated states
public:
State start;
void setStart(State s);
State* getSolution();
};
void UniformCost::setStart(State st) {
start = st;
}
State* UniformCost::getSolution() {
closedList.insert(start);
while(!closedList.empty()) {
State cur = *closedList.begin();
if(cur.isGoal()) {
return &cur;
}
closedList.erase(cur);
std::set<State> children = cur.expand();
for (std::set<State>::iterator i = children.begin(); i != children.end(); ++i) {
closedList.insert(*i);
}
}
}
My main application creates the initial Node that is a child class of State.
class Node : public State {
public:
std::pair<int,int> loc;
int g_val;
std::set<std::pair<int,int> > dirt;
std::vector<char> path;
bool isGoal() {
return dirt.size() == 0;
}
bool operator< (const State& s) const {
Node n = (Node) s;
if(loc == n.loc) {
return false;
}
if(g_val <= n.g_val) {
return true;
}
return false;
}
std::set<State> expand() {
std::set<State> ret;
return ret;
}
};
How can I override the operator in the Node class that is expecting a "operator< (const State&)"? Or a more general question, how would I handle future "casting" of States?
###MyClass.h###
#ifndef _MyClass
#define _MyClass
#include <string>
using namespace std;
class MyClass
{
public:
MyClass(const string name, const string text);
void display(ostream & out) const;
MyClass & operator = (const MyClass & m);
int compare(const MyClass & right) const;
private:
string _name;
string _text;
};
bool operator < (const MyClass & left, const MyClass & right);
ostream & operator << (ostream & out, const MyClass & mc);
#endif
###Node.h###
#include <string>
#include "MyClass.h"
using namespace std;
typedef MyClass * DataType;
class Node
{
private:
DataType item; // data
Node * lchild; // left child pointer
Node * rchild; // right child pointer
public:
Node(DataType Item);
DataType getItem() const;
void setItem(const DataType & data);
Node* getLChild() const;
void setLChild(Node * p);
Node* getRChild() const;
void setRChild(Node * p);
virtual ~Node();
};
###BST.h###
#include "Node.h"
using namespace std;
class BST
{
private:
Node * root;
bool Search(const DataType item, Node * r) const;
void Insert (DataType item, Node * ptr);
void Destructor(const Node * r);
public:
BST();
bool IsEmpty() const;
void Insert(const DataType item);
bool Search(const DataType item) const;
virtual ~BST();
};
###MyClass.cpp###
#include <iostream>
#include "MyClass.h"
using namespace std;
MyClass::MyClass(const string name, const string text)
{
_name = name;
_text = text;
}
void MyClass::display(ostream & out) const
{
out << "Name: " << _name << endl;
out << "Text: " << _text << endl;
}
MyClass & MyClass::operator = (const MyClass & m)
{
if (this == & m)
return *this;
_name = m._name;
_text = m._text;
return *this;
}
int MyClass::compare(const MyClass & right) const
{
return _name.compare(right._name);
}
bool operator < (const MyClass & left, const MyClass & right)
{
return left.compare(right) > 0;
}
ostream & operator << (ostream & out, const MyClass & mc)
{
mc.display(out);
return out;
}
###Node.cpp###
#include "Node.h"
Node::Node(DataType Item):item(Item)
{
lchild = 0;
rchild = 0;
}
DataType Node::getItem() const
{
DataType anItem = item;
return anItem;
}
void Node::setItem( const DataType & data)
{
item = data;
}
Node* Node::getLChild() const
{
Node * p = lchild;
return p;
}
void Node::setLChild(Node * p)
{
lchild = p;
}
Node* Node::getRChild() const
{
Node * p = rchild;
return p;
}
void Node::setRChild(Node * p)
{
rchild = p;
}
Node::~Node()
{
}
###BST.cpp###
#include <iostream>
#include "BST.h"
using namespace std;
bool BST::Search(const DataType item) const
{
return Search(item, root);
}
bool BST::Search(const DataType item, Node * r) const
{
if(r != 0)
{
if (item == r->getItem())
return true;
else
{
if (item < r->getItem())
return Search(item, r->getLChild());
else
return Search(item, r->getRChild());
}
}
else
return false;
}
BST::BST()
{
root = 0;
}
bool BST::IsEmpty() const
{
return (root == 0);
}
void BST::Insert(const DataType item)
{
if(root == 0)
root = new Node(item);
else
Insert(item, root);
}
void BST::Insert(DataType item, Node * ptr)
{
if (item < ptr->getItem())
{
if (ptr->getLChild() == 0)
ptr->setLChild(new Node(item));
else
Insert(item, ptr->getLChild());
}
else
{
if (ptr->getRChild() == 0)
ptr->setRChild(new Node(item));
else
Insert(item, ptr->getRChild());
}
}
void BST::Destructor(const Node * r)
{
if(r!=0)
{
Destructor( r->getLChild());
Destructor( r->getRChild());
delete r;
}
}
BST::~BST()
{
Destructor(root);
}
###main.cpp###
#include <iostream>
#include "MyClass.h"
#include "BST.h"
using namespace std;
void main()
{
MyClass * mc1 = new MyClass("Tree","This is a tree");
MyClass * mc2 = new MyClass("Book","This is a book");
MyClass * mc3 = new MyClass("Zoo","This is a zoo");
BST * tree = new BST();
tree->Insert(mc1);
tree->Insert(mc2);
tree->Insert(mc3);
cout << boolalpha << ("Book" < "Tree") << endl;
cout << (mc2 < mc1) << endl;
cout << (tree->Search(new MyClass("Book",""))) << endl;
}
Result is true false false
I don't know what's wrong with my operator overloading? (mc2 should
less than mc1)
I'm not sure if this is correct for searching a "MyClass" node in a BST?
and the result is "not found"....I traced it into "BST.cpp",
and found that the problem also occurs at " if (item < r->getItem()) "
Can anyone help me or give me a hint....thank you so much!
Here you are just comparing pointers, i.e memory addresses:
cout << (mc2 < mc1) << endl;
To compare the objects, you need to dereference the pointers:
cout << ((*mc2) < (*mc1)) << endl;
In your code snippet, there is no reason for mc1, mc2, etc. to be pointers, so you could avoid the problem by creating objects on the stack directly:
MyClass mc1("Tree","This is a tree");
and so on. I would even go further and say that you should only dynamically allocate objects with new if you really are sure you need to and have good reasons not to allocate automatically on the stack. And if you really must use dynamically allocated pointers, have a look at C++ smart pointers.