2010-11-30 Amruth Raj <amruthraj@motorola.com> and Ravi Kasibhatla <ravi.kasibhatla...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Dec 2010 17:34:02 +0000 (17:34 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Dec 2010 17:34:02 +0000 (17:34 +0000)
        Reviewed by Martin Robinson.

        [GTK] Implement RunLoop, WorkQueue, Connection classes for WebKit2
        https://bugs.webkit.org/show_bug.cgi?id=48507

        * Platform/CoreIPC/Connection.h: UNIX_X11 specific changes for GTK port.
        (CoreIPC::Connection::messageProcessingCompleted):
        * Platform/CoreIPC/gtk: Added.
        * Platform/CoreIPC/gtk/ConnectionGtk.cpp: Added. Connection class implementation for GTK port (UNIX_X11 specific).
        (CoreIPC::readBytesFromSocket):
        (CoreIPC::writeBytesToSocket):
        (CoreIPC::Connection::platformInitialize):
        (CoreIPC::Connection::platformInvalidate):
        (CoreIPC::Connection::processCompletedMessage):
        (CoreIPC::Connection::readEventHandler):
        (CoreIPC::Connection::open):
        (CoreIPC::Connection::platformCanSendOutgoingMessages):
        (CoreIPC::Connection::sendOutgoingMessage):
        * Platform/PlatformProcessIdentifier.h: UNIX_X11 specific changes for GTK port.
        * Platform/RunLoop.h: GTK port specific changes.
        * Platform/WorkQueue.h: GTK port specific changes.
        * Platform/gtk/RunLoopGtk.cpp: Added. Runs an event loop using GMainLoop and handles the incoming messages.
        (RunLoop::RunLoop):
        (RunLoop::~RunLoop):
        (RunLoop::run):
        (RunLoop::mainLoop):
        (RunLoop::stop):
        (RunLoop::queueWork):
        (RunLoop::wakeUp):
        (RunLoop::TimerBase::TimerBase):
        (RunLoop::TimerBase::~TimerBase):
        (RunLoop::TimerBase::resetTimerSource):
        (RunLoop::TimerBase::oneShotTimerFired):
        (RunLoop::TimerBase::repeatingTimerFired):
        (RunLoop::TimerBase::start):
        (RunLoop::TimerBase::stop):
        (RunLoop::TimerBase::isActive):
        * Platform/gtk/WorkQueueGtk.cpp: Added. Creates a thread and polls on a given fd using GIOChannel.
        Dispatches the obtained messages to RunLoop's GMainLoop.
        (WorkQueue::EventSource::EventSource):
        (WorkQueue::EventSource::dispatchSource):
        (WorkQueue::EventSource::performWorkOnce):
        (WorkQueue::EventSource::performWork):
        (WorkQueue::EventSource::deleteEventSource):
        (WorkQueue::platformInitialize):
        (WorkQueue::platformInvalidate):
        (WorkQueue::startWorkQueueThread):
        (WorkQueue::workQueueThreadBody):
        (WorkQueue::registerEventSourceHandler):
        (WorkQueue::unregisterEventSourceHandler):
        (WorkQueue::scheduleWork):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@73142 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebKit2/ChangeLog
WebKit2/Platform/CoreIPC/Connection.h
WebKit2/Platform/CoreIPC/gtk/ConnectionGtk.cpp [new file with mode: 0644]
WebKit2/Platform/PlatformProcessIdentifier.h
WebKit2/Platform/RunLoop.h
WebKit2/Platform/WorkQueue.h
WebKit2/Platform/gtk/RunLoopGtk.cpp [new file with mode: 0644]
WebKit2/Platform/gtk/WorkQueueGtk.cpp [new file with mode: 0644]

index c9478d7..ca3880a 100644 (file)
@@ -1,3 +1,57 @@
+2010-11-30 Amruth Raj  <amruthraj@motorola.com> and Ravi Kasibhatla  <ravi.kasibhatla@motorola.com>
+
+        Reviewed by Martin Robinson.
+
+        [GTK] Implement RunLoop, WorkQueue, Connection classes for WebKit2
+        https://bugs.webkit.org/show_bug.cgi?id=48507
+
+        * Platform/CoreIPC/Connection.h: UNIX_X11 specific changes for GTK port.
+        (CoreIPC::Connection::messageProcessingCompleted):
+        * Platform/CoreIPC/gtk: Added.
+        * Platform/CoreIPC/gtk/ConnectionGtk.cpp: Added. Connection class implementation for GTK port (UNIX_X11 specific).
+        (CoreIPC::readBytesFromSocket):
+        (CoreIPC::writeBytesToSocket):
+        (CoreIPC::Connection::platformInitialize):
+        (CoreIPC::Connection::platformInvalidate):
+        (CoreIPC::Connection::processCompletedMessage):
+        (CoreIPC::Connection::readEventHandler):
+        (CoreIPC::Connection::open):
+        (CoreIPC::Connection::platformCanSendOutgoingMessages):
+        (CoreIPC::Connection::sendOutgoingMessage):
+        * Platform/PlatformProcessIdentifier.h: UNIX_X11 specific changes for GTK port.
+        * Platform/RunLoop.h: GTK port specific changes.
+        * Platform/WorkQueue.h: GTK port specific changes.
+        * Platform/gtk/RunLoopGtk.cpp: Added. Runs an event loop using GMainLoop and handles the incoming messages.
+        (RunLoop::RunLoop):
+        (RunLoop::~RunLoop):
+        (RunLoop::run):
+        (RunLoop::mainLoop):
+        (RunLoop::stop):
+        (RunLoop::queueWork):
+        (RunLoop::wakeUp):
+        (RunLoop::TimerBase::TimerBase):
+        (RunLoop::TimerBase::~TimerBase):
+        (RunLoop::TimerBase::resetTimerSource):
+        (RunLoop::TimerBase::oneShotTimerFired):
+        (RunLoop::TimerBase::repeatingTimerFired):
+        (RunLoop::TimerBase::start):
+        (RunLoop::TimerBase::stop):
+        (RunLoop::TimerBase::isActive):
+        * Platform/gtk/WorkQueueGtk.cpp: Added. Creates a thread and polls on a given fd using GIOChannel. 
+        Dispatches the obtained messages to RunLoop's GMainLoop.
+        (WorkQueue::EventSource::EventSource):
+        (WorkQueue::EventSource::dispatchSource):
+        (WorkQueue::EventSource::performWorkOnce):
+        (WorkQueue::EventSource::performWork):
+        (WorkQueue::EventSource::deleteEventSource):
+        (WorkQueue::platformInitialize):
+        (WorkQueue::platformInvalidate):
+        (WorkQueue::startWorkQueueThread):
+        (WorkQueue::workQueueThreadBody):
+        (WorkQueue::registerEventSourceHandler):
+        (WorkQueue::unregisterEventSourceHandler):
+        (WorkQueue::scheduleWork):
+
 2010-12-02  Steve Falkenburg  <sfalken@apple.com>
 
         Reviewed by Adam Roben.
index ca109bd..4da2200 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2010 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -90,6 +91,8 @@ public:
     static bool createServerAndClientIdentifiers(Identifier& serverIdentifier, Identifier& clientIdentifier);
 #elif PLATFORM(QT)
     typedef const QString Identifier;
+#elif PLATFORM(GTK)
+    typedef int Identifier;
 #endif
 
     static PassRefPtr<Connection> createServerConnection(Identifier, Client*, RunLoop* clientRunLoop);
@@ -263,6 +266,15 @@ private:
     size_t m_currentMessageSize;
     QLocalSocket* m_socket;
     QString m_serverName;
+#elif PLATFORM(GTK)
+    void readEventHandler();
+    void processCompletedMessage();
+    bool messageProcessingCompleted() { return !m_currentMessageSize; }
+
+    int m_socket;
+    Vector<uint8_t> m_readBuffer;
+    size_t m_currentMessageSize;
+    size_t m_pendingBytes;
 #endif
 };
 
