blob: 6c5edac4bf09b5557a0f0fe3c71417808fcc20fa [file] [log] [blame]
Guido van Rossum48a69b71994-05-16 09:35:22 +00001# The following self-contained little program usually freezes with most
2# threads reporting
Tim Peters172c40b2001-01-21 07:07:30 +00003#
Guido van Rossum48a69b71994-05-16 09:35:22 +00004# Unhandled exception in thread:
5# Traceback (innermost last):
6# File "importbug.py", line 6
7# x = whrandom.randint(1,3)
8# AttributeError: randint
Tim Peters172c40b2001-01-21 07:07:30 +00009#
Guido van Rossum48a69b71994-05-16 09:35:22 +000010# Here's the program; it doesn't use anything from the attached module:
11
12import thread
13
14def task():
15 global N
16 import whrandom
17 x = whrandom.randint(1,3)
18 a.acquire()
19 N = N - 1
20 if N == 0: done.release()
21 a.release()
22
23a = thread.allocate_lock()
24done = thread.allocate_lock()
25N = 10
26
27done.acquire()
28for i in range(N):
29 thread.start_new_thread(task, ())
30done.acquire()
31print 'done'
32
33
34# Sticking an acquire/release pair around the 'import' statement makes the
35# problem go away.
Tim Peters172c40b2001-01-21 07:07:30 +000036#
Guido van Rossum48a69b71994-05-16 09:35:22 +000037# I believe that what happens is:
Tim Peters172c40b2001-01-21 07:07:30 +000038#
Guido van Rossum48a69b71994-05-16 09:35:22 +000039# 1) The first thread to hit the import atomically reaches, and executes
40# most of, get_module. In particular, it finds Lib/whrandom.pyc,
41# installs its name in sys.modules, and executes
Tim Peters172c40b2001-01-21 07:07:30 +000042#
Guido van Rossum48a69b71994-05-16 09:35:22 +000043# v = eval_code(co, d, d, d, (object *)NULL);
Tim Peters172c40b2001-01-21 07:07:30 +000044#
Guido van Rossum48a69b71994-05-16 09:35:22 +000045# to initialize the module.
Tim Peters172c40b2001-01-21 07:07:30 +000046#
Guido van Rossum48a69b71994-05-16 09:35:22 +000047# 2) eval_code "ticker"-slices the 1st thread out, and gives another thread
48# a chance. When this 2nd thread hits the same 'import', import_module
49# finds 'whrandom' in sys.modules, so just proceeds.
Tim Peters172c40b2001-01-21 07:07:30 +000050#
Guido van Rossum48a69b71994-05-16 09:35:22 +000051# 3) But the 1st thread is still "in the middle" of executing whrandom.pyc.
52# So the 2nd thread has a good chance of trying to look up 'randint'
53# before the 1st thread has placed it in whrandom's dict.
Tim Peters172c40b2001-01-21 07:07:30 +000054#
Guido van Rossum48a69b71994-05-16 09:35:22 +000055# 4) The more threads there are, the more likely that at least one of them
56# will do this before the 1st thread finishes the import work.
Tim Peters172c40b2001-01-21 07:07:30 +000057#
Guido van Rossum48a69b71994-05-16 09:35:22 +000058# If that's right, a perhaps not-too-bad workaround would be to introduce a
59# static "you can't interrupt this thread" flag in ceval.c, check it before
60# giving up interpreter_lock, and have IMPORT_NAME set it & restore (plain
61# clearing would not work) it around its call to import_module. To its
62# credit, there's something wonderfully perverse about fixing a race via an
63# unprotected static <grin>.
Tim Peters172c40b2001-01-21 07:07:30 +000064#
Guido van Rossum48a69b71994-05-16 09:35:22 +000065# as-with-most-other-things-(pseudo-)parallel-programming's-more-fun-
66# in-python-too!-ly y'rs - tim
Tim Peters172c40b2001-01-21 07:07:30 +000067#
Guido van Rossum48a69b71994-05-16 09:35:22 +000068# Tim Peters tim@ksr.com
69# not speaking for Kendall Square Research Corp