Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option use_cysignals #4

Merged
merged 4 commits into from
Dec 22, 2023
Merged

Add option use_cysignals #4

merged 4 commits into from
Dec 22, 2023

Conversation

mkoeppe
Copy link
Collaborator

@mkoeppe mkoeppe commented Dec 22, 2023

SageMath uses https://github.com/sagemath/cysignals for advanced interrupt and signal handling with Cython modules.

We add an option JobClient(use_cysignals=True) (default: False).
With the option set, it imports a context manager changesignal from cysignals, which replaces the use of the standard library function signals.signal.

This new option helps us solve sagemath/sage#36944

@mkoeppe mkoeppe requested a review from milahu December 22, 2023 07:06
@milahu
Copy link
Owner

milahu commented Dec 22, 2023

the diff looks wild because of the indent-change

diff minus indent
diff --git a/py/src/gnumake_tokenpool/tokenpool.py b/py/src/gnumake_tokenpool/tokenpool.py
index b0708ea..f67eb54 100644
--- a/py/src/gnumake_tokenpool/tokenpool.py
+++ b/py/src/gnumake_tokenpool/tokenpool.py
@@ -1,8 +1,9 @@
 import sys, os, stat, select, signal, time, re
+
+from contextlib import contextmanager
 from datetime import datetime
 from typing import List
 
-
 __version__ = '0.0.3'
 
 
@@ -26,6 +27,7 @@ class JobClient:
       max_load: int or None = None,
       debug: bool or None = None,
       debug2: bool or None = None,
+      use_cysignals: bool = False,
     ):
 
     self._fdRead = None
@@ -47,6 +49,10 @@ class JobClient:
     self._log = self._get_log(self._debug)
     self._log2 = self._get_log(self._debug2)
 
+    if use_cysignals:
+      from cysignals import changesignal
+      self._changesignal = changesignal
+
     makeFlags = os.environ.get("MAKEFLAGS", "")
     if makeFlags:
       self._log(f"init: MAKEFLAGS: {makeFlags}")
@@ -181,8 +187,8 @@ class JobClient:
       os.close(self._fdReadDup)
 
     # SIGALRM = timer has fired = read timeout
-    old_sigalrm_handler = signal.signal(signal.SIGALRM, read_timeout_handler)
-
+    with self._changesignal(signal.SIGALRM, read_timeout_handler):
+    try:
     # Set SA_RESTART to limit EINTR occurrences.
     # by default, signal.signal clears the SA_RESTART flag.
     # TODO is this necessary?
@@ -206,12 +212,9 @@ class JobClient:
         self._log(f"acquire: read failed: {e}")
         return None # jobserver is full, try again later
       raise e # unexpected error
-
+      finally:
     signal.setitimer(signal.ITIMER_REAL, 0) # clear timer. unix only
 
-    # clear signal handlers
-    signal.signal(signal.SIGALRM, old_sigalrm_handler)
-
     #if len(buffer) == 0:
     #  return None
     assert len(buffer) == 1
@@ -292,3 +295,13 @@ class JobClient:
         self._log(f"init failed: fd {fd} stat failed: {e}")
         raise NoJobServer()
       raise e # unexpected error
+
+  @staticmethod
+  @contextmanager
+  def _changesignal(sig, action):
+    old_sig_handler = signal.signal(sig, action)
+    try:
+      yield
+    finally:
+      # clear signal handler
+      signal.signal(sig, old_sig_handler)

looks good : )

if you have too much time (hah...) you could split it into 2 commits:
py: use signal context and py: add option use_cysignals

otherwise, feel free to merge

@milahu
Copy link
Owner

milahu commented Dec 22, 2023

would it make sense to automatically enable cysignals when available?

something like

try:
  from cysignals import changesignal
  self._log("using cysignals.changesignal")
  self._changesignal = changesignal
except ModuleNotFoundError:
  pass

i guess that cysignals has no significant downsides compared to signals

@mkoeppe
Copy link
Collaborator Author

mkoeppe commented Dec 22, 2023

would it make sense to automatically enable cysignals when available?

I can make that a third, default option.

