Author: stippi Date: Sat Feb 27 20:47:03 2010 New Revision: 241 URL: http://mmlr.dyndns.org/changeset/241 Log: Rewrote the SharedTimer impelementation. The old one used BMessageRunner, which is imprecise since it uses the registrar to fire messages across processes. This uses kernel ports and has bad timing resolution. The new message uses it's own high priority thread. The BMessageFilter is now a BHandler, which doesn't change much, other than how the message arrives. But sending messages within the same team attaches the message directly to the message queue, no ports involved. The asteroids demo stays totally smooth now. :-) Modified: webkit/trunk/WebCore/platform/haiku/SharedTimerHaiku.cpp Modified: webkit/trunk/WebCore/platform/haiku/SharedTimerHaiku.cpp ============================================================================== --- webkit/trunk/WebCore/platform/haiku/SharedTimerHaiku.cpp Sat Feb 27 18:26:46 2010 (r240) +++ webkit/trunk/WebCore/platform/haiku/SharedTimerHaiku.cpp Sat Feb 27 20:47:03 2010 (r241) @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Maxime Simon <simon.maxime@xxxxxxxxx> + * Copyright (C) 2010 Stephan Aßmus <superstippi@xxxxxx> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,7 +39,98 @@ namespace WebCore { -class SharedTimerHaiku : public BMessageFilter { +class TimerThread : public BLocker { +public: + TimerThread(const BMessenger& timer) + : m_timer(timer) + , m_timerThread(B_BAD_THREAD_ID) + , m_timerSem(B_BAD_SEM_ID) + , m_nextFireTime(0) + , m_threadWaitUntil(0) + , m_terminating(false) + { + m_timerSem = create_sem(0, "timer thread control"); + if (m_timerSem >= 0) { + m_timerThread = spawn_thread(timerThreadEntry, "timer thread", + B_DISPLAY_PRIORITY + 1, this); + if (m_timerThread >= 0) + resume_thread(m_timerThread); + } + } + + ~TimerThread() + { + m_terminating = true; + if (delete_sem(m_timerSem) == B_OK) { + int32 dummy; + wait_for_thread(m_timerThread, &dummy); + } + } + + bool isValid() const + { + return m_timerThread >= 0 && m_timerSem >= 0; + } + + void setNextEventTime(bigtime_t time) + { + Lock(); + m_nextFireTime = time; + if (m_nextFireTime < m_threadWaitUntil) + release_sem(m_timerSem); + Unlock(); + } + +private: + static int32 timerThreadEntry(void *data) + { + return ((TimerThread*)data)->timerThread(); + } + + int32 timerThread() + { + bool running = true; + while (running) { + bigtime_t waitUntil = B_INFINITE_TIMEOUT; + if (Lock()) { + if (m_nextFireTime > 0) + waitUntil = m_nextFireTime; + m_threadWaitUntil = waitUntil; + Unlock(); + } + status_t err = acquire_sem_etc(m_timerSem, 1, B_ABSOLUTE_TIMEOUT, waitUntil); + switch (err) { + case B_TIMED_OUT: + // do events, that are supposed to go off + if (!m_terminating && Lock() && system_time() >= m_nextFireTime) { + m_nextFireTime = 0; + Unlock(); + m_timer.SendMessage(FIRE_MESSAGE); + } + if (IsLocked()) + Unlock(); + break; + case B_BAD_SEM_ID: + running = false; + break; + case B_OK: + default: + break; + } + } + return 0; + } + +private: + BMessenger m_timer; + thread_id m_timerThread; + sem_id m_timerSem; + volatile bigtime_t m_nextFireTime; + volatile bigtime_t m_threadWaitUntil; + volatile bool m_terminating; +}; + +class SharedTimerHaiku : public BHandler { friend void setSharedTimerFiredFunction(void (*f)()); public: static SharedTimerHaiku* instance(); @@ -46,8 +138,13 @@ void start(double); void stop(); + void setTimerThread(TimerThread* thread) + { + m_timerThread = thread; + } + protected: - virtual filter_result Filter(BMessage*, BHandler**); + virtual void MessageReceived(BMessage*); private: SharedTimerHaiku(); @@ -55,28 +152,33 @@ void (*m_timerFunction)(); bool m_shouldRun; + TimerThread* m_timerThread; }; SharedTimerHaiku::SharedTimerHaiku() - : BMessageFilter(FIRE_MESSAGE) + : BHandler("WebKit shared timer") , m_timerFunction(0) , m_shouldRun(false) + , m_timerThread(0) { } SharedTimerHaiku::~SharedTimerHaiku() { + delete m_timerThread; } SharedTimerHaiku* SharedTimerHaiku::instance() { - BLooper* looper = BLooper::LooperForThread(find_thread(0)); static SharedTimerHaiku* timer; if (!timer) { + BLooper* looper = BLooper::LooperForThread(find_thread(0)); BAutolock lock(looper); timer = new SharedTimerHaiku(); - looper->AddCommonFilter(timer); + looper->AddHandler(timer); + // Only at this time, the timer can be a valid BMessenger. + timer->setTimerThread(new TimerThread(BMessenger(timer))); } return timer; @@ -89,8 +191,7 @@ double intervalInSeconds = fireTime - currentTime(); bigtime_t intervalInMicroSeconds = intervalInSeconds < 0 ? 0 : intervalInSeconds * 1000000; - BMessage message(FIRE_MESSAGE); - BMessageRunner::StartSending(Looper(), &message, intervalInMicroSeconds, 1); + m_timerThread->setNextEventTime(system_time() + intervalInMicroSeconds); } void SharedTimerHaiku::stop() @@ -98,12 +199,10 @@ m_shouldRun = false; } -filter_result SharedTimerHaiku::Filter(BMessage*, BHandler**) +void SharedTimerHaiku::MessageReceived(BMessage*) { if (m_shouldRun && m_timerFunction) m_timerFunction(); - - return B_SKIP_MESSAGE; } // WebCore functions