|
1637
|
1 # The following self-contained little program usually freezes with most
|
|
|
2 # threads reporting
|
|
|
3 #
|
|
|
4 # Unhandled exception in thread:
|
|
|
5 # Traceback (innermost last):
|
|
|
6 # File "importbug.py", line 6
|
|
|
7 # x = whrandom.randint(1,3)
|
|
|
8 # AttributeError: randint
|
|
|
9 #
|
|
|
10 # Here's the program; it doesn't use anything from the attached module:
|
|
|
11
|
|
|
12 import thread
|
|
|
13
|
|
|
14 def 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
|
|
|
23 a = thread.allocate_lock()
|
|
|
24 done = thread.allocate_lock()
|
|
|
25 N = 10
|
|
|
26
|
|
|
27 done.acquire()
|
|
|
28 for i in range(N):
|
|
|
29 thread.start_new_thread(task, ())
|
|
|
30 done.acquire()
|
|
|
31 print 'done'
|
|
|
32
|
|
|
33
|
|
|
34 # Sticking an acquire/release pair around the 'import' statement makes the
|
|
|
35 # problem go away.
|
|
|
36 #
|
|
|
37 # I believe that what happens is:
|
|
|
38 #
|
|
|
39 # 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
|
|
|
42 #
|
|
|
43 # v = eval_code(co, d, d, d, (object *)NULL);
|
|
|
44 #
|
|
|
45 # to initialize the module.
|
|
|
46 #
|
|
|
47 # 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.
|
|
|
50 #
|
|
|
51 # 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.
|
|
|
54 #
|
|
|
55 # 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.
|
|
|
57 #
|
|
|
58 # 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>.
|
|
|
64 #
|
|
|
65 # as-with-most-other-things-(pseudo-)parallel-programming's-more-fun-
|
|
|
66 # in-python-too!-ly y'rs - tim
|
|
|
67 #
|
|
|
68 # Tim Peters tim@ksr.com
|
|
|
69 # not speaking for Kendall Square Research Corp
|