diff --git a/WebKit2/Platform/CoreIPC/gtk/ConnectionGtk.cpp b/WebKit2/Platform/CoreIPC/gtk/ConnectionGtk.cpp
new file mode 100644 (file)
index 0000000..65b1254
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Connection.h"
+
+#include "ArgumentEncoder.h"
+#include "WorkItem.h"
+#include <errno.h>
+#include <glib.h>
+#include <sys/fcntl.h>
+
+using namespace std;
+
+namespace CoreIPC {
+
+static const size_t initialMessageBufferSize = 4096;
+
+static int readBytesFromSocket(int fileDescriptor, uint8_t* ptr, size_t length)
+{
+    ASSERT(fileDescriptor > 0);
+    ASSERT(buffer);
+    ASSERT(length > 0);
+
+    ssize_t numberOfBytesRead = 0;
+    size_t pendingBytesToRead = length;
+    uint8_t* buffer = ptr;
+
+    while (pendingBytesToRead > 0) {
+        if ((numberOfBytesRead = read(fileDescriptor, buffer, pendingBytesToRead)) < 0) {
+            if (errno == EINTR)
+                numberOfBytesRead = 0;
+            else if (errno == EAGAIN || errno == EWOULDBLOCK)
+                break;
+            else
+                return 0; 
+        } else if (!numberOfBytesRead)
+            break;
+
+        buffer += numberOfBytesRead;
+        pendingBytesToRead -= numberOfBytesRead;
+    }
+
+    return (length - pendingBytesToRead);
+}
+
+static bool writeBytesToSocket(int fileDescriptor, uint8_t* ptr, size_t length)
+{
+    ASSERT(fileDescriptor > 0);
+    ASSERT(buffer);
+    ASSERT(length > 0);
+
+    ssize_t numberOfBytesWritten = 0;
+    size_t pendingBytesToWrite = length;
+    uint8_t* buffer = ptr;
+
+    // Keep writing to the socket till the complete message has been written.
+    while (pendingBytesToWrite > 0) {
+        if ((numberOfBytesWritten = write(fileDescriptor, buffer, pendingBytesToWrite)) < 0) {
+            if (errno == EINTR)
+                numberOfBytesWritten = 0;
+            else
+                return false;
+        }
+        buffer += numberOfBytesWritten;
+        pendingBytesToWrite -= numberOfBytesWritten;
+    }
+
+    // Write operation failed if complete message is not written.
+    return !pendingBytesToWrite;
+}
+
+void Connection::platformInitialize(Identifier identifier)
+{
+    m_currentMessageSize = 0;
+    m_pendingBytes = 0;
+    m_readBuffer.resize(initialMessageBufferSize);
+    m_socket = identifier;
+    m_isConnected = true;
+}
+
+void Connection::platformInvalidate()
+{
+    if (!m_isConnected)
+        return;
+
+    m_connectionQueue.unregisterEventSourceHandler(m_socket);
+    if (m_socket > 0) {
+        close(m_socket);
+        m_socket = -1;
+    }
+
+    m_isConnected = false;
+}
+
+void Connection::processCompletedMessage()
+{
+    size_t realBufferSize = m_currentMessageSize - sizeof(MessageID);
+    unsigned messageID = *reinterpret_cast<unsigned*>(m_readBuffer.data() + realBufferSize);
+
+    processIncomingMessage(MessageID::fromInt(messageID), adoptPtr(new ArgumentDecoder(m_readBuffer.data(), realBufferSize)));
+
+    // Prepare for the next message.
+    m_currentMessageSize = 0;
+    m_pendingBytes = 0;
+}
+
+void Connection::readEventHandler()
+{
+    if (m_socket < 0)
+        return;
+
+    // Handle any previously unprocessed message.
+    if (!messageProcessingCompleted()) {
+        if ((m_pendingBytes -= readBytesFromSocket(m_socket, (m_readBuffer.data() + (m_currentMessageSize - m_pendingBytes)), m_pendingBytes)) > 0)
+            return;
+
+        // Message received completely. Process the message now.
+        processCompletedMessage();
+    }
+
+    // Prepare to read the next message.
+    uint8_t sizeBuffer[sizeof(size_t)];
+    memset(sizeBuffer, 0, sizeof(size_t));
+    
+    while (messageProcessingCompleted()) {
+        if (readBytesFromSocket(m_socket, sizeBuffer, sizeof(size_t)))
+            m_currentMessageSize = *reinterpret_cast<size_t*>(sizeBuffer);
+
+        if (!m_currentMessageSize)
+            break;
+
+        if (m_readBuffer.size() < m_currentMessageSize)
+            m_readBuffer.grow(m_currentMessageSize);
+
+        m_pendingBytes = m_currentMessageSize - readBytesFromSocket(m_socket, m_readBuffer.data(), m_currentMessageSize);
+        if (m_pendingBytes > 0) // Message partially received.
+            break;
+
+        // Message received completely. Process the message now.
+        processCompletedMessage();
+
+        memset(sizeBuffer, 0, sizeof(size_t));
+    }
+}
+
+bool Connection::open()
+{
+    int flags = fcntl(m_socket, F_GETFL, 0);
+    fcntl(m_socket, F_SETFL, flags | O_NONBLOCK);
+
+    // Register callbacks for connection termination and input data on the WorkQueue.
+    m_connectionQueue.registerEventSourceHandler(m_socket, (G_IO_HUP | G_IO_ERR), WorkItem::create(this, &Connection::connectionDidClose));
+    m_connectionQueue.registerEventSourceHandler(m_socket, G_IO_IN, WorkItem::create(this, &Connection::readEventHandler));
+    return true;
+}
+
+bool Connection::platformCanSendOutgoingMessages() const
+{
+    return (m_socket > 0);
+}
+
+bool Connection::sendOutgoingMessage(MessageID messageID, PassOwnPtr<ArgumentEncoder> arguments)
+{
+    if (m_socket < 0)
+        return false;
+
+    // We put the message ID last.
+    arguments->encodeUInt32(messageID.toInt());
+
+    size_t bufferSize = arguments->bufferSize();
+
+    // Send the message size first.
+    if (!writeBytesToSocket(m_socket, reinterpret_cast<uint8_t*>(&bufferSize), sizeof(size_t)))
+        return false;
+
+    if (!writeBytesToSocket(m_socket, arguments->buffer(), arguments->bufferSize()))
+        return false;
+
+    return true;
+}
+
+} // namespace CoreIPC
index ad1e394..0363692 100644 (file)
@@ -39,6 +39,8 @@ typedef pid_t PlatformProcessIdentifier;
 typedef HANDLE PlatformProcessIdentifier;
 #elif PLATFORM(QT)
 typedef QProcess* PlatformProcessIdentifier;
