Skip to content

Commit 0497f0c

Browse files
Merge branch 'release/4.9.1'
2 parents 6eeec45 + 520d94a commit 0497f0c

39 files changed

+230
-42
lines changed

.github/workflows/main.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
strategy:
3535
fail-fast: false
3636
matrix:
37-
python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10-dev"]
37+
python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11-dev"]
3838
steps:
3939
- uses: "actions/checkout@v2"
4040
- uses: "actions/setup-python@v2"

configure.ac

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
dnl vim: set sw=4 expandtab :
22
dnl
3-
dnl Copyright 2007-2021 GRAHAM DUMPLETON
3+
dnl Copyright 2007-2022 GRAHAM DUMPLETON
44
dnl
55
dnl Licensed under the Apache License, Version 2.0 (the "License");
66
dnl you may not use this file except in compliance with the License.

docs/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
# General information about the project.
4343
project = u'mod_wsgi'
44-
copyright = u'2007-2021, Graham Dumpleton'
44+
copyright = u'2007-2022, Graham Dumpleton'
4545

4646
# The version info for the project you're documenting, acts as replacement for
4747
# |version| and |release|, also used in various other places throughout the

docs/release-notes.rst

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Release Notes
55
.. toctree::
66
:maxdepth: 2
77

8+
release-notes/version-4.9.1
89
release-notes/version-4.9.0
910

1011
release-notes/version-4.8.0

docs/release-notes/version-4.4.9.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ The header names which are accepted for specifying the target host are
7878
to override the ``HTTP_HOST`` value in the WSGI ``environ`` dictionary.
7979

8080
The sole header name accepted for specifying the front end proxy server
81-
name is ``X-Fowarded-Server``. When found, the value will be used to
81+
name is ``X-Forwarded-Server``. When found, the value will be used to
8282
override the ``SERVER_NAME`` value in the WSGI ``environ`` dictionary.
8383

8484
The sole header name accepted for specifying the front end proxy server
85-
port is ``X-Fowarded-Port``. When found, the value will be used to
85+
port is ``X-Forwarded-Port``. When found, the value will be used to
8686
override the ``SERVER_PORT`` value in the WSGI ``environ`` dictionary.
8787

8888
The header names accepted for specifying the client IP address are

