/* libs/graphics/ports/SkOSEvent_android.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#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, NULL);
// 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