evdispatch 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/License.txt +20 -0
- data/Manifest.txt +96 -0
- data/README.txt +73 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +15 -0
- data/ext/revdispatch/extconf.rb +31 -0
- data/ext/revdispatch/libevdispatch/Changelog +0 -0
- data/ext/revdispatch/libevdispatch/LICENSE +0 -0
- data/ext/revdispatch/libevdispatch/Makefile.am +10 -0
- data/ext/revdispatch/libevdispatch/Makefile.in +637 -0
- data/ext/revdispatch/libevdispatch/README +3 -0
- data/ext/revdispatch/libevdispatch/TODO +5 -0
- data/ext/revdispatch/libevdispatch/aclocal.m4 +7459 -0
- data/ext/revdispatch/libevdispatch/autogen.sh +11 -0
- data/ext/revdispatch/libevdispatch/confdefs.h +32 -0
- data/ext/revdispatch/libevdispatch/config.guess +1516 -0
- data/ext/revdispatch/libevdispatch/config.h.in +112 -0
- data/ext/revdispatch/libevdispatch/config.sub +1626 -0
- data/ext/revdispatch/libevdispatch/configure +21949 -0
- data/ext/revdispatch/libevdispatch/configure.ac +40 -0
- data/ext/revdispatch/libevdispatch/depcomp +584 -0
- data/ext/revdispatch/libevdispatch/install-sh +507 -0
- data/ext/revdispatch/libevdispatch/libev/Changes +54 -0
- data/ext/revdispatch/libevdispatch/libev/LICENSE +25 -0
- data/ext/revdispatch/libevdispatch/libev/Makefile.am +18 -0
- data/ext/revdispatch/libevdispatch/libev/Makefile.in +677 -0
- data/ext/revdispatch/libevdispatch/libev/README +130 -0
- data/ext/revdispatch/libevdispatch/libev/aclocal.m4 +7430 -0
- data/ext/revdispatch/libevdispatch/libev/autogen.sh +7 -0
- data/ext/revdispatch/libevdispatch/libev/config.guess +1516 -0
- data/ext/revdispatch/libevdispatch/libev/config.h.in +106 -0
- data/ext/revdispatch/libevdispatch/libev/config.sub +1626 -0
- data/ext/revdispatch/libevdispatch/libev/configure +21636 -0
- data/ext/revdispatch/libevdispatch/libev/configure.ac +18 -0
- data/ext/revdispatch/libevdispatch/libev/ev++.h +779 -0
- data/ext/revdispatch/libevdispatch/libev/ev.3 +3276 -0
- data/ext/revdispatch/libevdispatch/libev/ev.c +2547 -0
- data/ext/revdispatch/libevdispatch/libev/ev.h +608 -0
- data/ext/revdispatch/libevdispatch/libev/ev.pod +3192 -0
- data/ext/revdispatch/libevdispatch/libev/ev_epoll.c +182 -0
- data/ext/revdispatch/libevdispatch/libev/ev_kqueue.c +194 -0
- data/ext/revdispatch/libevdispatch/libev/ev_poll.c +135 -0
- data/ext/revdispatch/libevdispatch/libev/ev_port.c +163 -0
- data/ext/revdispatch/libevdispatch/libev/ev_select.c +244 -0
- data/ext/revdispatch/libevdispatch/libev/ev_vars.h +157 -0
- data/ext/revdispatch/libevdispatch/libev/ev_win32.c +125 -0
- data/ext/revdispatch/libevdispatch/libev/ev_wrap.h +144 -0
- data/ext/revdispatch/libevdispatch/libev/event.c +404 -0
- data/ext/revdispatch/libevdispatch/libev/event.h +152 -0
- data/ext/revdispatch/libevdispatch/libev/install-sh +294 -0
- data/ext/revdispatch/libevdispatch/libev/libev.m4 +28 -0
- data/ext/revdispatch/libevdispatch/libev/ltmain.sh +6930 -0
- data/ext/revdispatch/libevdispatch/libev/missing +336 -0
- data/ext/revdispatch/libevdispatch/libev/mkinstalldirs +111 -0
- data/ext/revdispatch/libevdispatch/ltmain.sh +6930 -0
- data/ext/revdispatch/libevdispatch/missing +367 -0
- data/ext/revdispatch/libevdispatch/src/Makefile.am +11 -0
- data/ext/revdispatch/libevdispatch/src/Makefile.in +486 -0
- data/ext/revdispatch/libevdispatch/src/ev_dispatch.cc +264 -0
- data/ext/revdispatch/libevdispatch/src/ev_dispatch.h +300 -0
- data/ext/revdispatch/libevdispatch/src/ev_http.cc +238 -0
- data/ext/revdispatch/libevdispatch/src/ev_http.h +65 -0
- data/ext/revdispatch/libevdispatch/test/Makefile.am +16 -0
- data/ext/revdispatch/libevdispatch/test/Makefile.in +513 -0
- data/ext/revdispatch/libevdispatch/test/helper.rb +94 -0
- data/ext/revdispatch/libevdispatch/test/key_test.cc +52 -0
- data/ext/revdispatch/libevdispatch/test/next_test.cc +86 -0
- data/ext/revdispatch/libevdispatch/test/next_test.rb +8 -0
- data/ext/revdispatch/libevdispatch/test/server.rb +9 -0
- data/ext/revdispatch/revdispatch.cc +151 -0
- data/ext/revdispatch/server.rb +60 -0
- data/ext/revdispatch/test.rb +100 -0
- data/lib/evdispatch/loop.rb +16 -0
- data/lib/evdispatch/version.rb +9 -0
- data/lib/evdispatch.rb +8 -0
- data/log/debug.log +0 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/extconf/revdispatch.rake +43 -0
- data/tasks/extconf.rake +13 -0
- data/tasks/website.rake +17 -0
- data/test/test_evdispatch.rb +11 -0
- data/test/test_helper.rb +3 -0
- data/test/test_revdispatch_extn.rb +14 -0
- data/website/index.html +128 -0
- data/website/index.txt +55 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.html.erb +49 -0
- 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
|