docs/release-notes/version-4.9.1.rst

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
=============
2+
Version 4.9.1
3+
=============
4+
5+
Version 4.9.1 of mod_wsgi can be obtained from:
6+
7+
https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.9.1
8+
9+
Bugs Fixed
10+
----------
11+
12+
* When using ``--enable-debugger`` of mod_wsgi-express to enable Pdb, it was
13+
failing due to prior changes to run Apache in a sub processes to avoid Apache
14+
being shutdown when the window size changed. This was because standard input
15+
was being detached from Apache and so it was not possible to interact with
16+
Pdb. Now when ``--enable-debugger`` is used, or any feature which uses
17+
``--debug-mode``, Apache will not be run in a sub process so that you can
18+
still use standard input to interact with the process if needed. This does
19+
mean that a window size change event will again cause Apache to shutdown in
20+
these cases though.
21+
22+
* Update code so compiles on Python 3.11. Python 3.11 makes structures for
23+
Python frame objects opaque and requires functions to access struct members.
24+
25+
Features Changed
26+
----------------
27+
28+
* Historically when a process was being shutdown, mod_wsgi would do its best to
29+
destroy any Python sub interpreters as well as the main Python interpreter.
30+
This was done in case applications attempted to run any actions on process
31+
shutdown via ``atexit`` registered callbacks or other means.
32+
33+
Because of changes in Python 3.9, and possibly because mod_wsgi makes use of
34+
externally created C threads to handle requests, and not Python native
35+
threads, there is now a suspiscion that attempting to delete Python sub
36+
interpreters can hang. It is believed this may relate to Python core now
37+
expecting all Python thread state objects to have been deleted before the
38+
Python sub interpreter can be destroyed. If they aren't then Python core
39+
code can block indefinitely. If the issue isn't the externally created C
40+
threads that mod_wsgi uses, it might instead be arising as a problem when a
41+
hosted WSGI application creates its own background threads but they are
42+
still running when the attempt is made to destroy the sub interpreter.
43+
44+
In the case of using daemon mode the result is that processes can hang on
45+
shutdown, but will still at least be deleted after 5 seconds due to how
46+
Apache process management will forcibly kill managed processes after 5
47+
seconds if they do not exit cleanly themselves. In other words the issue
48+
may not be noticed.
49+
50+
For embedded mode however, the Apache child process can hang around
51+
indefinitely, possibly only being deleted if some higher level system
52+
application manager such as systemd is able to detect the problem and
53+
forcibly deleted the hung process.
54+
55+
Although mod_wsgi always attempts to ensure that the externally created C
56+
threads are not still handling HTTP requests and thus not active prior to
57+
destroying the Python interpreter, it is impossible to guarantee this.
58+
Similarly, there is no way to guarantee that background threads created by a
59+
WSGI application aren't still running. As such, it isn't possible to safely
60+
attempt to delete the Python thread state objects before deleting the Python
61+
sub interpreter.
62+
63+
Because of this uncertainty mod_wsgi now provides a way to disable the attempt
64+
to destroy the Python sub interpreters or the main Python interpreter when the
65+
process is being shutdown. This will though mean that ``atexit`` registered
66+
callbacks will not be called if this option is enabled. It is therefore
67+
important that you use mod_wsgi's own mechanism of being notified when a
68+
process is being shutdown to perform any special actions.
69+
70+
::
71+
72+
import mod_wsgi
73+
74+
def shutdown_handler(event, **kwargs):
75+
print('SHUTDOWN-HANDLER', event, kwargs)
76+
77+
mod_wsgi.subscribe_shutdown(shutdown_handler)
78+
79+
Use of this shutdown notification was necessary anyway to reliably attempt
80+
to stop background threads created by the WSGI application since ``atexit``
81+
registered callbacks are not called by Python core until after it thinks all
82+
threads have been stopped. In other words, ``atexit`` register callbacks
83+
couldn't be used to reliably stop background threads. Thus use of the
84+
mod_wsgi mechanism for performing actions on process shutdown is the
85+
preferred way.
86+
87+
Overall it is expected that the majority of users will not notice this
88+
change as it is very rare to see WSGI applications want to perform special
89+
actions on process shutdown. If you are affected, you should use mod_wsgi's
90+
mechanism to perform special actions on process shutdown.
91+
92+
If you need to enable this mode whereby no attempt is made to destroy the
93+
Python interpreter (including sub interpreters) on process shutdown, you can
94+
add at global scope in the Apache configuration::
95+
96+
WSGIDestroyInterpreter Off
97+
98+
If you are using mod_wsgi-express, you can instead supply the command line
99+
option ``--orphan-interpreter``.

setup.py

+3
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ def _version():
448448
'Programming Language :: Python :: 3.6',
449449
'Programming Language :: Python :: 3.7',
450450
'Programming Language :: Python :: 3.8',
451+
'Programming Language :: Python :: 3.9',
452+
'Programming Language :: Python :: 3.10',
453+
'Programming Language :: Python :: 3.11',
451454
'Topic :: Internet :: WWW/HTTP :: WSGI',
452455
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server'
453456
],

src/server/__init__.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,13 @@ def find_mimetypes():
353353
MaxConnectionsPerChild %(maximum_requests)s
354354
</IfDefine>
355355
356+
<IfDefine ORPHAN_INTERPRETER>
357+
WSGIDestroyInterpreter Off
358+
</IfDefine>
359+
<IfDefine !ORPHAN_INTERPRETER>
360+
WSGIDestroyInterpreter On
361+
</IfDefine>
362+
356363
<IfDefine !ONE_PROCESS>
357364
<IfDefine !EMBEDDED_MODE>
358365
WSGIRestrictEmbedded On
@@ -2692,6 +2699,10 @@ def add_option(platforms, *args, **kwargs):
26922699
help='Specify the name of a separate log file to be used for '
26932700
'the managed service.')
26942701

