packet 0.1.0 → 0.1.1

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.
@@ -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
+ }