Re: How to abort a read from a socket after some time?

2024-01-22 Thread Tomas Volf
Hello,

thank you very much for the email and the suggested approach, I will try it out.
I just have one more question:

On 2024-01-22 00:46:23 +0100, M wrote:
> >
> > All code below runs after handler is set:
> >
> >(sigaction SIGALRM (lambda _ (display "Alarm!\n")))
>
> Assuming the read-char takes too long, the kernel sends a SIGALRM to Guile. 
> Hence, the C signal handler is run (which sets some fields somewhere 
> indicating that this handler should be run later in the sense of 
> system-async-mark), and the syscall behind read-char returns EINTR.
>
> As this is a fake error (passing it on as a Scheme exception would result in 
> rather messy semantics, e.g. consider the case where things are interrupted 
> twice in a row, time such that the exception handler itself is interrupted 
> with a new exception), Guile decides to retry the syscall

I am confused about this.  I read the documentation for sigaction, and there is
this text for the flags argument:

  -- Variable: SA_RESTART
  If a signal occurs while in a system call, deliver the signal
  then restart the system call (as opposed to returning an
  ‘EINTR’ error from that call).

Based on that my expectation was to get EINTR.  But since the syscall seems to
be restarted even without the SA_RESTART, what exactly does this flag do then?

Thank you,
Tomas Volf

--
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.


signature.asc
Description: PGP signature


RE: How to abort a read from a socket after some time?

2024-01-21 Thread M
(Please ignore the wrong To: field, e-mail program is being silly)

>I am trying to figure out how to abort a read from a socket after some time 
>elapses.  >I failed to figure out how to do so.
>
> All code below runs after handler is set:
>
>(sigaction SIGALRM (lambda _ (display "Alarm!\n")))

Assuming the read-char takes too long, the kernel sends a SIGALRM to Guile. 
Hence, the C signal handler is run (which sets some fields somewhere indicating 
that this handler should be run later in the sense of system-async-mark), and 
the syscall behind read-char returns EINTR.

As this is a fake error (passing it on as a Scheme exception would result in 
rather messy semantics, e.g. consider the case where things are interrupted 
twice in a row, time such that the exception handler itself is interrupted with 
a new exception), Guile decides to retry the syscall, but before that, it 
checks if there is a system-async-mark to be done, so it does that thing – 
i.e., it displays “Alarm!\n” and merrily continue on reading a character.

Now, going by the rest of the mail, it appears you don’t just want to print a 
message, you want to _abort_. So you should modify your handlers to abort, 
i.e., call an escape continuation.

(define escaper (make-parameter #false)
(sigaction SIGALRM (lambda _ (let ((e (escaper))) (if e (e)
[set up sockets, etc.]
;; returns char on success, #false on timeout.
(define (read-char-or-timeout)
  (let/ec e
[set up an alarm]
(parameterize ((escaper (lambda () (e #false
  (read-char e ; XXX add port argument, maybe stop the alarm afterwards?

(This assumes the signal handler is run in the same thread as the thread using 
read-char-or-timeout, I don’t know if this is the case.)

This code has a bug, however: it might be the case that the signal is received 
right after read-char finishes but before the parameterize ends. In that case, 
the character is lost and read-char-or-timeout reports a “timeout”, losing the 
character, which is most likely undesired.

So, if you do things like this, I think you should modify the signal handler to 
instead set a a-timeout-happened flag, and let read-char-or-timeout read that 
flag.

Best regards,
Maxime Devos