1 /*****************************************************************************
2 * VLCEventManager.h: VLC.framework VLCEventManager implementation
3 * This is used to communicate in a sound way accross threads.
4 *****************************************************************************
5 * Copyright (C) 2007 Pierre d'Herbemont
6 * Copyright (C) 2007 the VideoLAN team
9 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 #import "VLCEventManager.h"
30 * Defines the type of interthread message on the queue.
34 VLCNotification, //< Standard NSNotification.
35 VLCObjectMethodWithObjectArg, //< Method with an object argument.
36 VLCObjectMethodWithArrayArg //< Method with an array argument.
40 * Data structured used to enqueue messages onto the queue.
43 id target; //< Target object that should receive the message (retained until method is called).
44 SEL sel; //< A selector that identifies the message to be sent to the target.
45 union u //< Object could either be a VLCNotification or other.
47 NSString * name; //< Name to be used for NSNotification
48 id object; //< Object argument to pass to the target via the selector.
50 message_type_t type; //< Type of queued message.
53 @interface VLCEventManager (Private)
54 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data;
55 - (void)callObjectMethodWithArgs:(NSData*)data;
56 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName:(NSString *)aNotificationName;
57 - (pthread_cond_t *)signalData;
58 - (pthread_mutex_t *)queueLock;
59 - (NSMutableArray *)messageQueue;
63 * Provides a function for the main entry point for the dispatch thread. It dispatches any messages that is queued.
64 * \param user_data Pointer to the VLCEventManager instance that instiated this thread.
66 static void * EventDispatcherMainLoop(void * user_data)
68 VLCEventManager * self = user_data;
72 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
73 message_t * message, * message_newer = NULL;
77 /* Sleep a bit not to flood the interface */
80 /* Wait for some data */
82 pthread_mutex_lock( [self queueLock] );
83 /* Wait until we have something on the queue */
84 while( [[self messageQueue] count] <= 0 )
86 pthread_cond_wait( [self signalData], [self queueLock] );
89 //if( [[self messageQueue] count] % 100 == 0 || [[self messageQueue] count] < 100 )
90 // NSLog(@"[EVENT_MANAGER] On the stack we have %d elements", [[self messageQueue] count]);
92 /* Get the first object off the queue. */
93 dataMessage = [[[self messageQueue] lastObject] retain]; // Released in 'call'
94 [[self messageQueue] removeLastObject];
95 message = (message_t *)[dataMessage bytes];
97 /* Remove duplicate notifications. */
98 if( message->type == VLCNotification )
100 for( i = [[self messageQueue] count]-1; i >= 0; i-- )
102 message_newer = (message_t *)[(NSData *)[[self messageQueue] objectAtIndex:i] bytes];
103 if( message_newer->type == VLCNotification &&
104 message_newer->target == message->target &&
105 [message_newer->u.name isEqualToString:message->u.name] )
107 [message_newer->target release];
108 [message_newer->u.name release];
109 [[self messageQueue] removeObjectAtIndex:i];
113 else if( message->type == VLCObjectMethodWithArrayArg )
115 NSMutableArray * newArg = nil;
117 /* Collapse messages that takes array arg by sending one bigger array */
118 for( i = [[self messageQueue] count]-1; i >= 0; i-- )
120 message_newer = (message_t *)[(NSData *)[[self messageQueue] objectAtIndex: i] bytes];
121 if( message_newer->type == VLCObjectMethodWithArrayArg &&
122 message_newer->target == message->target &&
123 message_newer->sel == message->sel )
127 newArg = [NSMutableArray arrayWithArray:message->u.object];
128 [message->u.object release];
131 [newArg addObjectsFromArray:message_newer->u.object];
132 [message_newer->target release];
133 [message_newer->u.object release];
134 [[self messageQueue] removeObjectAtIndex:i];
136 /* It should be a good idea not to collapse event, with other kind of event in-between
137 * Ignore for now only if target is the same */
138 else if( message_newer->target != message->target )
143 message->u.object = [newArg retain];
146 pthread_mutex_unlock( [self queueLock] );
148 if( message->type == VLCNotification )
149 [self performSelectorOnMainThread:@selector(callDelegateOfObjectAndSendNotificationWithArgs:)
150 withObject:dataMessage
153 [self performSelectorOnMainThread:@selector(callObjectMethodWithArgs:)
154 withObject:dataMessage
162 @implementation VLCEventManager
165 static VLCEventManager * defaultManager = NULL;
167 /* We do want a lock here to avoid leaks */
168 if ( !defaultManager )
170 defaultManager = [[VLCEventManager alloc] init];
173 return defaultManager;
178 /* Put Cocoa in multithreaded mode by calling a dummy function */
183 if( self = [super init] )
185 if(![NSThread isMultiThreaded])
187 [NSThread detachNewThreadSelector:@selector(dummy) toTarget:self withObject:nil];
188 NSAssert([NSThread isMultiThreaded], @"Can't put Cocoa in multithreaded mode");
191 pthread_mutex_init( &queueLock, NULL );
192 pthread_cond_init( &signalData, NULL );
193 pthread_create( &dispatcherThread, NULL, EventDispatcherMainLoop, self );
194 messageQueue = [[NSMutableArray alloc] initWithCapacity:10];
201 pthread_kill( dispatcherThread, SIGKILL );
202 pthread_join( dispatcherThread, NULL );
204 [messageQueue release];
208 - (void)callOnMainThreadDelegateOfObject:(id)aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
210 // NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
216 [aNotificationName retain],
220 // if( [NSThread isMainThread] )
222 [self callDelegateOfObjectAndSendNotificationWithArgs:[[NSData dataWithBytes:&message length:sizeof(message_t)] retain] /* released in the call */];
226 // pthread_mutex_lock( [self queueLock] );
227 // [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(message_t)] atIndex:0];
228 // pthread_cond_signal( [self signalData] );
229 // pthread_mutex_unlock( [self queueLock] );
235 - (void)callOnMainThreadObject:(id)aTarget withMethod:(SEL)aSelector withArgumentAsObject: (id)arg
237 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
243 [arg isKindOfClass:[NSArray class]] ? VLCObjectMethodWithArrayArg : VLCObjectMethodWithObjectArg
246 pthread_mutex_lock( [self queueLock] );
247 [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(message_t)] atIndex:0];
248 pthread_cond_signal( [self signalData] );
249 pthread_mutex_unlock( [self queueLock] );
255 @implementation VLCEventManager (Private)
256 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data
258 message_t * message = (message_t *)[data bytes];
260 [self callDelegateOfObject:message->target withDelegateMethod:message->sel withNotificationName:message->u.name];
261 [message->u.name release];
262 [message->target release];
266 - (void)callObjectMethodWithArgs:(NSData*)data
268 message_t * message = (message_t *)[data bytes];
269 void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[message->target methodForSelector: message->sel];
271 method( message->target, message->sel, message->u.object);
272 [message->u.object release];
273 [message->target release];
277 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
279 // [[NSNotificationCenter defaultCenter] postNotification: [NSNotification notificationWithName:aNotificationName object:aTarget]];
281 if (![aTarget delegate] || ![[aTarget delegate] respondsToSelector:aSelector])
284 void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[[aTarget delegate] methodForSelector: aSelector];
285 method( [aTarget delegate], aSelector, [NSNotification notificationWithName:aNotificationName object:aTarget]);
288 - (NSMutableArray *)messageQueue
293 - (pthread_cond_t *)signalData
298 - (pthread_mutex_t *)queueLock