VLCEventManager.m 9.84 KB
Newer Older
1
/*****************************************************************************
2
3
 * VLCEventManager.h: VLC.framework VLCEventManager implementation
 * This is used to communicate in a sound way accross threads.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 *****************************************************************************
 * Copyright (C) 2007 Pierre d'Herbemont
 * Copyright (C) 2007 the VideoLAN team
 * $Id$
 *
 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#import "VLCEventManager.h"
#import <pthread.h>

static VLCEventManager * defaultManager = NULL;

enum message_type_t
{
    VLCNotification,
34
35
    VLCObjectMethodWithObjectArg,
    VLCObjectMethodWithArrayArg
36
37
38
39
40
41
42
43
44
45
46
47
48
};

struct message {
    id target;
    SEL sel;
    union u
    {
        NSString * name;
        id object;
    } u;
    enum message_type_t type;
};

49
@interface VLCEventManager (Private)
50
51
52
53
54
55
56
57
58
59
60
- (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data;
- (void)callObjectMethodWithArgs:(NSData*)data;
- (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName;
- (pthread_cond_t *)signalData;
- (pthread_mutex_t *)queueLock;
- (NSMutableArray *)messageQueue;
@end

static void * EventDispatcherMainLoop(void * user_data)
{
    VLCEventManager * self = user_data;
61
    
62
63
64
65
    for(;;)
    {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        struct message * message, * message_newer = NULL;
66
        NSData * dataMessage;
67
68
        int i;

69
70
        /* Sleep a bit not to flood the interface */
        msleep(300);
71
        /* Wait for some data */
72

73
74
        pthread_mutex_lock( [self queueLock] );
        /* Wait until we have something on the queue */
75
        while( [[self messageQueue] count] <= 0 )
76
        {
77
            pthread_cond_wait( [self signalData], [self queueLock] );
78
        }
79
80
81
82

        //if( [[self messageQueue] count] % 100 == 0 || [[self messageQueue] count] < 100 )
          //  NSLog(@"[EVENT_MANAGER] On the stack we have %d elements", [[self messageQueue] count]);

83
84
85
86
87
88
89
        message = (struct message *)[(NSData *)[[self messageQueue] lastObject] bytes];
        
        /* Don't send the same notification twice */
        if( message->type == VLCNotification )
        {
            for( i = 0; i < [[self messageQueue] count]-1; i++ )
            {
90
                message_newer = (struct message *)[(NSData *)[[self messageQueue] objectAtIndex:i] bytes];
91
92
93
                if( message_newer->type == VLCNotification &&
                    message_newer->target == message->target &&
                   [message_newer->u.name isEqualToString:message->u.name] )
94
95
                {
                    [message_newer->target release];
96
97
                    [message_newer->u.name release];
                    [[self messageQueue] removeObjectAtIndex:i];
98
                    i--;
99
                    continue;
100
101
102
                }
            }
        }
103
104
105
106
107
108
109
110
111
112
113
114
        else if( message->type == VLCObjectMethodWithArrayArg )
        {
            NSMutableArray * newArg = nil;
            /* Collapse messages that takes array arg by sending one bigger array */
            for( i = [[self messageQueue] count]-2; i >= 0; i-- )
            {
                message_newer = (struct message *)[(NSData *)[[self messageQueue] objectAtIndex: i] bytes];
                if( message_newer->type == VLCObjectMethodWithArrayArg &&
                    message_newer->target == message->target && 
                    message_newer->sel == message->sel )
                {
                    if(!newArg)
115
116
                        newArg = [NSMutableArray arrayWithArray:message->u.object];
                    [newArg addObjectsFromArray:message_newer->u.object];
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
                    [message_newer->target release];
                    [message_newer->u.object release];
                    [[self messageQueue] removeObjectAtIndex: i];
                    i++;
                    continue;
                }
                /* It should be a good idea not to collapse event, with other kind of event in-between
                 * Ignore for now only if target is the same */
                else if( message_newer->target == message->target )
                    break;
            }
            if( newArg )
            {
                [message->u.object release];
                message->u.object = [newArg retain];
                [newArg retain];
            }
        }
135
136
137
138

        dataMessage = [[self messageQueue] lastObject];
        
        pthread_mutex_unlock( [self queueLock] );
139

140
        if( message->type == VLCNotification )
141
            [self performSelectorOnMainThread:@selector(callDelegateOfObjectAndSendNotificationWithArgs:) withObject:[dataMessage retain]  /* released in the call */ waitUntilDone: NO];
142
        else
143
144
            [self performSelectorOnMainThread:@selector(callObjectMethodWithArgs:) withObject:[dataMessage retain]  /* released in the call */ waitUntilDone: YES];

