uringmachine 0.11 → 0.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8297cccb35ae1c8086889c2400db8bb884b0dfccdbef561caa0a4e9f80b21053
4
- data.tar.gz: 990e84bb2f975de00739150cff966bbf97a551ba135714eb92bc3b86cd14b5d0
3
+ metadata.gz: ccb8488c3f313b7a3890ae110fc5cebc7ac0062bbaa9ef362fb7ff5b02c680b9
4
+ data.tar.gz: 41f2c23a9427609c4d01a6eef319cbd40d029cf85918fd6c8d00e5b502f83ef6
5
5
  SHA512:
6
- metadata.gz: f66149736c1cf58f8df65d387b81ff070fd890e99aa7c8aa806a5afcb04b8248b78c95c1673d30b7d3270c81e05bc0b84afdb025d2a0a2f02beca5977196b321
7
- data.tar.gz: 7b6e6c31193da3a1e3e9a6fa627c84ae0f4d35c36f5fa636595acf1a013b71cd419073bb78be04130333796bed05e0ce9d1bfa74ab195608346cd689ecf9abaf
6
+ metadata.gz: 9bd08ebae21a7a10407ff04102d9cbf491bfab65ebe601b6ab3a6b7790632c946873415af4f45321e959bd39e875ff386e8c85f74329743204a9c0c047ce5da0
7
+ data.tar.gz: 3cd63222c0b990ef83f2b324aa46b36bd142a7b43a1a273428a9b70a95cfe25e0a1b06a2985a7463fe51a995c78e82838e34ee20d3c332cc46278ffd738652dd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 2025-06-02 Version 0.11.1
2
+
3
+ - Fix `UM::Stream` behaviour on GC
4
+
1
5
  # 2025-06-02 Version 0.11
2
6
 
3
7
  - Implement `UM::Stream` class for read streams
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/inline'
3
+ # require 'bundler/inline'
4
4
 
5
- gemfile do
6
- source 'https://rubygems.org'
7
- gem 'uringmachine', path: '..'
8
- gem 'benchmark-ips'
9
- gem 'http_parser.rb'
10
- end
5
+ # gemfile do
6
+ # source 'https://rubygems.org'
7
+ # gem 'uringmachine', path: '..'
8
+ # gem 'benchmark-ips'
9
+ # gem 'http_parser.rb'
10
+ # end
11
11
 
12
- require 'benchmark/ips'
12
+ require 'bundler/setup'
13
13
  require 'uringmachine'
14
+ require 'benchmark/ips'
14
15
  require 'http/parser'
15
16
 
16
17
  $machine = UM.new
@@ -19,6 +20,8 @@ HTTP_MSG = "GET /foo/bar HTTP/1.1\r\nServer: foobar.com\r\nFoo: bar\r\n\r\n"
19
20
 
20
21
  $count = 0
21
22
 
23
+ STDOUT.sync = true
24
+
22
25
  def parse_http_parser
23
26
  current_fiber = Fiber.current
24
27
  $count += 1
@@ -45,10 +48,16 @@ def parse_http_parser
45
48
  end
46
49
 
47
50
  $machine.write(w.fileno, HTTP_MSG)
48
- $machine.yield
51
+ ret = $machine.yield
52
+ ret
53
+ rescue Exception => e
54
+ p e: e
55
+ exit
49
56
  ensure
50
- $machine.close(r.fileno)
51
- $machine.close(w.fileno)
57
+ r.close rescue nil
58
+ w.close rescue nil
59
+ # $machine.close(r.fileno) rescue nil
60
+ # $machine.close(w.fileno) rescue nil
52
61
  end
53
62
 
54
63
  require 'stringio'
@@ -100,50 +109,114 @@ def parse_headers(fd)
100
109
  end
101
110
 
102
111
  def parse_http_stringio
103
- current_fiber = Fiber.current
104
- r, w = IO.pipe
112
+ rfd, wfd = UM.pipe
113
+ queue = UM::Queue.new
105
114
 
106
115
  $machine.spin do
107
- headers = parse_headers(r.fileno)
108
- $machine.schedule(current_fiber, headers)
116
+ headers = parse_headers(rfd)
117
+ $machine.push(queue, headers)
109
118
  rescue Exception => e
110
119
  p e
111
120
  puts e.backtrace.join("\n")
112
121
  exit!
113
122
  end
114
123
 
115
- $machine.write(w.fileno, HTTP_MSG)
116
- $machine.yield
124
+ $machine.write(wfd, HTTP_MSG)
125
+ $machine.close(wfd)
126
+ $machine.shift(queue)
117
127
  ensure
