[vlc-devel] [PATCH] macosx: call input_changed in extensions
Josh Watzman
jwatzman at jwatzman.org
Mon Dec 31 04:52:53 CET 2012
This is obnoxiously complicated -- see comments in code. If anyone cares about
playing_changed or meta_changed, something similar will probably have to be
done.
---
modules/gui/macosx/intf.h | 2 +-
modules/gui/macosx/intf.m | 73 +++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 71 insertions(+), 4 deletions(-)
diff --git a/modules/gui/macosx/intf.h b/modules/gui/macosx/intf.h
index cf9edde..5fa2490 100644
--- a/modules/gui/macosx/intf.h
+++ b/modules/gui/macosx/intf.h
@@ -83,7 +83,7 @@ struct intf_sys_t
@interface VLCMain : NSObject <NSWindowDelegate, NSApplicationDelegate>
{
intf_thread_t *p_intf; /* The main intf object */
- input_thread_t *p_current_input;
+ input_thread_t *p_current_input, *p_input_changed;
id o_mainmenu; /* VLCMainMenu */
id o_prefs; /* VLCPrefs */
id o_sprefs; /* VLCSimplePrefs */
diff --git a/modules/gui/macosx/intf.m b/modules/gui/macosx/intf.m
index fbfd1dd..b917076 100644
--- a/modules/gui/macosx/intf.m
+++ b/modules/gui/macosx/intf.m
@@ -62,6 +62,7 @@
#import "CoreInteraction.h"
#import "TrackSynchronization.h"
#import "VLCVoutWindowController.h"
+#import "ExtensionsManager.h"
#import <AddressBook/AddressBook.h> /* for crashlog send mechanism */
#import <Sparkle/Sparkle.h> /* we're the update delegate */
@@ -249,6 +250,7 @@ void WindowClose(vout_window_t *p_wnd)
* Run: main loop
*****************************************************************************/
static NSLock * o_appLock = nil; // controls access to f_appExit
+static NSLock * o_plItemChangedLock = nil;
static void Run(intf_thread_t *p_intf)
{
@@ -256,12 +258,14 @@ static void Run(intf_thread_t *p_intf)
[VLCApplication sharedApplication];
o_appLock = [[NSLock alloc] init];
+ o_plItemChangedLock = [[NSLock alloc] init];
[[VLCMain sharedInstance] setIntf: p_intf];
[NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
[NSApp run];
[[VLCMain sharedInstance] applicationWillTerminate:nil];
+ [o_plItemChangedLock release];
[o_appLock release];
[o_pool release];
@@ -378,7 +382,60 @@ static int PLItemChanged(vlc_object_t *p_this, const char *psz_var,
vlc_value_t oldval, vlc_value_t new_val, void *param)
{
NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
- [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:NO];
+
+ // This is a pretty bizarre two-step system to inform the extension manager
+ // that the input has changed, but it's necessary to avoid a series of
+ // possible deadlocks and other issues. Here are other possible approaches
+ // that don't work:
+ //
+ // - Just call into the extension manager in -PlaylistItemChanged on the
+ // main thread. This can pretty easily cause a deadlock if we call
+ // -PlaylistItemChanged twice in quick succession. The first call will poke
+ // the condvar the extension is waiting on, causing the extension thread to
+ // wake up and run extension code; many parts of it -- including the dialog
+ // code -- must be run on the main thread. The extension thread goes back to
+ // sleep while blocking on the main thread to become available, while
+ // holding the extension lock. Meanwhile the main thread goes into the
+ // second call of -PlaylistItemChanged, attempts to lock the extension, and
+ // that's a deadlock.
+ //
+ // - Restructure the dialog manager to never block on the main thread while
+ // holding the extension lock. This should work, but as it turns out doesn't
+ // because the main thread will attempt to lock the same lock twice. What
+ // happens is that -performSelectorOnMainThread works by injecting an event
+ // into the main event loop of the main thread. For some unknown reason, as
+ // part of its processing, when creating an NSAttributedString with HTML, it
+ // runs the main event loop, which means we can end up executing one
+ // -performSelectoOnMainThread as part of another. Since the dialog manager
+ // uses attributed strings with HTML (since dialogs are HTML), we deadlock
+ // here too. This seems strictly like a flaw in NSAttributedString and/or
+ // in -performSelectorOnMainThread and is documented elsewhere:
+ // http://mrrsoftware.com/blog/tag/nsattributedstring/
+ // https://www.bluestatic.org/blog/2010/05/31/nsattributedstring-spins-a-nested-run-loop/
+ //
+ // - Change around this bit of code to not force it to run on the main
+ // thread. This would probably work, but, as a newcomer to VLC, I don't
+ // quite know the implications of doing this, particularly since a lot of
+ // code here seems to serailize on the main thread as a way of thread
+ // safety; it would likely require some somewhat intricate restructuring
+ // and adding of locks.
+ //
+ // - Let the extension manager deal with listening for events the same way
+ // that we do here. That would work, but would require duplicating a
+ // nontrivial amount of code from here to deal with tracking the current
+ // input.
+ //
+ // - So, instead, we just serialized all calls to -PlaylistItemChanged (so
+ // we make sure to process them in order, with no one trampling
+ // p_input_changed), do most of the work on the main thread as before, and
+ // then actually inform the extension manager out here where we don't block
+ // the main thread. It seems likely that there are other pre-existing
+ // deadlock possibilities here -- the main thread can't lock an extension!
+ // -- but it at least tends to work in my testing.
+ [o_plItemChangedLock lock];
+ [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:YES];
+ [[VLCMain sharedInstance] informInputChanged];
+ [o_plItemChangedLock unlock];
[o_pool release];
return VLC_SUCCESS;
@@ -579,7 +636,7 @@ static VLCMain *_o_sharedMainInstance = nil;
_o_sharedMainInstance = [super init];
p_intf = NULL;
- p_current_input = NULL;
+ p_current_input = p_input_changed = NULL;
o_msg_lock = [[NSLock alloc] init];
o_msg_arr = [[NSMutableArray arrayWithCapacity: 600] retain];
@@ -1274,7 +1331,7 @@ static VLCMain *_o_sharedMainInstance = nil;
{
if (p_current_input && (p_current_input->b_dead || !vlc_object_alive(p_current_input))) {
var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
- vlc_object_release(p_current_input);
+ p_input_changed = p_current_input;
p_current_input = NULL;
[o_mainmenu setRateControlsEnabled: NO];
@@ -1288,6 +1345,7 @@ static VLCMain *_o_sharedMainInstance = nil;
[o_mainmenu setRateControlsEnabled: YES];
if ([self activeVideoPlayback] && [[o_mainwindow videoView] isHidden])
[o_mainwindow performSelectorOnMainThread:@selector(togglePlaylist:) withObject: nil waitUntilDone:NO];
+ p_input_changed = vlc_object_hold(p_current_input);
}
}
@@ -1297,6 +1355,15 @@ static VLCMain *_o_sharedMainInstance = nil;
[self updateMainMenu];
}
+- (void)informInputChanged
+{
+ if (p_input_changed) {
+ [[ExtensionsManager getInstance:p_intf] inputChanged:p_input_changed];
+ vlc_object_release(p_input_changed);
+ p_input_changed = NULL;
+ }
+}
+
- (void)updateMainMenu
{
[o_mainmenu setupMenus];
--
1.7.10.2 (Apple Git-33)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 896 bytes
Desc: OpenPGP digital signature
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20121230/7574b0f9/attachment.sig>
More information about the vlc-devel
mailing list