コード例 #1
0
namespace WTF {

GSourceFuncs GSourceWrap::sourceFunctions = {
    // prepare
    [](GSource* source, gint*) -> gboolean
    {
        return g_source_get_ready_time(source) == 0;
    },
    nullptr, // check
    // dispatch
    [](GSource* source, GSourceFunc callback, gpointer data) -> gboolean
    {
        ASSERT(source);
        if (g_source_get_ready_time(source) == -1)
            return G_SOURCE_CONTINUE;
        DispatchContext context{ source, data };
        return callback(&context);
    },
    nullptr, // finalize
    nullptr, // closure_callback
    nullptr, // closure_marshall
};

gboolean GSourceWrap::staticDelayBasedVoidCallback(gpointer data)
{
    auto& dispatch = *reinterpret_cast<DispatchContext*>(data);
    auto& callback = *reinterpret_cast<DelayBased::CallbackContext<void ()>*>(dispatch.second);
    if (g_cancellable_is_cancelled(callback.second.cancellable.get()))
        return G_SOURCE_CONTINUE;

    callback.second.dispatching = true;
    g_source_set_ready_time(dispatch.first, -1);

    callback.first();

    callback.second.dispatching = false;
    return G_SOURCE_CONTINUE;
}

gboolean GSourceWrap::dynamicDelayBasedVoidCallback(gpointer data)
{
    auto& dispatch = *reinterpret_cast<DispatchContext*>(data);
    auto& callback = *reinterpret_cast<DelayBased::CallbackContext<void ()>*>(dispatch.second);
    if (g_cancellable_is_cancelled(callback.second.cancellable.get()))
        return G_SOURCE_CONTINUE;

    callback.second.dispatching = true;
    g_source_set_ready_time(dispatch.first, -1);
    g_source_set_callback(dispatch.first, nullptr, nullptr, nullptr);

    callback.first();

    callback.second.dispatching = false;
    return G_SOURCE_CONTINUE;
}

gboolean GSourceWrap::dynamicDelayBasedBoolCallback(gpointer data)
{
    auto& dispatch = *reinterpret_cast<DispatchContext*>(data);
    auto& callback = *reinterpret_cast<DelayBased::CallbackContext<bool ()>*>(dispatch.second);
    if (g_cancellable_is_cancelled(callback.second.cancellable.get()))
        return G_SOURCE_CONTINUE;

    callback.second.dispatching = true;
    g_source_set_ready_time(dispatch.first, -1);

    if (callback.first())
        g_source_set_ready_time(dispatch.first, targetTimeForDelay(callback.second.delay));
    else
        g_source_set_callback(dispatch.first, nullptr, nullptr, nullptr);

    callback.second.dispatching = false;
    return G_SOURCE_CONTINUE;
}

gboolean GSourceWrap::staticOneShotCallback(gpointer data)
{
    auto& dispatch = *reinterpret_cast<DispatchContext*>(data);
    auto& callback = *reinterpret_cast<OneShot::CallbackContext*>(dispatch.second);

    g_source_set_ready_time(dispatch.first, -1);
    callback.first();

    return G_SOURCE_REMOVE;
}

gboolean GSourceWrap::staticSocketCallback(GSocket*, GIOCondition condition, gpointer data)
{
    auto& callback = *reinterpret_cast<Socket::CallbackContext*>(data);
    if (g_cancellable_is_cancelled(callback.second.get()))
        return G_SOURCE_REMOVE;

    return callback.first(condition);
}

gint64 GSourceWrap::targetTimeForDelay(std::chrono::microseconds delay)
{
    if (!delay.count())
        return 0;

    gint64 currentTime = g_get_monotonic_time();
    gint64 targetTime = currentTime + std::min<gint64>(G_MAXINT64 - currentTime, delay.count());
    ASSERT(targetTime >= currentTime);

    return targetTime;
}

GSourceWrap::Base::~Base()
{
    if (m_source)
        g_source_destroy(m_source.get());
}

bool GSourceWrap::DelayBased::isScheduled() const
{
    ASSERT(m_source);
    return g_source_get_ready_time(m_source.get()) != -1;
}

bool GSourceWrap::DelayBased::isActive() const
{
    return isScheduled() || m_context.dispatching;
}

void GSourceWrap::DelayBased::initialize(const char* name, int priority, GMainContext* context)
{
    ASSERT(!m_source);
    m_source = adoptGRef(g_source_new(&sourceFunctions, sizeof(GSource)));

    m_context.delay = std::chrono::microseconds(0);
    m_context.cancellable = adoptGRef(g_cancellable_new());
    m_context.dispatching = false;

    g_source_set_name(m_source.get(), name);
    g_source_set_priority(m_source.get(), priority);

    if (!context)
        context = g_main_context_get_thread_default();
    g_source_attach(m_source.get(), context);
}

void GSourceWrap::DelayBased::schedule(std::chrono::microseconds delay)
{
    ASSERT(m_source);
    m_context.delay = delay;

    if (g_cancellable_is_cancelled(m_context.cancellable.get()))
        m_context.cancellable = adoptGRef(g_cancellable_new());

    g_source_set_ready_time(m_source.get(), targetTimeForDelay(delay));
}

void GSourceWrap::DelayBased::cancel()
{
    ASSERT(m_source);
    g_cancellable_cancel(m_context.cancellable.get());
    g_source_set_ready_time(m_source.get(), -1);
}

GSourceWrap::Static::Static(const char* name, std::function<void ()>&& function, int priority, GMainContext* context)
{
    initialize(name, WTF::move(function), priority, context);
}

void GSourceWrap::Static::initialize(const char* name, std::function<void ()>&& function, int priority, GMainContext* context)
{
    DelayBased::initialize(name, priority, context);

    g_source_set_callback(m_source.get(), static_cast<GSourceFunc>(staticDelayBasedVoidCallback),
        new CallbackContext<void ()>{ WTF::move(function), m_context }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext<void ()>>));
}