+#elif PLATFORM(GTK)
+typedef pid_t PlatformProcessIdentifier;
 #endif
 
 } // namespace WebKit 
index bf77763..aa87506 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2010 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include <wtf/ThreadSpecific.h>
 #include <wtf/Threading.h>
 #include <wtf/Vector.h>
+#if PLATFORM(GTK)
+typedef struct _GSource GSource;
+typedef struct _GMainLoop GMainLoop;
+typedef struct _GMainContext GMainContext;
+typedef int gboolean;
+#endif
 
 class WorkItem;
 
@@ -78,6 +85,11 @@ public:
         static void timerFired(RunLoop*, int ID);
         int m_ID;
         bool m_isRepeating;
+#elif PLATFORM(GTK)
+        static gboolean oneShotTimerFired(RunLoop::TimerBase*);
+        static gboolean repeatingTimerFired(RunLoop::TimerBase*);
+        void resetTimerSource();
+        GSource* m_timerSource;
 #endif
     };
 
@@ -129,6 +141,13 @@ private:
     TimerMap m_activeTimers;
     class TimerObject;
     TimerObject* m_timerObject;
+#elif PLATFORM(GTK)
+public:
+    static gboolean queueWork(RunLoop*);
+    GMainLoop* mainLoop();
+private:
+    GMainContext* m_runLoopContext;
+    GMainLoop* m_runLoopMain;
 #endif
 };
 
