Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
d18ccd19
Commit
d18ccd19
authored
Jul 24, 2014
by
Victor Stinner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tets
parent
316b16de
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
278 additions
and
41 deletions
+278
-41
Doc/c-api/exceptions.rst
Doc/c-api/exceptions.rst
+10
-3
Doc/library/signal.rst
Doc/library/signal.rst
+5
-0
Lib/test/test_signal.py
Lib/test/test_signal.py
+119
-11
Modules/signalmodule.c
Modules/signalmodule.c
+136
-19
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj
+8
-8
No files found.
Doc/c-api/exceptions.rst
View file @
d18ccd19
...
...
@@ -443,13 +443,20 @@ in various ways. There is a separate error indicator for each thread.
.. c:function:: int PySignal_SetWakeupFd(int fd)
This utility function specifies a file descriptor to which a ``'\0'`` byte will
be written whenever a signal is received. It returns the previous such file
descriptor. The value ``-1`` disables the feature; this is the initial state.
This utility function specifies a file descriptor to which the signal number
is written as a single byte whenever a signal is received. *fd* must be
non-blocking. It returns the previous such file descriptor.
On Windows, the function only supports socket handles.
The value ``-1`` disables the feature; this is the initial state.
This is equivalent to :func:`signal.set_wakeup_fd` in Python, but without any
error checking. *fd* should be a valid file descriptor. The function should
only be called from the main thread.
.. versionchanged:: 3.5
On Windows, the function now only supports socket handles.
.. c:function:: PyObject* PyErr_NewException(char *name, PyObject *base, PyObject *dict)
...
...
Doc/library/signal.rst
View file @
d18ccd19
...
...
@@ -308,6 +308,8 @@ The :mod:`signal` module defines the following functions:
a library to wakeup a poll or select call, allowing the signal to be fully
processed.
On Windows, the function only supports socket handles.
The old wakeup fd is returned. *fd* must be non-blocking. It is up to the
library to remove any bytes before calling poll or select again.
...
...
@@ -318,6 +320,9 @@ The :mod:`signal` module defines the following functions:
attempting to call it from other threads will cause a :exc:`ValueError`
exception to be raised.
.. versionchanged:: 3.5
On Windows, the function now only supports socket handles.
.. function:: siginterrupt(signalnum, flag)
...
...
Lib/test/test_signal.py
View file @
d18ccd19
...
...
@@ -6,6 +6,7 @@ import gc
import
pickle
import
select
import
signal
import
socket
import
struct
import
subprocess
import
traceback
...
...
@@ -251,21 +252,43 @@ class WindowsSignalTests(unittest.TestCase):
class
WakeupFDTests
(
unittest
.
TestCase
):
def
test_invalid_fd
(
self
):
fd
=
support
.
make_bad_fd
()
if
sys
.
platform
==
"win32"
:
sock
=
socket
.
socket
()
fd
=
sock
.
fileno
()
sock
.
close
()
else
:
fd
=
support
.
make_bad_fd
()
self
.
assertRaises
((
ValueError
,
OSError
),
signal
.
set_wakeup_fd
,
fd
)
@
unittest
.
skipUnless
(
sys
.
platform
==
"win32"
,
'test specific to Windows'
)
def
test_only_socket
(
self
):
# set_wakeup_fd() expects a socket on Windows
with
open
(
support
.
TESTFN
,
'wb'
)
as
fp
:
self
.
addCleanup
(
support
.
unlink
,
support
.
TESTFN
)
self
.
assertRaises
(
ValueError
,
signal
.
set_wakeup_fd
,
fp
.
fileno
())
def
test_set_wakeup_fd_result
(
self
):
r1
,
w1
=
os
.
pipe
()
self
.
addCleanup
(
os
.
close
,
r1
)
self
.
addCleanup
(
os
.
close
,
w1
)
r2
,
w2
=
os
.
pipe
()
self
.
addCleanup
(
os
.
close
,
r2
)
self
.
addCleanup
(
os
.
close
,
w2
)
signal
.
set_wakeup_fd
(
w1
)
self
.
assertIs
(
signal
.
set_wakeup_fd
(
w2
),
w1
)
self
.
assertIs
(
signal
.
set_wakeup_fd
(
-
1
),
w2
)
if
sys
.
platform
==
'win32'
:
sock1
=
socket
.
socket
()
self
.
addCleanup
(
sock1
.
close
)
fd1
=
sock1
.
fileno
()
sock2
=
socket
.
socket
()
self
.
addCleanup
(
sock2
.
close
)
fd2
=
sock2
.
fileno
()
else
:
r1
,
fd1
=
os
.
pipe
()
self
.
addCleanup
(
os
.
close
,
r1
)
self
.
addCleanup
(
os
.
close
,
fd1
)
r2
,
fd2
=
os
.
pipe
()
self
.
addCleanup
(
os
.
close
,
r2
)
self
.
addCleanup
(
os
.
close
,
fd2
)
signal
.
set_wakeup_fd
(
fd1
)
self
.
assertIs
(
signal
.
set_wakeup_fd
(
fd2
),
fd1
)
self
.
assertIs
(
signal
.
set_wakeup_fd
(
-
1
),
fd2
)
self
.
assertIs
(
signal
.
set_wakeup_fd
(
-
1
),
-
1
)
...
...
@@ -441,6 +464,90 @@ class WakeupSignalTests(unittest.TestCase):
"""
,
signal
.
SIGUSR1
,
signal
.
SIGUSR2
,
ordered
=
False
)
@
unittest
.
skipUnless
(
hasattr
(
socket
,
'socketpair'
),
'need socket.socketpair'
)
class
WakeupSocketSignalTests
(
unittest
.
TestCase
):
@
unittest
.
skipIf
(
_testcapi
is
None
,
'need _testcapi'
)
def
test_socket
(
self
):
# use a subprocess to have only one thread
code
=
"""if 1:
import signal
import socket
import struct
import _testcapi
signum = signal.SIGINT
signals = (signum,)
def handler(signum, frame):
pass
signal.signal(signum, handler)
read, write = socket.socketpair()
read.setblocking(False)
write.setblocking(False)
signal.set_wakeup_fd(write.fileno())
_testcapi.raise_signal(signum)
data = read.recv(1)
if not data:
raise Exception("no signum written")
raised = struct.unpack('B', data)
if raised != signals:
raise Exception("%r != %r" % (raised, signals))
read.close()
write.close()
"""
assert_python_ok
(
'-c'
,
code
)
@
unittest
.
skipIf
(
_testcapi
is
None
,
'need _testcapi'
)
def
test_send_error
(
self
):
# Use a subprocess to have only one thread.
if
os
.
name
==
'nt'
:
action
=
'send'
else
:
action
=
'write'
code
=
"""if 1:
import errno
import signal
import socket
import sys
import time
import _testcapi
from test.support import captured_stderr
signum = signal.SIGINT
def handler(signum, frame):
pass
signal.signal(signum, handler)
read, write = socket.socketpair()
read.setblocking(False)
write.setblocking(False)
signal.set_wakeup_fd(write.fileno())
# Close sockets: send() will fail
read.close()
write.close()
with captured_stderr() as err:
_testcapi.raise_signal(signum)
err = err.getvalue()
if ('Exception ignored when trying to {action} to the signal wakeup fd'
not in err):
raise AssertionError(err)
"""
.
format
(
action
=
action
)
assert_python_ok
(
'-c'
,
code
)
@
unittest
.
skipIf
(
sys
.
platform
==
"win32"
,
"Not valid on Windows"
)
class
SiginterruptTest
(
unittest
.
TestCase
):
...
...
@@ -990,6 +1097,7 @@ def test_main():
try
:
support
.
run_unittest
(
GenericTests
,
PosixTests
,
InterProcessSignalTests
,
WakeupFDTests
,
WakeupSignalTests
,
WakeupSocketSignalTests
,
SiginterruptTest
,
ItimerTest
,
WindowsSignalTests
,
PendingSignalsTests
)
finally
:
...
...
Modules/signalmodule.c
View file @
d18ccd19
...
...
@@ -4,6 +4,9 @@
/* XXX Signals should be recorded per thread, now we have thread state. */
#include "Python.h"
#ifdef MS_WINDOWS
#include "socketmodule.h"
/* needed for SOCKET_T */
#endif
#ifndef MS_WINDOWS
#include "posixmodule.h"
#endif
...
...
@@ -87,7 +90,18 @@ static volatile struct {
PyObject
*
func
;
}
Handlers
[
NSIG
];
#ifdef MS_WINDOWS
#define INVALID_SOCKET ((SOCKET_T)-1)
static
volatile
struct
{
SOCKET_T
fd
;
int
send_err_set
;
int
send_errno
;
int
send_win_error
;
}
wakeup
=
{
INVALID_SOCKET
,
0
,
0
};
#else
static
volatile
sig_atomic_t
wakeup_fd
=
-
1
;
#endif
/* Speed up sigcheck() when none tripped */
static
volatile
sig_atomic_t
is_tripped
=
0
;
...
...
@@ -171,6 +185,33 @@ checksignals_witharg(void * unused)
return
PyErr_CheckSignals
();
}
#ifdef MS_WINDOWS
static
int
report_wakeup_error
(
void
*
Py_UNUSED
(
data
))
{
PyObject
*
res
;
if
(
wakeup
.
send_win_error
)
{
/* PyErr_SetExcFromWindowsErr() invokes FormatMessage() which
recognizes the error codes used by both GetLastError() and
WSAGetLastError */
res
=
PyErr_SetExcFromWindowsErr
(
PyExc_OSError
,
wakeup
.
send_win_error
);
}
else
{
errno
=
wakeup
.
send_errno
;
res
=
PyErr_SetFromErrno
(
PyExc_OSError
);
}
assert
(
res
==
NULL
);
wakeup
.
send_err_set
=
0
;
PySys_WriteStderr
(
"Exception ignored when trying to send to the "
"signal wakeup fd:
\n
"
);
PyErr_WriteUnraisable
(
NULL
);
return
0
;
}
#else
static
int
report_wakeup_error
(
void
*
data
)
{
...
...
@@ -183,26 +224,51 @@ report_wakeup_error(void *data)
errno
=
save_errno
;
return
0
;
}
#endif
static
void
trip_signal
(
int
sig_num
)
{
unsigned
char
byte
;
int
rc
=
0
;
Py_ssize_t
rc
;
Handlers
[
sig_num
].
tripped
=
1
;
#ifdef MS_WINDOWS
if
(
wakeup
.
fd
!=
INVALID_SOCKET
)
{
byte
=
(
unsigned
char
)
sig_num
;
do
{
rc
=
send
(
wakeup
.
fd
,
&
byte
,
1
,
0
);
}
while
(
rc
<
0
&&
errno
==
EINTR
);
/* we only have a storage for one error in the wakeup structure */
if
(
rc
<
0
&&
!
wakeup
.
send_err_set
)
{
wakeup
.
send_err_set
=
1
;
wakeup
.
send_errno
=
errno
;
wakeup
.
send_win_error
=
GetLastError
();
Py_AddPendingCall
(
report_wakeup_error
,
NULL
);
}
}
#else
if
(
wakeup_fd
!=
-
1
)
{
byte
=
(
unsigned
char
)
sig_num
;
while
((
rc
=
write
(
wakeup_fd
,
&
byte
,
1
))
==
-
1
&&
errno
==
EINTR
);
if
(
rc
==
-
1
)
Py_AddPendingCall
(
report_wakeup_error
,
(
void
*
)
(
Py_intptr_t
)
errno
);
do
{
rc
=
write
(
wakeup_fd
,
&
byte
,
1
);
}
while
(
rc
<
0
&&
errno
==
EINTR
);
if
(
rc
<
0
)
{
Py_AddPendingCall
(
report_wakeup_error
,
(
void
*
)(
Py_intptr_t
)
errno
);
}
}
#endif
if
(
!
is_tripped
)
{
/* Set is_tripped after setting .tripped, as it gets
cleared in PyErr_CheckSignals() before .tripped. */
is_tripped
=
1
;
Py_AddPendingCall
(
checksignals_witharg
,
NULL
);
}
if
(
is_tripped
)
return
;
/* Set is_tripped after setting .tripped, as it gets
cleared in PyErr_CheckSignals() before .tripped. */
is_tripped
=
1
;
Py_AddPendingCall
(
checksignals_witharg
,
NULL
);
}
static
void
...
...
@@ -426,10 +492,28 @@ signal_siginterrupt(PyObject *self, PyObject *args)
static
PyObject
*
signal_set_wakeup_fd
(
PyObject
*
self
,
PyObject
*
args
)
{
struct
stat
buf
;
#ifdef MS_WINDOWS
PyObject
*
fdobj
;
SOCKET_T
fd
,
old_fd
;
int
res
;
int
res_size
=
sizeof
res
;
PyObject
*
mod
;
struct
stat
st
;
if
(
!
PyArg_ParseTuple
(
args
,
"O:set_wakeup_fd"
,
&
fdobj
))
return
NULL
;
fd
=
PyLong_AsSocket_t
(
fdobj
);
if
(
fd
==
(
SOCKET_T
)(
-
1
)
&&
PyErr_Occurred
())
return
NULL
;
#else
int
fd
,
old_fd
;
struct
stat
st
;
if
(
!
PyArg_ParseTuple
(
args
,
"i:set_wakeup_fd"
,
&
fd
))
return
NULL
;
#endif
#ifdef WITH_THREAD
if
(
PyThread_get_thread_ident
()
!=
main_thread
)
{
PyErr_SetString
(
PyExc_ValueError
,
...
...
@@ -438,28 +522,54 @@ signal_set_wakeup_fd(PyObject *self, PyObject *args)
}
#endif
if
(
fd
!=
-
1
)
{
if
(
!
_PyVerify_fd
(
fd
))
{
PyErr_SetString
(
PyExc_ValueError
,
"invalid fd"
);
#ifdef MS_WINDOWS
if
(
fd
!=
INVALID_SOCKET
)
{
/* Import the _socket module to call WSAStartup() */
mod
=
PyImport_ImportModuleNoBlock
(
"_socket"
);
if
(
mod
==
NULL
)
return
NULL
;
Py_DECREF
(
mod
);
/* test the socket */
if
(
getsockopt
(
fd
,
SOL_SOCKET
,
SO_ERROR
,
(
char
*
)
&
res
,
&
res_size
)
!=
0
)
{
int
err
=
WSAGetLastError
();
if
(
err
==
WSAENOTSOCK
)
PyErr_SetString
(
PyExc_ValueError
,
"fd is not a socket"
);
else
PyErr_SetExcFromWindowsErr
(
PyExc_OSError
,
err
);
return
NULL
;
}
}
if
(
fstat
(
fd
,
&
buf
)
!=
0
)
return
PyErr_SetFromErrno
(
PyExc_OSError
);
old_fd
=
wakeup
.
fd
;
wakeup
.
fd
=
fd
;
if
(
old_fd
!=
INVALID_SOCKET
)
return
PyLong_FromSocket_t
(
old_fd
);
else
return
PyLong_FromLong
(
-
1
);
#else
if
(
fd
!=
-
1
)
{
if
(
fstat
(
fd
,
&
st
)
!=
0
)
{
PyErr_SetFromErrno
(
PyExc_OSError
);
return
NULL
;
}
}
old_fd
=
wakeup_fd
;
wakeup_fd
=
fd
;
return
PyLong_FromLong
(
old_fd
);
#endif
}
PyDoc_STRVAR
(
set_wakeup_fd_doc
,
"set_wakeup_fd(fd) -> fd
\n
\
\n
\
Sets the fd to be written to (with
'
\\
0'
) when a signal
\n
\
Sets the fd to be written to (with
the signal number
) when a signal
\n
\
comes in. A library can use this to wakeup select or poll.
\n
\
The previous fd is returned.
\n
\
The previous fd
or -1
is returned.
\n
\
\n
\
The fd must be non-blocking."
);
...
...
@@ -467,10 +577,17 @@ The fd must be non-blocking.");
int
PySignal_SetWakeupFd
(
int
fd
)
{
int
old_fd
=
wakeup_fd
;
int
old_fd
;
if
(
fd
<
0
)
fd
=
-
1
;
#ifdef MS_WINDOWS
old_fd
=
Py_SAFE_DOWNCAST
(
wakeup
.
fd
,
SOCKET_T
,
int
);
wakeup
.
fd
=
fd
;
#else
old_fd
=
wakeup_fd
;
wakeup_fd
=
fd
;
#endif
return
old_fd
;
}
...
...
PCbuild/pythoncore.vcxproj
View file @
d18ccd19
...
...
@@ -176,7 +176,7 @@
<Command>
"$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<OutputFile>
$(OutDir)$(PyDllName).dll
</OutputFile>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
...
...
@@ -212,7 +212,7 @@ IF %ERRORLEVEL% NEQ 0 (
<Command>
"$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
</Link>
...
...
@@ -247,7 +247,7 @@ IF %ERRORLEVEL% NEQ 0 (
<Command>
"$(SolutionDir)make_buildinfo.exe" Debug "$(IntDir)"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
</Link>
...
...
@@ -285,7 +285,7 @@ IF %ERRORLEVEL% NEQ 0 (
<Command>
"$(SolutionDir)make_buildinfo.exe" Debug "$(IntDir)"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
</Link>
...
...
@@ -317,7 +317,7 @@ IF %ERRORLEVEL% NEQ 0 (
<Command>
"$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<OutputFile>
$(OutDir)$(PyDllName).dll
</OutputFile>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
...
...
@@ -353,7 +353,7 @@ IF %ERRORLEVEL% NEQ 0 (
<Command>
"$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
<TargetMachine>
MachineX64
</TargetMachine>
...
...
@@ -386,7 +386,7 @@ IF %ERRORLEVEL% NEQ 0 (
<Command>
"$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<OutputFile>
$(OutDir)$(PyDllName).dll
</OutputFile>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
...
...
@@ -422,7 +422,7 @@ IF %ERRORLEVEL% NEQ 0 (
<Command>
"$(SolutionDir)make_buildinfo.exe" Release "$(IntDir)\"
</Command>
</PreLinkEvent>
<Link>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;%(AdditionalDependencies)
</AdditionalDependencies>
<AdditionalDependencies>
$(IntDir)getbuildinfo.o;
ws2_32.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
libc;%(IgnoreSpecificDefaultLibraries)
</IgnoreSpecificDefaultLibraries>
<BaseAddress>
0x1e000000
</BaseAddress>
<TargetMachine>
MachineX64
</TargetMachine>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment