Eric Wong <normalperson / yhbt.net> wrote:
> Tanaka Akira <akr / fsij.org> wrote:
> > * create and cache a subclass of Errno::EAGAIN which includes IO::WaitReadable.
> 
> I think caching the subclass is still needed.

Here's my attempt to add caching, test/socket/test_nonblock.rb passes

From f0c274e9e87634f47515a68bb18693d9d8423bc8 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson / yhbt.net>
Date: Wed, 22 Sep 2010 17:20:28 -0700
Subject: [PATCH] error.c (rb_mod_sys_fail): use subclass and cache

While r28813 avoids clearing the method cache upon extending
with IO::Wait{Read,Writ}able modules, the method cache still
gets scanned and partially cleared when the exceptions are
garbage collected.

Reusing the same subclass now prevents rb_clear_cache_by_class()
from showing up at/near the top of profiler output.
---
 error.c |   40 ++++++++++++++++++++++++++++++++++++++--
 1 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/error.c b/error.c
index 479caff..bc7edef 100644
--- a/error.c
+++ b/error.c
@@ -27,6 +27,8 @@
 
 extern const char ruby_description[];
 
+static VALUE mod_sys_fail_cache;
+
 static const char *
 rb_strerrno(int err)
 {
@@ -1541,8 +1543,42 @@ rb_sys_fail(const char *mesg)
 void
 rb_mod_sys_fail(VALUE mod, const char *mesg)
 {
-    VALUE exc = make_errno_exc(mesg);
-    rb_extend_object(exc, mod);
+    int n = errno;
+    int need_aset_tmp = 0;
+    VALUE super_class;
+    VALUE cached_class;
+    VALUE per_class;
+    VALUE exc;
+    VALUE arg;
+
+    errno = 0;
+    if (n == 0) {
+	rb_bug("rb_mod_sys_fail(%s) - errno == 0", mesg ? mesg : "");
+    }
+    super_class = get_syserr(n);
+
+    if (! mod_sys_fail_cache) {
+	mod_sys_fail_cache = rb_hash_new();
+	rb_global_variable(&mod_sys_fail_cache);
+    }
+    per_class = rb_hash_aref(mod_sys_fail_cache, super_class);
+
+    if (NIL_P(per_class)) {
+	need_aset_tmp = 1;
+	per_class = rb_hash_new();
+    }
+
+    cached_class = rb_hash_aref(per_class, mod);
+    if (NIL_P(cached_class)) {
+	cached_class = rb_obj_dup(super_class);
+	rb_include_module(cached_class, mod);
+	rb_hash_aset(per_class, mod, cached_class);
+	if (need_aset_tmp)
+	    rb_hash_aset(mod_sys_fail_cache, super_class, per_class);
+    }
+
+    arg = mesg ? rb_str_new2(mesg) : Qnil;
+    exc = rb_class_new_instance(1, &arg, cached_class);
     rb_exc_raise(exc);
 }
 
-- 
Eric Wong