detecting a socket dropping in multi-threaded app

7 messages in this thread from ruby-talk in 2002-06

  1.   Firestone, Mark - Technical Support <mark.firestone@go...uk> 06-21 11:57
  2.   why@ry...com 06-21 17:48
  3.   Firestone, Mark - Technical Support <mark.firestone@go...uk> 06-21 20:22
  4.   Jonathan Gillette <jonathan@in...com> 06-21 20:56
  5.   Firestone, Mark - Technical Support <mark.firestone@go...uk> 06-22 11:05
  6.   Jonathan Gillette <jonathan@in...com> 06-24 16:35
  7.   Paul Brannan <pbrannan@at...com> 06-24 17:25

Firestone, Mark - Technical Support <mark.firestone@go...uk>

2002-06-21 11:57:57
Replies: why@ry...com
I'm still working on my Ruby BBS project.  I need to be able to run some
code when a user drops offline without logging off.  Right now, Ruby is just
killing the thread.  I need to know how to make it pass an exception (or
something) back to the main loop so the main loop can take the user out of
the online user database.  I've tried testing for nil and raising an
exception, but it doesn't seem to work.  Help please!! You guys never let me
down.

thanks in advance!

Mark


def getstr(echo,wrapon,width)			# Input  a line a character
at a time. 

  whole	= "" 
  fred		= "" 
  i		= 0
  wlen	= 0
  suppress	= false					#This is to prevent
telnet control codes from being treated as user input



    if @wrap != nil					#taking the length
of a nil is bad.  we must test for this or BOOM!
    whole = @wrap 
      i = whole.length
    end

   @wrap = ''
   @so...write whole

    @so...each_byte do  | fred| 

        case fred 
	  when TELNETCMD
	   suppress = true
	   
          when  PRINTABLE 
	   i = i + 1
	   if !suppress then
	    whole = whole + fred.chr 
            if echo then @so...write fred.chr else @so...write
NOECHOCHAR.chr end 
	   end
	   if wrapon
	   if (i >= width)  then
           if fred != 32
	   wlen = 0
	   @wrap = whole.scan(/\w+/).last 
	     wlen = @wr...length if @wrap != nil
	     wlen.times { @so...write BS.chr }
	     wlen.times { @so...write " " }
	     @so...write CR.chr+LF.chr 
	     endbit = whole.length - wlen
	     #puts "endbit: #{endbit}"
	     #puts "whole: #{whole}"
	     whole.slice!(endbit..whole.length) 
	    #puts "whole: #{whole}"
          break 
	    end
	    @so...write CR.chr+LF.chr 
	   break
	  end
	  end

          when CR 
           @so...write CR.chr+LF.chr 
          break 
	  
          when BS  
	   if i >= 1 
            whole.chop!					#the user pressed
backspace, so get rid 
	    i = i - 1
            @so...write fred.chr 
	  end
	 
          when DBS 
	   if i >= 1 
            whole.chop!					#for UNIX based
Telnet 
	     i = i - 1
            @so...write BS.chr
	  end 	#of if

        end		#of case
	
	if fred < 250 then suppress = false 
      end

       end #of iterator

  @so...flush 
  getstr = whole 
 end 

NOTICE:  This e-mail and any attachment(s) may contain confidential and
proprietary information of Goss International Corporation and/or its
subsidiaries and may be legally privileged. This e-mail is intended solely
for the addressee. If you are not the addressee, dissemination, copying or
other use of this e-mail or any of its content is strictly prohibited and
may be unlawful. If you are not the intended recipient please inform the
sender immediately and destroy the e-mail and any copies. All liability for
viruses is excluded to the fullest extent permitted by law. Any views
expressed in this message are those of the individual sender. No contract
may be construed by this e-mail.
Firestone, Mark - Technical Support (mark.firestone@go...uk) wrote:
> I'm still working on my Ruby BBS project.  I need to be able to run some
> code when a user drops offline without logging off.  Right now, Ruby is just
> killing the thread.  I need to know how to make it pass an exception (or
> something) back to the main loop so the main loop can take the user out of
> the online user database.  I've tried testing for nil and raising an
> exception, but it doesn't seem to work.  Help please!! You guys never let me
> down.
> 
> thanks in advance!
> 
> Mark
First, I don't see any begin..rescue around your TCPSocket#each_byte:

  begin
    @so...each_byte do |fred|
      # Process bytes sent     
    end
  rescue
    # Handle exception here..
  end

