]> git.sesse.net Git - vlc/blob - src/misc/threads.c
7b68ce72c467e89bb03096ea95f33b1fd147bbf8
[vlc] / src / misc / threads.c
1 /*****************************************************************************
2  * threads.c : threads implementation for the VideoLAN client
3  *****************************************************************************
4  * Copyright (C) 1999-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@netcourrier.com>
10  *          Clément Sténac
11  *          Rémi Denis-Courmont
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33
34 #include "libvlc.h"
35 #include <assert.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39
40 #if defined( LIBVLC_USE_PTHREAD )
41 # include <sched.h>
42 #endif
43
44 struct vlc_thread_boot
45 {
46     void * (*entry) (vlc_object_t *);
47     vlc_object_t *object;
48 };
49
50 static void *thread_entry (void *data)
51 {
52     vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object;
53     void *(*func) (vlc_object_t *) = ((struct vlc_thread_boot *)data)->entry;
54
55     free (data);
56     msg_Dbg (obj, "thread started");
57     func (obj);
58     msg_Dbg (obj, "thread ended");
59
60     return NULL;
61 }
62
63 #undef vlc_thread_create
64 /*****************************************************************************
65  * vlc_thread_create: create a thread
66  *****************************************************************************
67  * Note that i_priority is only taken into account on platforms supporting
68  * userland real-time priority threads.
69  *****************************************************************************/
70 int vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line,
71                        const char *psz_name, void *(*func) ( vlc_object_t * ),
72                        int i_priority )
73 {
74     int i_ret;
75     vlc_object_internals_t *p_priv = vlc_internals( p_this );
76
77     struct vlc_thread_boot *boot = malloc (sizeof (*boot));
78     if (boot == NULL)
79         return errno;
80     boot->entry = func;
81     boot->object = p_this;
82
83     /* Make sure we don't re-create a thread if the object has already one */
84     assert( !p_priv->b_thread );
85
86     p_priv->b_thread = true;
87     i_ret = vlc_clone( &p_priv->thread_id, thread_entry, boot, i_priority );
88     if( i_ret == 0 )
89         msg_Dbg( p_this, "thread (%s) created at priority %d (%s:%d)",
90                  psz_name, i_priority, psz_file, i_line );
91     else
92     {
93         p_priv->b_thread = false;
94         errno = i_ret;
95         msg_Err( p_this, "%s thread could not be created at %s:%d (%m)",
96                          psz_name, psz_file, i_line );
97     }
98
99     return i_ret;
100 }
101
102 /*****************************************************************************
103  * vlc_thread_set_priority: set the priority of the current thread when we
104  * couldn't set it in vlc_thread_create (for instance for the main thread)
105  *****************************************************************************/
106 int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file,
107                                int i_line, int i_priority )
108 {
109     vlc_object_internals_t *p_priv = vlc_internals( p_this );
110
111     if( !p_priv->b_thread )
112     {
113         msg_Err( p_this, "couldn't set priority of non-existent thread" );
114         return ESRCH;
115     }
116
117 #if defined( LIBVLC_USE_PTHREAD )
118 # ifndef __APPLE__
119     if( config_GetInt( p_this, "rt-priority" ) > 0 )
120 # endif
121     {
122         int i_error, i_policy;
123         struct sched_param param;
124
125         memset( &param, 0, sizeof(struct sched_param) );
126         if( config_GetType( p_this, "rt-offset" ) )
127             i_priority += config_GetInt( p_this, "rt-offset" );
128         if( i_priority <= 0 )
129         {
130             param.sched_priority = (-1) * i_priority;
131             i_policy = SCHED_OTHER;
132         }
133         else
134         {
135             param.sched_priority = i_priority;
136             i_policy = SCHED_RR;
137         }
138         if( (i_error = pthread_setschedparam( p_priv->thread_id,
139                                               i_policy, &param )) )
140         {
141             errno = i_error;
142             msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m",
143                       psz_file, i_line );
144             i_priority = 0;
145         }
146     }
147
148 #elif defined( WIN32 ) || defined( UNDER_CE )
149     VLC_UNUSED( psz_file); VLC_UNUSED( i_line );
150
151 #ifndef UNDER_CE
152     if( !SetThreadPriority(p_priv->thread_id, i_priority) )
153 #else
154     if( !SetThreadPriority(p_priv->thread_id->handle, i_priority) )
155 #endif
156     {
157         msg_Warn( p_this, "couldn't set a faster priority" );
158         return 1;
159     }
160
161 #endif
162
163     return 0;
164 }
165
166 /*****************************************************************************
167  * vlc_thread_join: wait until a thread exits, inner version
168  *****************************************************************************/
169 void __vlc_thread_join( vlc_object_t *p_this )
170 {
171     vlc_object_internals_t *p_priv = vlc_internals( p_this );
172
173 #if defined( WIN32 ) && !defined( UNDER_CE )
174     HANDLE hThread;
175     FILETIME create_ft, exit_ft, kernel_ft, user_ft;
176     int64_t real_time, kernel_time, user_time;
177
178     if( ! DuplicateHandle(GetCurrentProcess(),
179             p_priv->thread_id,
180             GetCurrentProcess(),
181             &hThread,
182             0,
183             FALSE,
184             DUPLICATE_SAME_ACCESS) )
185     {
186         p_priv->b_thread = false;
187         return; /* We have a problem! */
188     }
189 #endif
190
191     vlc_join( p_priv->thread_id, NULL );
192
193 #if defined( WIN32 ) && !defined( UNDER_CE )
194     /* FIXME: this could work on WinCE too... except that it seems always to
195      * return 0 for exit_ft and kernel_ft */
196     if( GetThreadTimes( hThread, &create_ft, &exit_ft, &kernel_ft, &user_ft ) )
197     {
198         real_time =
199           ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) -
200           ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime);
201         real_time /= 10;
202
203         kernel_time =
204           ((((int64_t)kernel_ft.dwHighDateTime)<<32)|
205            kernel_ft.dwLowDateTime) / 10;
206
207         user_time =
208           ((((int64_t)user_ft.dwHighDateTime)<<32)|
209            user_ft.dwLowDateTime) / 10;
210
211         msg_Dbg( p_this, "thread times: "
212                  "real %"PRId64"m%fs, kernel %"PRId64"m%fs, user %"PRId64"m%fs",
213                  real_time/60/1000000,
214                  (double)((real_time%(60*1000000))/1000000.0),
215                  kernel_time/60/1000000,
216                  (double)((kernel_time%(60*1000000))/1000000.0),
217                  user_time/60/1000000,
218                  (double)((user_time%(60*1000000))/1000000.0) );
219     }
220     CloseHandle( hThread );
221 #endif
222
223     p_priv->b_thread = false;
224 }
225
226 void vlc_thread_cancel (vlc_object_t *obj)
227 {
228     vlc_object_internals_t *priv = vlc_internals (obj);
229
230     if (priv->b_thread)
231         vlc_cancel (priv->thread_id);
232 }