void GSourceWrap::Static::schedule(std::chrono::microseconds delay)
{
    DelayBased::schedule(delay);
}

void GSourceWrap::Static::cancel()
{
    DelayBased::cancel();
}

GSourceWrap::Dynamic::Dynamic(const char* name, int priority, GMainContext* context)
{
    DelayBased::initialize(name, priority, context);
}

void GSourceWrap::Dynamic::schedule(std::function<void ()>&& function, std::chrono::microseconds delay)
{
    g_source_set_callback(m_source.get(), static_cast<GSourceFunc>(dynamicDelayBasedVoidCallback),
        new CallbackContext<void ()>{ WTF::move(function), m_context }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext<void ()>>));

    DelayBased::schedule(delay);
}

void GSourceWrap::Dynamic::schedule(std::function<bool ()>&& function, std::chrono::microseconds delay)
{
    g_source_set_callback(m_source.get(), static_cast<GSourceFunc>(dynamicDelayBasedBoolCallback),
        new CallbackContext<bool ()>{ WTF::move(function), m_context }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext<bool ()>>));

    DelayBased::schedule(delay);
}

void GSourceWrap::Dynamic::cancel()
{
    DelayBased::cancel();

    g_source_set_callback(m_source.get(), nullptr, nullptr, nullptr);
}

void GSourceWrap::OneShot::construct(const char* name, std::function<void ()>&& function, std::chrono::microseconds delay, int priority, GMainContext* context)
{
    GRefPtr<GSource> source = adoptGRef(g_source_new(&sourceFunctions, sizeof(GSource)));

    g_source_set_name(source.get(), name);
    g_source_set_priority(source.get(), priority);

    g_source_set_callback(source.get(), static_cast<GSourceFunc>(staticOneShotCallback),
        new CallbackContext{ WTF::move(function), nullptr }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext>));
    g_source_set_ready_time(source.get(), targetTimeForDelay(delay));

    if (!context)
        context = g_main_context_get_thread_default();
    g_source_attach(source.get(), context);
}

