LCOV - code coverage report
Current view: top level - glib - gthread-posix.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 81.6 % 244 199
Test Date: 2025-03-04 05:20:05 Functions: 87.1 % 31 27
Branches: - 0 0

             Branch data     Line data    Source code
       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                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       8                 :             :  *
       9                 :             :  * This library is free software; you can redistribute it and/or
      10                 :             :  * modify it under the terms of the GNU Lesser General Public
      11                 :             :  * License as published by the Free Software Foundation; either
      12                 :             :  * version 2.1 of the License, or (at your option) any later version.
      13                 :             :  *
      14                 :             :  * This library 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 GNU
      17                 :             :  * Lesser General Public License for more details.
      18                 :             :  *
      19                 :             :  * You should have received a copy of the GNU Lesser General Public
      20                 :             :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      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                 :             : /* The GMutex, GCond and GPrivate implementations in this file are some
      31                 :             :  * of the lowest-level code in GLib.  All other parts of GLib (messages,
      32                 :             :  * memory, slices, etc) assume that they can freely use these facilities
      33                 :             :  * without risking recursion.
      34                 :             :  *
      35                 :             :  * As such, these functions are NOT permitted to call any other part of
      36                 :             :  * GLib.
      37                 :             :  *
      38                 :             :  * The thread manipulation functions (create, exit, join, etc.) have
      39                 :             :  * more freedom -- they can do as they please.
      40                 :             :  */
      41                 :             : 
      42                 :             : #include "config.h"
      43                 :             : 
      44                 :             : #include "gthread.h"
      45                 :             : 
      46                 :             : #include "gmain.h"
      47                 :             : #include "gmessages.h"
      48                 :             : #include "gslice.h"
      49                 :             : #include "gstrfuncs.h"
      50                 :             : #include "gtestutils.h"
      51                 :             : #include "gthreadprivate.h"
      52                 :             : #include "gutils.h"
      53                 :             : 
      54                 :             : #include <stdlib.h>
      55                 :             : #include <stdio.h>
      56                 :             : #include <string.h>
      57                 :             : #include <errno.h>
      58                 :             : #include <pthread.h>
      59                 :             : 
      60                 :             : #include <sys/time.h>
      61                 :             : #include <unistd.h>
      62                 :             : 
      63                 :             : #ifdef HAVE_PTHREAD_SET_NAME_NP
      64                 :             : #include <pthread_np.h>
      65                 :             : #endif
      66                 :             : #ifdef HAVE_SCHED_H
      67                 :             : #include <sched.h>
      68                 :             : #endif
      69                 :             : #ifdef G_OS_WIN32
      70                 :             : #include <windows.h>
      71                 :             : #endif
      72                 :             : 
      73                 :             : #if defined(HAVE_SYS_SCHED_GETATTR)
      74                 :             : #include <sys/syscall.h>
      75                 :             : #endif
      76                 :             : 
      77                 :             : #if (defined(HAVE_FUTEX) || defined(HAVE_FUTEX_TIME64)) && \
      78                 :             :     (defined(HAVE_STDATOMIC_H) || defined(__ATOMIC_SEQ_CST))
      79                 :             : #define USE_NATIVE_MUTEX
      80                 :             : #endif
      81                 :             : 
      82                 :             : static void
      83                 :           0 : g_thread_abort (gint         status,
      84                 :             :                 const gchar *function)
      85                 :             : {
      86                 :           0 :   fprintf (stderr, "GLib (gthread-posix.c): Unexpected error from C library during '%s': %s.  Aborting.\n",
      87                 :             :            function, strerror (status));
      88                 :           0 :   g_abort ();
      89                 :             : }
      90                 :             : 
      91                 :             : /* {{{1 GMutex */
      92                 :             : 
      93                 :             : #if !defined(USE_NATIVE_MUTEX)
      94                 :             : 
      95                 :             : static pthread_mutex_t *
      96                 :             : g_mutex_impl_new (void)
      97                 :             : {
      98                 :             :   pthread_mutexattr_t *pattr = NULL;
      99                 :             :   pthread_mutex_t *mutex;
     100                 :             :   gint status;
     101                 :             : #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
     102                 :             :   pthread_mutexattr_t attr;
     103                 :             : #endif
     104                 :             : 
     105                 :             :   mutex = malloc (sizeof (pthread_mutex_t));
     106                 :             :   if G_UNLIKELY (mutex == NULL)
     107                 :             :     g_thread_abort (errno, "malloc");
     108                 :             : 
     109                 :             : #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
     110                 :             :   pthread_mutexattr_init (&attr);
     111                 :             :   pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
     112                 :             :   pattr = &attr;
     113                 :             : #endif
     114                 :             : 
     115                 :             :   if G_UNLIKELY ((status = pthread_mutex_init (mutex, pattr)) != 0)
     116                 :             :     g_thread_abort (status, "pthread_mutex_init");
     117                 :             : 
     118                 :             : #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
     119                 :             :   pthread_mutexattr_destroy (&attr);
     120                 :             : #endif
     121                 :             : 
     122                 :             :   return mutex;
     123                 :             : }
     124                 :             : 
     125                 :             : static void
     126                 :             : g_mutex_impl_free (pthread_mutex_t *mutex)
     127                 :             : {
     128                 :             :   pthread_mutex_destroy (mutex);
     129                 :             :   free (mutex);
     130                 :             : }
     131                 :             : 
     132                 :             : static inline pthread_mutex_t *
     133                 :             : g_mutex_get_impl (GMutex *mutex)
     134                 :             : {
     135                 :             :   pthread_mutex_t *impl = g_atomic_pointer_get (&mutex->p);
     136                 :             : 
     137                 :             :   if G_UNLIKELY (impl == NULL)
     138                 :             :     {
     139                 :             :       impl = g_mutex_impl_new ();
     140                 :             :       if (!g_atomic_pointer_compare_and_exchange (&mutex->p, NULL, impl))
     141                 :             :         g_mutex_impl_free (impl);
     142                 :             :       impl = mutex->p;
     143                 :             :     }
     144                 :             : 
     145                 :             :   return impl;
     146                 :             : }
     147                 :             : 
     148                 :             : 
     149                 :             : G_ALWAYS_INLINE static inline void
     150                 :             : g_mutex_init_impl (GMutex *mutex)
     151                 :             : {
     152                 :             :   mutex->p = g_mutex_impl_new ();
     153                 :             : }
     154                 :             : 
     155                 :             : G_ALWAYS_INLINE static inline void
     156                 :             : g_mutex_clear_impl (GMutex *mutex)
     157                 :             : {
     158                 :             :   g_mutex_impl_free (mutex->p);
     159                 :             : }
     160                 :             : 
     161                 :             : G_ALWAYS_INLINE static inline void
     162                 :             : g_mutex_lock_impl (GMutex *mutex)
     163                 :             : {
     164                 :             :   gint status;
     165                 :             : 
     166                 :             :   if G_UNLIKELY ((status = pthread_mutex_lock (g_mutex_get_impl (mutex))) != 0)
     167                 :             :     g_thread_abort (status, "pthread_mutex_lock");
     168                 :             : }
     169                 :             : 
     170                 :             : G_ALWAYS_INLINE static inline void
     171                 :             : g_mutex_unlock_impl (GMutex *mutex)
     172                 :             : {
     173                 :             :   gint status;
     174                 :             : 
     175                 :             :   if G_UNLIKELY ((status = pthread_mutex_unlock (g_mutex_get_impl (mutex))) != 0)
     176                 :             :     g_thread_abort (status, "pthread_mutex_unlock");
     177                 :             : }
     178                 :             : 
     179                 :             : G_ALWAYS_INLINE static inline gboolean
     180                 :             : g_mutex_trylock_impl (GMutex *mutex)
     181                 :             : {
     182                 :             :   gint status;
     183                 :             : 
     184                 :             :   if G_LIKELY ((status = pthread_mutex_trylock (g_mutex_get_impl (mutex))) == 0)
     185                 :             :     return TRUE;
     186                 :             : 
     187                 :             :   if G_UNLIKELY (status != EBUSY)
     188                 :             :     g_thread_abort (status, "pthread_mutex_trylock");
     189                 :             : 
     190                 :             :   return FALSE;
     191                 :             : }
     192                 :             : 
     193                 :             : #endif /* !defined(USE_NATIVE_MUTEX) */
     194                 :             : 
     195                 :             : /* {{{1 GRecMutex */
     196                 :             : 
     197                 :             : static pthread_mutex_t *
     198                 :         870 : g_rec_mutex_impl_new (void)
     199                 :             : {
     200                 :             :   pthread_mutexattr_t attr;
     201                 :             :   pthread_mutex_t *mutex;
     202                 :             : 
     203                 :         870 :   mutex = malloc (sizeof (pthread_mutex_t));
     204                 :         870 :   if G_UNLIKELY (mutex == NULL)
     205                 :           0 :     g_thread_abort (errno, "malloc");
     206                 :             : 
     207                 :         870 :   pthread_mutexattr_init (&attr);
     208                 :         870 :   pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
     209                 :         870 :   pthread_mutex_init (mutex, &attr);
     210                 :         870 :   pthread_mutexattr_destroy (&attr);
     211                 :             : 
     212                 :         870 :   return mutex;
     213                 :             : }
     214                 :             : 
     215                 :             : static void
     216                 :          21 : g_rec_mutex_impl_free (pthread_mutex_t *mutex)
     217                 :             : {
     218                 :          21 :   pthread_mutex_destroy (mutex);
     219                 :          21 :   free (mutex);
     220                 :          21 : }
     221                 :             : 
     222                 :             : static inline pthread_mutex_t *
     223                 :     1488207 : g_rec_mutex_get_impl (GRecMutex *rec_mutex)
     224                 :             : {
     225                 :     1488207 :   pthread_mutex_t *impl = g_atomic_pointer_get (&rec_mutex->p);
     226                 :             : 
     227                 :     1488207 :   if G_UNLIKELY (impl == NULL)
     228                 :             :     {
     229                 :         849 :       impl = g_rec_mutex_impl_new ();
     230                 :         849 :       if (!g_atomic_pointer_compare_and_exchange (&rec_mutex->p, NULL, impl))
     231                 :           0 :         g_rec_mutex_impl_free (impl);
     232                 :         849 :       impl = rec_mutex->p;
     233                 :             :     }
     234                 :             : 
     235                 :     1488207 :   return impl;
     236                 :             : }
     237                 :             : 
     238                 :             : G_ALWAYS_INLINE static inline void
     239                 :             : g_rec_mutex_init_impl (GRecMutex *rec_mutex)
     240                 :             : {
     241                 :          21 :   rec_mutex->p = g_rec_mutex_impl_new ();
     242                 :          21 : }
     243                 :             : 
     244                 :             : G_ALWAYS_INLINE static inline void
     245                 :             : g_rec_mutex_clear_impl (GRecMutex *rec_mutex)
     246                 :             : {
     247                 :          21 :   g_rec_mutex_impl_free (rec_mutex->p);
     248                 :          21 : }
     249                 :             : 
     250                 :             : G_ALWAYS_INLINE static inline void
     251                 :             : g_rec_mutex_lock_impl (GRecMutex *mutex)
     252                 :             : {
     253                 :     1478190 :   pthread_mutex_lock (g_rec_mutex_get_impl (mutex));
     254                 :     1478190 : }
     255                 :             : 
     256                 :             : G_ALWAYS_INLINE static inline void
     257                 :             : g_rec_mutex_unlock_impl (GRecMutex *rec_mutex)
     258                 :             : {
     259                 :     1484190 :   pthread_mutex_unlock (rec_mutex->p);
     260                 :     1484190 : }
     261                 :             : 
     262                 :             : G_ALWAYS_INLINE static inline gboolean
     263                 :             : g_rec_mutex_trylock_impl (GRecMutex *rec_mutex)
     264                 :             : {
     265                 :       10011 :   if (pthread_mutex_trylock (g_rec_mutex_get_impl (rec_mutex)) != 0)
     266                 :        4011 :     return FALSE;
     267                 :             : 
     268                 :        6000 :   return TRUE;
     269                 :             : }
     270                 :             : 
     271                 :             : /* {{{1 GRWLock */
     272                 :             : 
     273                 :             : static pthread_rwlock_t *
     274                 :        1021 : g_rw_lock_impl_new (void)
     275                 :             : {
     276                 :             :   pthread_rwlock_t *rwlock;
     277                 :             :   gint status;
     278                 :             : 
     279                 :        1021 :   rwlock = malloc (sizeof (pthread_rwlock_t));
     280                 :        1021 :   if G_UNLIKELY (rwlock == NULL)
     281                 :           0 :     g_thread_abort (errno, "malloc");
     282                 :             : 
     283                 :        1021 :   if G_UNLIKELY ((status = pthread_rwlock_init (rwlock, NULL)) != 0)
     284                 :           0 :     g_thread_abort (status, "pthread_rwlock_init");
     285                 :             : 
     286                 :        1021 :   return rwlock;
     287                 :             : }
     288                 :             : 
     289                 :             : static void
     290                 :          52 : g_rw_lock_impl_free (pthread_rwlock_t *rwlock)
     291                 :             : {
     292                 :          52 :   pthread_rwlock_destroy (rwlock);
     293                 :          52 :   free (rwlock);
     294                 :          52 : }
     295                 :             : 
     296                 :             : static inline pthread_rwlock_t *
     297                 :    21947033 : g_rw_lock_get_impl (GRWLock *lock)
     298                 :             : {
     299                 :    21947033 :   pthread_rwlock_t *impl = g_atomic_pointer_get (&lock->p);
     300                 :             : 
     301                 :    21947033 :   if G_UNLIKELY (impl == NULL)
     302                 :             :     {
     303                 :         969 :       impl = g_rw_lock_impl_new ();
     304                 :         969 :       if (!g_atomic_pointer_compare_and_exchange (&lock->p, NULL, impl))
     305                 :           0 :         g_rw_lock_impl_free (impl);
     306                 :         969 :       impl = lock->p;
     307                 :             :     }
     308                 :             : 
     309                 :    21947033 :   return impl;
     310                 :             : }
     311                 :             : 
     312                 :             : G_ALWAYS_INLINE static inline void
     313                 :             : g_rw_lock_init_impl (GRWLock *rw_lock)
     314                 :             : {
     315                 :          52 :   rw_lock->p = g_rw_lock_impl_new ();
     316                 :          52 : }
     317                 :             : 
     318                 :             : G_ALWAYS_INLINE static inline void
     319                 :             : g_rw_lock_clear_impl (GRWLock *rw_lock)
     320                 :             : {
     321                 :          52 :   g_rw_lock_impl_free (rw_lock->p);
     322                 :          52 : }
     323                 :             : 
     324                 :             : G_ALWAYS_INLINE static inline void
     325                 :             : g_rw_lock_writer_lock_impl (GRWLock *rw_lock)
     326                 :             : {
     327                 :      895133 :   int retval = pthread_rwlock_wrlock (g_rw_lock_get_impl (rw_lock));
     328                 :             : 
     329                 :      895133 :   if (retval != 0)
     330                 :           0 :     g_critical ("Failed to get RW lock %p: %s", rw_lock, g_strerror (retval));
     331                 :      895133 : }
     332                 :             : 
     333                 :             : G_ALWAYS_INLINE static inline gboolean
     334                 :             : g_rw_lock_writer_trylock_impl (GRWLock *rw_lock)
     335                 :             : {
     336                 :     1000006 :   if (pthread_rwlock_trywrlock (g_rw_lock_get_impl (rw_lock)) != 0)
     337                 :      438047 :     return FALSE;
     338                 :             : 
     339                 :      561959 :   return TRUE;
     340                 :             : }
     341                 :             : 
     342                 :             : G_ALWAYS_INLINE static inline void
     343                 :             : g_rw_lock_writer_unlock_impl (GRWLock *rw_lock)
     344                 :             : {
     345                 :     1457092 :   pthread_rwlock_unlock (g_rw_lock_get_impl (rw_lock));
     346                 :     1457092 : }
     347                 :             : 
     348                 :             : G_ALWAYS_INLINE static inline void
     349                 :             : g_rw_lock_reader_lock_impl (GRWLock *rw_lock)
     350                 :             : {
     351                 :     9297396 :   int retval = pthread_rwlock_rdlock (g_rw_lock_get_impl (rw_lock));
     352                 :             : 
     353                 :     9297396 :   if (retval != 0)
     354                 :           0 :     g_critical ("Failed to get RW lock %p: %s", rw_lock, g_strerror (retval));
     355                 :     9297396 : }
     356                 :             : 
     357                 :             : G_ALWAYS_INLINE static inline gboolean
     358                 :             : g_rw_lock_reader_trylock_impl (GRWLock *rw_lock)
     359                 :             : {
     360                 :           6 :   if (pthread_rwlock_tryrdlock (g_rw_lock_get_impl (rw_lock)) != 0)
     361                 :           2 :     return FALSE;
     362                 :             : 
     363                 :           4 :   return TRUE;
     364                 :             : }
     365                 :             : 
     366                 :             : G_ALWAYS_INLINE static inline void
     367                 :             : g_rw_lock_reader_unlock_impl (GRWLock *rw_lock)
     368                 :             : {
     369                 :     9297400 :   pthread_rwlock_unlock (g_rw_lock_get_impl (rw_lock));
     370                 :     9297400 : }
     371                 :             : 
     372                 :             : /* {{{1 GCond */
     373                 :             : 
     374                 :             : #if !defined(USE_NATIVE_MUTEX)
     375                 :             : 
     376                 :             : static pthread_cond_t *
     377                 :             : g_cond_impl_new (void)
     378                 :             : {
     379                 :             :   pthread_condattr_t attr;
     380                 :             :   pthread_cond_t *cond;
     381                 :             :   gint status;
     382                 :             : 
     383                 :             :   pthread_condattr_init (&attr);
     384                 :             : 
     385                 :             : #ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
     386                 :             : #elif defined (HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined (CLOCK_MONOTONIC)
     387                 :             :   if G_UNLIKELY ((status = pthread_condattr_setclock (&attr, CLOCK_MONOTONIC)) != 0)
     388                 :             :     g_thread_abort (status, "pthread_condattr_setclock");
     389                 :             : #else
     390                 :             : #error Cannot support GCond on your platform.
     391                 :             : #endif
     392                 :             : 
     393                 :             :   cond = malloc (sizeof (pthread_cond_t));
     394                 :             :   if G_UNLIKELY (cond == NULL)
     395                 :             :     g_thread_abort (errno, "malloc");
     396                 :             : 
     397                 :             :   if G_UNLIKELY ((status = pthread_cond_init (cond, &attr)) != 0)
     398                 :             :     g_thread_abort (status, "pthread_cond_init");
     399                 :             : 
     400                 :             :   pthread_condattr_destroy (&attr);
     401                 :             : 
     402                 :             :   return cond;
     403                 :             : }
     404                 :             : 
     405                 :             : static void
     406                 :             : g_cond_impl_free (pthread_cond_t *cond)
     407                 :             : {
     408                 :             :   pthread_cond_destroy (cond);
     409                 :             :   free (cond);
     410                 :             : }
     411                 :             : 
     412                 :             : static inline pthread_cond_t *
     413                 :             : g_cond_get_impl (GCond *cond)
     414                 :             : {
     415                 :             :   pthread_cond_t *impl = g_atomic_pointer_get (&cond->p);
     416                 :             : 
     417                 :             :   if G_UNLIKELY (impl == NULL)
     418                 :             :     {
     419                 :             :       impl = g_cond_impl_new ();
     420                 :             :       if (!g_atomic_pointer_compare_and_exchange (&cond->p, NULL, impl))
     421                 :             :         g_cond_impl_free (impl);
     422                 :             :       impl = cond->p;
     423                 :             :     }
     424                 :             : 
     425                 :             :   return impl;
     426                 :             : }
     427                 :             : 
     428                 :             : G_ALWAYS_INLINE static inline void
     429                 :             : g_cond_init_impl (GCond *cond)
     430                 :             : {
     431                 :             :   cond->p = g_cond_impl_new ();
     432                 :             : }
     433                 :             : 
     434                 :             : G_ALWAYS_INLINE static inline void
     435                 :             : g_cond_clear_impl (GCond *cond)
     436                 :             : {
     437                 :             :   g_cond_impl_free (cond->p);
     438                 :             : }
     439                 :             : 
     440                 :             : G_ALWAYS_INLINE static inline void
     441                 :             : g_cond_wait_impl (GCond  *cond,
     442                 :             :                   GMutex *mutex)
     443                 :             : {
     444                 :             :   gint status;
     445                 :             : 
     446                 :             :   if G_UNLIKELY ((status = pthread_cond_wait (g_cond_get_impl (cond), g_mutex_get_impl (mutex))) != 0)
     447                 :             :     g_thread_abort (status, "pthread_cond_wait");
     448                 :             : }
     449                 :             : 
     450                 :             : G_ALWAYS_INLINE static inline void
     451                 :             : g_cond_signal_impl (GCond *cond)
     452                 :             : {
     453                 :             :   gint status;
     454                 :             : 
     455                 :             :   if G_UNLIKELY ((status = pthread_cond_signal (g_cond_get_impl (cond))) != 0)
     456                 :             :     g_thread_abort (status, "pthread_cond_signal");
     457                 :             : }
     458                 :             : 
     459                 :             : G_ALWAYS_INLINE static inline void
     460                 :             : g_cond_broadcast_impl (GCond *cond)
     461                 :             : {
     462                 :             :   gint status;
     463                 :             : 
     464                 :             :   if G_UNLIKELY ((status = pthread_cond_broadcast (g_cond_get_impl (cond))) != 0)
     465                 :             :     g_thread_abort (status, "pthread_cond_broadcast");
     466                 :             : }
     467                 :             : 
     468                 :             : G_ALWAYS_INLINE static inline gboolean
     469                 :             : g_cond_wait_until_impl (GCond  *cond,
     470                 :             :                         GMutex *mutex,
     471                 :             :                         gint64  end_time)
     472                 :             : {
     473                 :             :   struct timespec ts;
     474                 :             :   gint status;
     475                 :             : 
     476                 :             : #ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
     477                 :             :   /* end_time is given relative to the monotonic clock as returned by
     478                 :             :    * g_get_monotonic_time().
     479                 :             :    *
     480                 :             :    * Since this pthreads wants the relative time, convert it back again.
     481                 :             :    */
     482                 :             :   {
     483                 :             :     gint64 now = g_get_monotonic_time ();
     484                 :             :     gint64 relative;
     485                 :             : 
     486                 :             :     if (end_time <= now)
     487                 :             :       return FALSE;
     488                 :             : 
     489                 :             :     relative = end_time - now;
     490                 :             : 
     491                 :             :     ts.tv_sec = relative / 1000000;
     492                 :             :     ts.tv_nsec = (relative % 1000000) * 1000;
     493                 :             : 
     494                 :             :     if ((status = pthread_cond_timedwait_relative_np (g_cond_get_impl (cond), g_mutex_get_impl (mutex), &ts)) == 0)
     495                 :             :       return TRUE;
     496                 :             :   }
     497                 :             : #elif defined (HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined (CLOCK_MONOTONIC)
     498                 :             :   /* This is the exact check we used during init to set the clock to
     499                 :             :    * monotonic, so if we're in this branch, timedwait() will already be
     500                 :             :    * expecting a monotonic clock.
     501                 :             :    */
     502                 :             :   {
     503                 :             :     ts.tv_sec = end_time / 1000000;
     504                 :             :     ts.tv_nsec = (end_time % 1000000) * 1000;
     505                 :             : 
     506                 :             :     if ((status = pthread_cond_timedwait (g_cond_get_impl (cond), g_mutex_get_impl (mutex), &ts)) == 0)
     507                 :             :       return TRUE;
     508                 :             :   }
     509                 :             : #else
     510                 :             : #error Cannot support GCond on your platform.
     511                 :             : #endif
     512                 :             : 
     513                 :             :   if G_UNLIKELY (status != ETIMEDOUT)
     514                 :             :     g_thread_abort (status, "pthread_cond_timedwait");
     515                 :             : 
     516                 :             :   return FALSE;
     517                 :             : }
     518                 :             : 
     519                 :             : #endif /* defined(USE_NATIVE_MUTEX) */
     520                 :             : 
     521                 :             : /* {{{1 GPrivate */
     522                 :             : 
     523                 :             : static pthread_key_t *
     524                 :           0 : g_private_impl_new (GDestroyNotify notify)
     525                 :             : {
     526                 :             :   pthread_key_t *key;
     527                 :             :   gint status;
     528                 :             : 
     529                 :           0 :   key = malloc (sizeof (pthread_key_t));
     530                 :           0 :   if G_UNLIKELY (key == NULL)
     531                 :           0 :     g_thread_abort (errno, "malloc");
     532                 :           0 :   status = pthread_key_create (key, notify);
     533                 :           0 :   if G_UNLIKELY (status != 0)
     534                 :           0 :     g_thread_abort (status, "pthread_key_create");
     535                 :             : 
     536                 :           0 :   return key;
     537                 :             : }
     538                 :             : 
     539                 :             : static void
     540                 :           0 : g_private_impl_free (pthread_key_t *key)
     541                 :             : {
     542                 :             :   gint status;
     543                 :             : 
     544                 :           0 :   status = pthread_key_delete (*key);
     545                 :           0 :   if G_UNLIKELY (status != 0)
     546                 :           0 :     g_thread_abort (status, "pthread_key_delete");
     547                 :           0 :   free (key);
     548                 :           0 : }
     549                 :             : 
     550                 :             : static gpointer
     551                 :        2093 : g_private_impl_new_direct (GDestroyNotify notify)
     552                 :             : {
     553                 :        2093 :   gpointer impl = (void *) (gssize) -1;
     554                 :             :   pthread_key_t key;
     555                 :             :   gint status;
     556                 :             : 
     557                 :        2093 :   status = pthread_key_create (&key, notify);
     558                 :        2093 :   if G_UNLIKELY (status != 0)
     559                 :           0 :     g_thread_abort (status, "pthread_key_create");
     560                 :             : 
     561                 :        2093 :   memcpy (&impl, &key, sizeof (pthread_key_t));
     562                 :             : 
     563                 :             :   /* pthread_key_create could theoretically put a NULL value into key.
     564                 :             :    * If that happens, waste the result and create a new one, since we
     565                 :             :    * use NULL to mean "not yet allocated".
     566                 :             :    *
     567                 :             :    * This will only happen once per program run.
     568                 :             :    *
     569                 :             :    * We completely avoid this problem for the case where pthread_key_t
     570                 :             :    * is smaller than void* (for example, on 64 bit Linux) by putting
     571                 :             :    * some high bits in the value of 'impl' to start with.  Since we only
     572                 :             :    * overwrite part of the pointer, we will never end up with NULL.
     573                 :             :    */
     574                 :             :   if (sizeof (pthread_key_t) == sizeof (gpointer))
     575                 :             :     {
     576                 :             :       if G_UNLIKELY (impl == NULL)
     577                 :             :         {
     578                 :             :           status = pthread_key_create (&key, notify);
     579                 :             :           if G_UNLIKELY (status != 0)
     580                 :             :             g_thread_abort (status, "pthread_key_create");
     581                 :             : 
     582                 :             :           memcpy (&impl, &key, sizeof (pthread_key_t));
     583                 :             : 
     584                 :             :           if G_UNLIKELY (impl == NULL)
     585                 :             :             g_thread_abort (status, "pthread_key_create (gave NULL result twice)");
     586                 :             :         }
     587                 :             :     }
     588                 :             : 
     589                 :        2093 :   return impl;
     590                 :             : }
     591                 :             : 
     592                 :             : static void
     593                 :           0 : g_private_impl_free_direct (gpointer impl)
     594                 :             : {
     595                 :             :   pthread_key_t tmp;
     596                 :             :   gint status;
     597                 :             : 
     598                 :           0 :   memcpy (&tmp, &impl, sizeof (pthread_key_t));
     599                 :             : 
     600                 :           0 :   status = pthread_key_delete (tmp);
     601                 :           0 :   if G_UNLIKELY (status != 0)
     602                 :           0 :     g_thread_abort (status, "pthread_key_delete");
     603                 :           0 : }
     604                 :             : 
     605                 :             : static inline pthread_key_t
     606                 :    20729274 : _g_private_get_impl (GPrivate *key)
     607                 :             : {
     608                 :             :   if (sizeof (pthread_key_t) > sizeof (gpointer))
     609                 :             :     {
     610                 :             :       pthread_key_t *impl = g_atomic_pointer_get (&key->p);
     611                 :             : 
     612                 :             :       if G_UNLIKELY (impl == NULL)
     613                 :             :         {
     614                 :             :           impl = g_private_impl_new (key->notify);
     615                 :             :           if (!g_atomic_pointer_compare_and_exchange (&key->p, NULL, impl))
     616                 :             :             {
     617                 :             :               g_private_impl_free (impl);
     618                 :             :               impl = key->p;
     619                 :             :             }
     620                 :             :         }
     621                 :             : 
     622                 :             :       return *impl;
     623                 :             :     }
     624                 :             :   else
     625                 :             :     {
     626                 :    20729274 :       gpointer impl = g_atomic_pointer_get (&key->p);
     627                 :             :       pthread_key_t tmp;
     628                 :             : 
     629                 :    20729274 :       if G_UNLIKELY (impl == NULL)
     630                 :             :         {
     631                 :        2093 :           impl = g_private_impl_new_direct (key->notify);
     632                 :        2093 :           if (!g_atomic_pointer_compare_and_exchange (&key->p, NULL, impl))
     633                 :             :             {
     634                 :           0 :               g_private_impl_free_direct (impl);
     635                 :           0 :               impl = key->p;
     636                 :             :             }
     637                 :             :         }
     638                 :             : 
     639                 :    20729274 :       memcpy (&tmp, &impl, sizeof (pthread_key_t));
     640                 :             : 
     641                 :    20729274 :       return tmp;
     642                 :             :     }
     643                 :             : }
     644                 :             : 
     645                 :             : G_ALWAYS_INLINE static inline gpointer
     646                 :             : g_private_get_impl (GPrivate *key)
     647                 :             : {
     648                 :             :   /* quote POSIX: No errors are returned from pthread_getspecific(). */
     649                 :    13399633 :   return pthread_getspecific (_g_private_get_impl (key));
     650                 :             : }
     651                 :             : 
     652                 :             : G_ALWAYS_INLINE static inline void
     653                 :             : g_private_set_impl (GPrivate *key,
     654                 :             :                     gpointer  value)
     655                 :             : {
     656                 :             :   gint status;
     657                 :             : 
     658                 :     7328863 :   if G_UNLIKELY ((status = pthread_setspecific (_g_private_get_impl (key), value)) != 0)
     659                 :           0 :     g_thread_abort (status, "pthread_setspecific");
     660                 :     7328863 : }
     661                 :             : 
     662                 :             : G_ALWAYS_INLINE static inline void
     663                 :             : g_private_replace_impl (GPrivate *key,
     664                 :             :                         gpointer  value)
     665                 :             : {
     666                 :         778 :   pthread_key_t impl = _g_private_get_impl (key);
     667                 :             :   gpointer old;
     668                 :             :   gint status;
     669                 :             : 
     670                 :         778 :   old = pthread_getspecific (impl);
     671                 :             : 
     672                 :         778 :   if G_UNLIKELY ((status = pthread_setspecific (impl, value)) != 0)
     673                 :           0 :     g_thread_abort (status, "pthread_setspecific");
     674                 :             : 
     675                 :         778 :   if (old && key->notify)
     676                 :          79 :     key->notify (old);
     677                 :         778 : }
     678                 :             : 
     679                 :             : /* {{{1 GThread */
     680                 :             : 
     681                 :             : #define posix_check_err(err, name) G_STMT_START{                        \
     682                 :             :   int error = (err);                                                    \
     683                 :             :   if (error)                                                            \
     684                 :             :     g_error ("file %s: line %d (%s): error '%s' during '%s'",         \
     685                 :             :            __FILE__, __LINE__, G_STRFUNC,                               \
     686                 :             :            g_strerror (error), name);                                   \
     687                 :             :   }G_STMT_END
     688                 :             : 
     689                 :             : #define posix_check_cmd(cmd) posix_check_err (cmd, #cmd)
     690                 :             : 
     691                 :             : typedef struct
     692                 :             : {
     693                 :             :   GRealThread thread;
     694                 :             : 
     695                 :             :   pthread_t system_thread;
     696                 :             :   gboolean  joined;
     697                 :             :   GMutex    lock;
     698                 :             : 
     699                 :             :   void *(*proxy) (void *);
     700                 :             : } GThreadPosix;
     701                 :             : 
     702                 :             : void
     703                 :       12661 : g_system_thread_free (GRealThread *thread)
     704                 :             : {
     705                 :       12661 :   GThreadPosix *pt = (GThreadPosix *) thread;
     706                 :             : 
     707                 :       12661 :   if (!pt->joined)
     708                 :         212 :     pthread_detach (pt->system_thread);
     709                 :             : 
     710                 :       12661 :   g_mutex_clear (&pt->lock);
     711                 :             : 
     712                 :       12661 :   g_slice_free (GThreadPosix, pt);
     713                 :       12661 : }
     714                 :             : 
     715                 :             : GRealThread *
     716                 :       13287 : g_system_thread_new (GThreadFunc proxy,
     717                 :             :                      gulong stack_size,
     718                 :             :                      const char *name,
     719                 :             :                      GThreadFunc func,
     720                 :             :                      gpointer data,
     721                 :             :                      GError **error)
     722                 :             : {
     723                 :             :   GThreadPosix *thread;
     724                 :             :   GRealThread *base_thread;
     725                 :             :   pthread_attr_t attr;
     726                 :             :   gint ret;
     727                 :             : 
     728                 :       13287 :   thread = g_slice_new0 (GThreadPosix);
     729                 :       13287 :   base_thread = (GRealThread*)thread;
     730                 :       13287 :   base_thread->ref_count = 2;
     731                 :       13287 :   base_thread->ours = TRUE;
     732                 :       13287 :   base_thread->thread.joinable = TRUE;
     733                 :       13287 :   base_thread->thread.func = func;
     734                 :       13287 :   base_thread->thread.data = data;
     735                 :       13287 :   if (name)
     736                 :        5858 :     g_strlcpy (base_thread->name, name, sizeof (base_thread->name));
     737                 :       13287 :   thread->proxy = proxy;
     738                 :             : 
     739                 :       13287 :   posix_check_cmd (pthread_attr_init (&attr));
     740                 :             : 
     741                 :             : #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
     742                 :       13287 :   if (stack_size)
     743                 :             :     {
     744                 :             : #ifdef _SC_THREAD_STACK_MIN
     745                 :           0 :       long min_stack_size = sysconf (_SC_THREAD_STACK_MIN);
     746                 :           0 :       if (min_stack_size >= 0)
     747                 :           0 :         stack_size = MAX ((gulong) min_stack_size, stack_size);
     748                 :             : #endif /* _SC_THREAD_STACK_MIN */
     749                 :             :       /* No error check here, because some systems can't do it and
     750                 :             :        * we simply don't want threads to fail because of that. */
     751                 :           0 :       pthread_attr_setstacksize (&attr, stack_size);
     752                 :             :     }
     753                 :             : #endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
     754                 :             : 
     755                 :             : #ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
     756                 :             :     {
     757                 :             :       /* While this is the default, better be explicit about it */
     758                 :       13287 :       pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED);
     759                 :             :     }
     760                 :             : #endif /* HAVE_PTHREAD_ATTR_SETINHERITSCHED */
     761                 :             : 
     762                 :       13287 :   ret = pthread_create (&thread->system_thread, &attr, (void* (*)(void*))proxy, thread);
     763                 :             : 
     764                 :       13287 :   posix_check_cmd (pthread_attr_destroy (&attr));
     765                 :             : 
     766                 :       13287 :   if (ret == EAGAIN)
     767                 :             :     {
     768                 :           1 :       g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, 
     769                 :             :                    "Error creating thread: %s", g_strerror (ret));
     770                 :           1 :       g_slice_free (GThreadPosix, thread);
     771                 :           1 :       return NULL;
     772                 :             :     }
     773                 :             : 
     774                 :       13286 :   posix_check_err (ret, "pthread_create");
     775                 :             : 
     776                 :       13286 :   g_mutex_init (&thread->lock);
     777                 :             : 
     778                 :       13286 :   return (GRealThread *) thread;
     779                 :             : }
     780                 :             : 
     781                 :             : G_ALWAYS_INLINE static inline void
     782                 :             : g_thread_yield_impl (void)
     783                 :             : {
     784                 :    19551887 :   sched_yield ();
     785                 :    19551887 : }
     786                 :             : 
     787                 :             : void
     788                 :       12449 : g_system_thread_wait (GRealThread *thread)
     789                 :             : {
     790                 :       12449 :   GThreadPosix *pt = (GThreadPosix *) thread;
     791                 :             : 
     792                 :       12449 :   g_mutex_lock (&pt->lock);
     793                 :             : 
     794                 :       12449 :   if (!pt->joined)
     795                 :             :     {
     796                 :       12449 :       posix_check_cmd (pthread_join (pt->system_thread, NULL));
     797                 :       12449 :       pt->joined = TRUE;
     798                 :             :     }
     799                 :             : 
     800                 :       12449 :   g_mutex_unlock (&pt->lock);
     801                 :       12449 : }
     802                 :             : 
     803                 :             : void
     804                 :          13 : g_system_thread_exit (void)
     805                 :             : {
     806                 :          13 :   pthread_exit (NULL);
     807                 :             : }
     808                 :             : 
     809                 :             : void
     810                 :        5857 : g_system_thread_set_name (const gchar *name)
     811                 :             : {
     812                 :             : #if defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID)
     813                 :             :   pthread_setname_np (name); /* on OS X and iOS */
     814                 :             : #elif defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)
     815                 :             : #ifdef __LINUX__
     816                 :             : #define MAX_THREADNAME_LEN 16
     817                 :             : #else
     818                 :             : #define MAX_THREADNAME_LEN 32
     819                 :             : #endif
     820                 :             :   char name_[MAX_THREADNAME_LEN];
     821                 :        5857 :   g_strlcpy (name_, name, MAX_THREADNAME_LEN);
     822                 :        5857 :   pthread_setname_np (pthread_self (), name_); /* on Linux and Solaris */
     823                 :             : #elif defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG)
     824                 :             :   pthread_setname_np (pthread_self (), "%s", (gchar *) name); /* on NetBSD */
     825                 :             : #elif defined(HAVE_PTHREAD_SET_NAME_NP)
     826                 :             :   pthread_set_name_np (pthread_self (), name); /* on FreeBSD, DragonFlyBSD, OpenBSD */
     827                 :             : #endif
     828                 :        5857 : }
     829                 :             : 
     830                 :             : void
     831                 :        7429 : g_system_thread_get_name (char  *buffer,
     832                 :             :                           gsize  length)
     833                 :             : {
     834                 :             : #ifdef HAVE_PTHREAD_GETNAME_NP
     835                 :        7429 :   pthread_getname_np (pthread_self (), buffer, length);
     836                 :             : #else
     837                 :             :   g_assert (length >= 1);
     838                 :             :   buffer[0] = '\0';
     839                 :             : #endif
     840                 :        7429 : }
     841                 :             : 
     842                 :             : /* {{{1 GMutex and GCond futex implementation */
     843                 :             : 
     844                 :             : #if defined(USE_NATIVE_MUTEX)
     845                 :             : /* We should expand the set of operations available in gatomic once we
     846                 :             :  * have better C11 support in GCC in common distributions (ie: 4.9).
     847                 :             :  *
     848                 :             :  * Before then, let's define a couple of useful things for our own
     849                 :             :  * purposes...
     850                 :             :  */
     851                 :             : 
     852                 :             : #ifdef HAVE_STDATOMIC_H
     853                 :             : 
     854                 :             : #include <stdatomic.h>
     855                 :             : 
     856                 :             : #define exchange_acquire(ptr, new) \
     857                 :             :   atomic_exchange_explicit((atomic_uint *) (ptr), (new), __ATOMIC_ACQUIRE)
     858                 :             : #define compare_exchange_acquire(ptr, old, new) \
     859                 :             :   atomic_compare_exchange_strong_explicit((atomic_uint *) (ptr), (old), (new), \
     860                 :             :                                           __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)
     861                 :             : 
     862                 :             : #define exchange_release(ptr, new) \
     863                 :             :   atomic_exchange_explicit((atomic_uint *) (ptr), (new), __ATOMIC_RELEASE)
     864                 :             : #define store_release(ptr, new) \
     865                 :             :   atomic_store_explicit((atomic_uint *) (ptr), (new), __ATOMIC_RELEASE)
     866                 :             : 
     867                 :             : #else
     868                 :             : 
     869                 :             : #define exchange_acquire(ptr, new) \
     870                 :             :   __atomic_exchange_4((ptr), (new), __ATOMIC_ACQUIRE)
     871                 :             : #define compare_exchange_acquire(ptr, old, new) \
     872                 :             :   __atomic_compare_exchange_4((ptr), (old), (new), 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)
     873                 :             : 
     874                 :             : #define exchange_release(ptr, new) \
     875                 :             :   __atomic_exchange_4((ptr), (new), __ATOMIC_RELEASE)
     876                 :             : #define store_release(ptr, new) \
     877                 :             :   __atomic_store_4((ptr), (new), __ATOMIC_RELEASE)
     878                 :             : 
     879                 :             : #endif
     880                 :             : 
     881                 :             : /* Our strategy for the mutex is pretty simple:
     882                 :             :  *
     883                 :             :  *  0: not in use
     884                 :             :  *
     885                 :             :  *  1: acquired by one thread only, no contention
     886                 :             :  *
     887                 :             :  *  2: contended
     888                 :             :  */
     889                 :             : 
     890                 :             : typedef enum {
     891                 :             :   G_MUTEX_STATE_EMPTY = 0,
     892                 :             :   G_MUTEX_STATE_OWNED,
     893                 :             :   G_MUTEX_STATE_CONTENDED,
     894                 :             : } GMutexState;
     895                 :             : 
     896                 :             :  /*
     897                 :             :  * As such, attempting to acquire the lock should involve an increment.
     898                 :             :  * If we find that the previous value was 0 then we can return
     899                 :             :  * immediately.
     900                 :             :  *
     901                 :             :  * On unlock, we always store 0 to indicate that the lock is available.
     902                 :             :  * If the value there was 1 before then we didn't have contention and
     903                 :             :  * can return immediately.  If the value was something other than 1 then
     904                 :             :  * we have the contended case and need to wake a waiter.
     905                 :             :  *
     906                 :             :  * If it was not 0 then there is another thread holding it and we must
     907                 :             :  * wait.  We must always ensure that we mark a value >1 while we are
     908                 :             :  * waiting in order to instruct the holder to do a wake operation on
     909                 :             :  * unlock.
     910                 :             :  */
     911                 :             : 
     912                 :             : void
     913                 :      142603 : g_mutex_init_impl (GMutex *mutex)
     914                 :             : {
     915                 :      142603 :   mutex->i[0] = G_MUTEX_STATE_EMPTY;
     916                 :      142603 : }
     917                 :             : 
     918                 :             : void
     919                 :      139297 : g_mutex_clear_impl (GMutex *mutex)
     920                 :             : {
     921                 :      139297 :   if G_UNLIKELY (mutex->i[0] != G_MUTEX_STATE_EMPTY)
     922                 :             :     {
     923                 :           0 :       fprintf (stderr, "g_mutex_clear() called on uninitialised or locked mutex\n");
     924                 :           0 :       g_abort ();
     925                 :             :     }
     926                 :      139297 : }
     927                 :             : 
     928                 :             : G_GNUC_NO_INLINE
     929                 :             : static void
     930                 :     6172670 : g_mutex_lock_slowpath (GMutex *mutex)
     931                 :             : {
     932                 :             :   /* Set to contended.  If it was empty before then we
     933                 :             :    * just acquired the lock.
     934                 :             :    *
     935                 :             :    * Otherwise, sleep for as long as the contended state remains...
     936                 :             :    */
     937                 :    14912105 :   while (exchange_acquire (&mutex->i[0], G_MUTEX_STATE_CONTENDED) != G_MUTEX_STATE_EMPTY)
     938                 :             :     {
     939                 :     8739437 :       g_futex_simple (&mutex->i[0], (gsize) FUTEX_WAIT_PRIVATE,
     940                 :             :                       G_MUTEX_STATE_CONTENDED, NULL);
     941                 :             :     }
     942                 :     6172668 : }
     943                 :             : 
     944                 :             : G_GNUC_NO_INLINE
     945                 :             : static void
     946                 :     9809063 : g_mutex_unlock_slowpath (GMutex *mutex,
     947                 :             :                          guint   prev)
     948                 :             : {
     949                 :             :   /* We seem to get better code for the uncontended case by splitting
     950                 :             :    * this out...
     951                 :             :    */
     952                 :     9809063 :   if G_UNLIKELY (prev == G_MUTEX_STATE_EMPTY)
     953                 :             :     {
     954                 :           0 :       fprintf (stderr, "Attempt to unlock mutex that was not locked\n");
     955                 :           0 :       g_abort ();
     956                 :             :     }
     957                 :             : 
     958                 :     9809063 :   g_futex_simple (&mutex->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
     959                 :     9809063 : }
     960                 :             : 
     961                 :             : inline void
     962                 :   266849335 : g_mutex_lock_impl (GMutex *mutex)
     963                 :             : {
     964                 :             :   /* empty -> owned and we're done.  Anything else, and we need to wait... */
     965                 :   266849335 :   if G_UNLIKELY (!g_atomic_int_compare_and_exchange (&mutex->i[0],
     966                 :             :                                                      G_MUTEX_STATE_EMPTY,
     967                 :             :                                                      G_MUTEX_STATE_OWNED))
     968                 :     6172670 :     g_mutex_lock_slowpath (mutex);
     969                 :   266849333 : }
     970                 :             : 
     971                 :             : void
     972                 :   267492600 : g_mutex_unlock_impl (GMutex *mutex)
     973                 :             : {
     974                 :             :   guint prev;
     975                 :             : 
     976                 :   267492600 :   prev = exchange_release (&mutex->i[0], G_MUTEX_STATE_EMPTY);
     977                 :             : 
     978                 :             :   /* 1-> 0 and we're done.  Anything else and we need to signal... */
     979                 :   267492600 :   if G_UNLIKELY (prev != G_MUTEX_STATE_OWNED)
     980                 :     9809063 :     g_mutex_unlock_slowpath (mutex, prev);
     981                 :   267492600 : }
     982                 :             : 
     983                 :             : gboolean
     984                 :     1128793 : g_mutex_trylock_impl (GMutex *mutex)
     985                 :             : {
     986                 :     1128793 :   GMutexState empty = G_MUTEX_STATE_EMPTY;
     987                 :             : 
     988                 :             :   /* We don't want to touch the value at all unless we can move it from
     989                 :             :    * exactly empty to owned.
     990                 :             :    */
     991                 :     1128793 :   return compare_exchange_acquire (&mutex->i[0], &empty, G_MUTEX_STATE_OWNED);
     992                 :             : }
     993                 :             : 
     994                 :             : /* Condition variables are implemented in a rather simple way as well.
     995                 :             :  * In many ways, futex() as an abstraction is even more ideally suited
     996                 :             :  * to condition variables than it is to mutexes.
     997                 :             :  *
     998                 :             :  * We store a generation counter.  We sample it with the lock held and
     999                 :             :  * unlock before sleeping on the futex.
    1000                 :             :  *
    1001                 :             :  * Signalling simply involves increasing the counter and making the
    1002                 :             :  * appropriate futex call.
    1003                 :             :  *
    1004                 :             :  * The only thing that is the slightest bit complicated is timed waits
    1005                 :             :  * because we must convert our absolute time to relative.
    1006                 :             :  */
    1007                 :             : 
    1008                 :             : void
    1009                 :      180074 : g_cond_init_impl (GCond *cond)
    1010                 :             : {
    1011                 :      180074 :   cond->i[0] = 0;
    1012                 :      180074 : }
    1013                 :             : 
    1014                 :             : void
    1015                 :      178659 : g_cond_clear_impl (GCond *cond)
    1016                 :             : {
    1017                 :      178659 : }
    1018                 :             : 
    1019                 :             : void
    1020                 :      351506 : g_cond_wait_impl (GCond  *cond,
    1021                 :             :                   GMutex *mutex)
    1022                 :             : {
    1023                 :      351506 :   guint sampled = (guint) g_atomic_int_get (&cond->i[0]);
    1024                 :             : 
    1025                 :      351506 :   g_mutex_unlock (mutex);
    1026                 :      351506 :   g_futex_simple (&cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, NULL);
    1027                 :      351339 :   g_mutex_lock (mutex);
    1028                 :      351339 : }
    1029                 :             : 
    1030                 :             : void
    1031                 :      644284 : g_cond_signal_impl (GCond *cond)
    1032                 :             : {
    1033                 :      644284 :   g_atomic_int_inc (&cond->i[0]);
    1034                 :             : 
    1035                 :      644284 :   g_futex_simple (&cond->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
    1036                 :      644284 : }
    1037                 :             : 
    1038                 :             : void
    1039                 :      139759 : g_cond_broadcast_impl (GCond *cond)
    1040                 :             : {
    1041                 :      139759 :   g_atomic_int_inc (&cond->i[0]);
    1042                 :             : 
    1043                 :      139759 :   g_futex_simple (&cond->i[0], (gsize) FUTEX_WAKE_PRIVATE, (gsize) INT_MAX, NULL);
    1044                 :      139759 : }
    1045                 :             : 
    1046                 :             : gboolean
    1047                 :        2911 : g_cond_wait_until_impl (GCond  *cond,
    1048                 :             :                         GMutex *mutex,
    1049                 :             :                         gint64  end_time)
    1050                 :             : {
    1051                 :             :   struct timespec now;
    1052                 :             :   struct timespec span;
    1053                 :             : 
    1054                 :             :   guint sampled;
    1055                 :             :   int res;
    1056                 :             :   gboolean success;
    1057                 :             : 
    1058                 :        2911 :   if (end_time < 0)
    1059                 :           0 :     return FALSE;
    1060                 :             : 
    1061                 :        2911 :   clock_gettime (CLOCK_MONOTONIC, &now);
    1062                 :        2911 :   span.tv_sec = (end_time / 1000000) - now.tv_sec;
    1063                 :        2911 :   span.tv_nsec = ((end_time % 1000000) * 1000) - now.tv_nsec;
    1064                 :        2911 :   if (span.tv_nsec < 0)
    1065                 :             :     {
    1066                 :        1261 :       span.tv_nsec += 1000000000;
    1067                 :        1261 :       span.tv_sec--;
    1068                 :             :     }
    1069                 :             : 
    1070                 :        2911 :   if (span.tv_sec < 0)
    1071                 :           0 :     return FALSE;
    1072                 :             : 
    1073                 :             :   /* `struct timespec` as defined by the libc headers does not necessarily
    1074                 :             :    * have any relation to the one used by the kernel for the `futex` syscall.
    1075                 :             :    *
    1076                 :             :    * Specifically, the libc headers might use 64-bit `time_t` while the kernel
    1077                 :             :    * headers use 32-bit types on certain systems.
    1078                 :             :    *
    1079                 :             :    * To get around this problem we
    1080                 :             :    *   a) check if `futex_time64` is available, which only exists on 32-bit
    1081                 :             :    *      platforms and always uses 64-bit `time_t`.
    1082                 :             :    *   b) if `futex_time64` is available, but the Android runtime's API level
    1083                 :             :    *      is < 30, `futex_time64` is blocked by seccomp and using it will cause
    1084                 :             :    *      the app to be terminated. Skip to c).
    1085                 :             :    *         https://android-review.googlesource.com/c/platform/bionic/+/1094758
    1086                 :             :    *   c) otherwise (or if that returns `ENOSYS`), we call the normal `futex`
    1087                 :             :    *      syscall with the `struct timespec` used by the kernel. By default, we
    1088                 :             :    *      use `__kernel_long_t` for both its fields, which is equivalent to
    1089                 :             :    *      `__kernel_old_time_t` and is available in the kernel headers for a
    1090                 :             :    *      longer time.
    1091                 :             :    *   d) With very old headers (~2.6.x), `__kernel_long_t` is not available, and
    1092                 :             :    *      we use an older definition that uses `__kernel_time_t` and `long`.
    1093                 :             :    *
    1094                 :             :    * Also some 32-bit systems do not define `__NR_futex` at all and only
    1095                 :             :    * define `__NR_futex_time64`.
    1096                 :             :    */
    1097                 :             : 
    1098                 :        2911 :   sampled = cond->i[0];
    1099                 :        2911 :   g_mutex_unlock (mutex);
    1100                 :             : 
    1101                 :             : #if defined(HAVE_FUTEX_TIME64)
    1102                 :             : #if defined(__ANDROID__)
    1103                 :             :   if (__builtin_available (android 30, *)) {
    1104                 :             : #else
    1105                 :             :   {
    1106                 :             : #endif
    1107                 :             :     struct
    1108                 :             :     {
    1109                 :             :       gint64 tv_sec;
    1110                 :             :       gint64 tv_nsec;
    1111                 :             :     } span_arg;
    1112                 :             : 
    1113                 :             :     span_arg.tv_sec = span.tv_sec;
    1114                 :             :     span_arg.tv_nsec = span.tv_nsec;
    1115                 :             : 
    1116                 :             :     res = syscall (__NR_futex_time64, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span_arg);
    1117                 :             : 
    1118                 :             :     /* If the syscall does not exist (`ENOSYS`), we retry again below with the
    1119                 :             :      * normal `futex` syscall. This can happen if newer kernel headers are
    1120                 :             :      * used than the kernel that is actually running.
    1121                 :             :      */
    1122                 :             : #  if defined(HAVE_FUTEX)
    1123                 :             :     if (res >= 0 || errno != ENOSYS)
    1124                 :             : #  endif /* defined(HAVE_FUTEX) */
    1125                 :             :       {
    1126                 :             :         success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
    1127                 :             :         g_mutex_lock (mutex);
    1128                 :             : 
    1129                 :             :         return success;
    1130                 :             :       }
    1131                 :             :   }
    1132                 :             : #endif
    1133                 :             : 
    1134                 :             : #if defined(HAVE_FUTEX)
    1135                 :             :   {
    1136                 :             : #  ifdef __kernel_long_t
    1137                 :             : #    define KERNEL_SPAN_SEC_TYPE __kernel_long_t
    1138                 :             :     struct
    1139                 :             :     {
    1140                 :             :       __kernel_long_t tv_sec;
    1141                 :             :       __kernel_long_t tv_nsec;
    1142                 :             :     } span_arg;
    1143                 :             : #  else
    1144                 :             :     /* Very old kernel headers: version 2.6.32 and thereabouts */
    1145                 :             : #    define KERNEL_SPAN_SEC_TYPE __kernel_time_t
    1146                 :             :     struct
    1147                 :             :     {
    1148                 :             :       __kernel_time_t tv_sec;
    1149                 :             :       long            tv_nsec;
    1150                 :             :     } span_arg;
    1151                 :             : #  endif
    1152                 :             :     /* Make sure to only ever call this if the end time actually fits into the target type */
    1153                 :             :     if (G_UNLIKELY (sizeof (KERNEL_SPAN_SEC_TYPE) < 8 && span.tv_sec > G_MAXINT32))
    1154                 :             :       g_error ("%s: Can’t wait for more than %us", G_STRFUNC, G_MAXINT32);
    1155                 :             : 
    1156                 :        2911 :     span_arg.tv_sec = span.tv_sec;
    1157                 :        2911 :     span_arg.tv_nsec = span.tv_nsec;
    1158                 :             : 
    1159                 :        2911 :     res = syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span_arg);
    1160                 :        2771 :     success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
    1161                 :        2771 :     g_mutex_lock (mutex);
    1162                 :             : 
    1163                 :        2771 :     return success;
    1164                 :             :   }
    1165                 :             : #  undef KERNEL_SPAN_SEC_TYPE
    1166                 :             : #endif /* defined(HAVE_FUTEX) */
    1167                 :             : 
    1168                 :             :   /* We can't end up here because of the checks above */
    1169                 :             :   g_assert_not_reached ();
    1170                 :             : }
    1171                 :             : 
    1172                 :             : #endif
    1173                 :             : 
    1174                 :             :   /* {{{1 Epilogue */
    1175                 :             : /* vim:set foldmethod=marker: */
        

Generated by: LCOV version 2.0-1