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