blob: 8063ed898949ffeee5c1a86417dd6b3666f45cab [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Module Overview: Starboard Mutex module
//
// Defines a mutually exclusive lock that can be used to coordinate with other
// threads.
#ifndef STARBOARD_MUTEX_H_
#define STARBOARD_MUTEX_H_
#include "starboard/configuration.h"
#include "starboard/export.h"
#include "starboard/log.h"
#include "starboard/thread.h"
#include "starboard/thread_types.h"
#include "starboard/types.h"
#ifdef __cplusplus
extern "C" {
#endif
// Enumeration of possible results from acquiring a mutex.
typedef enum SbMutexResult {
// The mutex was acquired successfully.
kSbMutexAcquired,
// The mutex was not acquired because it was held by someone else.
kSbMutexBusy,
// The mutex has already been destroyed.
kSbMutexDestroyed,
} SbMutexResult;
// Indicates whether the given result is a success. A value of |true| indicates
// that the mutex was acquired.
//
// |result|: The result being checked.
static SB_C_FORCE_INLINE bool SbMutexIsSuccess(SbMutexResult result) {
return result == kSbMutexAcquired;
}
// Creates a new mutex. The return value indicates whether the function
// was able to create a new mutex.
//
// |out_mutex|: The handle to the newly created mutex.
SB_EXPORT bool SbMutexCreate(SbMutex* out_mutex);
// Destroys a mutex. The return value indicates whether the destruction was
// successful.
//
// |mutex|: The mutex to be invalidated.
SB_EXPORT bool SbMutexDestroy(SbMutex* mutex);
// Acquires |mutex|, blocking indefinitely. The return value identifies
// the acquisition result. SbMutexes are not reentrant, so a recursive
// acquisition blocks forever.
//
// |mutex|: The mutex to be acquired.
SB_EXPORT SbMutexResult SbMutexAcquire(SbMutex* mutex);
// Acquires |mutex|, without blocking. The return value identifies
// the acquisition result. SbMutexes are not reentrant, so a recursive
// acquisition always fails.
//
// |mutex|: The mutex to be acquired.
SB_EXPORT SbMutexResult SbMutexAcquireTry(SbMutex* mutex);
// Releases |mutex| held by the current thread. The return value indicates
// whether the release was successful. Releases should always be successful
// if |mutex| is held by the current thread.
//
// |mutex|: The mutex to be released.
SB_EXPORT bool SbMutexRelease(SbMutex* mutex);
#ifdef __cplusplus
} // extern "C"
#endif
#ifdef __cplusplus
namespace starboard {
// Inline class wrapper for SbMutex.
class Mutex {
public:
Mutex() : mutex_() {
SbMutexCreate(&mutex_);
debugInit();
}
~Mutex() { SbMutexDestroy(&mutex_); }
void Acquire() const {
debugPreAcquire();
SbMutexAcquire(&mutex_);
debugSetAcquired();
}
bool AcquireTry() const {
bool ok = SbMutexAcquireTry(&mutex_) == kSbMutexAcquired;
if (ok) {
debugSetAcquired();
}
return ok;
}
void Release() const {
debugSetReleased();
SbMutexRelease(&mutex_);
}
void DCheckAcquired() const {
#ifdef _DEBUG
SB_DCHECK(currrent_thread_acquired_ == SbThreadGetCurrent());
#endif // _DEBUG
}
private:
#ifdef _DEBUG
void debugInit() { currrent_thread_acquired_ = kSbThreadInvalid; }
void debugSetReleased() const {
SbThread current_thread = SbThreadGetCurrent();
SB_DCHECK(currrent_thread_acquired_ == current_thread);
currrent_thread_acquired_ = kSbThreadInvalid;
}
void debugPreAcquire() const {
// Check that the mutex is not held by the current thread.
SbThread current_thread = SbThreadGetCurrent();
SB_DCHECK(currrent_thread_acquired_ != current_thread);
}
void debugSetAcquired() const {
// Check that the thread has already not been held.
SB_DCHECK(currrent_thread_acquired_ == kSbThreadInvalid);
currrent_thread_acquired_ = SbThreadGetCurrent();
}
mutable SbThread currrent_thread_acquired_;
#else
void debugInit() {}
void debugSetReleased() const {}
void debugPreAcquire() const {}
void debugSetAcquired() const {}
#endif
friend class ConditionVariable;
SbMutex* mutex() const { return &mutex_; }
mutable SbMutex mutex_;
SB_DISALLOW_COPY_AND_ASSIGN(Mutex);
};
// Scoped lock holder that works on starboard::Mutex.
class ScopedLock {
public:
explicit ScopedLock(const Mutex& mutex) : mutex_(mutex) { mutex_.Acquire(); }
~ScopedLock() { mutex_.Release(); }
private:
const Mutex& mutex_;
SB_DISALLOW_COPY_AND_ASSIGN(ScopedLock);
};
// Scoped lock holder that works on starboard::Mutex which uses AcquireTry()
// instead of Acquire().
class ScopedTryLock {
public:
explicit ScopedTryLock(const Mutex& mutex) : mutex_(mutex) {
is_locked_ = mutex_.AcquireTry();
}
~ScopedTryLock() {
if (is_locked_) {
mutex_.Release();
}
}
bool is_locked() const { return is_locked_; }
private:
const Mutex& mutex_;
bool is_locked_;
SB_DISALLOW_COPY_AND_ASSIGN(ScopedTryLock);
};
} // namespace starboard
#endif
#endif // STARBOARD_MUTEX_H_