mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
120 lines
3.6 KiB
C++
120 lines
3.6 KiB
C++
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_THREAD_BARRIER_H_
|
|
#define RUNTIME_VM_THREAD_BARRIER_H_
|
|
|
|
#include "vm/globals.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/lockers.h"
|
|
|
|
namespace dart {
|
|
|
|
// Thread barrier with:
|
|
// * fixed (at construction) number n of participating threads {T1,T2,T3,...,Tn}
|
|
// * unknown number of rounds.
|
|
// Requirements:
|
|
// * there is some R such that each participating thread makes
|
|
// R calls to Sync() followed by its one and only call to Exit().
|
|
// Guarantees:
|
|
// * for any two threads Ti and Tj and round number r <= R,
|
|
// everything done by Ti before its r'th call to Sync() happens before
|
|
// everything done by Tj after its r'th call to Sync().
|
|
// Note:
|
|
// * it's not required that the thread that constructs the barrier participates.
|
|
//
|
|
// Example usage with 3 threads (1 controller + 2 workers) and 3 rounds:
|
|
//
|
|
// T1:
|
|
// ThreadBarrier barrier(3);
|
|
// Dart::thread_pool()->Run( T2:
|
|
// new FooTask(&barrier)); fooSetup();
|
|
// Dart::thread_pool()->Run( ... T3:
|
|
// new BarTask(&barrier)); ... barSetup();
|
|
// barrier.Sync(); barrier_->Sync(); barrier_->Sync();
|
|
// /* Both tasks have finished setup */ ... ...
|
|
// prepareWorkForTasks(); ... ...
|
|
// barrier.Sync(); barrier_->Sync(); barrier_->Sync();
|
|
// /* Idle while tasks are working */ fooWork(); barWork();
|
|
// barrier.Sync(); barrier_->Sync(); barrier_->Sync();
|
|
// collectResultsFromTasks(); barrier_->Exit(); barrier_->Exit();
|
|
// barrier.Exit();
|
|
//
|
|
// Note that the calls to Sync() "line up" in time, but there is no such
|
|
// guarantee for Exit().
|
|
//
|
|
class ThreadBarrier {
|
|
public:
|
|
explicit ThreadBarrier(intptr_t num_threads,
|
|
Monitor* monitor,
|
|
Monitor* done_monitor)
|
|
: num_threads_(num_threads),
|
|
monitor_(monitor),
|
|
remaining_(num_threads),
|
|
parity_(false),
|
|
done_monitor_(done_monitor),
|
|
done_(false) {
|
|
ASSERT(remaining_ > 0);
|
|
}
|
|
|
|
void Sync() {
|
|
MonitorLocker ml(monitor_);
|
|
ASSERT(remaining_ > 0);
|
|
if (--remaining_ > 0) {
|
|
// I'm not last to arrive; wait until next round.
|
|
bool old_parity = parity_;
|
|
while (parity_ == old_parity) {
|
|
ml.Wait();
|
|
}
|
|
} else {
|
|
// Last one to arrive initiates the next round.
|
|
remaining_ = num_threads_;
|
|
parity_ = !parity_;
|
|
// Tell everyone else about the new round.
|
|
ml.NotifyAll();
|
|
}
|
|
}
|
|
|
|
void Exit() {
|
|
bool last = false;
|
|
{
|
|
MonitorLocker ml(monitor_);
|
|
ASSERT(remaining_ > 0);
|
|
last = (--remaining_ == 0);
|
|
}
|
|
if (last) {
|
|
// Last one to exit sets done_.
|
|
MonitorLocker ml(done_monitor_);
|
|
ASSERT(!done_);
|
|
done_ = true;
|
|
// Tell the destructor in case it's already waiting.
|
|
ml.Notify();
|
|
}
|
|
}
|
|
|
|
~ThreadBarrier() {
|
|
MonitorLocker ml(done_monitor_);
|
|
// Wait for everyone to exit before destroying the monitors.
|
|
while (!done_) {
|
|
ml.Wait();
|
|
}
|
|
ASSERT(remaining_ == 0);
|
|
}
|
|
|
|
private:
|
|
const intptr_t num_threads_;
|
|
|
|
Monitor* monitor_;
|
|
intptr_t remaining_;
|
|
bool parity_;
|
|
|
|
Monitor* done_monitor_; // TODO(koda): Try to optimize this away.
|
|
bool done_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ThreadBarrier);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_THREAD_BARRIER_H_
|