1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#ifndef WEBRTC_BASE_CRITICALSECTION_H__
12#define WEBRTC_BASE_CRITICALSECTION_H__
13
14#include "webrtc/base/constructormagic.h"
15#include "webrtc/base/thread_annotations.h"
16
17#if defined(WEBRTC_WIN)
18#include "webrtc/base/win32.h"
19#endif
20
21#if defined(WEBRTC_POSIX)
22#include <pthread.h>
23#endif
24
25#ifdef _DEBUG
26#define CS_TRACK_OWNER 1
27#endif  // _DEBUG
28
29#if CS_TRACK_OWNER
30#define TRACK_OWNER(x) x
31#else  // !CS_TRACK_OWNER
32#define TRACK_OWNER(x)
33#endif  // !CS_TRACK_OWNER
34
35namespace rtc {
36
37#if defined(WEBRTC_WIN)
38class LOCKABLE CriticalSection {
39 public:
40  CriticalSection() {
41    InitializeCriticalSection(&crit_);
42    // Windows docs say 0 is not a valid thread id
43    TRACK_OWNER(thread_ = 0);
44  }
45  ~CriticalSection() {
46    DeleteCriticalSection(&crit_);
47  }
48  void Enter() EXCLUSIVE_LOCK_FUNCTION() {
49    EnterCriticalSection(&crit_);
50    TRACK_OWNER(thread_ = GetCurrentThreadId());
51  }
52  bool TryEnter() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
53    if (TryEnterCriticalSection(&crit_) != FALSE) {
54      TRACK_OWNER(thread_ = GetCurrentThreadId());
55      return true;
56    }
57    return false;
58  }
59  void Leave() UNLOCK_FUNCTION() {
60    TRACK_OWNER(thread_ = 0);
61    LeaveCriticalSection(&crit_);
62  }
63
64#if CS_TRACK_OWNER
65  bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); }
66#endif  // CS_TRACK_OWNER
67
68 private:
69  CRITICAL_SECTION crit_;
70  TRACK_OWNER(DWORD thread_);  // The section's owning thread id
71};
72#endif // WEBRTC_WIN
73
74#if defined(WEBRTC_POSIX)
75class LOCKABLE CriticalSection {
76 public:
77  CriticalSection() {
78    pthread_mutexattr_t mutex_attribute;
79    pthread_mutexattr_init(&mutex_attribute);
80    pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
81    pthread_mutex_init(&mutex_, &mutex_attribute);
82    pthread_mutexattr_destroy(&mutex_attribute);
83    TRACK_OWNER(thread_ = 0);
84  }
85  ~CriticalSection() {
86    pthread_mutex_destroy(&mutex_);
87  }
88  void Enter() EXCLUSIVE_LOCK_FUNCTION() {
89    pthread_mutex_lock(&mutex_);
90    TRACK_OWNER(thread_ = pthread_self());
91  }
92  bool TryEnter() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
93    if (pthread_mutex_trylock(&mutex_) == 0) {
94      TRACK_OWNER(thread_ = pthread_self());
95      return true;
96    }
97    return false;
98  }
99  void Leave() UNLOCK_FUNCTION() {
100    TRACK_OWNER(thread_ = 0);
101    pthread_mutex_unlock(&mutex_);
102  }
103
104#if CS_TRACK_OWNER
105  bool CurrentThreadIsOwner() const { return pthread_equal(thread_, pthread_self()); }
106#endif  // CS_TRACK_OWNER
107
108 private:
109  pthread_mutex_t mutex_;
110  TRACK_OWNER(pthread_t thread_);
111};
112#endif // WEBRTC_POSIX
113
114// CritScope, for serializing execution through a scope.
115class SCOPED_LOCKABLE CritScope {
116 public:
117  explicit CritScope(CriticalSection *pcrit) EXCLUSIVE_LOCK_FUNCTION(pcrit) {
118    pcrit_ = pcrit;
119    pcrit_->Enter();
120  }
121  ~CritScope() UNLOCK_FUNCTION() {
122    pcrit_->Leave();
123  }
124 private:
125  CriticalSection *pcrit_;
126  DISALLOW_COPY_AND_ASSIGN(CritScope);
127};
128
129// Tries to lock a critical section on construction via
130// CriticalSection::TryEnter, and unlocks on destruction if the
131// lock was taken. Never blocks.
132//
133// IMPORTANT: Unlike CritScope, the lock may not be owned by this thread in
134// subsequent code. Users *must* check locked() to determine if the
135// lock was taken. If you're not calling locked(), you're doing it wrong!
136class TryCritScope {
137 public:
138  explicit TryCritScope(CriticalSection *pcrit) {
139    pcrit_ = pcrit;
140    locked_ = pcrit_->TryEnter();
141  }
142  ~TryCritScope() {
143    if (locked_) {
144      pcrit_->Leave();
145    }
146  }
147  bool locked() const {
148    return locked_;
149  }
150 private:
151  CriticalSection *pcrit_;
152  bool locked_;
153  DISALLOW_COPY_AND_ASSIGN(TryCritScope);
154};
155
156// TODO: Move this to atomicops.h, which can't be done easily because of
157// complex compile rules.
158class AtomicOps {
159 public:
160#if defined(WEBRTC_WIN)
161  // Assumes sizeof(int) == sizeof(LONG), which it is on Win32 and Win64.
162  static int Increment(int* i) {
163    return ::InterlockedIncrement(reinterpret_cast<LONG*>(i));
164  }
165  static int Decrement(int* i) {
166    return ::InterlockedDecrement(reinterpret_cast<LONG*>(i));
167  }
168#else
169  static int Increment(int* i) {
170    return __sync_add_and_fetch(i, 1);
171  }
172  static int Decrement(int* i) {
173    return __sync_sub_and_fetch(i, 1);
174  }
175#endif
176};
177
178} // namespace rtc
179
180#endif // WEBRTC_BASE_CRITICALSECTION_H__
181