Bug #293

context switch may occur during freeing io

Added by Yusuke Endoh almost 7 years ago. Updated about 4 years ago.

[ruby-dev:35578]
Status:Closed
Priority:Low
Assignee:-
ruby -v: Backport:

Description

=begin
遠藤です。

以下のように do_select の blocking region に native_thread_yield を
入れた上で、

Index: thread.c
===================================================================
--- thread.c (revision 18124)
+++ thread.c (working copy)
@@ -2004,6 +2004,7 @@
}
#else
BLOCKING_REGION({
+ native_thread_yield();
result = select(n, read, write, except, timeout);
if (result < 0) lerrno = errno;
}, ubf_select, GET_THREAD());

以下を実行すると、たまに落ちます。

$ ./ruby
t = Thread.new do
loop do
w = IO.pipe.last
w.sync = false
w.write("a" * 1000)
end
end
sleep 0.1
Thread.new { }
GC.start

-:3: [BUG] object allocation during garbage collection phase
ruby 1.9.0 (2008-07-18 revision 18124) [i686-linux]

-- control frame ----------
c:0008 p:---- s:0015 b:0015 l:000014 d:000014 CFUNC :(null)
c:0007 p:---- s:0013 b:0013 l:000012 d:000012 CFUNC :pipe
c:0006 p:0013 s:0010 b:0010 l:000f88 d:000009 BLOCK -:3
c:0005 p:---- s:0009 b:0009 l:000008 d:000008 FINISH
c:0004 p:---- s:0007 b:0007 l:000006 d:000006 CFUNC :loop
c:0003 p:0007 s:0004 b:0004 l:000f88 d:000003 BLOCK -:2
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH
c:0001 p:---- s:0002 b:0002 l:000001 d:000001 TOP


DBG> : "-:3:in (null)'"
DBG> : "-:3:in
pipe'"
DBG> : "-:3:in block (2 levels) in <main>'"
DBG> : "-:2:in
loop'"
DBG> : "-:2:in `block in '"
-- backtrace of native function call (Use addr2line) --
0x8102065
0x812948e
0x81294eb
0x8061b3a
0x806953c
0x80fdc90
0x80fe0f2
0x807e180
0x807e982
0x8066852
0x8059e20
0x806a768
0x80f2efe
0x80f4f4c
0x80f7eb9
0x80fbdcb
0x80fc414
0x80fc962
0x8059fec
0x80f4063
0x80f4f4c
0x80f7eb9
0x80fbdcb
0x80fc414
0x80fc784
0x8106b9c
0x8106be1
0xb7f6a240
0xb7e9e49e


アボートしました

原因は、T_FILE が GC されるとき

fptr_finalize
-> io_fflush
-> rb_thread_fd_writable
-> rb_thread_wait_fd_rw
-> do_select
-> BLOCKING_REGION

と呼び出しが進んで、GC 中に他のスレッドが動き出してしまうためです。

この問題がなかったとしても、fptr_finalize で io_fflush するのは
ブロックする可能性があってまずい気がします。どうしたもんでしょう。

--
Yusuke ENDOH mame@tsg.ne.jp
=end

History

#1 Updated by Nobuyoshi Nakada almost 7 years ago

=begin
なかだです。

At Fri, 18 Jul 2008 19:47:28 +0900,
Yusuke ENDOH wrote in :

原因は、T_FILE が GC されるとき

fptr_finalize
-> io_fflush
-> rb_thread_fd_writable
-> rb_thread_wait_fd_rw
-> do_select
-> BLOCKING_REGION

と呼び出しが進んで、GC 中に他のスレッドが動き出してしまうためです。

T_DATAのdfreeとfptr_finalizeはdeferredに回しましょうか。

この問題がなかったとしても、fptr_finalize で io_fflush するのは
ブロックする可能性があってまずい気がします。どうしたもんでしょう。

こっちはどうしましょうかねぇ。

Index: gc.c
===================================================================
--- gc.c (revision 18132)
+++ gc.c (working copy)
@@ -1324,5 +1324,5 @@ gc_mark_children(rb_objspace_t *objspace
}

-static void obj_free(rb_objspace_t *, VALUE);
+static int obj_free(rb_objspace_t *, VALUE);

