Show IMAPTypes.h syntax highlighted
#ifndef IMAP_TYPES
#define IMAP_TYPES
#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <exception>
#include <sstream>
#include <time.h>
#include <sys/types.h>
#include <boost/utility.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread.hpp>
#include <imap_debug.h>
namespace IMAP
{
class MessageAttributes;
class CacheError : public std::exception {
const char *msg;
public:
CacheError(const char *m) throw() { msg = m; }
const char *what() const throw() {
return msg;
}
};
/**
* Item is a reference counting data structure, with some helper methods
* added in (for example, it insures that access time is kept up to date. It
* is used to keep track of a pointer to actual data (of type S). This class
* is an implementation detail of CacheEntry, and should not be used outside
* of it.
*/
template<class S>
struct Item : public boost::noncopyable {
Item(S *p) : ptr(p) {
time(&access_time);
refcnt = 1;
}
~Item() {
delete ptr;
}
S &getItem(bool update_access_time = true) {
if(update_access_time) time(&access_time);
return *ptr;
}
time_t getLastAccessTime() const { return access_time; }
void touch() { time(&access_time); }
Item *ref() {
boost::mutex::scoped_lock lk(object_mutex);
refcnt++;
return this;
}
/**
* Dereference the specified data item, and return the reference count.
*/
int deref() {
boost::mutex::scoped_lock lk(object_mutex);
refcnt--;
return refcnt;
}
int getReferenceCount() {
boost::mutex::scoped_lock lk(object_mutex);
return refcnt;
}
private:
S *ptr;
time_t access_time;
int refcnt;
boost::mutex object_mutex;
};
/** A CacheEntry implements a reference counting scheme for time-stamped
* items. (@see IMAP::Item). It allows you to use assigment to copy pointers
* to a single item in a way that insures the item continues to exist until
* no more CacheEntry objects exists that have reference to it.
*
* This is a memory management tool, and is meant to be used much like
* auto_ptr<T>, but with reference counting and time stamping added in.
* These added features let you cache a set of objects in a central store
* where time can be used as a criteria for expiration, but also provides
* the safety of not actually freeing the object until all users of it have
* released their cache entries.
*
* The scheme is intended to be used in the following pattern:
*
* 1) The real object of interest is allocated via new() (p = new thing()).
* 2) A new CacheEntry object is created using the pointer from step 1 as an
* argument (i.e. vector<CacheEntry<T> > a; a.push_back(CacheEntry(p));)
* 3) Other CacheEntry objects are created as copies of this cache entry. If
* the original CacheEntry is deleted, the underlying object is still in
* existence.
* 4) At any point when all CacheEntry objects that point to the object are
* destroyed, the final CacheEntry cleans up the memory.
*/
template<class T>
class CacheEntry
{
public:
/**
* Containers often need a way to create an empty object. This
* constructor creates a CacheEntry that does not currently reference
* an object.
*/
CacheEntry()
{
boost::recursive_mutex::scoped_lock lk(object_mutex);
item = NULL;
/*
message.str("");
message << "Created cache entry:" << std::endl << *this;
DEBUG(message.str());
*/
}
/**
* Create a CacheEntry, and make it responsible for deleting the
* memory associated with an object.
*
* @param i A pointer to the object to be managed.
*/
CacheEntry(T *i)
{
boost::recursive_mutex::scoped_lock lk(object_mutex);
item = new Item<T>(i);
/*
message.str("");
message << "Created cache entry:" << std::endl << *this;
DEBUG(message.str());
*/
}
/**
* Copy constructor. Insures that proper reference counting is done
* when new cache entries are created that are supposed to reference
* an existing object. Also handles the case where an empty cache
* entry is copied.
*/
CacheEntry(const CacheEntry<T> &i) {
boost::recursive_mutex::scoped_lock lk(object_mutex);
boost::recursive_mutex::scoped_lock lk2(const_cast<boost::recursive_mutex&>(i.object_mutex));
if(i.item) {
this->item = i.item->ref();
} else {
item = NULL;
}
/*
message.str("");
message << "Copied cache entry" << std::endl <<
"Source: " << i << std::endl <<
"Dest: " << *this;
DEBUG(message.str());
*/
}
~CacheEntry() {
boost::recursive_mutex::scoped_lock lk(object_mutex);
/*
message.str("");
message << "Deleting cache entry: " << *this;
DEBUG(message.str());
*/
if(item != NULL && item->deref() == 0) {
DEBUG("Deleting referenced item");
delete item;
}
//DEBUG("Done with destructor");
}
/**
* Stop managing the current object, and start managing a different
* one.
* @param b The object to manage.
*/
CacheEntry &operator=(T *b) {
boost::recursive_mutex::scoped_lock lk(object_mutex);
/*
message.str("");
message << "Managing a new item: " << std::endl <<
" Reference: " << b << std::endl <<
" CacheEntry (before copy): " << *this << std::endl;
*/
// LHS is being re-assigned...drop the reference
if(item != NULL && item->deref() == 0) {
/*
message << "(deleted old referenced item. item: " << item <<
std::endl << " reference: " << &(item->getItem()) << ')'
<< std::endl;
*/
delete item;
}
item = new Item<T>(b);
//message << " CacheEntry (after copy): " << *this << std::endl;
//DEBUG(message.str());
}
CacheEntry &operator=(const CacheEntry &b)
{
boost::recursive_mutex::scoped_lock lk(object_mutex);
boost::recursive_mutex::scoped_lock lk2(const_cast<boost::recursive_mutex&>(b.object_mutex));
//message.str("");
//message << "Copying cache entry. Src: " << b << std::endl <<
//" dest:" << *this << std::endl;
// LHS is being re-assigned...drop the reference
if(item != NULL && item->deref() == 0) {
//message << " Deleted item: " << (void*)item << std::endl
//<< " which ref'd: " << (void*)&(item->getItem())
//<< std::endl;
delete item;
}
if(b.item)
item = b.item->ref();
else
item = NULL;
//message << " Dest (after copy): " << *this << std::endl;
//DEBUG(message.str());
return *this;
}
/**
* Check to see if this CacheEntry is managing an object.
*/
bool isEmpty() const { return (item == NULL); }
/**
* Get the managed item. This returns a reference to the acual
* underlying item. Changes to the item are seen by all users, so
* multi-threaded applications should implement locking when
* necessary.
*
* @returns A reference to the cached item
* @throws CacheError if an attempt is made to get a non-existent
* item.
*/
T &getItem(bool update_access_time = true) {
boost::recursive_mutex::scoped_lock lk(object_mutex);
if(item)
return item->getItem(update_access_time);
else
throw(CacheError("Attempt to dereference an empty cache cell"));
}
/**
* Determine how long it has been since the managed object has been
* accessed.
* @returns The number of seconds since the object was accessed.
*/
int getIdleTime() const {
time_t now;
time(&now);
return (now - item->getLastAccessTime());
}
/**
* Get the time of last access (in seconds since epoch. see time(2)).
* @returns The last access time, in a format as reported by the
* time(2) system call.
*/
time_t getLastAccessTime() const {
if(item) return item->getLastAccessTime();
else return 0;
}
/**
* Force an update of the last access time, without actually using the
* object.
*/
void touch() {
if(item) item->touch();
}
private:
template<class X>
friend std::ostream &operator<<(std::ostream &out,
const CacheEntry<X> &ce);
std::ostringstream message;
Item<T> *item;
boost::recursive_mutex object_mutex;
};
template<class T>
inline std::ostream &operator<<(std::ostream &out, const CacheEntry<T> &ce)
{
boost::recursive_mutex::scoped_lock
lk(const_cast<boost::recursive_mutex&>(ce.object_mutex));
out << " CacheEntry: " << (const void *)&ce << std::endl <<
" Item: " << (const void *)ce.item << std::endl;
if(ce.item) {
out <<
" Reference: " << (const void *)&(ce.item->getItem()) <<
std::endl <<
" Ref count: " << ce.item->getReferenceCount() << std::endl;
}
return out;
}
// Message attributes should include a UID
typedef u_int32_t IMAPUID;
typedef int IMAPFlags;
// A (possibly sorted) list of cache entries of attributes for messages
typedef std::vector<CacheEntry<MessageAttributes> > MessageList;
// A map of message UIDs to their attributes
typedef std::map<IMAPUID, CacheEntry<MessageAttributes> > MessageMap;
typedef std::vector<IMAPUID> UIDList;
// UID->thread level map
typedef std::map<IMAPUID, u_int8_t> ThreadLevelMap;
typedef std::vector<std::string> FolderList;
class IMAPError : public std::exception {
const char *msg;
public:
IMAPError(const char *m) throw() { msg = m; }
const char *what() const throw() {
return msg;
}
};
class BadFolder : public IMAPError {
public:
BadFolder(const char *m) throw() : IMAPError(m) {}
};
class InvalidCommand : public IMAPError {
public:
InvalidCommand(const char *m) throw() : IMAPError(m) {}
};
class IMAPFolder;
};
namespace Middleware {
typedef std::map<std::string, IMAP::CacheEntry<IMAP::IMAPFolder> > CacheMap;
typedef unsigned long RequestProcessorID;
};
#endif
See more files for this project here