about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2014-11-16 08:32:16 +0000
committerEric Wong <e@80x24.org>2015-12-13 02:50:12 +0000
commit5b5cf896871efdb110ae831fd7fc34fb78ec2243 (patch)
tree697b8c2dffafd2822148af3fc95e3f1d6677c782
parent639cc06cb0350e1a934f151fcbdb72a1c681792c (diff)
downloadunicorn-5b5cf896871efdb110ae831fd7fc34fb78ec2243.tar.gz
This provides some extra type safety if combined with other
C extensions, as well as allowing us to account for memory usage of
the HTTP parser in ObjectSpace.

This requires Ruby 1.9.3+ and has remained a stable API since
then.  This will become officially supported when Ruby 2.3.0 is
released later this month.

This API has only been documented in doc/extension.rdoc (formerly
README.EXT) in the Ruby source tree since April 2015, r50318
-rw-r--r--ext/unicorn_http/unicorn_http.rl35
-rw-r--r--test/unit/test_http_parser.rb14
2 files changed, 37 insertions, 12 deletions
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 046ccb5..957a5e3 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -442,11 +442,31 @@ post_exec: /* "_out:" also goes here */
   assert(hp->offset <= len && "offset longer than length");
 }
 
+static void hp_mark(void *ptr)
+{
+  struct http_parser *hp = ptr;
+
+  rb_gc_mark(hp->buf);
+  rb_gc_mark(hp->env);
+  rb_gc_mark(hp->cont);
+}
+
+static size_t hp_memsize(const void *ptr)
+{
+  return sizeof(struct http_parser);
+}
+
+static const rb_data_type_t hp_type = {
+  "unicorn_http",
+  { hp_mark, RUBY_TYPED_DEFAULT_FREE, hp_memsize, /* reserved */ },
+  /* parent, data, [ flags ] */
+};
+
 static struct http_parser *data_get(VALUE self)
 {
   struct http_parser *hp;
 
-  Data_Get_Struct(self, struct http_parser, hp);
+  TypedData_Get_Struct(self, struct http_parser, &hp_type, hp);
   assert(hp && "failed to extract http_parser struct");
   return hp;
 }
@@ -552,21 +572,12 @@ static void finalize_header(struct http_parser *hp)
     rb_hash_aset(hp->env, g_query_string, rb_str_new(NULL, 0));
 }
 
-static void hp_mark(void *ptr)
-{
-  struct http_parser *hp = ptr;
-
-  rb_gc_mark(hp->buf);
-  rb_gc_mark(hp->env);
-  rb_gc_mark(hp->cont);
-}
-
 static VALUE HttpParser_alloc(VALUE klass)
 {
   struct http_parser *hp;
-  return Data_Make_Struct(klass, struct http_parser, hp_mark, -1, hp);
-}
 
+  return TypedData_Make_Struct(klass, struct http_parser, &hp_type, hp);
+}
 
 /**
  * call-seq:
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index 431ede5..c72f7f2 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -851,4 +851,18 @@ class HttpParserTest < Test::Unit::TestCase
          File.readable?(LINUX_PROC_PID_STATUS) &&
          !defined?(RUBY_ENGINE)
 
+  def test_memsize
+    require 'objspace'
+    if ObjectSpace.respond_to?(:memsize_of)
+      n = ObjectSpace.memsize_of(Unicorn::HttpParser.new)
+      assert_kind_of Integer, n
+      # need to update this when 128-bit machines come out
+      # n.b. actual struct size on 64-bit is 56 bytes + 40 bytes for RVALUE
+      # Ruby <= 2.2 objspace did not count the 40-byte RVALUE, 2.3 does.
+      assert_operator n, :<=, 96
+      assert_operator n, :>, 0
+    end
+  rescue LoadError
+    # not all Ruby implementations have objspace
+  end
 end