blob: 14c4efedb4489d9508dc0eaade002120391bf51d [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
#include "fw_dunit.hpp"
#include <mutex>
#include <condition_variable>
#include <util/concurrent/spinlock_mutex.hpp>
#include <ace/Task.h>
#include <ace/Time_Value.h>
#include <ace/Guard_T.h>
namespace { // NOLINT(google-build-namespaces)
class semaphore {
public:
explicit semaphore(bool released) : released_(released) {}
void release() {
std::lock_guard<std::mutex> lock(mutex_);
released_ = true;
cv_.notify_one();
}
void acquire() {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock, [this]() { return released_; });
released_ = false;
}
semaphore& operator=(const semaphore& other) {
released_ = other.released_;
return *this;
}
protected:
bool released_;
std::mutex mutex_;
std::condition_variable cv_;
};
using apache::geode::util::concurrent::spinlock_mutex;
DUNIT_TASK(s1p1, Basic)
{
spinlock_mutex s;
{ std::lock_guard<spinlock_mutex> lk(s); }
}
END_TASK(Basic)
semaphore triggerA{0};
semaphore triggerB{0};
semaphore triggerM{0};
spinlock_mutex lock;
std::chrono::steady_clock::time_point btime;
class ThreadA : public ACE_Task_Base {
public:
ThreadA() : ACE_Task_Base() {}
int svc() override {
{
std::lock_guard<spinlock_mutex> lk(lock);
LOG("ThreadA: Acquired lock x.");
triggerM.release();
triggerA.acquire();
}
LOG("ThreadA: Released lock.");
return 0;
}
};
class ThreadB : public ACE_Task_Base {
public:
ThreadB() : ACE_Task_Base() {}
int svc() override {
triggerB.acquire();
{
std::lock_guard<spinlock_mutex> lk(lock);
btime = std::chrono::steady_clock::now();
LOG("ThreadB: Acquired lock.");
triggerM.release();
}
return 0;
}
};
DUNIT_TASK(s1p1, TwoThreads)
{
triggerA = semaphore{0};
triggerB = semaphore{0};
triggerM = semaphore{0};
ThreadA* threadA = new ThreadA();
ThreadB* threadB = new ThreadB();
threadA->activate();
threadB->activate();
// A runs, locks the spinlock, and triggers me. B is idle.
triggerM.acquire();
// A is now idle, but holds lock. Tell B to acquire the lock
auto stime = std::chrono::steady_clock::now();
triggerB.release();
SLEEP(5000);
// B will be stuck until we tell A to release it.
triggerA.release();
// wait until B tells us it has acquired the lock.
triggerM.acquire();
// Now diff btime (when B acquired the lock) and stime to see that it
// took longer than the 5000 seconds before A released it.
auto delta =
std::chrono::duration_cast<std::chrono::milliseconds>(btime - stime)
.count();
std::string msg = "acquire delay was " + std::to_string(delta);
LOG(msg);
ASSERT(delta >= 4900, "Expected 5 second or more spinlock delay");
// Note the test is against 4900 instead of 5000 as there are some
// measurement
// issues. Often delta comes back as 4999 on linux.
threadA->wait();
delete threadA;
threadB->wait();
delete threadB;
}
END_TASK(TwoThreads)
} // namespace