xref: /haiku/src/kits/media/TimeSource.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
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