1*3e777be0SXin Li //
2*3e777be0SXin Li // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3*3e777be0SXin Li // SPDX-License-Identifier: MIT
4*3e777be0SXin Li //
5*3e777be0SXin Li
6*3e777be0SXin Li #define LOG_TAG "ArmnnDriver"
7*3e777be0SXin Li
8*3e777be0SXin Li #include "ArmnnPreparedModel_1_3.hpp"
9*3e777be0SXin Li #include "RequestThread_1_3.hpp"
10*3e777be0SXin Li
11*3e777be0SXin Li #include <log/log.h>
12*3e777be0SXin Li
13*3e777be0SXin Li using namespace android;
14*3e777be0SXin Li
15*3e777be0SXin Li namespace armnn_driver
16*3e777be0SXin Li {
17*3e777be0SXin Li
18*3e777be0SXin Li template <template <typename HalVersion> class PreparedModel, typename HalVersion, typename CallbackContext>
RequestThread_1_3()19*3e777be0SXin Li RequestThread_1_3<PreparedModel, HalVersion, CallbackContext>::RequestThread_1_3()
20*3e777be0SXin Li {
21*3e777be0SXin Li ALOGV("RequestThread_1_3::RequestThread_1_3()");
22*3e777be0SXin Li m_Thread = std::make_unique<std::thread>(&RequestThread_1_3::Process, this);
23*3e777be0SXin Li }
24*3e777be0SXin Li
25*3e777be0SXin Li template <template <typename HalVersion> class PreparedModel, typename HalVersion, typename CallbackContext>
~RequestThread_1_3()26*3e777be0SXin Li RequestThread_1_3<PreparedModel, HalVersion, CallbackContext>::~RequestThread_1_3()
27*3e777be0SXin Li {
28*3e777be0SXin Li ALOGV("RequestThread_1_3::~RequestThread_1_3()");
29*3e777be0SXin Li
30*3e777be0SXin Li try
31*3e777be0SXin Li {
32*3e777be0SXin Li // Coverity fix: The following code may throw an exception of type std::length_error.
33*3e777be0SXin Li
34*3e777be0SXin Li // This code is meant to to terminate the inner thread gracefully by posting an EXIT message
35*3e777be0SXin Li // to the thread's message queue. However, according to Coverity, this code could throw an exception and fail.
36*3e777be0SXin Li // Since only one static instance of RequestThread is used in the driver (in ArmnnPreparedModel),
37*3e777be0SXin Li // this destructor is called only when the application has been closed, which means that
38*3e777be0SXin Li // the inner thread will be terminated anyway, although abruptly, in the event that the destructor code throws.
39*3e777be0SXin Li // Wrapping the destructor's code with a try-catch block simply fixes the Coverity bug.
40*3e777be0SXin Li
41*3e777be0SXin Li // Post an EXIT message to the thread
42*3e777be0SXin Li std::shared_ptr<AsyncExecuteData> nulldata(nullptr);
43*3e777be0SXin Li auto pMsg = std::make_shared<ThreadMsg>(ThreadMsgType::EXIT, nulldata);
44*3e777be0SXin Li PostMsg(pMsg);
45*3e777be0SXin Li // Wait for the thread to terminate, it is deleted automatically
46*3e777be0SXin Li m_Thread->join();
47*3e777be0SXin Li }
48*3e777be0SXin Li catch (const std::exception&) { } // Swallow any exception.
49*3e777be0SXin Li }
50*3e777be0SXin Li
51*3e777be0SXin Li template <template <typename HalVersion> class PreparedModel, typename HalVersion, typename CallbackContext>
PostMsg(PreparedModel<HalVersion> * model,std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>> & memPools,std::shared_ptr<armnn::InputTensors> & inputTensors,std::shared_ptr<armnn::OutputTensors> & outputTensors,CallbackContext callbackContext)52*3e777be0SXin Li void RequestThread_1_3<PreparedModel, HalVersion, CallbackContext>::PostMsg(PreparedModel<HalVersion>* model,
53*3e777be0SXin Li std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& memPools,
54*3e777be0SXin Li std::shared_ptr<armnn::InputTensors>& inputTensors,
55*3e777be0SXin Li std::shared_ptr<armnn::OutputTensors>& outputTensors,
56*3e777be0SXin Li CallbackContext callbackContext)
57*3e777be0SXin Li {
58*3e777be0SXin Li ALOGV("RequestThread_1_3::PostMsg(...)");
59*3e777be0SXin Li auto data = std::make_shared<AsyncExecuteData>(model,
60*3e777be0SXin Li memPools,
61*3e777be0SXin Li inputTensors,
62*3e777be0SXin Li outputTensors,
63*3e777be0SXin Li callbackContext);
64*3e777be0SXin Li auto pMsg = std::make_shared<ThreadMsg>(ThreadMsgType::REQUEST, data);
65*3e777be0SXin Li PostMsg(pMsg, model->GetModelPriority());
66*3e777be0SXin Li }
67*3e777be0SXin Li
68*3e777be0SXin Li template <template <typename HalVersion> class PreparedModel, typename HalVersion, typename CallbackContext>
PostMsg(std::shared_ptr<ThreadMsg> & pMsg,V1_3::Priority priority)69*3e777be0SXin Li void RequestThread_1_3<PreparedModel, HalVersion, CallbackContext>::PostMsg(std::shared_ptr<ThreadMsg>& pMsg,
70*3e777be0SXin Li V1_3::Priority priority)
71*3e777be0SXin Li {
72*3e777be0SXin Li ALOGV("RequestThread_1_3::PostMsg(pMsg)");
73*3e777be0SXin Li // Add a message to the queue and notify the request thread
74*3e777be0SXin Li std::unique_lock<std::mutex> lock(m_Mutex);
75*3e777be0SXin Li switch (priority) {
76*3e777be0SXin Li case V1_3::Priority::HIGH:
77*3e777be0SXin Li m_HighPriorityQueue.push(pMsg);
78*3e777be0SXin Li break;
79*3e777be0SXin Li case V1_3::Priority::LOW:
80*3e777be0SXin Li m_LowPriorityQueue.push(pMsg);
81*3e777be0SXin Li break;
82*3e777be0SXin Li case V1_3::Priority::MEDIUM:
83*3e777be0SXin Li default:
84*3e777be0SXin Li m_MediumPriorityQueue.push(pMsg);
85*3e777be0SXin Li }
86*3e777be0SXin Li m_Cv.notify_one();
87*3e777be0SXin Li }
88*3e777be0SXin Li
89*3e777be0SXin Li template <template <typename HalVersion> class PreparedModel, typename HalVersion, typename CallbackContext>
Process()90*3e777be0SXin Li void RequestThread_1_3<PreparedModel, HalVersion, CallbackContext>::Process()
91*3e777be0SXin Li {
92*3e777be0SXin Li ALOGV("RequestThread_1_3::Process()");
93*3e777be0SXin Li int retireRate = RETIRE_RATE;
94*3e777be0SXin Li int highPriorityCount = 0;
95*3e777be0SXin Li int mediumPriorityCount = 0;
96*3e777be0SXin Li while (true)
97*3e777be0SXin Li {
98*3e777be0SXin Li std::shared_ptr<ThreadMsg> pMsg(nullptr);
99*3e777be0SXin Li {
100*3e777be0SXin Li // Wait for a message to be added to the queue
101*3e777be0SXin Li // This is in a separate scope to minimise the lifetime of the lock
102*3e777be0SXin Li std::unique_lock<std::mutex> lock(m_Mutex);
103*3e777be0SXin Li while (m_HighPriorityQueue.empty() && m_MediumPriorityQueue.empty() && m_LowPriorityQueue.empty())
104*3e777be0SXin Li {
105*3e777be0SXin Li m_Cv.wait(lock);
106*3e777be0SXin Li }
107*3e777be0SXin Li // Get the message to process from the front of each queue based on priority from high to low
108*3e777be0SXin Li // Get high priority first if it does not exceed the retire rate
109*3e777be0SXin Li if (!m_HighPriorityQueue.empty() && highPriorityCount < retireRate)
110*3e777be0SXin Li {
111*3e777be0SXin Li pMsg = m_HighPriorityQueue.front();
112*3e777be0SXin Li m_HighPriorityQueue.pop();
113*3e777be0SXin Li highPriorityCount += 1;
114*3e777be0SXin Li }
115*3e777be0SXin Li // If high priority queue is empty or the count exceeds the retire rate, get medium priority message
116*3e777be0SXin Li else if (!m_MediumPriorityQueue.empty() && mediumPriorityCount < retireRate)
117*3e777be0SXin Li {
118*3e777be0SXin Li pMsg = m_MediumPriorityQueue.front();
119*3e777be0SXin Li m_MediumPriorityQueue.pop();
120*3e777be0SXin Li mediumPriorityCount += 1;
121*3e777be0SXin Li // Reset high priority count
122*3e777be0SXin Li highPriorityCount = 0;
123*3e777be0SXin Li }
124*3e777be0SXin Li // If medium priority queue is empty or the count exceeds the retire rate, get low priority message
125*3e777be0SXin Li else if (!m_LowPriorityQueue.empty())
126*3e777be0SXin Li {
127*3e777be0SXin Li pMsg = m_LowPriorityQueue.front();
128*3e777be0SXin Li m_LowPriorityQueue.pop();
129*3e777be0SXin Li // Reset high and medium priority count
130*3e777be0SXin Li highPriorityCount = 0;
131*3e777be0SXin Li mediumPriorityCount = 0;
132*3e777be0SXin Li }
133*3e777be0SXin Li else
134*3e777be0SXin Li {
135*3e777be0SXin Li // Reset high and medium priority count
136*3e777be0SXin Li highPriorityCount = 0;
137*3e777be0SXin Li mediumPriorityCount = 0;
138*3e777be0SXin Li continue;
139*3e777be0SXin Li }
140*3e777be0SXin Li }
141*3e777be0SXin Li
142*3e777be0SXin Li switch (pMsg->type)
143*3e777be0SXin Li {
144*3e777be0SXin Li case ThreadMsgType::REQUEST:
145*3e777be0SXin Li {
146*3e777be0SXin Li ALOGV("RequestThread_1_3::Process() - request");
147*3e777be0SXin Li // invoke the asynchronous execution method
148*3e777be0SXin Li PreparedModel<HalVersion>* model = pMsg->data->m_Model;
149*3e777be0SXin Li model->ExecuteGraph(pMsg->data->m_MemPools,
150*3e777be0SXin Li *(pMsg->data->m_InputTensors),
151*3e777be0SXin Li *(pMsg->data->m_OutputTensors),
152*3e777be0SXin Li pMsg->data->m_CallbackContext);
153*3e777be0SXin Li break;
154*3e777be0SXin Li }
155*3e777be0SXin Li
156*3e777be0SXin Li case ThreadMsgType::EXIT:
157*3e777be0SXin Li {
158*3e777be0SXin Li ALOGV("RequestThread_1_3::Process() - exit");
159*3e777be0SXin Li // delete all remaining messages (there should not be any)
160*3e777be0SXin Li std::unique_lock<std::mutex> lock(m_Mutex);
161*3e777be0SXin Li while (!m_HighPriorityQueue.empty())
162*3e777be0SXin Li {
163*3e777be0SXin Li m_HighPriorityQueue.pop();
164*3e777be0SXin Li }
165*3e777be0SXin Li while (!m_MediumPriorityQueue.empty())
166*3e777be0SXin Li {
167*3e777be0SXin Li m_MediumPriorityQueue.pop();
168*3e777be0SXin Li }
169*3e777be0SXin Li while (!m_LowPriorityQueue.empty())
170*3e777be0SXin Li {
171*3e777be0SXin Li m_LowPriorityQueue.pop();
172*3e777be0SXin Li }
173*3e777be0SXin Li return;
174*3e777be0SXin Li }
175*3e777be0SXin Li
176*3e777be0SXin Li default:
177*3e777be0SXin Li // this should be unreachable
178*3e777be0SXin Li throw armnn::RuntimeException("ArmNN: RequestThread_1_3: invalid message type");
179*3e777be0SXin Li }
180*3e777be0SXin Li }
181*3e777be0SXin Li }
182*3e777be0SXin Li
183*3e777be0SXin Li ///
184*3e777be0SXin Li /// Class template specializations
185*3e777be0SXin Li ///
186*3e777be0SXin Li
187*3e777be0SXin Li template class RequestThread_1_3<ArmnnPreparedModel_1_3, hal_1_3::HalPolicy, CallbackContext_1_3>;
188*3e777be0SXin Li
189*3e777be0SXin Li } // namespace armnn_driver
190