]> git.sesse.net Git - vlc/blob - modules/misc/lua/extension_thread.c
Extensions: fix extensions manager's locking scheme
[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             free( command->data[0] ); // Arg1 is int*, to free
126             break;
127     }
128     free( command );
129     FreeCommands( next );
130 }
131
132 /** Deactivate this extension: pushes immediate command and drops queued */
133 int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
134 {
135     (void) p_mgr;
136     vlc_mutex_lock( &p_ext->p_sys->command_lock );
137
138     if( p_ext->p_sys->b_exiting )
139     {
140         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
141         return VLC_EGENERIC;
142     }
143
144     /* Free the list of commands */
145     FreeCommands( p_ext->p_sys->command );
146
147     /* Push command */
148     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
149     cmd->i_command = CMD_DEACTIVATE;
150     p_ext->p_sys->command = cmd;
151
152     vlc_cond_signal( &p_ext->p_sys->wait );
153     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
154
155     return VLC_SUCCESS;
156 }
157
158 /** Remove an extension from the activated list */
159 static int RemoveActivated( extensions_manager_t *p_mgr, extension_t *p_ext )
160 {
161     if( p_mgr->p_sys->b_killed )
162         return VLC_SUCCESS;
163     vlc_mutex_lock( &p_mgr->p_sys->lock );
164
165     int i_idx = -1;
166     extension_t *p_iter;
167     FOREACH_ARRAY( p_iter, p_mgr->p_sys->activated_extensions )
168     {
169         i_idx++;
170         if( !p_iter )
171         {
172             i_idx = -1;
173             break;
174         }
175         assert( p_iter->psz_name != NULL );
176         if( !strcmp( p_iter->psz_name, p_ext->psz_name ) )
177             break;
178     }
179     FOREACH_END()
180
181     if( i_idx >= 0 )
182     {
183         ARRAY_REMOVE( p_mgr->p_sys->activated_extensions, i_idx );
184     }
185     else
186     {
187         msg_Dbg( p_mgr, "Can't find extension '%s' in the activated list",
188                  p_ext->psz_title );
189     }
190
191     vlc_mutex_unlock( &p_mgr->p_sys->lock );
192     return (i_idx >= 0) ? VLC_SUCCESS : VLC_EGENERIC;
193 }
194
195 /** Wait for an extension to finish */
196 void WaitForDeactivation( extension_t *p_ext )
197 {
198     void *pointer = NULL;
199     vlc_cond_signal( &p_ext->p_sys->wait );
200     vlc_join( p_ext->p_sys->thread, &pointer );
201 }
202
203 /** Push a UI command */
204 int PushCommand( extension_t *p_ext,
205                  int i_command,
206                  ... )
207 {
208     vlc_mutex_lock( &p_ext->p_sys->command_lock );
209
210     va_list args;
211     va_start( args, i_command );
212
213     /* Create command */
214     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
215     cmd->i_command = i_command;
216     switch( i_command )
217     {
218         case CMD_CLICK:
219             cmd->data[0] = va_arg( args, void* );
220             break;
221         case CMD_CLOSE:
222             // Nothing to do here
223             break;
224         case CMD_TRIGGERMENU:
225             {
226                 int *pi = malloc( sizeof( int ) );
227                 if( !pi )
228                 {
229                     free( cmd );
230                     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
231                     return VLC_ENOMEM;
232                 }
233                 *pi = va_arg( args, int );
234                 cmd->data[0] = pi;
235             }
236             break;
237         default:
238             msg_Dbg( p_ext->p_sys->p_mgr,
239                      "Unknown command send to extension: %d", i_command );
240             break;
241     }
242
243     va_end( args );
244
245     /* Push command to the end of the queue */
246     struct command_t *last = p_ext->p_sys->command;
247     if( !last )
248     {
249         p_ext->p_sys->command = cmd;
250     }
251     else
252     {
253         while( last->next != NULL )
254         {
255             last = last->next;
256         }
257         last->next = cmd;
258     }
259
260     vlc_cond_signal( &p_ext->p_sys->wait );
261     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
262     return VLC_SUCCESS;
263 }
264
265 /* Thread loop */
266 static void* Run( void *data )
267 {
268     extension_t *p_ext = data;
269     extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
270
271     vlc_mutex_lock( &p_ext->p_sys->command_lock );
272
273     while( !p_ext->p_sys->b_exiting )
274     {
275         /* Pop command in front */
276         struct command_t *cmd = p_ext->p_sys->command;
277         if( cmd )
278         {
279             p_ext->p_sys->command = cmd->next;
280         }
281
282         vlc_mutex_unlock( &p_ext->p_sys->command_lock );
283
284         /* Run command */
285         if( cmd )
286         {
287             if( LockExtension( p_ext ) )
288             {
289                 switch( cmd->i_command )
290                 {
291                     case CMD_ACTIVATE:
292                     {
293                         if( lua_ExecuteFunction( p_mgr, p_ext, "activate" ) < 0 )
294                         {
295                             msg_Dbg( p_mgr, "Could not activate extension!" );
296                             Deactivate( p_mgr, p_ext );
297                         }
298                         break;
299                     }
300
301                     case CMD_DEACTIVATE:
302                     {
303                         msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
304                         if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
305                         {
306                             msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
307                                       p_ext->psz_title );
308                         }
309                         p_ext->p_sys->b_exiting = true;
310                         RemoveActivated( p_mgr, p_ext );
311                         break;
312                     }
313
314                     case CMD_CLOSE:
315                     {
316                         lua_ExecuteFunction( p_mgr, p_ext, "close" );
317                         break;
318                     }
319
320                     case CMD_CLICK:
321                     {
322                         extension_widget_t *p_widget = cmd->data[0];
323                         assert( p_widget );
324                         msg_Dbg( p_mgr, "Clicking '%s': '%s'",
325                                  p_ext->psz_name, p_widget->psz_text );
326                         if( !lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget )
327                             < 0 )
328                         {
329                             msg_Warn( p_mgr, "Could not translate click" );
330                         }
331                         break;
332                     }
333
334                     case CMD_TRIGGERMENU:
335                     {
336                         int *pi_id = cmd->data[0];
337                         assert( pi_id );
338                         msg_Dbg( p_mgr, "Trigger menu %d of '%s'",
339                                  *pi_id, p_ext->psz_name );
340                         lua_ExtensionTriggerMenu( p_mgr, p_ext, *pi_id );
341                         free( pi_id );
342                         break;
343                     }
344
345                     default:
346                     {
347                         msg_Dbg( p_mgr, "Unknown command in extension command queue: %d",
348                                  cmd->i_command );
349                         break;
350                     }
351                 }
352                 UnlockExtension( p_ext );
353             }
354         }
355
356         vlc_mutex_lock( &p_ext->p_sys->command_lock );
357         if( !p_ext->p_sys->b_exiting && !p_ext->p_sys->command )
358         {
359             vlc_cond_wait( &p_ext->p_sys->wait, &p_ext->p_sys->command_lock );
360         }
361     }
362
363     vlc_mutex_unlock( &p_ext->p_sys->command_lock );
364     msg_Dbg( p_mgr, "Extension thread ending..." );
365
366     // Note: At this point, the extension should be deactivated
367     return NULL;
368 }