1 Based on https://github.com/gevent/gevent/commit/8224e81425762ad21d3b63ffb9cc0a0c2d789704.patch
2 and https://src.fedoraproject.org/rpms/python-gevent/raw/master/f/0001-code-replace.patch
4 From e6e2959184909c6292e0135b709282cd5f96065b Mon Sep 17 00:00:00 2001
5 From: Jason Madden <jamadden@gmail.com>
6 Date: Fri, 6 Sep 2019 16:29:58 -0500
7 Subject: [PATCH 1/9] Basic support for CPython 3.8.0b4.
9 Still needs the specific networking test classes added, but all the basics pass for me. Lets see about CI.
11 .travis.yml | 11 +++++++
13 examples/webproxy.py | 7 +++-
14 pyproject.toml | 5 +--
15 scripts/install.sh | 3 ++
16 src/gevent/__semaphore.pxd | 2 +-
17 src/gevent/_compat.py | 2 +-
18 src/gevent/_socket2.py | 1 +
19 src/gevent/_socketcommon.py | 12 +++++--
20 src/gevent/monkey.py | 46 +++++++++++++++++++++++++--
21 src/gevent/os.py | 29 ++++++++++++++---
22 src/gevent/subprocess.py | 32 +++++++++++++++++++
23 src/gevent/testing/testrunner.py | 17 +++++++---
24 src/gevent/tests/test__core_fork.py | 7 ++--
25 src/gevent/tests/test__threading_2.py | 26 +++++++++------
26 src/gevent/thread.py | 6 ++++
27 src/gevent/threading.py | 46 ++++++++++-----------------
28 17 files changed, 196 insertions(+), 60 deletions(-)
30 #diff --git a/.travis.yml b/.travis.yml
31 #index bf307df95..05dc24b12 100644
34 #@@ -42,6 +42,7 @@ env:
35 # - TRAVIS_PYTHON_VERSION=3.5
36 # - TRAVIS_PYTHON_VERSION=3.6
37 # - TRAVIS_PYTHON_VERSION=3.7
38 #+ - TRAVIS_PYTHON_VERSION=3.8
39 # - TRAVIS_PYTHON_VERSION=pypy2.7
40 # - TRAVIS_PYTHON_VERSION=pypy3.6
41 # - TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0
42 #@@ -144,6 +145,8 @@ jobs:
46 #+ - <<: *build-gevent
47 #+ env: TRAVIS_PYTHON_VERSION=3.8
49 # env: TRAVIS_PYTHON_VERSION=3.5
51 #@@ -278,6 +281,14 @@ jobs:
52 # env: TRAVIS_PYTHON_VERSION=3.7
56 #+ - <<: *test-libuv-jobs
57 #+ env: TRAVIS_PYTHON_VERSION=3.8
59 #+ - <<: *test-libev-jobs
60 #+ env: TRAVIS_PYTHON_VERSION=3.8
64 # # 2.7, no-embed. Run the tests that exercise the libraries we
66 #diff --git a/CHANGES.rst b/CHANGES.rst
67 #index 52b5e1c6f..53f4398d2 100644
71 # - Add an ``--module`` option to ``gevent.monkey`` allowing to run a Python
72 # module rather than a script. See :pr:`1440`.
74 #+- Add support for CPython 3.8.0b4.
76 #+- Improve the way joining the main thread works on Python 3.
81 diff -urNp -x '*.orig' gevent-1.4.0.org/examples/webproxy.py gevent-1.4.0/examples/webproxy.py
82 --- gevent-1.4.0.org/examples/webproxy.py 2019-01-04 12:51:44.000000000 +0100
83 +++ gevent-1.4.0/examples/webproxy.py 2021-03-05 09:44:15.299051013 +0100
84 @@ -15,7 +15,12 @@ from gevent import monkey; monkey.patch_
88 -from cgi import escape
91 + from cgi import escape
93 + # Python 3.8 removed this API
94 + from html import escape
98 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/__semaphore.pxd gevent-1.4.0/src/gevent/__semaphore.pxd
99 --- gevent-1.4.0.org/src/gevent/__semaphore.pxd 2019-01-04 12:51:44.000000000 +0100
100 +++ gevent-1.4.0/src/gevent/__semaphore.pxd 2021-03-05 09:44:15.299051013 +0100
101 @@ -13,7 +13,7 @@ cdef class Semaphore(AbstractLinkable):
103 cpdef _start_notify(self)
104 cpdef int wait(self, object timeout=*) except -1000
105 - cpdef bint acquire(self, int blocking=*, object timeout=*) except -1000
106 + cpdef bint acquire(self, bint blocking=*, object timeout=*) except -1000
107 cpdef __enter__(self)
108 cpdef __exit__(self, object t, object v, object tb)
110 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/_compat.py gevent-1.4.0/src/gevent/_compat.py
111 --- gevent-1.4.0.org/src/gevent/_compat.py 2019-01-04 12:51:44.000000000 +0100
112 +++ gevent-1.4.0/src/gevent/_compat.py 2021-03-05 09:44:15.299051013 +0100
113 @@ -14,7 +14,7 @@ PY3 = sys.version_info[0] >= 3
114 PYPY = hasattr(sys, 'pypy_version_info')
115 WIN = sys.platform.startswith("win")
116 LINUX = sys.platform.startswith('linux')
117 -OSX = sys.platform == 'darwin'
118 +OSX = MAC = sys.platform == 'darwin'
121 PURE_PYTHON = PYPY or os.getenv('PURE_PYTHON')
122 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/_socket2.py gevent-1.4.0/src/gevent/_socket2.py
123 --- gevent-1.4.0.org/src/gevent/_socket2.py 2019-01-04 12:51:44.000000000 +0100
124 +++ gevent-1.4.0/src/gevent/_socket2.py 2021-03-05 09:44:15.299051013 +0100
125 @@ -6,6 +6,7 @@ from __future__ import absolute_import
127 # Our import magic sadly makes this warning useless
128 # pylint: disable=undefined-variable
131 from gevent import _socketcommon
132 from gevent._util import copy_globals
133 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/_socketcommon.py gevent-1.4.0/src/gevent/_socketcommon.py
134 --- gevent-1.4.0.org/src/gevent/_socketcommon.py 2019-01-04 12:51:44.000000000 +0100
135 +++ gevent-1.4.0/src/gevent/_socketcommon.py 2021-03-05 09:44:15.302384451 +0100
136 @@ -74,6 +74,12 @@ from gevent._hub_local import get_hub_no
137 from gevent._compat import string_types, integer_types, PY3
138 from gevent._util import copy_globals
140 +if sys.version_info[:2] >= (3, 8):
141 + __imports__.extend([
143 + 'has_dualstack_ipv6'
146 is_windows = sys.platform == 'win32'
147 is_macos = sys.platform == 'darwin'
149 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/_ssl3.py gevent-1.4.0/src/gevent/_ssl3.py
150 --- gevent-1.4.0.org/src/gevent/_ssl3.py 2019-01-04 12:51:44.000000000 +0100
151 +++ gevent-1.4.0/src/gevent/_ssl3.py 2021-03-05 09:44:15.299051013 +0100
152 @@ -667,6 +667,11 @@ class SSLSocket(socket):
154 return self._sslobj.tls_unique_cb()
156 + def verify_client_post_handshake(self):
157 + # Only present in 3.7.1+; an attributeerror is alright
159 + return self._sslobj.verify_client_post_handshake()
160 + raise ValueError("No SSL wrapper around " + str(self))
162 # Python does not support forward declaration of types
163 SSLContext.sslsocket_class = SSLSocket
164 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/_tblib.py gevent-1.4.0/src/gevent/_tblib.py
165 --- gevent-1.4.0.org/src/gevent/_tblib.py 2019-01-04 12:51:44.000000000 +0100
166 +++ gevent-1.4.0/src/gevent/_tblib.py 2021-03-05 09:44:15.302384451 +0100
167 @@ -198,7 +198,10 @@ class Traceback(object):
169 f_code = current.tb_frame.f_code
170 code = compile('\n' * (current.tb_lineno - 1) + 'raise __traceback_maker', current.tb_frame.f_code.co_filename, 'exec')
172 + if hasattr(code, "replace"):
173 + # Python 3.8 and newer
174 + code = code.replace(co_argcount=0, co_freevars=(), co_cellvars=())
177 0, code.co_kwonlyargcount,
178 code.co_nlocals, code.co_stacksize, code.co_flags,
179 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/libuv/_corecffi_source.c gevent-1.4.0/src/gevent/libuv/_corecffi_source.c
180 --- gevent-1.4.0.org/src/gevent/libuv/_corecffi_source.c 2019-01-04 12:51:44.000000000 +0100
181 +++ gevent-1.4.0/src/gevent/libuv/_corecffi_source.c 2021-03-05 09:44:15.299051013 +0100
182 @@ -150,32 +150,34 @@ static void _gevent_fs_poll_callback3(vo
184 static void gevent_uv_walk_callback_close(uv_handle_t* handle, void* arg)
186 - if( handle && !uv_is_closing(handle) ) {
187 - uv_close(handle, NULL);
189 + if( handle && !uv_is_closing(handle) ) {
190 + uv_close(handle, NULL);
194 static void gevent_close_all_handles(uv_loop_t* loop)
197 uv_walk(loop, gevent_uv_walk_callback_close, NULL);
201 static void gevent_zero_timer(uv_timer_t* handle)
203 - memset(handle, 0, sizeof(uv_timer_t));
204 + memset(handle, 0, sizeof(uv_timer_t));
207 static void gevent_zero_check(uv_check_t* handle)
209 - memset(handle, 0, sizeof(uv_check_t));
210 + memset(handle, 0, sizeof(uv_check_t));
213 static void gevent_zero_prepare(uv_prepare_t* handle)
215 - memset(handle, 0, sizeof(uv_prepare_t));
216 + memset(handle, 0, sizeof(uv_prepare_t));
219 static void gevent_zero_loop(uv_loop_t* handle)
221 - memset(handle, 0, sizeof(uv_loop_t));
222 + memset(handle, 0, sizeof(uv_loop_t));
224 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/monkey.py gevent-1.4.0/src/gevent/monkey.py
225 --- gevent-1.4.0.org/src/gevent/monkey.py 2019-01-04 12:51:44.000000000 +0100
226 +++ gevent-1.4.0/src/gevent/monkey.py 2021-03-05 09:44:15.299051013 +0100
227 @@ -614,6 +614,22 @@ def patch_thread(threading=True, _thread
228 raise RuntimeError("Cannot join current thread")
229 if thread_greenlet is not None and thread_greenlet.dead:
231 + # You may ask: Why not call thread_greenlet.join()?
232 + # Well, in the one case we actually have a greenlet, it's the
233 + # low-level greenlet.greenlet object for the main thread, which
234 + # doesn't have a join method.
236 + # You may ask: Why not become the main greenlet's *parent*
237 + # so you can get notified when it finishes? Because you can't
238 + # create a greenlet cycle (the current greenlet is a descendent
239 + # of the parent), and nor can you set a greenlet's parent to None,
240 + # so there can only ever be one greenlet with a parent of None: the main
241 + # greenlet, the one we need to watch.
243 + # You may ask: why not swizzle out the problematic lock on the main thread
244 + # into a gevent friendly lock? Well, the interpreter actually depends on that
245 + # for the main thread in threading._shutdown; see below.
247 if not thread.is_alive():
250 @@ -646,8 +662,34 @@ def patch_thread(threading=True, _thread
251 if orig_current_thread == threading_mod.main_thread():
252 main_thread = threading_mod.main_thread()
253 _greenlet = main_thread._greenlet = greenlet.getcurrent()
254 + main_thread.__real_tstate_lock = main_thread._tstate_lock
256 + # The interpreter will call threading._shutdown
257 + # when the main thread exits and is about to
258 + # go away. It is called *in* the main thread. This
259 + # is a perfect place to notify other greenlets that
260 + # the main thread is done. We do this by overriding the
261 + # lock of the main thread during operation, and only restoring
262 + # it to the native blocking version at shutdown time
263 + # (the interpreter also has a reference to this lock in a
264 + # C data structure).
265 + main_thread._tstate_lock = threading_mod.Lock()
266 + main_thread._tstate_lock.acquire()
267 + orig_shutdown = threading_mod._shutdown
269 + # Release anyone trying to join() me,
270 + # and let us switch to them.
271 + if main_thread._tstate_lock:
272 + main_thread._tstate_lock.release()
273 + from gevent import sleep
275 + # The only truly blocking native shutdown lock to
276 + # acquire should be our own (hopefully)
277 + main_thread._tstate_lock = main_thread.__real_tstate_lock
278 + main_thread.__real_tstate_lock = None
281 - main_thread.join = make_join_func(main_thread, _greenlet)
282 + threading_mod._shutdown = _shutdown
284 # Patch up the ident of the main thread to match. This
285 # matters if threading was imported before monkey-patching
286 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/os.py gevent-1.4.0/src/gevent/os.py
287 --- gevent-1.4.0.org/src/gevent/os.py 2019-01-04 12:51:44.000000000 +0100
288 +++ gevent-1.4.0/src/gevent/os.py 2021-03-05 09:44:15.299051013 +0100
289 @@ -385,6 +385,12 @@ if hasattr(os, 'fork'):
290 # we're not watching it
291 return _waitpid(pid, options)
293 + def _watch_child(pid, callback=None, loop=None, ref=False):
294 + loop = loop or get_hub().loop
295 + watcher = loop.child(pid, ref=ref)
296 + _watched_children[pid] = watcher
297 + watcher.start(_on_child, watcher, callback)
299 def fork_and_watch(callback=None, loop=None, ref=False, fork=fork_gevent):
301 Fork a child process and start a child watcher for it in the parent process.
302 @@ -413,10 +419,7 @@ if hasattr(os, 'fork'):
306 - loop = loop or get_hub().loop
307 - watcher = loop.child(pid, ref=ref)
308 - _watched_children[pid] = watcher
309 - watcher.start(_on_child, watcher, callback)
310 + _watch_child(pid, callback, loop, ref)
313 __extensions__.append('fork_and_watch')
314 @@ -474,6 +477,23 @@ if hasattr(os, 'fork'):
315 # take any args to match fork_and_watch
316 return forkpty_and_watch(*args, **kwargs)
317 __implements__.append("waitpid")
319 + if hasattr(os, 'posix_spawn'):
320 + _raw_posix_spawn = os.posix_spawn
321 + _raw_posix_spawnp = os.posix_spawnp
323 + def posix_spawn(*args, **kwargs):
324 + pid = _raw_posix_spawn(*args, **kwargs)
328 + def posix_spawnp(*args, **kwargs):
329 + pid = _raw_posix_spawnp(*args, **kwargs)
333 + __implements__.append("posix_spawn")
334 + __implements__.append("posix_spawnp")
338 @@ -503,6 +523,7 @@ if hasattr(os, 'fork'):
340 __implements__.remove('fork')
343 __imports__ = copy_globals(os, globals(),
344 names_to_ignore=__implements__ + __extensions__,
345 dunder_names_to_keep=())
346 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/testing/testrunner.py gevent-1.4.0/src/gevent/testing/testrunner.py
347 --- gevent-1.4.0.org/src/gevent/testing/testrunner.py 2019-01-04 12:51:44.000000000 +0100
348 +++ gevent-1.4.0/src/gevent/testing/testrunner.py 2021-03-05 09:44:15.299051013 +0100
349 @@ -381,17 +381,26 @@ def _setup_environ(debug=False):
350 if 'GEVENT_DEBUG' not in os.environ and debug:
351 os.environ['GEVENT_DEBUG'] = 'debug'
353 - if 'PYTHONTRACEMALLOC' not in os.environ:
354 + if 'PYTHONTRACEMALLOC' not in os.environ and debug:
355 + # This slows the tests down quite a bit. Reserve
357 os.environ['PYTHONTRACEMALLOC'] = '10'
359 if 'PYTHONDEVMODE' not in os.environ:
361 + # Python 3.7 and above.
362 os.environ['PYTHONDEVMODE'] = '1'
364 - if 'PYTHONMALLOC' not in os.environ:
366 + if 'PYTHONMALLOC' not in os.environ and debug:
367 + # Python 3.6 and above.
368 + # This slows the tests down some, but
369 + # can detect memory corruption. Unfortunately
370 + # it can also be flaky, especially in pre-release
371 + # versions of Python (e.g., lots of crashes on Python 3.8b4).
372 os.environ['PYTHONMALLOC'] = 'debug'
374 + if sys.version_info.releaselevel != 'final' and not debug:
375 + os.environ['PYTHONMALLOC'] = 'default'
376 + os.environ['PYTHONDEVMODE'] = ''
380 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/tests/known_failures.py gevent-1.4.0/src/gevent/tests/known_failures.py
381 --- gevent-1.4.0.org/src/gevent/tests/known_failures.py 2021-03-05 09:44:15.072377256 +0100
382 +++ gevent-1.4.0/src/gevent/tests/known_failures.py 2021-03-05 09:44:15.302384451 +0100
383 @@ -610,6 +610,15 @@ RUN_ALONE = [
388 + # Strange failures sometimes, but only on Python 3.7, reporting
389 + # "ConnectionAbortedError: [WinError 10053] An established
390 + # connection was aborted by the software in your host machine"
391 + # when we've done no such thing. Try running not in parallel
398 if APPVEYOR or TRAVIS:
399 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/tests/test__core.py gevent-1.4.0/src/gevent/tests/test__core.py
400 --- gevent-1.4.0.org/src/gevent/tests/test__core.py 2019-01-04 12:51:44.000000000 +0100
401 +++ gevent-1.4.0/src/gevent/tests/test__core.py 2021-03-05 09:44:15.299051013 +0100
403 from __future__ import absolute_import, print_function, division
407 import gevent.testing as greentest
409 from gevent import core
410 @@ -130,6 +131,12 @@ class TestWatchersDefault(TestWatchers):
411 "See https://ci.appveyor.com/project/denik/gevent/build/1.0.1380/job/lrlvid6mkjtyrhn5#L1103 "
412 "It has also timed out, but only on Appveyor CPython 3.6; local CPython 3.6 does not. "
413 "See https://ci.appveyor.com/project/denik/gevent/build/1.0.1414/job/yn7yi8b53vtqs8lw#L1523")
415 + greentest.LIBUV and greentest.RUNNING_ON_TRAVIS and sys.version_info == (3, 8, 0, 'beta', 4),
416 + "Crashes on 3.8.0b4 on TravisCI. "
417 + "(https://travis-ci.org/gevent/gevent/jobs/582031266#L215) "
418 + "Unable to reproduce locally so far on macOS."
420 class TestWatchersDefaultDestroyed(TestWatchers):
423 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/tests/test__core_fork.py gevent-1.4.0/src/gevent/tests/test__core_fork.py
424 --- gevent-1.4.0.org/src/gevent/tests/test__core_fork.py 2019-01-04 12:51:44.000000000 +0100
425 +++ gevent-1.4.0/src/gevent/tests/test__core_fork.py 2021-03-05 09:44:15.299051013 +0100
427 from __future__ import print_function
429 gevent.monkey.patch_all()
434 import multiprocessing
438 hub = gevent.get_hub()
441 @@ -46,6 +47,7 @@ def test():
442 if __name__ == '__main__':
443 # Must call for Windows to fork properly; the fork can't be in the top-level
444 multiprocessing.freeze_support()
446 # fork watchers weren't firing in multi-threading processes.
447 # This test is designed to prove that they are.
448 # However, it fails on Windows: The fork watcher never runs!
449 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/tests/test__greenness.py gevent-1.4.0/src/gevent/tests/test__greenness.py
450 --- gevent-1.4.0.org/src/gevent/tests/test__greenness.py 2019-01-04 12:51:44.000000000 +0100
451 +++ gevent-1.4.0/src/gevent/tests/test__greenness.py 2021-03-05 09:44:15.299051013 +0100
452 @@ -29,13 +29,15 @@ monkey.patch_all()
453 import gevent.testing as greentest
458 from urllib import request as urllib2
460 - import BaseHTTPServer
462 from http import server as BaseHTTPServer
463 + from http.server import SimpleHTTPRequestHandler
467 + import BaseHTTPServer
468 + from SimpleHTTPServer import SimpleHTTPRequestHandler
472 from gevent.testing import params
473 @@ -47,7 +49,8 @@ class TestGreenness(greentest.TestCase):
475 server_address = params.DEFAULT_BIND_ADDR_TUPLE
476 BaseHTTPServer.BaseHTTPRequestHandler.protocol_version = "HTTP/1.0"
477 - self.httpd = BaseHTTPServer.HTTPServer(server_address, BaseHTTPServer.BaseHTTPRequestHandler)
478 + self.httpd = BaseHTTPServer.HTTPServer(server_address,
479 + SimpleHTTPRequestHandler)
480 self.httpd.request_count = 0
483 @@ -62,10 +65,10 @@ class TestGreenness(greentest.TestCase):
484 server = gevent.spawn(self.serve)
486 port = self.httpd.socket.getsockname()[1]
487 - with self.assertRaises(urllib2.HTTPError) as exc:
488 - urllib2.urlopen('http://127.0.0.1:%s' % port)
489 - self.assertEqual(exc.exception.code, 501)
491 + rsp = urllib2.urlopen('http://127.0.0.1:%s' % port)
495 self.assertEqual(self.httpd.request_count, 1)
498 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/tests/test__threading_2.py gevent-1.4.0/src/gevent/tests/test__threading_2.py
499 --- gevent-1.4.0.org/src/gevent/tests/test__threading_2.py 2019-01-04 12:51:44.000000000 +0100
500 +++ gevent-1.4.0/src/gevent/tests/test__threading_2.py 2021-03-05 09:44:15.299051013 +0100
501 @@ -33,6 +33,7 @@ try:
502 from test.support import verbose
504 from test.test_support import verbose
509 @@ -46,7 +47,7 @@ import unittest
512 from gevent.tests import lock_tests
515 # A trivial mutable counter.
517 def skipDueToHang(cls):
518 @@ -132,7 +133,7 @@ class ThreadTests(unittest.TestCase):
519 print('waiting for all tasks to complete')
522 - self.assertFalse(t.is_alive())
523 + self.assertFalse(t.is_alive(), t.__dict__)
524 if hasattr(t, 'ident'):
525 self.assertNotEqual(t.ident, 0)
526 self.assertFalse(t.ident is None)
527 @@ -351,28 +352,33 @@ class ThreadTests(unittest.TestCase):
529 # Raising SystemExit skipped threading._shutdown
531 - p = subprocess.Popen([sys.executable, "-W", "ignore", "-c", """if 1:
535 from time import sleep
540 # As a non-daemon thread we SHOULD wake up and nothing
541 # should be torn down yet
542 - print("Woke up, sleep function is: %%r" %% sleep)
543 + print("Woke up, sleep function is: %%s.%%s" %% (sleep.__module__, sleep.__name__))
545 threading.Thread(target=child).start()
549 + p = subprocess.Popen([sys.executable, "-W", "ignore", "-c", script],
550 stdout=subprocess.PIPE,
551 stderr=subprocess.PIPE)
552 stdout, stderr = p.communicate()
553 stdout = stdout.strip()
554 stdout = stdout.decode('utf-8')
555 stderr = stderr.decode('utf-8')
556 - assert re.match('^Woke up, sleep function is: <.*?sleep.*?>$', stdout), repr(stdout)
557 - stderr = re.sub(r"^\[\d+ refs\]", "", stderr, re.MULTILINE).strip()
561 + 'Woke up, sleep function is: gevent.hub.sleep',
564 # On Python 2, importing pkg_resources tends to result in some 'ImportWarning'
565 # being printed to stderr about packages missing __init__.py; the -W ignore is...
567 @@ -410,7 +416,7 @@ class ThreadTests(unittest.TestCase):
568 self.should_raise = should_raise
569 self.thread = threading.Thread(target=self._run,
571 - kwargs={'yet_another': self})
572 + kwargs={'_yet_another': self})
575 def _run(self, _other_ref, _yet_another):
576 @@ -463,7 +469,7 @@ class ThreadJoinOnShutdown(unittest.Test
577 t = threading.Thread(target=joiningfunc,
578 args=(threading.current_thread(),))
584 self._run_and_join(script)
585 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/thread.py gevent-1.4.0/src/gevent/thread.py
586 --- gevent-1.4.0.org/src/gevent/thread.py 2019-01-04 12:51:44.000000000 +0100
587 +++ gevent-1.4.0/src/gevent/thread.py 2021-03-05 09:44:15.302384451 +0100
588 @@ -31,6 +31,8 @@ else:
592 + if sys.version_info[:2] >= (3, 8):
593 + __imports__.append('get_native_id')
594 error = __thread__.error
595 from gevent._compat import PY3
596 from gevent._compat import PYPY
597 diff -urNp -x '*.orig' gevent-1.4.0.org/src/gevent/threading.py gevent-1.4.0/src/gevent/threading.py
598 --- gevent-1.4.0.org/src/gevent/threading.py 2019-01-04 12:51:44.000000000 +0100
599 +++ gevent-1.4.0/src/gevent/threading.py 2021-03-05 09:44:15.299051013 +0100
600 @@ -156,41 +156,25 @@ import sys
601 if sys.version_info[:2] >= (3, 4):
602 # XXX: Issue 18808 breaks us on Python 3.4.
603 # Thread objects now expect a callback from the interpreter itself
604 - # (threadmodule.c:release_sentinel). Because this never happens
605 + # (threadmodule.c:release_sentinel) when the C-level PyThreadState
606 + # object is being deallocated. Because this never happens
607 # when a greenlet exits, join() and friends will block forever.
608 - # The solution below involves capturing the greenlet when it is
609 - # started and deferring the known broken methods to it.
610 + # Fortunately this is easy to fix: just ensure that the allocation of the
611 + # lock, _set_sentinel, creates a *gevent* lock, and release it when
612 + # we're done. The main _shutdown code is in Python and deals with
615 class Thread(__threading__.Thread):
618 - def is_alive(self):
619 - return bool(self._greenlet)
623 def _set_tstate_lock(self):
624 - self._greenlet = getcurrent()
628 - super(Thread, self).run()
630 - # avoid ref cycles, but keep in __dict__ so we can
631 - # distinguish the started/never-started case
632 - self._greenlet = None
633 - self._stop() # mark as finished
635 - def join(self, timeout=None):
636 - if '_greenlet' not in self.__dict__:
637 - raise RuntimeError("Cannot join an inactive thread")
638 - if self._greenlet is None:
640 - self._greenlet.join(timeout=timeout)
642 - def _wait_for_tstate_lock(self, *args, **kwargs):
643 - # pylint:disable=arguments-differ
644 - raise NotImplementedError()
645 + super(Thread, self)._set_tstate_lock()
646 + greenlet = getcurrent()
647 + greenlet.rawlink(self.__greenlet_finished)
649 + def __greenlet_finished(self, _):
650 + if self._tstate_lock:
651 + self._tstate_lock.release()
654 __implements__.append('Thread')
656 @@ -199,6 +183,8 @@ if sys.version_info[:2] >= (3, 4):
658 __implements__.append('Timer')
660 + _set_sentinel = allocate_lock
661 + __implements__.append('_set_sentinel')
662 # The main thread is patched up with more care
663 # in _gevent_will_monkey_patch