nyara 0.0.1.pre.3 → 0.0.1.pre.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 52583810c7afb74e920b0da7336e82441471ab82
4
- data.tar.gz: 6fbd035f24bbd10ad1ade4ffdfce186e3a0d779f
3
+ metadata.gz: 7dc7b625bcf917f1fc957ca42953345df87faa40
4
+ data.tar.gz: f7c7669fed83475cb1626dec024e30eed7cf76ee
5
5
  SHA512:
6
- metadata.gz: 7a3abf537a5027658497d171a5a593153316e60214b81c80f4f93c1e0557478f7473136b5cec6fc0daa76eedbb7650b01a284c72098a3069fa95bffd18ec468a
7
- data.tar.gz: 8c5031033482180d9e0c1b3a4e541274abb562de5b1d04e81855c7ca556c7fcc44415abcf06a694b001f1c6a8dbd273509490a8ba45f3c58a0cf595921d92731
6
+ metadata.gz: e76227e8c9e4f4dc292b32e1cc72e11525e91a5b6f54784d4f7f6a63fba4b126f5fea81070cf793723908d82182faac1e3a47d3835903a99426fb82cb85e93b1
7
+ data.tar.gz: 592183cbad4bb90c3090a752832a2e7d26138e07b38bf2e7cb94544046619c3f6f620c566e21119f80f8c99c0f360c0be49f0303b51b19b6ac25a0c4a564c407
data/ext/event.c CHANGED
@@ -21,6 +21,7 @@ static void loop_body(int fd, int etype);
21
21
  static int qfd;
22
22
 
23
23
  #define MAX_RECEIVE_DATA 65536
24
+ // * 4
24
25
  static char received_data[MAX_RECEIVE_DATA];
25
26
  extern http_parser_settings nyara_request_parse_settings;
26
27
 
@@ -36,6 +37,7 @@ static ID id_not_found;
36
37
  static VALUE sym_term_close;
37
38
  static VALUE sym_writing;
38
39
  static VALUE sym_reading;
40
+ static VALUE sym_sleep;
39
41
  static Request* curr_request;
40
42
 
