mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
	
	
		
			70 lines
		
	
	
	
		
			2.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			70 lines
		
	
	
	
		
			2.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | # The following self-contained little program usually freezes with most | ||
|  | # threads reporting | ||
|  | #  | ||
|  | # Unhandled exception in thread: | ||
|  | # Traceback (innermost last): | ||
|  | #   File "importbug.py", line 6 | ||
|  | #     x = whrandom.randint(1,3) | ||
|  | # AttributeError: randint | ||
|  | #  | ||
|  | # Here's the program; it doesn't use anything from the attached module: | ||
|  | 
 | ||
|  | import thread | ||
|  | 
 | ||
|  | def task(): | ||
|  |     global N | ||
|  |     import whrandom | ||
|  |     x = whrandom.randint(1,3) | ||
|  |     a.acquire() | ||
|  |     N = N - 1 | ||
|  |     if N == 0: done.release() | ||
|  |     a.release() | ||
|  | 
 | ||
|  | a = thread.allocate_lock() | ||
|  | done = thread.allocate_lock() | ||
|  | N = 10 | ||
|  | 
 | ||
|  | done.acquire() | ||
|  | for i in range(N): | ||
|  |     thread.start_new_thread(task, ()) | ||
|  | done.acquire() | ||
|  | print 'done' | ||
|  | 
 | ||
|  | 
 | ||
|  | # Sticking an acquire/release pair around the 'import' statement makes the | ||
|  | # problem go away. | ||
|  | #  | ||
|  | # I believe that what happens is: | ||
|  | #  | ||
|  | # 1) The first thread to hit the import atomically reaches, and executes | ||
|  | #    most of, get_module.  In particular, it finds Lib/whrandom.pyc, | ||
|  | #    installs its name in sys.modules, and executes | ||
|  | #  | ||
|  | #         v = eval_code(co, d, d, d, (object *)NULL); | ||
|  | #  | ||
|  | #    to initialize the module. | ||
|  | #  | ||
|  | # 2) eval_code "ticker"-slices the 1st thread out, and gives another thread | ||
|  | #    a chance.  When this 2nd thread hits the same 'import', import_module | ||
|  | #    finds 'whrandom' in sys.modules, so just proceeds. | ||
|  | #  | ||
|  | # 3) But the 1st thread is still "in the middle" of executing whrandom.pyc. | ||
|  | #    So the 2nd thread has a good chance of trying to look up 'randint' | ||
|  | #    before the 1st thread has placed it in whrandom's dict. | ||
|  | #  | ||
|  | # 4) The more threads there are, the more likely that at least one of them | ||
|  | #    will do this before the 1st thread finishes the import work. | ||
|  | #  | ||
|  | # If that's right, a perhaps not-too-bad workaround would be to introduce a | ||
|  | # static "you can't interrupt this thread" flag in ceval.c, check it before | ||
|  | # giving up interpreter_lock, and have IMPORT_NAME set it & restore (plain | ||
|  | # clearing would not work) it around its call to import_module.  To its | ||
|  | # credit, there's something wonderfully perverse about fixing a race via an | ||
|  | # unprotected static <grin>. | ||
|  | #  | ||
|  | # as-with-most-other-things-(pseudo-)parallel-programming's-more-fun- | ||
|  | #    in-python-too!-ly y'rs  - tim | ||
|  | #  | ||
|  | # Tim Peters   tim@ksr.com | ||
|  | # not speaking for Kendall Square Research Corp |