headers/bsd: Add sys/queue.h.
[haiku.git] / src / kits / media / TimeSource.cpp
blobfa7e7be03ed1284185de7bebd3f217a788aed6e1
1 /*
2 * Copyright 2002-2012, Haiku. All Rights Reserved.
3 * This file may be used under the terms of the MIT License.
5 * Authors:
6 * Dario Casalinuovo
7 * Marcus Overhagen
8 */
11 #include <TimeSource.h>
13 #include <Autolock.h>
15 #include <string.h>
17 #include "debug.h"
18 #include "DataExchange.h"
19 #include "ServerInterface.h"
20 #include "TimeSourceObject.h"
21 #include "TMap.h"
23 #define DEBUG_TIMESOURCE 0
25 #if DEBUG_TIMESOURCE
26 #define TRACE_TIMESOURCE printf
27 #else
28 #define TRACE_TIMESOURCE if (1) {} else printf
29 #endif
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
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];
51 #define MAX_SLAVE_NODES 300
54 class SlaveNodes : public BLocker
56 public:
57 SlaveNodes();
58 ~SlaveNodes();
60 int32 CountSlaves() const;
61 bool GetNextSlave(port_id** id);
62 void Rewind();
64 bool InsertSlave(const media_node& node);
65 bool RemoveSlave(const media_node& node);
66 private:
67 Map<media_node_id, port_id> fSlaveList;
71 SlaveNodes::SlaveNodes()
73 BLocker("BTimeSource slavenodes")
78 SlaveNodes::~SlaveNodes()
80 fSlaveList.MakeEmpty();
84 int32
85 SlaveNodes::CountSlaves() const
87 return fSlaveList.CountItems();
91 bool
92 SlaveNodes::GetNextSlave(port_id** id)
94 return fSlaveList.GetNext(id);
98 void
99 SlaveNodes::Rewind()
101 fSlaveList.Rewind();
105 bool
106 SlaveNodes::InsertSlave(const media_node& node)
108 return fSlaveList.Insert(node.node, node.port);
112 bool
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()
128 CALLED();
129 if (fArea > 0)
130 delete_area(fArea);
131 delete fSlaveNodes;
134 /*************************************************************
135 * public BTimeSource
136 *************************************************************/
138 status_t
139 BTimeSource::SnoozeUntil(bigtime_t performance_time,
140 bigtime_t with_latency, bool retry_signals)
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;
153 bigtime_t
154 BTimeSource::Now()
156 PRINT(8, "CALLED BTimeSource::Now()\n");
157 return PerformanceTimeFor(RealTime());
161 bigtime_t
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;
167 float last_drift;
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);
177 bigtime_t
178 BTimeSource::RealTimeFor(bigtime_t performance_time,
179 bigtime_t with_latency)
181 PRINT(8, "CALLED BTimeSource::RealTimeFor()\n");
183 if (fIsRealtime) {
184 return performance_time - with_latency;
187 bigtime_t last_perf_time;
188 bigtime_t last_real_time;
189 float last_drift;
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);
199 bool
200 BTimeSource::IsRunning()
202 PRINT(8, "CALLED BTimeSource::IsRunning()\n");
204 bool isrunning;
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;
212 TRACE_TIMESOURCE("BTimeSource::IsRunning() node %" B_PRId32 ", port %"
213 B_PRId32 ", %s\n", fNodeID, fControlPort, isrunning ? "yes" : "no");
214 return isrunning;
218 status_t
219 BTimeSource::GetTime(bigtime_t* performance_time,
220 bigtime_t* real_time, float* drift)
222 PRINT(8, "CALLED BTimeSource::GetTime()\n");
224 if (fIsRealtime) {
225 *performance_time = *real_time = system_time();
226 *drift = 1.0f;
227 return B_OK;
230 if (fBuf == NULL)
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);
242 return B_OK;
246 bigtime_t
247 BTimeSource::RealTime()
249 PRINT(8, "CALLED BTimeSource::RealTime()\n");
250 return system_time();
254 status_t
255 BTimeSource::GetStartLatency(bigtime_t* out_latency)
257 CALLED();
258 *out_latency = 0;
259 return B_OK;
262 /*************************************************************
263 * protected BTimeSource
264 *************************************************************/
267 BTimeSource::BTimeSource()
269 BMediaNode("This one is never called"),
270 fStarted(false),
271 fArea(-1),
272 fBuf(NULL),
273 fSlaveNodes(new BPrivate::media::SlaveNodes),
274 fIsRealtime(false)
276 CALLED();
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.
284 status_t
285 BTimeSource::HandleMessage(int32 message, const void* rawdata,
286 size_t size)
288 PRINT(4, "BTimeSource::HandleMessage %#" B_PRIx32 ", node %" B_PRId32 "\n",
289 message, fNodeID);
290 status_t rv;
291 switch (message) {
292 case TIMESOURCE_OP:
294 const time_source_op_info* data
295 = static_cast<const time_source_op_info*>(rawdata);
297 status_t result;
298 result = TimeSourceOp(*data, NULL);
299 if (result != B_OK) {
300 ERROR("BTimeSource::HandleMessage: TimeSourceOp failed\n");
303 switch (data->op) {
304 case B_TIMESOURCE_START:
305 DirectStart(data->real_time);
306 break;
307 case B_TIMESOURCE_STOP:
308 DirectStop(data->real_time, false);
309 break;
310 case B_TIMESOURCE_STOP_IMMEDIATELY:
311 DirectStop(data->real_time, true);
312 break;
313 case B_TIMESOURCE_SEEK:
314 DirectSeek(data->performance_time, data->real_time);
315 break;
317 return B_OK;
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);
325 return B_OK;
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);
333 return B_OK;
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));
343 return B_OK;
346 return B_ERROR;
350 void
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);
357 if (0 == fBuf) {
358 ERROR("BTimeSource::PublishTime timesource %" B_PRId32
359 ", fBuf = NULL\n", ID());
360 fStarted = true;
361 return;
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);
373 void
374 BTimeSource::BroadcastTimeWarp(bigtime_t at_real_time,
375 bigtime_t new_performance_time)
377 CALLED();
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,
394 &cmd, sizeof(cmd));
396 fSlaveNodes->Rewind();
400 void
401 BTimeSource::SendRunMode(run_mode mode)
403 CALLED();
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;
413 cmd.mode = mode;
414 SendToPort(*port, NODE_SET_RUN_MODE,
415 &cmd, sizeof(cmd));
417 fSlaveNodes->Rewind();
421 void
422 BTimeSource::SetRunMode(run_mode mode)
424 CALLED();
425 BMediaNode::SetRunMode(mode);
426 SendRunMode(mode);
428 /*************************************************************
429 * private BTimeSource
430 *************************************************************/
433 //unimplemented
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; }
445 /* explicit */
446 BTimeSource::BTimeSource(media_node_id id)
448 BMediaNode("This one is never called"),
449 fStarted(false),
450 fArea(-1),
451 fBuf(NULL),
452 fSlaveNodes(NULL),
453 fIsRealtime(false)
455 CALLED();
456 AddNodeKind(B_TIME_SOURCE);
457 ASSERT(id > 0);
459 // This constructor is only called by the derived
460 // BPrivate::media::TimeSourceObject objects
461 // We create a clone of the communication area.
462 char name[32];
463 sprintf(name, "__timesource_buf_%" B_PRId32, id);
464 area_id area = find_area(name);
465 if (area <= 0) {
466 ERROR("BTimeSource::BTimeSource couldn't find area, node %" B_PRId32
467 "\n", id);
468 return;
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);
478 if (fArea <= 0) {
479 ERROR("BTimeSource::BTimeSource couldn't clone area, node %" B_PRId32
480 "\n", id);
481 return;
486 void
487 BTimeSource::FinishCreate()
489 CALLED();
491 char name[32];
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);
500 if (fArea <= 0) {
501 ERROR("BTimeSource::BTimeSource couldn't create area, node %" B_PRId32
502 "\n", ID());
503 fBuf = NULL;
504 return;
506 fBuf->readindex = 0;
507 fBuf->writeindex = 1;
508 fBuf->realtime[0] = 0;
509 fBuf->perftime[0] = 0;
510 fBuf->drift[0] = 1.0f;
511 fBuf->isrunning = fStarted;
515 status_t
516 BTimeSource::RemoveMe(BMediaNode* node)
518 CALLED();
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,
523 &cmd, sizeof(cmd));
524 } else {
525 DirectRemoveMe(node->Node());
527 return B_OK;
531 status_t
532 BTimeSource::AddMe(BMediaNode* node)
534 CALLED();
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));
539 } else {
540 DirectAddMe(node->Node());
542 return B_OK;
546 void
547 BTimeSource::DirectAddMe(const media_node& node)
549 CALLED();
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");
555 return;
557 if (fNodeID == node.node) {
558 ERROR("BTimeSource::DirectAddMe should not add itself to slave nodes\n");
559 return;
562 if (fSlaveNodes->InsertSlave(node) != true) {
563 ERROR("BTimeSource::DirectAddMe failed\n");
564 return;
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));
580 void
581 BTimeSource::DirectRemoveMe(const media_node& node)
583 CALLED();
584 ASSERT(fSlaveNodes != NULL);
585 BAutolock lock(fSlaveNodes);
587 if (fSlaveNodes->CountSlaves() == 0) {
588 ERROR("BTimeSource::DirectRemoveMe no slots used\n");
589 return;
592 if (fSlaveNodes->RemoveSlave(node) != true) {
593 ERROR("BTimeSource::DirectRemoveMe failed\n");
594 return;
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));
610 void
611 BTimeSource::DirectStart(bigtime_t at)
613 CALLED();
614 if (fBuf)
615 atomic_or(&fBuf->isrunning, 1);
616 else
617 fStarted = true;
621 void
622 BTimeSource::DirectStop(bigtime_t at, bool immediate)
624 CALLED();
625 if (fBuf)
626 atomic_and(&fBuf->isrunning, 0);
627 else
628 fStarted = false;
632 void
633 BTimeSource::DirectSeek(bigtime_t to, bigtime_t at)
635 UNIMPLEMENTED();
639 void
640 BTimeSource::DirectSetRunMode(run_mode mode)
642 UNIMPLEMENTED();