]> git.sesse.net Git - vlc/blob - modules/misc/lua/extension_thread.c
Lua: fix a crash (the command is already freeed).
[vlc] / modules / misc / lua / extension_thread.c
1 /*****************************************************************************
2  * extension_thread.c: Extensions Manager, Threads manager (no Lua here)
3  *****************************************************************************
4  * Copyright (C) 2009-2010 VideoLAN and authors
5  * $Id$
6  *
7  * Authors: Jean-Philippe AndrĂ© < jpeg # videolan.org >
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /* I don't want to include lua headers here */
25 typedef struct lua_State lua_State;
26
27 #include "extension.h"
28 #include "assert.h"
29
30 struct thread_sys_t
31 {
32     extensions_manager_t *p_mgr;
33     extension_t *p_ext;
34 };
35
36 /** Thread Run */
37 static void* Run( void *data );
38 static void FreeCommands( struct command_t *command );
39 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext );
40
41 /**
42  * Activate an extension
43  * @param p_mgr This manager
44  * @param p_ext Extension to activate
45  * @return The usual VLC return codes
46  **/
47 int Activate( extensions_manager_t *p_mgr, extension_t *p_ext )
48 {
49     assert( p_ext != NULL );
50
51     struct extension_sys_t *p_sys = p_ext->p_sys;
52     assert( p_sys != NULL );
53
54     msg_Dbg( p_mgr, "Activating extension '%s'", p_ext->psz_title );
55
56     if( IsActivated( p_mgr, p_ext ) )
57     {
58         msg_Warn( p_mgr, "Extension is already activated!" );
59         return VLC_EGENERIC;
60     }
61
62     /* Add this script to the activated extensions list */
63     vlc_mutex_lock( &p_mgr->p_sys->lock );
64     ARRAY_APPEND( p_mgr->p_sys->activated_extensions, p_ext );
65     vlc_mutex_unlock( &p_mgr->p_sys->lock );
66
67     /* Prepare first command */
68     p_sys->command = calloc( 1, sizeof( struct command_t ) );
69     if( !p_sys->command )
70         return VLC_ENOMEM;
71     p_sys->command->i_command = CMD_ACTIVATE; /* No params */
72
73     /* Start thread */
74     p_sys->b_exiting = false;
75
76     if( vlc_clone( &p_sys->thread, Run, p_ext, VLC_THREAD_PRIORITY_LOW )
77         != VLC_SUCCESS )
78     {
79         p_sys->b_exiting = true;
80         // Note: Automatically deactivating the extension...
81         Deactivate( p_mgr, p_ext );
82         return VLC_ENOMEM;
83     }
84
85     return VLC_SUCCESS;
86 }
87
88 /** Look for an extension in the activated extensions list */
89 bool IsActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
90 {
91     assert( p_ext != NULL );
92     vlc_mutex_lock( &p_mgr->p_sys->lock );
93
94     extension_t *p_iter;
95     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
96     {
97         if( !p_iter )
98             break;
99         assert( p_iter->psz_name != NULL );
100         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
101         {
102             vlc_mutex_unlock( &p_mgr->p_sys->lock );
103             return true;
104         }
105     }
106     FOREACH_END()
107
108     vlc_mutex_unlock( &p_mgr->p_sys->lock );
109     return false;
110 }
111
112 /** Recursively drop and free commands starting from "command" */
113 static void FreeCommands( struct command_t *command )
114 {
115     if( !command ) return;
116     struct command_t *next = command->next;
117     switch( command->i_command )
118     {
119         case CMD_ACTIVATE:
120         case CMD_DEACTIVATE:
121         case CMD_CLICK: // Arg1 must not be freed
122             break;
123
124         case CMD_TRIGGERMENU:
125         case CMD_PLAYING_CHANGED:
126             free( command->data[0] ); // Arg1 is int*, to free
127             break;
128
129         default:
130             break;
131     }
132     free( command );
133     FreeCommands( next );
134 }
135
136 /** Deactivate this extension: pushes immediate command and drops queued */
137 int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
138 {
139     (void) p_mgr;
140     vlc_mutex_lock( &p_ext->p_sys->command_lock );
141
142     if( p_ext->p_sys->b_exiting )
143     {
144         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
145         return VLC_EGENERIC;
146     }
147
148     /* Free the list of commands */
149     FreeCommands( p_ext->p_sys->command );
150
151     /* Push command */
152     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
153     cmd->i_command = CMD_DEACTIVATE;
154     p_ext->p_sys->command = cmd;
155
156     vlc_cond_signal( &p_ext->p_sys->wait );
157     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
158
159     return VLC_SUCCESS;
160 }
161
162 /** Remove an extension from the activated list */
163 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
164 {
165     if( p_mgr->p_sys->b_killed )
166         return VLC_SUCCESS;
167     vlc_mutex_lock( &p_mgr->p_sys->lock );
168
169     int i_idx = -1;
170     extension_t *p_iter;
171     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
172     {
173         i_idx++;
174         if( !p_iter )
175         {
176             i_idx = -1;
177             break;
178         }
179         assert( p_iter->psz_name != NULL );
180         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
181             break;
182     }
183     FOREACH_END()
184
185     if( i_idx >= 0 )
186     {
187         ARRAY_REMOVE( p_mgr->p_sys->activated_extensions, i_idx );
188     }
189     else
190     {
191         msg_Dbg( p_mgr, "Can't find extension '%s' in the activated list",
192                  p_ext->psz_title );
193     }
194
195     vlc_mutex_unlock( &p_mgr->p_sys->lock );
196     return (i_idx >= 0) ? VLC_SUCCESS : VLC_EGENERIC;
197 }
198
199 /** Wait for an extension to finish */
200 void WaitForDeactivation( extension_t *p_ext )
201 {
202     vlc_join( p_ext->p_sys->thread, NULL );
203 }
204
205 /** Push a UI command */
206 int __PushCommand( extension_t *p_ext,  bool b_unique, int i_command,
207                    va_list args )
208 {
209     vlc_mutex_lock( &p_ext->p_sys->command_lock );
210
211     /* Create command */
212     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
213     cmd->i_command = i_command;
214     switch( i_command )
215     {
216         case CMD_CLICK:
217             cmd->data[0] = va_arg( args, void* );
218             break;
219         case CMD_TRIGGERMENU:
220             {
221                 int *pi = malloc( sizeof( int ) );
222                 if( !pi )
223                 {
224                     free( cmd );
225                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
226                     return VLC_ENOMEM;
227                 }
228                 *pi = va_arg( args, int );
229                 cmd->data[0] = pi;
230             }
231             break;
232         case CMD_PLAYING_CHANGED:
233             {
234                 int *pi = malloc( sizeof( int ) );
235                 if( !pi )
236                 {
237                     free( cmd );
238                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
239                     return VLC_ENOMEM;
240                 }
241                 *pi = va_arg( args, int );
242                 cmd->data[0] = pi;
243             }
244             break;
245         case CMD_CLOSE:
246         case CMD_SET_INPUT:
247         case CMD_UPDATE_META:
248             // Nothing to do here
249             break;
250         default:
251             msg_Dbg( p_ext->p_sys->p_mgr,
252                      "Unknown command send to extension: %d", i_command );
253             break;
254     }
255
256     /* Push command to the end of the queue */
257     struct command_t *last = p_ext->p_sys->command;
258     if( !last )
259     {
260         p_ext->p_sys->command = cmd;
261     }
262     else
263     {
264         bool b_skip = false;
265         while( last->next != NULL )
266         {
267             if( b_unique && last->i_command == i_command )
268             {
269                 // Do not push this 'unique' command a second time
270                 b_skip = !memcmp( last->data, cmd->data, sizeof( cmd->data ) );
271                 break;
272             }
273             else
274             {
275                 last = last->next;
276             }
277         }
278         if( !b_skip )
279         {
280             if( !b_unique || ( last->i_command != i_command )
281                 || memcmp( last->data, cmd->data, sizeof( cmd->data ) ) != 0 )
282                 last->next = cmd;
283         }
284     }
285
286     vlc_cond_signal( &p_ext->p_sys->wait );
287     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
288     return VLC_SUCCESS;
289 }
290
291 /* Thread loop */
292 static void* Run( void *data )
293 {
294     extension_t *p_ext = data;
295     extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
296
297     vlc_mutex_lock( &p_ext->p_sys->command_lock );
298
299     while( !p_ext->p_sys->b_exiting )
300     {
301         /* Pop command in front */
302         struct command_t *cmd = p_ext->p_sys->command;
303         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
304
305         /* Run command */
306         if( cmd )
307         {
308             if( LockExtension( p_ext ) )
309             {
310                 switch( cmd->i_command )
311                 {
312                     case CMD_ACTIVATE:
313                     {
314                         if( lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ) < 0 )
315                         {
316                             msg_Dbg( p_mgr, "Could not activate extension!" );
317                             Deactivate( p_mgr, p_ext );
318                             cmd = NULL;
319                         }
320                         break;
321                     }
322
323                     case CMD_DEACTIVATE:
324                     {
325                         msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
326                         if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
327                         {
328                             msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
329                                       p_ext->psz_title );
330                         }
331                         p_ext->p_sys->b_exiting = true;
332                         RemoveActivated( p_mgr, p_ext );
333                         break;
334                     }
335
336                     case CMD_CLOSE:
337                     {
338                         lua_ExecuteFunction( p_mgr, p_ext, "close", LUA_END );
339                         break;
340                     }
341
342                     case CMD_CLICK:
343                     {
344                         extension_widget_t *p_widget = cmd->data[0];
345                         assert( p_widget );
346                         msg_Dbg( p_mgr, "Clicking '%s': '%s'",
347                                  p_ext->psz_name, p_widget->psz_text );
348                         if( !lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget )
349                             < 0 )
350                         {
351                             msg_Warn( p_mgr, "Could not translate click" );
352                         }
353                         break;
354                     }
355
356                     case CMD_TRIGGERMENU:
357                     {
358                         int *pi_id = cmd->data[0];
359                         assert( pi_id );
360                         msg_Dbg( p_mgr, "Trigger menu %d of '%s'",
361                                  *pi_id, p_ext->psz_name );
362                         lua_ExtensionTriggerMenu( p_mgr, p_ext, *pi_id );
363                         break;
364                     }
365
366                     case CMD_SET_INPUT:
367                     {
368                         lua_ExecuteFunction( p_mgr, p_ext, "input_changed", LUA_END );
369                         break;
370                     }
371
372                     case CMD_UPDATE_META:
373                     {
374                         lua_ExecuteFunction( p_mgr, p_ext, "meta_changed", LUA_END );
375                         break;
376                     }
377
378                     case CMD_PLAYING_CHANGED:
379                     {
380                         lua_ExecuteFunction( p_mgr, p_ext, "playing_changed",
381                                 LUA_NUM, *((int *)cmd->data[0]), LUA_END );
382                         break;
383                     }
384
385                     default:
386                     {
387                         msg_Dbg( p_mgr, "Unknown command in extension command queue: %d",
388                                  cmd->i_command );
389                         break;
390                     }
391                 }
392                 UnlockExtension( p_ext );
393             }
394         }
395
396         vlc_mutex_lock( &p_ext->p_sys->command_lock );
397         if( cmd )
398         {
399             p_ext->p_sys->command = cmd->next;
400             cmd->next = NULL; // This prevents FreeCommands from freeing next
401             FreeCommands( cmd );
402         }
403
404         if( !p_ext->p_sys->b_exiting && !p_ext->p_sys->command )
405         {
406             vlc_cond_wait( &p_ext->p_sys->wait, &p_ext->p_sys->command_lock );
407         }
408     }
409
410     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
411     msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
412
413     // Note: At this point, the extension should be deactivated
414     return NULL;
415 }