1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "barrier.h"
18 
19 #include "base/logging.h"
20 #include "base/mutex.h"
21 #include "base/time_utils.h"
22 #include "thread.h"
23 
24 namespace art {
25 
Barrier(int count)26 Barrier::Barrier(int count)
27     : count_(count),
28       lock_("GC barrier lock", kThreadSuspendCountLock),
29       condition_("GC barrier condition", lock_) {
30 }
31 
Pass(Thread * self)32 void Barrier::Pass(Thread* self) {
33   MutexLock mu(self, lock_);
34   SetCountLocked(self, count_ - 1);
35 }
36 
Wait(Thread * self)37 void Barrier::Wait(Thread* self) {
38   Increment(self, -1);
39 }
40 
Init(Thread * self,int count)41 void Barrier::Init(Thread* self, int count) {
42   MutexLock mu(self, lock_);
43   SetCountLocked(self, count);
44 }
45 
Increment(Thread * self,int delta)46 void Barrier::Increment(Thread* self, int delta) {
47   MutexLock mu(self, lock_);
48   SetCountLocked(self, count_ + delta);
49 
50   // Increment the count.  If it becomes zero after the increment
51   // then all the threads have already passed the barrier.  If
52   // it is non-zero then there is still one or more threads
53   // that have not yet called the Pass function.  When the
54   // Pass function is called by the last thread, the count will
55   // be decremented to zero and a Broadcast will be made on the
56   // condition variable, thus waking this up.
57   while (count_ != 0) {
58     condition_.Wait(self);
59   }
60 }
61 
Increment(Thread * self,int delta,uint32_t timeout_ms)62 bool Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) {
63   MutexLock mu(self, lock_);
64   SetCountLocked(self, count_ + delta);
65   bool timed_out = false;
66   if (count_ != 0) {
67     uint32_t timeout_ns = 0;
68     uint64_t abs_timeout = NanoTime() + MsToNs(timeout_ms);
69     for (;;) {
70       timed_out = condition_.TimedWait(self, timeout_ms, timeout_ns);
71       if (timed_out || count_ == 0) return timed_out;
72       // Compute time remaining on timeout.
73       uint64_t now = NanoTime();
74       int64_t time_left = abs_timeout - now;
75       if (time_left <= 0) return true;
76       timeout_ns = time_left % (1000*1000);
77       timeout_ms = time_left / (1000*1000);
78     }
79   }
80   return timed_out;
81 }
82 
SetCountLocked(Thread * self,int count)83 void Barrier::SetCountLocked(Thread* self, int count) {
84   count_ = count;
85   if (count == 0) {
86     condition_.Broadcast(self);
87   }
88 }
89 
~Barrier()90 Barrier::~Barrier() {
91   if (gAborting == 0) {
92     // Only check when not aborting.
93     CHECK_EQ(count_, 0) << "Attempted to destroy barrier with non zero count";
94   } else {
95     if (count_ != 0) {
96       LOG(WARNING) << "Attempted to destroy barrier with non zero count " << count_;
97     }
98   }
99 }
100 
101 }  // namespace art
102