]> git.sesse.net Git - vlc/blobdiff - src/misc/objects.c
Remove the requirement for vlc object to be locked when invoking waitpipe.
[vlc] / src / misc / objects.c
index 734cb3502ad6343d2b8b23709998146f35b5f30c..771961534fda1b68ad3021d7eba10255d7c2e440 100644 (file)
@@ -195,6 +195,7 @@ vlc_object_t *vlc_custom_create( vlc_object_t *p_this, size_t i_size,
     vlc_mutex_init( p_new, &p_new->object_lock );
     vlc_cond_init( p_new, &p_new->object_wait );
     vlc_mutex_init( p_new, &p_priv->var_lock );
+    vlc_spin_init( &p_priv->spin );
     p_priv->pipes[0] = p_priv->pipes[1] = -1;
 
     if( i_type == VLC_OBJECT_GLOBAL )
@@ -443,6 +444,7 @@ void __vlc_object_destroy( vlc_object_t *p_this )
 
     vlc_mutex_destroy( &p_this->object_lock );
     vlc_cond_destroy( &p_this->object_wait );
+    vlc_spin_destroy( &p_priv->spin );
     if( p_priv->pipes[0] != -1 )
         close( p_priv->pipes[0] );
     if( p_priv->pipes[1] != -1 )
@@ -467,6 +469,59 @@ void __vlc_object_unlock( vlc_object_t *obj )
     vlc_mutex_unlock( &obj->object_lock );
 }
 
+#ifdef WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+
+/**
+ * select()-able pipes emulated using Winsock
+ */
+static int pipe (int fd[2])
+{
+    SOCKADDR_IN addr;
+    int addrlen = sizeof (addr);
+
+    SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
+           c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
+        goto error;
+
+    memset (&addr, 0, sizeof (addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+    if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
+     || getsockname (l, (PSOCKADDR)&addr, &addrlen)
+     || listen (l, 1)
+     || connect (c, (PSOCKADDR)&addr, addrlen))
+        goto error;
+
+    a = accept (l, NULL, NULL);
+    if (a == INVALID_SOCKET)
+        goto error;
+
+    closesocket (l);
+    shutdown (a, 0);
+    shutdown (c, 1);
+    fd[0] = c;
+    fd[1] = a;
+    return 0;
+
+error:
+    if (l != INVALID_SOCKET)
+        closesocket (l);
+    if (c != INVALID_SOCKET)
+        closesocket (c);
+    return -1;
+}
+
+#undef  read
+#define read( a, b, c )  recv (a, b, c, 0)
+#undef  write
+#define write( a, b, c ) send (a, b, c, 0)
+#undef  close
+#define close( a )       closesocket (a)
+#endif /* WIN32 */
+
 /**
  * Returns the readable end of a pipe that becomes readable whenever
  * an object is signaled. This can be used to wait for VLC object events
@@ -479,28 +534,48 @@ void __vlc_object_unlock( vlc_object_t *obj )
  * Assuming the pipe is readable, vlc_object_wait() will not block.
  * Also note that, as with vlc_object_wait(), there may be spurious wakeups.
  *
- * @param obj object that would be signaled (object lock MUST hold)
+ * @param obj object that would be signaled
  * @return a readable pipe descriptor, or -1 on error.
  */
-int vlc_object_waitpipe( vlc_object_t *obj )
+int __vlc_object_waitpipe( vlc_object_t *obj )
 {
-    int *pipes = obj->p_internals->pipes;
-    vlc_assert_locked( &obj->object_lock );
+    int pfd[2] = { -1, -1 };
+    struct vlc_object_internals_t *internals = obj->p_internals;
+    vlc_bool_t race = VLC_FALSE, signaled = VLC_FALSE;
 
-    if( pipes[1] == -1 )
+    vlc_spin_lock (&internals->spin);
+    if (internals->pipes[0] == -1)
     {
-        /* This can only ever happen if someone killed us without locking */
-        assert( pipes[0] == -1 );
+        /* This can only ever happen if someone killed us without locking: */
+        assert (internals->pipes[1] == -1);
+        vlc_spin_unlock (&internals->spin);
 
-#ifndef WIN32
-        if( pipe( pipes ) )
-#else
-        errno = ENOSYS;
-#endif
+        if (pipe (pfd))
             return -1;
+
+        vlc_spin_lock (&internals->spin);
+        signaled = internals->b_signaled;
+        if ((!signaled) && (internals->pipes[0] == -1))
+        {
+            internals->pipes[0] = pfd[0];
+            internals->pipes[1] = pfd[1];
+        }
     }
+    vlc_spin_unlock (&internals->spin);
 
-    return pipes[0];
+    if (race || signaled)
+    {
+        close (pfd[0]);
+        close (pfd[1]);
+
+        if (signaled)
+        {   /* vlc_object_signal() was already invoked! */
+            errno = EINTR;
+            return -1;
+        }
+    }
+
+    return internals->pipes[0];
 }
 
 
@@ -513,20 +588,19 @@ int vlc_object_waitpipe( vlc_object_t *obj )
  */
 vlc_bool_t __vlc_object_wait( vlc_object_t *obj )
 {
+    int fd;
+
     vlc_assert_locked( &obj->object_lock );
 
-    int fd = obj->p_internals->pipes[0];
-    if( fd != -1 )
+    fd = obj->p_internals->pipes[0];
+    if (fd != -1)
     {
-        if( read( fd, &(char){ 0 }, 1 ) == 0 )
-        {
-            close( fd );
-            obj->p_internals->pipes[1] = -1;
-        }
+        while (read (fd, &(char){ 0 }, 1)  < 0);
         return obj->b_die;
     }
 
     vlc_cond_wait( &obj->object_wait, &obj->object_lock );
+    obj->p_internals->b_signaled = VLC_FALSE;
     return obj->b_die;
 }
 
@@ -588,9 +662,16 @@ vlc_bool_t __vlc_object_alive( vlc_object_t *obj )
  */
 void __vlc_object_signal_unlocked( vlc_object_t *obj )
 {
-    vlc_assert_locked( &obj->object_lock );
+    struct vlc_object_internals_t *internals = obj->p_internals;
+    int fd;
+
+    vlc_assert_locked (&obj->object_lock);
+
+    vlc_spin_lock (&internals->spin);
+    fd = internals->pipes[1];
+    internals->b_signaled = VLC_TRUE;
+    vlc_spin_unlock (&internals->spin);
 
-    int fd = obj->p_internals->pipes[1];
     if( fd != -1 )
         while( write( fd, &(char){ 0 }, 1 ) < 0 );
 
@@ -611,13 +692,6 @@ void __vlc_object_kill( vlc_object_t *p_this )
         for( int i = 0; i < p_this->i_children ; i++ )
             vlc_object_kill( p_this->pp_children[i] );
 
-    int fd = p_this->p_internals->pipes[1];
-    if( fd != -1 )
-    {
-        close( fd ); /* closing a pipe makes it readable too */
-        p_this->p_internals->pipes[1] = -1;
-    }
-
     vlc_object_signal_unlocked( p_this );
     vlc_mutex_unlock( &p_this->object_lock );
 }