Home | History | Annotate | Download | only in threads
      1 /*
      2  * Copyright (C) 2016 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 #include "common/libs/threads/cuttlefish_thread.h"
     17 
     18 #include <android-base/logging.h>
     19 #include "common/libs/threads/thunkers.h"
     20 #include "common/libs/time/monotonic_time.h"
     21 
     22 using cvd::ConditionVariable;
     23 using cvd::Mutex;
     24 using cvd::ScopedThread;
     25 using cvd::time::MonotonicTimePoint;
     26 using cvd::time::Milliseconds;
     27 
     28 static const int FINISHED = 100;
     29 
     30 static void SleepUntil(const MonotonicTimePoint& in) {
     31   struct timespec ts;
     32   in.ToTimespec(&ts);
     33 #ifdef CLOCK_MONOTONIC_RAW
     34   // WARNING:
     35   // While we do have CLOCK_MONOTONIC_RAW, we can't depend on it until:
     36   // - ALL places relying on MonotonicTimePoint are fixed,
     37   // - pthread supports pthread_timewait_monotonic.
     38   // - CLOCK_MONOTONIC_RAW is re-enabled in monotonic_time.h.
     39   //
     40   // This is currently observable as a LEGITIMATE problem while running
     41   // this test. DO NOT revert this to CLOCK_MONOTONIC_RAW until this is
     42   // fixed everywhere AND this test passes.
     43   clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
     44 #else
     45   clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
     46 #endif
     47 }
     48 
     49 class MutexTest {
     50  public:
     51   MutexTest() : busy_(NULL), stage_(0) {}
     52 
     53   void Run() {
     54     {
     55       ScopedThread thread_a(
     56           MutexTestThunker<void*()>::call<&MutexTest::FastThread>, this);
     57       ScopedThread thread_b(
     58           MutexTestThunker<void*()>::call<&MutexTest::SlowThread>, this);
     59     }
     60     LOG(INFO) << "MutexTest: completed at stage "
     61               << stage_
     62               << ", result: "
     63               << ((stage_ == FINISHED) ? "PASSED" : "FAILED");
     64   }
     65 
     66 protected:
     67   template <typename F> struct MutexTestThunker :
     68   ThunkerBase<void, MutexTest, F>{};
     69 
     70   void* FastThread() {
     71     mutex_.Lock();
     72     CHECK(busy_ == NULL);
     73     busy_ = "FastThread";
     74     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     75     stage_ = 1;
     76     busy_ = NULL;
     77     mutex_.Unlock();
     78     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(10));
     79     mutex_.Lock();
     80     CHECK(busy_ == NULL);
     81     busy_ = "FastThread";
     82     CHECK(stage_ == 2);
     83     stage_ = FINISHED;
     84     busy_ = NULL;
     85     mutex_.Unlock();
     86     return NULL;
     87   }
     88 
     89   void* SlowThread() {
     90     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(50));
     91     mutex_.Lock();
     92     CHECK(busy_== NULL);
     93     busy_ = "SlowThread";
     94     CHECK(stage_ == 1);
     95     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
     96     stage_ = 2;
     97     busy_ = NULL;
     98     mutex_.Unlock();
     99     return NULL;
    100   }
    101 
    102   Mutex mutex_;
    103   const char* busy_;
    104   int stage_;
    105 };
    106 
    107 class NotifyOneTest {
    108  public:
    109   NotifyOneTest() : cond_(&mutex_), signalled_(0) {}
    110 
    111   void Run() {
    112     {
    113       ScopedThread thread_s(
    114           Thunker<void*()>::call<&NotifyOneTest::SignalThread>, this);
    115       ScopedThread thread_w1(
    116           Thunker<void*()>::call<&NotifyOneTest::WaitThread>, this);
    117       ScopedThread thread_w2(
    118           Thunker<void*()>::call<&NotifyOneTest::WaitThread>, this);
    119     }
    120     LOG(INFO) << "NotifyOneTest: completed, signalled "
    121               << signalled_
    122               << ", result: "
    123               << ((signalled_ == 2) ? "PASSED" : "FAILED");
    124   }
    125 
    126 protected:
    127   template <typename F> struct Thunker :
    128   ThunkerBase<void, NotifyOneTest, F>{};
    129 
    130   void* SignalThread() {
    131     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
    132     mutex_.Lock();
    133     cond_.NotifyOne();
    134     mutex_.Unlock();
    135     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
    136     mutex_.Lock();
    137     CHECK(signalled_== 1);
    138     cond_.NotifyOne();
    139     mutex_.Unlock();
    140     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
    141     mutex_.Lock();
    142     CHECK(signalled_ == 2);
    143     mutex_.Unlock();
    144     return NULL;
    145   }
    146 
    147   void* WaitThread() {
    148     mutex_.Lock();
    149     cond_.Wait();
    150     signalled_++;
    151     mutex_.Unlock();
    152     return NULL;
    153   }
    154 
    155   Mutex mutex_;
    156   ConditionVariable cond_;
    157   int signalled_;
    158 };
    159 
    160 class NotifyAllTest {
    161  public:
    162   NotifyAllTest() : cond_(&mutex_), signalled_(0) {}
    163 
    164   void Run() {
    165     {
    166       ScopedThread thread_s(
    167           Thunker<void*()>::call<&NotifyAllTest::SignalThread>, this);
    168       ScopedThread thread_w1(
    169           Thunker<void*()>::call<&NotifyAllTest::WaitThread>, this);
    170       ScopedThread thread_w2(
    171           Thunker<void*()>::call<&NotifyAllTest::WaitThread>, this);
    172     }
    173     printf("NotifyAllTest: completed, signalled %d (%s)\n",
    174            signalled_, (signalled_ == 2) ? "PASSED" : "FAILED");
    175   }
    176 
    177 protected:
    178   template <typename F> struct Thunker :
    179   ThunkerBase<void, NotifyAllTest, F>{};
    180 
    181   void* SignalThread() {
    182     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
    183     mutex_.Lock();
    184     cond_.NotifyAll();
    185     mutex_.Unlock();
    186     SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
    187     mutex_.Lock();
    188     CHECK(signalled_ == 2);
    189     mutex_.Unlock();
    190     return NULL;
    191   }
    192 
    193   void* WaitThread() {
    194     mutex_.Lock();
    195     cond_.Wait();
    196     signalled_++;
    197     mutex_.Unlock();
    198     return NULL;
    199   }
    200 
    201   Mutex mutex_;
    202   ConditionVariable cond_;
    203   int signalled_;
    204 };
    205 
    206 class WaitUntilTest {
    207  public:
    208   WaitUntilTest() : cond_(&mutex_), stage_(0) {}
    209 
    210   void Run() {
    211     start_ = MonotonicTimePoint::Now();
    212     {
    213       ScopedThread thread_s(
    214           Thunker<void*()>::call<&WaitUntilTest::SignalThread>, this);
    215       ScopedThread thread_w2(
    216           Thunker<void*()>::call<&WaitUntilTest::WaitThread>, this);
    217     }
    218     printf("WaitUntilTest: completed, stage %d (%s)\n",
    219            stage_, (stage_ == FINISHED) ? "PASSED" : "FAILED");
    220   }
    221 
    222 protected:
    223   template <typename F> struct Thunker :
    224   ThunkerBase<void, WaitUntilTest, F>{};
    225 
    226   void* SignalThread() {
    227     SleepUntil(start_ + Milliseconds(200));
    228     mutex_.Lock();
    229     CHECK(stage_ == 2);
    230     cond_.NotifyOne();
    231     stage_ = 3;
    232     mutex_.Unlock();
    233     return NULL;
    234   }
    235 
    236   void* WaitThread() {
    237     mutex_.Lock();
    238     CHECK(stage_ == 0);
    239     stage_ = 1;
    240     cond_.WaitUntil(start_ + Milliseconds(50));
    241     MonotonicTimePoint current(MonotonicTimePoint::Now());
    242     CHECK(Milliseconds(current - start_).count() >= 50);
    243     CHECK(Milliseconds(current - start_).count() <= 100);
    244     stage_ = 2;
    245     cond_.WaitUntil(start_ + Milliseconds(1000));
    246     current = MonotonicTimePoint::Now();
    247     CHECK(Milliseconds(current - start_).count() <= 500);
    248     CHECK(stage_ == 3);
    249     stage_ = FINISHED;
    250     mutex_.Unlock();
    251     return NULL;
    252   }
    253 
    254   Mutex mutex_;
    255   ConditionVariable cond_;
    256   int stage_;
    257   MonotonicTimePoint start_;
    258 };
    259 
    260 int main(int, char**argv) {
    261   ::android::base::InitLogging(argv, android::base::StderrLogger);
    262   MutexTest mt;
    263   mt.Run();
    264   NotifyOneTest nt1;
    265   nt1.Run();
    266   NotifyAllTest nta;
    267   nta.Run();
    268   WaitUntilTest wu;
    269   wu.Run();
    270 }
    271