void GSourceWrap::Socket::initialize(const char* name, std::function<bool (GIOCondition)>&& function, GSocket* socket, GIOCondition condition, int priority, GMainContext* context)
{
    ASSERT(!m_source);
    GCancellable* cancellable = g_cancellable_new();
    m_source = adoptGRef(g_socket_create_source(socket, condition, cancellable));
    m_cancellable = adoptGRef(cancellable);

    g_source_set_name(m_source.get(), name);
    g_source_set_priority(m_source.get(), priority);

    g_source_set_callback(m_source.get(), reinterpret_cast<GSourceFunc>(staticSocketCallback),
        new CallbackContext{ WTF::move(function), m_cancellable }, static_cast<GDestroyNotify>(destroyCallbackContext<CallbackContext>));

    if (!context)
        context = g_main_context_get_thread_default();
    g_source_attach(m_source.get(), context);
}

void GSourceWrap::Socket::cancel()
{
    g_cancellable_cancel(m_cancellable.get());
}

GSourceWrap::Queue::Queue()
{
    g_mutex_init(&m_mutex);
}

GSourceWrap::Queue::~Queue()
{
    g_mutex_clear(&m_mutex);
}

void GSourceWrap::Queue::initialize(const char* name, int priority, GMainContext* context)
{
    m_sourceWrap.initialize(name, std::bind(&Queue::dispatchQueue, this), priority, context);
}

void GSourceWrap::Queue::queue(std::function<void ()>&& function)
{
    WTF::GMutexLocker<GMutex> lock(m_mutex);
    m_queue.append(WTF::move(function));

    m_sourceWrap.schedule();
}

