packet 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2004-2007 Hemant Kumar
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README CHANGED
@@ -242,6 +242,10 @@ end
242
242
  reasonably well. Mongrel, running on top of Packet is a tad
243
243
  slower than Mongrel running on top of EventMachine. More benchmarks coming soon.
244
244
 
245
+ == SVN repo:
246
+ Code for packet is on google code, svn repo is:
247
+ http://packet.googlecode.com/svn/trunk/
248
+
245
249
  == Credits
246
250
  Francis for awesome EventMachine lib, which has constantly acted as an inspiration.
247
251
  Ezra, for being a early user and porting mongrel to run on top of packet
data/Rakefile CHANGED
@@ -6,6 +6,7 @@ require 'rake/rdoctask'
6
6
  require 'rake/testtask'
7
7
  require 'spec/rake/spectask'
8
8
  require 'fileutils'
9
+
9
10
  def __DIR__
10
11
  File.dirname(__FILE__)
11
12
  end
@@ -31,7 +32,7 @@ task :doc => [:rdoc]
31
32
 
32
33
 
33
34
  Rake::RDocTask.new do |rdoc|
34
- files = ['README', 'LICENSE', 'CHANGELOG',
35
+ files = ['README', 'MIT-LICENSE', 'CHANGELOG',
35
36
  'lib/**/*.rb']
36
37
  rdoc.rdoc_files.add(files)
37
38
  rdoc.main = 'README'
@@ -45,20 +46,19 @@ spec = Gem::Specification.new do |s|
45
46
  s.version = Packet::VERSION
46
47
  s.platform = Gem::Platform::RUBY
47
48
  s.has_rdoc = true
48
- s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
49
+ s.extra_rdoc_files = ["README", "MIT-LICENSE", 'TODO']
49
50
  #s.rdoc_options += RDOC_OPTS +
50
51
  # ['--exclude', '^(app|uploads)']
51
- s.summary = "Packet, Events... we got em."
52
+ s.summary = "Packet, A Pure Ruby library for Event Driven Network Programming."
52
53
  s.description = s.summary
53
- s.author = "Hemant"
54
- s.email = 'foo@bar.com'
54
+ s.author = "Hemant Kumar"
55
+ s.email = 'mail@gnufied.org'
55
56
  s.homepage = 'http://code.google.com/p/packet/'
56
- s.required_ruby_version = '>= 1.8.4'
57
+ s.required_ruby_version = '>= 1.8.5'
57
58
 
58
- s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{bin,spec,lib,examples,script}/**/*")
59
+ s.files = %w(MIT-LICENSE README Rakefile TODO) + Dir.glob("{spec,lib,examples}/**/*")
59
60
 
60
61
  s.require_path = "lib"
61
- s.bindir = "bin"
62
62
  end
63
63
 
64
64
  Rake::GemPackageTask.new(spec) do |p|
@@ -85,3 +85,11 @@ task :svn_add do
85
85
  end
86
86
 
87
87
 
88
+ desc "Converts a YAML file into a test/spec skeleton"
89
+ task :yaml_to_spec do
90
+ require 'yaml'
91
+
92
+ puts YAML.load_file(ENV['FILE']||!puts("Pass in FILE argument.")&&exit).inject(''){|t,(c,s)|
93
+ t+(s ?%.context "#{c}" do.+s.map{|d|%.\n xspecify "#{d}" do\n end\n.}*''+"end\n\n":'')
94
+ }.strip
95
+ end
data/TODO CHANGED
@@ -0,0 +1,8 @@
1
+ * Overview *
2
+ ** TODO Implement ability to have a pool of workers.
3
+ ** TODO Remove class and class instance attributes from core classes.
4
+ ** TODO Cleanup callback mechanism
5
+ ** TODO Implement a sample worker, which provides capabilities to execute mysql statements async.
6
+
7
+
8
+
@@ -0,0 +1,296 @@
1
+ /* --------------------------------------------------------------------------
2
+ * Asteroid ruby extension.
3
+ * by Genki Takiuchi <takiuchi@drecom.co.jp>
4
+ * -------------------------------------------------------------------------- */
5
+ #include <ruby.h>
6
+ #include <rubysig.h>
7
+ #include <unistd.h>
8
+ #include <fcntl.h>
9
+ #include <errno.h>
10
+ #include <sys/types.h>
11
+ #include <sys/socket.h>
12
+ #include <sys/time.h>
13
+ #include <netinet/in.h>
14
+ #include <arpa/inet.h>
15
+ #include "extconf.h"
16
+ #include "asteroid.h"
17
+
18
+ /* --------------------------------------------------------------------------
19
+ * epoll / kqueue
20
+ * - 2007/03/09 *BSD kqueue port, Mac OS X MSG_NOSIGNAL missing.
21
+ * (Takanori Ishikawa)
22
+ * -------------------------------------------------------------------------- */
23
+ #ifdef HAVE_SYS_EVENT_H
24
+ #include <sys/event.h>
25
+ typedef int asteroid_pollfd_t;
26
+ typedef struct kevent asteroid_poll_event_t;
27
+ #endif
28
+
29
+ #ifdef HAVE_SYS_EPOLL_H
30
+ #include <sys/epoll.h>
31
+ typedef int asteroid_pollfd_t;
32
+ typedef struct epoll_event asteroid_poll_event_t;
33
+ #endif
34
+
35
+ static asteroid_pollfd_t asteroid_poll_create(int sizeHint) {
36
+ #ifdef HAVE_SYS_EVENT_H
37
+ return kqueue();
38
+ #endif
39
+ #ifdef HAVE_SYS_EPOLL_H
40
+ return epoll_create(sizeHint);
41
+ #endif
42
+ }
43
+
44
+ static int asteroid_poll_add(
45
+ asteroid_pollfd_t pollfd,
46
+ asteroid_poll_event_t *event,
47
+ int fd) {
48
+ #ifdef HAVE_SYS_EVENT_H
49
+ EV_SET(event, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
50
+ return kevent(pollfd, event, 1, NULL, 0, NULL);
51
+ #endif
52
+ #ifdef HAVE_SYS_EPOLL_H
53
+ event->events = (EPOLLIN | EPOLLPRI);
54
+ event->data.fd = fd;
55
+ return epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, event);
56
+ #endif
57
+ }
58
+
59
+ static int asteroid_poll_remove(
60
+ asteroid_pollfd_t pollfd,
61
+ asteroid_poll_event_t *event,
62
+ int fd) {
63
+ #ifdef HAVE_SYS_EVENT_H
64
+ EV_SET(event, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
65
+ return kevent(pollfd, event, 1, NULL, 0, NULL);
66
+ #endif
67
+ #ifdef HAVE_SYS_EPOLL_H
68
+ return epoll_ctl(pollfd, EPOLL_CTL_DEL, fd, event);
69
+ #endif
70
+ }
71
+
72
+ static int asteroid_poll_wait(
73
+ asteroid_pollfd_t pollfd,
74
+ asteroid_poll_event_t *events,
75
+ int maxevents,
76
+ int timeout) {
77
+ #ifdef HAVE_SYS_EVENT_H
78
+ struct timespec tv, *tvptr;
79
+ if (timeout < 0) {
80
+ tvptr = NULL;
81
+ }
82
+ else {
83
+ tv.tv_sec = (long) (timeout/1000);
84
+ tv.tv_nsec = (long) (timeout%1000)*1000;
85
+ tvptr = &tv;
86
+ }
87
+ return kevent(pollfd, NULL, 0, events, maxevents, tvptr);
88
+ #endif
89
+ #ifdef HAVE_SYS_EPOLL_H
90
+ return epoll_wait(pollfd, events, maxevents, timeout);
91
+ #endif
92
+ }
93
+
94
+ #ifdef HAVE_SYS_EVENT_H
95
+ #define AST_POLL_EVENT_SOCK(event) ((event)->ident)
96
+ #define AST_POLL_EVENT_CAN_READ(event) ((event)->filter == EVFILT_READ)
97
+ #endif
98
+ #ifdef HAVE_SYS_EPOLL_H
99
+ #define AST_POLL_EVENT_SOCK(event) ((event)->data.fd)
100
+ #define AST_POLL_EVENT_CAN_READ(event) ((event)->events & (EPOLLIN|EPOLLPRI))
101
+ #endif
102
+
103
+ #ifdef SO_NOSIGPIPE
104
+ #ifndef MSG_NOSIGNAL
105
+ #define MSG_NOSIGNAL 0
106
+ #endif
107
+
108
+ /*
109
+ * The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
110
+ * sending data to a dead peer (instead of relying on the 4th argument to send
111
+ * being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
112
+ * systems?
113
+ *
114
+ * curl-7.15.5/lib/connect.c
115
+ */
116
+
117
+ static void nosigpipe(int sockfd) {
118
+ int one = 1;
119
+ setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof(one));
120
+ }
121
+ #else
122
+ #define nosigpipe(x)
123
+ #endif
124
+
125
+ #define MAX_CONNECTION (102400)
126
+ #define EVENT_BUF_SIZE (1024)
127
+
128
+ static VALUE Asteroid;
129
+ static VALUE clients;
130
+ static volatile int running = 0;
131
+ static asteroid_pollfd_t epoll_fd = 0;
132
+ static asteroid_poll_event_t events[EVENT_BUF_SIZE];
133
+
134
+ int dispatch();
135
+ void runtime_error();
136
+
137
+ void Init_asteroid(){
138
+ Asteroid = rb_define_module("Asteroid");
139
+ rb_define_singleton_method(Asteroid, "run", asteroid_s_run, 3);
140
+ rb_define_singleton_method(Asteroid, "stop", asteroid_s_stop, 0);
141
+ rb_define_singleton_method(Asteroid, "now", asteroid_s_now, 0);
142
+ rb_define_class_variable(Asteroid, "@@clients", clients = rb_hash_new());
143
+ }
144
+
145
+ static VALUE close_socket_proc(VALUE Pair, VALUE Arg, VALUE Self) {
146
+ close(FIX2INT(RARRAY(Pair)->ptr[0]));
147
+ return Qnil;
148
+ }
149
+
150
+ static VALUE asteroid_s_run(VALUE Self, VALUE Host, VALUE Port, VALUE Module){
151
+ char *host = StringValuePtr(Host);
152
+ int port = FIX2INT(Port);
153
+
154
+ epoll_fd = asteroid_poll_create(1024);
155
+ if(epoll_fd == -1) runtime_error();
156
+
157
+ struct sockaddr_in addr;
158
+ addr.sin_family = AF_INET;
159
+ addr.sin_port = htons(port);
160
+ addr.sin_addr.s_addr = inet_addr(host);
161
+ int s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP), c, one = 1;
162
+ if(s == -1) runtime_error();
163
+ fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0) | O_NONBLOCK);
164
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
165
+ nosigpipe(s);
166
+ if(bind(s, (struct sockaddr*)&addr, sizeof(addr)) != 0) runtime_error();
167
+ if(listen(s, MAX_CONNECTION) != 0) runtime_error();
168
+ if(rb_block_given_p()) rb_yield(Qnil);
169
+
170
+ VALUE Class = rb_define_class_under(Asteroid, "Server", rb_cObject);
171
+ rb_define_method(Class, "send_data",
172
+ asteroid_server_send_data, 1);
173
+ rb_define_method(Class, "write_and_close",
174
+ asteroid_server_write_and_close, 0);
175
+ rb_include_module(Class, Module);
176
+ // Mac OS X, Fedora needs explicit rb_thread_schedule call.
177
+ for(running = 1; running; rb_thread_schedule()){
178
+ socklen_t len = sizeof(addr);
179
+ while((c = accept(s, (struct sockaddr*)&addr, &len)) != -1){
180
+ printf("A New client connected here\n");
181
+ fcntl(c, F_SETFL, fcntl(c, F_GETFL, 0) | O_NONBLOCK);
182
+ asteroid_poll_event_t event;
183
+ memset(&event, 0, sizeof(event));
184
+ if(asteroid_poll_add(epoll_fd, &event, c) == -1) runtime_error();
185
+ // instantiate server class which responds to client.
186
+ VALUE Server = rb_class_new_instance(0, NULL, Class);
187
+ rb_iv_set(Server, "@fd", rb_fix_new(c));
188
+ rb_hash_aset(clients, rb_fix_new(c), Server);
189
+ if(rb_respond_to(Server, rb_intern("post_init"))){
190
+ rb_funcall(Server, rb_intern("post_init"), 0);
191
+ }
192
+ }
193
+ if(dispatch() != 0) asteroid_s_stop(Asteroid);
194
+ // You must call them to give a chance for ruby to handle system events.
195
+ // CHECK_INTS;
196
+ }
197
+
198
+ rb_iterate(rb_each, clients, close_socket_proc, Qnil);
199
+ rb_funcall(clients, rb_intern("clear"), 0);
200
+ close(s);
201
+ close(epoll_fd);
202
+ return Qnil;
203
+ }
204
+
205
+ static VALUE asteroid_s_stop(VALUE Self){
206
+ running = 0;
207
+ return Qnil;
208
+ }
209
+
210
+ static VALUE asteroid_s_now(VALUE Self){
211
+ struct timeval now;
212
+ gettimeofday(&now, NULL);
213
+ return rb_float_new(now.tv_sec + now.tv_usec/1000000.0);
214
+ }
215
+
216
+ static VALUE asteroid_server_send_data(VALUE Self, VALUE Data){
217
+ VALUE Fd = rb_iv_get(Self, "@fd");
218
+ int fd = FIX2INT(Fd), remain = RSTRING(Data)->as.heap.len, len, trial = 100;
219
+ char *data = StringValuePtr(Data);
220
+ while(remain){
221
+ len = send(fd, data, remain, MSG_DONTWAIT|MSG_NOSIGNAL);
222
+ if(len == -1){
223
+ if(errno == EAGAIN && --trial){
224
+ rb_thread_schedule();
225
+ // CHECK_INTS;
226
+ }else{
227
+ if(rb_respond_to(Self, rb_intern("unbind"))){
228
+ rb_funcall(Self, rb_intern("unbind"), 0);
229
+ }
230
+ return Qnil;
231
+ }
232
+ }else{
233
+ remain -= len;
234
+ data += len;
235
+ }
236
+ }
237
+ return Qtrue;
238
+ }
239
+
240
+ static VALUE asteroid_server_write_and_close(VALUE Self){
241
+ VALUE Fd = rb_iv_get(Self, "@fd");
242
+ int fd = FIX2INT(Fd);
243
+ char buf[1];
244
+ if(read(fd, buf, 1) == -1 && errno != EAGAIN){
245
+ if(rb_respond_to(Self, rb_intern("unbind"))){
246
+ rb_funcall(Self, rb_intern("unbind"), 0);
247
+ }
248
+ }
249
+ asteroid_poll_event_t event;
250
+ memset(&event, 0, sizeof(event));
251
+ asteroid_poll_remove(epoll_fd, &event, fd);
252
+ close(fd);
253
+ rb_hash_delete(clients, Fd);
254
+ return Qnil;
255
+ }
256
+
257
+ int dispatch(){
258
+ int i, s, len;
259
+ while(1){
260
+ TRAP_BEG;
261
+ s = asteroid_poll_wait(epoll_fd, events, EVENT_BUF_SIZE, 1);
262
+ TRAP_END;
263
+ if(s <= 0) break;
264
+ for(i = 0; i < s; ++i){
265
+ asteroid_poll_event_t event = events[i];
266
+ int fd = AST_POLL_EVENT_SOCK(&event);
267
+ VALUE Fd = rb_fix_new(fd);
268
+ VALUE Server = rb_hash_aref(clients, Fd);
269
+ if(AST_POLL_EVENT_CAN_READ(&event)){
270
+ VALUE Buf = rb_str_new("", 0);
271
+ char buf[1024];
272
+ while((len = read(fd, buf, 1023)) > 0){
273
+ buf[len] = '\0';
274
+ rb_str_concat(Buf, rb_str_new2(buf));
275
+ }
276
+ if(len == -1 && errno == EAGAIN){
277
+ if(rb_respond_to(Server, rb_intern("receive_data"))){
278
+ rb_funcall(Server, rb_intern("receive_data"), 1, Buf);
279
+ }
280
+ }else{
281
+ if(rb_respond_to(Server, rb_intern("unbind"))){
282
+ rb_funcall(Server, rb_intern("unbind"), 0);
283
+ }
284
+ asteroid_poll_remove(epoll_fd, &event, fd);
285
+ rb_hash_delete(clients, Fd);
286
+ close(fd);
287
+ }
288
+ }
289
+ }
290
+ }
291
+ return 0;
292
+ }
293
+
294
+ void runtime_error(){
295
+ rb_raise(rb_eRuntimeError, strerror(errno));
296
+ }
@@ -0,0 +1,5 @@
1
+ static VALUE asteroid_s_run(VALUE Self, VALUE Host, VALUE Port, VALUE Module);
2
+ static VALUE asteroid_s_stop(VALUE Self);
3
+ static VALUE asteroid_s_now(VALUE Self);
4
+ static VALUE asteroid_server_send_data(VALUE Self, VALUE Data);
5
+ static VALUE asteroid_server_write_and_close(VALUE Self);
@@ -0,0 +1,53 @@
1
+ #include <ruby.h>
2
+ #include <rubysig.h>
3
+ #include <unistd.h>
4
+ #include <fcntl.h>
5
+ #include <errno.h>
6
+ #include <sys/types.h>
7
+ #include <sys/socket.h>
8
+ #include <sys/time.h>
9
+ #include <netinet/in.h>
10
+ #include <arpa/inet.h>
11
+
12
+ static VALUE ConcurrentThread;
13
+ static VALUE ConcurrentThreadPool;
14
+
15
+ static void count_to_10000(void* data){
16
+ int i = 0;
17
+ for( i = 0; i < 10000; i++ ) {
18
+ if ((*(VALUE *)data) == Qtrue) break;
19
+ printf("Currently Counting from thr 1: %d\n",i);
20
+ }
21
+ }
22
+
23
+ static void count_to_20000(void* data){
24
+ int i = 0;
25
+ for( i = 0; i < 10000; i++ ) {
26
+ if ((*(VALUE *)data) == Qtrue) break;
27
+ printf("Currently Counting from thr 2: %d\n",i);
28
+ }
29
+ }
30
+
31
+ static void stop_thr1(void* data) {
32
+ printf("Calling thread1 break method\n");
33
+ *((VALUE *)data) = Qtrue;
34
+ }
35
+
36
+ static void stop_thr2(void* data) {
37
+ printf("Calling thread2 break method\n");
38
+ *((VALUE *)data) = Qtrue;
39
+ }
40
+
41
+ static VALUE rb_concurrent_thread_method(VALUE self)
42
+ {
43
+ VALUE interrupt_flag;
44
+ VALUE interrupt_flag2;
45
+ rb_thread_blocking_region(count_to_10000,&interrupt_flag,stop_thr1,&interrupt_flag);
46
+ rb_thread_blocking_region(count_to_20000,&interrupt_flag2,stop_thr2,&interrupt_flag2);
47
+ }
48
+
49
+
50
+ void Init_concurrent_thread(){
51
+ ConcurrentThread = rb_define_class("ConcurrentThread",rb_cObject);
52
+ rb_define_method(ConcurrentThread,"defer",rb_concurrent_thread_method,0);
53
+ }