blob: 7761640af7781bc2b49ac98529d343418faecc00 [file] [log] [blame]
// Copyright 2020 Google LLC
//
// 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
//
// https://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.
#include "iree/base/synchronization.h"
#include <chrono>
#include <thread>
#include "iree/testing/gtest.h"
namespace {
//==============================================================================
// Test utils
//==============================================================================
template <typename T>
class Mutex;
template <>
class Mutex<iree_mutex_t> {
public:
static void Initialize(iree_mutex_t* out_mu) {
iree_mutex_initialize(out_mu);
}
static void Deinitialize(iree_mutex_t* mu) { iree_mutex_deinitialize(mu); }
static void Lock(iree_mutex_t* mu)
IREE_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) {
iree_mutex_lock(mu);
}
static bool TryLock(iree_mutex_t* mu)
IREE_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) {
return iree_mutex_try_lock(mu);
}
static void Unlock(iree_mutex_t* mu)
IREE_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) {
iree_mutex_unlock(mu);
}
};
template <>
class Mutex<iree_slim_mutex_t> {
public:
static void Initialize(iree_slim_mutex_t* out_mu) {
iree_slim_mutex_initialize(out_mu);
}
static void Deinitialize(iree_slim_mutex_t* mu) {
iree_slim_mutex_deinitialize(mu);
}
static void Lock(iree_slim_mutex_t* mu)
IREE_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) {
iree_slim_mutex_lock(mu);
}
static bool TryLock(iree_slim_mutex_t* mu)
IREE_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) {
return iree_slim_mutex_try_lock(mu);
}
static void Unlock(iree_slim_mutex_t* mu)
IREE_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) {
iree_slim_mutex_unlock(mu);
}
};
// Tests that a mutex allows exclusive access to a region by touching it from
// multiple threads.
template <typename T>
void TestMutexExclusiveAccess() {
// We'll increment the counter back and forth as we touch it from multiple
// threads.
int counter = 0;
T mu;
Mutex<T>::Initialize(&mu);
// Hold the lock at the start. The threads should block waiting for the lock
// to be released so they can take it.
ASSERT_EQ(0, counter);
Mutex<T>::Lock(&mu);
// Start up a thread to ++counter (it should block since we hold the lock).
std::thread th1([&]() {
Mutex<T>::Lock(&mu);
++counter;
Mutex<T>::Unlock(&mu);
});
// Unlock and wait for the thread to acquire the lock and finish its work.
ASSERT_EQ(0, counter);
Mutex<T>::Unlock(&mu);
th1.join();
// Thread should have been able to increment the counter.
ASSERT_EQ(1, counter);
Mutex<T>::Deinitialize(&mu);
}
// Tests that try lock bails when the lock is held by another thread.
template <typename T>
void TestMutexExclusiveAccessTryLock() {
int counter = 0;
T mu;
Mutex<T>::Initialize(&mu);
// Hold the lock at the start. The try lock should fail and the thread should
// exit without changing the counter value.
ASSERT_EQ(0, counter);
Mutex<T>::Lock(&mu);
std::thread th1([&]() {
if (Mutex<T>::TryLock(&mu)) {
++counter;
Mutex<T>::Unlock(&mu);
}
});
// Wait for the thread to try (and fail).
th1.join();
Mutex<T>::Unlock(&mu);
// The thread should not have been able to change the counter.
ASSERT_EQ(0, counter);
Mutex<T>::Deinitialize(&mu);
}
//==============================================================================
// iree_mutex_t
//==============================================================================
TEST(MutexTest, Lifetime) {
iree_mutex_t mutex;
iree_mutex_initialize(&mutex);
bool did_lock = iree_mutex_try_lock(&mutex);
EXPECT_TRUE(did_lock);
if (did_lock) iree_mutex_unlock(&mutex);
iree_mutex_lock(&mutex);
iree_mutex_unlock(&mutex);
iree_mutex_deinitialize(&mutex);
}
TEST(MutexTest, ExclusiveAccess) { TestMutexExclusiveAccess<iree_mutex_t>(); }
TEST(MutexTest, ExclusiveAccessTryLock) {
TestMutexExclusiveAccessTryLock<iree_mutex_t>();
}
//==============================================================================
// iree_slim_mutex_t
//==============================================================================
TEST(SlimMutexTest, Lifetime) {
iree_slim_mutex_t mutex;
iree_slim_mutex_initialize(&mutex);
bool did_lock = iree_slim_mutex_try_lock(&mutex);
EXPECT_TRUE(did_lock);
if (did_lock) iree_slim_mutex_unlock(&mutex);
iree_slim_mutex_lock(&mutex);
iree_slim_mutex_unlock(&mutex);
iree_slim_mutex_deinitialize(&mutex);
}
TEST(SlimMutexTest, ExclusiveAccess) {
TestMutexExclusiveAccess<iree_slim_mutex_t>();
}
TEST(SlimMutexTest, ExclusiveAccessTryLock) {
TestMutexExclusiveAccessTryLock<iree_slim_mutex_t>();
}
//==============================================================================
// iree_notification_t
//==============================================================================
// Tested implicitly in threading_test.cc.
} // namespace