Project

General

Profile

Actions

Bug #293

closed

context switch may occur during freeing io

Added by mame (Yusuke Endoh) over 16 years ago. Updated over 13 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
Backport:
[ruby-dev:35578]

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
=end

Actions #1

Updated by nobu (Nobuyoshi Nakada) over 16 years ago

=begin
なかだです。

At Fri, 18 Jul 2008 19:47:28 +0900,
Yusuke ENDOH wrote in [ruby-dev:35578]:

原因は、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

Actions #2

Updated by nobu (Nobuyoshi Nakada) over 16 years ago

=begin
なかだです。

At Sun, 20 Jul 2008 08:27:35 +0900,
Nobuyoshi Nakada wrote in [ruby-dev:35594]:

と呼び出しが進んで、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

Actions #3

Updated by nobu (Nobuyoshi Nakada) over 16 years ago

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

=begin
Applied in changeset r18221.
=end

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0