|
a/libupnpp/device/device.cxx |
|
b/libupnpp/device/device.cxx |
|
... |
|
... |
31 |
using namespace UPnPP;
|
31 |
using namespace UPnPP;
|
32 |
|
32 |
|
33 |
namespace UPnPProvider {
|
33 |
namespace UPnPProvider {
|
34 |
|
34 |
|
35 |
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
|
35 |
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
|
|
|
36 |
PTMutexInit o_devices_lock;
|
36 |
|
37 |
|
37 |
static bool vectorstoargslists(const vector<string>& names,
|
38 |
static bool vectorstoargslists(const vector<string>& names,
|
38 |
const vector<string>& values,
|
39 |
const vector<string>& values,
|
39 |
vector<string>& qvalues,
|
40 |
vector<string>& qvalues,
|
40 |
vector<const char *>& cnames,
|
41 |
vector<const char *>& cnames,
|
|
... |
|
... |
60 |
|
61 |
|
61 |
static const int expiretime = 3600;
|
62 |
static const int expiretime = 3600;
|
62 |
|
63 |
|
63 |
UpnpDevice::UpnpDevice(const string& deviceId,
|
64 |
UpnpDevice::UpnpDevice(const string& deviceId,
|
64 |
const unordered_map<string, string>& xmlfiles)
|
65 |
const unordered_map<string, string>& xmlfiles)
|
65 |
: m_deviceId(deviceId), m_needExit(false)
|
66 |
: m_deviceId(deviceId), m_needExit(false), m_evloopcond(PTHREAD_COND_INITIALIZER)
|
66 |
{
|
67 |
{
|
67 |
//LOGDEB("UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl);
|
68 |
//LOGDEB("UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl);
|
68 |
|
69 |
|
69 |
m_lib = LibUPnP::getLibUPnP(true);
|
70 |
m_lib = LibUPnP::getLibUPnP(true);
|
70 |
if (!m_lib) {
|
71 |
if (!m_lib) {
|
|
... |
|
... |
76 |
m_lib->errAsString("main", m_lib->getInitError()) << endl);
|
77 |
m_lib->errAsString("main", m_lib->getInitError()) << endl);
|
77 |
m_lib = 0;
|
78 |
m_lib = 0;
|
78 |
return;
|
79 |
return;
|
79 |
}
|
80 |
}
|
80 |
|
81 |
|
|
|
82 |
{
|
|
|
83 |
PTMutexLocker lock(o_devices_lock);
|
81 |
if (o_devices.empty()) {
|
84 |
if (o_devices.empty()) {
|
82 |
// First call: init callbacks
|
85 |
// First call: init callbacks
|
83 |
m_lib->registerHandler(UPNP_CONTROL_ACTION_REQUEST, sCallBack, this);
|
86 |
m_lib->registerHandler(UPNP_CONTROL_ACTION_REQUEST, sCallBack, this);
|
84 |
m_lib->registerHandler(UPNP_CONTROL_GET_VAR_REQUEST, sCallBack, this);
|
87 |
m_lib->registerHandler(UPNP_CONTROL_GET_VAR_REQUEST, sCallBack, this);
|
85 |
m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
|
88 |
m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
|
|
|
89 |
}
|
|
|
90 |
o_devices[m_deviceId] = this;
|
86 |
}
|
91 |
}
|
87 |
|
92 |
|
88 |
VirtualDir* theVD = VirtualDir::getVirtualDir();
|
93 |
VirtualDir* theVD = VirtualDir::getVirtualDir();
|
89 |
if (theVD == 0) {
|
94 |
if (theVD == 0) {
|
90 |
LOGFAT("UpnpDevice::UpnpDevice: can't get VirtualDir" << endl);
|
95 |
LOGFAT("UpnpDevice::UpnpDevice: can't get VirtualDir" << endl);
|
|
... |
|
... |
112 |
}
|
117 |
}
|
113 |
|
118 |
|
114 |
if ((ret = UpnpSendAdvertisement(m_dvh, expiretime)) != 0) {
|
119 |
if ((ret = UpnpSendAdvertisement(m_dvh, expiretime)) != 0) {
|
115 |
LOGERR("UpnpDevice: UpnpSendAdvertisement error: " << ret << endl);
|
120 |
LOGERR("UpnpDevice: UpnpSendAdvertisement error: " << ret << endl);
|
116 |
}
|
121 |
}
|
117 |
|
|
|
118 |
o_devices[m_deviceId] = this;
|
|
|
119 |
}
|
122 |
}
|
120 |
|
123 |
|
121 |
UpnpDevice::~UpnpDevice()
|
124 |
UpnpDevice::~UpnpDevice()
|
122 |
{
|
125 |
{
|
123 |
UpnpUnRegisterRootDevice(m_dvh);
|
126 |
UpnpUnRegisterRootDevice(m_dvh);
|
124 |
}
|
|
|
125 |
|
127 |
|
126 |
static PTMutexInit cblock;
|
128 |
PTMutexLocker lock(o_devices_lock);
|
|
|
129 |
unordered_map<std::string, UpnpDevice *>::iterator it = o_devices.find(m_deviceId);
|
|
|
130 |
if (it != o_devices.end())
|
|
|
131 |
o_devices.erase(it);
|
|
|
132 |
}
|
127 |
|
133 |
|
128 |
// Main libupnp callback: use the device id and call the right device
|
134 |
// Main libupnp callback: use the device id and call the right device
|
129 |
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
|
135 |
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
|
130 |
{
|
136 |
{
|
131 |
//LOGDEB("UpnpDevice::sCallBack" << endl);
|
137 |
//LOGDEB("UpnpDevice::sCallBack" << endl);
|
132 |
PTMutexLocker lock(cblock);
|
|
|
133 |
|
138 |
|
134 |
string deviceid;
|
139 |
string deviceid;
|
135 |
switch (et) {
|
140 |
switch (et) {
|
136 |
case UPNP_CONTROL_ACTION_REQUEST:
|
141 |
case UPNP_CONTROL_ACTION_REQUEST:
|
137 |
deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
|
142 |
deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
|
|
... |
|
... |
149 |
LOGERR("UpnpDevice::sCallBack: unknown event " << et << endl);
|
154 |
LOGERR("UpnpDevice::sCallBack: unknown event " << et << endl);
|
150 |
return UPNP_E_INVALID_PARAM;
|
155 |
return UPNP_E_INVALID_PARAM;
|
151 |
}
|
156 |
}
|
152 |
// LOGDEB("UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl);
|
157 |
// LOGDEB("UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl);
|
153 |
|
158 |
|
154 |
unordered_map<std::string, UpnpDevice *>::iterator it =
|
159 |
unordered_map<std::string, UpnpDevice *>::iterator it;
|
|
|
160 |
{
|
|
|
161 |
PTMutexLocker lock(o_devices_lock);
|
|
|
162 |
|
155 |
o_devices.find(deviceid);
|
163 |
it = o_devices.find(deviceid);
|
156 |
|
164 |
|
157 |
if (it == o_devices.end()) {
|
165 |
if (it == o_devices.end()) {
|
158 |
LOGERR("UpnpDevice::sCallBack: Device not found: [" <<
|
166 |
LOGERR("UpnpDevice::sCallBack: Device not found: [" <<
|
159 |
deviceid << "]" << endl);
|
167 |
deviceid << "]" << endl);
|
160 |
return UPNP_E_INVALID_PARAM;
|
168 |
return UPNP_E_INVALID_PARAM;
|
161 |
}
|
169 |
}
|
|
|
170 |
}
|
|
|
171 |
|
162 |
// LOGDEB("UpnpDevice::sCallBack: device found: [" << it->second
|
172 |
// LOGDEB("UpnpDevice::sCallBack: device found: [" << it->second
|
163 |
// << "]" << endl);
|
173 |
// << "]" << endl);
|
164 |
return (it->second)->callBack(et, evp);
|
174 |
return (it->second)->callBack(et, evp);
|
|
|
175 |
}
|
|
|
176 |
|
|
|
177 |
unordered_map<string, UpnpService*>::const_iterator UpnpDevice::findService(const string& serviceid)
|
|
|
178 |
{
|
|
|
179 |
PTMutexLocker lock(m_lock);
|
|
|
180 |
auto servit = m_servicemap.find(serviceid);
|
|
|
181 |
if (servit == m_servicemap.end()) {
|
|
|
182 |
LOGERR("UpnpDevice: Bad serviceID: " << serviceid << endl);
|
|
|
183 |
}
|
|
|
184 |
return servit;
|
165 |
}
|
185 |
}
|
166 |
|
186 |
|
167 |
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
|
187 |
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
|
168 |
{
|
188 |
{
|
169 |
switch (et) {
|
189 |
switch (et) {
|
170 |
case UPNP_CONTROL_ACTION_REQUEST:
|
190 |
case UPNP_CONTROL_ACTION_REQUEST:
|
171 |
{
|
191 |
{
|
172 |
struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
|
192 |
struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
|
|
|
193 |
|
173 |
DOMString pdoc = ixmlPrintDocument(act->ActionRequest);
|
194 |
DOMString pdoc = ixmlPrintDocument(act->ActionRequest);
|
174 |
LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
|
195 |
LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
|
175 |
". Params: " << pdoc << endl);
|
196 |
". Params: " << pdoc << endl);
|
176 |
ixmlFreeDOMString(pdoc);
|
197 |
ixmlFreeDOMString(pdoc);
|
177 |
|
198 |
|
178 |
unordered_map<string, UpnpService*>::const_iterator servit =
|
199 |
auto servit = findService(act->ServiceID);
|
179 |
m_servicemap.find(act->ServiceID);
|
|
|
180 |
if (servit == m_servicemap.end()) {
|
200 |
if (servit == m_servicemap.end()) {
|
181 |
LOGERR("Bad serviceID" << endl);
|
|
|
182 |
return UPNP_E_INVALID_PARAM;
|
201 |
return UPNP_E_INVALID_PARAM;
|
183 |
}
|
202 |
}
|
184 |
const string& servicetype = servit->second->getServiceType();
|
|
|
185 |
|
203 |
|
186 |
unordered_map<string, soapfun>::iterator callit =
|
|
|
187 |
m_calls.find(string(act->ActionName) + string(act->ServiceID));
|
|
|
188 |
if (callit == m_calls.end()) {
|
|
|
189 |
LOGINF("No such action: " << act->ActionName << endl);
|
|
|
190 |
return UPNP_E_INVALID_PARAM;
|
|
|
191 |
}
|
|
|
192 |
|
|
|
193 |
SoapArgs sc;
|
|
|
194 |
if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
|
|
|
195 |
LOGERR("Error decoding Action call arguments" << endl);
|
|
|
196 |
return UPNP_E_INVALID_PARAM;
|
|
|
197 |
}
|
|
|
198 |
SoapData dt;
|
204 |
SoapData dt;
|
|
|
205 |
{
|
|
|
206 |
PTMutexLocker lock(m_lock);
|
|
|
207 |
const string& servicetype = servit->second->getServiceType();
|
|
|
208 |
auto callit = m_calls.find(string(act->ActionName) + string(act->ServiceID));
|
|
|
209 |
if (callit == m_calls.end()) {
|
|
|
210 |
LOGINF("UpnpDevice: No such action: " << act->ActionName << endl);
|
|
|
211 |
return UPNP_E_INVALID_PARAM;
|
|
|
212 |
}
|
|
|
213 |
|
|
|
214 |
SoapArgs sc;
|
|
|
215 |
if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
|
|
|
216 |
LOGERR("Error decoding Action call arguments" << endl);
|
|
|
217 |
return UPNP_E_INVALID_PARAM;
|
|
|
218 |
}
|
199 |
dt.name = act->ActionName;
|
219 |
dt.name = act->ActionName;
|
200 |
dt.serviceType = servicetype;
|
220 |
dt.serviceType = servicetype;
|
201 |
|
221 |
|
202 |
// Call the action routine
|
222 |
// Call the action routine
|
203 |
int ret = callit->second(sc, dt);
|
223 |
int ret = callit->second(sc, dt);
|
204 |
if (ret != UPNP_E_SUCCESS) {
|
224 |
if (ret != UPNP_E_SUCCESS) {
|
205 |
LOGERR("Action failed: " << sc.name << endl);
|
225 |
LOGERR("UpnpDevice: Action failed: " << sc.name << endl);
|
206 |
return ret;
|
226 |
return ret;
|
|
|
227 |
}
|
207 |
}
|
228 |
}
|
208 |
|
229 |
|
209 |
// Encode result data
|
230 |
// Encode result data
|
210 |
act->ActionResult = buildSoapBody(dt);
|
231 |
act->ActionResult = buildSoapBody(dt);
|
211 |
|
232 |
|
212 |
//{DOMString pdoc = ixmlPrintDocument(act->ActionResult);
|
233 |
//{DOMString pdoc = ixmlPrintDocument(act->ActionResult);
|
213 |
//LOGDEB("Response data: " << pdoc << endl);
|
234 |
//LOGDEB("Response data: " << pdoc << endl);
|
214 |
//ixmlFreeDOMString(pdoc);
|
235 |
//ixmlFreeDOMString(pdoc);
|
215 |
//}
|
236 |
//}
|
216 |
|
237 |
|
217 |
return ret;
|
238 |
return UPNP_E_SUCCESS;
|
218 |
}
|
239 |
}
|
219 |
break;
|
240 |
break;
|
220 |
|
241 |
|
221 |
case UPNP_CONTROL_GET_VAR_REQUEST:
|
242 |
case UPNP_CONTROL_GET_VAR_REQUEST:
|
222 |
// Note that the "Control: query for variable" action is
|
243 |
// Note that the "Control: query for variable" action is
|
|
... |
|
... |
232 |
{
|
253 |
{
|
233 |
struct Upnp_Subscription_Request *act =
|
254 |
struct Upnp_Subscription_Request *act =
|
234 |
(struct Upnp_Subscription_Request*)evp;
|
255 |
(struct Upnp_Subscription_Request*)evp;
|
235 |
LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl);
|
256 |
LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl);
|
236 |
|
257 |
|
237 |
unordered_map<string, UpnpService*>::const_iterator servit =
|
258 |
auto servit = findService(act->ServiceId);
|
238 |
m_servicemap.find(act->ServiceId);
|
|
|
239 |
if (servit == m_servicemap.end()) {
|
259 |
if (servit == m_servicemap.end()) {
|
240 |
LOGERR("Bad serviceID" << endl);
|
|
|
241 |
return UPNP_E_INVALID_PARAM;
|
260 |
return UPNP_E_INVALID_PARAM;
|
242 |
}
|
261 |
}
|
243 |
|
262 |
|
244 |
vector<string> names, values, qvalues;
|
263 |
vector<string> names, values, qvalues;
|
|
|
264 |
{
|
|
|
265 |
PTMutexLocker lock(m_lock);
|
245 |
if (!servit->second->getEventData(true, names, values)) {
|
266 |
if (!servit->second->getEventData(true, names, values)) {
|
246 |
break;
|
267 |
break;
|
247 |
}
|
268 |
}
|
|
|
269 |
}
|
|
|
270 |
|
248 |
vector<const char *> cnames, cvalues;
|
271 |
vector<const char *> cnames, cvalues;
|
249 |
vectorstoargslists(names, values, qvalues, cnames, cvalues);
|
272 |
vectorstoargslists(names, values, qvalues, cnames, cvalues);
|
250 |
int ret =
|
273 |
int ret =
|
251 |
UpnpAcceptSubscription(m_dvh, act->UDN, act->ServiceId,
|
274 |
UpnpAcceptSubscription(m_dvh, act->UDN, act->ServiceId,
|
252 |
&cnames[0], &cvalues[0],
|
275 |
&cnames[0], &cvalues[0],
|
|
... |
|
... |
268 |
return UPNP_E_INVALID_PARAM;
|
291 |
return UPNP_E_INVALID_PARAM;
|
269 |
}
|
292 |
}
|
270 |
|
293 |
|
271 |
void UpnpDevice::addService(UpnpService *serv, const std::string& serviceId)
|
294 |
void UpnpDevice::addService(UpnpService *serv, const std::string& serviceId)
|
272 |
{
|
295 |
{
|
|
|
296 |
PTMutexLocker lock(m_lock);
|
273 |
m_servicemap[serviceId] = serv;
|
297 |
m_servicemap[serviceId] = serv;
|
274 |
m_serviceids.push_back(serviceId);
|
298 |
m_serviceids.push_back(serviceId);
|
275 |
}
|
299 |
}
|
276 |
|
300 |
|
277 |
void UpnpDevice::addActionMapping(const UpnpService* serv,
|
301 |
void UpnpDevice::addActionMapping(const UpnpService* serv,
|
278 |
const std::string& actName,
|
302 |
const std::string& actName,
|
279 |
soapfun fun)
|
303 |
soapfun fun)
|
280 |
{
|
304 |
{
|
|
|
305 |
PTMutexLocker lock(m_lock);
|
281 |
// LOGDEB("UpnpDevice::addActionMapping:" << actName << endl);
|
306 |
// LOGDEB("UpnpDevice::addActionMapping:" << actName << endl);
|
282 |
m_calls[actName + serv->getServiceId()] = fun;
|
307 |
m_calls[actName + serv->getServiceId()] = fun;
|
283 |
}
|
308 |
}
|
284 |
|
309 |
|
285 |
void UpnpDevice::notifyEvent(const string& serviceId,
|
310 |
void UpnpDevice::notifyEvent(const string& serviceId,
|
|
... |
|
... |
299 |
int(cnames.size()));
|
324 |
int(cnames.size()));
|
300 |
if (ret != UPNP_E_SUCCESS) {
|
325 |
if (ret != UPNP_E_SUCCESS) {
|
301 |
LOGERR("UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl);
|
326 |
LOGERR("UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl);
|
302 |
}
|
327 |
}
|
303 |
}
|
328 |
}
|
304 |
|
|
|
305 |
static pthread_cond_t evloopcond = PTHREAD_COND_INITIALIZER;
|
|
|
306 |
|
329 |
|
307 |
int timespec_diffms(const struct timespec& old, const struct timespec& recent)
|
330 |
int timespec_diffms(const struct timespec& old, const struct timespec& recent)
|
308 |
{
|
331 |
{
|
309 |
return (recent.tv_sec - old.tv_sec) * 1000 +
|
332 |
return (recent.tv_sec - old.tv_sec) * 1000 +
|
310 |
(recent.tv_nsec - old.tv_nsec) / (1000 * 1000);
|
333 |
(recent.tv_nsec - old.tv_nsec) / (1000 * 1000);
|
|
... |
|
... |
324 |
}
|
347 |
}
|
325 |
#endif // ! CLOCK_REALTIME
|
348 |
#endif // ! CLOCK_REALTIME
|
326 |
|
349 |
|
327 |
// Loop on services, and poll each for changed data. Generate event
|
350 |
// Loop on services, and poll each for changed data. Generate event
|
328 |
// only if changed data exists. Every now and then, we generate an
|
351 |
// only if changed data exists. Every now and then, we generate an
|
329 |
// artificial event with all the current state.
|
352 |
// artificial event with all the current state. This is normally run by the main thread.
|
330 |
void UpnpDevice::eventloop()
|
353 |
void UpnpDevice::eventloop()
|
331 |
{
|
354 |
{
|
332 |
int count = 0;
|
355 |
int count = 0;
|
333 |
const int loopwait_ms = 1000; // Polling the services every 1 S
|
356 |
const int loopwait_ms = 1000; // Polling the services every 1 S
|
334 |
const int nloopstofull = 10; // Full state every 10 S
|
357 |
const int nloopstofull = 10; // Full state every 10 S
|
|
... |
|
... |
341 |
timespec_addnanos(&wkuptime, loopwait_ms * 1000 * 1000);
|
364 |
timespec_addnanos(&wkuptime, loopwait_ms * 1000 * 1000);
|
342 |
|
365 |
|
343 |
//LOGDEB("eventloop: now " << time(0) << " wkup at "<<
|
366 |
//LOGDEB("eventloop: now " << time(0) << " wkup at "<<
|
344 |
// wkuptime.tv_sec << " S " << wkuptime.tv_nsec << " ns" << endl);
|
367 |
// wkuptime.tv_sec << " S " << wkuptime.tv_nsec << " ns" << endl);
|
345 |
|
368 |
|
346 |
PTMutexLocker lock(cblock);
|
369 |
PTMutexLocker lock(m_evlooplock);
|
347 |
int err = pthread_cond_timedwait(&evloopcond, lock.getMutex(),
|
370 |
int err = pthread_cond_timedwait(&m_evloopcond, lock.getMutex(),
|
348 |
&wkuptime);
|
371 |
&wkuptime);
|
349 |
if (m_needExit) {
|
372 |
if (m_needExit) {
|
350 |
break;
|
373 |
break;
|
351 |
} else if (err && err != ETIMEDOUT) {
|
374 |
} else if (err && err != ETIMEDOUT) {
|
352 |
LOGINF("UpnpDevice:eventloop: wait errno " << errno << endl);
|
375 |
LOGINF("UpnpDevice:eventloop: wait errno " << errno << endl);
|
|
... |
|
... |
380 |
|
403 |
|
381 |
count++;
|
404 |
count++;
|
382 |
bool all = count && ((count % nloopstofull) == 0);
|
405 |
bool all = count && ((count % nloopstofull) == 0);
|
383 |
//LOGDEB("UpnpDevice::eventloop count "<<count<<" all "<<all<<endl);
|
406 |
//LOGDEB("UpnpDevice::eventloop count "<<count<<" all "<<all<<endl);
|
384 |
|
407 |
|
385 |
for (vector<string>::const_iterator it =
|
408 |
// We can't lock m_lock around the loop because we don't want
|
|
|
409 |
// to hold id when calling notifyEvent() (which calls
|
|
|
410 |
// libupnp). This means that we should have a separate lock
|
|
|
411 |
// for the services arrays. This would only be useful during
|
|
|
412 |
// startup, while we add services, but the event loop is the
|
|
|
413 |
// last call the main program will make after adding the
|
|
|
414 |
// services, so locking does not seem necessary
|
386 |
m_serviceids.begin(); it != m_serviceids.end(); it++) {
|
415 |
for (auto it = m_serviceids.begin(); it != m_serviceids.end(); it++) {
|
387 |
vector<string> names, values;
|
416 |
vector<string> names, values;
|
|
|
417 |
{
|
|
|
418 |
PTMutexLocker lock(m_lock);
|
388 |
UpnpService* serv = m_servicemap[*it];
|
419 |
UpnpService* serv = m_servicemap[*it];
|
389 |
if (!serv->getEventData(all, names, values) || names.empty()) {
|
420 |
if (!serv->getEventData(all, names, values) || names.empty()) {
|
390 |
continue;
|
421 |
continue;
|
|
|
422 |
}
|
391 |
}
|
423 |
}
|
392 |
notifyEvent(*it, names, values);
|
424 |
notifyEvent(*it, names, values);
|
393 |
}
|
425 |
}
|
394 |
}
|
426 |
}
|
395 |
}
|
427 |
}
|
396 |
|
428 |
|
|
|
429 |
// Can't take the loop lock here. We're called from the service and
|
|
|
430 |
// hold the device lock. The locks would be taken in opposite order,
|
|
|
431 |
// causing a potential deadlock:
|
|
|
432 |
// - device action takes device lock
|
|
|
433 |
// - loop wakes up, takes loop lock
|
|
|
434 |
// - blocks on device lock before calling getevent
|
|
|
435 |
// - device calls loopwakeup which blocks on loop lock
|
|
|
436 |
// -> deadlock
|
397 |
void UpnpDevice::loopWakeup()
|
437 |
void UpnpDevice::loopWakeup()
|
398 |
{
|
438 |
{
|
399 |
pthread_cond_broadcast(&evloopcond);
|
439 |
pthread_cond_broadcast(&m_evloopcond);
|
400 |
}
|
440 |
}
|
401 |
|
441 |
|
402 |
void UpnpDevice::shouldExit()
|
442 |
void UpnpDevice::shouldExit()
|
403 |
{
|
443 |
{
|
404 |
m_needExit = true;
|
444 |
m_needExit = true;
|
405 |
pthread_cond_broadcast(&evloopcond);
|
445 |
pthread_cond_broadcast(&m_evloopcond);
|
406 |
}
|
446 |
}
|
407 |
|
447 |
|
408 |
}// End namespace UPnPProvider
|
448 |
}// End namespace UPnPProvider
|