unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Question about handling SIGINT properly in Guile
@ 2024-04-13 14:46 Vijay Marupudi
  2024-04-13 16:46 ` Olivier Dion
  2024-04-14 23:12 ` Maxime Devos
  0 siblings, 2 replies; 7+ messages in thread
From: Vijay Marupudi @ 2024-04-13 14:46 UTC (permalink / raw)
  To: guile-user

Hello folk of guile-user,

I am trying to create a simple Guile program that continues to perform a
task until an interrupt signal is sent. However, I am unable to
deterministically end the program when SIGINT is sent, as the
interrupt handler does not run soon after the call to (sleep ...) ends.

Try running the simple reproduction included below. Try typing ^C
(Ctrl+C) soon after the program begins. Sometimes "WORK" is run
once, and sometimes "WORK" is run twice. What do I need to do to make
"WORK" run only once in this instance?


(define quit? #f)

(sigaction SIGINT (lambda (arg)
                    (pk "SIG" arg)
                    (set! quit? #t)))

(let loop ()
  (pk "QUIT-CHECK")
  (if quit?
      (begin (format #t "Quitting!~%") (exit 0))
      (begin (pk "WORK" (do ((i 0 (+ i 1))
                             (sum 0 (+ sum i)))
                            ((= i 1000) sum)))
             (pk "SLEEP" (sleep 10))
             (loop))))


I understand that signal handlers are asynchronous (Asyncs) and aren't
guaranteed to run immediately. However, I expect them to be run before
the next primitive call. Am I doing something wrong?

~ Vijay



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about handling SIGINT properly in Guile
  2024-04-13 14:46 Question about handling SIGINT properly in Guile Vijay Marupudi
@ 2024-04-13 16:46 ` Olivier Dion
  2024-04-13 20:22   ` Vijay Marupudi
  2024-04-14 23:12 ` Maxime Devos
  1 sibling, 1 reply; 7+ messages in thread
From: Olivier Dion @ 2024-04-13 16:46 UTC (permalink / raw)
  To: Vijay Marupudi, guile-user

On Sat, 13 Apr 2024, Vijay Marupudi <vijaymarupudi@gatech.edu> wrote:
> Hello folk of guile-user,
>
> I am trying to create a simple Guile program that continues to perform a
> task until an interrupt signal is sent. However, I am unable to
> deterministically end the program when SIGINT is sent, as the
> interrupt handler does not run soon after the call to (sleep ...) ends.
>
> Try running the simple reproduction included below. Try typing ^C
> (Ctrl+C) soon after the program begins. Sometimes "WORK" is run
> once, and sometimes "WORK" is run twice. What do I need to do to make
> "WORK" run only once in this instance?

Depending where the signal is sent, you will have two WORKs.  For
example:

--8<---------------cut here---------------start------------->8---
(define quit? #f)

(sigaction SIGINT (lambda (arg)
                    (pk "SIG" arg)
                    (set! quit? #t)))

(let loop ()
  (pk "QUIT-CHECK")
  (if quit?
      (begin (format #t "Quitting!~%") (exit 0))
      (begin (pk "WORK" (do ((i 0 (+ i 1))
                             (sum 0 (+ sum i)))
                            ((= i 1000) sum)))
             (pk "SLEEP" (sleep 10))
             (kill (getpid) SIGINT)    ;; Ctrl-C emulation.
             (loop))))
--8<---------------cut here---------------end--------------->8---


> I understand that signal handlers are asynchronous (Asyncs) and aren't
> guaranteed to run immediately. However, I expect them to be run before
> the next primitive call. Am I doing something wrong?

So there is two things with signals.  First, when a process get a signal
queued, the OS only deliver the signal -- at least on linux -- when
going back to user-space.  Typically, before the process get
re-scheduled or after a system call.  So sending a signal is not
immediate.  Furthermore, Guile also queues the signals and deliver them
to threads with async marks I think.

-- 
Olivier Dion
oldiob.ca



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about handling SIGINT properly in Guile
  2024-04-13 16:46 ` Olivier Dion
@ 2024-04-13 20:22   ` Vijay Marupudi
  2024-04-14 22:50     ` Maxime Devos
  0 siblings, 1 reply; 7+ messages in thread
From: Vijay Marupudi @ 2024-04-13 20:22 UTC (permalink / raw)
  To: Olivier Dion, guile-user

> So there is two things with signals.  First, when a process get a signal
> queued, the OS only deliver the signal -- at least on linux -- when
> going back to user-space.  Typically, before the process get
> re-scheduled or after a system call.  So sending a signal is not
> immediate.  Furthermore, Guile also queues the signals and deliver them
> to threads with async marks I think.

I understand. I don't get this problem with C, so I assume it's the
asynchronity of Guile that is my issue. I tried running the
scm_async_tick function, which the documentation says runs queued asyncs
(by providing a 'safe point', but that doesn't seem to change anything.

(define-module (testing)
  #:use-module (system foreign)
  #:use-module (system foreign-library))

(define tick
  (foreign-library-function #f "scm_async_tick"))

(define quit? #f)

(sigaction SIGINT (lambda (arg)
                    (pk "SIG" arg)
                    (set! quit? #t)))

(let loop ()
  (pk "QUIT-CHECK")
  (tick)
  (if quit?
      (begin (format #t "Quitting!~%") (exit 0))
      (begin (pk "WORK" (do ((i 0 (+ i 1))
                             (sum 0 (+ sum i)))
                            ((= i 1000) sum)))
             (let ((ret (sleep 10)))
               (pk "SLEEP" ret)
               (loop)))))


~ Vijay



^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: Question about handling SIGINT properly in Guile
  2024-04-13 20:22   ` Vijay Marupudi
@ 2024-04-14 22:50     ` Maxime Devos
  0 siblings, 0 replies; 7+ messages in thread
From: Maxime Devos @ 2024-04-14 22:50 UTC (permalink / raw)
  To: Vijay Marupudi, Olivier Dion, guile-user@gnu.org



Sent from Mail for Windows

From: Vijay Marupudi
Sent: Saturday, 13 April 2024 22:24
To: Olivier Dion; guile-user@gnu.org
Subject: Re: Question about handling SIGINT properly in Guile

> So there is two things with signals.  First, when a process get a signal
> queued, the OS only deliver the signal -- at least on linux -- when
> going back to user-space.  Typically, before the process get
>> re-scheduled or after a system call.  So sending a signal is not
>> immediate.  Furthermore, Guile also queues the signals and deliver them
>> to threads with async marks I think.

>I understand. I don't get this problem with C, so I assume it's the
>asynchronity of Guile that is my issue. I tried running the
>scm_async_tick function, which the documentation says runs queued asyncs
>(by providing a 'safe point', but that doesn't seem to change anything.

scm_async_tick is a thing for loops in C code, to make them interruptible (in this case, in the sense that an async can be run).

Somewhere in the Scheme->bytecode compiliation process, the bytecode equivalent to scm_async_tick is automatically inserted in various cases (I don’t recall all cases, but at least this is the case in loops).  – you don’t need to manually scm_async_tick to make it possible to run the async. (Barring any bugs.)

(Now reading the previous e-mails ...)



^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: Question about handling SIGINT properly in Guile
  2024-04-13 14:46 Question about handling SIGINT properly in Guile Vijay Marupudi
  2024-04-13 16:46 ` Olivier Dion
@ 2024-04-14 23:12 ` Maxime Devos
  2024-04-16  0:43   ` Vijay Marupudi
  1 sibling, 1 reply; 7+ messages in thread
From: Maxime Devos @ 2024-04-14 23:12 UTC (permalink / raw)
  To: Vijay Marupudi, guile-user@gnu.org

>Try running the simple reproduction included below. Try typing ^C
(Ctrl+C) soon after the program begins. Sometimes "WORK" is run
once, and sometimes "WORK" is run twice. What do I need to do to make
"WORK" run only once in this instance?
>(define quit? #f)
>(sigaction SIGINT (lambda (arg)
 >                   (pk "SIG" arg)
  >                  (set! quit? #t)))
>
>(let loop ()
> (pk "QUIT-CHECK")
> (if quit?
>      (begin (format #t "Quitting!~%") (exit 0))
>      (begin (pk "WORK" (do ((i 0 (+ i 1))
>                             (sum 0 (+ sum i)))
>                            ((= i 1000) sum)))
>             (pk "SLEEP" (sleep 10))
>             (loop))))
>I understand that signal handlers are asynchronous (Asyncs) and aren't
guaranteed to run immediately. However, I expect them to be run before
the next primitive call. Am I doing something wrong?

(Disregard my previous e-mail – AFAIK nothing I said there is false, but its contents is beside the point).

Four things:
• Even in C, there can be a distance between sending a signal and receiving a signal. (At least, I assume so, I haven’t actually investigated this.) So Guile might simply not have received the SIGINT yet. However, given the (sleep 10), I doubt this is the case here
• I might misremember, but I don’t think there actually is a promise that scm_async_tick or equivalent is run before/during/after a primitive – as I understand it, the promise is that signals will be handled without much of a delay, doing scm_async_tick next to a syscall or equivalent is just an implementation. (Though IIRC scm_async_tick is done as part of SCM_SYSCALL*)
• You might want to check if scm_async_tick is done _after_ the syscall (or, more precisely, the call to whatever is in the C library) or _before_. In this case, if you are depending on (sleep ...) to do the tick, then it should be _after_. But maybe it is instead done _before_. (However, I’d think that ‘pk’ (indirectly) does a scm_async_tick, so that shouldn’t matter?)
• Better put a (call-with-blocked-asyncs ...) or whatever is the exact name around all the I/O unless you’re sure that the port I/O procedures can deal fine with the re-entrancy.

I don’t expect any of these to solve the problem (except perhaps 3) but it’s something you could investigate.

Best regards,
Maxime Devos.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: Question about handling SIGINT properly in Guile
  2024-04-14 23:12 ` Maxime Devos
@ 2024-04-16  0:43   ` Vijay Marupudi
  2024-04-17 14:28     ` Vijay Marupudi
  0 siblings, 1 reply; 7+ messages in thread
From: Vijay Marupudi @ 2024-04-16  0:43 UTC (permalink / raw)
  To: Maxime Devos, guile-user@gnu.org

Hi Maxime,

Thank you for your thoughts!

The documentation of libc suggests that the signal handler is run before
any system call return EINTR, so I think Guile should have added the
handler to the 'pending list' before the call to sleep returns.

https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html

Right, I don't think there is a promise of when scm_async_tick is run,
which is why I tried to run it manually just in case. It doesn't seem
to change anything.

As for 3, I tried putting async_ticks everywhere, doesn't seem to change
anything.

However, this morning, I'm having difficulty reproducing the issue... I
guess this is how it is with race conditions. Will update if I find
anything new...

~ Vijay



^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: Question about handling SIGINT properly in Guile
  2024-04-16  0:43   ` Vijay Marupudi
@ 2024-04-17 14:28     ` Vijay Marupudi
  0 siblings, 0 replies; 7+ messages in thread
From: Vijay Marupudi @ 2024-04-17 14:28 UTC (permalink / raw)
  To: Maxime Devos, guile-user@gnu.org

Here's another simple reproduction, Guile's own webserver module

(use-modules (web server))

(define (handler request body)
  (values '((content-type . (text/plain)))
          "Hello, World!"))

(run-server handler)

It needs two SIGINTs to properly end the program. I think I'm encountering a bug rather than a misconception...

~ Vijay



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2024-04-17 14:28 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-04-13 14:46 Question about handling SIGINT properly in Guile Vijay Marupudi
2024-04-13 16:46 ` Olivier Dion
2024-04-13 20:22   ` Vijay Marupudi
2024-04-14 22:50     ` Maxime Devos
2024-04-14 23:12 ` Maxime Devos
2024-04-16  0:43   ` Vijay Marupudi
2024-04-17 14:28     ` Vijay Marupudi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).