static void
@@ -1402,9 +1402,7 @@ gc_sweep(rb_objspace_t objspace)
while (p < pend) {
if (!(p->as.basic.flags & FL_MARK)) {
- if (p->as.basic.flags) {
- obj_free(objspace, (VALUE)p);
- }
- if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
- p->as.free.flags = FL_MARK; /
remain marked /
+ if (p->as.basic.flags && obj_free(objspace, (VALUE)p) ||
+ need_call_final && FL_TEST(p, FL_FINALIZE)) {
+ p->as.free.flags |= FL_MARK; /
remain marked */
p->as.free.next = final_list;
final_list = p;
@@ -1470,5 +1468,5 @@ rb_gc_force_recycle(VALUE p)
}

-static void
+static int
obj_free(rb_objspace_t objspace, VALUE obj)
{
@@ -1527,5 +1525,5 @@ obj_free(rb_objspace_t *objspace, VALUE
}
else if (RANY(obj)->as.data.dfree) {
- (*RANY(obj)->as.data.dfree)(DATA_PTR(obj));
+ return 1;
}
}
@@ -1542,5 +1540,5 @@ obj_free(rb_objspace_t *objspace, VALUE
case T_FILE:
if (RANY(obj)->as.file.fptr) {
- rb_io_fptr_finalize(RANY(obj)->as.file.fptr);
+ return 1;
}
break;
@@ -1571,5 +1569,5 @@ obj_free(rb_objspace_t *objspace, VALUE
break;
}
- return; /
no need to free iv_tbl /
+ break; /
no need to free iv_tbl */

    case T_STRUCT:

@@ -1584,4 +1582,6 @@ obj_free(rb_objspace_t objspace, VALUE
RANY(obj)->as.basic.flags & T_MASK, (void
)obj);
}
+ RANY(obj)->as.basic.flags &= ~T_MASK;
+ return 0;
}

@@ -2010,4 +2010,12 @@ run_final(rb_objspace_t objspace, VALUE
objid = rb_obj_id(obj); /
make obj into id */
rb_thread_critical = Qtrue;
+ switch (RANY(obj)->as.basic.flags & T_MASK) {
+ case T_DATA:
+ (*RANY(obj)->as.data.dfree)(DATA_PTR(obj));
+ break;
+ case T_FILE:
+ rb_io_fptr_finalize(RANY(obj)->as.file.fptr);
+ break;
+ }
args[1] = 0;
args[2] = (VALUE)rb_safe_level();

--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦

=end

#2 Updated by Nobuyoshi Nakada almost 7 years ago

=begin
なかだです。

At Sun, 20 Jul 2008 08:27:35 +0900,
Nobuyoshi Nakada wrote in :

と呼び出しが進んで、GC 中に他のスレッドが動き出してしまうためです。

T_DATAのdfreeとfptr_finalizeはdeferredに回しましょうか。

ObjectSpaceから見えてしまうとまずいので、訂正です。

Index: gc.c
===================================================================
--- gc.c (revision 18139)
+++ gc.c (working copy)
@@ -1317,4 +1317,7 @@ gc_mark_children(rb_objspace_t *objspace
break;

  • case T_UNDEF:
  • break; + default: rb_bug("rb_gc_mark(): unknown data type 0x%lx(%p) %s", @@ -1324,5 +1327,5 @@ gc_mark_children(rb_objspace_t *objspace }

-static void obj_free(rb_objspace_t *, VALUE);
+static int obj_free(rb_objspace_t *, VALUE);

static void
@@ -1402,9 +1405,7 @@ gc_sweep(rb_objspace_t objspace)
while (p < pend) {
if (!(p->as.basic.flags & FL_MARK)) {
- if (p->as.basic.flags) {
- obj_free(objspace, (VALUE)p);
- }
- if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
- p->as.free.flags = FL_MARK; /
remain marked /
+ if (p->as.basic.flags && obj_free(objspace, (VALUE)p) ||
+ need_call_final && FL_TEST(p, FL_FINALIZE)) {
+ p->as.free.flags |= FL_MARK; /
remain marked */
p->as.free.next = final_list;
final_list = p;
@@ -1470,5 +1471,5 @@ rb_gc_force_recycle(VALUE p)
}

-static void
+static int
obj_free(rb_objspace_t objspace, VALUE obj)
{
@@ -1527,5 +1528,7 @@ obj_free(rb_objspace_t *objspace, VALUE
}
else if (RANY(obj)->as.data.dfree) {
- (*RANY(obj)->as.data.dfree)(DATA_PTR(obj));
+ RANY(obj)->as.basic.flags &= ~T_MASK;
+ RANY(obj)->as.basic.flags |= T_UNDEF;
+ return 1;
}
}
@@ -1542,5 +1545,10 @@ obj_free(rb_objspace_t *objspace, VALUE
case T_FILE:
if (RANY(obj)->as.file.fptr) {
- rb_io_fptr_finalize(RANY(obj)->as.file.fptr);
+ rb_io_t *fptr = RANY(obj)->as.file.fptr;
+ RANY(obj)->as.basic.flags &= ~T_MASK;
+ RANY(obj)->as.basic.flags |= T_UNDEF;
+ RDATA(obj)->dfree = (void (
)(void*))rb_io_fptr_finalize;
+ RDATA(obj)->data = fptr;
+ return 1;
}
break;
@@ -1571,5 +1579,5 @@ obj_free(rb_objspace_t objspace, VALUE
break;
}
- return; /
no need to free iv_tbl /
+ break; /
no need to free iv_tbl */

    case T_STRUCT:

@@ -1580,8 +1588,13 @@ obj_free(rb_objspace_t *objspace, VALUE
break;

  • case T_UNDEF:
  • break; + default: rb_bug("gc_sweep(): unknown data type 0x%lx(%p)", RANY(obj)->as.basic.flags & T_MASK, (void*)obj); }
  • RANY(obj)->as.basic.flags &= ~T_MASK;
  • return 0; }

@@ -1855,4 +1868,5 @@ os_obj_of(rb_objspace_t objspace, VALUE
case T_ICLASS:
case T_NODE:
+ case T_UNDEF:
continue;
case T_CLASS:
@@ -2010,4 +2024,7 @@ run_final(rb_objspace_t *objspace, VALUE
objid = rb_obj_id(obj); /
make obj into id */
rb_thread_critical = Qtrue;
+ if ((RANY(obj)->as.basic.flags & T_MASK) == T_UNDEF) {
+ (*RANY(obj)->as.data.dfree)(DATA_PTR(obj));
+ }
args[1] = 0;
args[2] = (VALUE)rb_safe_level();

--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦

=end

#3 Updated by Nobuyoshi Nakada almost 7 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

=begin
Applied in changeset r18221.
=end

Also available in: Atom PDF