1 /* ------------------------------------------------------------------ 2 * Copyright (C) 1998-2009 PacketVideo 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 * express or implied. 14 * See the License for the specific language governing permissions 15 * and limitations under the License. 16 * ------------------------------------------------------------------- 17 */ 18 19 20 #ifndef PV_INTERFACE_PROXY_H_INCLUDED 21 #include "pv_interface_proxy.h" 22 #endif 23 24 #ifndef PV_INTERFACE_PROXY_HANDLER_H_INCLUDED 25 #include "pv_interface_proxy_handler.h" 26 #endif 27 28 #ifndef PV_INTERFACE_PROXY_NOTIFIER_H_INCLUDED 29 #include "pv_interface_proxy_notifier.h" 30 #endif 31 32 #ifndef OSCL_DLL_H_INCLUDED 33 #include "oscl_dll.h" 34 #endif 35 36 #include "oscl_mem.h" 37 38 #include "pvlogger.h" 39 40 #include "oscl_scheduler.h" 41 #include "oscl_error.h" 42 #include "oscl_error_trapcleanup.h" 43 #include "oscl_tls.h" 44 45 46 OSCL_DLL_ENTRY_POINT_DEFAULT() 47 48 49 // 50 //CPVInterfaceProxy 51 // 52 53 OSCL_EXPORT_REF CPVInterfaceProxy * CPVInterfaceProxy::NewL( 54 PVProxiedEngine& app 55 , Oscl_DefAlloc *alloc 56 , int32 stacksize 57 , uint32 nreserve1 58 , uint32 nreserve2 59 , int32 handlerPri 60 , int32 notifierPri) 61 //called under app thread context 62 { 63 OsclMemAllocator defallocL; 64 OsclAny *ptr = NULL; 65 if (alloc) 66 { 67 ptr = alloc->ALLOCATE(sizeof(CPVInterfaceProxy)); 68 OsclError::LeaveIfNull(ptr); 69 } 70 else 71 { 72 ptr = defallocL.ALLOCATE(sizeof(CPVInterfaceProxy)); 73 } 74 CPVInterfaceProxy *self = OSCL_PLACEMENT_NEW(ptr, CPVInterfaceProxy(app, alloc, stacksize)); 75 int32 err; 76 err = self->CPVIConstructL(nreserve1, nreserve2, handlerPri, notifierPri); 77 if (err != OSCL_ERR_NONE) 78 { 79 self->Delete(); 80 return NULL; 81 } 82 return self; 83 } 84 85 OSCL_EXPORT_REF CPVInterfaceProxy::CPVInterfaceProxy(PVProxiedEngine& app, Oscl_DefAlloc*alloc, int32 stacksize) 86 : iPVApp(app) 87 //called under app thread context 88 { 89 iCommandIdCounter = 0; 90 iProxyIdCounter = 0; 91 iHandler = NULL; 92 iPVScheduler = NULL; 93 iNotifier = NULL; 94 iStacksize = stacksize; 95 iStopped = true; 96 iAlloc = (alloc) ? alloc : &iDefAlloc; 97 iLogger = NULL; 98 } 99 100 OSCL_EXPORT_REF void CPVInterfaceProxy::ConstructL(uint32 nreserve1, uint32 nreserve2, int32 handlerPri, int32 notifierPri) 101 { 102 // Create the semaphores and critical sections 103 if (iInitSem.Create() != OsclProcStatus::SUCCESS_ERROR 104 || iExitedSem.Create() != OsclProcStatus::SUCCESS_ERROR 105 || iCounterCrit.Create() != OsclProcStatus::SUCCESS_ERROR 106 || iHandlerQueueCrit.Create() != OsclProcStatus::SUCCESS_ERROR 107 || iNotifierQueueCrit.Create() != OsclProcStatus::SUCCESS_ERROR 108 || iProxyListCrit.Create() != OsclProcStatus::SUCCESS_ERROR) 109 { 110 OsclError::Leave(OsclErrGeneral); 111 } 112 //reserve space in vectors... 113 if (nreserve1 > 0) 114 iProxyList.reserve(nreserve1); 115 if (nreserve2 > 0) 116 { 117 iCommandQueue.reserve(nreserve2); 118 // iNotificationQueue.reserve(nreserve2); 119 } 120 121 //create handler 122 OsclAny *ptr = iAlloc->ALLOCATE(sizeof(CPVInterfaceProxyHandler)); 123 OsclError::LeaveIfNull(ptr); 124 iHandler = OSCL_PLACEMENT_NEW(ptr, CPVInterfaceProxyHandler(this, handlerPri)); 125 126 //create notifier 127 ptr = iAlloc->ALLOCATE(sizeof(CPVInterfaceProxyNotifier)); 128 OsclError::LeaveIfNull(ptr); 129 iNotifier = OSCL_PLACEMENT_NEW(ptr, CPVInterfaceProxyNotifier(this, notifierPri)); 130 } 131 132 OSCL_EXPORT_REF void CPVInterfaceProxy::Delete() 133 //called under app thread context 134 { 135 this->~CPVInterfaceProxy(); 136 iAlloc->deallocate(this); 137 } 138 139 OSCL_EXPORT_REF CPVInterfaceProxy::~CPVInterfaceProxy() 140 //called under app thread context 141 { 142 //make sure thread was stopped. 143 StopPVThread(); 144 145 CleanupAppThreadQueues(); 146 147 //delete handler and notifier 148 if (iHandler) 149 { 150 iHandler->~CPVInterfaceProxyHandler(); 151 iAlloc->deallocate(iHandler); 152 } 153 iHandler = NULL; 154 if (iNotifier) 155 { 156 iNotifier->~CPVInterfaceProxyNotifier(); 157 iAlloc->deallocate(iNotifier); 158 } 159 iNotifier = NULL; 160 161 iCounterCrit.Close(); 162 iHandlerQueueCrit.Close(); 163 iNotifierQueueCrit.Close(); 164 iProxyListCrit.Close(); 165 iInitSem.Close(); 166 iExitedSem.Close(); 167 } 168 169 //forward... 170 TOsclThreadFuncRet OSCL_THREAD_DECL pvproxythreadmain(TOsclThreadFuncArg *aPtr); 171 172 OSCL_EXPORT_REF bool CPVInterfaceProxy::StartPVThread() 173 //called under app thread context 174 { 175 if (!iStopped) 176 return false;//thread already active 177 178 //add notifier AO to app thread scheduler, if it has one. 179 if (PVThreadContext::ThreadHasScheduler()) 180 { 181 //Add notification active object to API thread. 182 iNotifier->AddToScheduler(); 183 iNotifier->PendForExec(); 184 } 185 186 // Create the PV thread. 187 OsclProcStatus::eOsclProcError err; 188 err = iPVThread.Create((TOsclThreadFuncPtr)pvproxythreadmain, 189 iStacksize, 190 (TOsclThreadFuncArg)this); 191 if (err == OSCL_ERR_NONE) 192 { 193 iStopped = false; 194 //Wait for PV thread to initialize its scheduler. 195 if (iInitSem.Wait() != OsclProcStatus::SUCCESS_ERROR) 196 OsclError::Leave(OsclErrSystemCallFailed);//unexpected sem error 197 return true; 198 } 199 else 200 { 201 //error cleanup 202 iNotifier->RemoveFromScheduler(); 203 return false; 204 } 205 } 206 207 OSCL_EXPORT_REF void CPVInterfaceProxy::StopPVThread() 208 //called under app thread context. 209 { 210 //if called under the PV thread, we'll get deadlock.. 211 //so don't allow it. 212 if (iPVThreadContext.IsSameThreadContext()) 213 OsclError::Leave(OsclErrThreadContextIncorrect); 214 215 if (iStopped) 216 return ; 217 218 //deque notifier AO 219 iNotifierQueueCrit.Lock(); 220 if (iNotifier && iNotifier->IsAdded()) 221 iNotifier->RemoveFromScheduler(); 222 iNotifierQueueCrit.Unlock(); 223 224 //Stop the scheduler loop. 225 if (iPVScheduler) 226 iPVScheduler->StopScheduler(); 227 228 //Wait for PV thread to finish up, then it's safe 229 //to delete the remaining stuff. 230 if (iExitedSem.Wait() != OsclProcStatus::SUCCESS_ERROR) 231 OsclError::Leave(OsclErrSystemCallFailed);//unexpected sem error 232 233 //let the destructor know it's exited. 234 iStopped = true; 235 236 //The thread will exit on its own, but go ahead and 237 //forcibly terminate it now to make sure it's cleaned 238 //up by the time this call returns. 239 iPVThread.Terminate(0); 240 } 241 242 OSCL_EXPORT_REF void CPVInterfaceProxy::DeliverNotifications(int32 aTargetCount, int32& aNoticesPending) 243 //deliver notifications off the queue, from the app thread size 244 { 245 //make sure this isn't called under PV thread... 246 if (iPVThreadContext.IsSameThreadContext()) 247 OsclError::Leave(OsclErrThreadContextIncorrect); 248 249 for (int32 count = 0; count < aTargetCount;) 250 { 251 //get next notification or cleanup message. 252 iNotifierQueueCrit.Lock(); 253 CPVProxyMsg notice(0, 0, NULL); 254 if (iNotificationQueue.size() > 0) 255 { 256 notice = iNotificationQueue[0]; 257 iNotificationQueue.erase(&iNotificationQueue[0]); 258 } 259 iNotifierQueueCrit.Unlock(); 260 261 if (notice.iMsg) 262 { 263 count++; 264 CPVProxyInterface *ext = FindInterface(notice.iProxyId); 265 if (ext) 266 ext->iClient->HandleNotification(notice.iMsgId, notice.iMsg); 267 else 268 { //since messages are cleaned up when interfaces 269 //get unregistered, we should not get here. 270 OSCL_ASSERT(0);//debug error. 271 } 272 } 273 else 274 break;//no more messages. 275 } 276 //return number of notices left after trying to process 277 //the desired number. 278 iNotifierQueueCrit.Lock(); 279 aNoticesPending = iNotificationQueue.size(); 280 iNotifierQueueCrit.Unlock(); 281 } 282 283 void CPVInterfaceProxy::CleanupAppThreadQueues() 284 //cleanup memory allocated in App thread. 285 { 286 //un-sent commands... 287 iHandlerQueueCrit.Lock(); 288 while (!iCommandQueue.empty()) 289 { 290 CPVProxyMsg *msg = &iCommandQueue[0]; 291 CPVProxyInterface *proxy = FindInterface(msg->iProxyId); 292 if (proxy) 293 proxy->iClient->CleanupCommand(msg->iMsgId, msg->iMsg); 294 iCommandQueue.erase(msg); 295 } 296 iCommandQueue.clear(); 297 iCommandQueue.destroy(); 298 iHandlerQueueCrit.Unlock(); 299 300 //proxy list... 301 iProxyListCrit.Lock(); 302 iProxyList.clear(); 303 iProxyList.destroy(); 304 iProxyListCrit.Unlock(); 305 } 306 307 void CPVInterfaceProxy::CleanupPVThreadQueues() 308 //cleanup memory allocated in PV thread. 309 { 310 //un-sent notifications 311 iNotifierQueueCrit.Lock(); 312 while (!iNotificationQueue.empty()) 313 { 314 CPVProxyMsg *msg = &iNotificationQueue[0]; 315 CPVProxyInterface *proxy = FindInterface(msg->iProxyId); 316 if (proxy) 317 proxy->iServer->CleanupNotification(msg->iMsgId, msg->iMsg); 318 iNotificationQueue.erase(msg); 319 } 320 iNotificationQueue.clear(); 321 iNotificationQueue.destroy(); 322 iNotifierQueueCrit.Unlock(); 323 } 324 325 OSCL_EXPORT_REF void CPVInterfaceProxy::CancelCommand(TPVProxyId aProxyId, TPVProxyMsgId aMsgId) 326 { 327 CleanupCommands(FindInterface(aProxyId), false, aMsgId); 328 } 329 330 OSCL_EXPORT_REF void CPVInterfaceProxy::CancelAllCommands(TPVProxyId aProxyId) 331 { 332 CleanupCommands(FindInterface(aProxyId), true); 333 } 334 335 void CPVInterfaceProxy::CleanupCommands(CPVProxyInterface *aExt, bool aAll, TPVProxyMsgId aMsgId) 336 { 337 if (!aExt) 338 return ; 339 iHandlerQueueCrit.Lock(); 340 for (uint32 i = 0; i < iCommandQueue.size(); i++) 341 { 342 CPVProxyMsg *msg = &iCommandQueue[i]; 343 if (msg->iProxyId == aExt->iProxyId 344 && (aAll || msg->iMsgId == aMsgId)) 345 { 346 aExt->iClient->CleanupCommand(msg->iMsgId, msg->iMsg); 347 iCommandQueue.erase(msg); 348 i--;//move back one since vecter gets scrunched. 349 if (!aAll) 350 { 351 iHandlerQueueCrit.Unlock(); 352 return ; 353 } 354 } 355 } 356 iHandlerQueueCrit.Unlock(); 357 } 358 359 OSCL_EXPORT_REF void CPVInterfaceProxy::CancelNotification(TPVProxyId aProxyId, TPVProxyMsgId aMsgId) 360 { 361 CleanupNotifications(FindInterface(aProxyId), false, aMsgId); 362 } 363 364 OSCL_EXPORT_REF void CPVInterfaceProxy::CancelAllNotifications(TPVProxyId aProxyId) 365 { 366 CleanupNotifications(FindInterface(aProxyId), true); 367 } 368 369 void CPVInterfaceProxy::CleanupNotifications(CPVProxyInterface *aExt, bool aAll, TPVProxyMsgId aMsgId) 370 { 371 if (!aExt) 372 return ; 373 iNotifierQueueCrit.Lock(); 374 for (uint i = 0; i < iNotificationQueue.size(); i++) 375 { 376 CPVProxyMsg *msg = &iNotificationQueue[i]; 377 if (msg->iProxyId == aExt->iProxyId 378 && (aAll || msg->iMsgId == aMsgId)) 379 { 380 aExt->iServer->CleanupNotification(msg->iMsgId, msg->iMsg); 381 iNotificationQueue.erase(msg); 382 i--;//move back one since vector gets scrunched. 383 if (!aAll) 384 { 385 iNotifierQueueCrit.Unlock(); 386 return ; 387 } 388 } 389 } 390 iNotifierQueueCrit.Unlock(); 391 } 392 393 void CPVInterfaceProxy::CleanupInterfaceMessages(CPVProxyInterface *aExt) 394 //cleanup all extension messages for a particular interface. 395 { 396 CleanupCommands(aExt, true); 397 CleanupNotifications(aExt, true); 398 } 399 400 OSCL_EXPORT_REF TPVProxyId CPVInterfaceProxy::RegisterProxiedInterface( 401 PVProxiedInterfaceServer& server_proxy, 402 PVProxiedInterfaceClient& client_proxy) 403 //Proxy extensions call this to register themselves. 404 { 405 TPVProxyId id = ++iProxyIdCounter; 406 iProxyListCrit.Lock(); 407 CPVProxyInterface proxy(id, &server_proxy, &client_proxy); 408 int32 err; 409 OSCL_TRY(err, iProxyList.push_back(proxy);); 410 iProxyListCrit.Unlock(); 411 OsclError::LeaveIfError(err); 412 return id; 413 } 414 415 OSCL_EXPORT_REF void CPVInterfaceProxy::UnregisterProxiedInterface(TPVProxyId aProxyId) 416 //Proxy extensions call this to unregister themselves. 417 { 418 iProxyListCrit.Lock(); 419 CPVProxyInterface *ext = FindInterface(aProxyId, true); 420 if (ext) 421 { 422 //cleanup unprocessed messages and remove. 423 CleanupInterfaceMessages(ext); 424 iProxyList.erase(ext); 425 } 426 iProxyListCrit.Unlock(); 427 } 428 429 OSCL_EXPORT_REF TPVProxyMsgId CPVInterfaceProxy::SendCommand(TPVProxyId aProxyId, OsclAny *aCmd) 430 //Proxy extensions call this to send commands from app side 431 //to PV side. 432 { 433 int32 err = OSCL_ERR_NONE; 434 iCounterCrit.Lock(); 435 TPVProxyMsgId id = ++iCommandIdCounter; 436 iCounterCrit.Unlock(); 437 iHandlerQueueCrit.Lock(); 438 CPVProxyMsg msg(aProxyId, id, aCmd); 439 OSCL_TRY(err, iCommandQueue.push_back(msg);); 440 //if the queue was empty, signal the AO. 441 //note: when the queue is empty it is safe to assume the handler AO has a request 442 //pending. 443 if (iCommandQueue.size() == 1) 444 iHandler->PendComplete(OSCL_REQUEST_ERR_NONE); 445 iHandlerQueueCrit.Unlock(); 446 //propagate any allocation failure... 447 OsclError::LeaveIfError(err); 448 return id; 449 } 450 451 OSCL_EXPORT_REF TPVProxyMsgId CPVInterfaceProxy::SendNotification(TPVProxyId aProxyId, OsclAny *aResp) 452 //Proxy extensions call this to send notifications from PV 453 //side to app side. 454 { 455 int32 err = OSCL_ERR_NONE; 456 iCounterCrit.Lock(); 457 TPVProxyMsgId id = ++iCommandIdCounter; 458 iCounterCrit.Unlock(); 459 iNotifierQueueCrit.Lock(); 460 CPVProxyMsg msg(aProxyId, id, aResp); 461 OSCL_TRY(err, iNotificationQueue.push_back(msg);); 462 //if the queue was empty and the notifier is scheduled, 463 //signal it. 464 //note: when the queue is empty it is safe to assume the notifier AO has a request 465 //pending. 466 if (iNotifier 467 && iNotifier->IsAdded() 468 && iNotificationQueue.size() == 1) 469 iNotifier->PendComplete(OSCL_REQUEST_ERR_NONE); 470 iNotifierQueueCrit.Unlock(); 471 //propagate any allocation failure... 472 OsclError::LeaveIfError(err); 473 return id; 474 } 475 476 CPVProxyInterface * CPVInterfaceProxy::FindInterface(TPVProxyId aId, bool locked) 477 //lookup a registered proxy interface 478 { 479 if (!locked) 480 iProxyListCrit.Lock(); 481 for (uint32 i = 0; i < iProxyList.size(); i++) 482 { 483 if (iProxyList[i].iProxyId == aId) 484 { 485 if (!locked) 486 iProxyListCrit.Unlock(); 487 return &iProxyList[i]; 488 } 489 } 490 if (!locked) 491 iProxyListCrit.Unlock(); 492 return NULL; 493 } 494 495 void CPVInterfaceProxy::InThread() 496 //this is the guts of the proxy thread routine. 497 //it's a separate routine since it needs to be 498 //run under a trap handler in order to avoid any 499 //Cbase 66 panic from the cleanup stack. 500 { 501 int32 errTerm(OsclErrNone); 502 int32 errSched(OsclErrNone); 503 504 //create & install scheduler 505 OsclScheduler::Init("PVProxy"); 506 iPVScheduler = OsclExecScheduler::Current(); 507 508 iPVThreadContext.EnterThreadContext(); 509 510 //add handler to scheduler 511 iHandler->AddToScheduler(); 512 iHandler->PendForExec(); 513 514 //App thread logon... 515 int32 err; 516 err = AppThreadLogon(); 517 if (err != OsclErrNone) 518 errTerm = err; 519 520 //Start scheduler. This call blocks until scheduler is 521 //either stopped or exits due to an error. 522 err = AppStartScheduler(); 523 if (err != OsclErrNone) 524 { 525 errSched = err; 526 PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_NOTICE, (0, "appstartscheduler error %d...", errSched)); 527 } 528 529 530 //Cleanup un-processed data. 531 CleanupPVThreadQueues(); 532 533 //App thread logoff... 534 err = AppThreadLogoff(); 535 if (err != OSCL_ERR_NONE) 536 errTerm = err; 537 538 //Deque the handler AO 539 iHandlerQueueCrit.Lock(); 540 iHandler->RemoveFromScheduler(); 541 iHandlerQueueCrit.Unlock(); 542 543 iPVThreadContext.ExitThreadContext(); 544 545 //Uninstall scheduler 546 OsclScheduler::Cleanup(); 547 iPVScheduler = NULL; 548 549 //Generate panics if any leaves happened. 550 OSCL_ASSERT(errTerm == OsclErrNone);//EPVProxyPanicEngineLeave 551 OSCL_ASSERT(errSched == OsclErrNone);//EPVProxyPanicSchedulerLeave 552 } 553 554 //////////////////////////////// 555 // OS-specific Thread routines 556 //////////////////////////////// 557 558 559 #include "oscl_mem_audit.h" 560 561 TOsclThreadFuncRet OSCL_THREAD_DECL pvproxythreadmain(TOsclThreadFuncArg *aPtr) 562 //PV Thread main routine 563 { 564 565 CPVInterfaceProxy *proxy = (CPVInterfaceProxy *) aPtr; 566 567 //Init OSCL and create logger. 568 OsclBase::Init(); 569 OsclErrorTrap::Init(); 570 OsclMem::Init(); 571 PVLogger::Init(); 572 573 //Call the proxied app routine to create its logger appenders. 574 proxy->iPVApp.CreateLoggerAppenders(); 575 proxy->iLogger = PVLogger::GetLoggerObject("pvproxy"); 576 577 PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, proxy->iLogger, PVLOGMSG_NOTICE, (0, "PVPROXY:Proxy Thread 0x%x Entry...", OsclExecScheduler::GetId())); 578 579 580 int32 leave; 581 OSCL_TRY(leave, proxy->InThread();); 582 583 if (leave != OsclErrNone) 584 { 585 PVLOGGER_LOGMSG(PVLOGMSG_INST_REL, proxy->iLogger, PVLOGMSG_ERR, (0, "PVPROXY:Proxy Thread 0x%x Exit: Leave Reason %d", OsclExecScheduler::GetId(), leave)); 586 } 587 else 588 { 589 PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, proxy->iLogger, PVLOGMSG_NOTICE, (0, "PVPROXY:Proxy Thread 0x%x Exit: Normal", OsclExecScheduler::GetId())); 590 } 591 592 //Cleanup logger. 593 PVLogger::Cleanup(); 594 595 OsclMem::Cleanup(); 596 proxy->iLogger = NULL; 597 OsclErrorTrap::Cleanup(); 598 OsclBase::Cleanup(); 599 600 //Signal & Exit 601 proxy->iExitedSem.Signal(); 602 603 return 0; 604 } 605 606 int32 CPVInterfaceProxy::CPVIConstructL(uint32 &aNreserve1, uint32 &aNreserve2, int32 &aHandlerPri, int32 &aNotifierPri) 607 { 608 int32 err; 609 OSCL_TRY(err, ConstructL(aNreserve1, aNreserve2, aHandlerPri, aNotifierPri);); 610 return err; 611 } 612 613 614 int32 CPVInterfaceProxy::AppThreadLogon() 615 { 616 int32 err; 617 OSCL_TRY(err, iPVApp.PVThreadLogon(*this);); 618 return err; 619 } 620 621 int32 CPVInterfaceProxy::AppThreadLogoff() 622 { 623 int32 err; 624 OSCL_TRY(err, iPVApp.PVThreadLogoff(*this);); 625 return err; 626 } 627 628 int32 CPVInterfaceProxy::AppStartScheduler() 629 { 630 int32 err; 631 OSCL_TRY(err, iPVScheduler->StartScheduler(&iInitSem);); 632 return err; 633 } 634 635