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.
- 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
|