118
- $machine.close(r.fileno)
119
- $machine.close(w.fileno)
128
+ ($machine.close(rfd) rescue nil) if rfd
129
+ ($machine.close(wfd) rescue nil) if wfd
120
130
  end
121
131
 
122
- # p parse_http_parser
123
- # p parse_http_stringio
124
- # exit
132
+ def stream_parse_headers(fd)
133
+ stream = UM::Stream.new($machine, fd)
125
134
 
126
- GC.disable
135
+ headers = stream_get_request_line(stream)
136
+ return nil if !headers
127
137
 
128
- def alloc_count
129
- count0 = ObjectSpace.count_objects[:TOTAL]
130
- yield
131
- count1 = ObjectSpace.count_objects[:TOTAL]
132
- count1 - count0
138
+ while true
139
+ line = stream.get_line()
140
+ break if line.empty?
141
+
142
+ m = line.match(RE_HEADER_LINE)
143
+ raise "Invalid header" if !m
144
+
145
+ headers[m[1]] = m[2]
146
+ end
147
+
148
+ headers
133
149
  end
134
150
 
135
- X = 100
136
- p(
137
- alloc_http_parser: alloc_count { X.times { parse_http_parser } },
138
- alloc_stringio: alloc_count { X.times { parse_http_stringio } }
139
- )
140
- exit
151
+ def stream_get_request_line(stream)
152
+ line = stream.get_line()
153
+
154
+ m = line.match(RE_REQUEST_LINE)
155
+ return nil if !m
156
+
157
+ {
158
+ 'method' => m[1].downcase,
159
+ 'path' => m[2],
160
+ 'protocol' => m[3].downcase
161
+ }
162
+ end
163
+
164
+ def parse_http_stream
165
+ rfd, wfd = UM.pipe
166
+ queue = UM::Queue.new
167
+
168
+ $machine.spin do
169
+ headers = stream_parse_headers(rfd)
170
+ $machine.push(queue, headers)
171
+ rescue Exception => e
172
+ p e
173
+ puts e.backtrace.join("\n")
174
+ exit!
175
+ end
176
+
177
+ $machine.write(wfd, HTTP_MSG)
178
+ $machine.shift(queue)
179
+ ensure
180
+ ($machine.close(rfd) rescue nil) if rfd
181
+ ($machine.close(wfd) rescue nil) if wfd
182
+ end
183
+
184
+ # 10000.times { parse_http_parser }
185
+ # 10000.times { parse_http_stringio }
186
+ # 10000.times { parse_http_stream }
187
+ # exit
188
+
189
+ # GC.disable
190
+
191
+ # OS = ObjectSpace
192
+
193
+ # def object_count
194
+ # counts = ObjectSpace.count_objects
195
+ # counts[:TOTAL] - counts[:FREE]
196
+ # end
197
+
198
+ # def alloc_count
199
+ # GC.start
200
+ # count0 = object_count
201
+ # yield
202
+ # count1 = object_count
203
+ # count1 - count0
204
+ # end
205
+
206
+ # X = 100
207
+ # p(
208
+ # alloc_http_parser: alloc_count { X.times { parse_http_parser } },
209
+ # alloc_stringio: alloc_count { X.times { parse_http_stringio } },
210
+ # alloc_stream: alloc_count { X.times { parse_http_stream } }
211
+ # )
212
+ # exit
141
213
 
142
214
  Benchmark.ips do |x|
143
215
  x.config(:time => 5, :warmup => 3)
144
216
 
145
217
  x.report("http_parser") { parse_http_parser }
146
- x.report("homegrown") { parse_http_stringio }
218
+ x.report("stringio") { parse_http_stringio }
219
+ x.report("stream") { parse_http_stream }
147
220
 
148
221
  x.compare!
149
222
  end
data/ext/um/um.h CHANGED
@@ -271,7 +271,8 @@ VALUE um_queue_unshift(struct um *machine, struct um_queue *queue, VALUE value);
271
271
  VALUE um_queue_shift(struct um *machine, struct um_queue *queue);
272
272
 
273
273
  int stream_read_more(struct um_stream *stream);
274
-
274
+ VALUE stream_get_line(struct um_stream *stream);
275
+ VALUE stream_get_string(struct um_stream *stream, ulong len);
275
276
  VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer);
276
277
  VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer);
277
278
  VALUE resp_decode(struct um_stream *stream, VALUE out_buffer);
data/ext/um/um_stream.c CHANGED
@@ -1,5 +1,32 @@
1
1
  #include "um.h"
2
2
 
