osxvideosink: make sure all selectors are performed on the same thread
authorAndoni Morales Alastruey <ylatuya@gmail.com>
Thu, 24 May 2012 18:43:16 +0000 (20:43 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Fri, 25 May 2012 09:07:35 +0000 (11:07 +0200)
When we are using a dedicated thread to run the main run loop we
must make sure that all selectors are performed on this same thread.
For instance if performSelectorOnMainThread is called from the real
main thread, it will not go through the message queue and will be
executed from the real main thread. By forcing the target thread,
we ensure that all functions will be called either from the real
main thread when the main run loop is running or from our thread
spinning the main loop.

sys/osxvideo/cocoawindow.h
sys/osxvideo/cocoawindow.m
sys/osxvideo/osxvideosink.h
sys/osxvideo/osxvideosink.m

index d811cb6fd4abc1f9bacd04595c01f0c0f55d27ef..9d9052597ba723e8875dc3eb0b504b0f4c2c5684 100644 (file)
@@ -49,6 +49,7 @@ struct _GstOSXImage;
     NSTrackingArea *trackingArea;
     GstNavigation *navigation;
     NSRect drawingBounds;
+    NSThread *mainThread;
 }
 - (void) drawQuad;
 - (void) drawRect: (NSRect) rect;
@@ -68,6 +69,7 @@ struct _GstOSXImage;
 - (void) addToSuperview: (NSView *)superview;
 - (void) removeFromSuperview: (id)unused;
 - (void) setNavigation: (GstNavigation *) nav;
+- (void) setMainThread: (NSThread *) thread;
 
 @end
 
index 2e9398356ea31048366998010749618bf9117c24..d8fde9becd4c2ca4548d564e5c0fc92006251aa5 100644 (file)
       userInfo:nil];
 
   [self addTrackingArea:trackingArea];
+  mainThread = [NSThread mainThread];
 
   [self initTextures];
   return self;
   [self reshape];
 }
 
+- (void) setMainThread: (NSThread *) thread {
+  mainThread = thread;
+}
+
 - (void) haveSuperviewReal:(NSMutableArray *)closure {
        BOOL haveSuperview = [self superview] != nil;
        [closure addObject:[NSNumber numberWithBool:haveSuperview]];
 
 - (BOOL) haveSuperview {
        NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
-       [self performSelectorOnMainThread:@selector(haveSuperviewReal:)
-                       withObject:(id)closure waitUntilDone:YES];
+       [self performSelector:@selector(haveSuperviewReal:)
+               onThread:mainThread
+               withObject:(id)closure waitUntilDone:YES];
 
        return [[closure objectAtIndex:0] boolValue];
 }
 }
 
 - (void) addToSuperview: (NSView *)superview {
-       [self performSelectorOnMainThread:@selector(addToSuperviewReal:)
-                       withObject:superview waitUntilDone:YES];
+       [self performSelector:@selector(addToSuperviewReal:)
+               onThread:mainThread
+               withObject:superview waitUntilDone:YES];
 }
 
 - (void) removeFromSuperview: (id)unused
index 137124ba286c29465f7d8ede522cbc8acd63d38b..f3c9eac5eab810c9245aab9fb446b3054b927fa0 100644 (file)
@@ -76,7 +76,7 @@ struct _GstOSXVideoSink {
   void *osxvideosinkobject;
   NSView *superview;
 #ifdef RUN_NS_APP_THREAD
-  GThread *ns_app_thread;
+  NSThread *ns_app_thread;
 #else
   guint cocoa_timeout;
   gboolean app_started;
@@ -131,6 +131,9 @@ GType gst_osx_video_sink_get_type(void);
 -(void) resize;
 -(void) destroy;
 -(void) showFrame: (GstBufferObject*) buf;
+#ifdef RUN_NS_APP_THREAD
+-(void) nsAppThread;
+#endif
 @end
 
 G_END_DECLS
index 8fcb00f40d41a4557c2822f33597ace243eb5c00..d4f33308e0de5a6c302d57406eec68f9bff6f26a 100644 (file)
@@ -80,12 +80,13 @@ static GstVideoSinkClass *parent_class = NULL;
 
 /* Helper to trigger calls from the main thread */
 static void
-gst_osx_video_sink_call_from_main_thread(NSObject * object, SEL function,
-    NSObject *data, BOOL waitUntilDone)
+gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
+    NSObject * object, SEL function, NSObject *data, BOOL waitUntilDone)
 {
+
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
-  [object performSelectorOnMainThread:function
+  [object performSelector:function onThread:osxvideosink->ns_app_thread
           withObject:data waitUntilDone:waitUntilDone];
   [pool release];
 }
@@ -113,37 +114,6 @@ run_ns_app_loop (void) {
   [pool release];
 }
 
-#ifdef RUN_NS_APP_THREAD
-static gpointer
-ns_app_loop_thread (gpointer data)
-{
-  NSAutoreleasePool *pool;
-
-  /* set the main runloop as the runloop for the current thread. This has the
-   * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
-   * runs the main runloop.
-   */
-  _CFRunLoopSetCurrent(CFRunLoopGetMain());
-
-  /* this is needed to make IsMainThread checks in core foundation work from the
-   * current thread
-   */
-  _CFMainPThread = pthread_self();
-
-  pool = [[NSAutoreleasePool alloc] init];
-
-  [NSApplication sharedApplication];
-  [NSApp finishLaunching];
-
-  /* run the loop */
-  run_ns_app_loop ();
-
-  [pool release];
-
-  return NULL;
-}
-#endif
-
 static void
 gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * osxvideosink )
 {
@@ -168,8 +138,10 @@ gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * osxvideosink )
 
     method_exchangeImplementations(origIsMainThread, ourIsMainThread);
 
