mwrap user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: <mwrap-public@80x24.org>
Cc: Sam Saffron <sam.saffron@gmail.com>
Subject: [PATCH 2/3] support Ruby 3.0.x
Date: Sat, 20 Aug 2022 21:35:22 +0000	[thread overview]
Message-ID: <20220820213523.18504-3-e@80x24.org> (raw)
In-Reply-To: <20220820213523.18504-1-e@80x24.org>

HEAP_PAGE_SIZE no longer estimates malloc overhead in Ruby 3.0.x,
but we can now rely on the GC::INTERNAL_CONSTANTS hash to access
the true value.

We also need to account for Ractors in 3.0+, and thus we need to
rely on thread-specific `ruby_current_ec' instead of process-wide
`ruby_current_execution_context_ptr' (which no longer exists in
3.0+).

Finally, the VM seems prone to making some small immortal
allocations in a few places we were not expecting in 2.7 and
earlier.  Account for that and loosen some exact checks to
account for it.

Tested on Ruby 3.0.3 and 3.0.4
---
 ext/mwrap/extconf.rb |  7 +++++++
 ext/mwrap/mwrap.c    | 25 +++++++++++++++++++++----
 test/test_mwrap.rb   | 27 ++++++++++++++++-----------
 3 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/ext/mwrap/extconf.rb b/ext/mwrap/extconf.rb
index e9dbb1e..254a3bb 100644
--- a/ext/mwrap/extconf.rb
+++ b/ext/mwrap/extconf.rb
@@ -25,4 +25,11 @@ else
   abort 'missing __builtin_add_overflow'
 end
 
+begin
+  if n = GC::INTERNAL_CONSTANTS[:HEAP_PAGE_SIZE]
+    $defs << "-DHEAP_PAGE_SIZE=#{n}"
+  end
+rescue NameError
+end
+
 create_makefile 'mwrap'
diff --git a/ext/mwrap/mwrap.c b/ext/mwrap/mwrap.c
index 8592ea7..cd68eed 100644
--- a/ext/mwrap/mwrap.c
+++ b/ext/mwrap/mwrap.c
@@ -3,7 +3,7 @@
  * License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
  */
 #define _LGPL_SOURCE /* allows URCU to inline some stuff */
-#include <ruby/ruby.h>
+#include <ruby.h> /* defines HAVE_RUBY_RACTOR_H on 3.0+ */
 #include <ruby/thread.h>
 #include <ruby/io.h>
 #include <execinfo.h>
@@ -22,10 +22,24 @@
 #include <urcu/rculist.h>
 #include "jhash.h"
 
+#if __STDC_VERSION__ >= 201112
+#	define MWRAP_TSD _Thread_local
+#elif defined(__GNUC__)
+#	define MWRAP_TSD __thread
+#else
+#	error _Thread_local nor __thread supported
+#endif
+
 static ID id_uminus;
 const char *rb_source_location_cstr(int *line); /* requires 2.6.0dev */
 extern int __attribute__((weak)) ruby_thread_has_gvl_p(void);
+
+#ifdef HAVE_RUBY_RACTOR_H /* Ruby 3.0+ */
+extern MWRAP_TSD void * __attribute__((weak)) ruby_current_ec;
+#else /* Ruby 2.6-2.7 */
 extern void * __attribute__((weak)) ruby_current_execution_context_ptr;
+#	define ruby_current_ec ruby_current_execution_context_ptr
+#endif
 extern void * __attribute__((weak)) ruby_current_vm_ptr; /* for rb_gc_count */
 extern size_t __attribute__((weak)) rb_gc_count(void);
 extern VALUE __attribute__((weak)) rb_cObject;
@@ -40,9 +54,12 @@ static size_t total_bytes_inc, total_bytes_dec;
 /* match values in Ruby gc.c */
 #define HEAP_PAGE_ALIGN_LOG 14
 enum {
-	HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG),
+	HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG)
+#ifndef HEAP_PAGE_SIZE /* Ruby 2.6-2.7 only */
+	,
 	REQUIRED_SIZE_BY_MALLOC = (sizeof(size_t) * 5),
 	HEAP_PAGE_SIZE = (HEAP_PAGE_ALIGN - REQUIRED_SIZE_BY_MALLOC)
+#endif
 };
 
 #define IS_HEAP_PAGE_BODY ((struct src_loc *)-1)
@@ -75,7 +92,7 @@ static int resolving_malloc;
 	} \
 } while (0)
 
-static __thread size_t locating;
+static MWRAP_TSD size_t locating;
 static size_t generation;
 static size_t page_size;
 static struct cds_lfht *totals;