41
43
  static void _set_nonblock(int fd) {
@@ -59,6 +61,9 @@ static VALUE _fiber_func(VALUE _, VALUE args) {
59
61
  static void _handle_request(VALUE request) {
60
62
  Request* p;
61
63
  Data_Get_Struct(request, Request, p);
64
+ if (p->sleeping) {
65
+ return;
66
+ }
62
67
  curr_request = p;
63
68
 
64
69
  // read and parse data
@@ -115,8 +120,8 @@ static void _handle_request(VALUE request) {
115
120
  // do nothing
116
121
  } else if (state == sym_reading) {
117
122
  // do nothing
118
- } else {
119
- // double value: sleep
123
+ } else if (state == sym_sleep) {
124
+ // do nothing
120
125
  }
121
126
  }
122
127
 
@@ -178,6 +183,34 @@ static VALUE ext_run_queue(VALUE _, VALUE v_fd) {
178
183
  return Qnil;
179
184
  }
180
185
 
186
+ static VALUE ext_request_sleep(VALUE _, VALUE request) {
187
+ Request* p;
188
+ Data_Get_Struct(request, Request, p);
189
+
190
+ VALUE* v_fds = RARRAY_PTR(p->watched_fds);
191
+ long v_fds_len = RARRAY_LEN(p->watched_fds);
192
+ for (long i = 0; i < v_fds_len; i++) {
193
+ DEL_E(FIX2INT(v_fds[i]));
194
+ }
195
+ DEL_E(p->fd);
196
+ p->sleeping = true;
197
+ return Qnil;
198
+ }
199
+
200
+ static VALUE ext_request_wakeup(VALUE _, VALUE request) {
201
+ // NOTE should not use current_request
202
+ Request* p;
203
+ Data_Get_Struct(request, Request, p);
204
+
205
+ VALUE* v_fds = RARRAY_PTR(p->watched_fds);
206
+ long v_fds_len = RARRAY_LEN(p->watched_fds);
207
+ for (long i = 0; i < v_fds_len; i++) {
208
+ ADD_E(FIX2INT(v_fds[i]), ETYPE_CONNECT);
209
+ }
210
+ ADD_E(p->fd, ETYPE_HANDLE_REQUEST);
211
+ return Qnil;
212
+ }
213
+
181
214
  static VALUE ext_set_nonblock(VALUE _, VALUE v_fd) {
182
215
  int fd = FIX2INT(v_fd);
183
216
  _set_nonblock(fd);
@@ -273,10 +306,14 @@ void Init_event(VALUE ext) {
273
306
  sym_term_close = ID2SYM(rb_intern("term_close"));
274
307
  sym_writing = ID2SYM(rb_intern("writing"));
275
308
  sym_reading = ID2SYM(rb_intern("reading"));
309
+ sym_sleep = ID2SYM(rb_intern("sleep"));
276
310
 
277
311
  rb_define_singleton_method(ext, "init_queue", ext_init_queue, 0);
278
312
  rb_define_singleton_method(ext, "run_queue", ext_run_queue, 1);
279
313
 
314
+ rb_define_singleton_method(ext, "request_sleep", ext_request_sleep, 0);
315
+ rb_define_singleton_method(ext, "request_wakeup", ext_request_wakeup, 0);
316
+
280
317
  // fd operations
281
318
  rb_define_singleton_method(ext, "set_nonblock", ext_set_nonblock, 1);
282
319
  rb_define_singleton_method(ext, "fd_watch", ext_fd_watch, 1);
data/ext/inc/epoll.h CHANGED
@@ -8,6 +8,9 @@ static struct epoll_event qevents[MAX_E];
8
8
 
9
9
  static void ADD_E(int fd, uint64_t etype) {
10
10
  struct epoll_event e;
11
+ // not using edge trigger flag EPOLLET
12
+ // because edge trigger only fire once when fd is readable/writable
13
+ // but the event may not be consumed in our handler
11
14
  e.events = EPOLLIN | EPOLLOUT;
12
15
  e.data.u64 = (etype << 32) | (uint64_t)fd;
13
16
 
@@ -20,8 +23,7 @@ static void ADD_E(int fd, uint64_t etype) {
20
23
  # endif
21
24
  }
22
25
 
23
- // either epoll or kqueue removes the event watch from queue when fd closed
24
- // seems this is not required in epoll?
26
+ // NOTE either epoll or kqueue removes the event watch from queue when fd closed
25
27
  static void DEL_E(int fd) {
26
28
  struct epoll_event e;
27
29
  e.events = EPOLLIN | EPOLLOUT;
@@ -38,8 +40,7 @@ static void DEL_E(int fd) {
38
40
  static void INIT_E() {
39
41
  qfd = epoll_create(10); // size not important
40
42
  if (qfd == -1) {
41
- printf("%s\n", strerror(errno));
42
- exit(-1);
43
+ rb_sys_fail("epoll_create(2)");
43
44
  }
44
45
  }
45
46
 
data/ext/inc/kqueue.h CHANGED
@@ -16,8 +16,9 @@ static struct kevent qevents[MAX_E];
16
16
 
17
17
  static void ADD_E(int fd, uint64_t etype) {
18
18
  struct kevent e;
19
+ // without EV_CLEAR, it is level-triggered
20
+ // http://www.cs.helsinki.fi/linux/linux-kernel/2001-38/0547.html
19
21
  EV_SET(&e, fd, EVFILT_READ | EVFILT_WRITE, EV_ADD, 0, 0, (void*)etype);
20
- // todo timeout
21
22
  # ifdef NDEBUG
22
23
  kevent(qfd, &e, 1, NULL, 0, NULL);
23
24
  # else
@@ -26,7 +27,7 @@ static void ADD_E(int fd, uint64_t etype) {
26
27
  # endif
27
28
  }
28
29
 
29
- static void DEL_E(int fd, int filter) {
30
+ static void DEL_E_WITH_FILTER(int fd, int filter) {
30
31
  struct kevent e;
31
32
  EV_SET(&e, fd, filter, EV_DELETE, 0, 0, NULL);
32
33
  # ifdef NDEBUG
@@ -37,11 +38,14 @@ static void DEL_E(int fd, int filter) {
37
38
  # endif
38
39
  }
39
40
 
41
+ static void DEL_E(int fd) {
42
+ DEL_E_WITH_FILTER(fd, EVFILT_READ | EVFILT_WRITE);
43
+ }
44
+
40
45
  static void INIT_E() {
41
46
  qfd = kqueue();
42
47
  if (qfd == -1) {
43
- printf("%s\n", strerror(errno));
44
- exit(-1);
48
+ rb_sys_fail("kqueue(2)");
45
49
  }
46
50
  }
47
51
 
@@ -61,7 +65,7 @@ static void LOOP_E() {
61
65
  if (qevents[i].flags & EV_EOF) {
62
66
  // EV_EOF is set if the read side of the socket is shutdown
63
67
  // the event can keep flipping back to consume cpu if we don't remove it
64
- DEL_E(fd, qevents[i].filter);
68
+ DEL_E_WITH_FILTER(fd, qevents[i].filter);
65
69
  }
66
70
  if (qevents[i].filter & (EVFILT_READ | EVFILT_WRITE)) {
67
71
  loop_body(fd, (int)qevents[i].udata);
data/ext/inc/version.inc CHANGED
@@ -1 +1 @@
1
- #define NYARA_VERSION "0.0.1.pre"
1
+ #define NYARA_VERSION "0.0.1"
data/ext/nyara.c CHANGED
@@ -20,6 +20,8 @@ void Init_nyara() {
20
20
  set_fd_limit(20000);
21
21
 
22
22
  VALUE nyara = rb_define_module("Nyara");
23
+ # include "inc/version.inc"
24
+ rb_const_set(nyara, rb_intern("VERSION"), rb_str_new2(NYARA_VERSION));
23
25
 
24
26
  // utils: hashes
25
27
  Init_hashes(nyara);
data/ext/request.c CHANGED
@@ -73,6 +73,8 @@ static Request* _request_alloc() {
73
73
  volatile VALUE watched_fds = rb_ary_new();
74
74
  p->watched_fds = watched_fds;
75
75
 
76
+ p->sleeping = false;
77
+
76
78
  p->self = Data_Wrap_Struct(request_class, request_mark, request_free, p);
77
79
  return p;
78
80
  }
data/ext/request.h CHANGED
@@ -40,4 +40,6 @@ typedef struct {
40
40
  VALUE response_header_extra_lines;
41
41
 
42
42
  VALUE watched_fds;
43
+
44
+ bool sleeping;
43
45
  } Request;
@@ -314,7 +314,17 @@ module Nyara
314
314
 
315
315
  # Resume action after +seconds+
316
316
  def sleep seconds
317
- Fiber.yield seconds.to_f # todo
317
+ seconds = seconds.to_f
318
+ raise ArgumentError, 'bad sleep seconds' if seconds < 0
319
+
320
+ # NOTE request_wake requires request as param, so this method can not be generalized to Fiber.sleep
321
+
322
+ Ext.request_sleep self # place sleep actions before wake
323
+ Thread.new do
324
+ sleep seconds
325
+ Ext.request_wakeup self
326
+ end
327
+ Fiber.yield :sleep # see event.c for the handler
318
328
  end
319
329
 
320
330
  # One shot render, and terminate the action.
data/lib/nyara/nyara.rb CHANGED
@@ -59,7 +59,7 @@ module Nyara
59
59
 
60
60
  def patch_tcp_socket
61
61
  puts "patching TCPSocket"
62
- require_relative "patch_tcp_socket"
62
+ require_relative "patches/tcp_socket"
63
63
  end
64
64
 
65
65
  def start_production_server port
data/nyara.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "nyara"
3
- s.version = "0.0.1.pre.3"
3
+ s.version = "0.0.1.pre.4"
4
4
  s.author = "Zete Lui"
5
5
  s.email = "nobody@example.com"
6
6
  s.homepage = "https://github.com/luikore/nyara"
data/rakefile CHANGED
@@ -11,7 +11,7 @@ desc "code generate"
11
11
  task :gen => [status_file, version_file]
12
12
 
13
13
  desc "generate #{status_file}"
14
- file status_file do
14
+ file status_file => __FILE__ do
15
15
  puts "generating: #{status_file}"
16
16
  require "nokogiri"
17
17
  require "open-uri"
@@ -28,13 +28,13 @@ file status_file do
28
28
  end
29
29
 
30
30
  desc "generate #{version_file}"
31
- file version_file => 'nyara.gemspec' do
31
+ file version_file => ['nyara.gemspec', __FILE__] do
32
32
  puts "generating: #{version_file}"
33
33
  lines = File.readlines('nyara.gemspec')
34
34
  version = nil
35
35
  lines.each do |line|
36
36
  if line =~ /s\.version =/
37
- version = line[/\d+(\.\d+)*(\.pre)?/]
37
+ version = line[/\d+(\.\d+)*(\.pre\d+)?/]
38
38
  break
39
39
  end
40
40
  end
data/readme.md CHANGED
@@ -1,14 +1,20 @@
1
- Not Yet Another Ruby Async web framework and server. Not on rack nor rack-compatible neither eventmachine.
1
+ Not Yet Another Ruby Async web framework and server.
2
+
3
+ [![Build Status](https://travis-ci.org/luikore/nyara.png)](https://travis-ci.org/luikore/nyara)
2
4
 
3
5
  - Evented IO while API remains synchrony
4
- - Prefork production server
6
+ - Prefork production server, mixing a bit blocking operations won't block other users
5
7
  - Sinatra-like http method and scanf-like http path and path helper
6
8
  - Request format matcher with just `case ... when`
7
9
  - Easy to stream the view with `Fiber.yield`
8
10
 
9
11
  # Getting started
10
12
 
11
- Requires Ruby 2.0+, BSD/Linux/Mac OS X, GCC4.7+ or Clang.
13
+ Requirement
14
+
15
+ - BSD/Linux/Mac OS X
16
+ - Ruby 2.0.0-p195 or higher (due to some syntax issues, doesn't work on 2.0.0-p0)
17
+ - GCC4.5+ or Clang
12
18
 
13
19
  Install
14
20
 
@@ -31,95 +37,17 @@ And start server
31
37
  ruby nyahaha.rb
32
38
  ```
33
39
 
34
- # Build from source
35
-
36
- After cloning
37
-
38
- ```bash
39
- git submodule update --init
40
- bundle
41
- rake gen
42
- rake gem
43
- ```
44
-
45
- If you have cloned the repo once, and want to update code
46
-
47
- ```bash
48
- git pull --recurse-submodules
49
- git submodule foreach git fetch
50
- ```
51
-
52
- # Testing
53
-
54
- Simply run the test
55
-
56
- ```bash
57
- rspec -c
58
- ```
59
-
60
- Test in GC.stress mode
61
-
62
- ```bash
63
- rspec -c -f d
64
- ```
65
-
66
- With coverage (generates *coverage/index.html*)
67
-
68
- ```bash
69
- COVERAGE=1 rspec -c
70
- ```
71
-
72
- # Why fast
73
-
74
- ### Solid http parsers written in C
75
-
76
- Nyara uses two evented parsers:
77
-
78
- - [http_parser](https://github.com/joyent/http-parser) with chunked encoding support
79
- - [multipart-parser-c](https://github.com/iafonov/multipart-parser-c) (todo)
80
-
81
- And implemented the following in addition:
82
-
83
- - RFC2616 compliant `Accept*` parser
84
- - MIME type matcher
85
- - Url-encoded parser for path / query / request body
86
-
87
- ### Decisive routing on header complete
88
-
89
- To support HTTP methods like PUT, DELETE for archaic browsers, a technique called **method override** is used, and the HTTP method can be overriden by a request param (usually named `_method`). In Rack the param may rest in request body, so it needs to parse the whole body before routing to a desired action. In Nyara the param is always in request path query, and routing starts when header completes. So server can do a lot of things before a huge file completely uploaded and provide better user experience.
90
-
91
- ### Thin evented IO layer built for BSD or Linux
92
-
93
- Nyara is only for systems with kqueue or epoll (maybe iocp in the future). Manufactural event queue is a waste of time.
94
-
95
- ### Solve sequential problems with Fiber
96
-
97
- The common caveats of an evented framework is: mutual callbacks must be used to ensure the order of operations. In eventmachine, sent data buffers are copied and chained in a deque to flush, and `close_connection_after_writing` must be called to ensure that all data are written before close.
98
-
99
- While in Nyara, the data sending is much simpler: we send them directly, if there are remaining bytes, the action fiber is paused. When the socket is ready again, we wake up the fiber to continue the send action. So a lot of duplications, memory copy and schedule are avoided and `close` is the `close`.
100
-
101
- ### More stable memory management
102
-
103
- To make better user experience, you may tune the server to stop GC while doing request, and start GC again after every serveral requests. But by doing so you are increasing the probability of OOM: there are cases when `malloc` or `new` fails to get memory while the GC stopped by you can release some. With C-ext this can be partly fixed with Ruby's `ALLOC` and `ALLOC_N`, which can make GC release some memory when `malloc` fails. But with C++ this becomes a bit messy: you need to redefine `new` operators.
104
-
105
- In Nyara, the use of C++ memory allocation is limited to boot time (the use of C++ may possibly removed in the future) so your server has less chance to be quit by a silent `OutOfMemoryException`.
106
-
107
- ### Shared buffer in layout rendering
108
-
109
- Consider you have a page with nested layout: `layout1` encloses `layout2`, and `layout2` contains `page`.
110
-
111
- When rendering `layout2`, the output string of `page` becomes an element inside the array buffer of `layout2`, then the output of `page` is duplicated in the output of `layout2`. When rendering `layout1`, the output of `layout2` is duplicated so a string containing the output of `page` is duplicated, again.
112
-
113
- In Nyara, nested templates of Slim, ERB or Haml share the same output buffer, so the duplication is greatly reduced.
40
+ # Documentation
114
41
 
115
- # How fast
42
+ - [Manual](https://github.com/luikore/nyara/wiki/Manual)
43
+ - [API doc](http://rubydoc.info/github/luikore/nyara/master/frames)
44
+ - [Building from source](https://github.com/luikore/nyara/wiki/Building)
116
45
 
117
- Performance is feature, there are specs on:
46
+ # Caveats
118
47
 
119
- - Accept-* parse vs sinatra
120
- - param parse vs ruby
121
- - layout rendering vs tilt
48
+ - *Nyara* is not based on [rack](https://github.com/rack/rack).
49
+ - *Nyara* is not compatible with [eventmachine](https://github.com/eventmachine/eventmachine). It won't work if you add gems like [em-synchrony](https://github.com/igrigorik/em-synchrony).
122
50
 
123
51
  # License
124
52
 
125
- BSD, see copying
53
+ BSD, see [copying](https://github.com/luikore/nyara/blob/master/copying)
@@ -1,4 +1,5 @@
1
1
  require_relative "spec_helper"
2
+ unless ENV['SKIP_PERFORMANCE']
2
3
 
3
4
  # run benchmarks in performance/, each output is a hash dumped with Marshal
4
5
  #
@@ -18,7 +19,7 @@ describe 'performance' do
18
19
 
19
20
  it "[parse_accept_value] faster than sinatra" do
20
21
  res = bm 'parse_accept_value'
21
- assert res[:nyara] * 1.8 < res[:sinatra], res.inspect
22
+ assert res[:nyara] * 1.7 < res[:sinatra], res.inspect
22
23
  end
23
24
 
24
25
  it "[parse_param] faster than parse in pure ruby" do
@@ -31,3 +32,5 @@ describe 'performance' do
31
32
  assert res[:nyara] * 1.1 < res[:tilt], res.inspect
32
33
  end
33
34
  end
35
+
36
+ end # unless
@@ -1,4 +1,4 @@
1
- require_relative "lib/nyara"
1
+ require_relative "../lib/nyara"
2
2
  require "open-uri"
3
3
  require "pry"
4
4
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nyara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre.3
4
+ version: 0.0.1.pre.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zete Lui
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-25 00:00:00.000000000 Z
11
+ date: 2013-06-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Fast, slim and fuzzy ruby web framework + server, based on preforked
14
14
  event queue and Fiber. NO rack NOR eventmachine are used.
@@ -26,7 +26,6 @@ files:
26
26
  - example/hello.rb
27
27
  - example/stream.rb
28
28
  - ext/extconf.rb
29
- - hello.rb
30
29
  - lib/nyara/config.rb
31
30
  - lib/nyara/controller.rb
32
31
  - lib/nyara/cookie.rb
@@ -36,7 +35,7 @@ files:
36
35
  - lib/nyara/hashes/param_hash.rb
37
36
  - lib/nyara/mime_types.rb
38
37
  - lib/nyara/nyara.rb
39
- - lib/nyara/patch_tcp_socket.rb
38
+ - lib/nyara/patches/tcp_socket.rb
40
39
  - lib/nyara/request.rb
41
40
  - lib/nyara/route.rb
42
41
  - lib/nyara/route_entry.rb
@@ -64,6 +63,7 @@ files:
64
63
  - spec/spec_helper.rb
65
64
  - spec/view_spec.rb
66
65
  - tools/bench-cookie.rb
66
+ - tools/hello.rb
67
67
  - ext/http-parser/http_parser.h
68
68
  - ext/inc/epoll.h
69
69
  - ext/inc/kqueue.h