37
37
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
38
# SOFTWARE.
39
39
import random
40
+ import re
41
+ import threading
40
42
import unittest
41
43
from test import support
42
44
45
+ from _thread import start_new_thread
46
+
43
47
thread = support .import_module ('_thread' )
44
48
import time
45
49
import weakref
46
50
47
- from test import lock_tests
48
51
49
52
NUMTASKS = 10
50
53
NUMTRIPS = 3
51
54
POLL_SLEEP = 0.010 # seconds = 10 ms
52
55
53
56
_print_mutex = thread .allocate_lock ()
54
57
58
+ support .verbose = False
59
+
55
60
56
61
def verbose_print (arg ):
57
62
"""Helper function for printing out debugging output."""
@@ -71,8 +76,10 @@ def setUp(self):
71
76
self .running = 0
72
77
self .next_ident = 0
73
78
74
- key = support .threading_setup ()
75
- self .addCleanup (support .threading_cleanup , * key )
79
+ self ._threads = support .threading_setup ()
80
+
81
+ def tearDown (self ):
82
+ support .threading_cleanup (* self ._threads )
76
83
77
84
78
85
class ThreadRunningTests (BasicThreadTest ):
@@ -111,32 +118,32 @@ def test_stack_size(self):
111
118
thread .stack_size (0 )
112
119
self .assertEqual (thread .stack_size (), 0 , "stack_size not reset to default" )
113
120
114
- def test__count (self ):
115
- # Test the _count() function.
116
- orig = thread ._count ()
117
- mut = thread .allocate_lock ()
118
- mut .acquire ()
119
- started = []
120
-
121
- def task ():
122
- started .append (None )
123
- mut .acquire ()
124
- mut .release ()
125
- thread .start_new_thread (task , ())
126
- while not started :
127
- time .sleep (POLL_SLEEP )
128
- self .assertEqual (thread ._count (), orig + 1 )
129
- # Allow the task to finish.
130
- mut .release ()
131
- # The only reliable way to be sure that the thread ended from the
132
- # interpreter's point of view is to wait for the function object to be
133
- # destroyed.
134
- done = []
135
- wr = weakref .ref (task , lambda _ : done .append (None ))
136
- del task
137
- while not done :
138
- time .sleep (POLL_SLEEP )
139
- self .assertEqual (thread ._count (), orig )
121
+ # def test__count(self):
122
+ # # Test the _count() function.
123
+ # orig = thread._count()
124
+ # mut = thread.allocate_lock()
125
+ # mut.acquire()
126
+ # started = []
127
+ #
128
+ # def task():
129
+ # started.append(None)
130
+ # mut.acquire()
131
+ # mut.release()
132
+ # thread.start_new_thread(task, ())
133
+ # while not started:
134
+ # time.sleep(POLL_SLEEP)
135
+ # self.assertEqual(thread._count(), orig + 1)
136
+ # # Allow the task to finish.
137
+ # mut.release()
138
+ # # The only reliable way to be sure that the thread ended from the
139
+ # # interpreter's point of view is to wait for the function object to be
140
+ # # destroyed.
141
+ # done = []
142
+ # wr = weakref.ref(task, lambda _: done.append(None))
143
+ # del task
144
+ # while not done:
145
+ # time.sleep(POLL_SLEEP)
146
+ # self.assertEqual(thread._count(), orig)
140
147
141
148
# def test_save_exception_state_on_error(self):
142
149
# # See issue #14474
@@ -225,5 +232,256 @@ def task2(self, ident):
225
232
self .done_mutex .release ()
226
233
227
234
228
- class LockTests (lock_tests .LockTests ):
235
+ def _wait ():
236
+ # A crude wait/yield function not relying on synchronization primitives.
237
+ time .sleep (0.01 )
238
+
239
+
240
+ class Bunch (object ):
241
+ """
242
+ A bunch of threads.
243
+ """
244
+ def __init__ (self , f , n , wait_before_exit = False ):
245
+ """
246
+ Construct a bunch of `n` threads running the same function `f`.
247
+ If `wait_before_exit` is True, the threads won't terminate until
248
+ do_finish() is called.
249
+ """
250
+ self .f = f
251
+ self .n = n
252
+ self .started = []
253
+ self .finished = []
254
+ self ._can_exit = not wait_before_exit
255
+
256
+ def task ():
257
+ tid = threading .get_ident ()
258
+ self .started .append (tid )
259
+ try :
260
+ f ()
261
+ finally :
262
+ self .finished .append (tid )
263
+ while not self ._can_exit :
264
+ _wait ()
265
+ try :
266
+ for i in range (n ):
267
+ start_new_thread (task , ())
268
+ except :
269
+ self ._can_exit = True
270
+ raise
271
+
272
+ def wait_for_started (self ):
273
+ while len (self .started ) < self .n :
274
+ _wait ()
275
+
276
+ def wait_for_finished (self ):
277
+ while len (self .finished ) < self .n :
278
+ _wait ()
279
+
280
+ def do_finish (self ):
281
+ self ._can_exit = True
282
+
283
+
284
+ class BaseTestCase (unittest .TestCase ):
285
+ def setUp (self ):
286
+ self ._threads = support .threading_setup ()
287
+
288
+ def tearDown (self ):
289
+ support .threading_cleanup (* self ._threads )
290
+ support .reap_children ()
291
+
292
+ def assertTimeout (self , actual , expected ):
293
+ # The waiting and/or time.time() can be imprecise, which
294
+ # is why comparing to the expected value would sometimes fail
295
+ # (especially under Windows).
296
+ self .assertGreaterEqual (actual , expected * 0.6 )
297
+ # Test nothing insane happened
298
+ self .assertLess (actual , expected * 10.0 )
299
+
300
+ def assertRegexpMatches (self , text , expected_regexp , msg = None ):
301
+ """Fail the test unless the text matches the regular expression."""
302
+ if isinstance (expected_regexp , str ):
303
+ expected_regexp = re .compile (expected_regexp )
304
+ if not expected_regexp .search (text ):
305
+ msg = msg or "Regexp didn't match"
306
+ msg = '%s: %r not found in %r' % (msg , expected_regexp .pattern , text )
307
+ raise self .failureException (msg )
308
+
309
+
310
+ class LockTests (BaseTestCase ):
229
311
locktype = thread .allocate_lock
312
+
313
+ def test_constructor (self ):
314
+ lock = self .locktype ()
315
+ del lock
316
+
317
+ def test_repr (self ):
318
+ lock = self .locktype ()
319
+ self .assertRegexpMatches (repr (lock ), "<unlocked .* object (.*)?at .*>" )
320
+ del lock
321
+
322
+ def test_locked_repr (self ):
323
+ lock = self .locktype ()
324
+ lock .acquire ()
325
+ self .assertRegexpMatches (repr (lock ), "<locked .* object (.*)?at .*>" )
326
+ del lock
327
+
328
+ def test_acquire_destroy (self ):
329
+ lock = self .locktype ()
330
+ lock .acquire ()
331
+ del lock
332
+
333
+ def test_acquire_release (self ):
334
+ lock = self .locktype ()
335
+ lock .acquire ()
336
+ lock .release ()
337
+ del lock
338
+
339
+ def test_try_acquire (self ):
340
+ lock = self .locktype ()
341
+ self .assertTrue (lock .acquire (False ))
342
+ lock .release ()
343
+
344
+ def test_try_acquire_contended (self ):
345
+ lock = self .locktype ()
346
+ lock .acquire ()
347
+ result = []
348
+
349
+ def f ():
350
+ result .append (lock .acquire (False ))
351
+ Bunch (f , 1 ).wait_for_finished ()
352
+ self .assertFalse (result [0 ])
353
+ lock .release ()
354
+
355
+ # def test_acquire_contended(self):
356
+ # lock = self.locktype()
357
+ # lock.acquire()
358
+ # N = 5
359
+ #
360
+ # def f():
361
+ # lock.acquire()
362
+ # lock.release()
363
+ #
364
+ # b = Bunch(f, N)
365
+ # b.wait_for_started()
366
+ # _wait()
367
+ # self.assertEqual(len(b.finished), 0)
368
+ # lock.release()
369
+ # b.wait_for_finished()
370
+ # self.assertEqual(len(b.finished), N)
371
+
372
+ def test_with (self ):
373
+ lock = self .locktype ()
374
+ def f ():
375
+ lock .acquire ()
376
+ lock .release ()
377
+ def _with (err = None ):
378
+ with lock :
379
+ if err is not None :
380
+ raise err
381
+ _with ()
382
+ # Check the lock is unacquired
383
+ Bunch (f , 1 ).wait_for_finished ()
384
+ self .assertRaises (TypeError , _with , TypeError )
385
+ # Check the lock is unacquired
386
+ Bunch (f , 1 ).wait_for_finished ()
387
+
388
+ def test_thread_leak (self ):
389
+ # The lock shouldn't leak a Thread instance when used from a foreign
390
+ # (non-threading) thread.
391
+ lock = self .locktype ()
392
+
393
+ def f ():
394
+ lock .acquire ()
395
+ lock .release ()
396
+ n = len (threading .enumerate ())
397
+ # We run many threads in the hope that existing threads ids won't
398
+ # be recycled.
399
+ Bunch (f , 15 ).wait_for_finished ()
400
+ if len (threading .enumerate ()) != n :
401
+ # There is a small window during which a Thread instance's
402
+ # target function has finished running, but the Thread is still
403
+ # alive and registered. Avoid spurious failures by waiting a
404
+ # bit more (seen on a buildbot).
405
+ time .sleep (0.4 )
406
+ self .assertEqual (n , len (threading .enumerate ()))
407
+
408
+ # def test_timeout(self):
409
+ # lock = self.locktype()
410
+ # # Can't set timeout if not blocking
411
+ # self.assertRaises(ValueError, lock.acquire, 0, 1)
412
+ # # Invalid timeout values
413
+ # self.assertRaises(ValueError, lock.acquire, timeout=-100)
414
+ # self.assertRaises(OverflowError, lock.acquire, timeout=1e100)
415
+ # self.assertRaises(OverflowError, lock.acquire, timeout=TIMEOUT_MAX + 1)
416
+ # # TIMEOUT_MAX is ok
417
+ # lock.acquire(timeout=TIMEOUT_MAX)
418
+ # lock.release()
419
+ # t1 = time.time()
420
+ # self.assertTrue(lock.acquire(timeout=5))
421
+ # t2 = time.time()
422
+ # # Just a sanity test that it didn't actually wait for the timeout.
423
+ # self.assertLess(t2 - t1, 5)
424
+ # results = []
425
+ #
426
+ # def f():
427
+ # t1 = time.time()
428
+ # results.append(lock.acquire(timeout=0.5))
429
+ # t2 = time.time()
430
+ # results.append(t2 - t1)
431
+ # Bunch(f, 1).wait_for_finished()
432
+ # self.assertFalse(results[0])
433
+ # self.assertTimeout(results[1], 0.5)
434
+
435
+ def test_weakref_exists (self ):
436
+ lock = self .locktype ()
437
+ ref = weakref .ref (lock )
438
+ self .assertTrue (ref () is not None )
439
+
440
+ # weakrefs are not yet full functional
441
+ # def test_weakref_deleted(self):
442
+ # lock = self.locktype()
443
+ # ref = weakref.ref(lock)
444
+ # del lock
445
+ # self.assertIsNone(ref())
446
+
447
+ def test_reacquire (self ):
448
+ # Lock needs to be released before re-acquiring.
449
+ lock = self .locktype ()
450
+ phase = []
451
+
452
+ def f ():
453
+ lock .acquire ()
454
+ phase .append (None )
455
+ lock .acquire ()
456
+ phase .append (None )
457
+ start_new_thread (f , ())
458
+ while len (phase ) == 0 :
459
+ _wait ()
460
+ _wait ()
461
+ self .assertEqual (len (phase ), 1 )
462
+ lock .release ()
463
+ while len (phase ) == 1 :
464
+ _wait ()
465
+ self .assertEqual (len (phase ), 2 )
466
+
467
+ def test_different_thread (self ):
468
+ # Lock can be released from a different thread.
469
+ lock = self .locktype ()
470
+ lock .acquire ()
471
+
472
+ def f ():
473
+ lock .release ()
474
+ b = Bunch (f , 1 )
475
+ b .wait_for_finished ()
476
+ lock .acquire ()
477
+ lock .release ()
478
+
479
+ def test_state_after_timeout (self ):
480
+ # Issue #11618: check that lock is in a proper state after a
481
+ # (non-zero) timeout.
482
+ lock = self .locktype ()
483
+ lock .acquire ()
484
+ self .assertFalse (lock .acquire (timeout = 0.01 ))
485
+ lock .release ()
486
+ self .assertFalse (lock .locked ())
487
+ self .assertTrue (lock .acquire (blocking = False ))
0 commit comments