evdispatch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. data/History.txt +3 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +96 -0
  4. data/README.txt +73 -0
  5. data/Rakefile +4 -0
  6. data/config/hoe.rb +70 -0
  7. data/config/requirements.rb +15 -0
  8. data/ext/revdispatch/extconf.rb +31 -0
  9. data/ext/revdispatch/libevdispatch/Changelog +0 -0
  10. data/ext/revdispatch/libevdispatch/LICENSE +0 -0
  11. data/ext/revdispatch/libevdispatch/Makefile.am +10 -0
  12. data/ext/revdispatch/libevdispatch/Makefile.in +637 -0
  13. data/ext/revdispatch/libevdispatch/README +3 -0
  14. data/ext/revdispatch/libevdispatch/TODO +5 -0
  15. data/ext/revdispatch/libevdispatch/aclocal.m4 +7459 -0
  16. data/ext/revdispatch/libevdispatch/autogen.sh +11 -0
  17. data/ext/revdispatch/libevdispatch/confdefs.h +32 -0
  18. data/ext/revdispatch/libevdispatch/config.guess +1516 -0
  19. data/ext/revdispatch/libevdispatch/config.h.in +112 -0
  20. data/ext/revdispatch/libevdispatch/config.sub +1626 -0
  21. data/ext/revdispatch/libevdispatch/configure +21949 -0
  22. data/ext/revdispatch/libevdispatch/configure.ac +40 -0
  23. data/ext/revdispatch/libevdispatch/depcomp +584 -0
  24. data/ext/revdispatch/libevdispatch/install-sh +507 -0
  25. data/ext/revdispatch/libevdispatch/libev/Changes +54 -0
  26. data/ext/revdispatch/libevdispatch/libev/LICENSE +25 -0
  27. data/ext/revdispatch/libevdispatch/libev/Makefile.am +18 -0
  28. data/ext/revdispatch/libevdispatch/libev/Makefile.in +677 -0
  29. data/ext/revdispatch/libevdispatch/libev/README +130 -0
  30. data/ext/revdispatch/libevdispatch/libev/aclocal.m4 +7430 -0
  31. data/ext/revdispatch/libevdispatch/libev/autogen.sh +7 -0
  32. data/ext/revdispatch/libevdispatch/libev/config.guess +1516 -0
  33. data/ext/revdispatch/libevdispatch/libev/config.h.in +106 -0
  34. data/ext/revdispatch/libevdispatch/libev/config.sub +1626 -0
  35. data/ext/revdispatch/libevdispatch/libev/configure +21636 -0
  36. data/ext/revdispatch/libevdispatch/libev/configure.ac +18 -0
  37. data/ext/revdispatch/libevdispatch/libev/ev++.h +779 -0
  38. data/ext/revdispatch/libevdispatch/libev/ev.3 +3276 -0
  39. data/ext/revdispatch/libevdispatch/libev/ev.c +2547 -0
  40. data/ext/revdispatch/libevdispatch/libev/ev.h +608 -0
  41. data/ext/revdispatch/libevdispatch/libev/ev.pod +3192 -0
  42. data/ext/revdispatch/libevdispatch/libev/ev_epoll.c +182 -0
  43. data/ext/revdispatch/libevdispatch/libev/ev_kqueue.c +194 -0
  44. data/ext/revdispatch/libevdispatch/libev/ev_poll.c +135 -0
  45. data/ext/revdispatch/libevdispatch/libev/ev_port.c +163 -0
  46. data/ext/revdispatch/libevdispatch/libev/ev_select.c +244 -0
  47. data/ext/revdispatch/libevdispatch/libev/ev_vars.h +157 -0
  48. data/ext/revdispatch/libevdispatch/libev/ev_win32.c +125 -0
  49. data/ext/revdispatch/libevdispatch/libev/ev_wrap.h +144 -0
  50. data/ext/revdispatch/libevdispatch/libev/event.c +404 -0
  51. data/ext/revdispatch/libevdispatch/libev/event.h +152 -0
  52. data/ext/revdispatch/libevdispatch/libev/install-sh +294 -0
  53. data/ext/revdispatch/libevdispatch/libev/libev.m4 +28 -0
  54. data/ext/revdispatch/libevdispatch/libev/ltmain.sh +6930 -0
  55. data/ext/revdispatch/libevdispatch/libev/missing +336 -0
  56. data/ext/revdispatch/libevdispatch/libev/mkinstalldirs +111 -0
  57. data/ext/revdispatch/libevdispatch/ltmain.sh +6930 -0
  58. data/ext/revdispatch/libevdispatch/missing +367 -0
  59. data/ext/revdispatch/libevdispatch/src/Makefile.am +11 -0
  60. data/ext/revdispatch/libevdispatch/src/Makefile.in +486 -0
  61. data/ext/revdispatch/libevdispatch/src/ev_dispatch.cc +264 -0
  62. data/ext/revdispatch/libevdispatch/src/ev_dispatch.h +300 -0
  63. data/ext/revdispatch/libevdispatch/src/ev_http.cc +238 -0
  64. data/ext/revdispatch/libevdispatch/src/ev_http.h +65 -0
  65. data/ext/revdispatch/libevdispatch/test/Makefile.am +16 -0
  66. data/ext/revdispatch/libevdispatch/test/Makefile.in +513 -0
  67. data/ext/revdispatch/libevdispatch/test/helper.rb +94 -0
  68. data/ext/revdispatch/libevdispatch/test/key_test.cc +52 -0
  69. data/ext/revdispatch/libevdispatch/test/next_test.cc +86 -0
  70. data/ext/revdispatch/libevdispatch/test/next_test.rb +8 -0
  71. data/ext/revdispatch/libevdispatch/test/server.rb +9 -0
  72. data/ext/revdispatch/revdispatch.cc +151 -0
  73. data/ext/revdispatch/server.rb +60 -0
  74. data/ext/revdispatch/test.rb +100 -0
  75. data/lib/evdispatch/loop.rb +16 -0
  76. data/lib/evdispatch/version.rb +9 -0
  77. data/lib/evdispatch.rb +8 -0
  78. data/log/debug.log +0 -0
  79. data/script/console +10 -0
  80. data/script/destroy +14 -0
  81. data/script/generate +14 -0
  82. data/script/txt2html +74 -0
  83. data/setup.rb +1585 -0
  84. data/tasks/deployment.rake +34 -0
  85. data/tasks/environment.rake +7 -0
  86. data/tasks/extconf/revdispatch.rake +43 -0
  87. data/tasks/extconf.rake +13 -0
  88. data/tasks/website.rake +17 -0
  89. data/test/test_evdispatch.rb +11 -0
  90. data/test/test_helper.rb +3 -0
  91. data/test/test_revdispatch_extn.rb +14 -0
  92. data/website/index.html +128 -0
  93. data/website/index.txt +55 -0
  94. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  95. data/website/stylesheets/screen.css +138 -0
  96. data/website/template.html.erb +49 -0
  97. metadata +157 -0