2702+
add_option('all', '--orphan-interpreter', action='store_true',
2703+
default=False, help='Flag indicating whether should skip over '
2704+
'destroying the Python interpreter on process shutdown.')
2705+
26952706
add_option('unix', '--embedded-mode', action='store_true', default=False,
26962707
help='Flag indicating whether to run in embedded mode rather '
26972708
'than the default daemon mode. Numerous daemon mode specific '
@@ -3348,6 +3359,9 @@ def _cmd_setup_server(command, args, options):
33483359
else:
33493360
options['https_url'] = None
33503361

3362+
if options['orphan_interpreter']:
3363+
options['httpd_arguments_list'].append('-DORPHAN_INTERPRETER')
3364+
33513365
if options['embedded_mode']:
33523366
options['httpd_arguments_list'].append('-DEMBEDDED_MODE')
33533367
options['disable_reloading'] = True
@@ -3662,7 +3676,7 @@ def cmd_start_server(params):
36623676
else:
36633677
executable = posixpath.join(config['server_root'], 'apachectl')
36643678

3665-
if sys.stdout.isatty():
3679+
if sys.stdout.isatty() and not config['debug_mode']:
36663680
process = None
36673681

36683682
def handler(signum, frame):

src/server/management/commands/runmodwsgi.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def handle(self, *args, **options):
140140
executable = os.path.join(options['server_root'], 'apachectl')
141141
name = executable.ljust(len(options['process_name']))
142142

143-
if sys.stdout.isatty():
143+
if sys.stdout.isatty() and not options['debug_mode']:
144144
process = None
145145

146146
def handler(signum, frame):

src/server/mod_wsgi.c

+49-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* ------------------------------------------------------------------------- */
22

33
/*
4-
* Copyright 2007-2021 GRAHAM DUMPLETON
4+
* Copyright 2007-2022 GRAHAM DUMPLETON
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.
@@ -4306,6 +4306,11 @@ static apr_status_t wsgi_python_child_cleanup(void *data)
43064306
wsgi_publish_process_stopping(wsgi_shutdown_reason);
43074307
#endif
43084308

4309+
/* Skip destruction of Python interpreter. */
4310+
4311+
if (wsgi_server_config->destroy_interpreter == 0)
4312+
return APR_SUCCESS;
4313+
43094314
/* In a multithreaded MPM must protect table. */
43104315

43114316
#if APR_HAS_THREADS
@@ -5043,6 +5048,28 @@ static const char *wsgi_set_python_hash_seed(cmd_parms *cmd, void *mconfig,
50435048
return NULL;
50445049
}
50455050

5051+
static const char *wsgi_set_destroy_interpreter(cmd_parms *cmd, void *mconfig,
5052+
const char *f)
5053+
{
5054+
const char *error = NULL;
5055+
WSGIServerConfig *sconfig = NULL;
5056+
5057+
error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5058+
if (error != NULL)
5059+
return error;
5060+
5061+
sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5062+
5063+
if (strcasecmp(f, "Off") == 0)
5064+
sconfig->destroy_interpreter = 0;
5065+
else if (strcasecmp(f, "On") == 0)
5066+
sconfig->destroy_interpreter = 1;
5067+
else
5068+
return "WSGIDestroyInterpreter must be one of: Off | On";
5069+
5070+
return NULL;
5071+
}
5072+
50465073
static const char *wsgi_set_restrict_embedded(cmd_parms *cmd, void *mconfig,
50475074
const char *f)
50485075
{
@@ -9521,17 +9548,26 @@ static void wsgi_log_stack_traces(void)
95219548
const char *filename = NULL;
95229549
const char *name = NULL;
95239550

9551+
#if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 9)
9552+
lineno = PyFrame_GetLineNumber(current);
9553+
#else
95249554
if (current->f_trace) {
95259555
lineno = current->f_lineno;
95269556
}
95279557
else {
95289558
lineno = PyCode_Addr2Line(current->f_code,
95299559
current->f_lasti);
95309560
}
9561+
#endif
95319562

95329563
#if PY_MAJOR_VERSION >= 3
9564+
#if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 9)
9565+
filename = PyUnicode_AsUTF8(PyFrame_GetCode(current)->co_filename);
9566+
name = PyUnicode_AsUTF8(PyFrame_GetCode(current)->co_name);
9567+
#else
95339568
filename = PyUnicode_AsUTF8(current->f_code->co_filename);
95349569
name = PyUnicode_AsUTF8(current->f_code->co_name);
9570+
#endif
95359571
#else
95369572
filename = PyString_AsString(current->f_code->co_filename);
95379573
name = PyString_AsString(current->f_code->co_name);
@@ -9544,7 +9580,11 @@ static void wsgi_log_stack_traces(void)
95449580
getpid(), thread_id, filename, lineno, name);
95459581
}
95469582
else {
9583+
#if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 9)
9584+
if (PyFrame_GetBack(current)) {
9585+
#else
95479586
if (current->f_back) {
9587+
#endif
95489588
ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
95499589
"mod_wsgi (pid=%d): called from file "
95509590
"\"%s\", line %d, in %s,", getpid(),
@@ -9558,7 +9598,11 @@ static void wsgi_log_stack_traces(void)
95589598
}
95599599
}
95609600

9601+
#if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 9)
9602+
current = PyFrame_GetBack(current);
9603+
#else
95619604
current = current->f_back;
9605+
#endif
95629606
}
95639607
}
95649608
}
@@ -10011,7 +10055,7 @@ static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon)
1001110055

