From 041916e302a4a9f124cb541bf3c35c141d31a7b7 Mon Sep 17 00:00:00 2001
From: Wolf <wolf@wolfsden.cz>
Date: Tue, 26 Sep 2017 01:59:55 +0200
Subject: [PATCH] Release gvl while doing (f)stat

At the moment rb_stat function is blocking. This patch changes the
behaviour to release the gvl while waiting for OS to return from
f(stat).

There is behaviour impact, but not significant (times are for 100000
iterations):

   $ ~/releaseruby_patch/bin/ruby bench.rb
Rehearsal ------------------------------------------------
File.exist?:   0.036412   0.056616   0.093028 (  0.093075)
--------------------------------------- total: 0.093028sec

                   user     system      total        real
File.exist?:   0.042953   0.049783   0.092736 (  0.092804)

   $ ~/releaseruby_no_patch/bin/ruby bench.rb
Rehearsal ------------------------------------------------
File.exist?:   0.056094   0.026293   0.082387 (  0.082389)
--------------------------------------- total: 0.082387sec

                   user     system      total        real
File.exist?:   0.037250   0.046702   0.083952 (  0.083956)
---
 file.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 54 insertions(+), 2 deletions(-)

diff --git a/file.c b/file.c
index f42d4f408c..c8a6f39336 100644
--- a/file.c
+++ b/file.c
@@ -27,6 +27,7 @@
 #include "internal.h"
 #include "ruby/io.h"
 #include "ruby/util.h"
+#include "ruby/thread.h"
 #include "dln.h"
 #include "encindex.h"
 
@@ -1022,21 +1023,70 @@ rb_stat_inspect(VALUE self)
     return str;
 }
 
+typedef struct no_gvl_fstat_data {
+    int fd;
+    struct stat * st;
+    int result;
+} no_gvl_fstat_data;
+
+static void * no_gvl_fstat(void * data)
+{
+    ((no_gvl_fstat_data *) data)->result = fstat(
+	((no_gvl_fstat_data *) data)->fd,
+	((no_gvl_fstat_data *) data)->st
+    );
+    return NULL;
+}
+
+typedef struct no_gvl_stat_data {
+    const char * path;
+    struct stat * st;
+    int result;
+} no_gvl_stat_data;
+
+static void * no_gvl_stat(void * data)
+{
+    ((no_gvl_stat_data *) data)->result = STAT(
+	((no_gvl_stat_data *) data)->path,
+	((no_gvl_stat_data *) data)->st
+    );
+    return NULL;
+}
+
+
 static int
 rb_stat(VALUE file, struct stat *st)
 {
     VALUE tmp;
+    no_gvl_fstat_data fstat_data;
+    no_gvl_stat_data stat_data;
 
     tmp = rb_check_convert_type_with_id(file, T_FILE, "IO", idTo_io);
     if (!NIL_P(tmp)) {
 	rb_io_t *fptr;
 
 	GetOpenFile(tmp, fptr);
-	return fstat(fptr->fd, st);
+	fstat_data.fd = fptr->fd;
+	fstat_data.st = st;
+	rb_thread_call_without_gvl(
+	    no_gvl_fstat,
+	    (void *) &fstat_data,
+	    RUBY_UBF_IO,
+	    NULL
+	);
+	return fstat_data.result;
     }
     FilePathValue(file);
     file = rb_str_encode_ospath(file);
-    return STAT(StringValueCStr(file), st);
+    stat_data.path = StringValueCStr(file);
+    stat_data.st = st;
+    rb_thread_call_without_gvl(
+	no_gvl_stat,
+	(void *) &stat_data,
+	RUBY_UBF_IO,
+	NULL
+    );
+    return stat_data.result;
 }
 
 #ifdef _WIN32
@@ -6263,3 +6313,5 @@ Init_File(void)
     rb_define_method(rb_cStat, "setgid?",  rb_stat_sgid, 0);
     rb_define_method(rb_cStat, "sticky?",  rb_stat_sticky, 0);
 }
+
+/* vim: set ts=8 sw=4 noexpandtab: */
-- 
2.14.1

