2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "native_client/include/portability.h"
38 # include "native_client/service_runtime/win/condition_variable.h"
39 #elif NACL_LINUX || NACL_OSX
40 # include "native_client/service_runtime/linux/condition_variable.h"
43 #include "gtest/gtest.h"
47 #define SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(tsp, cond) \
48 for (int i =0; i < (tsp).InMilliseconds()/10; i++) \
55 TEST(ConditionVariableTest
, StartupShutdownTest
) {
56 const NaCl::TimeDelta TEN_MS
= NaCl::TimeDelta::FromMilliseconds(10);
59 // First try trivial startup/shutdown.
60 NaCl::ConditionVariable
* cv
= new NaCl::ConditionVariable();
63 // Exercise with at least a few waits.
64 cv
= new NaCl::ConditionVariable();
66 NaCl::AutoLock
auto_lock(lock
);
67 cv
->TimedWaitRel(lock
, TEN_MS
); // Wait for 10 ms.
68 cv
->TimedWaitRel(lock
, TEN_MS
); // Wait for 10 ms.
71 NaCl::AutoLock
auto_lock(lock
);
72 cv
->TimedWaitRel(lock
, TEN_MS
); // Wait for 10 ms.
73 cv
->TimedWaitRel(lock
, TEN_MS
); // Wait for 10 ms.
74 cv
->TimedWaitRel(lock
, TEN_MS
); // Wait for 10 ms.
79 TEST(ConditionVariableTest
, LockedExpressionTest
) {
83 // Old LOCKED_EXPRESSION macro caused syntax errors here.
84 // ... yes... compiler will optimize this example.
85 // Syntax error is what I'm after precluding.
87 LOCKED_EXPRESSION(lock
, i
= 1);
89 LOCKED_EXPRESSION(lock
, i
= 2);
94 TEST(ConditionVariableTest
, TimeoutTest
) {
96 NaCl::ConditionVariable cv
;
99 NaCl::AutoLock
auto_lock(lock
);
100 NaCl::TimeTicks start
= NaCl::TimeTicks::Now();
101 const NaCl::TimeDelta WAIT_TIME
= NaCl::TimeDelta::FromMilliseconds(300);
102 // Allow for clocking rate granularity.
103 const NaCl::TimeDelta FUDGE_TIME
= NaCl::TimeDelta::FromMilliseconds(50);
105 cv
.TimedWaitRel(lock
, WAIT_TIME
+ FUDGE_TIME
);
106 NaCl::TimeDelta duration
= NaCl::TimeTicks::Now() - start
;
107 EXPECT_TRUE(duration
>= WAIT_TIME
);
111 // TODO - make the code below use cross-platform thread functions
113 // Forward declare the worker_process task
114 DWORD WINAPI
worker_process(void* p
);
116 // Caller is responsible for synchronizing access to the following class.
117 // The cs_ member should be used for all synchronized access.
120 explicit WorkQueue(int thread_count
)
121 : thread_count_(thread_count
),
122 shutdown_task_count_(0),
123 assignment_history_(thread_count
),
124 completion_history_(thread_count
),
125 handles_(new HANDLE
[thread_count
]) {
126 EXPECT_GE(thread_count_
, 1);
129 SetWorkTime(NaCl::TimeDelta::FromMilliseconds(30));
130 thread_started_counter_
= 0;
131 allow_help_requests_
= false;
134 for (int i
= 0; i
< thread_count_
; i
++) {
135 handles_
[i
] = CreateThread(NULL
, // security.
136 0, // <64K stack size.
137 worker_process
, // Static function.
138 reinterpret_cast<void*>(this),
139 0, // Create running process.
140 NULL
); // OS version of thread id.
141 EXPECT_TRUE(NULL
!= handles_
[i
]);
147 NaCl::AutoLock
auto_lock(lock_
);
150 work_is_available_
.Broadcast(); // Tell them all to terminate.
152 DWORD result
= WaitForMultipleObjects(
155 true, // Wait for all
156 10000); // Ten seconds max.
158 for (int i
= 0; i
< thread_count_
; i
++) {
159 int ret_value
= CloseHandle(handles_
[i
]);
160 // CHECK(0 != ret_value);
166 // Worker threads only call the following six methods.
167 // They should use the lock to get exclusive access.
169 // DCHECK(!EveryIdWasAllocated());
170 return thread_started_counter_
++; // Give out Unique IDs.
173 bool EveryIdWasAllocated() {
174 return thread_count_
== thread_started_counter_
;
177 NaCl::TimeDelta
GetAnAssignment(int thread_id
) {
178 // DCHECK(0 < task_count_);
179 assignment_history_
[thread_id
]++;
180 if (0 == --task_count_
) {
181 no_more_tasks_
.Signal();
183 return worker_delay_
;
186 void WorkIsCompleted(int thread_id
) {
187 completion_history_
[thread_id
]++;
190 int GetRemainingTaskCount() {return task_count_
;}
191 bool GetAllowHelp() {return allow_help_requests_
;}
192 bool shutdown() {return shutdown_
;}
193 void thread_shutting_down() {shutdown_task_count_
++;}
194 int shutdown_task_count() { return shutdown_task_count_
; }
196 // Both worker threads and controller use the following to synchronize.
198 NaCl::Lock
* lock() { return &lock_
; }
199 NaCl::ConditionVariable work_is_available_
; // To tell threads there is work.
200 NaCl::ConditionVariable
* work_is_available() {
201 return &work_is_available_
;
204 // Conditions to tell the controlling process (if it is interested)
205 NaCl::ConditionVariable all_threads_have_ids_
; // All threads are running.
206 NaCl::ConditionVariable
* all_threads_have_ids() {
207 return &all_threads_have_ids_
;
209 NaCl::ConditionVariable no_more_tasks_
; // Task count is zero.
210 NaCl::ConditionVariable
* no_more_tasks() { return &no_more_tasks_
; }
212 //-------------------------------------------------------------------
213 // The rest of the methods are for the controlling master
215 void ResetHistory() {
216 for (int i
= 0; i
< thread_count_
; i
++) {
217 assignment_history_
[i
] = 0;
218 completion_history_
[i
] = 0;
222 int GetMinCompletionsByWorkerThread() {
223 int min
= completion_history_
[0];
224 for (int i
= 0; i
< thread_count_
; i
++)
225 if (min
> completion_history_
[i
]) min
= completion_history_
[i
];
229 int GetMaxCompletionsByWorkerThread() {
230 int max
= completion_history_
[0];
231 for (int i
= 0; i
< thread_count_
; i
++)
232 if (max
< completion_history_
[i
]) max
= completion_history_
[i
];
236 int GetNumThreadsTakingAssignments() {
238 for (int i
= 0; i
< thread_count_
; i
++)
239 if (assignment_history_
[i
])
244 int GetNumThreadsCompletingTasks() {
246 for (int i
= 0; i
< thread_count_
; i
++)
247 if (completion_history_
[i
])
252 int GetNumberOfCompletedTasks() {
254 for (int i
= 0; i
< thread_count_
; i
++)
255 total
+= completion_history_
[i
];
259 void SetPerformance(bool performance
) { performance_
= performance
; }
260 bool performance() { return performance_
; }
262 void SetWorkTime(NaCl::TimeDelta delay
) { worker_delay_
= delay
; }
263 void SetTaskCount(int count
) { task_count_
= count
; }
264 void SetAllowHelp(bool allow
) { allow_help_requests_
= allow
; }
265 void SetShutdown() { shutdown_
= true; }
268 const int thread_count_
;
270 std::vector
<int> assignment_history_
; // Number of assignment per worker.
271 std::vector
<int> completion_history_
; // Number of completions per worker.
272 int thread_started_counter_
; // Used to issue unique id to workers.
273 int shutdown_task_count_
; // Number of tasks told to shutdown
274 int task_count_
; // Number of assignment tasks waiting to be processed.
275 NaCl::TimeDelta worker_delay_
; // Time each task takes to complete.
276 bool allow_help_requests_
; // Workers can signal more workers.
277 bool shutdown_
; // Set when threads need to terminate.
278 bool performance_
; // performance vs fairness in thread waking and waiting.
281 // The remaining tests involve several threads with a task to perform
282 // as directed in the preceding class WorkQueue.
284 // a) make sure there are more tasks (there is a task counter).
285 // a1) Wait on condition variable if there are no tasks currently.
286 // b) Call a function to see what should be done.
287 // c) Do some computation based on the number of milliseconds returned in (b).
288 // d) go back to (a).
290 // worker_process() implements the above task for all threads.
291 // It calls the controlling object to tell the creator about progress,
292 // and to ask about tasks.
293 DWORD WINAPI
worker_process(void* p
) {
294 NaCl::Lock private_lock
; // Used to waste time on "our work".
297 class WorkQueue
* queue
= reinterpret_cast<WorkQueue
*>(p
);
299 NaCl::AutoLock
auto_lock(*queue
->lock());
300 thread_id
= queue
->GetThreadId();
301 if (queue
->EveryIdWasAllocated())
302 queue
->all_threads_have_ids_
.Signal(); // Tell creator we're ready.
305 while (1) { // This is the main consumer loop.
306 NaCl::TimeDelta work_time
;
309 NaCl::AutoLock
auto_lock(*queue
->lock());
310 if (queue
->performance()) {
311 // Get best performance by not waiting until there is no work.
312 while (0 == queue
->GetRemainingTaskCount() && !queue
->shutdown()) {
313 queue
->work_is_available_
.Wait(*queue
->lock());
316 // Be ridiculously "fair" and try to let other threads go. :-/.
318 queue
->work_is_available_
.Wait(*queue
->lock());
319 } while (0 == queue
->GetRemainingTaskCount() && !queue
->shutdown());
321 if (queue
->shutdown()) {
322 queue
->thread_shutting_down();
323 return 0; // termination time.
325 work_time
= queue
->GetAnAssignment(thread_id
);
326 could_use_help
= (queue
->GetRemainingTaskCount() > 0) &&
327 queue
->GetAllowHelp();
330 // Do work (out of true critical region, consisting of waiting :-).
332 queue
->work_is_available_
.Signal(); // Get help from other threads.
334 if (work_time
> NaCl::TimeDelta::FromMilliseconds(0)) {
335 NaCl::AutoLock
auto_lock(private_lock
);
336 NaCl::ConditionVariable private_cv
;
337 private_cv
.TimedWaitRel(private_lock
, work_time
);
341 NaCl::AutoLock
auto_lock(*queue
->lock());
342 queue
->WorkIsCompleted(thread_id
);
348 /* TODO - This test fails periodically, fix it */
349 TEST(ConditionVariableTest
, MultiThreadConsumerTest
) {
350 const int kThreadCount
= 10;
351 WorkQueue
queue(kThreadCount
); // Start the threads.
353 NaCl::Lock private_lock
; // Used locally for master to wait.
354 NaCl::AutoLock
private_held_lock(private_lock
);
355 NaCl::ConditionVariable private_cv
;
357 const NaCl::TimeDelta ZERO_MS
= NaCl::TimeDelta::FromMilliseconds(0);
358 const NaCl::TimeDelta TEN_MS
= NaCl::TimeDelta::FromMilliseconds(10);
359 const NaCl::TimeDelta THIRTY_MS
= NaCl::TimeDelta::FromMilliseconds(30);
360 const NaCl::TimeDelta FORTY_FIVE_MS
= NaCl::TimeDelta::FromMilliseconds(45);
361 const NaCl::TimeDelta SIXTY_MS
= NaCl::TimeDelta::FromMilliseconds(60);
362 const NaCl::TimeDelta ONE_HUNDRED_MS
= NaCl::TimeDelta::FromMilliseconds(100);
365 NaCl::AutoLock
auto_lock(*queue
.lock());
366 while (!queue
.EveryIdWasAllocated())
367 queue
.all_threads_have_ids_
.Wait(*queue
.lock());
370 // Wait a bit more to allow threads to reach their wait state.
371 private_cv
.TimedWaitRel(private_lock
, TEN_MS
);
374 // Since we have no tasks, all threads should be waiting by now.
375 NaCl::AutoLock
auto_lock(*queue
.lock());
376 EXPECT_EQ(0, queue
.GetNumThreadsTakingAssignments());
377 EXPECT_EQ(0, queue
.GetNumThreadsCompletingTasks());
378 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
379 EXPECT_EQ(0, queue
.GetMaxCompletionsByWorkerThread());
380 EXPECT_EQ(0, queue
.GetMinCompletionsByWorkerThread());
381 EXPECT_EQ(0, queue
.GetNumberOfCompletedTasks());
383 // Set up to make one worker do 3 30ms tasks.
384 queue
.ResetHistory();
385 queue
.SetTaskCount(3);
386 queue
.SetWorkTime(THIRTY_MS
);
387 queue
.SetAllowHelp(false);
389 queue
.work_is_available()->Signal(); // Start up one thread.
390 // Wait to allow solo worker insufficient time to get done.
391 // Should take about 90 ms.
392 private_cv
.TimedWaitRel(private_lock
, FORTY_FIVE_MS
);
395 // Check that all work HASN'T completed yet.
396 NaCl::AutoLock
auto_lock(*queue
.lock());
397 EXPECT_EQ(1, queue
.GetNumThreadsTakingAssignments());
398 EXPECT_EQ(1, queue
.GetNumThreadsCompletingTasks());
399 EXPECT_GT(2, queue
.GetRemainingTaskCount()); // 2 should have started.
400 EXPECT_GT(3, queue
.GetMaxCompletionsByWorkerThread());
401 EXPECT_EQ(0, queue
.GetMinCompletionsByWorkerThread());
402 EXPECT_EQ(1, queue
.GetNumberOfCompletedTasks());
404 // Wait to allow solo workers to get done.
405 private_cv
.TimedWaitRel(private_lock
, SIXTY_MS
); // Should take about 45ms more.
408 // Check that all work was done by one thread id.
409 NaCl::AutoLock
auto_lock(*queue
.lock());
410 EXPECT_EQ(1, queue
.GetNumThreadsTakingAssignments());
411 EXPECT_EQ(1, queue
.GetNumThreadsCompletingTasks());
412 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
413 EXPECT_EQ(3, queue
.GetMaxCompletionsByWorkerThread());
414 EXPECT_EQ(0, queue
.GetMinCompletionsByWorkerThread());
415 EXPECT_EQ(3, queue
.GetNumberOfCompletedTasks());
417 // Set up to make each task include getting help from another worker.
418 queue
.ResetHistory();
419 queue
.SetTaskCount(3);
420 queue
.SetWorkTime(THIRTY_MS
);
421 queue
.SetAllowHelp(true);
423 queue
.work_is_available()->Signal(); // But each worker can signal another.
424 // Wait to allow the 3 workers to get done.
425 // Should take about 30 ms.
426 private_cv
.TimedWaitRel(private_lock
, FORTY_FIVE_MS
);
428 NaCl::AutoLock
auto_lock(*queue
.lock());
429 EXPECT_EQ(3, queue
.GetNumThreadsTakingAssignments());
430 EXPECT_EQ(3, queue
.GetNumThreadsCompletingTasks());
431 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
432 EXPECT_EQ(1, queue
.GetMaxCompletionsByWorkerThread());
433 EXPECT_EQ(0, queue
.GetMinCompletionsByWorkerThread());
434 EXPECT_EQ(3, queue
.GetNumberOfCompletedTasks());
436 // Try to ask all workers to help, and only a few will do the work.
437 queue
.ResetHistory();
438 queue
.SetTaskCount(3);
439 queue
.SetWorkTime(THIRTY_MS
);
440 queue
.SetAllowHelp(false);
442 queue
.work_is_available()->Broadcast(); // Make them all try.
443 // Wait to allow the 3 workers to get done.
444 private_cv
.TimedWaitRel(private_lock
, FORTY_FIVE_MS
);
447 NaCl::AutoLock
auto_lock(*queue
.lock());
448 EXPECT_EQ(3, queue
.GetNumThreadsTakingAssignments());
449 EXPECT_EQ(3, queue
.GetNumThreadsCompletingTasks());
450 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
451 EXPECT_EQ(1, queue
.GetMaxCompletionsByWorkerThread());
452 EXPECT_EQ(0, queue
.GetMinCompletionsByWorkerThread());
453 EXPECT_EQ(3, queue
.GetNumberOfCompletedTasks());
455 // Set up to make each task get help from another worker.
456 queue
.ResetHistory();
457 queue
.SetTaskCount(3);
458 queue
.SetWorkTime(THIRTY_MS
);
459 queue
.SetAllowHelp(true); // Allow (unnecessary) help requests.
461 queue
.work_is_available()->Broadcast(); // We already signal all threads.
462 // Wait to allow the 3 workers to get done.
463 private_cv
.TimedWaitRel(private_lock
, ONE_HUNDRED_MS
);
466 NaCl::AutoLock
auto_lock(*queue
.lock());
467 EXPECT_EQ(3, queue
.GetNumThreadsTakingAssignments());
468 EXPECT_EQ(3, queue
.GetNumThreadsCompletingTasks());
469 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
470 EXPECT_EQ(1, queue
.GetMaxCompletionsByWorkerThread());
471 EXPECT_EQ(0, queue
.GetMinCompletionsByWorkerThread());
472 EXPECT_EQ(3, queue
.GetNumberOfCompletedTasks());
474 // Set up to make each task get help from another worker.
475 queue
.ResetHistory();
476 queue
.SetTaskCount(20);
477 queue
.SetWorkTime(THIRTY_MS
);
478 queue
.SetAllowHelp(true);
480 queue
.work_is_available()->Signal(); // But each worker can signal another.
481 // Wait to allow the 10 workers to get done.
482 // Should take about 60 ms.
483 private_cv
.TimedWaitRel(private_lock
, ONE_HUNDRED_MS
);
486 NaCl::AutoLock
auto_lock(*queue
.lock());
487 EXPECT_EQ(10, queue
.GetNumThreadsTakingAssignments());
488 EXPECT_EQ(10, queue
.GetNumThreadsCompletingTasks());
489 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
490 EXPECT_EQ(2, queue
.GetMaxCompletionsByWorkerThread());
491 EXPECT_EQ(2, queue
.GetMinCompletionsByWorkerThread());
492 EXPECT_EQ(20, queue
.GetNumberOfCompletedTasks());
494 // Same as last test, but with Broadcast().
495 queue
.ResetHistory();
496 queue
.SetTaskCount(20); // 2 tasks per process.
497 queue
.SetWorkTime(THIRTY_MS
);
498 queue
.SetAllowHelp(true);
500 queue
.work_is_available()->Broadcast();
501 // Wait to allow the 10 workers to get done.
502 // Should take about 60 ms.
503 private_cv
.TimedWaitRel(private_lock
, ONE_HUNDRED_MS
);
506 NaCl::AutoLock
auto_lock(*queue
.lock());
507 EXPECT_EQ(10, queue
.GetNumThreadsTakingAssignments());
508 EXPECT_EQ(10, queue
.GetNumThreadsCompletingTasks());
509 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
510 EXPECT_EQ(2, queue
.GetMaxCompletionsByWorkerThread());
511 EXPECT_EQ(2, queue
.GetMinCompletionsByWorkerThread());
512 EXPECT_EQ(20, queue
.GetNumberOfCompletedTasks());
516 queue
.work_is_available()->Broadcast(); // Force check for shutdown.
518 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(NaCl::TimeDelta::FromMinutes(1),
519 queue
.shutdown_task_count() == kThreadCount
);
520 Sleep(10); // Be sure they're all shutdown.
523 #endif /* disabled MultiThreadConsumerTest */
525 TEST(ConditionVariableTest
, LargeFastTaskTest
) {
526 const int kThreadCount
= 200;
527 WorkQueue
queue(kThreadCount
); // Start the threads.
529 NaCl::Lock private_lock
; // Used locally for master to wait.
530 NaCl::AutoLock
private_held_lock(private_lock
);
531 NaCl::ConditionVariable private_cv
;
533 const NaCl::TimeDelta ZERO_MS
= NaCl::TimeDelta::FromMilliseconds(0);
534 const NaCl::TimeDelta TEN_MS
= NaCl::TimeDelta::FromMilliseconds(10);
535 const NaCl::TimeDelta THIRTY_MS
= NaCl::TimeDelta::FromMilliseconds(30);
536 const NaCl::TimeDelta FORTY_FIVE_MS
= NaCl::TimeDelta::FromMilliseconds(45);
537 const NaCl::TimeDelta SIXTY_MS
= NaCl::TimeDelta::FromMilliseconds(60);
538 const NaCl::TimeDelta ONE_HUNDRED_MS
= NaCl::TimeDelta::FromMilliseconds(100);
541 NaCl::AutoLock
auto_lock(*queue
.lock());
542 while (!queue
.EveryIdWasAllocated())
543 queue
.all_threads_have_ids_
.Wait(*queue
.lock());
546 // Wait a bit more to allow threads to reach their wait state.
547 private_cv
.TimedWaitRel(private_lock
, THIRTY_MS
);
550 // Since we have no tasks, all threads should be waiting by now.
551 NaCl::AutoLock
auto_lock(*queue
.lock());
552 EXPECT_EQ(0, queue
.GetNumThreadsTakingAssignments());
553 EXPECT_EQ(0, queue
.GetNumThreadsCompletingTasks());
554 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
555 EXPECT_EQ(0, queue
.GetMaxCompletionsByWorkerThread());
556 EXPECT_EQ(0, queue
.GetMinCompletionsByWorkerThread());
557 EXPECT_EQ(0, queue
.GetNumberOfCompletedTasks());
559 // Set up to make all workers do (an average of) 20 tasks.
560 queue
.ResetHistory();
561 queue
.SetTaskCount(20 * kThreadCount
);
562 queue
.SetWorkTime(FORTY_FIVE_MS
);
563 queue
.SetAllowHelp(false);
565 queue
.work_is_available()->Broadcast(); // Start up all threads.
566 // Wait until we've handed out all tasks.
568 NaCl::AutoLock
auto_lock(*queue
.lock());
569 while (queue
.GetRemainingTaskCount() != 0)
570 queue
.no_more_tasks()->Wait(*queue
.lock());
573 // Wait till the last of the tasks complete.
574 // Don't bother to use locks: We may not get info in time... but we'll see it
576 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(NaCl::TimeDelta::FromMinutes(1),
578 queue
.GetNumberOfCompletedTasks());
581 // With Broadcast(), every thread should have participated.
582 // but with racing.. they may not all have done equal numbers of tasks.
583 NaCl::AutoLock
auto_lock(*queue
.lock());
584 EXPECT_EQ(kThreadCount
, queue
.GetNumThreadsTakingAssignments());
585 EXPECT_EQ(kThreadCount
, queue
.GetNumThreadsCompletingTasks());
586 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
587 EXPECT_LE(20, queue
.GetMaxCompletionsByWorkerThread());
588 // EXPECT_LE(1, queue.GetMinCompletionsByWorkerThread());
589 EXPECT_EQ(20 * kThreadCount
, queue
.GetNumberOfCompletedTasks());
591 // Set up to make all workers do (an average of) 4 tasks.
592 queue
.ResetHistory();
593 queue
.SetTaskCount(kThreadCount
* 4);
594 queue
.SetWorkTime(FORTY_FIVE_MS
);
595 queue
.SetAllowHelp(true); // Might outperform Broadcast().
597 queue
.work_is_available()->Signal(); // Start up one thread.
599 // Wait until we've handed out all tasks
601 NaCl::AutoLock
auto_lock(*queue
.lock());
602 while (queue
.GetRemainingTaskCount() != 0)
603 queue
.no_more_tasks()->Wait(*queue
.lock());
606 // Wait till the last of the tasks complete.
607 // Don't bother to use locks: We may not get info in time... but we'll see it
609 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(NaCl::TimeDelta::FromMinutes(1),
611 queue
.GetNumberOfCompletedTasks());
614 // With Signal(), every thread should have participated.
615 // but with racing.. they may not all have done four tasks.
616 NaCl::AutoLock
auto_lock(*queue
.lock());
617 EXPECT_EQ(kThreadCount
, queue
.GetNumThreadsTakingAssignments());
618 EXPECT_EQ(kThreadCount
, queue
.GetNumThreadsCompletingTasks());
619 EXPECT_EQ(0, queue
.GetRemainingTaskCount());
620 EXPECT_LE(4, queue
.GetMaxCompletionsByWorkerThread());
621 // EXPECT_LE(1, queue.GetMinCompletionsByWorkerThread());
622 EXPECT_EQ(4 * kThreadCount
, queue
.GetNumberOfCompletedTasks());
626 queue
.work_is_available()->Broadcast(); // Force check for shutdown.
628 // Wait for shutdows to complete.
629 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(NaCl::TimeDelta::FromMinutes(1),
630 queue
.shutdown_task_count() ==
632 Sleep(10); // Be sure they're all shutdown.
635 #endif // NACL_WINDOWS