]> git.sesse.net Git - vlc/blob - modules/lua/extension_thread.c
Lua: remove lock_and_wait
[vlc] / modules / 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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 /* I don't want to include lua headers here */
29 typedef struct lua_State lua_State;
30
31 #include "extension.h"
32 #include "assert.h"
33
34 struct thread_sys_t
35 {
36     extensions_manager_t *p_mgr;
37     extension_t *p_ext;
38 };
39
40 /** Thread Run */
41 static void* Run( void *data );
42 static void FreeCommands( struct command_t *command );
43 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext );
44
45 /**
46  * Activate an extension
47  * @param p_mgr This manager
48  * @param p_ext Extension to activate
49  * @return The usual VLC return codes
50  **/
51 int Activate( extensions_manager_t *p_mgr, extension_t *p_ext )
52 {
53     assert( p_ext != NULL );
54
55     struct extension_sys_t *p_sys = p_ext->p_sys;
56     assert( p_sys != NULL );
57
58     msg_Dbg( p_mgr, "Activating extension '%s'", p_ext->psz_title );
59
60     if( IsActivated( p_mgr, p_ext ) )
61     {
62         msg_Warn( p_mgr, "Extension is already activated!" );
63         return VLC_EGENERIC;
64     }
65
66     /* Add this script to the activated extensions list */
67     vlc_mutex_lock( &p_mgr->p_sys->lock );
68     ARRAY_APPEND( p_mgr->p_sys->activated_extensions, p_ext );
69     vlc_mutex_unlock( &p_mgr->p_sys->lock );
70
71     /* Prepare first command */
72     p_sys->command = calloc( 1, sizeof( struct command_t ) );
73     if( !p_sys->command )
74         return VLC_ENOMEM;
75     p_sys->command->i_command = CMD_ACTIVATE; /* No params */
76
77     /* Start thread */
78     p_sys->b_exiting = false;
79
80     if( vlc_clone( &p_sys->thread, Run, p_ext, VLC_THREAD_PRIORITY_LOW )
81         != VLC_SUCCESS )
82     {
83         p_sys->b_exiting = true;
84         // Note: Automatically deactivating the extension...
85         Deactivate( p_mgr, p_ext );
86         return VLC_ENOMEM;
87     }
88
89     return VLC_SUCCESS;
90 }
91
92 /** Look for an extension in the activated extensions list
93  * @todo FIXME Should be entered with the lock held
94  **/
95 bool IsActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
96 {
97     assert( p_ext != NULL );
98     vlc_mutex_lock( &p_mgr->p_sys->lock );
99
100     extension_t *p_iter;
101     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
102     {
103         if( !p_iter )
104             break;
105         assert( p_iter->psz_name != NULL );
106         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
107         {
108             vlc_mutex_unlock( &p_mgr->p_sys->lock );
109             return true;
110         }
111     }
112     FOREACH_END()
113
114     vlc_mutex_unlock( &p_mgr->p_sys->lock );
115     return false;
116 }
117
118 /** Recursively drop and free commands starting from "command" */
119 static void FreeCommands( struct command_t *command )
120 {
121     if( !command ) return;
122     struct command_t *next = command->next;
123     switch( command->i_command )
124     {
125         case CMD_ACTIVATE:
126         case CMD_DEACTIVATE:
127         case CMD_CLICK: // Arg1 must not be freed
128             break;
129
130         case CMD_TRIGGERMENU:
131         case CMD_PLAYING_CHANGED:
132             free( command->data[0] ); // Arg1 is int*, to free
133             break;
134
135         default:
136             break;
137     }
138     free( command );
139     FreeCommands( next );
140 }
141
142 /** Deactivate this extension: pushes immediate command and drops queued */
143 int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
144 {
145     (void) p_mgr;
146     vlc_mutex_lock( &p_ext->p_sys->command_lock );
147
148     if( p_ext->p_sys->b_exiting )
149     {
150         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
151         return VLC_EGENERIC;
152     }
153
154     if( p_ext->p_sys->progress )
155     {
156         // Extension is stuck, kill it now
157         dialog_ProgressDestroy( p_ext->p_sys->progress );
158         p_ext->p_sys->progress = NULL;
159         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
160         KillExtension( p_mgr, p_ext );
161         return VLC_SUCCESS;
162     }
163
164     /* Free the list of commands */
165     if( p_ext->p_sys->command )
166         FreeCommands( p_ext->p_sys->command->next );
167
168     /* Push command */
169     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
170     cmd->i_command = CMD_DEACTIVATE;
171     if( p_ext->p_sys->command )
172         p_ext->p_sys->command->next = cmd;
173     else
174         p_ext->p_sys->command = cmd;
175
176     vlc_cond_signal( &p_ext->p_sys->wait );
177     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
178
179     return VLC_SUCCESS;
180 }
181
182 /** Remove an extension from the activated list */
183 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
184 {
185     if( p_mgr->p_sys->b_killed )
186         return VLC_SUCCESS;
187     vlc_mutex_lock( &p_mgr->p_sys->lock );
188
189     int i_idx = -1;
190     extension_t *p_iter;
191     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
192     {
193         i_idx++;
194         if( !p_iter )
195         {
196             i_idx = -1;
197             break;
198         }
199         assert( p_iter->psz_name != NULL );
200         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
201             break;
202     }
203     FOREACH_END()
204
205     if( i_idx >= 0 )
206     {
207         ARRAY_REMOVE( p_mgr->p_sys->activated_extensions, i_idx );
208     }
209     else
210     {
211         msg_Dbg( p_mgr, "Can't find extension '%s' in the activated list",
212                  p_ext->psz_title );
213     }
214
215     vlc_mutex_unlock( &p_mgr->p_sys->lock );
216     return (i_idx >= 0) ? VLC_SUCCESS : VLC_EGENERIC;
217 }
218
219 void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext )
220 {
221     /* Cancel thread if it seems stuck for a while */
222     msg_Dbg( p_mgr, "Killing extension now" );
223     vlc_cancel( p_ext->p_sys->thread );
224     lua_ExtensionDeactivate( p_mgr, p_ext );
225     p_ext->p_sys->b_exiting = true;
226     RemoveActivated( p_mgr, p_ext );
227 }
228
229 /** Push a UI command */
230 int __PushCommand( extension_t *p_ext,  bool b_unique, command_type_e i_command,
231                    va_list args )
232 {
233     vlc_mutex_lock( &p_ext->p_sys->command_lock );
234
235     /* Create command */
236     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
237     cmd->i_command = i_command;
238     switch( i_command )
239     {
240         case CMD_CLICK:
241             cmd->data[0] = va_arg( args, void* );
242             break;
243         case CMD_TRIGGERMENU:
244             {
245                 int *pi = malloc( sizeof( int ) );
246                 if( !pi )
247                 {
248                     free( cmd );
249                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
250                     return VLC_ENOMEM;
251                 }
252                 *pi = va_arg( args, int );
253                 cmd->data[0] = pi;
254             }
255             break;
256         case CMD_PLAYING_CHANGED:
257             {
258                 int *pi = malloc( sizeof( int ) );
259                 if( !pi )
260                 {
261                     free( cmd );
262                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
263                     return VLC_ENOMEM;
264                 }
265                 *pi = va_arg( args, int );
266                 cmd->data[0] = pi;
267             }
268             break;
269         case CMD_CLOSE:
270         case CMD_SET_INPUT:
271         case CMD_UPDATE_META:
272             // Nothing to do here
273             break;
274         default:
275             msg_Dbg( p_ext->p_sys->p_mgr,
276                      "Unknown command send to extension: %d", i_command );
277             break;
278     }
279
280     /* Push command to the end of the queue */
281     struct command_t *last = p_ext->p_sys->command;
282     if( !last )
283     {
284         p_ext->p_sys->command = cmd;
285     }
286     else
287     {
288         bool b_skip = false;
289         while( last->next != NULL )
290         {
291             if( b_unique && last->i_command == i_command )
292             {
293                 // Do not push this 'unique' command a second time
294                 b_skip = !memcmp( last->data, cmd->data, sizeof( cmd->data ) );
295                 break;
296             }
297             else
298             {
299                 last = last->next;
300             }
301         }
302         if( !b_skip )
303         {
304             last->next = cmd;
305         }
306         else
307         {
308             FreeCommands( cmd );
309         }
310     }
311
312     vlc_cond_signal( &p_ext->p_sys->wait );
313     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
314     return VLC_SUCCESS;
315 }
316
317 /* Thread loop */
318 static void* Run( void *data )
319 {
320     extension_t *p_ext = data;
321     extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
322
323     int cancel = vlc_savecancel();
324     vlc_mutex_lock( &p_ext->p_sys->command_lock );
325
326     while( !p_ext->p_sys->b_exiting )
327     {
328         /* Pop command in front */
329         struct command_t *cmd = p_ext->p_sys->command;
330         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
331         vlc_restorecancel( cancel );
332
333         /* Run command */
334         if( cmd )
335         {
336             if( LockExtension( p_ext ) )
337             {
338                 mutex_cleanup_push( &p_ext->p_sys->running_lock );
339                 switch( cmd->i_command )
340                 {
341                     case CMD_ACTIVATE:
342                     {
343                         if( lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ) < 0 )
344                         {
345                             msg_Err( p_mgr, "Could not activate extension!" );
346                             Deactivate( p_mgr, p_ext );
347                             cmd = NULL;
348                         }
349                         break;
350                     }
351
352                     case CMD_DEACTIVATE:
353                     {
354                         msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
355                         if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
356                         {
357                             msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
358                                       p_ext->psz_title );
359                         }
360                         p_ext->p_sys->b_exiting = true;
361                         RemoveActivated( p_mgr, p_ext );
362                         break;
363                     }
364
365                     case CMD_CLOSE:
366                     {
367                         lua_ExecuteFunction( p_mgr, p_ext, "close", LUA_END );
368                         break;
369                     }
370
371                     case CMD_CLICK:
372                     {
373                         extension_widget_t *p_widget = cmd->data[0];
374                         assert( p_widget );
375                         msg_Dbg( p_mgr, "Clicking '%s': '%s'",
376                                  p_ext->psz_name, p_widget->psz_text );
377                         if( !lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget )
378                             < 0 )
379                         {
380                             msg_Warn( p_mgr, "Could not translate click" );
381                         }
382                         break;
383                     }
384
385                     case CMD_TRIGGERMENU:
386                     {
387                         int *pi_id = cmd->data[0];
388                         assert( pi_id );
389                         msg_Dbg( p_mgr, "Trigger menu %d of '%s'",
390                                  *pi_id, p_ext->psz_name );
391                         lua_ExtensionTriggerMenu( p_mgr, p_ext, *pi_id );
392                         break;
393                     }
394
395                     case CMD_SET_INPUT:
396                     {
397                         lua_ExecuteFunction( p_mgr, p_ext, "input_changed", LUA_END );
398                         break;
399                     }
400
401                     case CMD_UPDATE_META:
402                     {
403                         lua_ExecuteFunction( p_mgr, p_ext, "meta_changed", LUA_END );
404                         break;
405                     }
406
407                     case CMD_PLAYING_CHANGED:
408                     {
409                         lua_ExecuteFunction( p_mgr, p_ext, "playing_changed",
410                                 LUA_NUM, *((int *)cmd->data[0]), LUA_END );
411                         break;
412                     }
413
414                     default:
415                     {
416                         msg_Dbg( p_mgr, "Unknown command in extension command queue: %d",
417                                  cmd->i_command );
418                         break;
419                     }
420                 }
421                 vlc_cleanup_pop();
422                 UnlockExtension( p_ext );
423             }
424         }
425
426         cancel = vlc_savecancel();
427         vlc_mutex_lock( &p_ext->p_sys->command_lock );
428         if( p_ext->p_sys->command )
429         {
430             cmd = p_ext->p_sys->command;
431             p_ext->p_sys->command = cmd->next;
432             cmd->next = NULL; // This prevents FreeCommands from freeing next
433             FreeCommands( cmd );
434         }
435
436         if( !p_ext->p_sys->b_exiting && !p_ext->p_sys->command )
437         {
438             vlc_cond_wait( &p_ext->p_sys->wait, &p_ext->p_sys->command_lock );
439         }
440     }
441
442     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
443     msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
444     vlc_restorecancel( cancel );
445
446     // Note: At this point, the extension should be deactivated
447     return NULL;
448 }