@@ -0,0 +1,264 @@
1
+ #include "config.h"
2
+ #include <stdio.h>
3
+ #include "ev_dispatch.h"
4
+ #include "ev_http.h"
5
+
6
+ #define DEBUG
7
+
8
+ #ifdef DEBUG
9
+ #include <assert.h>
10
+ #define ASSERT_IS_D_THRAD\
11
+ pthread_t tid = pthread_self();\
12
+ assert( tid == this->m_tid );
13
+ #else
14
+ #define ASSERT_IS_D_THRAD
15
+ #endif
16
+
17
+ // ---
18
+ // TODO:
19
+ // libcurl: is used to handle the http requests
20
+ // sphinxclient: library for the search engine requests
21
+ // memcached: client library for the memcached requests
22
+ //
23
+
24
+ namespace EVD {
25
+
26
+ Dispatch::Dispatch() : m_loop(NULL), m_counter(0), m_loop_started(false), m_http_client(NULL), m_pending(0)
27
+ {
28
+ // zero everything out
29
+ memset(&m_clock,0,sizeof(ev_timer));
30
+ memset(&m_request_watcher,0,sizeof(ev_async));
31
+ memset(&m_tid,0,sizeof(pthread_t));
32
+ m_pid = getpid();
33
+ }
34
+
35
+ Dispatch::~Dispatch()
36
+ {
37
+ if( m_http_client ) {
38
+ delete m_http_client;
39
+ }
40
+ }
41
+
42
+ // start up the background event listener
43
+ bool Dispatch::start()
44
+ {
45
+ int rc;
46
+ if( m_loop_started ){ return false; }
47
+
48
+ rc = pthread_create( &m_tid, NULL, Dispatch::event_loop_start, this );
49
+ if( rc ){ return false; }
50
+ pthread_detach( m_tid );
51
+
52
+ m_http_client = new HttpClient(this);
53
+
54
+ // block until we get notified that the event thead is up and running
55
+ Guard lock(m_lock);
56
+ int count = 0;
57
+
58
+ // wait until the event loop is started
59
+ while( !m_loop_started && count < 100 ) { // never wait more then 100 iterations
60
+ m_cond.timed_wait( m_lock, Timer(1,0) );
61
+ //printf( "waiting another second\n" );
62
+ ++count;
63
+ }
64
+
65
+ return (count < 100);
66
+ }
67
+
68
+ // tell the background event listener to terminate
69
+ void Dispatch::stop()
70
+ {
71
+ if( !m_loop_started ){ return ; }
72
+
73
+ //printf( "EVD::Dispatch stopping...\n" );
74
+ m_loop_started = false;
75
+
76
+ m_cond.broadcast();
77
+ m_requests.signal();
78
+ m_responses.signal();
79
+ ev_unloop( m_loop, EVUNLOOP_ALL );
80
+ if( m_http_client ) {
81
+ delete m_http_client;
82
+ m_http_client = NULL;
83
+ }
84
+ }
85
+
86
+ // all event loop activity happens on this thread
87
+ void* Dispatch::event_loop_start( void *ptr )
88
+ {
89
+ Dispatch *dis = (Dispatch*)ptr;
90
+ dis->event_loop_main();
91
+ //printf( "EVD::Dispatch event loop stopped\n" );
92
+ return NULL;
93
+ }
94
+
95
+ static void
96
+ sigint_cb( struct ev_loop *loop, struct ev_signal *w, int revents )
97
+ {
98
+ Dispatch *ptr = ((Dispatch*)w->data);
99
+ ptr->stop();
100
+ }
101
+
102
+ // called when a new request was signaled from the main thread
103
+ void Dispatch::request_cb_start( struct ev_loop *loop, struct ev_async *w, int revents )
104
+ {
105
+ Dispatch *ptr = (Dispatch*)w->data;
106
+ ptr->request_cb( loop, w, revents );
107
+ }
108
+ void Dispatch::request_cb( struct ev_loop *loop, struct ev_async *w, int revents )
109
+ {
110
+ ASSERT_IS_D_THRAD
111
+
112
+ // create a new queue to store incoming requests
113
+ std::queue<Request*> new_requests;
114
+ // lock down the request queue
115
+ m_requests.m_lock.lock();
116
+ // lock and read as much as we can out of the queue
117
+ new_requests = m_requests.m_queue;
118
+ m_requests.m_queue = std::queue<Request*>(); // empty out the request queue
119
+ m_requests.m_lock.unlock(); // unlock the queue
120
+
121
+ // process the new requests
122
+ while( !new_requests.empty() ) {
123
+ Request *req = new_requests.front();
124
+ new_requests.pop();
125
+ //printf( "Request: %s, time since requested: %.2f\n", req->url.c_str(), difftime(time(NULL),req->start_time) );
126
+
127
+ // inc before we enable the request, since the time to enable and push to responses queue may
128
+ // be long enough for a context switch to allow someone to check request, response and pending count all be zero.
129
+ ++m_pending;
130
+ if( !req->enable() ) { // XXX: if using DNS in requests expect this method to block while dns resolves
131
+ --m_pending;
132
+ printf("EVD::Dispatch request error\n");
133
+ // for some reason we couldn't enable the request cleanup the request and continue
134
+ delete req;
135
+ }
136
+ }
137
+
138
+ }
139
+
140
+ // the main event loop
141
+ void Dispatch::event_loop_main()
142
+ {
143
+ // struct ev_signal signal_exit_watcher;
144
+ m_loop = ev_loop_new(0); //ev_default_loop(0);
145
+
146
+ // m_clock.data = this;
147
+
148
+ // every 100s of a second we'll check the queue for requests
149
+ // ev_timer_init( &m_clock, timeout_cb_start, 0.5, 0.5 );
150
+ // ev_timer_again( m_loop, &m_clock ); // start timer
151
+
152
+ // trap sigint to ensure we clean up the event loop before exit
153
+ // signal_exit_watcher.data = this;
154
+ // ev_signal_init( &signal_exit_watcher, sigint_cb, SIGINT );
155
+ // ev_signal_start( m_loop, &signal_exit_watcher );
156
+
157
+ m_request_watcher.data = this;
158
+ ev_async_init( &m_request_watcher, request_cb_start );
159
+ ev_async_start( m_loop, &m_request_watcher );
160
+
161
+ // start the main event loop
162
+
163
+ m_lock.lock();
164
+ m_loop_started = true;
165
+ m_cond.signal(); // let the world know we're ready for them
166
+ m_lock.unlock();
167
+
168
+ ev_loop( m_loop, 0 );
169
+ ev_loop_destroy( m_loop );
170
+ pthread_exit(NULL);
171
+ }
172
+ void Dispatch::timeout_cb_start(struct ev_loop *loop, struct ev_timer *w, int revents)
173
+ {
174
+ Dispatch *ptr = ((Dispatch*)w->data);
175
+ ptr->timeout_cb( loop, w, revents );
176
+ }
177
+
178
+ // called on the event loop thread
179
+ void Dispatch::timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents)
180
+ {
181
+ ASSERT_IS_D_THRAD
182
+ //printf("timeout cb\n");
183
+ }
184
+
185
+ // called on the main thread, not the event loop thread
186
+ request_t Dispatch::request( Request::Type type, const std::string &url )
187
+ {
188
+ request_t key = m_counter++;
189
+ Request *req = NULL;
190
+ // check the request type and
191
+ switch( type ) {
192
+ case Request::HTTP:
193
+ req = new HttpRequest( m_http_client, key, url );
194
+ break;
195
+ case Request::SPHINX:
196
+ //req = new SphinxRequest( key, url );
197
+ break;
198
+ case Request::MEMCACHED:
199
+ break;
200
+ default:
201
+ // TODO: log an error
202
+ break;
203
+ }
204
+ m_requests.push( req );
205
+ ev_async_send( m_loop, &m_request_watcher );
206
+ return key;
207
+ }
208
+
209
+ Response *Dispatch::response_for( request_t id )
210
+ {
211
+ RespondedTable::iterator loc = m_responded.find(id);
212
+ if( loc == m_responded.end() ){ return NULL; }
213
+ Response *res = loc->second;
214
+ m_responded.erase(loc);
215
+ return res;
216
+ }
217
+
218
+ Queue<Response>::POP_STATE Dispatch::wait_for_response_by_id( request_t id, Timer timeout )
219
+ {
220
+ // first check if the response is loaded
221
+ Response *rep = NULL;
222
+ Queue<Response>::POP_STATE rstate;
223
+ RespondedTable::iterator loc = m_responded.find(id);
224
+
225
+ if( loc != m_responded.end() ){ return Queue<Response>::POPPED; }
226
+
227
+ while( (rep = m_responses.pop_or_wait( &rstate, m_loop_started, timeout )) ) {
228
+ //printf( "rep: %s, id: %d\n", rep->name.c_str(), rep->id );
229
+ m_responded[rep->id] = rep;
230
+ if( rep->id == id ){ return rstate; }
231
+ }
232
+
233
+ return rstate;
234
+ }
235
+
236
+ Response *Dispatch::get_next_response( Timer timer )
237
+ {
238
+ Response *rep = NULL;
239
+ Queue<Response>::POP_STATE rstate;
240
+
241
+ do {
242
+ rep = m_responses.pop_or_wait( &rstate, m_loop_started, timer );
243
+ if( rstate == Queue<Response>::EXITING ){ rep = NULL; break; }
244
+ if( !rep && rstate == Queue<Response>::EXPIRED ) {
245
+ int pending_count = m_pending;
246
+ if( pending_count == 0 ) { // if pending is zero then acquire locks on the request and response queue
247
+ int request_count = m_requests.size();
248
+ int response_count = m_responses.size();
249
+ //printf( "pending: %d, request: %d, response: %d\n", pending_count, request_count, response_count );
250
+ if( pending_count == 0 && request_count == 0 && response_count == 0 ) {
251
+ return NULL;
252
+ }
253
+ }
254
+ }
255
+ else if( rep ) {
256
+ --m_pending;
257
+ }
258
+
259
+ } while( !rep && rstate == Queue<Response>::EXPIRED );
260
+
261
+ return rep;
262
+ }
263
+
264
+ }// end namespace EVD
@@ -0,0 +1,300 @@
1
+ #ifndef EV_DISPATCH_H
2
+ #define EV_DISPATCH_H
3
+
4
+ #include "config.h"
5
+ #include <time.h>
6
+ #include <sys/time.h>
7
+ #include <ev.h>
8
+ #include <pthread.h>
9
+ #include <string>
10
+ #include <list>
11
+ #include <queue>
12
+ #include <map>
13
+
14
+ namespace EVD {
15
+
16
+ // unique id to represent a request
17
+ typedef unsigned long request_t;
18
+
19
+ struct Request {
20
+ enum Type{
21
+ HTTP,
22
+ SPHINX,
23
+ MEMCACHED
24
+ };
25
+ Request( request_t k, const std::string &u ) : key(k), url(u){ time( &start_time); }
26
+ virtual ~Request (){ }
27
+
28
+ // attach to the event loop
29
+ virtual bool enable() = 0;
30
+
31
+ // TODO: add a method to access the response
32
+
33
+ request_t key;
34
+ std::string url;
35
+ time_t start_time;
36
+ };
37
+
38
+ // TODO:
39
+ //struct MemcachedRequesst : public Request {
40
+ //};
41
+
42
+ // TODO:
43
+ //struct SphinxRequest : public Request {
44
+ //};
45
+
46
+ struct Response {
47
+ virtual ~Response(){}
48
+ std::string name;
49
+ std::string body;
50
+ request_t id;
51
+ double response_time; // computed as difftime(start_time,end_time);
52
+ };
53
+
54
+ struct Mutex {
55
+ Mutex() {
56
+ pthread_mutex_init( &m_lock, NULL );
57
+ }
58
+ ~Mutex() {
59
+ pthread_mutex_destroy( &m_lock );
60
+ }
61
+ void lock() {
62
+ pthread_mutex_lock( &m_lock );
63
+ }
64
+ void unlock() {
65
+ pthread_mutex_unlock( &m_lock );
66
+ }
67
+
68
+ pthread_mutex_t m_lock;
69
+ };
70
+
71
+ struct Timer {
72
+ Timer( long int seconds, long int nanoseconds ){
73
+ struct timeval now;
74
+ gettimeofday(&now, NULL);
75
+ m_time.tv_sec = now.tv_sec + seconds;
76
+ m_time.tv_nsec = (now.tv_usec * 1000) + nanoseconds;
77
+ }
78
+ struct timespec m_time;
79
+ };
80
+
81
+ struct Cond {
82
+ Cond() {
83
+ pthread_cond_init( &m_cond, NULL );
84
+ }
85
+ ~Cond() {
86
+ pthread_cond_destroy( &m_cond );
87
+ }
88
+
89
+ void wait( Mutex &m ) {
90
+ pthread_cond_wait( &m_cond, &(m.m_lock) );
91
+ }
92
+
93
+ // wait 500 ms
94
+ // timed_wait( m, Timer(0,500) );
95
+ void timed_wait( Mutex &m, const Timer &t ) {
96
+ pthread_cond_timedwait( &m_cond, &(m.m_lock), &(t.m_time) );
97
+ }
98
+
99
+ void signal() {
100
+ pthread_cond_signal( &m_cond );
101
+ }
102
+
103
+ void broadcast() {
104
+ pthread_cond_broadcast( &m_cond );
105
+ }
106
+
107
+ pthread_cond_t m_cond;
108
+ };
109
+
110
+ struct Guard {
111
+ Guard( Mutex &mutex ): m_mutex(mutex){ m_mutex.lock(); }
112
+ ~Guard(){ m_mutex.unlock(); }
113
+ Mutex &m_mutex;
114
+ };
115
+
116
+ // thread safe queue
117
+ template <typename T>
118
+ struct Queue {
119
+ Queue() {}
120
+ ~Queue() {}
121
+
122
+ // the reason the pop returned, time expired, event loop is exiting, or an object was popped
123
+ enum POP_STATE {
124
+ POPPED = 0,
125
+ EXPIRED = 1,
126
+ EXITING = 2
127
+ };
128
+
129
+ // heap allocated T object
130
+ void push( T *req ) {
131
+ m_lock.lock();
132
+ m_queue.push( req );
133
+ m_lock.unlock();
134
+ m_cond.signal();
135
+ }
136
+
137
+ // returns a value from the queue
138
+ // rstate: the status of the return, see POP_STATE above.
139
+ // cond: an external reason to abort and not pop e.g. exiting event loop
140
+ T *pop_or_wait( POP_STATE *rstate, volatile bool &cond = true, Timer timer = Timer(1,0) ) {
141
+
142
+ Guard lock(m_lock);
143
+ T *req = NULL;
144
+ size_t size = m_queue.size();
145
+ if( size > 0 ){
146
+ req = m_queue.front();
147
+ }
148
+ while( !req ) {
149
+ m_cond.timed_wait( m_lock, timer );
150
+ if( !cond ){ *rstate = EXITING; break; }
151
+ size = m_queue.size();
152
+ if( size > 0 ){
153
+ req = m_queue.front();
154
+ }
155
+ else {
156
+ *rstate = EXPIRED;
157
+ req = NULL;
158
+ break;
159
+ }
160
+ }
161
+ if( req ){
162
+ *rstate = POPPED;
163
+ m_queue.pop();
164
+ }
165
+ return req;
166
+ }
167
+ size_t size(){
168
+ Guard lock(m_lock);
169
+ return m_queue.size();
170
+ }
171
+
172
+ T *pop() {
173
+ Guard lock(m_lock);
174
+ T *req = NULL;
175
+ req = m_queue.front();
176
+ if( req ){
177
+ m_queue.pop();
178
+ }
179
+ return req;
180
+ }
181
+
182
+ void signal(){ m_cond.signal(); }
183
+
184
+ std::queue<T*> m_queue;
185
+ Mutex m_lock;
186
+ Cond m_cond;
187
+ };
188
+
189
+ // used to keep track of pending requests vs completed responses
190
+ struct AtomicCounter {
191
+ // initialization is not threadsafe
192
+ AtomicCounter( int init ) : m_count(init){}
193
+ inline operator int(){ Guard g(m_lock); return m_count; }
194
+ inline int operator++(){ Guard g(m_lock); return ++m_count; }
195
+ inline int operator--(){ Guard g(m_lock); return --m_count; }
196
+
197
+ Mutex m_lock;
198
+ int m_count;
199
+ };
200
+
201
+ //
202
+ // Dispatch requests to a thread running an event loop
203
+ // all methods here should be used within the same thread
204
+ //
205
+ // create a Dispatcher
206
+ // call start
207
+ //
208
+ // send the dispatcher some requests
209
+ //
210
+ // do some work
211
+ //
212
+ // request the dispatcher results
213
+ //
214
+ // TODO: optionally you can tell the dispatcher to store the results on the file system via a pipe
215
+ //
216
+ struct Dispatch {
217
+ typedef std::map<request_t,Response*> RespondedTable;
218
+
219
+ Dispatch();
220
+ ~Dispatch();
221
+
222
+ // start up the background event listener
223
+ bool start();
224
+ // tell the background event listener to terminate
225
+ void stop();
226
+
227
+ request_t request( Request::Type type, const std::string &url );
228
+
229
+ // from the main thread, get the next available response
230
+ // just keep pop'ing off the response queue until we get something
231
+ // if you get a non NULL response from this method it's your responsiblity to delete it
232
+ //
233
+ // Use this method if you want to get all pending requests
234
+ Response *get_next_response( Timer timer = Timer(1,0) );
235
+
236
+ // get a specific respone by id
237
+ // if you get a non NULL response from this method it's your responsiblity to delete it
238
+ //
239
+ // Use this method if you want to get a specific request
240
+ Response *response_for( request_t id );
241
+
242
+ // call this method to block and wait for a specific request
243
+ // then call response_for to retrieve the response, in the background this will collect
244
+ // other pending requests into an internal lookup table
245
+ Queue<Response>::POP_STATE wait_for_response_by_id( request_t id, Timer timeout );
246
+
247
+ // from the backend called on the event loop thread
248
+ inline void send_response( Response *response ) {
249
+ m_responses.push( response );
250
+ }
251
+
252
+ inline struct ev_loop *get_loop(){ return m_loop; }
253
+
254
+ inline pthread_t get_thread_id()const{ return m_tid; }
255
+
256
+ protected:
257
+
258
+ // all event loop activity happens on this thread
259
+ static void* event_loop_start( void *ptr );
260
+ void event_loop_main();
261
+
262
+ static void timeout_cb_start(struct ev_loop *loop, struct ev_timer *w, int revents);
263
+ void timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents);
264
+
265
+ static void request_cb_start( struct ev_loop *loop, struct ev_async *w, int revents );
266
+ void request_cb( struct ev_loop *loop, struct ev_async *w, int revents );
267
+
268
+ bool store_response_for( request_t id );
269
+
270
+ protected:
271
+
272
+ struct ev_loop *m_loop;
273
+
274
+ // this triggers a callback once every N milliseconds
275
+ struct ev_timer m_clock;
276
+
277
+ struct ev_async m_request_watcher;
278
+
279
+ pid_t m_pid;
280
+ pthread_t m_tid;
281
+ request_t m_counter; // used to create ids for each requests
282
+
283
+ // sync startup to main thread
284
+ Mutex m_lock;
285
+ Cond m_cond;
286
+ bool m_loop_started;
287
+
288
+ Queue <Request> m_requests;
289
+ Queue <Response> m_responses;
290
+
291
+ // stores all responded messages stored when calling wait_for_request_by_id( request_t id )
292
+ RespondedTable m_responded;
293
+
294
+ struct HttpClient *m_http_client;
295
+ AtomicCounter m_pending;
296
+ };
297
+
298
+ }
299
+
300
+ #endif