1001210056
if (status != APR_SUCCESS) {
1001310057
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
10014-
"mod_wsgi (pid=%d): Couldn't intialise accept "
10058+
"mod_wsgi (pid=%d): Couldn't initialise accept "
1001510059
"mutex in daemon process '%s'.",
1001610060
getpid(), daemon->group->mutex_path);
1001710061

@@ -16224,6 +16268,9 @@ static const command_rec wsgi_commands[] =
1622416268
AP_INIT_TAKE1("WSGIPythonHashSeed", wsgi_set_python_hash_seed,
1622516269
NULL, RSRC_CONF, "Python hash seed."),
1622616270

16271+
AP_INIT_TAKE1("WSGIDestroyInterpreter", wsgi_set_destroy_interpreter,
16272+
NULL, RSRC_CONF, "Enable/Disable destruction of Python interpreter."),
16273+
1622716274
#if defined(MOD_WSGI_WITH_DAEMONS)
1622816275
AP_INIT_TAKE1("WSGIRestrictEmbedded", wsgi_set_restrict_embedded,
1622916276
NULL, RSRC_CONF, "Enable/Disable use of embedded mode."),

src/server/wsgi_apache.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* ------------------------------------------------------------------------- */
22

33
/*
4-
* Copyright 2007-2021 GRAHAM DUMPLETON
4+
* Copyright 2007-2022 GRAHAM DUMPLETON
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.

src/server/wsgi_apache.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* ------------------------------------------------------------------------- */
55

66
/*
7-
* Copyright 2007-2021 GRAHAM DUMPLETON
7+
* Copyright 2007-2022 GRAHAM DUMPLETON
88
*
99
* Licensed under the Apache License, Version 2.0 (the "License");
1010
* you may not use this file except in compliance with the License.

src/server/wsgi_buckets.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* ------------------------------------------------------------------------- */
22

33
/*
4-
* Copyright 2007-2021 GRAHAM DUMPLETON
4+
* Copyright 2007-2022 GRAHAM DUMPLETON
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.

src/server/wsgi_buckets.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* ------------------------------------------------------------------------- */
55

66
/*
7-
* Copyright 2007-2021 GRAHAM DUMPLETON
7+
* Copyright 2007-2022 GRAHAM DUMPLETON
88
*
99
* Licensed under the Apache License, Version 2.0 (the "License");
1010
* you may not use this file except in compliance with the License.

src/server/wsgi_convert.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* ------------------------------------------------------------------------- */
22

33
/*
4-
* Copyright 2007-2021 GRAHAM DUMPLETON
4+
* Copyright 2007-2022 GRAHAM DUMPLETON
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.

src/server/wsgi_convert.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* ------------------------------------------------------------------------- */
55

66
/*
7-
* Copyright 2007-2021 GRAHAM DUMPLETON
7+
* Copyright 2007-2022 GRAHAM DUMPLETON
88
*
99
* Licensed under the Apache License, Version 2.0 (the "License");
1010
* you may not use this file except in compliance with the License.

src/server/wsgi_daemon.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* ------------------------------------------------------------------------- */
22

33
/*
4-
* Copyright 2007-2021 GRAHAM DUMPLETON
4+
* Copyright 2007-2022 GRAHAM DUMPLETON
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)