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