aboutsummaryrefslogtreecommitdiffhomepage
path: root/libs/graphics/ports/SkOSEvent_android.cpp
blob: 9c7360f7bff0c195ed51b85585b74d67ff137f1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "SkEvent.h"
#include "utils/threads.h"
#include <stdio.h>

using namespace android;

Mutex gEventQMutex;
Condition gEventQCondition;

void SkEvent::SignalNonEmptyQueue()
{
	gEventQCondition.broadcast();
}

///////////////////////////////////////////////////////////////////

#ifdef FMS_ARCH_ANDROID_ARM

// don't have pthreads.h, and therefore no timedwait(), so we punt for the demo

void SkEvent::SignalQueueTimer(SkMSec delay)
{
}

void SkEvent_start_timer_thread()
{
}

void SkEvent_stop_timer_thread()
{
}

#else

#include <pthread.h>
#include <errno.h>

static pthread_t		gTimerThread;
static pthread_mutex_t	gTimerMutex;
static pthread_cond_t	gTimerCond;
static timespec			gTimeSpec;

static void* timer_event_thread_proc(void*)
{
	for (;;)
	{
		int status;
		
		pthread_mutex_lock(&gTimerMutex);

		timespec spec = gTimeSpec;
		// mark our global to be zero
		// so we don't call timedwait again on a stale value
		gTimeSpec.tv_sec = 0;
		gTimeSpec.tv_nsec = 0;

		if (spec.tv_sec == 0 && spec.tv_nsec == 0)
			status = pthread_cond_wait(&gTimerCond, &gTimerMutex);
		else
			status = pthread_cond_timedwait(&gTimerCond, &gTimerMutex, &spec);
		
		if (status == 0)	// someone signaled us with a new time
		{
			pthread_mutex_unlock(&gTimerMutex);
		}
		else
		{
			SkASSERT(status == ETIMEDOUT);	// no need to unlock the mutex (its unlocked)
			// this is the payoff. Signal the event queue to wake up
			// and also check the delay-queue
			gEventQCondition.broadcast();
		}
	}
	return 0;
}

#define kThousand	(1000)
#define kMillion	(kThousand * kThousand)
#define kBillion	(kThousand * kThousand * kThousand)

void SkEvent::SignalQueueTimer(SkMSec delay)
{
	pthread_mutex_lock(&gTimerMutex);

	if (delay)
	{
		struct timeval tv;
		gettimeofday(&tv, nil);

		// normalize tv
		if (tv.tv_usec >= kMillion)
		{
			tv.tv_sec += tv.tv_usec / kMillion;
			tv.tv_usec %= kMillion;
		}

		// add tv + delay, scale each up to land on nanoseconds
		gTimeSpec.tv_nsec	= (tv.tv_usec + (delay % kThousand) * kThousand) * kThousand;
		gTimeSpec.tv_sec	= (tv.tv_sec + (delay / kThousand) * kThousand) * kThousand;
		
		// check for overflow in nsec
		if ((unsigned long)gTimeSpec.tv_nsec >= kBillion)
		{
			gTimeSpec.tv_nsec -= kBillion;
			gTimeSpec.tv_sec += 1;
			SkASSERT((unsigned long)gTimeSpec.tv_nsec < kBillion);
		}

	//	printf("SignalQueueTimer(%d) timespec(%d %d)\n", delay, gTimeSpec.tv_sec, gTimeSpec.tv_nsec);
	}
	else	// cancel the timer
	{
		gTimeSpec.tv_nsec = 0;
		gTimeSpec.tv_sec = 0;
	}

	pthread_mutex_unlock(&gTimerMutex);
	pthread_cond_signal(&gTimerCond);
}

void SkEvent_start_timer_thread()
{
	int				status;
	pthread_attr_t	attr;
	
	status = pthread_attr_init(&attr);
	SkASSERT(status == 0);
	status = pthread_create(&gTimerThread, &attr, timer_event_thread_proc, 0);
	SkASSERT(status == 0);
}

void SkEvent_stop_timer_thread()
{
	int status = pthread_cancel(gTimerThread);
	SkASSERT(status == 0);
}

#endif