318 lines
11 KiB
C++
318 lines
11 KiB
C++
![]() |
/*
|
||
|
* Copyright (c) 2017, Intel Corporation
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included
|
||
|
* in all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
#include "runtime/event/async_events_handler.h"
|
||
|
#include "runtime/event/event.h"
|
||
|
#include "runtime/event/user_event.h"
|
||
|
#include "runtime/platform/platform.h"
|
||
|
#include "unit_tests/helpers/debug_manager_state_restore.h"
|
||
|
#include "unit_tests/mocks/mock_async_event_handler.h"
|
||
|
#include "test.h"
|
||
|
#include "gmock/gmock.h"
|
||
|
|
||
|
using namespace OCLRT;
|
||
|
using namespace ::testing;
|
||
|
|
||
|
class AsyncEventsHandlerTests : public ::testing::Test {
|
||
|
public:
|
||
|
class MyEvent : public Event {
|
||
|
public:
|
||
|
MyEvent(CommandQueue *cmdQueue, cl_command_type cmdType, uint32_t taskLevel, uint32_t taskCount)
|
||
|
: Event(cmdQueue, cmdType, taskLevel, taskCount) {}
|
||
|
int getExecutionStatus() {
|
||
|
//return execution status without updating
|
||
|
return executionStatus.load();
|
||
|
}
|
||
|
void setTaskStamp(uint32_t taskLevel, uint32_t taskCount) {
|
||
|
this->taskLevel.store(taskLevel);
|
||
|
this->updateTaskCount(taskCount);
|
||
|
}
|
||
|
|
||
|
MOCK_METHOD1(wait, bool(bool blocking));
|
||
|
};
|
||
|
|
||
|
static void CL_CALLBACK callbackFcn(cl_event e, cl_int status, void *data) {
|
||
|
++(*(int *)data);
|
||
|
}
|
||
|
|
||
|
void SetUp() override {
|
||
|
dbgRestore.reset(new DebugManagerStateRestore());
|
||
|
DebugManager.flags.EnableAsyncEventsHandler.set(false);
|
||
|
handler.reset(new MockHandler());
|
||
|
|
||
|
event1 = new NiceMock<MyEvent>(nullptr, CL_COMMAND_BARRIER, Event::eventNotReady, Event::eventNotReady);
|
||
|
event2 = new NiceMock<MyEvent>(nullptr, CL_COMMAND_BARRIER, Event::eventNotReady, Event::eventNotReady);
|
||
|
event3 = new NiceMock<MyEvent>(nullptr, CL_COMMAND_BARRIER, Event::eventNotReady, Event::eventNotReady);
|
||
|
}
|
||
|
|
||
|
void TearDown() override {
|
||
|
event1->release();
|
||
|
event2->release();
|
||
|
event3->release();
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<DebugManagerStateRestore> dbgRestore;
|
||
|
std::unique_ptr<MockHandler> handler;
|
||
|
int counter = 0;
|
||
|
|
||
|
NiceMock<MyEvent> *event1 = nullptr;
|
||
|
NiceMock<MyEvent> *event2 = nullptr;
|
||
|
NiceMock<MyEvent> *event3 = nullptr;
|
||
|
};
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenEventsWhenListIsProcessedThenUpdateExecutionStatus) {
|
||
|
event1->setTaskStamp(0, 0);
|
||
|
event2->setTaskStamp(0, 0);
|
||
|
|
||
|
handler->registerEvent(event1);
|
||
|
handler->registerEvent(event2);
|
||
|
|
||
|
EXPECT_EQ(CL_QUEUED, event1->getExecutionStatus());
|
||
|
EXPECT_EQ(CL_QUEUED, event2->getExecutionStatus());
|
||
|
|
||
|
handler->process();
|
||
|
|
||
|
EXPECT_NE(CL_QUEUED, event1->getExecutionStatus());
|
||
|
EXPECT_NE(CL_QUEUED, event2->getExecutionStatus());
|
||
|
|
||
|
EXPECT_TRUE(handler->peekIsListEmpty()); // auto-unregister when no callbacs
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, updateEventsRefInternalCount) {
|
||
|
event1->setTaskStamp(Event::eventNotReady, 0);
|
||
|
|
||
|
handler->registerEvent(event1);
|
||
|
EXPECT_EQ(2, event1->getRefInternalCount());
|
||
|
handler->process();
|
||
|
EXPECT_TRUE(handler->peekIsListEmpty());
|
||
|
EXPECT_EQ(1, event1->getRefInternalCount());
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenNotCalledCallbacksWhenListIsProcessedThenDontUnregister) {
|
||
|
int submittedCounter(0), completeCounter(0);
|
||
|
event1->setTaskStamp(Event::eventNotReady, 0);
|
||
|
event1->addCallback(&this->callbackFcn, CL_SUBMITTED, &submittedCounter);
|
||
|
event1->addCallback(&this->callbackFcn, CL_COMPLETE, &completeCounter);
|
||
|
handler->registerEvent(event1);
|
||
|
|
||
|
auto expect = [&](int status, int sCounter, int cCounter, bool empty) {
|
||
|
EXPECT_EQ(status, event1->getExecutionStatus());
|
||
|
EXPECT_EQ(sCounter, submittedCounter);
|
||
|
EXPECT_EQ(cCounter, completeCounter);
|
||
|
EXPECT_EQ(empty, handler->peekIsListEmpty());
|
||
|
};
|
||
|
|
||
|
handler->process();
|
||
|
expect(CL_QUEUED, 0, 0, false);
|
||
|
|
||
|
event1->setStatus(CL_SUBMITTED);
|
||
|
handler->process();
|
||
|
expect(CL_SUBMITTED, 1, 0, false);
|
||
|
|
||
|
event1->setStatus(CL_COMPLETE);
|
||
|
handler->process();
|
||
|
expect(CL_COMPLETE, 1, 1, true);
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenDoubleRegisteredEventWhenListIsProcessedAndNoCallbacksToProcessThenUnregister) {
|
||
|
event1->setTaskStamp(Event::eventNotReady - 1, 0);
|
||
|
event1->addCallback(&this->callbackFcn, CL_SUBMITTED, &counter);
|
||
|
handler->registerEvent(event1);
|
||
|
handler->registerEvent(event1);
|
||
|
|
||
|
handler->process();
|
||
|
EXPECT_EQ(CL_SUBMITTED, event1->getExecutionStatus());
|
||
|
EXPECT_EQ(1, counter);
|
||
|
EXPECT_TRUE(handler->peekIsListEmpty());
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenEventsNotHandlerByHanlderWhenDestructingThenUnreferenceAll) {
|
||
|
auto myHandler = new MockHandler();
|
||
|
event1->setTaskStamp(Event::eventNotReady, 0);
|
||
|
event2->setTaskStamp(Event::eventNotReady, 0);
|
||
|
event1->addCallback(&this->callbackFcn, CL_SUBMITTED, &counter);
|
||
|
event2->addCallback(&this->callbackFcn, CL_SUBMITTED, &counter);
|
||
|
|
||
|
myHandler->registerEvent(event1);
|
||
|
myHandler->process();
|
||
|
myHandler->registerEvent(event2);
|
||
|
|
||
|
EXPECT_FALSE(myHandler->peekIsListEmpty());
|
||
|
EXPECT_FALSE(myHandler->peekIsRegisterListEmpty());
|
||
|
|
||
|
EXPECT_EQ(3, event1->getRefInternalCount());
|
||
|
EXPECT_EQ(3, event2->getRefInternalCount());
|
||
|
delete myHandler;
|
||
|
// 1 left because of callbacks
|
||
|
EXPECT_EQ(2, event1->getRefInternalCount());
|
||
|
EXPECT_EQ(2, event2->getRefInternalCount());
|
||
|
// release callbacks
|
||
|
event1->setStatus(CL_SUBMITTED);
|
||
|
event2->setStatus(CL_SUBMITTED);
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, dontCreateThreadByDefault) {
|
||
|
MockHandler myHandler;
|
||
|
EXPECT_EQ(nullptr, myHandler.thread.get());
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, createThreadOnFirstRegister) {
|
||
|
event1->setTaskStamp(Event::eventNotReady, 0);
|
||
|
|
||
|
EXPECT_FALSE(handler->openThreadCalled);
|
||
|
handler->registerEvent(event1);
|
||
|
EXPECT_TRUE(handler->openThreadCalled);
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, processAsynchronously) {
|
||
|
DebugManager.flags.EnableAsyncEventsHandler.set(true);
|
||
|
|
||
|
event1->setTaskStamp(Event::eventNotReady, 0);
|
||
|
event2->setTaskStamp(Event::eventNotReady, 0);
|
||
|
|
||
|
event1->addCallback(&this->callbackFcn, CL_SUBMITTED, &counter);
|
||
|
event2->addCallback(&this->callbackFcn, CL_SUBMITTED, &counter);
|
||
|
|
||
|
EXPECT_EQ(CL_QUEUED, event1->getExecutionStatus());
|
||
|
EXPECT_EQ(CL_QUEUED, event2->getExecutionStatus());
|
||
|
|
||
|
// unblock to submit
|
||
|
event1->taskLevel.store(0);
|
||
|
event2->taskLevel.store(0);
|
||
|
|
||
|
while (event1->getExecutionStatus() == CL_QUEUED || event2->getExecutionStatus() == CL_QUEUED) {
|
||
|
std::this_thread::yield();
|
||
|
}
|
||
|
|
||
|
EXPECT_EQ(CL_SUBMITTED, event1->getExecutionStatus());
|
||
|
EXPECT_EQ(CL_SUBMITTED, event2->getExecutionStatus());
|
||
|
|
||
|
platform()->getAsyncEventsHandler()->closeThread();
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, callToWakeupAndDeletedWhenDestructed) {
|
||
|
handler->allowThreadCreating = true;
|
||
|
handler->openThread();
|
||
|
|
||
|
// wait for sleep
|
||
|
while (handler->transferCounter == 0) {
|
||
|
std::this_thread::yield();
|
||
|
}
|
||
|
std::unique_lock<std::mutex> lock(handler->asyncMtx);
|
||
|
lock.unlock();
|
||
|
handler->closeThread();
|
||
|
EXPECT_EQ(nullptr, handler->thread.get());
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenReadyEventWhenCallbackIsAddedThenDontOpenThread) {
|
||
|
DebugManager.flags.EnableAsyncEventsHandler.set(true);
|
||
|
auto myHandler = new MockHandler(true);
|
||
|
platform()->createAsyncEventsHandler(myHandler);
|
||
|
event1->setTaskStamp(0, 0);
|
||
|
event1->addCallback(&this->callbackFcn, CL_SUBMITTED, &counter);
|
||
|
|
||
|
EXPECT_EQ(platform()->getAsyncEventsHandler(), myHandler);
|
||
|
EXPECT_FALSE(event1->peekHasCallbacks());
|
||
|
EXPECT_FALSE(myHandler->openThreadCalled);
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenUserEventWhenCallbackIsAddedThenDontRegister) {
|
||
|
DebugManager.flags.EnableAsyncEventsHandler.set(true);
|
||
|
auto myHandler = new MockHandler(true);
|
||
|
platform()->createAsyncEventsHandler(myHandler);
|
||
|
|
||
|
UserEvent userEvent;
|
||
|
userEvent.addCallback(&this->callbackFcn, CL_COMPLETE, &counter);
|
||
|
|
||
|
EXPECT_TRUE(handler->peekIsListEmpty());
|
||
|
EXPECT_TRUE(handler->peekIsRegisterListEmpty());
|
||
|
EXPECT_TRUE(userEvent.peekHasCallbacks());
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenRegistredEventsWhenProcessIsCalledThenReturnCandidateWithLowestTaskCount) {
|
||
|
int event1Counter(0), event2Counter(0), event3Counter(0);
|
||
|
|
||
|
event1->setTaskStamp(0, 1);
|
||
|
event2->setTaskStamp(0, 2);
|
||
|
event3->setTaskStamp(0, 3);
|
||
|
|
||
|
event2->addCallback(&this->callbackFcn, CL_COMPLETE, &event2Counter);
|
||
|
handler->registerEvent(event2);
|
||
|
event1->addCallback(&this->callbackFcn, CL_COMPLETE, &event1Counter);
|
||
|
handler->registerEvent(event1);
|
||
|
event3->addCallback(&this->callbackFcn, CL_COMPLETE, &event3Counter);
|
||
|
handler->registerEvent(event3);
|
||
|
|
||
|
auto sleepCandidate = handler->process();
|
||
|
EXPECT_EQ(event1, sleepCandidate);
|
||
|
|
||
|
event1->setStatus(CL_COMPLETE);
|
||
|
event2->setStatus(CL_COMPLETE);
|
||
|
event3->setStatus(CL_COMPLETE);
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenEventWithoutCallbacksWhenProcessedThenDontReturnAsSleepCandidate) {
|
||
|
event1->setTaskStamp(0, 1);
|
||
|
event2->setTaskStamp(0, 2);
|
||
|
|
||
|
handler->registerEvent(event1);
|
||
|
event2->addCallback(&this->callbackFcn, CL_COMPLETE, &counter);
|
||
|
handler->registerEvent(event2);
|
||
|
|
||
|
auto sleepCandidate = handler->process();
|
||
|
EXPECT_EQ(event2, sleepCandidate);
|
||
|
|
||
|
event2->setStatus(CL_COMPLETE);
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, givenSleepCandidateWhenProcessedThenCallWait) {
|
||
|
event1->setTaskStamp(0, 1);
|
||
|
event1->addCallback(&this->callbackFcn, CL_COMPLETE, &counter);
|
||
|
handler->registerEvent(event1);
|
||
|
handler->allowAsyncProcess.store(true);
|
||
|
|
||
|
// break infinite loop after first iteartion
|
||
|
auto unsetAsyncFlag = [&](bool arg) {
|
||
|
handler->allowAsyncProcess.store(false);
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
EXPECT_CALL(*event1, wait(true)).Times(1).WillOnce(Invoke(unsetAsyncFlag));
|
||
|
|
||
|
handler->asyncProcess();
|
||
|
|
||
|
event1->setStatus(CL_COMPLETE);
|
||
|
}
|
||
|
|
||
|
TEST_F(AsyncEventsHandlerTests, asyncProcessCallsProcessListBeforeReturning) {
|
||
|
Event *event = new Event(nullptr, CL_COMMAND_NDRANGE_KERNEL, 0, 0);
|
||
|
|
||
|
handler->registerEvent(event);
|
||
|
handler->allowAsyncProcess.store(false);
|
||
|
|
||
|
handler->asyncProcess();
|
||
|
EXPECT_TRUE(handler->peekIsListEmpty());
|
||
|
EXPECT_EQ(1, event->getRefInternalCount());
|
||
|
|
||
|
event->release();
|
||
|
}
|