@mkoeppe
Copy link
Collaborator Author

mkoeppe commented Dec 22, 2023

you could split it into 2 commits:

done

@mkoeppe
Copy link
Collaborator Author

mkoeppe commented Dec 22, 2023

I can make that a third, default option.

done in 32e735b

@mkoeppe
Copy link
Collaborator Author

mkoeppe commented Dec 22, 2023

I'll merge it and cut a new release

@mkoeppe mkoeppe merged commit 9904c96 into milahu:main Dec 22, 2023
@mkoeppe mkoeppe deleted the cysignals_option branch December 22, 2023 20:49
@mkoeppe
Copy link
Collaborator Author

mkoeppe commented Dec 22, 2023

@milahu
Copy link
Owner

milahu commented Dec 23, 2023

thanks : )

for the record:
the messing with signals is needed to get a "read with timeout"

ideally, python's f.read would allow

with open("/dev/stdin") as f:
    text = f.read(timeout=5)

but that fails with

TypeError: TextIOWrapper.read() takes no keyword arguments

also python's input function has no timeout parameter

in javascript im using child_process.spawnSync to "read with timeout"
(calling dd which only works on linux...)

  const reader = child_process.spawnSync('dd', ddArgs, {
    timeout,
    stdio,
    windowsHide: true,
    ...options,
  });

so in python we could use subprocess.run

#!/usr/bin/env python3

import sys
import subprocess

fd_read = 0 # stdin # TODO use the jobserver's fd_read

# pipe one byte from fd_read to stdout
py_code = f"""
import sys; sys.stdout.buffer.write(open({fd_read}, "rb").read(1))
"""

args = [
  sys.executable, # python
  "-c", py_code,
]

try:
  input_byte = subprocess.run(
    args,
    capture_output=True,
    timeout=2,
  ).stdout
except subprocess.TimeoutExpired:
  input_byte = None

print("input_byte", input_byte)
$ ./test-read.py
input_byte None

$ echo xy | ./test-read.py
input_byte b'x'

@mkoeppe
Copy link
Collaborator Author

mkoeppe commented Dec 23, 2023

Thanks for the explanation!
For now, I think, the solution with signals works well though

vbraun pushed a commit to vbraun/sage that referenced this pull request Dec 23, 2023
…mInterrupt` problems

<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes sagemath#1234" use "Introduce new method to
calculate 1+1"
-->
<!-- Describe your changes here in detail -->
The new release 0.0.4 provides cysignals integration:
- milahu/gnumake-tokenpool#4

Fixes sagemath#36944

To verify that the tokenpool mechanism is being used:
```
$ DEBUG_JOBCLIENT=1 make ptest
```

<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes sagemath#12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [ ] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->

URL: sagemath#36948
Reported by: Matthias Köppe
Reviewer(s): John H. Palmieri
vbraun pushed a commit to vbraun/sage that referenced this pull request Dec 24, 2023
…mInterrupt` problems

    
<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes sagemath#1234" use "Introduce new method to
calculate 1+1"
-->
<!-- Describe your changes here in detail -->
The new release 0.0.4 provides cysignals integration:
- milahu/gnumake-tokenpool#4

Fixes sagemath#36944

To verify that the tokenpool mechanism is being used:
```
$ DEBUG_JOBCLIENT=1 make ptest
```

<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes sagemath#12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [ ] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: sagemath#36948
Reported by: Matthias Köppe
Reviewer(s): John H. Palmieri
vbraun pushed a commit to vbraun/sage that referenced this pull request Dec 26, 2023
…mInterrupt` problems

    
<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes sagemath#1234" use "Introduce new method to
calculate 1+1"
-->
<!-- Describe your changes here in detail -->
The new release 0.0.4 provides cysignals integration:
- milahu/gnumake-tokenpool#4

Fixes sagemath#36944

To verify that the tokenpool mechanism is being used:
```
$ DEBUG_JOBCLIENT=1 make ptest
```

<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes sagemath#12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [ ] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: sagemath#36948
Reported by: Matthias Köppe
Reviewer(s): John H. Palmieri
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants