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