Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALInference 4.1.9.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
omp_interruptible.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017 Leo Singer
3 *
4 * These preprocessor macros help make long-running Python C extensions,
5 * possibly that contain OpenMP parallel for loops, respond gracefully to
6 * signals.
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with with program; see the file COPYING. If not, write to the
21 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301 USA
23 */
24
25
26/*
27 * In a normal C program that does not mess with signals, when the user
28 * types Ctrl-C, the process is sent the SIGINT signal. The default SIGINT
29 * signal handler terminates the process swiftly, even if the program is in
30 * the middle of a CPU-intensive loop, even an OpenMP parallel for loop.
31 *
32 * It's different for a Python program. Python itself attaches handlers for
33 * most (all?) signals. Python's SIGINT handler sets a flag to remind itself to
34 * raise a KeyboardInterrupt exception on the main thread before interpreting
35 * the next instruction.
36 *
37 * If Python is in the middle of executing a long-running method in a Python C
38 * extension, then the interpreter will remain unresponsive until the method
39 * has returned, when it can raise the KeyboardInterrupt. This delay can be
40 * very annoying to the user.
41 *
42 * This header provides a few macros to temporarily change the SIGINT handler
43 * and provide a flag that C functions can check periodically to terminate
44 * early. Here's a skeleton code sample that includes an OpenMP loop to show
45 * how to use the macros. We start with our basic function, foo, which contains
46 * an OpenMP parallel for loop:
47 *
48 * int foo(int n)
49 * {
50 * int retval = 0;
51 * #pragma omp parallel for
52 * for (int i = 0; i < n; i ++)
53 * {
54 * ... // The actual work occurs here.
55 * }
56 * return retval;
57 * }
58 *
59 * We add the macros:
60 *
61 * #include "omp_interruptible.h"
62 *
63 * // Native C function that does the work.
64 * int foo(int n)
65 * {
66 * int retval = 0;
67 * OMP_BEGIN_INTERRUPTIBLE // Replace SIGINT handler.
68 * #pragma omp parallel for
69 * for (int i = 0; i < n; i ++)
70 * {
71 * // Exit loop early if SIGINT has fired.
72 * // Note: you can replace OMP_EXIT_LOOP_EARLY with a simple
73 * // `break;` statement or check it in the loop conditional,
74 * // if you are not using an OpenMP loop.
75 * if (OMP_WAS_INTERRUPTED)
76 * OMP_EXIT_LOOP_EARLY
77 * ... // The actual work occurs here.
78 * }
79 * if (OMP_WAS_INTERRUPTED)
80 * retval = -1;
81 * OMP_END_INTERRUPTIBLE // Restore SIGINT handler.
82 * return retval;
83 * }
84 *
85 * Finally, here's the Python C extension:
86 *
87 * #include <Python.h>
88 *
89 * static PyObject *mod_foo(PyObject *module, PyObject *args)
90 * {
91 * int reval;
92 *
93 * // Run the underlying C function, releasing the global interpreter
94 * // lock (GIL) in the mean time so that other Python threads (if
95 * // any) can run.
96 * Py_BEGIN_ALLOW_THREADS
97 * int retval = foo(1000);
98 * Py_END_ALLOW_THREADS
99 *
100 * // Important: call PyErr_CheckSignals() to give Python a chance to
101 * // raise a KeyboardInterrupt exception, if needed.
102 *
103 * // Indicate success or failure of the method to the interpreter.
104 * PyErr_CheckSignals();
105 * if (retval == 0)
106 * Py_RETURN_NONE;
107 * else
108 * return NULL;
109 * }
110 *
111 * static PyMethodDef methods[] = {
112 * {"foo", (PyCFunction)mod_foo, METH_NOARGS, "doc string here"},
113 * {NULL, NULL, 0, NULL}
114 * };
115 *
116 * static PyModuleDef moduledef = {
117 * PyModuleDef_HEAD_INIT,
118 * "mod", NULL, -1, methods,
119 * NULL, NULL, NULL, NULL
120 * };
121 *
122 * PyMODINIT_FUNC PyInit_mod(void)
123 * {
124 * return PyModule_Create(&moduledef);
125 * }
126 */
127
128
129#ifndef OMP_INTERRUPTIBLE_H
130#define OMP_INTERRUPTIBLE_H
131
132#include <signal.h>
133#include <stdlib.h>
134
135
136/* This is a per-thread pointer to an integer flag that we set when we
137 * receive SIGINT. This is a pointer, and not the flag itself, because the
138 * flag should be shared among all OpenMP threads, and therefore cannot
139 * itself be thread-local. */
140static __thread int *omp_interruptible_flag_ptr = NULL;
141
142
143static __thread struct sigaction omp_interruptible_old_action = {
144 .sa_handler = NULL
145};
146
147
149{
150 int ret = sigaction(sig, &omp_interruptible_old_action, NULL);
151 (void)ret; /* FIXME: should probably do something with this return value */
152 omp_interruptible_old_action = (struct sigaction) {.sa_handler = NULL};
154}
155
156
157static void omp_interruptible_handler(int sig)
158{
160 #pragma omp flush
162 raise(sig);
163}
164
165
166static const struct sigaction omp_interruptible_action = {
167 .sa_handler = omp_interruptible_handler
168};
169
170
171static void omp_interruptible_set_handler(int sig, int *flag_ptr)
172{
175 int ret = sigaction(
177 (void)ret; /* FIXME: should probably do something with this return value */
178}
179
180
181#define OMP_BEGIN_INTERRUPTIBLE { \
182 int omp_was_interrupted; \
183 omp_interruptible_set_handler(SIGINT, &omp_was_interrupted);
184
185
186#define OMP_END_INTERRUPTIBLE \
187 omp_interruptible_restore_handler(SIGINT); \
188}
189
190
191#define OMP_WAS_INTERRUPTED omp_was_interrupted
192
193
194#if _OPENMP
195#define OMP_EXIT_LOOP_EARLY continue;
196#else
197#define OMP_EXIT_LOOP_EARLY break;
198#endif
199
200
201#endif /* OMP_INTERRUPTIBLE_H */
static __thread int * omp_interruptible_flag_ptr
static void omp_interruptible_handler(int sig)
static __thread struct sigaction omp_interruptible_old_action
static const struct sigaction omp_interruptible_action
static void omp_interruptible_restore_handler(int sig)
static void omp_interruptible_set_handler(int sig, int *flag_ptr)