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