void GSourceWrap::Queue::dispatchQueue()
{
    while (1) {
        decltype(m_queue) queue;
        {
            WTF::GMutexLocker<GMutex> lock(m_mutex);
            queue = WTF::move(m_queue);
        }

        if (!queue.size())
            break;

        for (auto& function : queue)
            function();
    }
}

} // namespace WTF
コード例 #2
0
bool GSourceWrap::DelayBased::isScheduled() const
{
    ASSERT(m_source);
    return g_source_get_ready_time(m_source.get()) != -1;
}
コード例 #3
0
bool RunLoop::TimerBase::isActive() const
{
    return g_source_get_ready_time(m_source.get()) != -1;
}
コード例 #4
0
namespace WTF {

static GSourceFuncs runLoopSourceFunctions = {
    nullptr, // prepare
    nullptr, // check
    // dispatch
    [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean
    {
        if (g_source_get_ready_time(source) == -1)
            return G_SOURCE_CONTINUE;
        g_source_set_ready_time(source, -1);
        return callback(userData);
    },
    nullptr, // finalize
    nullptr, // closure_callback
    nullptr, // closure_marshall
};

RunLoop::RunLoop()
{
    m_mainContext = g_main_context_get_thread_default();
    if (!m_mainContext)
        m_mainContext = isMainThread() ? g_main_context_default() : adoptGRef(g_main_context_new());
    ASSERT(m_mainContext);

    GRefPtr<GMainLoop> innermostLoop = adoptGRef(g_main_loop_new(m_mainContext.get(), FALSE));
    ASSERT(innermostLoop);
    m_mainLoops.append(innermostLoop);

    m_source = adoptGRef(g_source_new(&runLoopSourceFunctions, sizeof(GSource)));
    g_source_set_name(m_source.get(), "[WebKit] RunLoop work");
    g_source_set_can_recurse(m_source.get(), TRUE);
    g_source_set_callback(m_source.get(), [](gpointer userData) -> gboolean {
        static_cast<RunLoop*>(userData)->performWork();
        return G_SOURCE_CONTINUE;
    }, this, nullptr);
    g_source_set_priority(m_source.get(), G_PRIORITY_HIGH + 30);
    g_source_attach(m_source.get(), m_mainContext.get());
}

RunLoop::~RunLoop()
{
    g_source_destroy(m_source.get());

    for (int i = m_mainLoops.size() - 1; i >= 0; --i) {
        if (!g_main_loop_is_running(m_mainLoops[i].get()))
            continue;
        g_main_loop_quit(m_mainLoops[i].get());
    }
}

void RunLoop::run()
{
    RunLoop& runLoop = RunLoop::current();
    GMainContext* mainContext = runLoop.m_mainContext.get();

    // The innermost main loop should always be there.
    ASSERT(!runLoop.m_mainLoops.isEmpty());

    GMainLoop* innermostLoop = runLoop.m_mainLoops[0].get();
    if (!g_main_loop_is_running(innermostLoop)) {
        g_main_context_push_thread_default(mainContext);
        g_main_loop_run(innermostLoop);
        g_main_context_pop_thread_default(mainContext);
        return;
    }

    // Create and run a nested loop if the innermost one was already running.
    GMainLoop* nestedMainLoop = g_main_loop_new(mainContext, FALSE);
    runLoop.m_mainLoops.append(adoptGRef(nestedMainLoop));

    g_main_context_push_thread_default(mainContext);
    g_main_loop_run(nestedMainLoop);
    g_main_context_pop_thread_default(mainContext);

    runLoop.m_mainLoops.removeLast();
}

void RunLoop::stop()
{
    // The innermost main loop should always be there.
    ASSERT(!m_mainLoops.isEmpty());
    GRefPtr<GMainLoop> lastMainLoop = m_mainLoops.last();
    if (g_main_loop_is_running(lastMainLoop.get()))
        g_main_loop_quit(lastMainLoop.get());
}

void RunLoop::wakeUp()
{
    g_source_set_ready_time(m_source.get(), g_get_monotonic_time());
}

RunLoop::TimerBase::TimerBase(RunLoop& runLoop)
    : m_runLoop(runLoop)
    , m_source(adoptGRef(g_source_new(&runLoopSourceFunctions, sizeof(GSource))))
{
    g_source_set_name(m_source.get(), "[WebKit] RunLoop::Timer work");
    g_source_set_callback(m_source.get(), [](gpointer userData) -> gboolean {
        RunLoop::TimerBase* timer = static_cast<RunLoop::TimerBase*>(userData);
        timer->fired();
        if (timer->m_isRepeating)
            timer->updateReadyTime();
        return G_SOURCE_CONTINUE;
    }, this, nullptr);
    g_source_set_priority(m_source.get(), G_PRIORITY_HIGH + 30);
    g_source_attach(m_source.get(), m_runLoop.m_mainContext.get());
}

RunLoop::TimerBase::~TimerBase()
{
    g_source_destroy(m_source.get());
}

void RunLoop::TimerBase::setPriority(int priority)
{
    g_source_set_priority(m_source.get(), priority);
}

void RunLoop::TimerBase::updateReadyTime()
{
    if (!m_fireInterval.count()) {
        g_source_set_ready_time(m_source.get(), 0);
        return;
    }

    gint64 currentTime = g_get_monotonic_time();
    gint64 targetTime = currentTime + std::min<gint64>(G_MAXINT64 - currentTime, m_fireInterval.count());
    ASSERT(targetTime >= currentTime);
    g_source_set_ready_time(m_source.get(), targetTime);
}

void RunLoop::TimerBase::start(double fireInterval, bool repeat)
{
    auto intervalDuration = std::chrono::duration<double>(fireInterval);
    auto safeDuration = std::chrono::microseconds::max();
    if (intervalDuration < safeDuration)
        safeDuration = std::chrono::duration_cast<std::chrono::microseconds>(intervalDuration);

    m_fireInterval = safeDuration;
    m_isRepeating = repeat;
    updateReadyTime();
}

void RunLoop::TimerBase::stop()
{
    g_source_set_ready_time(m_source.get(), -1);
}

bool RunLoop::TimerBase::isActive() const
{
    return g_source_get_ready_time(m_source.get()) != -1;
}

} // namespace WTF
コード例 #5
0
ファイル: HeapTimer.cpp プロジェクト: cos2004/webkit
namespace JSC {

#if USE(CF)
    
const CFTimeInterval HeapTimer::s_decade = 60 * 60 * 24 * 365 * 10;

static const void* retainAPILock(const void* info)
{
    static_cast<JSLock*>(const_cast<void*>(info))->ref();
    return info;
}

static void releaseAPILock(const void* info)
{
    static_cast<JSLock*>(const_cast<void*>(info))->deref();
}

HeapTimer::HeapTimer(VM* vm, CFRunLoopRef runLoop)
    : m_vm(vm)
    , m_runLoop(runLoop)
{
    memset(&m_context, 0, sizeof(CFRunLoopTimerContext));
    m_context.info = &vm->apiLock();
    m_context.retain = retainAPILock;
    m_context.release = releaseAPILock;
    m_timer = adoptCF(CFRunLoopTimerCreate(kCFAllocatorDefault, s_decade, s_decade, 0, 0, HeapTimer::timerDidFire, &m_context));
    CFRunLoopAddTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes);
}

HeapTimer::~HeapTimer()
{
    CFRunLoopRemoveTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes);
    CFRunLoopTimerInvalidate(m_timer.get());
}

