Add a surrogate for thread priorities using PID niceness for systems with
[platform/upstream/glib.git] / gthread / gthread-posix.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * gthread.c: posix thread system implementation
5  * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GLib Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
28  */
29
30 /* 
31  * MT safe
32  */
33
34 #include <pthread.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #ifdef HAVE_SYS_TIME_H
38 # include <sys/time.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43
44 #ifdef HAVE_SCHED_H
45 #include <sched.h>
46 #endif
47
48 #ifdef G_THREAD_USE_PID_SURROGATE
49 # include <sys/resource.h>
50 # define PID_IN_THREAD(thread) (*(pid_t*)(((gchar*)thread)+sizeof(pthread_t)))
51 # define SET_PRIO(pid, prio)                                            \
52   posix_check_cmd_prio ((setpriority (PRIO_PROCESS, (pid),              \
53                                g_thread_priority_map [prio]) == -1) ?   \
54                                   (errno == EACCES ? EPERM : errno ): 0)
55 #endif /* G_THREAD_USE_PID_SURROGATE */
56
57 #define posix_check_err(err, name) G_STMT_START{                        \
58   int error = (err);                                                    \
59   if (error)                                                            \
60     g_error ("file %s: line %d (%s): error '%s' during '%s'",           \
61            __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION,                  \
62            g_strerror (error), name);                                   \
63   }G_STMT_END
64
65 #define posix_check_cmd(cmd) posix_check_err (posix_error (cmd), #cmd)
66
67 #ifdef G_ENABLE_DEBUG
68 static gboolean posix_check_cmd_prio_warned = FALSE;
69 # define posix_check_cmd_prio(cmd) G_STMT_START{                        \
70     int err = posix_error (cmd);                                        \
71     if (err == EPERM)                                                   \
72       {                                                                 \
73         if (!posix_check_cmd_prio_warned)                               \
74           {                                                             \
75             posix_check_cmd_prio_warned = TRUE;                         \
76             g_warning ("Priorities can only be changed "                \
77                         "(resp. increased) by root.");                  \
78           }                                                             \
79       }                                                                 \
80     else                                                                \
81       posix_check_err (err, #cmd);                                      \
82      }G_STMT_END
83 #else /* G_ENABLE_DEBUG */
84 # define posix_check_cmd_prio(cmd) G_STMT_START{                        \
85     int err = posix_error (cmd);                                        \
86     if (err != EPERM)                                                   \
87       posix_check_err (err, #cmd);                                      \
88      }G_STMT_END
89 #endif /* G_ENABLE_DEBUG */
90
91 #if defined(G_THREADS_IMPL_POSIX)
92 # define posix_error(what) (what)
93 # define mutexattr_default NULL
94 # define condattr_default NULL
95 #elif defined(G_THREADS_IMPL_DCE)
96 # define posix_error(what) ((what) == -1 ? errno : 0)
97 # define pthread_key_create(a, b) pthread_keycreate (a, b)
98 # define pthread_attr_init(a) pthread_attr_create (a)
99 # define pthread_attr_destroy(a) pthread_attr_delete (a)
100 # define pthread_create(a, b, c, d) pthread_create (a, *b, c, d) 
101 # define mutexattr_default (pthread_mutexattr_default)
102 # define condattr_default (pthread_condattr_default)
103 #else /* neither G_THREADS_IMPL_POSIX nor G_THREADS_IMPL_DCE are defined */
104 # error This should not happen. Contact the GLib team.
105 #endif
106
107 #ifdef G_THREAD_USE_PID_SURROGATE
108 # define PRIORITY_LOW_VALUE 15
109 # define PRIORITY_NORMAL_VALUE 0
110 # define PRIORITY_HIGH_VALUE -15
111 # define PRIORITY_URGENT_VALUE -20
112 #elif defined (POSIX_MIN_PRIORITY) && defined (POSIX_MAX_PRIORITY)
113 # define HAVE_PRIORITIES 1
114 # define PRIORITY_LOW_VALUE POSIX_MIN_PRIORITY
115 # define PRIORITY_URGENT_VALUE POSIX_MAX_PRIORITY
116 #endif /* POSIX_MIN_PRIORITY && POSIX_MAX_PRIORITY */
117
118 gulong g_thread_min_stack_size = 0;
119
120 #define G_MUTEX_SIZE (sizeof (pthread_mutex_t))
121
122 #ifdef _SC_THREAD_STACK_MIN
123 #define HAVE_G_THREAD_IMPL_INIT
124 static void 
125 g_thread_impl_init()
126 {
127   g_thread_min_stack_size = MAX (sysconf (_SC_THREAD_STACK_MIN), 0);
128 }
129 #endif /* _SC_THREAD_STACK_MIN */
130
131 static GMutex *
132 g_mutex_new_posix_impl (void)
133 {
134   GMutex *result = (GMutex *) g_new (pthread_mutex_t, 1);
135   posix_check_cmd (pthread_mutex_init ((pthread_mutex_t *) result, 
136                                        mutexattr_default));
137   return result;
138 }
139
140 static void
141 g_mutex_free_posix_impl (GMutex * mutex)
142 {
143   posix_check_cmd (pthread_mutex_destroy ((pthread_mutex_t *) mutex));
144   g_free (mutex);
145 }
146
147 /* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use
148    functions from gmem.c and gmessages.c; */
149
150 /* pthread_mutex_lock, pthread_mutex_unlock can be taken directly, as
151    signature and semantic are right, but without error check then!!!!,
152    we might want to change this therefore. */
153
154 static gboolean
155 g_mutex_trylock_posix_impl (GMutex * mutex)
156 {
157   int result;
158
159   result = pthread_mutex_trylock ((pthread_mutex_t *) mutex);
160
161 #ifdef G_THREADS_IMPL_POSIX
162   if (result == EBUSY)
163     return FALSE;
164 #else /* G_THREADS_IMPL_DCE */
165   if (result == 0)
166     return FALSE;
167 #endif
168
169   posix_check_err (posix_error (result), "pthread_mutex_trylock");
170   return TRUE;
171 }
172
173 static GCond *
174 g_cond_new_posix_impl (void)
175 {
176   GCond *result = (GCond *) g_new (pthread_cond_t, 1);
177   posix_check_cmd (pthread_cond_init ((pthread_cond_t *) result, 
178                                       condattr_default));
179   return result;
180 }
181
182 /* pthread_cond_signal, pthread_cond_broadcast and pthread_cond_wait
183    can be taken directly, as signature and semantic are right, but
184    without error check then!!!!, we might want to change this
185    therfore. */
186
187 #define G_NSEC_PER_SEC 1000000000
188
189 static gboolean
190 g_cond_timed_wait_posix_impl (GCond * cond,
191                               GMutex * entered_mutex,
192                               GTimeVal * abs_time)
193 {
194   int result;
195   struct timespec end_time;
196   gboolean timed_out;
197
198   g_return_val_if_fail (cond != NULL, FALSE);
199   g_return_val_if_fail (entered_mutex != NULL, FALSE);
200
201   if (!abs_time)
202     {
203       g_cond_wait (cond, entered_mutex);
204       return TRUE;
205     }
206
207   end_time.tv_sec = abs_time->tv_sec;
208   end_time.tv_nsec = abs_time->tv_usec * (G_NSEC_PER_SEC / G_USEC_PER_SEC);
209   g_assert (end_time.tv_nsec < G_NSEC_PER_SEC);
210   result = pthread_cond_timedwait ((pthread_cond_t *) cond,
211                                    (pthread_mutex_t *) entered_mutex,
212                                    &end_time);
213
214 #ifdef G_THREADS_IMPL_POSIX
215   timed_out = (result == ETIMEDOUT);
216 #else /* G_THREADS_IMPL_DCE */
217   timed_out = (result == -1) && (errno = EAGAIN);
218 #endif
219
220   if (!timed_out)
221     posix_check_err (posix_error (result), "pthread_cond_timedwait");
222   return !timed_out;
223 }
224
225 static void
226 g_cond_free_posix_impl (GCond * cond)
227 {
228   posix_check_cmd (pthread_cond_destroy ((pthread_cond_t *) cond));
229   g_free (cond);
230 }
231
232 static GPrivate *
233 g_private_new_posix_impl (GDestroyNotify destructor)
234 {
235   GPrivate *result = (GPrivate *) g_new (pthread_key_t, 1);
236   posix_check_cmd (pthread_key_create ((pthread_key_t *) result, destructor));
237   return result;
238 }
239
240 /* NOTE: the functions g_private_get and g_private_set may not use
241    functions from gmem.c and gmessages.c */
242
243 static void
244 g_private_set_posix_impl (GPrivate * private_key, gpointer value)
245 {
246   if (!private_key)
247     return;
248   pthread_setspecific (*(pthread_key_t *) private_key, value);
249 }
250
251 static gpointer
252 g_private_get_posix_impl (GPrivate * private_key)
253 {
254   if (!private_key)
255     return NULL;
256 #ifdef G_THREADS_IMPL_POSIX
257   return pthread_getspecific (*(pthread_key_t *) private_key);
258 #else /* G_THREADS_IMPL_DCE */
259   {
260     void* data;
261     posix_check_cmd (pthread_getspecific (*(pthread_key_t *) private_key, 
262                                           &data));
263     return data;
264   }
265 #endif
266 }
267
268 #ifdef G_THREAD_USE_PID_SURROGATE
269 struct proxy_data
270 {
271   GThreadFunc thread_func;
272   gpointer arg;
273   gpointer thread;
274   GThreadPriority priority;
275 };
276
277 static void
278 g_thread_create_posix_impl_proxy (struct proxy_data *data)
279 {
280   GThreadFunc thread_func = data->thread_func;
281   GThreadFunc arg = data->arg;
282   PID_IN_THREAD (data->thread) = getpid();
283   SET_PRIO (PID_IN_THREAD (data->thread), data->priority);
284   g_free (data);
285   thread_func (arg);
286 }
287 #endif /* G_THREAD_USE_PID_SURROGATE */
288
289 static void
290 g_thread_create_posix_impl (GThreadFunc thread_func, 
291                             gpointer arg, 
292                             gulong stack_size,
293                             gboolean joinable,
294                             gboolean bound,
295                             GThreadPriority priority,
296                             gpointer thread,
297                             GError **error)
298 {
299   pthread_attr_t attr;
300   gint ret;
301
302   g_return_if_fail (thread_func);
303   g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
304   g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
305
306   posix_check_cmd (pthread_attr_init (&attr));
307   
308 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
309   if (stack_size)
310     {
311       stack_size = MAX (g_thread_min_stack_size, stack_size);
312       posix_check_cmd (pthread_attr_setstacksize (&attr, stack_size));
313     }
314 #endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
315
316 #ifdef PTHREAD_SCOPE_SYSTEM
317   if (bound)
318     /* No error check here, because some systems can't do it and we
319      * simply don't want threads to fail because of that. */
320     pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
321 #endif /* PTHREAD_SCOPE_SYSTEM */
322
323 #ifdef G_THREADS_IMPL_POSIX
324   posix_check_cmd (pthread_attr_setdetachstate (&attr,
325           joinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
326 #endif /* G_THREADS_IMPL_POSIX */
327   
328 #ifdef G_THREAD_USE_PID_SURROGATE
329   {
330     struct proxy_data *data = g_new (struct proxy_data, 1); 
331     data->thread_func = thread_func;
332     data->arg = arg;
333     data->thread = thread;
334     data->priority = priority;
335     PID_IN_THREAD (thread) = 0;
336     ret = posix_error (pthread_create (thread, &attr, (void* (*)(void*))
337                                        g_thread_create_posix_impl_proxy, 
338                                        data));
339   }
340 #else /* G_THREAD_USE_PID_SURROGATE */
341 # ifdef HAVE_PRIORITIES
342 #  ifdef G_THREADS_IMPL_POSIX
343   {
344     struct sched_param sched;
345     posix_check_cmd (pthread_attr_getschedparam (&attr, &sched));
346     sched.sched_priority = g_thread_priority_map [priority];
347     posix_check_cmd_prio (pthread_attr_setschedparam (&attr, &sched));
348   }
349 #  else /* G_THREADS_IMPL_DCE */
350   posix_check_cmd_prio 
351     (pthread_attr_setprio (&attr, g_thread_priority_map [priority]));
352 #  endif /* G_THREADS_IMPL_DCE */
353 # endif /* HAVE_PRIORITIES */
354   ret = posix_error (pthread_create (thread, &attr, 
355                                      (void* (*)(void*))thread_func, arg));
356 #endif /* !G_THREAD_USE_PID_SURROGATE */
357
358   posix_check_cmd (pthread_attr_destroy (&attr));
359
360   if (ret == EAGAIN)
361     {
362       g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, 
363                    "Error creating thread: %s", g_strerror (ret));
364       return;
365     }
366
367   posix_check_err (ret, "pthread_create");
368
369 #ifdef G_THREADS_IMPL_DCE
370   if (!joinable)
371     posix_check_cmd (pthread_detach (thread));
372 #endif /* G_THREADS_IMPL_DCE */
373 }
374
375 static void 
376 g_thread_yield_posix_impl (void)
377 {
378   POSIX_YIELD_FUNC;
379 }
380
381 static void
382 g_thread_join_posix_impl (gpointer thread)
383 {     
384   gpointer ignore;
385   posix_check_cmd (pthread_join (*(pthread_t*)thread, &ignore));
386 }
387
388 static void 
389 g_thread_exit_posix_impl (void) 
390 {
391   pthread_exit (NULL);
392 }
393
394 static void
395 g_thread_set_priority_posix_impl (gpointer thread, GThreadPriority priority)
396 {
397   g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
398   g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
399 #ifdef HAVE_PRIORITIES
400 # ifdef G_THREADS_IMPL_POSIX
401   {
402     struct sched_param sched;
403     int policy;
404     posix_check_cmd (pthread_getschedparam (*(pthread_t*)thread, &policy, 
405                                             &sched));
406     sched.sched_priority = g_thread_priority_map [priority];
407     posix_check_cmd_prio (pthread_setschedparam (*(pthread_t*)thread, policy, 
408                                                  &sched));
409   }
410 # else /* G_THREADS_IMPL_DCE */
411   posix_check_cmd_prio (pthread_setprio (*(pthread_t*)thread, 
412                                          g_thread_priority_map [priority]));
413 # endif
414 #elif defined (G_THREAD_USE_PID_SURROGATE)
415   /* If the addressed thread hasn't yet been able to provide it's pid,
416    * we ignore the request. Should be more than rare */
417   if (PID_IN_THREAD (thread) != 0)
418     SET_PRIO (PID_IN_THREAD (thread), priority);
419 #endif /* G_THREAD_USE_PID_SURROGATE */
420 }
421
422 static void
423 g_thread_self_posix_impl (gpointer thread)
424 {
425   *(pthread_t*)thread = pthread_self();
426 #ifdef G_THREAD_USE_PID_SURROGATE
427   PID_IN_THREAD (thread) = getpid();
428 #endif /* G_THREAD_USE_PID_SURROGATE */
429 }
430
431 static GThreadFunctions g_thread_functions_for_glib_use_default =
432 {
433   g_mutex_new_posix_impl,
434   (void (*)(GMutex *)) pthread_mutex_lock,
435   g_mutex_trylock_posix_impl,
436   (void (*)(GMutex *)) pthread_mutex_unlock,
437   g_mutex_free_posix_impl,
438   g_cond_new_posix_impl,
439   (void (*)(GCond *)) pthread_cond_signal,
440   (void (*)(GCond *)) pthread_cond_broadcast,
441   (void (*)(GCond *, GMutex *)) pthread_cond_wait,
442   g_cond_timed_wait_posix_impl,
443   g_cond_free_posix_impl,
444   g_private_new_posix_impl,
445   g_private_get_posix_impl,
446   g_private_set_posix_impl,
447   g_thread_create_posix_impl,
448   g_thread_yield_posix_impl,
449   g_thread_join_posix_impl,
450   g_thread_exit_posix_impl,
451   g_thread_set_priority_posix_impl,
452   g_thread_self_posix_impl
453 };