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