I'm writing a template class that is supposed to provide a queue protected against concurrent access, basically to be able to write classes that can treat a list of items with a variable number of concurrent worker threads.
The template class has a method:
bool getFront(T &value)
{ CritSectEx::Scope scope(listLock);
bool ret = false;
if( !itemList.empty() ){
value = itemList.front();
itemList.pop();
ret = true;
}
return ret;
}
which locks the queue, and if it's not empty fetches the 1st element, pops it and returns true, so that each worker can do
while( getFront(entry) ){
// do something with entry
nTreated += 1;
}
// exit worker
The question is: what exactly am I returning here through the value
reference? When I test this code, I get a double free error because the type T
I'm using contains a pointer (let's call it B) that's deleted in its dtor (when B is not marked as being a pointer to a global structure). That dtor has been called during the pop() in getFront(), which seems logical. I took care that type T's ctors all allocate a copy of B when creating a new T from an existing instance and it does not subclass anything which could provide an '=' operator, so I'm not sure how I'd end up with 2 instances of T that each contain the same value of B and the "we own B" member.
I don't do this sort of thing often enough, so I'm probably overlooking something here, but what? Any suggestions how to troubleshoot this?
Additional observation: it appears that it's always the last element popped from the queue that shows this behaviour.
To make this less vague, here's what I use for typename T:
struct folder_info
{
// plenty of members snipped
#ifdef __cplusplus
public:
folder_info()
{
memset( this, 0, sizeof(struct folder_info) );
}
folder_info(const struct folder_info *src)
{
init(src);
}
folder_info(const struct folder_info &src)
{
init(&src);
}
private:
void init(const struct folder_info *src)
{
memcpy( this, src, sizeof(struct folder_info) );
// we don't duplicate the filetypeslist!
filetypeslist = NULL;
filetypeslistlen = filetypeslistsize = 0;
}
#endif
};
typedef struct FileEntry {
public:
std::string fileName;
struct stat fileInfo;
FolderInfo *folderInfo;
bool freeFolderInfo;
FileEntry();
FileEntry( const char *name, const struct stat *finfo, FolderInfo *dinfo, const bool ownInfo=false );
FileEntry( const char *name, const struct stat *finfo, FolderInfo &dinfo );
FileEntry(const FileEntry &ref);
~FileEntry();
} FileEntry;
FileEntry::FileEntry()
{
folderInfo = NULL;
freeFolderInfo = false;
}
FileEntry::FileEntry( const char *name, const struct stat *finfo, FolderInfo *dinfo, const bool ownInfo )
{
fileName = name;
fileInfo = *finfo;
folderInfo = (ownInfo)? new FolderInfo(dinfo) : dinfo;
freeFolderInfo = ownInfo;
}
FileEntry::FileEntry( const char *name, const struct stat *finfo, FolderInfo &dinfo )
{
// creating a FileEntry with a FolderInfo reference always makes a copy
// of the FolderInfo structure
FileEntry( name, finfo, new FolderInfo(dinfo), true );
}
FileEntry::FileEntry(const FileEntry &ref)
{
fileName = ref.fileName;
fileInfo = ref.fileInfo;
if( ref.freeFolderInfo ){
folderInfo = new FolderInfo(ref.folderInfo);
}
else{
folderInfo = ref.folderInfo;
}
freeFolderInfo = ref.freeFolderInfo;
}
FileEntry::~FileEntry()
{
if( freeFolderInfo && folderInfo ){
delete folderInfo;
folderInfo = NULL;
}
}