ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

501 lines
13 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
  4. //
  5. // See accompanying file COPYING.TXT file for licensing details.
  6. //
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #define CPPCMS_SOURCE
  9. // MUST BE FIRST TO COMPILE CORRECTLY UNDER CYGWIN
  10. #include <cppcms/defs.h>
  11. #ifndef CPPCMS_WIN32
  12. #if defined(__sun)
  13. #define _POSIX_PTHREAD_SEMANTICS
  14. #endif
  15. #include <signal.h>
  16. #endif
  17. #include "tcp_cache_protocol.h"
  18. #include "cache_storage.h"
  19. #include <cppcms/cppcms_error.h>
  20. #include <cppcms/config.h>
  21. #include <cppcms/session_storage.h>
  22. #include <cppcms_boost/bind.hpp>
  23. #include <booster/shared_ptr.h>
  24. #include <booster/enable_shared_from_this.h>
  25. #include <booster/thread.h>
  26. #include <booster/aio/socket.h>
  27. #include <booster/aio/io_service.h>
  28. #include <booster/aio/endpoint.h>
  29. #include <booster/aio/buffer.h>
  30. #include <booster/aio/deadline_timer.h>
  31. #include <booster/log.h>
  32. #include <time.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <iostream>
  36. #include "tcp_cache_server.h"
  37. namespace boost = cppcms_boost;
  38. namespace cppcms {
  39. namespace impl {
  40. namespace io = booster::aio;
  41. class tcp_cache_service::session : public booster::enable_shared_from_this<tcp_cache_service::session> {
  42. std::vector<char> data_in_;
  43. std::string data_out_;
  44. cppcms::impl::tcp_operation_header hout_;
  45. cppcms::impl::tcp_operation_header hin_;
  46. public:
  47. io::stream_socket socket_;
  48. booster::intrusive_ptr<cppcms::impl::base_cache> cache_;
  49. booster::shared_ptr<cppcms::sessions::session_storage> sessions_;
  50. session(io::io_service &srv,
  51. booster::intrusive_ptr<cppcms::impl::base_cache> c,
  52. booster::shared_ptr<cppcms::sessions::session_storage_factory> f) :
  53. socket_(srv),
  54. cache_(c)
  55. {
  56. if(f) {
  57. sessions_ = f->get();
  58. }
  59. }
  60. void run()
  61. {
  62. socket_.async_read(io::buffer(&hin_,sizeof(hin_)),
  63. boost::bind(&session::on_header_in,shared_from_this(),
  64. _1));
  65. }
  66. void on_header_in(booster::system::error_code const &e)
  67. {
  68. if(e) { handle_error(e); return; }
  69. data_in_.clear();
  70. data_in_.resize(hin_.size);
  71. if(hin_.size > 0) {
  72. socket_.async_read(io::buffer(data_in_),
  73. boost::bind(&session::on_data_in,shared_from_this(),
  74. _1));
  75. }
  76. else {
  77. on_data_in(e);
  78. }
  79. }
  80. void fetch()
  81. {
  82. std::string a;
  83. std::set<std::string> tags,*ptags=0;
  84. std::string key;
  85. key.assign(data_in_.begin(),data_in_.end());
  86. if(hin_.operations.fetch.transfer_triggers)
  87. ptags=&tags;
  88. uint64_t generation;
  89. time_t timeout;
  90. if(!cache_->fetch(key,&a,ptags,&timeout,&generation)) {
  91. hout_.opcode=opcodes::no_data;
  92. return;
  93. }
  94. if(hin_.operations.fetch.transfer_if_not_uptodate
  95. && generation==hin_.operations.fetch.current_gen)
  96. {
  97. hout_.opcode=opcodes::uptodate;
  98. return;
  99. }
  100. hout_.opcode=opcodes::data;
  101. data_out_.swap(a);
  102. hout_.operations.data.data_len=data_out_.size();
  103. if(ptags) {
  104. for(std::set<std::string>::iterator p=tags.begin(),e=tags.end();p!=e;++p) {
  105. data_out_.append(p->c_str(),p->size()+1);
  106. }
  107. }
  108. hout_.operations.data.triggers_len=data_out_.size()-hout_.operations.data.data_len;
  109. hout_.size=data_out_.size();
  110. hout_.operations.data.generation=generation;
  111. hout_.operations.data.timeout = timeout;
  112. }
  113. void rise()
  114. {
  115. std::string key;
  116. key.assign(data_in_.begin(),data_in_.end());
  117. cache_->rise(key);
  118. hout_.opcode=opcodes::done;
  119. }
  120. void clear()
  121. {
  122. cache_->clear();
  123. hout_.opcode=opcodes::done;
  124. }
  125. void stats()
  126. {
  127. unsigned k,t;
  128. cache_->stats(k,t);
  129. hout_.opcode=opcodes::out_stats;
  130. hout_.operations.out_stats.keys=k;
  131. hout_.operations.out_stats.triggers=t;
  132. }
  133. bool load_triggers(std::set<std::string> &triggers,char const *start,unsigned len)
  134. {
  135. int slen=len;
  136. while(slen>0) {
  137. unsigned size=strlen(start);
  138. if(size==0) {
  139. return false;
  140. }
  141. std::string tmp;
  142. tmp.assign(start,size);
  143. slen-=size+1;
  144. start+=size+1;
  145. triggers.insert(tmp);
  146. }
  147. return true;
  148. }
  149. void store()
  150. {
  151. std::set<std::string> triggers;
  152. if( hin_.operations.store.key_len
  153. +hin_.operations.store.data_len
  154. +hin_.operations.store.triggers_len != hin_.size
  155. || hin_.operations.store.key_len == 0)
  156. {
  157. hout_.opcode=opcodes::error;
  158. return;
  159. }
  160. std::string ts;
  161. std::vector<char>::iterator p=data_in_.begin()
  162. +hin_.operations.store.key_len
  163. +hin_.operations.store.data_len;
  164. ts.assign(p,p + hin_.operations.store.triggers_len);
  165. if(!load_triggers(triggers,ts.c_str(),
  166. hin_.operations.store.triggers_len))
  167. {
  168. hout_.opcode=opcodes::error;
  169. return;
  170. }
  171. time_t timeout=to_time_t(hin_.operations.store.timeout);
  172. std::string key;
  173. key.assign(data_in_.begin(),data_in_.begin()+hin_.operations.store.key_len);
  174. std::string data;
  175. data.assign(data_in_.begin()+hin_.operations.store.key_len,
  176. data_in_.begin() + hin_.operations.store.key_len + hin_.operations.store.data_len);
  177. cache_->store(key,data,triggers,timeout);
  178. hout_.opcode=opcodes::done;
  179. }
  180. void save()
  181. {
  182. if(hin_.size < 32)
  183. {
  184. hout_.opcode=opcodes::error;
  185. return;
  186. }
  187. time_t timeout=to_time_t(hin_.operations.session_save.timeout);
  188. std::string sid(data_in_.begin(),data_in_.begin()+32);
  189. std::string value(data_in_.begin()+32,data_in_.end());
  190. sessions_->save(sid,timeout,value);
  191. hout_.opcode=opcodes::done;
  192. }
  193. void load()
  194. {
  195. if(hin_.size!=32) {
  196. hout_.opcode=opcodes::error;
  197. return;
  198. }
  199. time_t timeout;
  200. int toffset;
  201. std::string sid(data_in_.begin(),data_in_.end());
  202. if(!sessions_->load(sid,timeout,data_out_) || (toffset=(timeout)) < 0) {
  203. hout_.opcode=opcodes::no_data;
  204. return;
  205. }
  206. hout_.opcode=opcodes::session_load_data;
  207. hout_.size=data_out_.size();
  208. hout_.operations.session_data.timeout=toffset;
  209. }
  210. void remove()
  211. {
  212. if(hin_.size!=32) {
  213. hout_.opcode=opcodes::error;
  214. return;
  215. }
  216. std::string sid(data_in_.begin(),data_in_.end());
  217. sessions_->remove(sid);
  218. }
  219. void handle_error(booster::system::error_code const &e)
  220. {
  221. if(e.category() == booster::aio::aio_error_cat && e.value() == booster::aio::aio_error::eof) {
  222. BOOSTER_DEBUG("cppcms_scale") << "Client disconnected, fd=" << socket_.native()
  223. <<"; " << e.message();
  224. return;
  225. }
  226. BOOSTER_WARNING("cppcms_scale") << "Error on connection, fd=" << socket_.native()
  227. <<"; " << e.message();
  228. }
  229. void on_data_in(booster::system::error_code const &e)
  230. {
  231. if(e) {
  232. handle_error(e);
  233. return;
  234. }
  235. memset(&hout_,0,sizeof(hout_));
  236. BOOSTER_DEBUG("cppcms_scale") << "Received command " << hin_.opcode << "("
  237. << opcodes::to_name(hin_.opcode) <<"); fd="<< socket_.native();
  238. switch(hin_.opcode) {
  239. case opcodes::fetch:
  240. case opcodes::rise:
  241. case opcodes::clear:
  242. case opcodes::store:
  243. case opcodes::stats:
  244. if(!cache_)
  245. hout_.opcode=opcodes::error;
  246. break;
  247. case opcodes::session_save:
  248. case opcodes::session_load:
  249. case opcodes::session_remove:
  250. if(!sessions_)
  251. hout_.opcode=opcodes::error;
  252. break;
  253. default:
  254. hout_.opcode=opcodes::error;
  255. }
  256. if(hout_.opcode!=opcodes::error) {
  257. switch(hin_.opcode){
  258. case opcodes::fetch: fetch(); break;
  259. case opcodes::rise: rise(); break;
  260. case opcodes::clear: clear(); break;
  261. case opcodes::store: store(); break;
  262. case opcodes::stats: stats(); break;
  263. case opcodes::session_save: save(); break;
  264. case opcodes::session_load: load(); break;
  265. case opcodes::session_remove: remove(); break;
  266. default:
  267. hout_.opcode=opcodes::error;
  268. }
  269. }
  270. BOOSTER_DEBUG("cppcms_scale") << "Returning answer " << hout_.opcode << "("
  271. << opcodes::to_name(hout_.opcode) <<"); fd="<< socket_.native();
  272. io::const_buffer packet = io::buffer(&hout_,sizeof(hout_));
  273. if(hout_.size > 0) {
  274. packet += io::buffer(data_out_.c_str(),hout_.size);
  275. }
  276. socket_.async_write(packet,
  277. boost::bind(&session::on_data_out,shared_from_this(),
  278. _1));
  279. }
  280. void on_data_out(booster::system::error_code const &e)
  281. {
  282. if(e) { handle_error(e); return; }
  283. run();
  284. }
  285. };
  286. class tcp_cache_service::server {
  287. io::acceptor acceptor_;
  288. size_t counter;
  289. booster::intrusive_ptr<cppcms::impl::base_cache> cache_;
  290. std::vector<io::io_service *> services_;
  291. booster::shared_ptr<cppcms::sessions::session_storage_factory> sessions_;
  292. void on_accept(booster::system::error_code const &e,booster::shared_ptr<tcp_cache_service::session> s)
  293. {
  294. if(!e) {
  295. BOOSTER_DEBUG("cppcms_scale") << "Accepted connection, fd=" << s->socket_.native();
  296. s->socket_.set_option(io::stream_socket::tcp_no_delay,true);
  297. if(&acceptor_.get_io_service() == &s->socket_.get_io_service()) {
  298. s->run();
  299. }
  300. else {
  301. s->socket_.get_io_service().post(boost::bind(&session::run,s));
  302. }
  303. start_accept();
  304. }
  305. else {
  306. BOOSTER_ERROR("cppcms_scale") << "Failed to accept connection:" << e.message();
  307. }
  308. }
  309. io::io_service &get_next_io_service()
  310. {
  311. int id = counter++;
  312. if(counter >= services_.size())
  313. counter = 0;
  314. return *services_[id];
  315. }
  316. void start_accept()
  317. {
  318. booster::shared_ptr<session> s(new session(get_next_io_service(),cache_,sessions_));
  319. acceptor_.async_accept(s->socket_,boost::bind(&server::on_accept,this,_1,s));
  320. }
  321. public:
  322. server( std::vector<booster::shared_ptr<io::io_service> > &io,
  323. std::string ip,
  324. int port,
  325. booster::intrusive_ptr<cppcms::impl::base_cache> c,
  326. booster::shared_ptr<cppcms::sessions::session_storage_factory> f
  327. ):
  328. acceptor_(*io[0]),
  329. counter(0),
  330. cache_(c),
  331. sessions_(f)
  332. {
  333. services_.resize(io.size());
  334. for(size_t i=0;i<io.size();i++)
  335. services_[i] = io[i].get();
  336. io::endpoint ep(ip,port);
  337. acceptor_.open(ep.family());
  338. acceptor_.set_option(io::basic_socket::reuse_address,true);
  339. acceptor_.bind(ep);
  340. acceptor_.listen(10);
  341. start_accept();
  342. }
  343. };
  344. class garbage_collector
  345. {
  346. public:
  347. garbage_collector( booster::shared_ptr<cppcms::sessions::session_storage_factory> f,
  348. int seconds)
  349. : timer_(srv_),
  350. io_(f),
  351. seconds_(seconds)
  352. {
  353. }
  354. void async_run(booster::system::error_code const &e)
  355. {
  356. if(e) return;
  357. timer_.expires_from_now(booster::ptime::seconds(seconds_));
  358. timer_.async_wait(boost::bind(&garbage_collector::async_run,this,_1));
  359. io_->gc_job();
  360. }
  361. void stop()
  362. {
  363. srv_.stop();
  364. }
  365. void run()
  366. {
  367. try {
  368. async_run(booster::system::error_code());
  369. srv_.run();
  370. }
  371. catch(std::exception const &e) {
  372. BOOSTER_ERROR("cppcms_scale") << "garbage_collector::run: " <<
  373. e.what() << booster::trace(e);
  374. }
  375. }
  376. private:
  377. booster::aio::io_service srv_;
  378. booster::aio::deadline_timer timer_;
  379. booster::shared_ptr<cppcms::sessions::session_storage_factory> io_;
  380. int seconds_;
  381. };
  382. static void thread_function(io::io_service *io)
  383. {
  384. bool stop=false;
  385. try{
  386. while(!stop) {
  387. try {
  388. io->run();
  389. stop=true;
  390. }
  391. catch(cppcms::cppcms_error const &e) {
  392. // Not much to do...
  393. // Object will be destroyed automatically
  394. // Because it does not resubmit itself
  395. BOOSTER_ERROR("cppcms_scale") << "Error:" << e.what() << booster::trace(e);
  396. }
  397. }
  398. }
  399. catch(std::exception const &e)
  400. {
  401. BOOSTER_ERROR("cppcms_scale") << "Fatal:" << e.what() << booster::trace(e);
  402. }
  403. catch(...){
  404. BOOSTER_ERROR("cppcms_scale") << "Unknown exception" << std::endl;
  405. }
  406. }
  407. struct tcp_cache_service::_data {
  408. std::vector<booster::shared_ptr<io::io_service> > io;
  409. std::auto_ptr<server> srv_cache;
  410. booster::intrusive_ptr<base_cache> cache;
  411. std::vector<booster::shared_ptr<booster::thread> > threads;
  412. booster::shared_ptr<booster::thread> gc_thread;
  413. booster::shared_ptr<garbage_collector> gc_runner;
  414. };
  415. tcp_cache_service::tcp_cache_service( booster::intrusive_ptr<base_cache> cache,
  416. booster::shared_ptr<cppcms::sessions::session_storage_factory> factory,
  417. int threads,
  418. std::string ip,
  419. int port,
  420. int gc_timeout) :
  421. d(new _data)
  422. {
  423. d->io.resize(threads);
  424. for(int i=0;i<threads;i++) {
  425. d->io[i].reset(new io::io_service());
  426. }
  427. d->cache=cache;
  428. d->srv_cache.reset(new server(d->io,ip,port,cache,factory));
  429. #ifndef CPPCMS_WIN32
  430. sigset_t new_mask;
  431. sigfillset(&new_mask);
  432. sigset_t old_mask;
  433. pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);
  434. #endif
  435. if(factory && factory->requires_gc()) {
  436. d->gc_runner.reset(new garbage_collector(factory,gc_timeout));
  437. d->gc_thread.reset(new booster::thread(boost::bind(&garbage_collector::run,d->gc_runner)));
  438. }
  439. for(int i=0;i<threads;i++){
  440. booster::shared_ptr<booster::thread> thread;
  441. thread.reset(new booster::thread(boost::bind(thread_function,d->io[i].get())));
  442. d->threads.push_back(thread);
  443. }
  444. #ifndef CPPCMS_WIN32
  445. // Restore previous mask
  446. pthread_sigmask(SIG_SETMASK,&old_mask,0);
  447. #endif
  448. }
  449. void tcp_cache_service::stop()
  450. {
  451. for(size_t i=0;i<d->io.size();i++)
  452. d->io[i]->stop();
  453. if(d->gc_runner) {
  454. d->gc_runner->stop();
  455. }
  456. }
  457. tcp_cache_service::~tcp_cache_service()
  458. {
  459. try {
  460. stop();
  461. for(unsigned i=0;i<d->threads.size();i++)
  462. d->threads[i]->join();
  463. if(d->gc_thread)
  464. d->gc_thread->join();
  465. d->srv_cache.reset();
  466. }
  467. catch(...){}
  468. }
  469. } // impl
  470. } // cppcms