-    osxvideosink->ns_app_thread = g_thread_new ("GstNSAppThread",
-        ns_app_loop_thread, NULL);
+    osxvideosink->ns_app_thread = [[NSThread alloc]
+        initWithTarget:osxvideosink->osxvideosinkobject
+        selector:@selector(nsAppThread) object:nil];
+    [osxvideosink->ns_app_thread start];
 #else
   /* assume that there is a GMainLoop and iterate the main runloop from there
    */
@@ -227,7 +199,10 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
 
   GST_INFO_OBJECT (osxvideosink, "'have-ns-view' message sent");
 
+  osxvideosink->ns_app_thread = [NSThread mainThread];
   gst_osx_video_sink_run_cocoa_loop (osxvideosink);
+  [osxwindow->gstview setMainThread:osxvideosink->ns_app_thread];
+
   /* check if have-ns-view was handled and osxwindow->gstview was added to a
    * superview
    */
@@ -244,7 +219,7 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
        * from the main thread
        */
       GST_INFO_OBJECT (osxvideosink, "we have a superview, adding our view to it");
-      gst_osx_video_sink_call_from_main_thread(osxwindow->gstview,
+      gst_osx_video_sink_call_from_main_thread(osxvideosink, osxwindow->gstview,
           @selector(addToSuperview:), osxvideosink->superview, NO);
 
     } else {
@@ -255,7 +230,8 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
          */
         GST_INFO_OBJECT (osxvideosink, "no superview");
       } else {
-        gst_osx_video_sink_call_from_main_thread(osxvideosink->osxvideosinkobject,
+        gst_osx_video_sink_call_from_main_thread(osxvideosink,
+          osxvideosink->osxvideosinkobject,
           @selector(createInternalWindow), nil, YES);
         GST_INFO_OBJECT (osxvideosink, "No superview, creating an internal window.");
       }
@@ -277,8 +253,9 @@ gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink)
   g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
   pool = [[NSAutoreleasePool alloc] init];
 
-  gst_osx_video_sink_call_from_main_thread(osxvideosink->osxvideosinkobject,
-        @selector(destroy), (id) nil, YES);
+  gst_osx_video_sink_call_from_main_thread(osxvideosink,
+      osxvideosink->osxvideosinkobject,
+      @selector(destroy), (id) nil, YES);
   gst_osx_video_sink_stop_cocoa_loop (osxvideosink);
   [pool release];
 }
@@ -301,7 +278,8 @@ gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
 
   /* Directly resize the underlying view */
   GST_DEBUG_OBJECT (osxvideosink, "Calling setVideoSize on %p", osxwindow->gstview);
-  gst_osx_video_sink_call_from_main_thread(object, @selector(resize), (id)nil, YES);
+  gst_osx_video_sink_call_from_main_thread(osxvideosink, object,
+      @selector(resize), (id)nil, YES);
 
   [pool release];
 }
@@ -404,7 +382,8 @@ gst_osx_video_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
 
   GST_DEBUG ("show_frame");
   bufferobject = [[GstBufferObject alloc] initWithBuffer:buf];
-  gst_osx_video_sink_call_from_main_thread(osxvideosink->osxvideosinkobject,
+  gst_osx_video_sink_call_from_main_thread(osxvideosink,
+      osxvideosink->osxvideosinkobject,
       @selector(showFrame:), bufferobject, NO);
   [pool release];
   return GST_FLOW_OK;
@@ -646,7 +625,8 @@ gst_osx_video_sink_set_window_handle (GstXOverlay * overlay, guintptr handle_id)
   if (osxvideosink->superview) {
     GST_INFO_OBJECT (osxvideosink, "old xwindow id %p", osxvideosink->superview);
     if (osxvideosink->osxwindow) {
-      gst_osx_video_sink_call_from_main_thread(osxvideosink->osxwindow->gstview,
+      gst_osx_video_sink_call_from_main_thread(osxvideosink,
+          osxvideosink->osxwindow->gstview,
           @selector(removeFromSuperview:), (id)nil, YES);
     }
     [osxvideosink->superview release];
@@ -656,7 +636,8 @@ gst_osx_video_sink_set_window_handle (GstXOverlay * overlay, guintptr handle_id)
   GST_INFO_OBJECT (osxvideosink, "set xwindow id 0x%lx", window_id);
   osxvideosink->superview = [((NSView *) window_id) retain];
   if (osxvideosink->osxwindow) {
-      gst_osx_video_sink_call_from_main_thread(osxvideosink->osxwindow->gstview,
+      gst_osx_video_sink_call_from_main_thread(osxvideosink,
+        osxvideosink->osxwindow->gstview,
         @selector(addToSuperview:), osxvideosink->superview, YES);
   }
 }
@@ -872,6 +853,34 @@ gst_osx_video_sink_get_type (void)
   [pool release];
 }
 
+#ifdef RUN_NS_APP_THREAD
+-(void) nsAppThread
+{
+  NSAutoreleasePool *pool;
+
+  /* set the main runloop as the runloop for the current thread. This has the
+   * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
+   * runs the main runloop.
+   */
+  _CFRunLoopSetCurrent(CFRunLoopGetMain());
+
+  /* this is needed to make IsMainThread checks in core foundation work from the
+   * current thread
+   */
+  _CFMainPThread = pthread_self();
+
+  pool = [[NSAutoreleasePool alloc] init];
+
+  [NSApplication sharedApplication];
+  [NSApp finishLaunching];
+
+  /* run the loop */
+  run_ns_app_loop ();
+
+  [pool release];
+}
+#endif
+
 @end
 
 @ implementation GstBufferObject