Lisp HUG Maillist Archive

(Lisp-HUG) Hanging Sockets...

Greetings to the List from the Rocky Mountains -

Once a day, in our soft real-time embedded application, I am 
connecting to a remote time server and asking it what day and time 
etc it thinks it is.

Everything is fine, except that occasionally things hang for about a 
minute - in fact, *everything* hangs - all the threads, CAPI, et. al. 
hang.  I have narrowed the hang down to the code that reads data from 
the time server.

A separate thread - one of many (10?) - makes the actual connection, 
reads and deciphers the data, etc.

I open the stream to the remote server, somewhere on the other side 
of this planet, and read the anticipated 51-bytes of date/time 
information with:
----------------------------------------------------------------------------------------------------------------------------------------
           (with-open-stream 
(time-server                                ; Attempt Communication...
                              (comm:open-tcp-stream 
; ...with Remote Server...
                               url 
13                                               ; Port 13 on Remote Server...
                               :direction 
:input                                 ; Read Only...
                               :element-type 
'base-char                    ; Gonna Read ASCII Characters...
                               :errorp 
nil                                          ; Don't Signal an Error 
on Error...
                               :read-timeout 
3                                  ; Three-Seconds until Timeout...
                               :write-timeout 
3                                  ; You Never Know...
                               :timeout 
3))                                        ; Three-Seconds - 
Connection Timeout...

             (if (null 
time-server)                                              ; Did 
Server Connection Fail?
                 (throw 'server-error 
nil)                                      ; Yes - Exit...

               (let ((received-string (make-string 
51))                  ; Receive Buffer...
                     (position 
-1)                                                 ; Current 
Position in Buffer...
                     (eor)) 
; Set True on Valid End of Record...

                 (while (and (< position 
51)                                ; Loop and Gather Received Characters...
                             (not 
eor))                                            ; Keep on until 
Overflow or EOR...

                   (if (listen 
time-server)                                      ; Anything There Yet?

                       (let ((character (read-char time-server nil :eor)))

                         (cond ((eql character 
:eor)                         ; End of Transmission?
                                (if (not (eql position 
50))                     ; Yes - Correct String Length?
                                    (throw 'server-error 
nil)                    ; No - Weird Received String...
                                  (setq eor 
t)))                                    ; Yes - Set Loop Sentinal...
                                                                                        ; 
Got another Good Character...
                               (t (setf (elt received-string (incf position))
                                    character)))) 
; ...Insert into Received String...

                     (mp:process-allow-scheduling))) 
; No Joy. Nothing There Yet: Timeslice...
--------------------------------------------------------------------------------------------------------------------------------------------------

I have excised declarations etc.

Thus, after opening the stream to the time server, I repeatedly 
(listen stream) and if a character is there, I read
and buffer it - otherwise, I give up the CPU and let other threads 
have a go at whatever they think they are doing.

Obviously, I repeatedly do the (listen) so as not to hang  whilst 
waiting for the network - but, I,and all the threads, *do* hang (occasionally).

Clearly, network latency will force numerous delays - but things on 
my side should not hang(?!) as I check for Is There A Character Ready?

"Common Lisp: The Language" mumbles, "The predicate listen is true if 
there is a character immediately available from input-stream and is 
false if not."

I was using the more obvious to use Read-Char-No-Hang rather than 
Listen/Read-Char, but it seemed to hang even more (probably wrong).

Question: Does anyone know if Listen is "guaranteed" to not hang 
buried in the bowels of the TCP/IP stack if an incoming character 
does not exist in the socket buffer???

I am running LispWorks 5.1 under Windows XP.

Thoughts?

Regards,

Jack Harper
Secure Outcomes Inc.
Evergreen, Colorado USA







Re: (Lisp-HUG) Hanging Sockets...

Unable to parse email body. Email id is 10475

Re: (Lisp-HUG) Hanging Sockets...

Hello,

I'm not sure if your example is meant to be a 
demonstration/generalization of an issue you're experiencing elsewhere. 
  If not, I noticed a few things:

1.  At only 50 bytes, the client running the example should have the 
entire daytime response buffered (by the OS) after receiving the first 
(and only) payload packet from the server, unless the connection is 
severed immediately after establishment, or the server does something 
like send one byte per packet, or the TCP receive window is set to an 
unusually-low value, or the server just doesn't respond, etc.

2.  If you're using a separate worker thread, why not use blocking reads 
with a timeout?

3.  In your specific example as-written, character should never be :eor 
(if the listen call returns non-nil, read-char shouldn't return eof), so 
the first cond branch is never taken, eor is never set, and the loop 
continues forever if less than the expected number of bytes are returned 
by the server.


Mike


Re: (Lisp-HUG) Hanging Sockets...

At 02:49 PM 8/18/2010, Paul Tarvydas wrote:
>After a brief look, I don't see anything particularly wrong with your code.
>
>If no one else has better suggestions, spin up a cpu meter 
>(ctrl-alt-del task manager) and an LW process browser and watch what 
>happens when you "hang".  Is anything hogging the cpu or is 
>everything idle?  What state are the LW processes in?  Does task 
>manager's performance viewer show LW high in the cup% list or 
>not?  It might give you some more clues.  If you're really adept, 
>you might even be able to "remote debug" / "break" your process from 
>the process browser while it is  hung, letting the debugger show you 
>what line of code it's stuck at.


Thank You Paul -

I tried that and the Task Manager thingus reported no CPU activity at 
all - zero - everybody is waiting...

Regards,

Jack Harper
Secure Outcomes Inc.
Evergreen, Colorado USA




Re: (Lisp-HUG) Hanging Sockets...

At 03:30 PM 8/18/2010, Mike Watters wrote:
>Hello,
>
>I'm not sure if your example is meant to be a 
>demonstration/generalization of an issue you're experiencing 
>elsewhere.  If not, I noticed a few things:
>
>1.  At only 50 bytes, the client running the example should have the 
>entire daytime response buffered (by the OS) after receiving the 
>first (and only) payload packet from the server, unless the 
>connection is severed immediately after establishment, or the server 
>does something like send one byte per packet, or the TCP receive 
>window is set to an unusually-low value, or the server just doesn't 
>respond, etc.
>
>2.  If you're using a separate worker thread, why not use blocking 
>reads with a timeout?
>
>3.  In your specific example as-written, character should never be 
>:eor (if the listen call returns non-nil, read-char shouldn't return 
>eof), so the first cond branch is never taken, eor is never set, and 
>the loop continues forever if less than the expected number of bytes 
>are returned by the server.


Thank You Mike for your observations -

(1) Yes, I agree that the entire 51-byte message from the time server 
should arrive all at once, but my understanding is that there are no 
firm guarantees on that. It should and *always* does, but then, maybe 
not in some pathological case.

(2) I am running under LispWorks 5.1 where there are no *real* 
threads in a preemptive multitasking sense. If a logical thread 
blocks by going down the WIN32 rabbit hole - all of the other threads 
will block also. This appears to be what is happening with my code, 
even though it should not with the (listen stream) call.

(3) Drat - I just now introduced that bug when I went from the 
original read-char-no-hang to the current listen/read-char. I 
appreciate you pointing that thing out.

Regards,

Jack Harper
Secure Outcomes Inc.




Re: (Lisp-HUG) Hanging Sockets...

Unable to parse email body. Email id is 10481

Re: (Lisp-HUG) Hanging Sockets...

Thank You Martin for the valuable input.

....things slowly become more clear through the murk :)

I will try the hanging read-char and see what's what with that.

I *still* cannot say that I really understand the exact interaction 
between (sleep) and CAPI "threads" (or is it threads :)

Regards,

Jack Harper
Secure Outcomes Inc
Evergreen, Colorado USA



At 07:51 AM 8/19/2010, you wrote:

> >>>>> On Wed, 18 Aug 2010 16:07:28 -0600, Jack Harper said:
> >
> > (2) I am running under LispWorks 5.1 where there are no *real*
> > threads in a preemptive multitasking sense. If a logical thread
> > blocks by going down the WIN32 rabbit hole - all of the other threads
> > will block also. This appears to be what is happening with my code,
> > even though it should not with the (listen stream) call.
>
>That's not true -- LispWorks 5.1 uses a real native thread for each MP
>process.  The restriction is that only one thread can run Lisp code at once
>(this restriction has been removed in LispWorks 6.0).
>
>The implementation of listen for socket streams calls out of Lisp, so other
>threads should be able to run at that point even if the underlying Windows
>function hangs.
>
>Are you 100% sure that it hangs inside the call to listen?
>
>Since this is being run in a separate thread, I would implement it without the
>call to listen (i.e. just let read-char hang until the next char is
>available).  The :read-timeout should prevent it from hanging forever (it will
>get eof).
>
>--
>Martin Simmons
>LispWorks Ltd
>http://www.lispworks.com/


Updated at: 2020-12-10 08:38 UTC