index 9803f61..78fa8b7 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,6 +44,9 @@
 class QLocalSocket;
 class QObject;
 class QThread;
+#elif PLATFORM(GTK)
+typedef struct _GMainContext GMainContext;
+typedef struct _GMainLoop GMainLoop;
 #endif
 
 class WorkQueue {
@@ -79,6 +83,9 @@ public:
     void disconnectSignal(QObject*, const char* signal);
 
     void moveSocketToWorkThread(QLocalSocket*);
+#elif PLATFORM(GTK)
+    void registerEventSourceHandler(int, int, PassOwnPtr<WorkItem>);
+    void unregisterEventSourceHandler(int);
 #endif
 
 private:
@@ -151,6 +158,18 @@ private:
     HashMap<QObject*, WorkItemQt*> m_signalListeners;
     QThread* m_workThread;
     friend class WorkItemQt;
+#elif PLATFORM(GTK)
+    static void* startWorkQueueThread(WorkQueue*);
+    void workQueueThreadBody();
+
+    ThreadIdentifier m_workQueueThread;
+    GMainContext* m_eventContext;
+    Mutex m_eventLoopLock;
+    GMainLoop* m_eventLoop;
+    Mutex m_eventSourcesLock;
+    class EventSource;
+    HashMap<int, Vector<EventSource*> > m_eventSources;
+    typedef HashMap<int, Vector<EventSource*> >::iterator EventSourceIterator; 
 #endif
 };
 
diff --git a/WebKit2/Platform/gtk/RunLoopGtk.cpp b/WebKit2/Platform/gtk/RunLoopGtk.cpp
new file mode 100644 (file)
index 0000000..9ff9a9f
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "RunLoop.h"
+
+#include "WKBase.h"
+#include <glib.h>
+
+RunLoop::RunLoop()
+{
+    m_runLoopContext = g_main_context_default();
+    ASSERT(m_runLoopContext);
+    m_runLoopMain = g_main_loop_new(m_runLoopContext, FALSE);
+    ASSERT(m_runLoopMain);
+}
+
+RunLoop::~RunLoop()
+{
+    if (m_runLoopMain) {
+        g_main_loop_quit(m_runLoopMain);
+        g_main_loop_unref(m_runLoopMain);
+    }
+
+    if (m_runLoopContext)
+        g_main_context_unref(m_runLoopContext);
+}
+
+void RunLoop::run()
+{
+    g_main_loop_run(RunLoop::main()->mainLoop());
+}
+
+GMainLoop* RunLoop::mainLoop()
+{
+    return m_runLoopMain;
+}
+
+void RunLoop::stop()
+{
+    g_main_loop_quit(m_runLoopMain);
+}
+
+gboolean RunLoop::queueWork(RunLoop* runLoop)
+{
+    runLoop->performWork();
+    return FALSE;
+}
+
+void RunLoop::wakeUp()
+{
+    GSource* source = g_timeout_source_new(0);
+    g_source_set_callback(source, reinterpret_cast<GSourceFunc>(&RunLoop::queueWork), this, 0);
+    g_source_attach(source, m_runLoopContext);
+
+    g_main_context_wakeup(m_runLoopContext);
+}
+
+RunLoop::TimerBase::TimerBase(RunLoop* runLoop)
+    : m_runLoop(runLoop)
+    , m_timerSource(0)
+{
+}
+
+RunLoop::TimerBase::~TimerBase()
+{
+    stop();
+}
+
+void RunLoop::TimerBase::resetTimerSource()
+{
+    m_timerSource = 0;
+}
+
+gboolean RunLoop::TimerBase::oneShotTimerFired(RunLoop::TimerBase* timer)
+{
+    timer->fired();
+    timer->resetTimerSource();
+    return FALSE;
+}
+
+gboolean RunLoop::TimerBase::repeatingTimerFired(RunLoop::TimerBase* timer)
+{
+    timer->fired();
+    return TRUE;
+}
+
+void RunLoop::TimerBase::start(double nextFireInterval, double repeatInterval)
+{
+    if (m_timerSource)
+        stop();
+
+    if (repeatInterval) {
+        m_timerSource = g_timeout_source_new(static_cast<guint>(repeatInterval));
+        g_source_set_callback(m_timerSource, reinterpret_cast<GSourceFunc>(&RunLoop::TimerBase::repeatingTimerFired), this, 0);
+    } else {
+        m_timerSource = g_timeout_source_new(static_cast<guint>(nextFireInterval));
+        g_source_set_callback(m_timerSource, reinterpret_cast<GSourceFunc>(&RunLoop::TimerBase::oneShotTimerFired), this, 0);
+    }
+    g_source_attach(m_timerSource, m_runLoop->m_runLoopContext);
+}
+
+void RunLoop::TimerBase::stop()
+{
+    if (!m_timerSource)
+        return;
+
+    g_source_destroy(m_timerSource);
+    m_timerSource = 0;
+}
+
+bool RunLoop::TimerBase::isActive() const
+{
+    return m_timerSource;
+}
diff --git a/WebKit2/Platform/gtk/WorkQueueGtk.cpp b/WebKit2/Platform/gtk/WorkQueueGtk.cpp
new file mode 100644 (file)
index 0000000..1f0d118
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "WorkQueue.h"
+
+#include "WKBase.h"
+#include <glib.h>
+
+// WorkQueue::EventSource
+class WorkQueue::EventSource {
+public:
+    EventSource(GSource* dispatchSource, PassOwnPtr<WorkItem> workItem, WorkQueue* workQueue)
+        : m_dispatchSource(dispatchSource)
+        , m_workItem(workItem)
+        , m_workQueue(workQueue)
+    {
+    }
+
+    GSource* dispatchSource() { return m_dispatchSource; }
+
+    static gboolean performWorkOnce(EventSource* eventSource)
+    {
+        ASSERT(eventSource);
+        WorkQueue* queue = eventSource->m_workQueue;
+        {
+            MutexLocker locker(queue->m_isValidMutex);
+            if (!queue->m_isValid)
+                return FALSE;
+        }
+
+        eventSource->m_workItem->execute();
+        return FALSE;
+    }
+
+    static gboolean performWork(GIOChannel* channel, GIOCondition condition, EventSource* eventSource) 
+    {
+        ASSERT(eventSource);
+
+        if (!(condition & G_IO_IN) && !(condition & G_IO_HUP) && !(condition & G_IO_ERR))
+            return FALSE;
+
+        WorkQueue* queue = eventSource->m_workQueue;
+        {
+            MutexLocker locker(queue->m_isValidMutex);
+            if (!queue->m_isValid)
+                return FALSE;
+        }
+
+        eventSource->m_workItem->execute();
+
+        if ((condition & G_IO_HUP) || (condition & G_IO_ERR))
+            return FALSE;
+
+        return TRUE;
+    }
+    
+    static void deleteEventSource(EventSource* eventSource) 
+    {
+        ASSERT(eventSource);
+        delete eventSource;
+    }
+   
+public:
+    GSource* m_dispatchSource;
+    PassOwnPtr<WorkItem> m_workItem;
+    WorkQueue* m_workQueue;
+};
+
+// WorkQueue
+void WorkQueue::platformInitialize(const char* name)
+{
+    m_eventContext = g_main_context_new();
+    ASSERT(m_eventContext);
+    m_eventLoop = g_main_loop_new(m_eventContext, FALSE);
+    ASSERT(m_eventLoop);
+    m_workQueueThread = createThread(reinterpret_cast<WTF::ThreadFunction>(&WorkQueue::startWorkQueueThread), this, name);
+}
+
+void WorkQueue::platformInvalidate()
+{
+    MutexLocker locker(m_eventLoopLock);
+
+    if (m_eventLoop) {
+        if (g_main_loop_is_running(m_eventLoop))
+            g_main_loop_quit(m_eventLoop);
+
+        g_main_loop_unref(m_eventLoop);
+        m_eventLoop = 0;
+    }
+
+    if (m_eventContext) {
+        g_main_context_unref(m_eventContext);
+        m_eventContext = 0;
+    }
+}
+
+void* WorkQueue::startWorkQueueThread(WorkQueue* workQueue)
+{
+    workQueue->workQueueThreadBody();
+    return 0;
+}
+
+void WorkQueue::workQueueThreadBody()
+{
+    g_main_loop_run(m_eventLoop);
+}
+
+void WorkQueue::registerEventSourceHandler(int fileDescriptor, int condition, PassOwnPtr<WorkItem> item)
+{
+    GIOChannel* channel = g_io_channel_unix_new(fileDescriptor);
+    ASSERT(channel);
+    GSource* dispatchSource = g_io_create_watch(channel, static_cast<GIOCondition>(condition));
+    ASSERT(dispatchSource);
+    EventSource* eventSource = new EventSource(dispatchSource, item, this);
+    ASSERT(eventSource);
+
+    g_source_set_callback(dispatchSource, reinterpret_cast<GSourceFunc>(&WorkQueue::EventSource::performWork), 
+        eventSource, reinterpret_cast<GDestroyNotify>(&WorkQueue::EventSource::deleteEventSource));
+
+    // Set up the event sources under the mutex since this is shared across multiple threads.
+    {
+        MutexLocker locker(m_eventSourcesLock);
+        ASSERT(!m_eventSources.contains(fileDescriptor));
+        Vector<EventSource*> sources;
+        EventSourceIterator it = m_eventSources.find(fileDescriptor);
+        if (it != m_eventSources.end()) 
+            sources = it->second;
+
+        sources.append(eventSource);
+        m_eventSources.set(fileDescriptor, sources);
+    }
+
+    // Attach the event source to the GMainContext under the mutex since this is shared across multiple threads.
+    {
+        MutexLocker locker(m_eventLoopLock);
+        g_source_attach(dispatchSource, m_eventContext);
+    }
+}
+
+void WorkQueue::unregisterEventSourceHandler(int fileDescriptor)
+{
+    ASSERT(fileDescriptor);
+    
+    MutexLocker locker(m_eventSourcesLock);
+    
+    EventSourceIterator it = m_eventSources.find(fileDescriptor);
+    ASSERT(it != m_eventSources.end());
+    ASSERT(m_eventSources.contains(fileDescriptor));
+
+    if (it != m_eventSources.end()) {
+        Vector<EventSource*> sources = it->second;
+        for (unsigned i = 0; i < sources.size(); i++)
+            g_source_destroy(sources[i]->dispatchSource());
+
+        m_eventSources.remove(it);
+    }
+}
+
+void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
+{
+    GSource* dispatchSource = g_timeout_source_new(0);
+    ASSERT(dispatchSource);
+    EventSource* eventSource = new EventSource(dispatchSource, item, this);
+    
+    g_source_set_callback(dispatchSource, 
+                          reinterpret_cast<GSourceFunc>(&WorkQueue::EventSource::performWorkOnce), 
+                          eventSource, 
+                          reinterpret_cast<GDestroyNotify>(&WorkQueue::EventSource::deleteEventSource));
+    {
+        MutexLocker locker(m_eventLoopLock);
+        g_source_attach(dispatchSource, m_eventContext);
+    }
+}