2 * Copyright 2002-2012, Haiku. All Rights Reserved.
3 * This file may be used under the terms of the MIT License.
11 #include <TimeSource.h>
18 #include "DataExchange.h"
19 #include "ServerInterface.h"
20 #include "TimeSourceObject.h"
23 #define DEBUG_TIMESOURCE 0
26 #define TRACE_TIMESOURCE printf
28 #define TRACE_TIMESOURCE if (1) {} else printf
31 namespace BPrivate
{ namespace media
{
33 #define _atomic_read(p) atomic_or((p), 0)
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
40 // sizeof(TimeSourceTransmit) must be <= TS_AREA_SIZE
41 struct TimeSourceTransmit
46 bigtime_t realtime
[TS_INDEX_COUNT
];
47 bigtime_t perftime
[TS_INDEX_COUNT
];
48 float drift
[TS_INDEX_COUNT
];
51 #define MAX_SLAVE_NODES 300
54 class SlaveNodes
: public BLocker
60 int32
CountSlaves() const;
61 bool GetNextSlave(port_id
** id
);
64 bool InsertSlave(const media_node
& node
);
65 bool RemoveSlave(const media_node
& node
);
67 Map
<media_node_id
, port_id
> fSlaveList
;
71 SlaveNodes::SlaveNodes()
73 BLocker("BTimeSource slavenodes")
78 SlaveNodes::~SlaveNodes()
80 fSlaveList
.MakeEmpty();
85 SlaveNodes::CountSlaves() const
87 return fSlaveList
.CountItems();
92 SlaveNodes::GetNextSlave(port_id
** id
)
94 return fSlaveList
.GetNext(id
);
106 SlaveNodes::InsertSlave(const media_node
& node
)
108 return fSlaveList
.Insert(node
.node
, node
.port
);
113 SlaveNodes::RemoveSlave(const media_node
& node
)
115 return fSlaveList
.Remove(node
.node
);
119 } } // namespace BPrivate::media
122 /*************************************************************
123 * protected BTimeSource
124 *************************************************************/
126 BTimeSource::~BTimeSource()
134 /*************************************************************
136 *************************************************************/
139 BTimeSource::SnoozeUntil(bigtime_t performance_time
,
140 bigtime_t with_latency
, bool retry_signals
)
146 time
= RealTimeFor(performance_time
, with_latency
);
147 err
= snooze_until(time
, B_SYSTEM_TIMEBASE
);
148 } while (err
== B_INTERRUPTED
&& retry_signals
);
156 PRINT(8, "CALLED BTimeSource::Now()\n");
157 return PerformanceTimeFor(RealTime());
162 BTimeSource::PerformanceTimeFor(bigtime_t real_time
)
164 PRINT(8, "CALLED BTimeSource::PerformanceTimeFor()\n");
165 bigtime_t last_perf_time
;
166 bigtime_t last_real_time
;
169 if (GetTime(&last_perf_time
, &last_real_time
, &last_drift
) != B_OK
)
170 debugger("BTimeSource::PerformanceTimeFor: GetTime failed");
172 return last_perf_time
173 + (bigtime_t
)((real_time
- last_real_time
) * last_drift
);
178 BTimeSource::RealTimeFor(bigtime_t performance_time
,
179 bigtime_t with_latency
)
181 PRINT(8, "CALLED BTimeSource::RealTimeFor()\n");
184 return performance_time
- with_latency
;
187 bigtime_t last_perf_time
;
188 bigtime_t last_real_time
;
191 if (GetTime(&last_perf_time
, &last_real_time
, &last_drift
) != B_OK
)
192 debugger("BTimeSource::RealTimeFor: GetTime failed");
194 return last_real_time
- with_latency
195 + (bigtime_t
)((performance_time
- last_perf_time
) / last_drift
);
200 BTimeSource::IsRunning()
202 PRINT(8, "CALLED BTimeSource::IsRunning()\n");
206 // The system time source is always running
210 isrunning
= fBuf
? atomic_add(&fBuf
->isrunning
, 0) : fStarted
;
212 TRACE_TIMESOURCE("BTimeSource::IsRunning() node %" B_PRId32
", port %"
213 B_PRId32
", %s\n", fNodeID
, fControlPort
, isrunning
? "yes" : "no");
219 BTimeSource::GetTime(bigtime_t
* performance_time
,
220 bigtime_t
* real_time
, float* drift
)
222 PRINT(8, "CALLED BTimeSource::GetTime()\n");
225 *performance_time
= *real_time
= system_time();
231 debugger("BTimeSource::GetTime: fBuf == NULL");
233 int32 index
= _atomic_read(&fBuf
->readindex
);
234 index
&= (TS_INDEX_COUNT
- 1);
235 *real_time
= fBuf
->realtime
[index
];
236 *performance_time
= fBuf
->perftime
[index
];
237 *drift
= fBuf
->drift
[index
];
239 TRACE_TIMESOURCE("BTimeSource::GetTime timesource %" B_PRId32
240 ", perf %16" B_PRId64
", real %16" B_PRId64
", drift %2.2f\n", ID(),
241 *performance_time
, *real_time
, *drift
);
247 BTimeSource::RealTime()
249 PRINT(8, "CALLED BTimeSource::RealTime()\n");
250 return system_time();
255 BTimeSource::GetStartLatency(bigtime_t
* out_latency
)
262 /*************************************************************
263 * protected BTimeSource
264 *************************************************************/
267 BTimeSource::BTimeSource()
269 BMediaNode("This one is never called"),
273 fSlaveNodes(new BPrivate::media::SlaveNodes
),
277 AddNodeKind(B_TIME_SOURCE
);
278 // This constructor is only called by real time sources that inherit
279 // BTimeSource. We create the communication area in FinishCreate(),
280 // since we don't have a correct ID() until this node is registered.
285 BTimeSource::HandleMessage(int32 message
, const void* rawdata
,
288 PRINT(4, "BTimeSource::HandleMessage %#" B_PRIx32
", node %" B_PRId32
"\n",
294 const time_source_op_info
* data
295 = static_cast<const time_source_op_info
*>(rawdata
);
298 result
= TimeSourceOp(*data
, NULL
);
299 if (result
!= B_OK
) {
300 ERROR("BTimeSource::HandleMessage: TimeSourceOp failed\n");
304 case B_TIMESOURCE_START
:
305 DirectStart(data
->real_time
);
307 case B_TIMESOURCE_STOP
:
308 DirectStop(data
->real_time
, false);
310 case B_TIMESOURCE_STOP_IMMEDIATELY
:
311 DirectStop(data
->real_time
, true);
313 case B_TIMESOURCE_SEEK
:
314 DirectSeek(data
->performance_time
, data
->real_time
);
320 case TIMESOURCE_ADD_SLAVE_NODE
:
322 const timesource_add_slave_node_command
* data
323 = static_cast<const timesource_add_slave_node_command
*>(rawdata
);
324 DirectAddMe(data
->node
);
328 case TIMESOURCE_REMOVE_SLAVE_NODE
:
330 const timesource_remove_slave_node_command
* data
331 = static_cast<const timesource_remove_slave_node_command
*>(rawdata
);
332 DirectRemoveMe(data
->node
);
336 case TIMESOURCE_GET_START_LATENCY
:
338 const timesource_get_start_latency_request
* request
339 = static_cast<const timesource_get_start_latency_request
*>(rawdata
);
340 timesource_get_start_latency_reply reply
;
341 rv
= GetStartLatency(&reply
.start_latency
);
342 request
->SendReply(rv
, &reply
, sizeof(reply
));
351 BTimeSource::PublishTime(bigtime_t performance_time
,
352 bigtime_t real_time
, float drift
)
354 TRACE_TIMESOURCE("BTimeSource::PublishTime timesource %" B_PRId32
355 ", perf %16" B_PRId64
", real %16" B_PRId64
", drift %2.2f\n", ID(),
356 performance_time
, real_time
, drift
);
358 ERROR("BTimeSource::PublishTime timesource %" B_PRId32
359 ", fBuf = NULL\n", ID());
364 int32 index
= atomic_add(&fBuf
->writeindex
, 1);
365 index
&= (TS_INDEX_COUNT
- 1);
366 fBuf
->realtime
[index
] = real_time
;
367 fBuf
->perftime
[index
] = performance_time
;
368 fBuf
->drift
[index
] = drift
;
369 atomic_add(&fBuf
->readindex
, 1);
374 BTimeSource::BroadcastTimeWarp(bigtime_t at_real_time
,
375 bigtime_t new_performance_time
)
378 ASSERT(fSlaveNodes
!= NULL
);
380 // calls BMediaNode::TimeWarp() of all slaved nodes
382 TRACE("BTimeSource::BroadcastTimeWarp: at_real_time %" B_PRId64
383 ", new_performance_time %" B_PRId64
"\n", at_real_time
,
384 new_performance_time
);
386 BAutolock
lock(fSlaveNodes
);
388 port_id
* port
= NULL
;
389 while (fSlaveNodes
->GetNextSlave(&port
) == true) {
390 node_time_warp_command cmd
;
391 cmd
.at_real_time
= at_real_time
;
392 cmd
.to_performance_time
= new_performance_time
;
393 SendToPort(*port
, NODE_TIME_WARP
,
396 fSlaveNodes
->Rewind();
401 BTimeSource::SendRunMode(run_mode mode
)
404 ASSERT(fSlaveNodes
!= NULL
);
406 // send the run mode change to all slaved nodes
408 BAutolock
lock(fSlaveNodes
);
410 port_id
* port
= NULL
;
411 while (fSlaveNodes
->GetNextSlave(&port
) == true) {
412 node_set_run_mode_command cmd
;
414 SendToPort(*port
, NODE_SET_RUN_MODE
,
417 fSlaveNodes
->Rewind();
422 BTimeSource::SetRunMode(run_mode mode
)
425 BMediaNode::SetRunMode(mode
);
428 /*************************************************************
429 * private BTimeSource
430 *************************************************************/
434 BTimeSource::BTimeSource(const BTimeSource &clone)
435 BTimeSource &BTimeSource::operator=(const BTimeSource &clone)
438 status_t
BTimeSource::_Reserved_TimeSource_0(void *) { return B_ERROR
; }
439 status_t
BTimeSource::_Reserved_TimeSource_1(void *) { return B_ERROR
; }
440 status_t
BTimeSource::_Reserved_TimeSource_2(void *) { return B_ERROR
; }
441 status_t
BTimeSource::_Reserved_TimeSource_3(void *) { return B_ERROR
; }
442 status_t
BTimeSource::_Reserved_TimeSource_4(void *) { return B_ERROR
; }
443 status_t
BTimeSource::_Reserved_TimeSource_5(void *) { return B_ERROR
; }
446 BTimeSource::BTimeSource(media_node_id id
)
448 BMediaNode("This one is never called"),
456 AddNodeKind(B_TIME_SOURCE
);
459 // This constructor is only called by the derived
460 // BPrivate::media::TimeSourceObject objects
461 // We create a clone of the communication area.
463 sprintf(name
, "__timesource_buf_%" B_PRId32
, id
);
464 area_id area
= find_area(name
);
466 ERROR("BTimeSource::BTimeSource couldn't find area, node %" B_PRId32
470 sprintf(name
, "__cloned_timesource_buf_%" B_PRId32
, id
);
472 void** buf
= reinterpret_cast<void**>
473 (const_cast<BPrivate::media::TimeSourceTransmit
**>(&fBuf
));
475 fArea
= clone_area(name
, buf
, B_ANY_ADDRESS
,
476 B_READ_AREA
| B_WRITE_AREA
, area
);
479 ERROR("BTimeSource::BTimeSource couldn't clone area, node %" B_PRId32
487 BTimeSource::FinishCreate()
492 sprintf(name
, "__timesource_buf_%" B_PRId32
, ID());
494 void** buf
= reinterpret_cast<void**>
495 (const_cast<BPrivate::media::TimeSourceTransmit
**>(&fBuf
));
497 fArea
= create_area(name
, buf
, B_ANY_ADDRESS
, TS_AREA_SIZE
,
498 B_FULL_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
501 ERROR("BTimeSource::BTimeSource couldn't create area, node %" B_PRId32
507 fBuf
->writeindex
= 1;
508 fBuf
->realtime
[0] = 0;
509 fBuf
->perftime
[0] = 0;
510 fBuf
->drift
[0] = 1.0f
;
511 fBuf
->isrunning
= fStarted
;
516 BTimeSource::RemoveMe(BMediaNode
* node
)
519 if (fKinds
& NODE_KIND_SHADOW_TIMESOURCE
) {
520 timesource_remove_slave_node_command cmd
;
521 cmd
.node
= node
->Node();
522 SendToPort(fControlPort
, TIMESOURCE_REMOVE_SLAVE_NODE
,
525 DirectRemoveMe(node
->Node());
532 BTimeSource::AddMe(BMediaNode
* node
)
535 if (fKinds
& NODE_KIND_SHADOW_TIMESOURCE
) {
536 timesource_add_slave_node_command cmd
;
537 cmd
.node
= node
->Node();
538 SendToPort(fControlPort
, TIMESOURCE_ADD_SLAVE_NODE
, &cmd
, sizeof(cmd
));
540 DirectAddMe(node
->Node());
547 BTimeSource::DirectAddMe(const media_node
& node
)
550 ASSERT(fSlaveNodes
!= NULL
);
551 BAutolock
lock(fSlaveNodes
);
553 if (fSlaveNodes
->CountSlaves() == MAX_SLAVE_NODES
) {
554 ERROR("BTimeSource::DirectAddMe reached maximum number of slaves\n");
557 if (fNodeID
== node
.node
) {
558 ERROR("BTimeSource::DirectAddMe should not add itself to slave nodes\n");
562 if (fSlaveNodes
->InsertSlave(node
) != true) {
563 ERROR("BTimeSource::DirectAddMe failed\n");
567 if (fSlaveNodes
->CountSlaves() == 1) {
568 // start the time source
569 time_source_op_info msg
;
570 msg
.op
= B_TIMESOURCE_START
;
571 msg
.real_time
= RealTime();
573 TRACE_TIMESOURCE("starting time source %" B_PRId32
"\n", ID());
575 write_port(fControlPort
, TIMESOURCE_OP
, &msg
, sizeof(msg
));
581 BTimeSource::DirectRemoveMe(const media_node
& node
)
584 ASSERT(fSlaveNodes
!= NULL
);
585 BAutolock
lock(fSlaveNodes
);
587 if (fSlaveNodes
->CountSlaves() == 0) {
588 ERROR("BTimeSource::DirectRemoveMe no slots used\n");
592 if (fSlaveNodes
->RemoveSlave(node
) != true) {
593 ERROR("BTimeSource::DirectRemoveMe failed\n");
597 if (fSlaveNodes
->CountSlaves() == 0) {
598 // stop the time source
599 time_source_op_info msg
;
600 msg
.op
= B_TIMESOURCE_STOP_IMMEDIATELY
;
601 msg
.real_time
= RealTime();
603 TRACE_TIMESOURCE("stopping time source %" B_PRId32
"\n", ID());
605 write_port(fControlPort
, TIMESOURCE_OP
, &msg
, sizeof(msg
));
611 BTimeSource::DirectStart(bigtime_t at
)
615 atomic_or(&fBuf
->isrunning
, 1);
622 BTimeSource::DirectStop(bigtime_t at
, bool immediate
)
626 atomic_and(&fBuf
->isrunning
, 0);
633 BTimeSource::DirectSeek(bigtime_t to
, bigtime_t at
)
640 BTimeSource::DirectSetRunMode(run_mode mode
)