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"
29 static VLCEventManager * defaultManager = NULL;
34 VLCObjectMethodWithObjectArg,
35 VLCObjectMethodWithArrayArg
46 enum message_type_t type;
49 @interface VLCEventManager (Private)
50 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data;
51 - (void)callObjectMethodWithArgs:(NSData*)data;
52 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName;
53 - (pthread_cond_t *)signalData;
54 - (pthread_mutex_t *)queueLock;
55 - (NSMutableArray *)messageQueue;
58 static void * EventDispatcherMainLoop(void * user_data)
60 VLCEventManager * self = user_data;
64 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
65 struct message * message, * message_newer = NULL;
69 /* Sleep a bit not to flood the interface */
71 /* Wait for some data */
73 pthread_mutex_lock( [self queueLock] );
74 /* Wait until we have something on the queue */
75 while([[self messageQueue] count] <= 0 )
77 pthread_cond_wait( [self signalData], [self queueLock] );
80 //if( [[self messageQueue] count] % 100 == 0 || [[self messageQueue] count] < 100 )
81 // NSLog(@"[EVENT_MANAGER] On the stack we have %d elements", [[self messageQueue] count]);
83 message = (struct message *)[(NSData *)[[self messageQueue] lastObject] bytes];
85 /* Don't send the same notification twice */
86 if( message->type == VLCNotification )
88 for( i = 0; i < [[self messageQueue] count]-1; i++ )
90 message_newer = (struct message *)[(NSData *)[[self messageQueue] objectAtIndex: i] bytes];
91 if( message_newer->type == VLCNotification &&
92 message_newer->target == message->target &&
93 [message_newer->u.name isEqualToString:message->u.name] )
95 [message_newer->target release];
96 [message->u.name release];
97 [[self messageQueue] removeObjectAtIndex: i];
103 else if( message->type == VLCObjectMethodWithArrayArg )
105 NSMutableArray * newArg = nil;
106 /* Collapse messages that takes array arg by sending one bigger array */
107 for( i = [[self messageQueue] count]-2; i >= 0; i-- )
109 message_newer = (struct message *)[(NSData *)[[self messageQueue] objectAtIndex: i] bytes];
110 if( message_newer->type == VLCObjectMethodWithArrayArg &&
111 message_newer->target == message->target &&
112 message_newer->sel == message->sel )
115 newArg = [NSMutableArray arrayWithArray: message->u.object];
116 [newArg addObjectsFromArray: message_newer->u.object];
117 [message_newer->target release];
118 [message_newer->u.object release];
119 [[self messageQueue] removeObjectAtIndex: i];
123 /* It should be a good idea not to collapse event, with other kind of event in-between
124 * Ignore for now only if target is the same */
125 else if( message_newer->target == message->target )
130 [message->u.object release];
131 message->u.object = [newArg retain];
136 dataMessage = [[self messageQueue] lastObject];
138 pthread_mutex_unlock( [self queueLock] );
140 if( message->type == VLCNotification )
141 [self performSelectorOnMainThread:@selector(callDelegateOfObjectAndSendNotificationWithArgs:) withObject:[dataMessage retain] /* released in the call */ waitUntilDone: NO];
143 [self performSelectorOnMainThread:@selector(callObjectMethodWithArgs:) withObject:[dataMessage retain] /* released in the call */ waitUntilDone: YES];
146 pthread_mutex_lock( [self queueLock] );
147 [[self messageQueue] removeLastObject];
148 pthread_mutex_unlock( [self queueLock] );
155 @implementation VLCEventManager
158 /* We do want a lock here to avoid leaks */
159 if ( !defaultManager )
161 defaultManager = [[VLCEventManager alloc] init];
164 return defaultManager;
169 /* Put Cocoa in multithreaded mode by calling a dummy function */
174 if( self = [super init] )
176 if(![NSThread isMultiThreaded])
178 [NSThread detachNewThreadSelector:@selector(dummy) toTarget:self withObject:nil];
179 NSAssert([NSThread isMultiThreaded], @"Can't put Cocoa in multithreaded mode");
182 pthread_mutex_init( &queueLock, NULL );
183 pthread_cond_init( &signalData, NULL );
184 pthread_create( &dispatcherThread, NULL, EventDispatcherMainLoop, self );
185 messageQueue = [[NSMutableArray alloc] initWithCapacity:10];
192 pthread_kill( dispatcherThread, SIGKILL );
193 pthread_join( dispatcherThread, NULL );
195 [messageQueue release];
199 - (void)callOnMainThreadDelegateOfObject:(id)aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
201 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
203 struct message message =
207 [aNotificationName retain],
211 if([NSThread isMainThread])
213 [self callDelegateOfObjectAndSendNotificationWithArgs:[[NSData dataWithBytes:&message length:sizeof(struct message)] retain] /* released in the call */];
217 // pthread_mutex_lock( [self queueLock] );
218 // [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
219 // pthread_cond_signal( [self signalData] );
220 // pthread_mutex_unlock( [self queueLock] );
225 - (void)callOnMainThreadObject:(id)aTarget withMethod:(SEL)aSelector withArgumentAsObject: (id)arg
227 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
228 struct message message =
233 [arg isKindOfClass:[NSArray class]] ? VLCObjectMethodWithArrayArg : VLCObjectMethodWithObjectArg
236 pthread_mutex_lock( [self queueLock] );
237 [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
238 pthread_cond_signal( [self signalData] );
239 pthread_mutex_unlock( [self queueLock] );
245 @implementation VLCEventManager (Private)
246 - (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data
248 struct message * message = (struct message *)[data bytes];
250 [self callDelegateOfObject:message->target withDelegateMethod:message->sel withNotificationName:message->u.name];
251 [message->u.name release];
252 [message->target release];
256 - (void)callObjectMethodWithArgs:(NSData*)data
258 struct message * message = (struct message *)[data bytes];
259 void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[message->target methodForSelector: message->sel];
261 method( message->target, message->sel, message->u.object);
262 [message->u.object release];
263 [message->target release];
267 - (NSMutableArray *)messageQueue
272 - (pthread_cond_t *)signalData
277 - (pthread_mutex_t *)queueLock
282 - (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
284 // [[NSNotificationCenter defaultCenter] postNotification: [NSNotification notificationWithName:aNotificationName object:aTarget]];
286 if (![aTarget delegate] || ![[aTarget delegate] respondsToSelector: aSelector])
289 void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[[aTarget delegate] methodForSelector: aSelector];
290 method( [aTarget delegate], aSelector, [NSNotification notificationWithName:aNotificationName object:aTarget]);