1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sdext.hxx" 26 27 #include "PresenterTimer.hxx" 28 #include <osl/doublecheckedlocking.h> 29 #include <osl/thread.hxx> 30 #include <boost/bind.hpp> 31 #include <boost/function.hpp> 32 #include <boost/enable_shared_from_this.hpp> 33 #include <set> 34 35 using namespace ::com::sun::star; 36 using namespace ::com::sun::star::uno; 37 38 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString))) 39 40 namespace sdext { namespace presenter { 41 42 namespace { 43 class TimerTask 44 { 45 public: 46 TimerTask ( 47 const PresenterTimer::Task& rTask, 48 const TimeValue& rDueTime, 49 const sal_Int64 nRepeatIntervall, 50 const sal_Int32 nTaskId); 51 ~TimerTask (void) {} 52 53 PresenterTimer::Task maTask; 54 TimeValue maDueTime; 55 const sal_Int64 mnRepeatIntervall; 56 const sal_Int32 mnTaskId; 57 bool mbIsCanceled; 58 }; 59 60 typedef ::boost::shared_ptr<TimerTask> SharedTimerTask; 61 62 63 class TimerTaskComparator 64 { 65 public: 66 bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) 67 { 68 return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds 69 || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds 70 && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec); 71 } 72 }; 73 74 75 76 77 /** Queue all scheduled tasks and process them when their time has come. 78 */ 79 class TimerScheduler 80 : public ::boost::enable_shared_from_this<TimerScheduler>, 81 public ::osl::Thread 82 { 83 public: 84 static ::boost::shared_ptr<TimerScheduler> Instance (void); 85 static SharedTimerTask CreateTimerTask ( 86 const PresenterTimer::Task& rTask, 87 const TimeValue& rDueTime, 88 const sal_Int64 nRepeatIntervall); 89 90 void ScheduleTask (const SharedTimerTask& rpTask); 91 void CancelTask (const sal_Int32 nTaskId); 92 93 static bool GetCurrentTime (TimeValue& rCurrentTime); 94 static sal_Int64 GetTimeDifference ( 95 const TimeValue& rTargetTime, 96 const TimeValue& rCurrentTime); 97 static void ConvertToTimeValue ( 98 TimeValue& rTimeValue, 99 const sal_Int64 nTimeDifference); 100 static sal_Int64 ConvertFromTimeValue ( 101 const TimeValue& rTimeValue); 102 103 private: 104 static ::boost::shared_ptr<TimerScheduler> mpInstance; 105 static ::osl::Mutex maInstanceMutex; 106 static sal_Int32 mnTaskId; 107 108 ::osl::Mutex maTaskContainerMutex; 109 typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer; 110 TaskContainer maScheduledTasks; 111 bool mbIsRunning; 112 ::osl::Mutex maCurrentTaskMutex; 113 SharedTimerTask mpCurrentTask; 114 115 static void Release (void); 116 117 TimerScheduler (void); 118 virtual ~TimerScheduler (void); 119 class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } }; 120 friend class Deleter; 121 122 virtual void SAL_CALL run (void); 123 virtual void SAL_CALL onTerminated (void); 124 }; 125 126 127 128 129 bool GetDateTime (oslDateTime& rDateTime); 130 } // end of anonymous namespace 131 132 133 //===== PresenterTimer ======================================================== 134 135 sal_Int32 PresenterTimer::ScheduleSingleTaskRelative ( 136 const Task& rTask, 137 const sal_Int64 nDelay) 138 { 139 return ScheduleRepeatedTask(rTask, nDelay, 0); 140 } 141 142 143 144 145 sal_Int32 PresenterTimer::ScheduleSingleTaskAbsolute ( 146 const Task& rTask, 147 const TimeValue& rDueTime) 148 { 149 SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, rDueTime, 0)); 150 TimerScheduler::Instance()->ScheduleTask(pTask); 151 return pTask->mnTaskId; 152 } 153 154 155 156 157 sal_Int32 PresenterTimer::ScheduleRepeatedTask ( 158 const Task& rTask, 159 const sal_Int64 nDelay, 160 const sal_Int64 nIntervall) 161 { 162 TimeValue aCurrentTime; 163 if (TimerScheduler::GetCurrentTime(aCurrentTime)) 164 { 165 TimeValue aDueTime; 166 TimerScheduler::ConvertToTimeValue( 167 aDueTime, 168 TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay); 169 SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nIntervall)); 170 TimerScheduler::Instance()->ScheduleTask(pTask); 171 return pTask->mnTaskId; 172 } 173 174 return NotAValidTaskId; 175 } 176 177 178 179 180 void PresenterTimer::CancelTask (const sal_Int32 nTaskId) 181 { 182 return TimerScheduler::Instance()->CancelTask(nTaskId); 183 } 184 185 186 187 188 //===== TimerScheduler ======================================================== 189 190 ::boost::shared_ptr<TimerScheduler> TimerScheduler::mpInstance; 191 ::osl::Mutex TimerScheduler::maInstanceMutex; 192 sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId; 193 194 ::boost::shared_ptr<TimerScheduler> TimerScheduler::Instance (void) 195 { 196 ::boost::shared_ptr<TimerScheduler> pInstance = mpInstance; 197 if (pInstance.get() == NULL) 198 { 199 ::osl::MutexGuard aGuard (maInstanceMutex); 200 pInstance = mpInstance; 201 if (pInstance.get() == NULL) 202 { 203 pInstance.reset(new TimerScheduler(), TimerScheduler::Deleter()); 204 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 205 mpInstance = pInstance; 206 } 207 } 208 else 209 { 210 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 211 } 212 return pInstance; 213 } 214 215 216 217 218 void TimerScheduler::Release (void) 219 { 220 ::osl::MutexGuard aGuard (maInstanceMutex); 221 mpInstance.reset(); 222 } 223 224 225 226 227 TimerScheduler::TimerScheduler (void) 228 : maTaskContainerMutex(), 229 maScheduledTasks(), 230 mbIsRunning(false), 231 maCurrentTaskMutex(), 232 mpCurrentTask() 233 { 234 } 235 236 237 238 239 TimerScheduler::~TimerScheduler (void) 240 { 241 } 242 243 244 245 SharedTimerTask TimerScheduler::CreateTimerTask ( 246 const PresenterTimer::Task& rTask, 247 const TimeValue& rDueTime, 248 const sal_Int64 nRepeatIntervall) 249 { 250 return SharedTimerTask(new TimerTask(rTask, rDueTime, nRepeatIntervall, ++mnTaskId)); 251 } 252 253 254 255 256 void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask) 257 { 258 if (rpTask.get() == NULL) 259 return; 260 if (rpTask->mbIsCanceled) 261 return; 262 263 osl::MutexGuard aGuard (maTaskContainerMutex); 264 maScheduledTasks.insert(rpTask); 265 266 if ( ! mbIsRunning) 267 { 268 mbIsRunning = true; 269 create(); 270 } 271 } 272 273 274 275 276 void TimerScheduler::CancelTask (const sal_Int32 nTaskId) 277 { 278 // Set of scheduled tasks is sorted after their due times, not their 279 // task ids. Therefore we have to do a linear search for the task to 280 // cancel. 281 { 282 ::osl::MutexGuard aGuard (maTaskContainerMutex); 283 TaskContainer::iterator iTask (maScheduledTasks.begin()); 284 TaskContainer::const_iterator iEnd (maScheduledTasks.end()); 285 for ( ; iTask!=iEnd; ++iTask) 286 { 287 if ((*iTask)->mnTaskId == nTaskId) 288 { 289 maScheduledTasks.erase(iTask); 290 break; 291 } 292 } 293 } 294 295 // The task that is to be canceled may be currently about to be 296 // processed. Mark it with a flag that a) prevents a repeating task 297 // from being scheduled again and b) tries to prevent its execution. 298 if (mpCurrentTask.get() != NULL 299 && mpCurrentTask->mnTaskId == nTaskId) 300 { 301 mpCurrentTask->mbIsCanceled = true; 302 } 303 304 // When the last active task was canceled then the timer can be 305 // stopped. 306 if (maScheduledTasks.size() == 0) 307 { 308 mbIsRunning = false; 309 resume(); 310 // join(); 311 } 312 } 313 314 315 316 317 void SAL_CALL TimerScheduler::run (void) 318 { 319 while (mbIsRunning) 320 { 321 // Get the current time. 322 TimeValue aCurrentTime; 323 if ( ! GetCurrentTime(aCurrentTime)) 324 { 325 // We can not get the current time and thus can not schedule anything. 326 break; 327 } 328 329 // Restrict access to the maScheduledTasks member to one, mutext 330 // guarded, block. 331 SharedTimerTask pTask; 332 sal_Int64 nDifference = 0; 333 { 334 ::osl::MutexGuard aGuard (maTaskContainerMutex); 335 336 // There are no more scheduled task. Leave this loop, function and 337 // live of the TimerScheduler. 338 if (maScheduledTasks.empty()) 339 break; 340 341 nDifference = GetTimeDifference( 342 (*maScheduledTasks.begin())->maDueTime, 343 aCurrentTime); 344 if (nDifference <= 0) 345 { 346 pTask = *maScheduledTasks.begin(); 347 maScheduledTasks.erase(maScheduledTasks.begin()); 348 } 349 } 350 351 // Acquire a reference to the current task. 352 { 353 ::osl::MutexGuard aGuard (maCurrentTaskMutex); 354 mpCurrentTask = pTask; 355 } 356 357 if (mpCurrentTask.get() == NULL) 358 { 359 // Wait until the first task becomes due. 360 TimeValue aTimeValue; 361 ConvertToTimeValue(aTimeValue, nDifference); 362 wait(aTimeValue); 363 } 364 else 365 { 366 // Execute task. 367 if ( ! mpCurrentTask->maTask.empty() 368 && ! mpCurrentTask->mbIsCanceled) 369 { 370 mpCurrentTask->maTask(aCurrentTime); 371 372 // Re-schedule repeating tasks. 373 if (mpCurrentTask->mnRepeatIntervall > 0) 374 { 375 ConvertToTimeValue( 376 mpCurrentTask->maDueTime, 377 ConvertFromTimeValue(mpCurrentTask->maDueTime) 378 + mpCurrentTask->mnRepeatIntervall); 379 ScheduleTask(mpCurrentTask); 380 } 381 } 382 383 } 384 385 // Release reference to the current task. 386 { 387 ::osl::MutexGuard aGuard (maCurrentTaskMutex); 388 mpCurrentTask.reset(); 389 } 390 } 391 } 392 393 394 395 396 void SAL_CALL TimerScheduler::onTerminated (void) 397 { 398 Release(); 399 } 400 401 402 403 404 bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime) 405 { 406 TimeValue aSystemTime; 407 if (osl_getSystemTime(&aSystemTime)) 408 return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime); 409 return false; 410 } 411 412 413 414 415 sal_Int64 TimerScheduler::GetTimeDifference ( 416 const TimeValue& rTargetTime, 417 const TimeValue& rCurrentTime) 418 { 419 return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime); 420 } 421 422 423 424 425 void TimerScheduler::ConvertToTimeValue ( 426 TimeValue& rTimeValue, 427 const sal_Int64 nTimeDifference) 428 { 429 rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L); 430 rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L); 431 } 432 433 434 435 436 sal_Int64 TimerScheduler::ConvertFromTimeValue ( 437 const TimeValue& rTimeValue) 438 { 439 return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec; 440 } 441 442 443 444 445 //===== TimerTask ============================================================= 446 447 namespace { 448 449 TimerTask::TimerTask ( 450 const PresenterTimer::Task& rTask, 451 const TimeValue& rDueTime, 452 const sal_Int64 nRepeatIntervall, 453 const sal_Int32 nTaskId) 454 : maTask(rTask), 455 maDueTime(rDueTime), 456 mnRepeatIntervall(nRepeatIntervall), 457 mnTaskId(nTaskId), 458 mbIsCanceled(false) 459 { 460 } 461 462 } // end of anonymous namespace 463 464 465 466 467 //===== PresenterTimer ======================================================== 468 469 470 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance; 471 472 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance ( 473 const css::uno::Reference<css::uno::XComponentContext>& rxContext) 474 { 475 ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex()); 476 477 ::rtl::Reference<PresenterClockTimer> pTimer; 478 if (mpInstance.is()) 479 { 480 pTimer = mpInstance; 481 } 482 if ( ! pTimer.is()) 483 { 484 pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext)); 485 mpInstance = pTimer; 486 } 487 return pTimer; 488 } 489 490 491 492 493 PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext) 494 : PresenterClockTimerInterfaceBase(m_aMutex), 495 maListeners(), 496 maDateTime(), 497 mnTimerTaskId(PresenterTimer::NotAValidTaskId), 498 mbIsCallbackPending(false), 499 mxRequestCallback() 500 { 501 Reference<lang::XMultiComponentFactory> xFactory ( 502 rxContext->getServiceManager(), UNO_QUERY); 503 if (xFactory.is()) 504 mxRequestCallback = Reference<awt::XRequestCallback>( 505 xFactory->createInstanceWithContext( 506 A2S("com.sun.star.awt.AsyncCallback"), 507 rxContext), 508 UNO_QUERY_THROW); 509 } 510 511 512 513 514 PresenterClockTimer::~PresenterClockTimer (void) 515 { 516 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId) 517 { 518 PresenterTimer::CancelTask(mnTimerTaskId); 519 mnTimerTaskId = PresenterTimer::NotAValidTaskId; 520 } 521 522 Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY); 523 if (xComponent.is()) 524 xComponent->dispose(); 525 mxRequestCallback = NULL; 526 } 527 528 529 530 531 void PresenterClockTimer::AddListener (const SharedListener& rListener) 532 { 533 osl::MutexGuard aGuard (maMutex); 534 535 maListeners.push_back(rListener); 536 537 // Create a timer task when the first listener is added. 538 if (mnTimerTaskId==PresenterTimer::NotAValidTaskId) 539 { 540 mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask( 541 ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1), 542 0, 543 250000000 /*ns*/); 544 } 545 } 546 547 548 549 550 void PresenterClockTimer::RemoveListener (const SharedListener& rListener) 551 { 552 osl::MutexGuard aGuard (maMutex); 553 554 ListenerContainer::iterator iListener (::std::find( 555 maListeners.begin(), 556 maListeners.end(), 557 rListener)); 558 if (iListener != maListeners.end()) 559 maListeners.erase(iListener); 560 if (maListeners.size() == 0) 561 { 562 // We have no more clients and therefore are not interested in time changes. 563 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId) 564 { 565 PresenterTimer::CancelTask(mnTimerTaskId); 566 mnTimerTaskId = PresenterTimer::NotAValidTaskId; 567 } 568 mpInstance = NULL; 569 } 570 } 571 572 573 574 575 oslDateTime PresenterClockTimer::GetCurrentTime (void) 576 { 577 TimeValue aCurrentTime; 578 TimerScheduler::GetCurrentTime(aCurrentTime); 579 oslDateTime aDateTime; 580 osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime); 581 return aDateTime; 582 } 583 584 585 586 587 sal_Int64 PresenterClockTimer::GetTimeDifference ( 588 const oslDateTime& rNow, 589 const oslDateTime& rThen) 590 { 591 TimeValue aNow; 592 TimeValue aThen; 593 if (osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rNow),&aNow) 594 && osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rThen),&aThen)) 595 { 596 return TimerScheduler::GetTimeDifference(aNow, aThen); 597 } 598 else 599 return -1; 600 } 601 602 603 604 605 void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime) 606 { 607 css::uno::Reference<css::awt::XRequestCallback> xRequestCallback; 608 css::uno::Reference<css::awt::XCallback> xCallback; 609 { 610 osl::MutexGuard aGuard (maMutex); 611 612 TimeValue aCurrentTime (rCurrentTime); 613 oslDateTime aDateTime; 614 if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime)) 615 { 616 if (aDateTime.Seconds != maDateTime.Seconds 617 || aDateTime.Minutes != maDateTime.Minutes 618 || aDateTime.Seconds != maDateTime.Seconds) 619 { 620 // The displayed part of the current time has changed. 621 // Prepare to call the listeners. 622 maDateTime = aDateTime; 623 624 // Schedule notification of listeners. 625 if (mxRequestCallback.is() && ! mbIsCallbackPending) 626 { 627 mbIsCallbackPending = true; 628 xRequestCallback = mxRequestCallback; 629 xCallback = this; 630 } 631 } 632 } 633 } 634 if (mxRequestCallback.is() && xCallback.is()) 635 xRequestCallback->addCallback(xCallback, Any()); 636 } 637 638 639 640 641 //----- XCallback ------------------------------------------------------------- 642 643 void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData) 644 throw (css::uno::RuntimeException) 645 { 646 (void)rUserData; 647 648 ListenerContainer aListenerCopy (maListeners); 649 650 { 651 osl::MutexGuard aGuard (maMutex); 652 653 mbIsCallbackPending = false; 654 655 ::std::copy( 656 maListeners.begin(), 657 maListeners.end(), 658 ::std::back_inserter(aListenerCopy)); 659 } 660 661 if (aListenerCopy.size() > 0) 662 { 663 ListenerContainer::const_iterator iListener; 664 ListenerContainer::const_iterator iEnd (aListenerCopy.end()); 665 for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener) 666 { 667 (*iListener)->TimeHasChanged(maDateTime); 668 } 669 } 670 } 671 672 673 674 } } // end of namespace ::sdext::presenter 675