|
4 | 4 | import gc
|
5 | 5 | import sys
|
6 | 6 | import types
|
| 7 | +from io import BytesIO |
| 8 | +import time |
7 | 9 | try:
|
8 | 10 | import threading
|
9 | 11 | withThreads = True
|
@@ -428,6 +430,64 @@ def test_invalid_args_tasklet_kill(self):
|
428 | 430 | self.assertRaises(TypeError, func, False, None)
|
429 | 431 |
|
430 | 432 |
|
| 433 | +class TestCPickleBombHandling_Dict(dict): |
| 434 | + pass |
| 435 | + |
| 436 | + |
| 437 | +class TestCPickleBombHandling_Cls(object): |
| 438 | + def __getstate__(self): |
| 439 | + try: |
| 440 | + started = self.started |
| 441 | + except AttributeError: |
| 442 | + pass |
| 443 | + else: |
| 444 | + self.started = None |
| 445 | + started.set() |
| 446 | + # print("started") |
| 447 | + time.sleep(0.05) # give the other thread a chance to run |
| 448 | + return self.__dict__ |
| 449 | + |
| 450 | + |
| 451 | +class TestCPickleBombHandling(StacklessTestCase): |
| 452 | + def other_thread(self, pickler, c): |
| 453 | + try: |
| 454 | + pickler.dump(c) |
| 455 | + except TaskletExit: |
| 456 | + self.killed = None |
| 457 | + except: |
| 458 | + self.killed = sys.exc_info() |
| 459 | + else: |
| 460 | + self.killed = False |
| 461 | + |
| 462 | + def test_kill_during_cPickle_stack_switch(self): |
| 463 | + # this test kills the main/current tasklet of a other-thread, |
| 464 | + # which is fast-pickling a recursive structure. This leads to an |
| 465 | + # infinite recursion, which gets interrupted by a bomb thrown from |
| 466 | + # main-thread. Until issue #98 got fixed, this caused a crash. |
| 467 | + # See https://bitbucket.org/stackless-dev/stackless/issues/98 |
| 468 | + buf = BytesIO() |
| 469 | + import _pickle as pickle |
| 470 | + pickler = pickle.Pickler(buf, protocol=-1) |
| 471 | + pickler.fast = 1 |
| 472 | + |
| 473 | + started = threading.Event() |
| 474 | + |
| 475 | + c = TestCPickleBombHandling_Cls() |
| 476 | + c.started = started |
| 477 | + d = TestCPickleBombHandling_Dict() |
| 478 | + d[1] = d |
| 479 | + c.recursive = d |
| 480 | + self.killed = "undefined" |
| 481 | + t = threading.Thread(target=self.other_thread, name="other_thread", args=(pickler, c)) |
| 482 | + t.start() |
| 483 | + started.wait() |
| 484 | + stackless.get_thread_info(t.ident)[0].kill(pending=True) |
| 485 | + # print("killing") |
| 486 | + t.join() |
| 487 | + if isinstance(self.killed, tuple): |
| 488 | + raise (self.killed[0], self.killed[1], self.killed[2]) |
| 489 | + self.assertIsNone(self.killed) |
| 490 | + |
431 | 491 | if __name__ == '__main__':
|
432 | 492 | if not sys.argv[1:]:
|
433 | 493 | sys.argv.append('-v')
|
|
0 commit comments