M2 icon indicating copy to clipboard operation
M2 copied to clipboard

Keyword shield doesn't work

Open mahrud opened this issue 2 years ago • 11 comments

The documentation claims:

shield x -- executes the expression x, temporarily ignoring interrupts.

Unless I'm misunderstanding the page, which doesn't include any examples, shield doesn't seem to be working:

i1 : f = t -> shield sleep t;

i2 : f 10
^Cstdio:2:1:(3): error: interrupted

i3 : f = t -> ( alarm 1; shield sleep t );

i4 : f 5
stdio:4:1:(3): error: alarm occurred

mahrud avatar Sep 05 '22 10:09 mahrud

Yup, it seems that that doesn't work. Good catch.

DanGrayson avatar Sep 05 '22 12:09 DanGrayson

The interrupt mechanism in the interpreter is pretty complicated, so I doubt this will be easy to do for a volunteer. The relevant code is in evaluate.d and the most recent changes to it are from 13 years ago by you (there are no other contributors).

mahrud avatar Sep 05 '22 22:09 mahrud

If this is still available, I'd love to take a swing at it!

pqcfox avatar Feb 13 '23 04:02 pqcfox

I've assigned you, thanks for volunteering!

DanGrayson avatar Feb 18 '23 00:02 DanGrayson

Of course, thanks so much!

pqcfox avatar Feb 20 '23 22:02 pqcfox

Finally got to figuring out the issue here, my apologies for the delay!

This is what I've been able to surmise is happening during e.g. shield sleep 5:

  • shieldfun sets the interruptShield flag and executes the sleep 5 expression
  • the sleep 5 expression in turn calls sleep(2) which suspends the thread
  • the SIGINT is received, waking the prompt thread and passing execution into interrupt_handler
  • interrupt_handler sees that interruptShield is set, and correspondingly sets interruptPending
  • control then passes immediately back to sleep(2), which passes control flow back to shieldfun
  • shieldfun turns off interruptShield and stores interruptPending into interruptFlag
  • evalraw sees interruptFlag is set and gives the exception

The "core" issue here seems to just be that the sleep concludes immediately when SIGINT is received. There's two possible fixes I thought of--if either of them seem useful, I can write them up real quick and share a PR:

  1. Make both the sleep and nanosleep keywords both use nanosleep(2) directly, saving the return value (a timespec with the remaining amount of sleep time before the interrupt) somewhere interrupt_handler can access it, and having interrupt_handler resume sleep if it sees that value set.
  2. Change sleep and nanosleep to manually mask signals before execution and unmask signals after.

Do either of these seem like a useful approach I should try implementing?

pqcfox avatar Jul 02 '23 07:07 pqcfox

(Wanted to quickly follow up on this, in case it's still relevant--would either of these solutions help?)

pqcfox avatar Jun 19 '24 03:06 pqcfox

Yes, I think it's definitely still relevant! I'm not sure which approach is better.

Are sleep and nanosleep the only things for which shield isn't working? It seems to be doing the correct thing for engine computations.

d-torrance avatar Jun 20 '24 13:06 d-torrance

I'll make a PR in that case!

I'm not sure if there's any other similar issues with shield (I haven't seen any), but @mahrud would know better than me.

pqcfox avatar Jun 21 '24 23:06 pqcfox

Change sleep and nanosleep to manually mask signals before execution and unmask signals after.

Wouldn't it be simpler to mask signals when shieldfun starts?

Hopefully this will cover shielding calls to external libraries as well.

mahrud avatar Jun 22 '24 17:06 mahrud

That sounds like a better plan--will give that a shot :)

pqcfox avatar Jun 24 '24 09:06 pqcfox