Ruby Tk and Threads error

Hi,

I'm coding an application with Ruby and RubyTk that draws a huge
amount of lines on a TkcCanvas. I split the drawing code in a new
thread so the main interface remains responsive to the user to cancel
the operation at any time.

The problem is when I click the "stop drawing" button which is bound
to a "proc { @drawing_thread.exit }" I *often* (not always) get the
following error:

/usr/local/rubytk/lib/ruby/1.8/tk.rb:1537:in `mainloop': killed thread
(ThreadError)
        from /usr/local/rubytk/lib/ruby/1.8/tk.rb:1537:in `mainloop'
        from plfg.rb:250:in `initialize'
        from plfg.rb:841

I'm on Ubuntu Edgy 6.10 with ruby-1.8.4 compiled from source with
--enable-pthread

I believe it has something to do with some system library or the
compilation flags, because in windows using the one click installer
and the activetcl 8.4 the application never seems to crash like that.

Thanks in advance for your help,

jorge santiago.

Message-ID: <bb2bb0e80611300813u2234a087w5f387631aefa73bf@mail.gmail.com>

The problem is when I click the "stop drawing" button which is bound
to a "proc { @drawing_thread.exit }" I *often* (not always) get the
following error:

/usr/local/rubytk/lib/ruby/1.8/tk.rb:1537:in `mainloop': killed thread
(ThreadError)

Probably, that is a bug which depends on timing of callback operation.
Please try the following patch.

Index: ext/tk/tcltklib.c

···

From: "jorge santiago" <jorge.santiago@gmail.com>
Subject: Ruby Tk and Threads error
Date: Fri, 1 Dec 2006 02:08:56 +0900

RCS file: /var/cvs/src/ruby/ext/tk/tcltklib.c,v
retrieving revision 1.40.2.13
diff -u -r1.40.2.13 tcltklib.c
--- ext/tk/tcltklib.c 10 Jul 2006 09:51:30 -0000 1.40.2.13
+++ ext/tk/tcltklib.c 1 Dec 2006 07:20:02 -0000
@@ -4,7 +4,7 @@
  * Oct. 24, 1997 Y. Matsumoto
  */

-#define TCLTKLIB_RELEASE_DATE "2006-07-10"
+#define TCLTKLIB_RELEASE_DATE "2006-12-01"

#include "ruby.h"
#include "rubysig.h"
@@ -5526,10 +5526,15 @@
     *(q->done) = -1;

     /* back to caller */