145
146
147
148
149
        pthread_mutex_lock( [self queueLock] );
        [[self messageQueue] removeLastObject];
        pthread_mutex_unlock( [self queueLock] );
    
        [pool release];
150
    }
151
    return nil;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
}

@implementation VLCEventManager
+ (id)sharedManager
{
    /* We do want a lock here to avoid leaks */
    if ( !defaultManager )
    {
        defaultManager = [[VLCEventManager alloc] init];
    }

    return defaultManager;
}

166
167
168
169
170
- (void)dummy
{
    /* Put Cocoa in multithreaded mode by calling a dummy function */
}

171
172
173
174
- (id)init
{
    if( self = [super init] )
    {
175
176
177
178
179
180
        if(![NSThread isMultiThreaded])
        {
            [NSThread detachNewThreadSelector:@selector(dummy) toTarget:self withObject:nil];
            NSAssert([NSThread isMultiThreaded], @"Can't put Cocoa in multithreaded mode");
        }

181
182
183
184
185
186
187
188
189
190
191
192
        pthread_mutex_init( &queueLock, NULL );
        pthread_cond_init( &signalData, NULL );
        pthread_create( &dispatcherThread, NULL, EventDispatcherMainLoop, self );
        messageQueue = [[NSMutableArray alloc] initWithCapacity:10];
    }
    return self;
}

- (void)dealloc
{
    pthread_kill( dispatcherThread, SIGKILL );
    pthread_join( dispatcherThread, NULL );
193

194
    [messageQueue release];
195
196
197
198
199
200
    [super dealloc];
}

- (void)callOnMainThreadDelegateOfObject:(id)aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
201
    
202
203
204
205
206
207
208
    struct message message = 
    { 
        [aTarget retain], 
        aSelector, 
        [aNotificationName retain], 
        VLCNotification 
    };
209
210
211
212
213
214
215

    if([NSThread isMainThread])
    {
        [self callDelegateOfObjectAndSendNotificationWithArgs:[[NSData dataWithBytes:&message length:sizeof(struct message)] retain] /* released in the call */];
        return;
    }

216
217
218
219
   // pthread_mutex_lock( [self queueLock] );
   // [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
   // pthread_cond_signal( [self signalData] );
   // pthread_mutex_unlock( [self queueLock] );
220
    
221
222
223
224
225
226
    [pool release];
}

- (void)callOnMainThreadObject:(id)aTarget withMethod:(SEL)aSelector withArgumentAsObject: (id)arg
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
227
    struct message message = 
228
229
230
231
    { 
        [aTarget retain], 
        aSelector, 
        [arg retain], 
232
        [arg isKindOfClass:[NSArray class]] ? VLCObjectMethodWithArrayArg : VLCObjectMethodWithObjectArg 
233
    };
234

235
236
237
238
    pthread_mutex_lock( [self queueLock] );
    [[self messageQueue] insertObject:[NSData dataWithBytes:&message length:sizeof(struct message)] atIndex:0];
    pthread_cond_signal( [self signalData] );
    pthread_mutex_unlock( [self queueLock] );
239
    
240
241
242
243
    [pool release];
}
@end

244
@implementation VLCEventManager (Private)
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
- (void)callDelegateOfObjectAndSendNotificationWithArgs:(NSData*)data
{
    struct message * message = (struct message *)[data bytes];

    [self callDelegateOfObject:message->target withDelegateMethod:message->sel withNotificationName:message->u.name];
    [message->u.name release];
    [message->target release];
    [data release];
}

- (void)callObjectMethodWithArgs:(NSData*)data
{
    struct message * message = (struct message *)[data bytes];
    void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[message->target methodForSelector: message->sel];

    method( message->target, message->sel, message->u.object);
    [message->u.object release];
    [message->target release];
    [data release];
}

- (NSMutableArray *)messageQueue
{
    return messageQueue;
}
270

271
272
273
274
275
276
277
278
279
280
281
282
- (pthread_cond_t *)signalData
{
    return &signalData;
}

- (pthread_mutex_t *)queueLock
{
    return &queueLock;
}

- (void)callDelegateOfObject:(id) aTarget withDelegateMethod:(SEL)aSelector withNotificationName: (NSString *)aNotificationName
{
283
//    [[NSNotificationCenter defaultCenter] postNotification: [NSNotification notificationWithName:aNotificationName object:aTarget]];
284
285
286
287
288
289
290
291

    if (![aTarget delegate] || ![[aTarget delegate] respondsToSelector: aSelector])
        return;

    void (*method)(id, SEL, id) = (void (*)(id, SEL, id))[[aTarget delegate] methodForSelector: aSelector];
    method( [aTarget delegate], aSelector, [NSNotification notificationWithName:aNotificationName object:aTarget]);
}
@end