Home | History | Annotate | Download | only in runtime
      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 <android-base/logging.h>
     20 
     21 #include "base/aborting.h"
     22 #include "base/mutex.h"
     23 #include "base/time_utils.h"
     24 #include "thread.h"
     25 
     26 namespace art {
     27 
     28 Barrier::Barrier(int count)
     29     : count_(count),
     30       lock_("GC barrier lock", kThreadSuspendCountLock),
     31       condition_("GC barrier condition", lock_) {
     32 }
     33 
     34 template void Barrier::Increment<Barrier::kAllowHoldingLocks>(Thread* self, int delta);
     35 template void Barrier::Increment<Barrier::kDisallowHoldingLocks>(Thread* self, int delta);
     36 
     37 void Barrier::Pass(Thread* self) {
     38   MutexLock mu(self, lock_);
     39   SetCountLocked(self, count_ - 1);
     40 }
     41 
     42 void Barrier::Wait(Thread* self) {
     43   Increment(self, -1);
     44 }
     45 
     46 void Barrier::Init(Thread* self, int count) {
     47   MutexLock mu(self, lock_);
     48   SetCountLocked(self, count);
     49 }
     50 
     51 template <Barrier::LockHandling locks>
     52 void Barrier::Increment(Thread* self, int delta) {
     53   MutexLock mu(self, lock_);
     54   SetCountLocked(self, count_ + delta);
     55 
     56   // Increment the count.  If it becomes zero after the increment
     57   // then all the threads have already passed the barrier.  If
     58   // it is non-zero then there is still one or more threads
     59   // that have not yet called the Pass function.  When the
     60   // Pass function is called by the last thread, the count will
     61   // be decremented to zero and a Broadcast will be made on the
     62   // condition variable, thus waking this up.
     63   while (count_ != 0) {
     64     if (locks == kAllowHoldingLocks) {
     65       condition_.WaitHoldingLocks(self);
     66     } else {
     67       condition_.Wait(self);
     68     }
     69   }
     70 }
     71 
     72 bool Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) {
     73   MutexLock mu(self, lock_);
     74   SetCountLocked(self, count_ + delta);
     75   bool timed_out = false;
     76   if (count_ != 0) {
     77     uint32_t timeout_ns = 0;
     78     uint64_t abs_timeout = NanoTime() + MsToNs(timeout_ms);
     79     for (;;) {
     80       timed_out = condition_.TimedWait(self, timeout_ms, timeout_ns);
     81       if (timed_out || count_ == 0) return timed_out;
     82       // Compute time remaining on timeout.
     83       uint64_t now = NanoTime();
     84       int64_t time_left = abs_timeout - now;
     85       if (time_left <= 0) return true;
     86       timeout_ns = time_left % (1000*1000);
     87       timeout_ms = time_left / (1000*1000);
     88     }
     89   }
     90   return timed_out;
     91 }
     92 
     93 int Barrier::GetCount(Thread* self) {
     94   MutexLock mu(self, lock_);
     95   return count_;
     96 }
     97 
     98 void Barrier::SetCountLocked(Thread* self, int count) {
     99   count_ = count;
    100   if (count == 0) {
    101     condition_.Broadcast(self);
    102   }
    103 }
    104 
    105 Barrier::~Barrier() {
    106   if (gAborting == 0) {
    107     // Only check when not aborting.
    108     CHECK_EQ(count_, 0) << "Attempted to destroy barrier with non zero count";
    109   } else {
    110     if (count_ != 0) {
    111       LOG(WARNING) << "Attempted to destroy barrier with non zero count " << count_;
    112     }
    113   }
    114 }
    115 
    116 }  // namespace art
    117