blob: bb4a186821ab826cb20f78a7780b37af74cb7b4c [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// 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 "starboard/shared/starboard/queue_application.h"
#include "starboard/common/atomic.h"
#include "starboard/common/condition_variable.h"
#include "starboard/common/log.h"
#include "starboard/common/time.h"
#include "starboard/event.h"
namespace starboard {
namespace shared {
namespace starboard {
void QueueApplication::Wake() {
if (IsCurrentThread()) {
return;
}
if (!MayHaveSystemEvents()) {
event_queue_.Wake();
return;
} else {
WakeSystemEventWait();
}
}
Application::Event* QueueApplication::GetNextEvent() {
if (!MayHaveSystemEvents()) {
return GetNextInjectedEvent();
}
// The construction of this loop is somewhat deliberate. The main UI message
// pump will inject an event every time it needs to do deferred work. If we
// don't prioritize system window events, they can get starved by a constant
// stream of work.
Event* event = NULL;
while (!event) {
event = PollNextSystemEvent();
if (event) {
continue;
}
// Then poll the generic queue.
event = PollNextInjectedEvent();
if (event) {
continue;
}
// Then we block indefinitely on the Window's DFB queue.
event = WaitForSystemEventWithTimeout(GetNextTimedEventTargetTime() -
CurrentMonotonicTime());
}
return event;
}
void QueueApplication::Inject(Event* event) {
event_queue_.Put(event);
if (MayHaveSystemEvents()) {
WakeSystemEventWait();
}
}
void QueueApplication::InjectTimedEvent(TimedEvent* timed_event) {
if (timed_event_queue_.Inject(timed_event)) {
// The time to wake up has moved earlier, so wake up the event queue to
// recalculate the wait.
Wake();
}
}
void QueueApplication::CancelTimedEvent(SbEventId event_id) {
timed_event_queue_.Cancel(event_id);
// The wait duration will only get longer after cancelling an event, so the
// waiter will wake up as previously scheduled, see there is nothing to do,
// and go back to sleep.
}
void QueueApplication::InjectAndProcess(SbEventType type,
bool checkSystemEvents) {
atomic_bool event_processed;
Event* flagged_event = new Event(
type, const_cast<atomic_bool*>(&event_processed), [](void* flag) {
auto* bool_flag =
const_cast<atomic_bool*>(static_cast<atomic_bool*>(flag));
bool_flag->store(true);
});
Inject(flagged_event);
while (!event_processed.load()) {
if (checkSystemEvents) {
DispatchAndDelete(GetNextEvent());
} else {
DispatchAndDelete(GetNextInjectedEvent());
}
}
}
Application::TimedEvent* QueueApplication::GetNextDueTimedEvent() {
return timed_event_queue_.Get();
}
int64_t QueueApplication::GetNextTimedEventTargetTime() {
return timed_event_queue_.GetTime();
}
QueueApplication::TimedEventQueue::TimedEventQueue() : set_(&IsLess) {}
QueueApplication::TimedEventQueue::~TimedEventQueue() {
ScopedLock lock(mutex_);
for (TimedEventMap::iterator i = map_.begin(); i != map_.end(); ++i) {
delete i->second;
}
map_.clear();
set_.clear();
}
bool QueueApplication::TimedEventQueue::Inject(TimedEvent* timed_event) {
ScopedLock lock(mutex_);
int64_t oldTime = GetTimeLocked();
map_[timed_event->id] = timed_event;
set_.insert(timed_event);
return (timed_event->target_time < oldTime);
}
void QueueApplication::TimedEventQueue::Cancel(SbEventId event_id) {
ScopedLock lock(mutex_);
TimedEventMap::iterator i = map_.find(event_id);
if (i == map_.end()) {
return;
}
TimedEvent* timed_event = i->second;
map_.erase(i);
set_.erase(timed_event);
delete timed_event;
}
Application::TimedEvent* QueueApplication::TimedEventQueue::Get() {
ScopedLock lock(mutex_);
if (set_.empty()) {
return NULL;
}
TimedEvent* timed_event = *(set_.begin());
if (timed_event->target_time > CurrentMonotonicTime()) {
return NULL;
}
map_.erase(timed_event->id);
set_.erase(timed_event);
return timed_event;
}
int64_t QueueApplication::TimedEventQueue::GetTime() {
ScopedLock lock(mutex_);
return GetTimeLocked();
}
int64_t QueueApplication::TimedEventQueue::GetTimeLocked() {
if (set_.empty()) {
return kSbInt64Max;
}
TimedEvent* timed_event = *(set_.begin());
int64_t time = timed_event->target_time;
int64_t now = CurrentMonotonicTime();
if (time < now) {
return now;
}
return time;
}
// static
bool QueueApplication::TimedEventQueue::IsLess(const TimedEvent* lhs,
const TimedEvent* rhs) {
int64_t time_difference = lhs->target_time - rhs->target_time;
if (time_difference != 0) {
return time_difference < 0;
}
// If the time differences are the same, ensure there is a strict and stable
// ordering.
return reinterpret_cast<uintptr_t>(lhs) < reinterpret_cast<uintptr_t>(rhs);
}
Application::Event* QueueApplication::PollNextInjectedEvent() {
Event* event = event_queue_.Poll();
if (event != NULL) {
return event;
}
TimedEvent* timed_event = GetNextDueTimedEvent();
if (timed_event != NULL) {
return new Event(timed_event);
}
return NULL;
}
Application::Event* QueueApplication::GetNextInjectedEvent() {
for (;;) {
int64_t delay = GetNextTimedEventTargetTime() - CurrentMonotonicTime();
Event* event = event_queue_.GetTimed(delay);
if (event != NULL) {
return event;
}
TimedEvent* timed_event = GetNextDueTimedEvent();
if (timed_event != NULL) {
return new Event(timed_event);
}
}
}
} // namespace starboard
} // namespace shared
} // namespace starboard