@@ -214,7 +231,7 @@ static char *int2str(int num, char *dst, size_t * size)
 static int has_ec_p(void)
 {
 	return (ruby_thread_has_gvl_p() && ruby_current_vm_ptr &&
-		ruby_current_execution_context_ptr);
+		ruby_current_ec);
 }
 
 struct acc {
diff --git a/test/test_mwrap.rb b/test/test_mwrap.rb
index 48fba23..fadaef6 100644
--- a/test/test_mwrap.rb
+++ b/test/test_mwrap.rb
@@ -29,7 +29,8 @@ class TestMwrap < Test::Unit::TestCase
       tmp.rewind
       lines = tmp.readlines
       line_1 = lines.grep(/\s-e:1\b/)[0].strip
-      assert_equal '10001', line_1.split(/\s+/)[0]
+      bytes = line_1.split(/\s+/)[0].to_i
+      assert_operator bytes, :>=, 10001
     end
   end
 
@@ -42,7 +43,7 @@ class TestMwrap < Test::Unit::TestCase
       res = system(env, *cmd, { 5 => tmp })
       assert res, $?.inspect
       tmp.rewind
-      assert_match(/\b10001\s+1\s+-e:1$/, tmp.read)
+      assert_match(/\b1\d{4}\s+[1-9]\d*\s+-e:1$/, tmp.read)
 
       env['MWRAP'] = 'dump_fd:1,dump_min:10000'
       tmp.rewind
@@ -50,14 +51,14 @@ class TestMwrap < Test::Unit::TestCase
       res = system(env, *cmd, { 1 => tmp })
       assert res, $?.inspect
       tmp.rewind
-      assert_match(/\b10001\s+1\s+-e:1$/, tmp.read)
+      assert_match(/\b1\d{4}\s+[1-9]\d*\s+-e:1$/, tmp.read)
 
       tmp.rewind
       tmp.truncate(0)
       env['MWRAP'] = "dump_path:#{tmp.path},dump_min:10000"
       res = system(env, *cmd)
       assert res, $?.inspect
-      assert_match(/\b10001\s+1\s+-e:1$/, tmp.read)
+      assert_match(/\b1\d{4}\s+[1-9]\d*\s+-e:1$/, tmp.read)
 
       tmp.rewind
       tmp.truncate(0)
@@ -98,7 +99,7 @@ class TestMwrap < Test::Unit::TestCase
       tmp.rewind
       buf = tmp.read
       assert_not_match(/\s+-e:1$/, buf)
-      assert_match(/\b20001\s+1\s+-e:3$/, buf)
+      assert_match(/\b2\d{4}\s+[0-9]\d*\s+-e:3$/, buf)
     end
   end
 
@@ -176,8 +177,8 @@ class TestMwrap < Test::Unit::TestCase
       -e GC.disable
       -e keep=("0"*10000)
       -e loc=Mwrap["-e:3"]
-      -e loc.each{|size,gen|p([size,gen,count])}
-    )
+      -e
+    ) + [ 'loc.each{|size,gen|p([size,gen,count]) if size > 10000}' ]
     buf = IO.popen(@@env, cmd, &:read)
     assert_predicate $?, :success?
     assert_match(/\A\[\s*\d+,\s*\d+,\s*\d+\]\s*\z/s, buf)
@@ -230,7 +231,8 @@ class TestMwrap < Test::Unit::TestCase
       loc.name == k or abort 'SourceLocation#name broken'
       loc.total >= 10000 or abort 'SourceLocation#total broken'
       loc.frees == 0 or abort 'SourceLocation#frees broken'
-      loc.allocations == 1 or abort 'SourceLocation#allocations broken'
+      loc.allocations >= 1 or
+        abort "SourceLocation#allocations broken: #{loc.allocations}"
       seen = false
       loc.each do |*x| seen = x end
       seen[1] == loc.total or 'SourceLocation#each broken'
@@ -240,7 +242,9 @@ class TestMwrap < Test::Unit::TestCase
       freed = false
       until freed
         freed = true
-        loc.each do freed = false end
+        loc.each do |size, gen|
+          freed = false if size >= 10000
+        end
       end
       loc.frees == 1 or abort 'SourceLocation#frees broken (after free)'
       Float === loc.mean_lifespan or abort 'mean_lifespan broken'
@@ -264,8 +268,9 @@ class TestMwrap < Test::Unit::TestCase
     assert_separately(+"#{<<~"begin;"}\n#{<<~'end;'}")
     begin;
       require 'mwrap'
-      before = __LINE__
+      before = nil
       res = Mwrap.quiet do |depth|
+        before = __LINE__
         depth == 1 or abort 'depth is not 1'
         ('a' * 10000).clear
         Mwrap.quiet { |d| d == 2 or abort 'depth is not 2' }
@@ -304,7 +309,7 @@ class TestMwrap < Test::Unit::TestCase
         gen <= GC.count && gen >= 0 or abort "bad generation: #{gen}"
         (0 == (addr & 16383)) or abort "addr not aligned: #{'%x' % addr}"
       end
-      nr == ap or abort 'HeapPageBody.each missed page'
+      nr == ap or abort "HeapPageBody.each missed page #{nr} != #{ap}"
       10.times { (1..20000).to_a.map(&:to_s) }
       3.times { GC.start }
       Mwrap::HeapPageBody.stat(h)

  parent reply	other threads:[~2022-08-20 21:35 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-20 21:35 [PATCH 0/3] Ruby 3.x support Eric Wong
2022-08-20 21:35 ` [PATCH 1/3] constify arg for totals_add_rcu Eric Wong
2022-08-20 21:35 ` Eric Wong [this message]
2022-08-20 21:35 ` [PATCH 3/3] disable HeapPageBody count test for Ruby 3.1 Eric Wong
2022-08-20 23:18   ` Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://80x24.org/mwrap/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220820213523.18504-3-e@80x24.org \
    --to=e@80x24.org \
    --cc=mwrap-public@80x24.org \
    --cc=sam.saffron@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mwrap.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).