1 /* 2 * Copyright 2002-2012, Haiku. All Rights Reserved. 3 * This file may be used under the terms of the MIT License. 4 * 5 * Authors: 6 * Dario Casalinuovo 7 * Marcus Overhagen 8 */ 9 10 11 #include <TimeSource.h> 12 13 #include <Autolock.h> 14 15 #include <string.h> 16 17 #include "debug.h" 18 #include "DataExchange.h" 19 #include "ServerInterface.h" 20 #include "TimeSourceObject.h" 21 #include "TMap.h" 22 23 #define DEBUG_TIMESOURCE 0 24 25 #if DEBUG_TIMESOURCE 26 #define TRACE_TIMESOURCE printf 27 #else 28 #define TRACE_TIMESOURCE if (1) {} else printf 29 #endif 30 31 namespace BPrivate { namespace media { 32 33 #define _atomic_read(p) atomic_or((p), 0) 34 35 // must be multiple of page size 36 #define TS_AREA_SIZE B_PAGE_SIZE 37 // must be power of two 38 #define TS_INDEX_COUNT 128 39 40 // sizeof(TimeSourceTransmit) must be <= TS_AREA_SIZE 41 struct TimeSourceTransmit 42 { 43 int32 readindex; 44 int32 writeindex; 45 int32 isrunning; 46 bigtime_t realtime[TS_INDEX_COUNT]; 47 bigtime_t perftime[TS_INDEX_COUNT]; 48 float drift[TS_INDEX_COUNT]; 49 }; 50 51 #define MAX_SLAVE_NODES 300 52 53 54 class SlaveNodes : public BLocker 55 { 56 public: 57 SlaveNodes(); 58 ~SlaveNodes(); 59 60 int32 CountSlaves() const; 61 bool GetNextSlave(port_id** id); 62 void Rewind(); 63 64 bool InsertSlave(const media_node& node); 65 bool RemoveSlave(const media_node& node); 66 private: 67 Map<media_node_id, port_id> fSlaveList; 68 }; 69 70 71 SlaveNodes::SlaveNodes() 72 : 73 BLocker("BTimeSource slavenodes") 74 { 75 } 76 77 78 SlaveNodes::~SlaveNodes() 79 { 80 fSlaveList.MakeEmpty(); 81 } 82 83 84 int32 85 SlaveNodes::CountSlaves() const 86 { 87 return fSlaveList.CountItems(); 88 } 89 90 91 bool 92 SlaveNodes::GetNextSlave(port_id** id) 93 { 94 return fSlaveList.GetNext(id); 95 } 96 97 98 void 99 SlaveNodes::Rewind() 100 { 101 fSlaveList.Rewind(); 102 } 103 104 105 bool 106 SlaveNodes::InsertSlave(const media_node& node) 107 { 108 return fSlaveList.Insert(node.node, node.port); 109 } 110 111 112 bool 113 SlaveNodes::RemoveSlave(const media_node& node) 114 { 115 return fSlaveList.Remove(node.node); 116 } 117 118 119 } } // namespace BPrivate::media 120 121 122 /************************************************************* 123 * protected BTimeSource 124 *************************************************************/ 125 126 BTimeSource::~BTimeSource() 127 { 128 CALLED(); 129 if (fArea > 0) 130 delete_area(fArea); 131 delete fSlaveNodes; 132 } 133 134 /************************************************************* 135 * public BTimeSource 136 *************************************************************/ 137 138 status_t 139 BTimeSource::SnoozeUntil(bigtime_t performance_time, 140 bigtime_t with_latency, bool retry_signals) 141 { 142 CALLED(); 143 bigtime_t time; 144 status_t err; 145 do { 146 time = RealTimeFor(performance_time, with_latency); 147 err = snooze_until(time, B_SYSTEM_TIMEBASE); 148 } while (err == B_INTERRUPTED && retry_signals); 149 return err; 150 } 151 152 153 bigtime_t 154 BTimeSource::Now() 155 { 156 PRINT(8, "CALLED BTimeSource::Now()\n"); 157 return PerformanceTimeFor(RealTime()); 158 } 159 160 161 bigtime_t 162 BTimeSource::PerformanceTimeFor(bigtime_t real_time) 163 { 164 PRINT(8, "CALLED BTimeSource::PerformanceTimeFor()\n"); 165 bigtime_t last_perf_time; 166 bigtime_t last_real_time; 167 float last_drift; 168 169 if (GetTime(&last_perf_time, &last_real_time, &last_drift) != B_OK) 170 debugger("BTimeSource::PerformanceTimeFor: GetTime failed"); 171 172 return last_perf_time 173 + (bigtime_t)((real_time - last_real_time) * last_drift); 174 } 175 176 177 bigtime_t 178 BTimeSource::RealTimeFor(bigtime_t performance_time, 179 bigtime_t with_latency) 180 { 181 PRINT(8, "CALLED BTimeSource::RealTimeFor()\n"); 182 183 if (fIsRealtime) { 184 return performance_time - with_latency; 185 } 186 187 bigtime_t last_perf_time; 188 bigtime_t last_real_time; 189 float last_drift; 190 191 if (GetTime(&last_perf_time, &last_real_time, &last_drift) != B_OK) 192 debugger("BTimeSource::RealTimeFor: GetTime failed"); 193 194 return last_real_time - with_latency 195 + (bigtime_t)((performance_time - last_perf_time) / last_drift); 196 } 197 198 199 bool 200 BTimeSource::IsRunning() 201 { 202 PRINT(8, "CALLED BTimeSource::IsRunning()\n"); 203 204 bool isrunning; 205 206 // The system time source is always running 207 if (fIsRealtime) 208 isrunning = true; 209 else 210 isrunning = fBuf ? atomic_add(&fBuf->isrunning, 0) : fStarted; 211 212 TRACE_TIMESOURCE("BTimeSource::IsRunning() node %" B_PRId32 ", port %" 213 B_PRId32 ", %s\n", fNodeID, fControlPort, isrunning ? "yes" : "no"); 214 return isrunning; 215 } 216 217 218 status_t 219 BTimeSource::GetTime(bigtime_t* performance_time, 220 bigtime_t* real_time, float* drift) 221 { 222 PRINT(8, "CALLED BTimeSource::GetTime()\n"); 223 224 if (fIsRealtime) { 225 *performance_time = *real_time = system_time(); 226 *drift = 1.0f; 227 return B_OK; 228 } 229 230 int32 index; 231 index = _atomic_read(&fBuf->readindex); 232 index &= (TS_INDEX_COUNT - 1); 233 *real_time = fBuf->realtime[index]; 234 *performance_time = fBuf->perftime[index]; 235 *drift = fBuf->drift[index]; 236 237 TRACE_TIMESOURCE("BTimeSource::GetTime timesource %" B_PRId32 238 ", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(), 239 *performance_time, *real_time, *drift); 240 return B_OK; 241 } 242 243 244 bigtime_t 245 BTimeSource::RealTime() 246 { 247 PRINT(8, "CALLED BTimeSource::RealTime()\n"); 248 return system_time(); 249 } 250 251 252 status_t 253 BTimeSource::GetStartLatency(bigtime_t* out_latency) 254 { 255 CALLED(); 256 *out_latency = 0; 257 return B_OK; 258 } 259 260 /************************************************************* 261 * protected BTimeSource 262 *************************************************************/ 263 264 265 BTimeSource::BTimeSource() 266 : 267 BMediaNode("This one is never called"), 268 fStarted(false), 269 fArea(-1), 270 fBuf(NULL), 271 fSlaveNodes(new BPrivate::media::SlaveNodes), 272 fIsRealtime(false) 273 { 274 CALLED(); 275 AddNodeKind(B_TIME_SOURCE); 276 // This constructor is only called by real time sources that inherit 277 // BTimeSource. We create the communication area in FinishCreate(), 278 // since we don't have a correct ID() until this node is registered. 279 } 280 281 282 status_t 283 BTimeSource::HandleMessage(int32 message, const void* rawdata, 284 size_t size) 285 { 286 PRINT(4, "BTimeSource::HandleMessage %#" B_PRIx32 ", node %" B_PRId32 "\n", 287 message, fNodeID); 288 status_t rv; 289 switch (message) { 290 case TIMESOURCE_OP: 291 { 292 const time_source_op_info* data 293 = static_cast<const time_source_op_info*>(rawdata); 294 295 status_t result; 296 result = TimeSourceOp(*data, NULL); 297 if (result != B_OK) { 298 ERROR("BTimeSource::HandleMessage: TimeSourceOp failed\n"); 299 } 300 301 switch (data->op) { 302 case B_TIMESOURCE_START: 303 DirectStart(data->real_time); 304 break; 305 case B_TIMESOURCE_STOP: 306 DirectStop(data->real_time, false); 307 break; 308 case B_TIMESOURCE_STOP_IMMEDIATELY: 309 DirectStop(data->real_time, true); 310 break; 311 case B_TIMESOURCE_SEEK: 312 DirectSeek(data->performance_time, data->real_time); 313 break; 314 } 315 return B_OK; 316 } 317 318 case TIMESOURCE_ADD_SLAVE_NODE: 319 { 320 const timesource_add_slave_node_command* data 321 = static_cast<const timesource_add_slave_node_command*>(rawdata); 322 DirectAddMe(data->node); 323 return B_OK; 324 } 325 326 case TIMESOURCE_REMOVE_SLAVE_NODE: 327 { 328 const timesource_remove_slave_node_command* data 329 = static_cast<const timesource_remove_slave_node_command*>(rawdata); 330 DirectRemoveMe(data->node); 331 return B_OK; 332 } 333 334 case TIMESOURCE_GET_START_LATENCY: 335 { 336 const timesource_get_start_latency_request* request 337 = static_cast<const timesource_get_start_latency_request*>(rawdata); 338 timesource_get_start_latency_reply reply; 339 rv = GetStartLatency(&reply.start_latency); 340 request->SendReply(rv, &reply, sizeof(reply)); 341 return B_OK; 342 } 343 } 344 return B_ERROR; 345 } 346 347 348 void 349 BTimeSource::PublishTime(bigtime_t performance_time, 350 bigtime_t real_time, float drift) 351 { 352 TRACE_TIMESOURCE("BTimeSource::PublishTime timesource %" B_PRId32 353 ", perf %16" B_PRId64 ", real %16" B_PRId64 ", drift %2.2f\n", ID(), 354 performance_time, real_time, drift); 355 if (0 == fBuf) { 356 ERROR("BTimeSource::PublishTime timesource %" B_PRId32 357 ", fBuf = NULL\n", ID()); 358 fStarted = true; 359 return; 360 } 361 362 int32 index; 363 index = atomic_add(&fBuf->writeindex, 1); 364 index &= (TS_INDEX_COUNT - 1); 365 fBuf->realtime[index] = real_time; 366 fBuf->perftime[index] = performance_time; 367 fBuf->drift[index] = drift; 368 atomic_add(&fBuf->readindex, 1); 369 } 370 371 372 void 373 BTimeSource::BroadcastTimeWarp(bigtime_t at_real_time, 374 bigtime_t new_performance_time) 375 { 376 CALLED(); 377 ASSERT(fSlaveNodes != NULL); 378 379 // calls BMediaNode::TimeWarp() of all slaved nodes 380 381 TRACE("BTimeSource::BroadcastTimeWarp: at_real_time %" B_PRId64 382 ", new_performance_time %" B_PRId64 "\n", at_real_time, 383 new_performance_time); 384 385 BAutolock lock(fSlaveNodes); 386 387 port_id* port = NULL; 388 while (fSlaveNodes->GetNextSlave(&port) == true) { 389 node_time_warp_command cmd; 390 cmd.at_real_time = at_real_time; 391 cmd.to_performance_time = new_performance_time; 392 SendToPort(*port, NODE_TIME_WARP, 393 &cmd, sizeof(cmd)); 394 } 395 fSlaveNodes->Rewind(); 396 } 397 398 399 void 400 BTimeSource::SendRunMode(run_mode mode) 401 { 402 CALLED(); 403 ASSERT(fSlaveNodes != NULL); 404 405 // send the run mode change to all slaved nodes 406 407 BAutolock lock(fSlaveNodes); 408 409 port_id* port = NULL; 410 while (fSlaveNodes->GetNextSlave(&port) == true) { 411 node_set_run_mode_command cmd; 412 cmd.mode = mode; 413 SendToPort(*port, NODE_SET_RUN_MODE, 414 &cmd, sizeof(cmd)); 415 } 416 fSlaveNodes->Rewind(); 417 } 418 419 420 void 421 BTimeSource::SetRunMode(run_mode mode) 422 { 423 CALLED(); 424 BMediaNode::SetRunMode(mode); 425 SendRunMode(mode); 426 } 427 /************************************************************* 428 * private BTimeSource 429 *************************************************************/ 430 431 /* 432 //unimplemented 433 BTimeSource::BTimeSource(const BTimeSource &clone) 434 BTimeSource &BTimeSource::operator=(const BTimeSource &clone) 435 */ 436 437 status_t BTimeSource::_Reserved_TimeSource_0(void *) { return B_ERROR; } 438 status_t BTimeSource::_Reserved_TimeSource_1(void *) { return B_ERROR; } 439 status_t BTimeSource::_Reserved_TimeSource_2(void *) { return B_ERROR; } 440 status_t BTimeSource::_Reserved_TimeSource_3(void *) { return B_ERROR; } 441 status_t BTimeSource::_Reserved_TimeSource_4(void *) { return B_ERROR; } 442 status_t BTimeSource::_Reserved_TimeSource_5(void *) { return B_ERROR; } 443 444 /* explicit */ 445 BTimeSource::BTimeSource(media_node_id id) 446 : 447 BMediaNode("This one is never called"), 448 fStarted(false), 449 fArea(-1), 450 fBuf(NULL), 451 fSlaveNodes(NULL), 452 fIsRealtime(false) 453 { 454 CALLED(); 455 AddNodeKind(B_TIME_SOURCE); 456 ASSERT(id > 0); 457 458 // This constructor is only called by the derived 459 // BPrivate::media::TimeSourceObject objects 460 // We create a clone of the communication area. 461 char name[32]; 462 sprintf(name, "__timesource_buf_%" B_PRId32, id); 463 area_id area = find_area(name); 464 if (area <= 0) { 465 ERROR("BTimeSource::BTimeSource couldn't find area, node %" B_PRId32 466 "\n", id); 467 return; 468 } 469 sprintf(name, "__cloned_timesource_buf_%" B_PRId32, id); 470 471 void** buf = reinterpret_cast<void**> 472 (const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf)); 473 474 fArea = clone_area(name, buf, B_ANY_ADDRESS, 475 B_READ_AREA | B_WRITE_AREA, area); 476 477 if (fArea <= 0) { 478 ERROR("BTimeSource::BTimeSource couldn't clone area, node %" B_PRId32 479 "\n", id); 480 return; 481 } 482 } 483 484 485 void 486 BTimeSource::FinishCreate() 487 { 488 CALLED(); 489 490 char name[32]; 491 sprintf(name, "__timesource_buf_%" B_PRId32, ID()); 492 493 void** buf = reinterpret_cast<void**> 494 (const_cast<BPrivate::media::TimeSourceTransmit**>(&fBuf)); 495 496 fArea = create_area(name, buf, B_ANY_ADDRESS, TS_AREA_SIZE, 497 B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA); 498 499 if (fArea <= 0) { 500 ERROR("BTimeSource::BTimeSource couldn't create area, node %" B_PRId32 501 "\n", ID()); 502 fBuf = NULL; 503 return; 504 } 505 fBuf->readindex = 0; 506 fBuf->writeindex = 1; 507 fBuf->realtime[0] = 0; 508 fBuf->perftime[0] = 0; 509 fBuf->drift[0] = 1.0f; 510 fBuf->isrunning = fStarted; 511 } 512 513 514 status_t 515 BTimeSource::RemoveMe(BMediaNode* node) 516 { 517 CALLED(); 518 if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) { 519 timesource_remove_slave_node_command cmd; 520 cmd.node = node->Node(); 521 SendToPort(fControlPort, TIMESOURCE_REMOVE_SLAVE_NODE, 522 &cmd, sizeof(cmd)); 523 } else { 524 DirectRemoveMe(node->Node()); 525 } 526 return B_OK; 527 } 528 529 530 status_t 531 BTimeSource::AddMe(BMediaNode* node) 532 { 533 CALLED(); 534 if (fKinds & NODE_KIND_SHADOW_TIMESOURCE) { 535 timesource_add_slave_node_command cmd; 536 cmd.node = node->Node(); 537 SendToPort(fControlPort, TIMESOURCE_ADD_SLAVE_NODE, &cmd, sizeof(cmd)); 538 } else { 539 DirectAddMe(node->Node()); 540 } 541 return B_OK; 542 } 543 544 545 void 546 BTimeSource::DirectAddMe(const media_node& node) 547 { 548 CALLED(); 549 ASSERT(fSlaveNodes != NULL); 550 BAutolock lock(fSlaveNodes); 551 552 if (fSlaveNodes->CountSlaves() == MAX_SLAVE_NODES) { 553 ERROR("BTimeSource::DirectAddMe reached maximum number of slaves\n"); 554 return; 555 } 556 if (fNodeID == node.node) { 557 ERROR("BTimeSource::DirectAddMe should not add itself to slave nodes\n"); 558 return; 559 } 560 561 if (fSlaveNodes->InsertSlave(node) != true) { 562 ERROR("BTimeSource::DirectAddMe failed\n"); 563 return; 564 } 565 566 if (fSlaveNodes->CountSlaves() == 1) { 567 // start the time source 568 time_source_op_info msg; 569 msg.op = B_TIMESOURCE_START; 570 msg.real_time = RealTime(); 571 572 TRACE_TIMESOURCE("starting time source %" B_PRId32 "\n", ID()); 573 574 write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg)); 575 } 576 } 577 578 579 void 580 BTimeSource::DirectRemoveMe(const media_node& node) 581 { 582 CALLED(); 583 ASSERT(fSlaveNodes != NULL); 584 BAutolock lock(fSlaveNodes); 585 586 if (fSlaveNodes->CountSlaves() == 0) { 587 ERROR("BTimeSource::DirectRemoveMe no slots used\n"); 588 return; 589 } 590 591 if (fSlaveNodes->RemoveSlave(node) != true) { 592 ERROR("BTimeSource::DirectRemoveMe failed\n"); 593 return; 594 } 595 596 if (fSlaveNodes->CountSlaves() == 0) { 597 // stop the time source 598 time_source_op_info msg; 599 msg.op = B_TIMESOURCE_STOP_IMMEDIATELY; 600 msg.real_time = RealTime(); 601 602 TRACE_TIMESOURCE("stopping time source %" B_PRId32 "\n", ID()); 603 604 write_port(fControlPort, TIMESOURCE_OP, &msg, sizeof(msg)); 605 } 606 } 607 608 609 void 610 BTimeSource::DirectStart(bigtime_t at) 611 { 612 CALLED(); 613 if (fBuf) 614 atomic_or(&fBuf->isrunning, 1); 615 else 616 fStarted = true; 617 } 618 619 620 void 621 BTimeSource::DirectStop(bigtime_t at, bool immediate) 622 { 623 CALLED(); 624 if (fBuf) 625 atomic_and(&fBuf->isrunning, 0); 626 else 627 fStarted = false; 628 } 629 630 631 void 632 BTimeSource::DirectSeek(bigtime_t to, bigtime_t at) 633 { 634 UNIMPLEMENTED(); 635 } 636 637 638 void 639 BTimeSource::DirectSetRunMode(run_mode mode) 640 { 641 UNIMPLEMENTED(); 642 } 643