I find the surest way of finding a disconnect is to implement a simple PING request from the server to the client.  It looks like your byte commands could include a PING response from the client.  I just run a separate thread that periodically checks that the PING count is climbing..  Then, set Thread#abort_on_exception = false so you can make sure that only clients that don't return pings get killed.  If you go this route, then don't forget to use a mutex.

-- 

Jonathan Gillette
iNetZ Media

Firestone, Mark - Technical Support <mark.firestone@go...uk>

2002-06-21 20:22:22
Thanks for your reply.  I had tried doing what you described, and it never
generates an exception.  It just sits there.

If I set abort on exception = true, then the @so...write command before
the interator generates an exception I can trap, just before the whole
program abends.

I tried testing for nils after the @so...eachbyte, but that doesn't work
either.  I am not sure where to go from here.. ideas?  I don't understand
your ping idea, as the client is a telnet session.  Maybe I am being
thick...


Thanks,

Mark

> -----Original Message-----
> From:	why@ry...com [SMTP:why@ry...com]
> Sent:	21 June 2002 18:48
> To:	ruby-talk@ru...org
> Subject:	Re: detecting a socket dropping in multi-threaded app
> 
> Firestone, Mark - Technical Support (mark.firestone@go...uk)
> wrote:
> > I'm still working on my Ruby BBS project.  I need to be able to run some
> > code when a user drops offline without logging off.  Right now, Ruby is
> just
> > killing the thread.  I need to know how to make it pass an exception (or
> > something) back to the main loop so the main loop can take the user out
> of
> > the online user database.  I've tried testing for nil and raising an
> > exception, but it doesn't seem to work.  Help please!! You guys never
> let me
> > down.
> > 
> > thanks in advance!
> > 
> > Mark
> 
> First, I don't see any begin..rescue around your TCPSocket#each_byte:
> 
>   begin
>     @so...each_byte do |fred|
>       # Process bytes sent     
>     end
>   rescue
>     # Handle exception here..
>   end
> 
> I find the surest way of finding a disconnect is to implement a simple
> PING request from the server to the client.  It looks like your byte
> commands could include a PING response from the client.  I just run a
> separate thread that periodically checks that the PING count is climbing..
> Then, set Thread#abort_on_exception = false so you can make sure that only
> clients that don't return pings get killed.  If you go this route, then
> don't forget to use a mutex.
> 
> -- 
> 
> Jonathan Gillette
> iNetZ Media
NOTICE:  This e-mail and any attachment(s) may contain confidential and
proprietary information of Goss International Corporation and/or its
subsidiaries and may be legally privileged. This e-mail is intended solely
for the addressee. If you are not the addressee, dissemination, copying or
other use of this e-mail or any of its content is strictly prohibited and
may be unlawful. If you are not the intended recipient please inform the
sender immediately and destroy the e-mail and any copies. All liability for
viruses is excluded to the fullest extent permitted by law. Any views
expressed in this message are those of the individual sender. No contract
may be construed by this e-mail.

Jonathan Gillette <jonathan@in...com>

2002-06-21 20:56:26
Firestone, Mark - Technical Support (mark.firestone@go...uk) wrote:
> If I set abort on exception = true, then the @so...write command before
> the interator generates an exception I can trap, just before the whole
> program abends.
Yep.  You could wrap all socket read/writes in the begin..rescue block.

> I tried testing for nils after the @so...eachbyte, but that doesn't work
> either.  I am not sure where to go from here.. ideas?  I don't understand
> your ping idea, as the client is a telnet session.  Maybe I am being
> thick...
Yeah, I forgot your client would be a simple telnet session.  In the same vein as a ping, you could have a thread watching for inactive threads and shutting them down.  So you could keep a timestamp on the last time a thread read a byte and shutdown the thread if too much time elapsed.  In my Ruby socket servers, I tend to stay away from trying to catch all the possible exceptions and just keep a single thread that monitors the others.  It's just less of a headache.  I guess I hate having to worry about missing an exception.