void HeapTimer::timerDidFire(CFRunLoopTimerRef timer, void* context)
{
    JSLock* apiLock = static_cast<JSLock*>(context);
    apiLock->lock();

    VM* vm = apiLock->vm();
    // The VM has been destroyed, so we should just give up.
    if (!vm) {
        apiLock->unlock();
        return;
    }

    HeapTimer* heapTimer = 0;
    if (vm->heap.fullActivityCallback() && vm->heap.fullActivityCallback()->m_timer.get() == timer)
        heapTimer = vm->heap.fullActivityCallback();
    else if (vm->heap.edenActivityCallback() && vm->heap.edenActivityCallback()->m_timer.get() == timer)
        heapTimer = vm->heap.edenActivityCallback();
    else if (vm->heap.sweeper()->m_timer.get() == timer)
        heapTimer = vm->heap.sweeper();
    else
        RELEASE_ASSERT_NOT_REACHED();

    {
        JSLockHolder locker(vm);
        heapTimer->doWork();
    }

    apiLock->unlock();
}

#elif PLATFORM(EFL)

HeapTimer::HeapTimer(VM* vm)
    : m_vm(vm)
    , m_timer(0)
{
}

HeapTimer::~HeapTimer()
{
    stop();
}

Ecore_Timer* HeapTimer::add(double delay, void* agent)
{
    return ecore_timer_add(delay, reinterpret_cast<Ecore_Task_Cb>(timerEvent), agent);
}
    
void HeapTimer::stop()
{
    if (!m_timer)
        return;

    ecore_timer_del(m_timer);
    m_timer = 0;
}

bool HeapTimer::timerEvent(void* info)
{
    HeapTimer* agent = static_cast<HeapTimer*>(info);
    
    JSLockHolder locker(agent->m_vm);
    agent->doWork();
    agent->m_timer = 0;
    
    return ECORE_CALLBACK_CANCEL;
}

#elif USE(GLIB)

static GSourceFuncs heapTimerSourceFunctions = {
    nullptr, // prepare
    nullptr, // check
    // dispatch
    [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean
    {
        if (g_source_get_ready_time(source) == -1)
            return G_SOURCE_CONTINUE;
        g_source_set_ready_time(source, -1);
        return callback(userData);
    },
    nullptr, // finalize
    nullptr, // closure_callback
    nullptr, // closure_marshall
};

HeapTimer::HeapTimer(VM* vm)
    : m_vm(vm)
    , m_apiLock(&vm->apiLock())
    , m_timer(adoptGRef(g_source_new(&heapTimerSourceFunctions, sizeof(GSource))))
{
    g_source_set_name(m_timer.get(), "[JavaScriptCore] HeapTimer");
    g_source_set_callback(m_timer.get(), [](gpointer userData) -> gboolean {
        static_cast<HeapTimer*>(userData)->timerDidFire();
        return G_SOURCE_CONTINUE;
    }, this, nullptr);
    g_source_attach(m_timer.get(), g_main_context_get_thread_default());
}

HeapTimer::~HeapTimer()
{
    g_source_destroy(m_timer.get());
}

void HeapTimer::timerDidFire()
{
    m_apiLock->lock();

    if (!m_apiLock->vm()) {
        // The VM has been destroyed, so we should just give up.
        m_apiLock->unlock();
        return;
    }

    {
        JSLockHolder locker(m_vm);
        doWork();
    }

    m_apiLock->unlock();
}

#else
HeapTimer::HeapTimer(VM* vm)
    : m_vm(vm)
{
}

HeapTimer::~HeapTimer()
{
}

void HeapTimer::invalidate()
{
}

#endif
    

} // namespace JSC