- DUMP2("back to caller (caller thread:%lx)", q->thread);
- DUMP2(" (current thread:%lx)", rb_thread_current());
- rb_thread_run(q->thread);
- DUMP1("finish back to caller");
+ if (RTEST(rb_funcall(q->thread, ID_alive_p, 0, 0))) {
+ DUMP2("back to caller (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ rb_thread_run(q->thread);
+ DUMP1("finish back to caller");
+ } else {
+ DUMP2("caller is dead (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ }

     /* end of handler : remove it */
     return 1;
@@ -5844,10 +5849,15 @@
     *(q->done) = -1;

     /* back to caller */
- DUMP2("back to caller (caller thread:%lx)", q->thread);
- DUMP2(" (current thread:%lx)", rb_thread_current());
- rb_thread_run(q->thread);
- DUMP1("finish back to caller");
+ if (RTEST(rb_funcall(q->thread, ID_alive_p, 0, 0))) {
+ DUMP2("back to caller (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ rb_thread_run(q->thread);
+ DUMP1("finish back to caller");
+ } else {
+ DUMP2("caller is dead (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ }

     /* end of handler : remove it */
     return 1;
@@ -6921,10 +6931,15 @@
     *(q->done) = -1;

     /* back to caller */
- DUMP2("back to caller (caller thread:%lx)", q->thread);
- DUMP2(" (current thread:%lx)", rb_thread_current());
- rb_thread_run(q->thread);
- DUMP1("finish back to caller");
+ if (RTEST(rb_funcall(q->thread, ID_alive_p, 0, 0))) {
+ DUMP2("back to caller (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ rb_thread_run(q->thread);
+ DUMP1("finish back to caller");
+ } else {
+ DUMP2("caller is dead (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ }

     /* end of handler : remove it */
     return 1;

--
Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)

Jorge,

I'm also on Ubuntu Edgy and I earlier compiled Ruby with the pthreads
option, and it gave me a warning that unless Tcl/Tk is also compiled
with pthreads, you will have problems like segfaults. I then rebuilt
without pthreads and I have no issues with it. The Tcl/Tk that is
standard for the Ubuntu/Debian distro is apparently (from what I saw)
NOT compiled with pthreads, so try compiling Ruby without pthreads and
see if that helps. I believe you don't need pthreads unless Tcl/Tk is
compiled with it.

···

--
Posted via http://www.ruby-forum.com/.

Hi Hidetoshi,

Your patch worked GREAT! My app has never crashed since I applied the patch.

Thanks a lot!

jorge santiago.

···

On 12/1/06, Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp> wrote:

From: "jorge santiago" <jorge.santiago@gmail.com>
Subject: Ruby Tk and Threads error
Date: Fri, 1 Dec 2006 02:08:56 +0900
Message-ID: <bb2bb0e80611300813u2234a087w5f387631aefa73bf@mail.gmail.com>
> The problem is when I click the "stop drawing" button which is bound
> to a "proc { @drawing_thread.exit }" I *often* (not always) get the
> following error:
>
> /usr/local/rubytk/lib/ruby/1.8/tk.rb:1537:in `mainloop': killed thread
> (ThreadError)

Probably, that is a bug which depends on timing of callback operation.
Please try the following patch.

Index: ext/tk/tcltklib.c

RCS file: /var/cvs/src/ruby/ext/tk/tcltklib.c,v
retrieving revision 1.40.2.13
diff -u -r1.40.2.13 tcltklib.c
--- ext/tk/tcltklib.c 10 Jul 2006 09:51:30 -0000 1.40.2.13
+++ ext/tk/tcltklib.c 1 Dec 2006 07:20:02 -0000
@@ -4,7 +4,7 @@
  * Oct. 24, 1997 Y. Matsumoto
  */

-#define TCLTKLIB_RELEASE_DATE "2006-07-10"
+#define TCLTKLIB_RELEASE_DATE "2006-12-01"

#include "ruby.h"
#include "rubysig.h"
@@ -5526,10 +5526,15 @@
     *(q->done) = -1;

     /* back to caller */
- DUMP2("back to caller (caller thread:%lx)", q->thread);
- DUMP2(" (current thread:%lx)", rb_thread_current());
- rb_thread_run(q->thread);
- DUMP1("finish back to caller");
+ if (RTEST(rb_funcall(q->thread, ID_alive_p, 0, 0))) {
+ DUMP2("back to caller (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ rb_thread_run(q->thread);
+ DUMP1("finish back to caller");
+ } else {
+ DUMP2("caller is dead (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ }

     /* end of handler : remove it */
     return 1;
@@ -5844,10 +5849,15 @@
     *(q->done) = -1;

     /* back to caller */
- DUMP2("back to caller (caller thread:%lx)", q->thread);
- DUMP2(" (current thread:%lx)", rb_thread_current());
- rb_thread_run(q->thread);
- DUMP1("finish back to caller");
+ if (RTEST(rb_funcall(q->thread, ID_alive_p, 0, 0))) {
+ DUMP2("back to caller (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ rb_thread_run(q->thread);
+ DUMP1("finish back to caller");
+ } else {
+ DUMP2("caller is dead (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ }

     /* end of handler : remove it */
     return 1;
@@ -6921,10 +6931,15 @@
     *(q->done) = -1;

     /* back to caller */
- DUMP2("back to caller (caller thread:%lx)", q->thread);
- DUMP2(" (current thread:%lx)", rb_thread_current());
- rb_thread_run(q->thread);
- DUMP1("finish back to caller");
+ if (RTEST(rb_funcall(q->thread, ID_alive_p, 0, 0))) {
+ DUMP2("back to caller (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ rb_thread_run(q->thread);
+ DUMP1("finish back to caller");
+ } else {
+ DUMP2("caller is dead (caller thread:%lx)", q->thread);
+ DUMP2(" (current thread:%lx)", rb_thread_current());
+ }

     /* end of handler : remove it */
     return 1;

--
Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)

Message-ID: <bb2bb0e80612041140x363ddf20l1333f9841a93adb2@mail.gmail.com>

Your patch worked GREAT! My app has never crashed since I applied the patch.
Thanks a lot!

I have to say thank you.
Bug reports and requests from users improve Ruby/Tk. :slight_smile:
Thank you for your report.

···

From: "jorge santiago" <jorge.santiago@gmail.com>
Subject: Re: Ruby Tk and Threads error
Date: Tue, 5 Dec 2006 04:40:39 +0900
--
Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)