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 25namespace 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 32struct 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 46class SlaveNodes 47{ 48public: 49 SlaveNodes(); 50 ~SlaveNodes(); 51public: 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 59SlaveNodes::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 68SlaveNodes::~SlaveNodes() 69{ 70 delete locker; 71} 72 73 74} } 75 76 77/************************************************************* 78 * protected BTimeSource 79 *************************************************************/ 80 81BTimeSource::~BTimeSource() 82{ 83 CALLED(); 84 if (fArea > 0) 85 delete_area(fArea); 86 delete fSlaveNodes; 87} 88 89/************************************************************* 90 * public BTimeSource 91 *************************************************************/ 92 93status_t 94BTimeSource::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 109bigtime_t 110BTimeSource::Now() 111{ 112 PRINT(8, "CALLED BTimeSource::Now()\n"); 113 return PerformanceTimeFor(RealTime()); 114} 115 116 117bigtime_t 118BTimeSource::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 132bigtime_t 133BTimeSource::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 153bool 154BTimeSource::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 171status_t 172BTimeSource::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 211bigtime_t 212BTimeSource::RealTime() 213{ 214 PRINT(8, "CALLED BTimeSource::RealTime()\n"); 215 return system_time(); 216} 217 218 219status_t 220BTimeSource::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 232BTimeSource::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 250status_t 251BTimeSource::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 313void 314BTimeSource::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 340void 341BTimeSource::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 367void 368BTimeSource::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 388void 389BTimeSource::SetRunMode(run_mode mode) 390{ 391 CALLED(); 392 BMediaNode::SetRunMode(mode); 393 SendRunMode(mode); 394} 395/************************************************************* 396 * private BTimeSource 397 *************************************************************/ 398 399/* 400//unimplemented 401BTimeSource::BTimeSource(const BTimeSource &clone) 402BTimeSource &BTimeSource::operator=(const BTimeSource &clone) 403*/ 404 405status_t BTimeSource::_Reserved_TimeSource_0(void *) { return B_ERROR; } 406status_t BTimeSource::_Reserved_TimeSource_1(void *) { return B_ERROR; } 407status_t BTimeSource::_Reserved_TimeSource_2(void *) { return B_ERROR; } 408status_t BTimeSource::_Reserved_TimeSource_3(void *) { return B_ERROR; } 409status_t BTimeSource::_Reserved_TimeSource_4(void *) { return B_ERROR; } 410status_t BTimeSource::_Reserved_TimeSource_5(void *) { return B_ERROR; } 411 412/* explicit */ 413BTimeSource::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 447void 448BTimeSource::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 471status_t 472BTimeSource::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 486status_t 487BTimeSource::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 501void 502BTimeSource::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 538void 539BTimeSource::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 571void 572BTimeSource::DirectStart(bigtime_t at) 573{ 574 CALLED(); 575 if (fBuf) 576 atomic_or(&fBuf->isrunning, 1); 577 else 578 fStarted = true; 579} 580 581 582void 583BTimeSource::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 594void 595BTimeSource::DirectSeek(bigtime_t to, 596 bigtime_t at) 597{ 598 UNIMPLEMENTED(); 599} 600 601 602void 603BTimeSource::DirectSetRunMode(run_mode mode) 604{ 605 UNIMPLEMENTED(); 606} 607