From 49595a8251899494b0d95c07911841acba47e1a8 Mon Sep 17 00:00:00 2001
From: Artyom Beilis
Date: Sun, 8 Jun 2008 13:37:53 +0000
Subject: [PATCH] Merged cache brunch to trunk: 306:323
/framework/branches/cache
---
Makefile.am | 4 +-
archive.h | 74 ++++++++++++
base_cache.cpp | 41 +++++++
base_cache.h | 25 +++++
cache_interface.cpp | 141 +++++++++++++++++++++++
cache_interface.h | 45 ++++++++
cppcms_error.h | 15 +++
data.h | 77 -------------
global_config.cpp | 29 +++--
global_config.h | 23 ++--
http_error.h | 28 -----
main.cpp | 71 ------------
main_thread.cpp | 266 -------------------------------------------
main_thread.h | 64 -----------
templates.cpp | 231 --------------------------------------
templates.h | 128 ---------------------
text_tool.cpp | 316 +---------------------------------------------------
text_tool.h | 27 +----
textstream.cpp | 25 -----
textstream.h | 39 -------
thread_cache.cpp | 268 ++++++++++++++++++++++++++++++++++++++++++++
thread_cache.h | 56 ++++++++++
thread_pool.cpp | 36 +++---
thread_pool.h | 101 +++++++++--------
url.cpp | 34 +++---
url.h | 32 +++---
worker_thread.cpp | 126 ++++++++++-----------
worker_thread.h | 35 ++++--
28 files changed, 898 insertions(+), 1459 deletions(-)
create mode 100644 archive.h
create mode 100644 base_cache.cpp
create mode 100644 base_cache.h
create mode 100644 cache_interface.cpp
create mode 100644 cache_interface.h
create mode 100644 cppcms_error.h
delete mode 100644 data.h
delete mode 100644 http_error.h
delete mode 100644 main.cpp
delete mode 100644 main_thread.cpp
delete mode 100644 main_thread.h
delete mode 100644 templates.cpp
delete mode 100644 templates.h
delete mode 100644 textstream.cpp
delete mode 100644 textstream.h
create mode 100644 thread_cache.cpp
create mode 100644 thread_cache.h
diff --git a/Makefile.am b/Makefile.am
index 3eec9a2..88e5cc8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,6 +3,6 @@
#test_fcgi_LDADD = libcppcms.la
lib_LTLIBRARIES = libcppcms.la
-libcppcms_la_SOURCES = FCgiIO.cpp global_config.cpp templates.cpp textstream.cpp thread_pool.cpp url.cpp worker_thread.cpp text_tool.cpp
+libcppcms_la_SOURCES = FCgiIO.cpp global_config.cpp thread_pool.cpp url.cpp worker_thread.cpp text_tool.cpp cache_interface.cpp base_cache.cpp thread_cache.cpp
-nobase_pkginclude_HEADERS = global_config.h templates.h text_tool.h url.h http_error.h textstream.h thread_pool.h worker_thread.h FCgiIO.h share/bytecode.h
+nobase_pkginclude_HEADERS = global_config.h text_tool.h url.h cppcms_error.h thread_pool.h worker_thread.h FCgiIO.h cache_interface.h archive.h base_cache.h thread_cache.h
diff --git a/archive.h b/archive.h
new file mode 100644
index 0000000..a3eadc4
--- /dev/null
+++ b/archive.h
@@ -0,0 +1,74 @@
+#ifndef ARCHIVE_H
+#define ARCHIVE_H
+#include "cppcms_error.h"
+#include
+#include
+
+namespace cppcms {
+
+using namespace std;
+
+class archive {
+ string data;
+ size_t ptr;
+public:
+ archive() { ptr=0; };
+ archive(string const &s) : data(s) { ptr=0; };
+ void set(string const &s) { data=s; ptr=0; };
+ string const &get() const { return data; };
+ template
+ archive &operator<<(T const &val) {
+ size_t size=sizeof(T);
+ data.append((char const *)&size,sizeof(size_t));
+ data.append((char const *)&val,size);
+ return *this;
+ }
+ archive &operator<<(string const &val) {
+ size_t size=val.size();
+ data.append((char const *)&size,sizeof(size_t));
+ data.append(val.c_str(),size);
+ return *this;
+ }
+ template
+ archive &operator>>(T &val)
+ {
+ if(ptr+sizeof(size_t)+sizeof(T)>data.size()) {
+ throw cppcms_error("Format violation");
+ }
+ char const *start=data.c_str()+ptr;
+ if(*(size_t const *)start!=sizeof(T)) {
+ throw cppcms_error("Invalid size read");
+ }
+ start+=sizeof(size_t);
+
+ memcpy(&val,start,sizeof(T));
+
+ ptr+=sizeof(size_t)+sizeof(T);
+ return *this;
+ }
+ archive &operator>>(string &val)
+ {
+ if(ptr+sizeof(size_t)>data.size()) {
+ throw cppcms_error("Format violation");
+ }
+ char const *start=data.c_str()+ptr;
+ size_t s=*(size_t const *)start;
+ if(ptr+sizeof(size_t)+s>data.size()) {
+ throw cppcms_error("String too long");
+ }
+ start+=sizeof(size_t);
+ val=string(start,s);
+ ptr+=sizeof(size_t)+s;
+ return *this;
+ }
+};
+
+class serializable {
+public:
+ virtual void load(archive &a) = 0;
+ virtual void save(archive &a) const = 0;
+ virtual ~serializable() {};
+};
+}
+
+#endif
diff --git a/base_cache.cpp b/base_cache.cpp
new file mode 100644
index 0000000..f1bf476
--- /dev/null
+++ b/base_cache.cpp
@@ -0,0 +1,41 @@
+#include "base_cache.h"
+namespace cppcms {
+
+using namespace std;
+
+bool base_cache::fetch_page(string const &key,string &output,bool gzip)
+{
+ return false;
+}
+bool base_cache::fetch(string const &key,archive &a,set &tags)
+{
+ return false;
+};
+
+void base_cache::clear()
+{
+ // Nothing
+}
+void base_cache::rise(string const &trigger)
+{
+ // Nothing
+}
+
+void base_cache::store(string const &key,set const &triggers,time_t timeout,archive const &a)
+{
+ // Nothing
+}
+
+base_cache::~base_cache()
+{
+ // Nothing
+}
+
+void base_cache::stats(unsigned &keys,unsigned &triggers)
+{
+ keys=0;
+ triggers=0;
+}
+
+}
+
diff --git a/base_cache.h b/base_cache.h
new file mode 100644
index 0000000..1896ef2
--- /dev/null
+++ b/base_cache.h
@@ -0,0 +1,25 @@
+#ifndef BASE_CACHE_H
+#define BASE_CACHE_H
+
+#include
+#include
+#include "archive.h"
+
+namespace cppcms {
+
+using namespace std;
+
+class base_cache {
+public:
+ virtual bool fetch_page(string const &key,string &output,bool gzip);
+ virtual bool fetch(string const &key,archive &a,set &tags);
+ virtual void rise(string const &trigger);
+ virtual void clear();
+ virtual void store(string const &key,set const &triggers,time_t timeout,archive const &a);
+ virtual void stats(unsigned &keys,unsigned &triggers);
+ virtual ~base_cache();
+};
+
+}
+
+#endif
diff --git a/cache_interface.cpp b/cache_interface.cpp
new file mode 100644
index 0000000..dcea471
--- /dev/null
+++ b/cache_interface.cpp
@@ -0,0 +1,141 @@
+#include "cache_interface.h"
+#include "worker_thread.h"
+#include "global_config.h"
+#include
+#include
+#include
+#include
+
+namespace cppcms {
+using namespace std;
+
+void deflate(string const &text,ostream &stream)
+{
+ using namespace boost::iostreams;
+ gzip_params params;
+ long level,length;
+ if((level=global_config.lval("gzip.level",-1))!=-1){
+ params.level=level;
+ }
+
+ filtering_ostream zstream;
+
+ if((length=global_config.lval("gzip.buffer",-1))!=-1){
+ zstream.push(gzip_compressor(params,length));
+ }
+ else {
+ zstream.push(gzip_compressor(params));
+ }
+
+ zstream.push(stream);
+ zstream<caching_module) return false;
+ if(cms->caching_module->fetch_page(key,cms->out,cms->gzip)) {
+ cms->gzip_done=true;
+ return true;
+ }
+ return false;
+}
+
+void cache_iface::store_page(string const &key,time_t timeout)
+{
+ if(!cms->caching_module) return;
+ archive a;
+ string compr=deflate(cms->out);
+ a<<(cms->out)<gzip){
+ cms->out=compr;
+ cms->gzip_done=true;
+ }
+ cms->caching_module->store(key,triggers,timeout,a);
+}
+
+void cache_iface::add_trigger(string const &t)
+{
+ if(!cms->caching_module) return;
+ triggers.insert(t);
+}
+
+void cache_iface::rise(string const &t)
+{
+ if(!cms->caching_module) return;
+ cms->caching_module->rise(t);
+}
+
+bool cache_iface::fetch_data(string const &key,serializable &data)
+{
+ if(!cms->caching_module) return false;
+ archive a;
+ set new_trig;
+ if(cms->caching_module->fetch(key,a,new_trig)) {
+ data.load(a);
+ triggers.insert(new_trig.begin(),new_trig.end());
+ return true;
+ }
+ return false;
+}
+
+void cache_iface::store_data(string const &key,serializable const &data,
+ set const &triggers,
+ time_t timeout)
+{
+ if(!cms->caching_module) return;
+ archive a;
+ data.save(a);
+ this->triggers.insert(triggers.begin(),triggers.end());
+ cms->caching_module->store(key,triggers,timeout,a);
+}
+
+bool cache_iface::fetch_frame(string const &key,string &result)
+{
+ if(!cms->caching_module) return false;
+ archive a;
+ set new_trig;
+ if(cms->caching_module->fetch(key,a,new_trig)) {
+ a>>result;
+ triggers.insert(new_trig.begin(),new_trig.end());
+ return true;
+ }
+ return false;
+}
+
+void cache_iface::store_frame(string const &key,string const &data,
+ set const &triggers,
+ time_t timeout)
+{
+ if(!cms->caching_module) return;
+ archive a;
+ a<triggers.insert(triggers.begin(),triggers.end());
+ cms->caching_module->store(key,triggers,timeout,a);
+}
+
+void cache_iface::clear()
+{
+ if(cms->caching_module)
+ cms->caching_module->clear();
+}
+
+bool cache_iface::stats(unsigned &k,unsigned &t)
+{
+ if(!cms->caching_module)
+ return false;
+ cms->caching_module->stats(k,t);
+ return true;
+}
+
+} // End of namespace cppcms
diff --git a/cache_interface.h b/cache_interface.h
new file mode 100644
index 0000000..9ee784b
--- /dev/null
+++ b/cache_interface.h
@@ -0,0 +1,45 @@
+#ifndef CACHE_IFACE_H
+#define CACHE_IFACE_H
+
+#include
+#include
+#include "archive.h"
+
+using namespace std;
+
+namespace cppcms {
+
+const time_t infty=(sizeof(time_t)==4 ? 0x7FFFFFFF: 0x7FFFFFFFFFFFFFFFULL );
+
+class worker_thread;
+class cache_iface {
+ worker_thread *cms;
+ set triggers;
+public:
+ void reset() { triggers.clear(); };
+ cache_iface(worker_thread *w) : cms (w) {};
+ bool fetch_page(string const &key);
+ void store_page(string const &key,time_t timeout=infty);
+ void rise(string const &trigger);
+ void add_trigger(string const &trigger);
+ bool fetch_frame(string const &key,string &result);
+ void store_frame(string const &key,
+ string const &frame,
+ set const &triggers=set(),
+ time_t timeout=infty);
+ bool fetch_data(string const &key,serializable &data);
+ void store_data(string const &key,serializable const &data,
+ set const &triggers=set(),
+ time_t timeout=infty);
+ void clear();
+ bool stats(unsigned &keys,unsigned &triggers);
+
+};
+
+void deflate(string const &text,ostream &stream);
+string deflate(string const &text);
+
+
+}
+
+#endif
diff --git a/cppcms_error.h b/cppcms_error.h
new file mode 100644
index 0000000..5a08e97
--- /dev/null
+++ b/cppcms_error.h
@@ -0,0 +1,15 @@
+#ifndef _HTTP_ERROR_H
+#define _HTTP_ERROR_H
+
+#include
+#include
+
+namespace cppcms {
+
+class cppcms_error : public std::runtime_error {
+public:
+ cppcms_error(std::string const &error) : std::runtime_error(error) {};
+};
+
+}
+#endif /* _HTTP_ERROR_H */
diff --git a/data.h b/data.h
deleted file mode 100644
index 189fde2..0000000
--- a/data.h
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// C++ Interface: data
-//
-// Description:
-//
-//
-// Author: artik , (C) 2007
-//
-// Copyright: See COPYING file that comes with this distribution
-//
-//
-
-#ifndef DATA_H
-#define DATA_H
-
-#include "easy_bdb.h"
-
-using namespace ebdb;
-
-typedef varchar<32> username_t;
-typedef varchar<16> password_t;
-
-struct user_t {
- int id;
- username_t username;
- password_t password;
-};
-
-struct message_t {
- int id;
- int user_id;
- long text_id;
-};
-
-class Users {
-public:
- typedef Index_Auto_Increment id_idx;
- typedef id_idx::cursor_t id_c;
- id_idx id;
-
- typedef Index_Var username_idx;
- typedef username_idx::cursor_t username_c;
- username_idx username;
- Users(Environment &env) :
- id(env,"users_id.db",DB_BTREE),
- username(env,"users_username.db",DB_BTREE,&id)
- {};
- void open() { id.open(); username.open();};
- void create() { id.create(); username.create();};
- void close() { id.close(); username.close();};
-};
-
-class Messages {
-public:
- typedef Index_Auto_Increment id_idx;
- typedef id_idx::cursor_t id_c;
- id_idx id;
-
- Messages(Environment &env) :
- id(env,"messages_id.db",DB_BTREE)
- {};
- void open() { id.open();};
- void create() { id.create(); };
- void close() { id.close();};
-};
-
-
-
-
-extern auto_ptr users;
-extern auto_ptr all_messages;
-extern auto_ptr texts;
-
-
-
-
-#endif
diff --git a/global_config.cpp b/global_config.cpp
index 7aa55bc..994b029 100644
--- a/global_config.cpp
+++ b/global_config.cpp
@@ -2,9 +2,12 @@
#include
#include
-Global_Config global_config;
-bool Global_Config::get_tocken(FILE *f,tocken_t &T)
+namespace cppcms {
+
+cppcms_config global_config;
+
+bool cppcms_config::get_tocken(FILE *f,tocken_t &T)
{
int c;
while((c=fgetc(f))!=EOF) {
@@ -52,7 +55,7 @@ bool Global_Config::get_tocken(FILE *f,tocken_t &T)
}
}
if(T.second=="-" || T.second=="." || T.second=="-.") {
- throw HTTP_Error("Illegal charrecters");
+ throw cppcms_error("Illegal charrecters");
}
if(c!=EOF) {
ungetc(c,f);
@@ -75,7 +78,7 @@ bool Global_Config::get_tocken(FILE *f,tocken_t &T)
}
}
if(c==EOF){
- throw HTTP_Error("Unexpected EOF ");
+ throw cppcms_error("Unexpected EOF ");
}
if(c=='\n') line_counter++;
if(c=='\"') {
@@ -97,13 +100,13 @@ bool Global_Config::get_tocken(FILE *f,tocken_t &T)
}
else {
- throw HTTP_Error(string("Unexpected charrecter")+(char)c);
+ throw cppcms_error(string("Unexpected charrecter")+(char)c);
}
}
return false;
}
-void Global_Config::load(char const *fname)
+void cppcms_config::load(char const *fname)
{
if(loaded){
return;
@@ -111,7 +114,7 @@ void Global_Config::load(char const *fname)
FILE *f=fopen(fname,"r");
line_counter=1;
if(!f) {
- throw HTTP_Error(string("Failed to open file:")+fname);
+ throw cppcms_error(string("Failed to open file:")+fname);
}
tocken_t T;
string key;
@@ -164,21 +167,21 @@ void Global_Config::load(char const *fname)
}
}
if(state!=0) {
- throw HTTP_Error("Parsing error");
+ throw cppcms_error("Parsing error");
}
}
- catch (HTTP_Error &err){
+ catch (cppcms_error &err){
fclose(f);
char stmp[32];
snprintf(stmp,32," at line %d",line_counter);
- throw HTTP_Error(string(err.get())+stmp);
+ throw cppcms_error(string(err.what())+stmp);
}
fclose(f);
loaded=true;
}
-void Global_Config::load(int argc,char *argv[],char const *def)
+void cppcms_config::load(int argc,char *argv[],char const *def)
{
if(loaded) {
return;
@@ -196,7 +199,9 @@ void Global_Config::load(int argc,char *argv[],char const *def)
}
}
if(def_file==NULL) {
- throw HTTP_Error("Configuration file not defined");
+ throw cppcms_error("Configuration file not defined");
}
load(def_file);
}
+
+}
diff --git a/global_config.h b/global_config.h
index 5cae95d..dd37ce2 100644
--- a/global_config.h
+++ b/global_config.h
@@ -3,18 +3,19 @@
#include
#include
\n";
- state=NOTHING;
- }
- else {
- out+=content;
- }
- break;
- case CODE:
- if(input!=L_CODE) {
- out+="\n";
- state=NOTHING;
- }
- else {
- out+=content;
- }
- break;
- case UL:
- if(input==L_TEXT){
- out+=content;
- }
- else if(input==L_UL){
- out+="\n";
- out+="\n";
- out+=content;
- }
- else {
- out+="\n";
- state=NOTHING;
- }
- break;
- case OL:
- if(input==L_TEXT){
- out+=content;
- }
- else if(input==L_OL){
- out+="\n";
- out+="\n";
- out+=content;
- }
- else {
- out+="\n";
- state=NOTHING;
- }
- break;
- case P:
- if(input!=L_TEXT) {
- out+="\n";
- state=NOTHING;
- }
- else {
- out+=content;
- }
- break;
- };
- if(state==NOTHING) {
- switch(input){
- case L_BLANK: break;
- case L_TEXT:
- out+="\n";
- out+=content;
- state=P;
- break;
- case L_H:
- {
- char buf[16];
- snprintf(buf,16,"",header_level);
- out+=buf;
- out+=content;
- snprintf(buf,16,"\n",header_level);
- out+=buf;
- }
- break;
- case L_QUOTE:
- out+="
\n";
- out+=content;
- state=QUOTE;
- break;
- case L_CODE:
- out+="
\n";
- out+=content;
- state=CODE;
- break;
- case L_UL:
- out+="
- \n";
- out+=content;
- state=UL;
- break;
- case L_OL:
- out+="
- \n";
- out+=content;
- state=OL;
- break;
- case L_EOF:
- state=FINISH;
- break;
- }
- }
- }
}
diff --git a/text_tool.h b/text_tool.h
index d019b31..cfef427 100644
--- a/text_tool.h
+++ b/text_tool.h
@@ -3,27 +3,12 @@
#include
-using std::string;
-
-class Text_Tool {
- // State definitions:
- enum { NOTHING, QUOTE, CODE, UL, OL, P, FINISH };
- enum { L_BLANK, L_TEXT, L_H, L_QUOTE, L_CODE, L_UL, L_OL ,L_EOF };
- int state;
- int input;
- size_t ptr;
- string content;
- int header_level;
- void getline(string &s);
- void init();
- void to_html(string s);
- void basic_to_html(string s);
-public:
- void markdown2html(char const *in,string &out);
- void markdown2html(string &in,string &out) { markdown2html(in.c_str(),out);};
- void text2html(char const *s,string &);
- void text2html(string &s,string &out) { text2html(s.c_str(),out);};
- void text2url(char const *s,string &);
+namespace cppcms {
+ namespace texttool {
+ using std::string;
+ void text2html(string const &s,string &out);
+ void text2url(string const &s,string &out);
+ };
};
diff --git a/textstream.cpp b/textstream.cpp
deleted file mode 100644
index 5b1612c..0000000
--- a/textstream.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "textstream.h"
-#include
-#include
-
-
-void Text_Stream::printf(char *format,...)
-{
- va_list ap;
-
- va_start(ap,format);
- int size=vsnprintf(buffer,TS_BUFFER_SIZE,format,ap);
- va_end(ap);
-
- if(size>TS_BUFFER_SIZE) {
- char *str=new char[size+1];
- va_start(ap,format);
- vsnprintf(str,size+1,format,ap);
- va_end(ap);
- text += str;
- delete [] str;
- }
- else {
- text += buffer;
- }
-}
diff --git a/textstream.h b/textstream.h
deleted file mode 100644
index 2814a93..0000000
--- a/textstream.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef _TEXTSTREAM_H
-#define _TEXTSTREAM_H
-
-#include
-
-#include "global_config.h"
-
-using namespace std;
-
-#define TS_BUFFER_SIZE 1024
-#define TS_INITAL_ALLOCATION 0x10000
-
-
-class Text_Stream {
- int inital_alloc;
- char buffer[TS_BUFFER_SIZE];
- string text;
- friend class Renderer;
-public:
- void reset(void) {
- text.clear();
- text.reserve(inital_alloc);
- };
- Text_Stream() {
- inital_alloc=global_config.lval("performance.textalloc",
- TS_INITAL_ALLOCATION);
- reset();
- };
- ~Text_Stream() {};
- void puts(char const *t) { text += t; };
- void putchar(char c) { text += c; };
- char const *get() { return text.c_str(); };
- string &getstring() { return text; };
- int len() { return text.size(); };
- void printf(char *format, ...);
- void puts(string &str) { text += str; };
-};
-
-#endif /* _TEXTSTREAM_H */
diff --git a/thread_cache.cpp b/thread_cache.cpp
new file mode 100644
index 0000000..14da9f5
--- /dev/null
+++ b/thread_cache.cpp
@@ -0,0 +1,268 @@
+#include "thread_cache.h"
+#include
+#include
+
+using boost::format;
+using boost::str;
+
+namespace cppcms {
+
+class mutex_lock {
+ pthread_mutex_t &m;
+public:
+ mutex_lock(pthread_mutex_t &p): m(p) { pthread_mutex_lock(&m); };
+ ~mutex_lock() { pthread_mutex_unlock(&m); };
+};
+
+class rwlock_rdlock {
+ pthread_rwlock_t &m;
+public:
+ rwlock_rdlock(pthread_rwlock_t &p): m(p) { pthread_rwlock_rdlock(&m); };
+ ~rwlock_rdlock() { pthread_rwlock_unlock(&m); };
+};
+
+class rwlock_wrlock {
+ pthread_rwlock_t &m;
+public:
+ rwlock_wrlock(pthread_rwlock_t &p): m(p) { pthread_rwlock_wrlock(&m); };
+ ~rwlock_wrlock() { pthread_rwlock_unlock(&m); };
+};
+
+thread_cache::~thread_cache()
+{
+ pthread_mutex_destroy(&lru_mutex);
+ pthread_rwlock_destroy(&access_lock);
+}
+
+string *thread_cache::get(string const &key,set *triggers)
+{
+ pointer p;
+ time_t now;
+ time(&now);
+ if(debug_mode) print_all();
+ if((p=primary.find(key))==primary.end() || p->second.timeout->first < now) {
+ if(debug_mode) {
+ string res;
+ if(p==primary.end()) {
+ res=str(boost::format("Not found [%1%]\n") % key);
+ }
+ else {
+ res=str(boost::format("Found [%1%] but timeout of %2% seconds\n")
+ % key % (now - p->second.timeout->first));
+ }
+ write(fd,res.c_str(),res.size());
+ }
+ return NULL;
+ }
+ if(triggers) {
+ list::iterator tp;
+ for(tp=p->second.triggers.begin();tp!=p->second.triggers.end();tp++) {
+ triggers->insert((*tp)->first);
+ }
+ }
+ {
+ mutex_lock lock(lru_mutex);
+ lru.erase(p->second.lru);
+ lru.push_front(p);
+ p->second.lru=lru.begin();
+ }
+ if(debug_mode){
+ string res=str(boost::format("Fetched [%1%] triggers:") % key);
+ list::iterator tp;
+ for(tp=p->second.triggers.begin();
+ tp!=p->second.triggers.end();tp++)
+ {
+ res+=(*tp)->first;
+ res+=" ";
+ }
+ res+="\n";
+ write(fd,res.c_str(),res.size());
+ }
+ return &(p->second.data);
+}
+
+bool thread_cache::fetch_page(string const &key,string &out,bool gzip)
+{
+ rwlock_rdlock lock(access_lock);
+ string *r=get(key,NULL);
+ if(!r) return false;
+ size_t size=r->size();
+ size_t s;
+ char const *ptr=r->c_str();
+ if(sizesize-sizeof(size_t))
+ return false;
+ if(!gzip){
+ out.assign(ptr+sizeof(size_t),s);
+ }
+ else {
+ ptr+=s+sizeof(size_t);
+ size-=s+sizeof(size_t);
+ if(size &tags)
+{
+ rwlock_rdlock lock(access_lock);
+ string *r=get(key,&tags);
+ if(!r) return false;
+ a.set(*r);
+ return true;
+}
+
+void thread_cache::clear()
+{
+ rwlock_wrlock lock(access_lock);
+ timeout.clear();
+ lru.clear();
+ primary.clear();
+ triggers.clear();
+}
+void thread_cache::stats(unsigned &keys,unsigned &triggers)
+{
+ rwlock_rdlock lock(access_lock);
+ keys=primary.size();
+ triggers=this->triggers.size();
+}
+
+void thread_cache::rise(string const &trigger)
+{
+ rwlock_wrlock lock(access_lock);
+ if(debug_mode) print_all();
+ pair range=triggers.equal_range(trigger);
+ triggers_ptr p;
+ list kill_list;
+ for(p=range.first;p!=range.second;p++) {
+ kill_list.push_back(p->second);
+ }
+ list::iterator lptr;
+ if(debug_mode){
+ string out=str(boost::format("Trigger [%1%] dropping: ") % trigger);
+ write(fd,out.c_str(),out.size());
+ }
+
+ for(lptr=kill_list.begin();lptr!=kill_list.end();lptr++) {
+ if(debug_mode) {
+ write(fd,(*lptr)->first.c_str(),(*lptr)->first.size());
+ write(fd," ",1);
+ }
+ delete_node(*lptr);
+ }
+ if(debug_mode)
+ write(fd,"\n",1);
+}
+
+void thread_cache::store(string const &key,set const &triggers_in,time_t timeout_in,archive const &a)
+{
+ rwlock_wrlock lock(access_lock);
+ if(debug_mode) print_all();
+ pointer main;
+ if(debug_mode) {
+ string res;
+ res=str(boost::format("Storing key [%1%], triggers:") % key);
+ for(set::iterator ps=triggers_in.begin(),pe=triggers_in.end();ps!=pe;ps++) {
+ res+=*ps;
+ res+=" ";
+ }
+ res+="\n";
+ write(fd,res.c_str(),res.size());
+ }
+ main=primary.find(key);
+ if(main==primary.end() && primary.size()>=limit && limit>0) {
+ if(debug_mode) {
+ char const *msg="Not found, size limit\n";
+ write(fd,msg,strlen(msg));
+ }
+ time_t now;
+ time(&now);
+ if(timeout.begin()->firstsecond;
+ if(debug_mode) {
+ string res;
+ res=str(boost::format("Deleting timeout node [%1%] with "
+ "delta of %2% seconds\n") % main->first
+ % (now - main->second.timeout->first));
+ write(fd,res.c_str(),res.size());
+ }
+ }
+ else {
+ main=lru.back();
+ if(debug_mode) {
+ string res;
+ res=str(boost::format("Deleting LRU [%1%]\n") % main->first);
+ write(fd,res.c_str(),res.size());
+ }
+ }
+ }
+ if(main!=primary.end())
+ delete_node(main);
+ pair res=primary.insert(pair(key,container()));
+ main=res.first;
+ container &cont=main->second;
+ cont.data=a.get();
+ lru.push_front(main);
+ cont.lru=lru.begin();
+ cont.timeout=timeout.insert(pair(timeout_in,main));
+ if(triggers_in.find(key)==triggers_in.end()){
+ cont.triggers.push_back(triggers.insert(pair(key,main)));
+ }
+ set::const_iterator si;
+ for(si=triggers_in.begin();si!=triggers_in.end();si++) {
+ cont.triggers.push_back(triggers.insert(pair(*si,main)));
+ }
+}
+
+void thread_cache::delete_node(pointer p)
+{
+ lru.erase(p->second.lru);
+ timeout.erase(p->second.timeout);
+ list::iterator i;
+ for(i=p->second.triggers.begin();i!=p->second.triggers.end();i++) {
+ triggers.erase(*i);
+ }
+ primary.erase(p);
+}
+
+void thread_cache::print_all()
+{
+ string res;
+ res+="Printing stored keys\n";
+ int N_triggers=0;
+ int N_keys=0;
+ time_t now;
+ time(&now);
+ for(pointer p=primary.begin();p!=primary.end();p++) {
+ N_keys++;
+ res+=str(boost::format("%1%: timeount in %2% sec, triggers:") % p->first
+ % (p->second.timeout->first - now));
+ for(list::iterator p1=p->second.triggers.begin(),
+ p2=p->second.triggers.end();
+ p2!=p1;p1++)
+ {
+ N_triggers++;
+ res+=(*p1)->first;
+ res+=" ";
+ }
+ res+="\n";
+ }
+ res+="LRU order:";
+ for(list::iterator pl=lru.begin();pl!=lru.end();pl++) {
+ res+=(*pl)->first;
+ res+=" ";
+ }
+ res+="\n";
+ if(N_keys!=timeout.size() || N_keys!=lru.size() || N_triggers!=triggers.size()){
+ res+=str(boost::format("Internal error #prim=%1%, #lru=%2%, "
+ "#prim.triggers=%3% #triggers=%4%\n")
+ % N_keys % lru.size() % N_triggers % triggers.size());
+ }
+ else {
+ res+=str(boost::format("#Keys=%1% #Triggers=%2%\n") % N_keys % N_triggers);
+ }
+ write(fd,res.c_str(),res.size());
+}
+
+};
diff --git a/thread_cache.h b/thread_cache.h
new file mode 100644
index 0000000..40db690
--- /dev/null
+++ b/thread_cache.h
@@ -0,0 +1,56 @@
+#ifndef THREAD_CHACHE_H
+#define THREAD_CHACHE_H
+#include "base_cache.h"
+#include "pthread.h"
+#include