3
+ VALUE stream_get_line(struct um_stream *stream) {
4
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
5
+ while (true) {
6
+ char * lf_ptr = memchr(start, '\n', stream->len - stream->pos);
7
+ if (lf_ptr) {
8
+ ulong len = lf_ptr - start;
9
+ if (len && (start[len - 1] == '\r')) len -= 1;
10
+
11
+ VALUE str = rb_str_new(start, len);
12
+ stream->pos += lf_ptr - start + 1;
13
+ return str;
14
+ }
15
+
16
+ if (!stream_read_more(stream)) return Qnil;
17
+ }
18
+ }
19
+
20
+ VALUE stream_get_string(struct um_stream *stream, ulong len) {
21
+ while (stream->len - stream->pos < len)
22
+ if (!stream_read_more(stream)) return Qnil;
23
+
24
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
25
+ VALUE str = rb_utf8_str_new(start, len);
26
+ stream->pos += len;
27
+ return str;
28
+ }
29
+
3
30
  static inline void stream_check_truncate_buffer(struct um_stream *stream) {
4
31
  if ((stream->pos == stream->len) && (stream->len >= 1 << 12)) {
5
32
  rb_str_modify(stream->buffer);
@@ -58,6 +85,7 @@ VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer) {
58
85
  VALUE str = rb_str_new(start, len + 1);
59
86
  rb_str_set_len(str, len);
60
87
  RSTRING_PTR(str)[len] = 0;
88
+ RB_GC_GUARD(str);
61
89
  return str;
62
90
  }
63
91
 
@@ -37,6 +37,8 @@ static VALUE Stream_allocate(VALUE klass) {
37
37
  VALUE Stream_initialize(VALUE self, VALUE machine, VALUE fd) {
38
38
  struct um_stream *stream = RTYPEDDATA_DATA(self);
39
39
 
40
+ stream->self = self;
41
+
40
42
  stream->machine = RTYPEDDATA_DATA(machine);
41
43
  stream->fd = NUM2ULONG(fd);
42
44
  stream->buffer = rb_utf8_str_new_literal("");
@@ -54,35 +56,14 @@ VALUE Stream_get_line(VALUE self) {
54
56
  struct um_stream *stream = RTYPEDDATA_DATA(self);
55
57
  if (unlikely(stream->eof)) return Qnil;
56
58
 
57
- char *start = RSTRING_PTR(stream->buffer) + stream->pos;
58
- while (true) {
59
- char * lf_ptr = memchr(start, '\n', stream->len - stream->pos);
60
- if (lf_ptr) {
61
- ulong len = lf_ptr - start;
62
- if (len && (start[len - 1] == '\r')) len -= 1;
63
-
64
- VALUE str = rb_str_new(start, len);
65
- stream->pos += lf_ptr - start + 1;
66
- return str;
67
- }
68
-
69
- if (!stream_read_more(stream)) return Qnil;
70
- }
59
+ return stream_get_line(stream);
71
60
  }
72
61
 
73
62
  VALUE Stream_get_string(VALUE self, VALUE len) {
74
63
  struct um_stream *stream = RTYPEDDATA_DATA(self);
75
64
  if (unlikely(stream->eof)) return Qnil;
76
65
 
77
- ulong ulen = NUM2ULONG(len);
78
-
79
- while (stream->len - stream->pos < ulen)
80
- if (!stream_read_more(stream)) return Qnil;
81
-
82
- char *start = RSTRING_PTR(stream->buffer) + stream->pos;
83
- VALUE str = rb_utf8_str_new(start, ulen);
84
- stream->pos += ulen;
85
- return str;
66
+ return stream_get_string(stream, NUM2ULONG(len));
86
67
  }
87
68
 
88
69
  VALUE Stream_resp_get_line(VALUE self) {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.11'
4
+ VERSION = '0.11.1'
5
5
  end
data/uringmachine.gemspec CHANGED
@@ -23,4 +23,5 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency 'rake-compiler', '1.2.9'
24
24
  s.add_development_dependency 'minitest', '5.25.4'
25
25
  s.add_development_dependency 'benchmark-ips', '2.14.0'
26
+ s.add_development_dependency 'http_parser.rb', '0.8.0'
26
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uringmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.11'
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - '='
52
52
  - !ruby/object:Gem::Version
53
53
  version: 2.14.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: http_parser.rb
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '='
59
+ - !ruby/object:Gem::Version
60
+ version: 0.8.0
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '='
66
+ - !ruby/object:Gem::Version
67
+ version: 0.8.0
54
68
  email: sharon@noteflakes.com
55
69
  executables: []
56
70
  extensions: