Title: [148286] trunk/Source/WebKit2
Revision
148286
Author
carlo...@webkit.org
Date
2013-04-12 10:24:19 -0700 (Fri, 12 Apr 2013)

Log Message

[GTK] Web Process crash when the UI process finishes too early
https://bugs.webkit.org/show_bug.cgi?id=112729

Reviewed by Anders Carlsson.

The problem is when creating the GSocket in the WorkQeue for the
socket descriptor. GLib considers a programmer error to create a
GSocket providing an invalid socket and finishes the process with
g_error(). We are actually providing a valid socket when creating
the GSocket, but it can be invalidated by the worker thread while
the GSocket is being created. This is because
registerEventSourceHandler is called from the main thread and
unregisterEventSourceHandler can be called from the worker
thread. We are currently registering two event handlers, one to
read data from the socket and another one to close the CoreIPC
connection when the socket connection is broken. Every event
source registered uses its own GSocket (even if the file
descriptor is actually the same), so that when the UI process
finishes too early, the first event handler can be executed in the
worker thread, closing the socket descriptor, while the main
thread is creating the GSocket for the second one.
We don't really need to use a separate event handler to monitor
the connection, because GSocket always notifies when condition is
G_IO_HUP and G_IO_ERR even if they haven't been explicitly set in
g_socket_create_source(). We can register socket event sources
differently, providing also a function to be called when the
connection is closed, using a single socket and the same even source.

* Platform/CoreIPC/unix/ConnectionUnix.cpp:
(CoreIPC::Connection::platformInvalidate):
(CoreIPC::Connection::open): Register a single socket event
handler providing also a function to be called when the connection
is closed.
* Platform/WorkQueue.h:
(WorkQueue):
* Platform/gtk/WorkQueueGtk.cpp: The EventSource class has been
split, moving everyting specific to socket event source to a
derived class SocketEventSource.
(WorkQueue::EventSource::EventSource):
(WorkQueue::EventSource::performWork):
(WorkQueue::EventSource::performWorkOnce):
(WorkQueue::EventSource::performWorkOnTermination):
(WorkQueue::EventSource::deleteEventSource):
(WorkQueue::EventSource):
(WorkQueue::SocketEventSource):
(WorkQueue::SocketEventSource::SocketEventSource):
(WorkQueue::SocketEventSource::cancel):
(WorkQueue::SocketEventSource::didClose):
(WorkQueue::SocketEventSource::checkCondition):
(WorkQueue::SocketEventSource::eventCallback):
(WorkQueue::registerSocketEventHandler):
(WorkQueue::unregisterSocketEventHandler):
(WorkQueue::dispatchOnSource):

Modified Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (148285 => 148286)


--- trunk/Source/WebKit2/ChangeLog	2013-04-12 17:23:42 UTC (rev 148285)
+++ trunk/Source/WebKit2/ChangeLog	2013-04-12 17:24:19 UTC (rev 148286)
@@ -1,3 +1,59 @@
+2013-04-12  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [GTK] Web Process crash when the UI process finishes too early
+        https://bugs.webkit.org/show_bug.cgi?id=112729
+
+        Reviewed by Anders Carlsson.
+
+        The problem is when creating the GSocket in the WorkQeue for the
+        socket descriptor. GLib considers a programmer error to create a
+        GSocket providing an invalid socket and finishes the process with
+        g_error(). We are actually providing a valid socket when creating
+        the GSocket, but it can be invalidated by the worker thread while
+        the GSocket is being created. This is because
+        registerEventSourceHandler is called from the main thread and
+        unregisterEventSourceHandler can be called from the worker
+        thread. We are currently registering two event handlers, one to
+        read data from the socket and another one to close the CoreIPC
+        connection when the socket connection is broken. Every event
+        source registered uses its own GSocket (even if the file
+        descriptor is actually the same), so that when the UI process
+        finishes too early, the first event handler can be executed in the
+        worker thread, closing the socket descriptor, while the main
+        thread is creating the GSocket for the second one.
+        We don't really need to use a separate event handler to monitor
+        the connection, because GSocket always notifies when condition is
+        G_IO_HUP and G_IO_ERR even if they haven't been explicitly set in
+        g_socket_create_source(). We can register socket event sources
+        differently, providing also a function to be called when the
+        connection is closed, using a single socket and the same even source.
+
+        * Platform/CoreIPC/unix/ConnectionUnix.cpp:
+        (CoreIPC::Connection::platformInvalidate):
+        (CoreIPC::Connection::open): Register a single socket event
+        handler providing also a function to be called when the connection
+        is closed.
+        * Platform/WorkQueue.h:
+        (WorkQueue):
+        * Platform/gtk/WorkQueueGtk.cpp: The EventSource class has been
+        split, moving everyting specific to socket event source to a
+        derived class SocketEventSource.
+        (WorkQueue::EventSource::EventSource):
+        (WorkQueue::EventSource::performWork):
+        (WorkQueue::EventSource::performWorkOnce):
+        (WorkQueue::EventSource::performWorkOnTermination):
+        (WorkQueue::EventSource::deleteEventSource):
+        (WorkQueue::EventSource):
+        (WorkQueue::SocketEventSource):
+        (WorkQueue::SocketEventSource::SocketEventSource):
+        (WorkQueue::SocketEventSource::cancel):
+        (WorkQueue::SocketEventSource::didClose):
+        (WorkQueue::SocketEventSource::checkCondition):
+        (WorkQueue::SocketEventSource::eventCallback):
+        (WorkQueue::registerSocketEventHandler):
+        (WorkQueue::unregisterSocketEventHandler):
+        (WorkQueue::dispatchOnSource):
+
 2013-04-12  Alexey Proskuryakov  <a...@apple.com>
 
         <rdar://problem/13126204> [Mac] Tweak sandbox profile.

Modified: trunk/Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp (148285 => 148286)


--- trunk/Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp	2013-04-12 17:23:42 UTC (rev 148285)
+++ trunk/Source/WebKit2/Platform/CoreIPC/unix/ConnectionUnix.cpp	2013-04-12 17:24:19 UTC (rev 148286)
@@ -140,7 +140,7 @@
         return;
 
 #if PLATFORM(GTK)
-    m_connectionQueue->unregisterEventSourceHandler(m_socketDescriptor);
+    m_connectionQueue->unregisterSocketEventHandler(m_socketDescriptor);
 #endif
 
 #if PLATFORM(QT)
@@ -424,8 +424,7 @@
 #if PLATFORM(QT)
     m_socketNotifier = m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, QSocketNotifier::Read, WTF::bind(&Connection::readyReadHandler, this));
 #elif PLATFORM(GTK)
-    m_connectionQueue->registerEventSourceHandler(m_socketDescriptor, (G_IO_HUP | G_IO_ERR), WTF::bind(&Connection::connectionDidClose, this));
-    m_connectionQueue->registerEventSourceHandler(m_socketDescriptor, G_IO_IN, WTF::bind(&Connection::readyReadHandler, this));
+    m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, G_IO_IN, WTF::bind(&Connection::readyReadHandler, this), WTF::bind(&Connection::connectionDidClose, this));
 #elif PLATFORM(EFL)
     m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, WTF::bind(&Connection::readyReadHandler, this));
 #endif

Modified: trunk/Source/WebKit2/Platform/WorkQueue.h (148285 => 148286)


--- trunk/Source/WebKit2/Platform/WorkQueue.h	2013-04-12 17:23:42 UTC (rev 148285)
+++ trunk/Source/WebKit2/Platform/WorkQueue.h	2013-04-12 17:24:19 UTC (rev 148286)
@@ -79,8 +79,8 @@
     QSocketNotifier* registerSocketEventHandler(int, QSocketNotifier::Type, const Function<void()>&);
     void dispatchOnTermination(WebKit::PlatformProcessIdentifier, const Function<void()>&);
 #elif PLATFORM(GTK)
-    void registerEventSourceHandler(int, int, const Function<void()>&);
-    void unregisterEventSourceHandler(int);
+    void registerSocketEventHandler(int, int, const Function<void()>& function, const Function<void()>& closeFunction);
+    void unregisterSocketEventHandler(int);
     void dispatchOnTermination(WebKit::PlatformProcessIdentifier, const Function<void()>&);
 #elif PLATFORM(EFL)
     void registerSocketEventHandler(int, const Function<void()>&);
@@ -163,8 +163,9 @@
     GRefPtr<GMainLoop> m_eventLoop;
     Mutex m_eventSourcesLock;
     class EventSource;
-    HashMap<int, Vector<EventSource*> > m_eventSources;
-    typedef HashMap<int, Vector<EventSource*> >::iterator EventSourceIterator; 
+    class SocketEventSource;
+    HashMap<int, Vector<SocketEventSource*> > m_eventSources;
+    typedef HashMap<int, Vector<SocketEventSource*> >::iterator SocketEventSourceIterator;
 #elif PLATFORM(EFL)
     class TimerWorkItem {
     public:

Modified: trunk/Source/WebKit2/Platform/gtk/WorkQueueGtk.cpp (148285 => 148286)


--- trunk/Source/WebKit2/Platform/gtk/WorkQueueGtk.cpp	2013-04-12 17:23:42 UTC (rev 148285)
+++ trunk/Source/WebKit2/Platform/gtk/WorkQueueGtk.cpp	2013-04-12 17:24:19 UTC (rev 148286)
@@ -35,59 +35,91 @@
 // WorkQueue::EventSource
 class WorkQueue::EventSource {
 public:
-    EventSource(const Function<void()>& function, WorkQueue* workQueue, GCancellable* cancellable)
+    EventSource(const Function<void()>& function, WorkQueue* workQueue)
         : m_function(function)
         , m_workQueue(workQueue)
-        , m_cancellable(cancellable)
     {
+        ASSERT(workQueue);
     }
 
-    void cancel()
+    void performWork()
     {
-        if (!m_cancellable)
-            return;
-        g_cancellable_cancel(m_cancellable);
+        m_function();
     }
 
-    static void executeEventSource(EventSource* eventSource)
+    static gboolean performWorkOnce(EventSource* eventSource)
     {
         ASSERT(eventSource);
-        eventSource->m_function();
+        eventSource->performWork();
+        return FALSE;
     }
 
-    static gboolean performWorkOnce(EventSource* eventSource)
+    static gboolean performWorkOnTermination(GPid, gint, EventSource* eventSource)
     {
-        executeEventSource(eventSource);
+        ASSERT(eventSource);
+        eventSource->performWork();
         return FALSE;
     }
 
-    static gboolean performWork(GSocket* socket, GIOCondition condition, EventSource* eventSource)
+    static void deleteEventSource(EventSource* eventSource)
     {
-        if (!(condition & G_IO_IN) && !(condition & G_IO_HUP) && !(condition & G_IO_ERR)) {
-            // EventSource has been cancelled, return FALSE to destroy the source.
-            return FALSE;
-        }
+        ASSERT(eventSource);
+        delete eventSource;
+    }
 
-        executeEventSource(eventSource);
-        return TRUE;
+private:
+    Function<void()> m_function;
+    RefPtr<WorkQueue> m_workQueue;
+};
+
+class WorkQueue::SocketEventSource : public WorkQueue::EventSource {
+public:
+    SocketEventSource(const Function<void()>& function, WorkQueue* workQueue, int condition, GCancellable* cancellable, const Function<void()>& closeFunction)
+        : EventSource(function, workQueue)
+        , m_condition(condition)
+        , m_cancellable(cancellable)
+        , m_closeFunction(closeFunction)
+    {
+        ASSERT(cancellable);
     }
 
-    static gboolean performWorkOnTermination(GPid, gint, EventSource* eventSource)
+    void cancel()
     {
-        executeEventSource(eventSource);
-        return FALSE;
+        g_cancellable_cancel(m_cancellable);
     }
 
-    static void deleteEventSource(EventSource* eventSource) 
+    void didClose()
     {
+        m_closeFunction();
+    }
+
+    bool checkCondition(GIOCondition condition) const
+    {
+        return condition & m_condition;
+    }
+
+    static gboolean eventCallback(GSocket* socket, GIOCondition condition, SocketEventSource* eventSource)
+    {
         ASSERT(eventSource);
-        delete eventSource;
+
+        if (condition & G_IO_HUP || condition & G_IO_ERR) {
+            eventSource->didClose();
+            return FALSE;
+        }
+
+        if (eventSource->checkCondition(condition)) {
+            eventSource->performWork();
+            return TRUE;
+        }
+
+        // EventSource has been cancelled, return FALSE to destroy the source.
+        return FALSE;
     }
-   
-public:
-    Function<void()> m_function;
-    RefPtr<WorkQueue> m_workQueue;
+
+private:
+    int m_condition;
     GCancellable* m_cancellable;
+    Function<void()> m_closeFunction;
 };
 
 // WorkQueue
@@ -139,25 +171,25 @@
     g_main_loop_run(m_eventLoop.get());
 }
 
-void WorkQueue::registerEventSourceHandler(int fileDescriptor, int condition, const Function<void()>& function)
+void WorkQueue::registerSocketEventHandler(int fileDescriptor, int condition, const Function<void()>& function, const Function<void()>& closeFunction)
 {
     GRefPtr<GSocket> socket = adoptGRef(g_socket_new_from_fd(fileDescriptor, 0));
     ASSERT(socket);
     GRefPtr<GCancellable> cancellable = adoptGRef(g_cancellable_new());
     GRefPtr<GSource> dispatchSource = adoptGRef(g_socket_create_source(socket.get(), static_cast<GIOCondition>(condition), cancellable.get()));
     ASSERT(dispatchSource);
-    EventSource* eventSource = new EventSource(function, this, cancellable.get());
+    SocketEventSource* eventSource = new SocketEventSource(function, this, condition, cancellable.get(), closeFunction);
     ASSERT(eventSource);
 
-    g_source_set_callback(dispatchSource.get(), reinterpret_cast<GSourceFunc>(&WorkQueue::EventSource::performWork),
+    g_source_set_callback(dispatchSource.get(), reinterpret_cast<GSourceFunc>(&WorkQueue::SocketEventSource::eventCallback),
         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);
-        Vector<EventSource*> sources;
-        EventSourceIterator it = m_eventSources.find(fileDescriptor);
-        if (it != m_eventSources.end()) 
+        Vector<SocketEventSource*> sources;
+        SocketEventSourceIterator it = m_eventSources.find(fileDescriptor);
+        if (it != m_eventSources.end())
             sources = it->value;
 
         sources.append(eventSource);
@@ -167,18 +199,18 @@
     g_source_attach(dispatchSource.get(), m_eventContext.get());
 }
 
-void WorkQueue::unregisterEventSourceHandler(int fileDescriptor)
+void WorkQueue::unregisterSocketEventHandler(int fileDescriptor)
 {
     ASSERT(fileDescriptor);
-    
+
     MutexLocker locker(m_eventSourcesLock);
-    
-    EventSourceIterator it = m_eventSources.find(fileDescriptor);
+
+    SocketEventSourceIterator it = m_eventSources.find(fileDescriptor);
     ASSERT(it != m_eventSources.end());
     ASSERT(m_eventSources.contains(fileDescriptor));
 
     if (it != m_eventSources.end()) {
-        Vector<EventSource*> sources = it->value;
+        Vector<SocketEventSource*> sources = it->value;
         for (unsigned i = 0; i < sources.size(); i++)
             sources[i]->cancel();
 
@@ -188,11 +220,9 @@
 
 void WorkQueue::dispatchOnSource(GSource* dispatchSource, const Function<void()>& function, GSourceFunc sourceCallback)
 {
-    EventSource* eventSource = new EventSource(function, this, 0);
+    g_source_set_callback(dispatchSource, sourceCallback, new EventSource(function, this),
+        reinterpret_cast<GDestroyNotify>(&WorkQueue::EventSource::deleteEventSource));
 
-    g_source_set_callback(dispatchSource, sourceCallback, eventSource,
-                          reinterpret_cast<GDestroyNotify>(&WorkQueue::EventSource::deleteEventSource));
-
     g_source_attach(dispatchSource, m_eventContext.get());
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to