--=-tBrw8XDRiCCR68BAXhtB
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

On Sat, 2007-05-12 at 22:39 +0900, Tim Becker wrote:
> Any thought? Am I missing an easier/build-in method to block a thread
> only for a limited about of time?

Unless/until the built-in classes support timeouts, using a thread to
track the timeout is sadly your best option.

However, there are a number of problems with the specific approach
you've used here (e.g. not all accesses to @t are protected by the
mutex)... here's an alternate implementation...

        require 'thread'
        begin
          require 'fastthread'
        rescue LoadError
        end
       =20
        class MessageQueue
          def initialize
            @lock =3D Mutex.new
            @messages =3D []
            @readers =3D []
          end
       =20
          def enqueue msg
            @lock.synchronize do
              unless @readers.empty?
                @readers.pop << msg
              else
                @messages.push msg
              end
            end
          end
       =20
          def dequeue timeout=3Dnil
            timeout_thread =3D nil
            begin
              reader =3D nil
              @lock.synchronize do
             unless @messages.empty?
               # fast path
               return @messages.shift
             else
                  reader =3D Queue.new
                  @readers.push reader
                  if timeout
                    timeout_thread =3D Thread.new do
                      sleep timeout
                      @lock.synchronize do
                        @readers.delete reader
                        reader << nil
                      end
                    end
                  end
                end
              end
              # either timeout or writer will send to us
              reader.shift
            ensure
              # (try to) clean up timeout thread
              timeout_thread.run if timeout_thread
            end
          end
        end

Queue may seem sort of heavyweight to use for "callbacks" this way, but
if you use fastthread they are pretty inexpensive, and queues take care
of a _lot_ of the bookkeeping you would need to do to make this safe
otherwise.

One remaining issue is that it's possible for the timeout thread to
sleep _after_ the call to timeout_thread.run, so the cleanup isn't
necessarily that effective.  However, Thread#kill (or Thread#raise)
isn't an option, since those can leave things in an inconsistent state.

A better solution would be to maintain a dedicated thread for tracking
timeouts which you don't have to worry about cleaning up -- I'll be
releasing a library to do that soon, but for now the above solution is
the best I can do.

-mental

--=-tBrw8XDRiCCR68BAXhtB
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (GNU/Linux)

iD8DBQBGRh//SuZBmZzm14ERApEmAJwIBTJcNfwWTfpY+1F+AOl8jArKugCdF0uO
ywA9OrNz9y8nnJRgjAkxclQ=
=fxeX
-----END PGP SIGNATURE-----

--=-tBrw8XDRiCCR68BAXhtB--