-- 

Jonathan Gillette
iNetZ Media

Firestone, Mark - Technical Support <mark.firestone@go...uk>

2002-06-22 11:05:18
How about this?  My @who data structure is shared with a mutex between the
threads.  It keeps a list of the thread id with each person's username and
whatever who is logged on.  When someone hangs up, Ruby just kills the
thread (I verified this with an experiment last night...)

I write a thread that wakes up every so often and checks Thread.current, and
compares that list with the list in @who, and if someone is missing from
Thread.current, zaps them from the list.

Is this a reasonable idea?

Thanks,

Mark

> -----Original Message-----
> From:	Jonathan Gillette [SMTP:jonathan@in...com]
> Sent:	21 June 2002 21:56
> To:	ruby-talk@ru...org
> Subject:	Re: detecting a socket dropping in multi-threaded app
> 
> 
> Yeah, I forgot your client would be a simple telnet session.  In the same
> vein as a ping, you could have a thread watching for inactive threads and
> shutting them down.  So you could keep a timestamp on the last time a
> thread read a byte and shutdown the thread if too much time elapsed.  In
> my Ruby socket servers, I tend to stay away from trying to catch all the
> possible exceptions and just keep a single thread that monitors the
> others.  It's just less of a headache.  I guess I hate having to worry
> about missing an exception.
> 
> -- 
> 
> Jonathan Gillette
> iNetZ Media
NOTICE:  This e-mail and any attachment(s) may contain confidential and
proprietary information of Goss International Corporation and/or its
subsidiaries and may be legally privileged. This e-mail is intended solely
for the addressee. If you are not the addressee, dissemination, copying or
other use of this e-mail or any of its content is strictly prohibited and
may be unlawful. If you are not the intended recipient please inform the
sender immediately and destroy the e-mail and any copies. All liability for
viruses is excluded to the fullest extent permitted by law. Any views
expressed in this message are those of the individual sender. No contract
may be construed by this e-mail.

Jonathan Gillette <jonathan@in...com>

2002-06-24 16:35:28
Yep.  That's a reasonable idea.  As long as you're sure the user's Thread dies, this should work.

Firestone, Mark - Technical Support (mark.firestone@go...uk) wrote:
> How about this?  My @who data structure is shared with a mutex between the
> threads.  It keeps a list of the thread id with each person's username and
> whatever who is logged on.  When someone hangs up, Ruby just kills the
> thread (I verified this with an experiment last night...)
> 
> I write a thread that wakes up every so often and checks Thread.current, and
> compares that list with the list in @who, and if someone is missing from
> Thread.current, zaps them from the list.
> 
> Is this a reasonable idea?
> 
> Thanks,
> 
> Mark
-- 

Jonathan Gillette
iNetZ Media

Paul Brannan <pbrannan@at...com>

2002-06-24 17:25:11
> Firestone, Mark - Technical Support (mark.firestone@go...uk) wrote:
> > How about this?  My @who data structure is shared with a mutex between the
> > threads.  It keeps a list of the thread id with each person's username and
> > whatever who is logged on.  When someone hangs up, Ruby just kills the
> > thread (I verified this with an experiment last night...)
> > 
> > I write a thread that wakes up every so often and checks Thread.current, and
> > compares that list with the list in @who, and if someone is missing from
> > Thread.current, zaps them from the list.
> > 
> > Is this a reasonable idea?
Why not wrap your thread with an ensure block that removes the person
from the list when it is executed?

  t = Thread.new do
    # add the person to the list
    begin
      loop do
        # Your logic here; when the socket (or other IO object) goes
        # away, break out of the loop.
      end
    ensure
      # remove the person from the list
    end
  end

This reduces possibility for reading from a socket that isn't there, or
for doing something for a user who isn't logged in.

(I never received the original message, btw, so I'm not 100% sure I
understand exactly what the problem is).

Paul

7 messages in this thread from ruby-talk in 2002-06