higgs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +208 -0
- data/LICENSE +26 -0
- data/README +2 -0
- data/Rakefile +75 -0
- data/bin/higgs_backup +67 -0
- data/bin/higgs_dump_index +43 -0
- data/bin/higgs_dump_jlog +42 -0
- data/bin/higgs_verify +37 -0
- data/lib/cgi/session/higgs.rb +72 -0
- data/lib/higgs/block.rb +192 -0
- data/lib/higgs/cache.rb +117 -0
- data/lib/higgs/dbm.rb +55 -0
- data/lib/higgs/exceptions.rb +31 -0
- data/lib/higgs/flock.rb +77 -0
- data/lib/higgs/index.rb +164 -0
- data/lib/higgs/jlog.rb +159 -0
- data/lib/higgs/lock.rb +189 -0
- data/lib/higgs/storage.rb +1086 -0
- data/lib/higgs/store.rb +228 -0
- data/lib/higgs/tar.rb +390 -0
- data/lib/higgs/thread.rb +370 -0
- data/lib/higgs/tman.rb +513 -0
- data/lib/higgs/utils/bman.rb +285 -0
- data/lib/higgs/utils.rb +22 -0
- data/lib/higgs/version.rb +21 -0
- data/lib/higgs.rb +59 -0
- data/misc/cache_bench/cache_bench.rb +43 -0
- data/misc/dbm_bench/.strc +8 -0
- data/misc/dbm_bench/Rakefile +78 -0
- data/misc/dbm_bench/dbm_multi_thread.rb +199 -0
- data/misc/dbm_bench/dbm_rnd_delete.rb +43 -0
- data/misc/dbm_bench/dbm_rnd_read.rb +44 -0
- data/misc/dbm_bench/dbm_rnd_update.rb +44 -0
- data/misc/dbm_bench/dbm_seq_read.rb +45 -0
- data/misc/dbm_bench/dbm_seq_write.rb +44 -0
- data/misc/dbm_bench/st_verify.rb +28 -0
- data/misc/io_bench/cksum_bench.rb +48 -0
- data/misc/io_bench/jlog_bench.rb +71 -0
- data/misc/io_bench/write_bench.rb +128 -0
- data/misc/thread_bench/lock_bench.rb +132 -0
- data/mkrdoc.rb +8 -0
- data/rdoc.yml +13 -0
- data/sample/count.rb +60 -0
- data/sample/dbmtest.rb +38 -0
- data/test/Rakefile +45 -0
- data/test/run.rb +32 -0
- data/test/test_block.rb +163 -0
- data/test/test_cache.rb +214 -0
- data/test/test_cgi_session.rb +142 -0
- data/test/test_flock.rb +162 -0
- data/test/test_index.rb +258 -0
- data/test/test_jlog.rb +180 -0
- data/test/test_lock.rb +320 -0
- data/test/test_online_backup.rb +169 -0
- data/test/test_storage.rb +439 -0
- data/test/test_storage_conf.rb +202 -0
- data/test/test_storage_init_opts.rb +89 -0
- data/test/test_store.rb +211 -0
- data/test/test_tar.rb +432 -0
- data/test/test_thread.rb +541 -0
- data/test/test_tman.rb +875 -0
- data/test/test_tman_init_opts.rb +56 -0
- data/test/test_utils_bman.rb +234 -0
- metadata +115 -0
data/test/test_thread.rb
ADDED
@@ -0,0 +1,541 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
|
3
|
+
require 'higgs/thread'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
Thread.abort_on_exception = true if $DEBUG
|
8
|
+
|
9
|
+
module Higgs::Test
|
10
|
+
module ThreadParams
|
11
|
+
COUNT_OF_THREADS = (ENV['THREADS'] || '10').to_i
|
12
|
+
WORK_COUNT = (ENV['WORK'] || '100').to_i
|
13
|
+
DELTA_T = (ENV['DELTA_T'] || '0.1').to_f
|
14
|
+
|
15
|
+
if ($DEBUG) then
|
16
|
+
puts 'thread test parameters...'
|
17
|
+
for name in constants
|
18
|
+
puts "#{name} = #{const_get(name)}"
|
19
|
+
end
|
20
|
+
puts ''
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class LatchTest < Test::Unit::TestCase
|
25
|
+
include Higgs
|
26
|
+
include ThreadParams
|
27
|
+
include Timeout
|
28
|
+
|
29
|
+
# for ident(1)
|
30
|
+
CVS_ID = '$Id: test_thread.rb 559 2007-09-25 15:20:20Z toki $'
|
31
|
+
|
32
|
+
def test_start_wait
|
33
|
+
latch = Latch.new
|
34
|
+
|
35
|
+
lock = Mutex.new
|
36
|
+
count = 0
|
37
|
+
th_grp = ThreadGroup.new
|
38
|
+
COUNT_OF_THREADS.times do
|
39
|
+
th_grp.add Thread.new{
|
40
|
+
latch.wait
|
41
|
+
lock.synchronize{ count += 1 }
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
sleep(DELTA_T)
|
46
|
+
assert_equal(0, lock.synchronize{ count })
|
47
|
+
|
48
|
+
latch.start
|
49
|
+
timeout(10) {
|
50
|
+
for t in th_grp.list
|
51
|
+
t.join
|
52
|
+
end
|
53
|
+
}
|
54
|
+
assert_equal(COUNT_OF_THREADS, lock.synchronize{ count })
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class CountDownLatchTest < Test::Unit::TestCase
|
59
|
+
include Higgs
|
60
|
+
include ThreadParams
|
61
|
+
include Timeout
|
62
|
+
|
63
|
+
# for ident(1)
|
64
|
+
CVS_ID = '$Id: test_thread.rb 559 2007-09-25 15:20:20Z toki $'
|
65
|
+
|
66
|
+
def test_count_down_wait
|
67
|
+
latch = CountDownLatch.new(3)
|
68
|
+
|
69
|
+
lock = Mutex.new
|
70
|
+
count = 0
|
71
|
+
th_grp = ThreadGroup.new
|
72
|
+
COUNT_OF_THREADS.times do
|
73
|
+
th_grp.add Thread.new{
|
74
|
+
latch.wait
|
75
|
+
lock.synchronize{ count += 1 }
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
sleep(DELTA_T)
|
80
|
+
assert_equal(0, lock.synchronize{ count })
|
81
|
+
|
82
|
+
latch.count_down
|
83
|
+
sleep(DELTA_T)
|
84
|
+
assert_equal(0, lock.synchronize{ count })
|
85
|
+
|
86
|
+
latch.count_down
|
87
|
+
sleep(DELTA_T)
|
88
|
+
assert_equal(0, lock.synchronize{ count })
|
89
|
+
|
90
|
+
latch.count_down
|
91
|
+
timeout(10) {
|
92
|
+
for t in th_grp.list
|
93
|
+
t.join
|
94
|
+
end
|
95
|
+
}
|
96
|
+
assert_equal(COUNT_OF_THREADS, lock.synchronize{ count })
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class BarrierTest < Test::Unit::TestCase
|
101
|
+
include Higgs
|
102
|
+
include ThreadParams
|
103
|
+
include Timeout
|
104
|
+
|
105
|
+
# for ident(1)
|
106
|
+
CVS_ID = '$Id: test_thread.rb 559 2007-09-25 15:20:20Z toki $'
|
107
|
+
|
108
|
+
def test_wait
|
109
|
+
barrier = Barrier.new(COUNT_OF_THREADS)
|
110
|
+
|
111
|
+
lock = Mutex.new
|
112
|
+
count = 0
|
113
|
+
th_new = proc{
|
114
|
+
Thread.new{
|
115
|
+
barrier.wait
|
116
|
+
lock.synchronize{ count += 1 }
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
th_grp = ThreadGroup.new
|
121
|
+
(COUNT_OF_THREADS - 1).times do
|
122
|
+
th_grp.add(th_new.call)
|
123
|
+
end
|
124
|
+
|
125
|
+
sleep(DELTA_T)
|
126
|
+
assert_equal(0, lock.synchronize{ count })
|
127
|
+
|
128
|
+
th_grp.add(th_new.call)
|
129
|
+
timeout(10) {
|
130
|
+
for t in th_grp.list
|
131
|
+
t.join
|
132
|
+
end
|
133
|
+
}
|
134
|
+
assert_equal(COUNT_OF_THREADS, lock.synchronize{ count })
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_not_recycle
|
138
|
+
barrier = Barrier.new(1)
|
139
|
+
barrier.wait
|
140
|
+
assert_raise(RuntimeError) { barrier.wait }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class SharedWorkTest < Test::Unit::TestCase
|
145
|
+
include Higgs
|
146
|
+
include ThreadParams
|
147
|
+
include Timeout
|
148
|
+
|
149
|
+
# for ident(1)
|
150
|
+
CVS_ID = '$Id: test_thread.rb 559 2007-09-25 15:20:20Z toki $'
|
151
|
+
|
152
|
+
def calc
|
153
|
+
@s = 0 # @s's scope is over multi-threading
|
154
|
+
for i in 1..WORK_COUNT
|
155
|
+
@s += i
|
156
|
+
end
|
157
|
+
@s
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_calc_single_thread
|
161
|
+
a = calc
|
162
|
+
b = calc
|
163
|
+
assert_equal(a, b)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_calc_race_condition
|
167
|
+
a = nil
|
168
|
+
b = nil
|
169
|
+
begin
|
170
|
+
barrier = Barrier.new(3)
|
171
|
+
|
172
|
+
th1 = Thread.new{
|
173
|
+
barrier.wait
|
174
|
+
a = calc
|
175
|
+
}
|
176
|
+
|
177
|
+
th2 = Thread.new{
|
178
|
+
barrier.wait
|
179
|
+
b = calc
|
180
|
+
}
|
181
|
+
|
182
|
+
barrier.wait
|
183
|
+
th1.join
|
184
|
+
th2.join
|
185
|
+
end until (a != b) # race condition
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_result
|
189
|
+
expected_result = calc
|
190
|
+
|
191
|
+
latch = Latch.new
|
192
|
+
work = SharedWork.new{
|
193
|
+
latch.wait
|
194
|
+
calc
|
195
|
+
}
|
196
|
+
|
197
|
+
barrier = Barrier.new(COUNT_OF_THREADS + 1)
|
198
|
+
lock = Mutex.new
|
199
|
+
count = 0
|
200
|
+
|
201
|
+
th_grp = ThreadGroup.new
|
202
|
+
COUNT_OF_THREADS.times{|i| # `i' should be local scope of thread block
|
203
|
+
th_grp.add Thread.new{
|
204
|
+
barrier.wait
|
205
|
+
assert_equal(expected_result, work.result, "th#{i}")
|
206
|
+
lock.synchronize{ count += 1 }
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
barrier.wait
|
211
|
+
assert_equal(0, lock.synchronize{ count })
|
212
|
+
|
213
|
+
latch.start
|
214
|
+
timeout(10) {
|
215
|
+
for t in th_grp.list
|
216
|
+
t.join
|
217
|
+
end
|
218
|
+
}
|
219
|
+
assert_equal(COUNT_OF_THREADS, lock.synchronize{ count })
|
220
|
+
assert_equal(expected_result, work.result)
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_no_work_block
|
224
|
+
assert_raise(RuntimeError) { SharedWork.new }
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_set_result
|
228
|
+
work = SharedWork.new{ :foo }
|
229
|
+
work.result = :bar
|
230
|
+
assert_equal(:bar, work.result)
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_set_result_after_work
|
234
|
+
work = SharedWork.new{ :foo }
|
235
|
+
assert_equal(:foo, work.result)
|
236
|
+
work.result = :bar
|
237
|
+
assert_equal(:bar, work.result)
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_set_result_after_work_multithread
|
241
|
+
barrier = Barrier.new(3)
|
242
|
+
|
243
|
+
work = SharedWork.new{
|
244
|
+
barrier.wait
|
245
|
+
sleep(DELTA_T)
|
246
|
+
:foo
|
247
|
+
}
|
248
|
+
|
249
|
+
th_set_result = Thread.new{
|
250
|
+
barrier.wait
|
251
|
+
work.result = :bar
|
252
|
+
}
|
253
|
+
|
254
|
+
th_work = Thread.new{
|
255
|
+
assert_equal(:foo, work.result)
|
256
|
+
}
|
257
|
+
|
258
|
+
barrier.wait
|
259
|
+
th_set_result.join
|
260
|
+
th_work.join
|
261
|
+
|
262
|
+
assert_equal(:bar, work.result)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
class ReadWriteLockTest < Test::Unit::TestCase
|
267
|
+
include Higgs
|
268
|
+
include ThreadParams
|
269
|
+
|
270
|
+
# for ident(1)
|
271
|
+
CVS_ID = '$Id: test_thread.rb 559 2007-09-25 15:20:20Z toki $'
|
272
|
+
|
273
|
+
def setup
|
274
|
+
@rw_lock = ReadWriteLock.new
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_read_lock_single_thread
|
278
|
+
v = "foo"
|
279
|
+
r_lock = @rw_lock.read_lock
|
280
|
+
WORK_COUNT.times do
|
281
|
+
assert_equal("foo", r_lock.synchronize{ v })
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_write_lock_single_thread
|
286
|
+
count = 0
|
287
|
+
w_lock = @rw_lock.write_lock
|
288
|
+
WORK_COUNT.times do
|
289
|
+
w_lock.synchronize{
|
290
|
+
count += 1
|
291
|
+
}
|
292
|
+
end
|
293
|
+
assert_equal(WORK_COUNT, count)
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_read_lock_multithread
|
297
|
+
v = "foo"
|
298
|
+
th_grp = ThreadGroup.new
|
299
|
+
barrier = Barrier.new(COUNT_OF_THREADS + 1)
|
300
|
+
|
301
|
+
COUNT_OF_THREADS.times{|i| # `i' should be local scope of thread block
|
302
|
+
th_grp.add Thread.new{
|
303
|
+
r_lock = @rw_lock.read_lock
|
304
|
+
r_lock.synchronize{
|
305
|
+
barrier.wait
|
306
|
+
WORK_COUNT.times do |j|
|
307
|
+
assert_equal("foo", v, "read_lock: #{i}.#{j}")
|
308
|
+
end
|
309
|
+
}
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
barrier.wait
|
314
|
+
for t in th_grp.list
|
315
|
+
t.join
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def test_write_lock_multithread
|
320
|
+
count = 0
|
321
|
+
th_grp = ThreadGroup.new
|
322
|
+
barrier = Barrier.new(COUNT_OF_THREADS + 1)
|
323
|
+
|
324
|
+
COUNT_OF_THREADS.times do
|
325
|
+
th_grp.add Thread.new{
|
326
|
+
w_lock = @rw_lock.write_lock
|
327
|
+
barrier.wait
|
328
|
+
WORK_COUNT.times do
|
329
|
+
w_lock.synchronize{
|
330
|
+
count += 1
|
331
|
+
}
|
332
|
+
end
|
333
|
+
}
|
334
|
+
end
|
335
|
+
|
336
|
+
barrier.wait
|
337
|
+
for t in th_grp.list
|
338
|
+
t.join
|
339
|
+
end
|
340
|
+
assert_equal(COUNT_OF_THREADS * WORK_COUNT, count)
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_read_write_lock_multithread
|
344
|
+
count = 0
|
345
|
+
th_grp = ThreadGroup.new
|
346
|
+
barrier = Barrier.new(COUNT_OF_THREADS * 2 + 1)
|
347
|
+
|
348
|
+
COUNT_OF_THREADS.times{|i| # `i' should be local scope of thread block
|
349
|
+
th_grp.add Thread.new{
|
350
|
+
r_lock = @rw_lock.read_lock
|
351
|
+
r_lock.synchronize{
|
352
|
+
barrier.wait
|
353
|
+
WORK_COUNT.times do |j|
|
354
|
+
assert_equal(0, count, "read_lock: #{i}.#{j}")
|
355
|
+
end
|
356
|
+
}
|
357
|
+
}
|
358
|
+
}
|
359
|
+
|
360
|
+
COUNT_OF_THREADS.times do
|
361
|
+
th_grp.add Thread.new{
|
362
|
+
w_lock = @rw_lock.write_lock
|
363
|
+
barrier.wait
|
364
|
+
WORK_COUNT.times do
|
365
|
+
w_lock.synchronize{
|
366
|
+
count += 1
|
367
|
+
}
|
368
|
+
end
|
369
|
+
}
|
370
|
+
end
|
371
|
+
|
372
|
+
barrier.wait
|
373
|
+
for t in th_grp.list
|
374
|
+
t.join
|
375
|
+
end
|
376
|
+
assert_equal(COUNT_OF_THREADS * WORK_COUNT, count)
|
377
|
+
end
|
378
|
+
|
379
|
+
def test_write_read_lock_multithread
|
380
|
+
count = 0
|
381
|
+
th_grp = ThreadGroup.new
|
382
|
+
barrier = Barrier.new(COUNT_OF_THREADS + 2)
|
383
|
+
|
384
|
+
th_grp.add Thread.new{
|
385
|
+
w_lock = @rw_lock.write_lock
|
386
|
+
w_lock.synchronize{
|
387
|
+
barrier.wait
|
388
|
+
COUNT_OF_THREADS.times do
|
389
|
+
WORK_COUNT.times do
|
390
|
+
count += 1
|
391
|
+
end
|
392
|
+
end
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
COUNT_OF_THREADS.times{|i| # `i' should be local scope of thread block
|
397
|
+
th_grp.add Thread.new{
|
398
|
+
r_lock = @rw_lock.read_lock
|
399
|
+
barrier.wait
|
400
|
+
r_lock.synchronize{
|
401
|
+
assert_equal(COUNT_OF_THREADS * WORK_COUNT, count, "read_lock: #{i}")
|
402
|
+
}
|
403
|
+
}
|
404
|
+
}
|
405
|
+
|
406
|
+
barrier.wait
|
407
|
+
for t in th_grp.list
|
408
|
+
t.join
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def test_read_write_race
|
413
|
+
count = 0
|
414
|
+
value = true
|
415
|
+
th_grp = ThreadGroup.new
|
416
|
+
barrier = Barrier.new(COUNT_OF_THREADS + 2)
|
417
|
+
|
418
|
+
COUNT_OF_THREADS.times{|i| # `i' should be local scope of thread block
|
419
|
+
th_grp.add Thread.new{
|
420
|
+
r_lock = @rw_lock.read_lock
|
421
|
+
barrier.wait
|
422
|
+
WORK_COUNT.times do
|
423
|
+
r_lock.synchronize{
|
424
|
+
p "#{i}: #{count}" if $DEBUG
|
425
|
+
assert_equal(true, value, "read_lock: #{i}")
|
426
|
+
}
|
427
|
+
end
|
428
|
+
}
|
429
|
+
}
|
430
|
+
|
431
|
+
th_grp.add Thread.new{
|
432
|
+
w_lock = @rw_lock.write_lock
|
433
|
+
barrier.wait
|
434
|
+
WORK_COUNT.times do
|
435
|
+
w_lock.synchronize{
|
436
|
+
count += 1
|
437
|
+
value = false
|
438
|
+
value = true
|
439
|
+
}
|
440
|
+
end
|
441
|
+
}
|
442
|
+
|
443
|
+
barrier.wait
|
444
|
+
for t in th_grp.list
|
445
|
+
t.join
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
class PoolTest < Test::Unit::TestCase
|
451
|
+
include Higgs
|
452
|
+
include ThreadParams
|
453
|
+
|
454
|
+
# for ident(1)
|
455
|
+
CVS_ID = '$Id: test_thread.rb 559 2007-09-25 15:20:20Z toki $'
|
456
|
+
|
457
|
+
class Counter
|
458
|
+
def initialize
|
459
|
+
@value = 0
|
460
|
+
end
|
461
|
+
|
462
|
+
attr_reader :value
|
463
|
+
|
464
|
+
def count
|
465
|
+
@value += 1
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
def setup
|
470
|
+
@pool = Pool.new(2) { Counter.new }
|
471
|
+
end
|
472
|
+
|
473
|
+
def test_transaction
|
474
|
+
th_grp = ThreadGroup.new
|
475
|
+
barrier = Barrier.new(COUNT_OF_THREADS + 1)
|
476
|
+
|
477
|
+
COUNT_OF_THREADS.times{|i| # `i' should be local scope of thread block
|
478
|
+
th_grp.add Thread.new{
|
479
|
+
barrier.wait
|
480
|
+
WORK_COUNT.times do |j|
|
481
|
+
@pool.transaction{|c|
|
482
|
+
v = c.value
|
483
|
+
c.count
|
484
|
+
assert_equal(v + 1, c.value, "thread: #{i}.#{j}")
|
485
|
+
}
|
486
|
+
end
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
barrier.wait
|
491
|
+
for t in th_grp.list
|
492
|
+
t.join
|
493
|
+
end
|
494
|
+
|
495
|
+
n = 0
|
496
|
+
s = 0
|
497
|
+
@pool.shutdown{|c|
|
498
|
+
n += 1
|
499
|
+
s += c.value
|
500
|
+
}
|
501
|
+
assert_equal(@pool.size, n)
|
502
|
+
assert_equal(WORK_COUNT * COUNT_OF_THREADS, s)
|
503
|
+
end
|
504
|
+
|
505
|
+
def test_shutdown
|
506
|
+
th_grp = ThreadGroup.new
|
507
|
+
barrier = Barrier.new(COUNT_OF_THREADS + 1)
|
508
|
+
latch = CountDownLatch.new(COUNT_OF_THREADS)
|
509
|
+
|
510
|
+
COUNT_OF_THREADS.times{|i| # `i' should be local scope of thread block
|
511
|
+
th_grp.add Thread.new{
|
512
|
+
barrier.wait
|
513
|
+
assert_raise(Pool::ShutdownException) {
|
514
|
+
j = 0
|
515
|
+
loop do
|
516
|
+
@pool.transaction{|c|
|
517
|
+
v = c.value
|
518
|
+
c.count
|
519
|
+
assert_equal(v + 1, c.value, "thread: #{i}.#{j}")
|
520
|
+
latch.count_down if (j > WORK_COUNT)
|
521
|
+
}
|
522
|
+
j += 1
|
523
|
+
end
|
524
|
+
}
|
525
|
+
}
|
526
|
+
}
|
527
|
+
|
528
|
+
barrier.wait
|
529
|
+
latch.wait
|
530
|
+
@pool.shutdown
|
531
|
+
for t in th_grp.list
|
532
|
+
t.join
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
# Local Variables:
|
539
|
+
# mode: Ruby
|
540
|
+
# indent-tabs-mode: nil
|
541
|
+
# End:
|