1#pragma once
2/*
3 * Copyright (C) 2016 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18// Concurreny classess for cuttlefish.
19//
20// These more or less mimic the interface of the C++ classes:
21//   Mutex is similar to std::mutex
22//   ConditionVariable is similar to std::condition_variable
23//   LockGuard is similar to std::lock_guard
24//
25// There are some extensions:
26//   ScopedThread creates a Thread and joins it when the class is destroyed
27//   This comes in handy during unit tests. It should be used cautiously, if
28//   at all, in production code because thread creation isn't free.
29
30#include <stdint.h>
31#include <pthread.h>
32#include "common/libs/time/monotonic_time.h"
33
34namespace cvd {
35
36class Mutex {
37 friend class ConditionVariable;
38
39 public:
40  Mutex() {
41    pthread_mutex_init(&mutex_, NULL);
42  }
43
44  ~Mutex() {
45    pthread_mutex_destroy(&mutex_);
46  }
47
48  void Lock() {
49    pthread_mutex_lock(&mutex_);
50  }
51
52  void Unlock() {
53    pthread_mutex_unlock(&mutex_);
54  }
55
56  // TODO(ghartman): Add TryLock if and only if there's a good use case.
57
58 protected:
59
60  pthread_mutex_t* GetMutex() {
61    return &mutex_;
62  }
63
64  pthread_mutex_t mutex_;
65
66 private:
67  Mutex(const Mutex&);
68  Mutex& operator= (const Mutex&);
69};
70
71class ConditionVariable {
72 public:
73  explicit ConditionVariable(Mutex* mutex) : mutex_(mutex) {
74    pthread_condattr_t attr;
75    pthread_condattr_init(&attr);
76    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
77    pthread_cond_init(&cond_, &attr);
78    pthread_condattr_destroy(&attr);
79  }
80
81  ~ConditionVariable() {
82    pthread_cond_destroy(&cond_);
83  }
84
85  int NotifyOne() {
86    return pthread_cond_signal(&cond_);
87  }
88
89  int NotifyAll() {
90    return pthread_cond_broadcast(&cond_);
91  }
92
93  int Wait() {
94    return pthread_cond_wait(&cond_, mutex_->GetMutex());
95  }
96
97  int WaitUntil(const cvd::time::MonotonicTimePoint& tp) {
98    struct timespec ts;
99    tp.ToTimespec(&ts);
100    return pthread_cond_timedwait(&cond_, mutex_->GetMutex(), &ts);
101  }
102
103 protected:
104  Mutex* mutex_;
105  pthread_cond_t cond_;
106
107 private:
108  ConditionVariable(const ConditionVariable&);
109  ConditionVariable& operator= (const ConditionVariable&);
110};
111
112template <typename M> class LockGuard {
113 public:
114  explicit LockGuard(M& mutex) : mutex_(mutex) {
115    mutex_.Lock();
116  }
117
118  ~LockGuard() {
119    mutex_.Unlock();
120  }
121
122 private:
123  M& mutex_;
124
125  LockGuard(const LockGuard&);
126  LockGuard& operator= (const LockGuard&);
127};
128
129// Use only in cases where the mutex can't be upgraded to a Mutex.
130template<> class LockGuard<pthread_mutex_t> {
131 public:
132  explicit LockGuard(pthread_mutex_t& mutex) : mutex_(mutex), unlock_(false) {
133    unlock_ = (pthread_mutex_lock(&mutex_) == 0);
134  }
135
136  ~LockGuard() {
137    if (unlock_) {
138      pthread_mutex_unlock(&mutex_);
139    }
140  }
141
142 private:
143  pthread_mutex_t& mutex_;
144  bool unlock_;
145
146  LockGuard(const LockGuard&);
147  LockGuard& operator= (const LockGuard&);
148};
149
150class ScopedThread {
151 public:
152  ScopedThread(void* (*start)(void*), void* arg) {
153    pthread_create(&thread_, NULL, start, arg);
154  }
155
156  ~ScopedThread() {
157    void* value;
158    pthread_join(thread_, &value);
159  }
160
161 protected:
162  pthread_t thread_;
163
164 private:
165  ScopedThread(const ScopedThread&);
166  ScopedThread& operator= (const ScopedThread&);
167};
168
169}  // namespace cvd
170