1/* libs/graphics/ports/SkOSEvent_android.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkEvent.h"
19#include "utils/threads.h"
20#include <stdio.h>
21
22using namespace android;
23
24Mutex gEventQMutex;
25Condition gEventQCondition;
26
27void SkEvent::SignalNonEmptyQueue()
28{
29    gEventQCondition.broadcast();
30}
31
32///////////////////////////////////////////////////////////////////
33
34#ifdef FMS_ARCH_ANDROID_ARM
35
36// don't have pthreads.h, and therefore no timedwait(), so we punt for the demo
37
38void SkEvent::SignalQueueTimer(SkMSec delay)
39{
40}
41
42void SkEvent_start_timer_thread()
43{
44}
45
46void SkEvent_stop_timer_thread()
47{
48}
49
50#else
51
52#include <pthread.h>
53#include <errno.h>
54
55static pthread_t        gTimerThread;
56static pthread_mutex_t  gTimerMutex;
57static pthread_cond_t   gTimerCond;
58static timespec         gTimeSpec;
59
60static void* timer_event_thread_proc(void*)
61{
62    for (;;)
63    {
64        int status;
65
66        pthread_mutex_lock(&gTimerMutex);
67
68        timespec spec = gTimeSpec;
69        // mark our global to be zero
70        // so we don't call timedwait again on a stale value
71        gTimeSpec.tv_sec = 0;
72        gTimeSpec.tv_nsec = 0;
73
74        if (spec.tv_sec == 0 && spec.tv_nsec == 0)
75            status = pthread_cond_wait(&gTimerCond, &gTimerMutex);
76        else
77            status = pthread_cond_timedwait(&gTimerCond, &gTimerMutex, &spec);
78
79        if (status == 0)    // someone signaled us with a new time
80        {
81            pthread_mutex_unlock(&gTimerMutex);
82        }
83        else
84        {
85            SkASSERT(status == ETIMEDOUT);  // no need to unlock the mutex (its unlocked)
86            // this is the payoff. Signal the event queue to wake up
87            // and also check the delay-queue
88            gEventQCondition.broadcast();
89        }
90    }
91    return 0;
92}
93
94#define kThousand   (1000)
95#define kMillion    (kThousand * kThousand)
96#define kBillion    (kThousand * kThousand * kThousand)
97
98void SkEvent::SignalQueueTimer(SkMSec delay)
99{
100    pthread_mutex_lock(&gTimerMutex);
101
102    if (delay)
103    {
104        struct timeval tv;
105        gettimeofday(&tv, NULL);
106
107        // normalize tv
108        if (tv.tv_usec >= kMillion)
109        {
110            tv.tv_sec += tv.tv_usec / kMillion;
111            tv.tv_usec %= kMillion;
112        }
113
114        // add tv + delay, scale each up to land on nanoseconds
115        gTimeSpec.tv_nsec   = (tv.tv_usec + (delay % kThousand) * kThousand) * kThousand;
116        gTimeSpec.tv_sec    = (tv.tv_sec + (delay / kThousand) * kThousand) * kThousand;
117
118        // check for overflow in nsec
119        if ((unsigned long)gTimeSpec.tv_nsec >= kBillion)
120        {
121            gTimeSpec.tv_nsec -= kBillion;
122            gTimeSpec.tv_sec += 1;
123            SkASSERT((unsigned long)gTimeSpec.tv_nsec < kBillion);
124        }
125
126    //  printf("SignalQueueTimer(%d) timespec(%d %d)\n", delay, gTimeSpec.tv_sec, gTimeSpec.tv_nsec);
127    }
128    else    // cancel the timer
129    {
130        gTimeSpec.tv_nsec = 0;
131        gTimeSpec.tv_sec = 0;
132    }
133
134    pthread_mutex_unlock(&gTimerMutex);
135    pthread_cond_signal(&gTimerCond);
136}
137
138void SkEvent_start_timer_thread()
139{
140    int             status;
141    pthread_attr_t  attr;
142
143    status = pthread_attr_init(&attr);
144    SkASSERT(status == 0);
145    status = pthread_create(&gTimerThread, &attr, timer_event_thread_proc, 0);
146    SkASSERT(status == 0);
147}
148
149void SkEvent_stop_timer_thread()
150{
151    int status = pthread_cancel(gTimerThread);
152    SkASSERT(status == 0);
153}
154
155#endif
156