(root)/
glib-2.79.0/
glib/
gthread-win32.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: solaris thread system implementation
       5   * Copyright 1998-2001 Sebastian Wilhelmi; University of Karlsruhe
       6   * Copyright 2001 Hans Breuer
       7   *
       8   * SPDX-License-Identifier: LGPL-2.1-or-later
       9   *
      10   * This library is free software; you can redistribute it and/or
      11   * modify it under the terms of the GNU Lesser General Public
      12   * License as published by the Free Software Foundation; either
      13   * version 2.1 of the License, or (at your option) any later version.
      14   *
      15   * This library is distributed in the hope that it will be useful,
      16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      18   * Lesser General Public License for more details.
      19   *
      20   * You should have received a copy of the GNU Lesser General Public
      21   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      22   */
      23  
      24  /*
      25   * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
      26   * file for a list of people on the GLib Team.  See the ChangeLog
      27   * files for a list of changes.  These files are distributed with
      28   * GLib at ftp://ftp.gtk.org/pub/gtk/.
      29   */
      30  
      31  /* The GMutex and GCond implementations in this file are some of the
      32   * lowest-level code in GLib.  All other parts of GLib (messages,
      33   * memory, slices, etc) assume that they can freely use these facilities
      34   * without risking recursion.
      35   *
      36   * As such, these functions are NOT permitted to call any other part of
      37   * GLib.
      38   *
      39   * The thread manipulation functions (create, exit, join, etc.) have
      40   * more freedom -- they can do as they please.
      41   */
      42  
      43  #include "config.h"
      44  
      45  #include "glib.h"
      46  #include "glib-init.h"
      47  #include "gthread.h"
      48  #include "gthreadprivate.h"
      49  #include "gslice.h"
      50  
      51  #include <windows.h>
      52  
      53  #include <process.h>
      54  #include <stdlib.h>
      55  #include <stdio.h>
      56  
      57  static void
      58  g_thread_abort (gint         status,
      59                  const gchar *function)
      60  {
      61    fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s.  Aborting.\n",
      62             strerror (status), function);
      63    g_abort ();
      64  }
      65  
      66  /* Starting with Vista and Windows 2008, we have access to the
      67   * CONDITION_VARIABLE and SRWLock primitives on Windows, which are
      68   * pretty reasonable approximations of the primitives specified in
      69   * POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively).
      70   *
      71   * Both of these types are structs containing a single pointer.  That
      72   * pointer is used as an atomic bitfield to support user-space mutexes
      73   * that only get the kernel involved in cases of contention (similar
      74   * to how futex()-based mutexes work on Linux).  The biggest advantage
      75   * of these new types is that they can be statically initialised to
      76   * zero.  That means that they are completely ABI compatible with our
      77   * GMutex and GCond APIs.
      78   */
      79  
      80  /* {{{1 GMutex */
      81  void
      82  g_mutex_init (GMutex *mutex)
      83  {
      84    InitializeSRWLock ((gpointer) mutex);
      85  }
      86  
      87  void
      88  g_mutex_clear (GMutex *mutex)
      89  {
      90  }
      91  
      92  void
      93  g_mutex_lock (GMutex *mutex)
      94  {
      95    AcquireSRWLockExclusive ((gpointer) mutex);
      96  }
      97  
      98  gboolean
      99  g_mutex_trylock (GMutex *mutex)
     100  {
     101    return TryAcquireSRWLockExclusive ((gpointer) mutex);
     102  }
     103  
     104  void
     105  g_mutex_unlock (GMutex *mutex)
     106  {
     107    ReleaseSRWLockExclusive ((gpointer) mutex);
     108  }
     109  
     110  /* {{{1 GRecMutex */
     111  
     112  static CRITICAL_SECTION *
     113  g_rec_mutex_impl_new (void)
     114  {
     115    CRITICAL_SECTION *cs;
     116  
     117    cs = g_slice_new (CRITICAL_SECTION);
     118    InitializeCriticalSection (cs);
     119  
     120    return cs;
     121  }
     122  
     123  static void
     124  g_rec_mutex_impl_free (CRITICAL_SECTION *cs)
     125  {
     126    DeleteCriticalSection (cs);
     127    g_slice_free (CRITICAL_SECTION, cs);
     128  }
     129  
     130  static CRITICAL_SECTION *
     131  g_rec_mutex_get_impl (GRecMutex *mutex)
     132  {
     133    CRITICAL_SECTION *impl = mutex->p;
     134  
     135    if G_UNLIKELY (mutex->p == NULL)
     136      {
     137        impl = g_rec_mutex_impl_new ();
     138        if (InterlockedCompareExchangePointer (&mutex->p, impl, NULL) != NULL)
     139          g_rec_mutex_impl_free (impl);
     140        impl = mutex->p;
     141      }
     142  
     143    return impl;
     144  }
     145  
     146  void
     147  g_rec_mutex_init (GRecMutex *mutex)
     148  {
     149    mutex->p = g_rec_mutex_impl_new ();
     150  }
     151  
     152  void
     153  g_rec_mutex_clear (GRecMutex *mutex)
     154  {
     155    g_rec_mutex_impl_free (mutex->p);
     156  }
     157  
     158  void
     159  g_rec_mutex_lock (GRecMutex *mutex)
     160  {
     161    EnterCriticalSection (g_rec_mutex_get_impl (mutex));
     162  }
     163  
     164  void
     165  g_rec_mutex_unlock (GRecMutex *mutex)
     166  {
     167    LeaveCriticalSection (mutex->p);
     168  }
     169  
     170  gboolean
     171  g_rec_mutex_trylock (GRecMutex *mutex)
     172  {
     173    return TryEnterCriticalSection (g_rec_mutex_get_impl (mutex));
     174  }
     175  
     176  /* {{{1 GRWLock */
     177  
     178  void
     179  g_rw_lock_init (GRWLock *lock)
     180  {
     181    InitializeSRWLock ((gpointer) lock);
     182  }
     183  
     184  void
     185  g_rw_lock_clear (GRWLock *lock)
     186  {
     187  }
     188  
     189  void
     190  g_rw_lock_writer_lock (GRWLock *lock)
     191  {
     192    AcquireSRWLockExclusive ((gpointer) lock);
     193  }
     194  
     195  gboolean
     196  g_rw_lock_writer_trylock (GRWLock *lock)
     197  {
     198    return TryAcquireSRWLockExclusive ((gpointer) lock);
     199  }
     200  
     201  void
     202  g_rw_lock_writer_unlock (GRWLock *lock)
     203  {
     204    ReleaseSRWLockExclusive ((gpointer) lock);
     205  }
     206  
     207  void
     208  g_rw_lock_reader_lock (GRWLock *lock)
     209  {
     210    AcquireSRWLockShared ((gpointer) lock);
     211  }
     212  
     213  gboolean
     214  g_rw_lock_reader_trylock (GRWLock *lock)
     215  {
     216    return TryAcquireSRWLockShared ((gpointer) lock);
     217  }
     218  
     219  void
     220  g_rw_lock_reader_unlock (GRWLock *lock)
     221  {
     222    ReleaseSRWLockShared ((gpointer) lock);
     223  }
     224  
     225  /* {{{1 GCond */
     226  void
     227  g_cond_init (GCond *cond)
     228  {
     229    InitializeConditionVariable ((gpointer) cond);
     230  }
     231  
     232  void
     233  g_cond_clear (GCond *cond)
     234  {
     235  }
     236  
     237  void
     238  g_cond_signal (GCond *cond)
     239  {
     240    WakeConditionVariable ((gpointer) cond);
     241  }
     242  
     243  void
     244  g_cond_broadcast (GCond *cond)
     245  {
     246    WakeAllConditionVariable ((gpointer) cond);
     247  }
     248  
     249  void
     250  g_cond_wait (GCond  *cond,
     251               GMutex *entered_mutex)
     252  {
     253    SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, INFINITE, 0);
     254  }
     255  
     256  gboolean
     257  g_cond_wait_until (GCond  *cond,
     258                     GMutex *entered_mutex,
     259                     gint64  end_time)
     260  {
     261    gint64 span, start_time;
     262    DWORD span_millis;
     263    gboolean signalled;
     264  
     265    start_time = g_get_monotonic_time ();
     266    do
     267      {
     268        span = end_time - start_time;
     269  
     270        if G_UNLIKELY (span < 0)
     271          span_millis = 0;
     272        else if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * (DWORD) INFINITE)
     273          span_millis = INFINITE;
     274        else
     275          /* Round up so we don't time out too early */
     276          span_millis = (span + 1000 - 1) / 1000;
     277  
     278        /* We never want to wait infinitely */
     279        if (span_millis >= INFINITE)
     280          span_millis = INFINITE - 1;
     281  
     282        signalled = SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, span_millis, 0);
     283        if (signalled)
     284          break;
     285  
     286        /* In case we didn't wait long enough after a timeout, wait again for the
     287         * remaining time */
     288        start_time = g_get_monotonic_time ();
     289      }
     290    while (start_time < end_time);
     291  
     292    return signalled;
     293  }
     294  
     295  /* {{{1 GPrivate */
     296  
     297  typedef struct _GPrivateDestructor GPrivateDestructor;
     298  
     299  struct _GPrivateDestructor
     300  {
     301    DWORD               index;
     302    GDestroyNotify      notify;
     303    GPrivateDestructor *next;
     304  };
     305  
     306  static GPrivateDestructor *g_private_destructors;  /* (atomic) prepend-only */
     307  static CRITICAL_SECTION g_private_lock;
     308  
     309  static DWORD
     310  g_private_get_impl (GPrivate *key)
     311  {
     312    DWORD impl = (DWORD) GPOINTER_TO_UINT(key->p);
     313  
     314    if G_UNLIKELY (impl == 0)
     315      {
     316        EnterCriticalSection (&g_private_lock);
     317        impl = (UINT_PTR) key->p;
     318        if (impl == 0)
     319          {
     320            GPrivateDestructor *destructor;
     321  
     322            impl = TlsAlloc ();
     323  
     324            if G_UNLIKELY (impl == 0)
     325              {
     326                /* Ignore TLS index 0 temporarily (as 0 is the indicator that we
     327                 * haven't allocated TLS yet) and alloc again;
     328                 * See https://gitlab.gnome.org/GNOME/glib/-/issues/2058 */
     329                DWORD impl2 = TlsAlloc ();
     330                TlsFree (impl);
     331                impl = impl2;
     332              }
     333  
     334            if (impl == TLS_OUT_OF_INDEXES || impl == 0)
     335              g_thread_abort (0, "TlsAlloc");
     336  
     337            if (key->notify != NULL)
     338              {
     339                destructor = malloc (sizeof (GPrivateDestructor));
     340                if G_UNLIKELY (destructor == NULL)
     341                  g_thread_abort (errno, "malloc");
     342                destructor->index = impl;
     343                destructor->notify = key->notify;
     344                destructor->next = g_atomic_pointer_get (&g_private_destructors);
     345  
     346                /* We need to do an atomic store due to the unlocked
     347                 * access to the destructor list from the thread exit
     348                 * function.
     349                 *
     350                 * It can double as a sanity check...
     351                 */
     352                if (!g_atomic_pointer_compare_and_exchange (&g_private_destructors,
     353                                                            destructor->next,
     354                                                            destructor))
     355                  g_thread_abort (0, "g_private_get_impl(1)");
     356              }
     357  
     358            /* Ditto, due to the unlocked access on the fast path */
     359            if (!g_atomic_pointer_compare_and_exchange (&key->p, NULL, GUINT_TO_POINTER (impl)))
     360              g_thread_abort (0, "g_private_get_impl(2)");
     361          }
     362        LeaveCriticalSection (&g_private_lock);
     363      }
     364  
     365    return impl;
     366  }
     367  
     368  gpointer
     369  g_private_get (GPrivate *key)
     370  {
     371    return TlsGetValue (g_private_get_impl (key));
     372  }
     373  
     374  void
     375  g_private_set (GPrivate *key,
     376                 gpointer  value)
     377  {
     378    TlsSetValue (g_private_get_impl (key), value);
     379  }
     380  
     381  void
     382  g_private_replace (GPrivate *key,
     383                     gpointer  value)
     384  {
     385    DWORD impl = g_private_get_impl (key);
     386    gpointer old;
     387  
     388    old = TlsGetValue (impl);
     389    TlsSetValue (impl, value);
     390    if (old && key->notify)
     391      key->notify (old);
     392  }
     393  
     394  /* {{{1 GThread */
     395  
     396  #define win32_check_for_error(what) G_STMT_START{			\
     397    if (!(what))								\
     398      g_error ("file %s: line %d (%s): error %s during %s",		\
     399  	     __FILE__, __LINE__, G_STRFUNC,				\
     400  	     g_win32_error_message (GetLastError ()), #what);		\
     401    }G_STMT_END
     402  
     403  #define G_MUTEX_SIZE (sizeof (gpointer))
     404  
     405  typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
     406  
     407  typedef struct
     408  {
     409    GRealThread thread;
     410  
     411    GThreadFunc proxy;
     412    HANDLE      handle;
     413  } GThreadWin32;
     414  
     415  void
     416  g_system_thread_free (GRealThread *thread)
     417  {
     418    GThreadWin32 *wt = (GThreadWin32 *) thread;
     419  
     420    win32_check_for_error (CloseHandle (wt->handle));
     421    g_slice_free (GThreadWin32, wt);
     422  }
     423  
     424  void
     425  g_system_thread_exit (void)
     426  {
     427    _endthreadex (0);
     428  }
     429  
     430  static guint __stdcall
     431  g_thread_win32_proxy (gpointer data)
     432  {
     433    GThreadWin32 *self = data;
     434  
     435    self->proxy (self);
     436  
     437    g_system_thread_exit ();
     438  
     439    g_assert_not_reached ();
     440  
     441    return 0;
     442  }
     443  
     444  GRealThread *
     445  g_system_thread_new (GThreadFunc proxy,
     446                       gulong stack_size,
     447                       const char *name,
     448                       GThreadFunc func,
     449                       gpointer data,
     450                       GError **error)
     451  {
     452    GThreadWin32 *thread;
     453    GRealThread *base_thread;
     454    guint ignore;
     455    const gchar *message = NULL;
     456    int thread_prio;
     457  
     458    thread = g_slice_new0 (GThreadWin32);
     459    thread->proxy = proxy;
     460    thread->handle = (HANDLE) NULL;
     461    base_thread = (GRealThread*)thread;
     462    base_thread->ref_count = 2;
     463    base_thread->ours = TRUE;
     464    base_thread->thread.joinable = TRUE;
     465    base_thread->thread.func = func;
     466    base_thread->thread.data = data;
     467    base_thread->name = g_strdup (name);
     468  
     469    thread->handle = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_win32_proxy, thread,
     470                                              CREATE_SUSPENDED, &ignore);
     471  
     472    if (thread->handle == NULL)
     473      {
     474        message = "Error creating thread";
     475        goto error;
     476      }
     477  
     478    /* For thread priority inheritance we need to manually set the thread
     479     * priority of the new thread to the priority of the current thread. We
     480     * also have to start the thread suspended and resume it after actually
     481     * setting the priority here.
     482     *
     483     * On Windows, by default all new threads are created with NORMAL thread
     484     * priority.
     485     */
     486    {
     487      HANDLE current_thread = GetCurrentThread ();
     488      thread_prio = GetThreadPriority (current_thread);
     489    }
     490  
     491    if (thread_prio == THREAD_PRIORITY_ERROR_RETURN)
     492      {
     493        message = "Error getting current thread priority";
     494        goto error;
     495      }
     496  
     497    if (SetThreadPriority (thread->handle, thread_prio) == 0)
     498      {
     499        message = "Error setting new thread priority";
     500        goto error;
     501      }
     502  
     503    if (ResumeThread (thread->handle) == (DWORD) -1)
     504      {
     505        message = "Error resuming new thread";
     506        goto error;
     507      }
     508  
     509    return (GRealThread *) thread;
     510  
     511  error:
     512    {
     513      gchar *win_error = g_win32_error_message (GetLastError ());
     514      g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
     515                   "%s: %s", message, win_error);
     516      g_free (win_error);
     517      if (thread->handle)
     518        CloseHandle (thread->handle);
     519      g_slice_free (GThreadWin32, thread);
     520      return NULL;
     521    }
     522  }
     523  
     524  void
     525  g_thread_yield (void)
     526  {
     527    Sleep(0);
     528  }
     529  
     530  void
     531  g_system_thread_wait (GRealThread *thread)
     532  {
     533    GThreadWin32 *wt = (GThreadWin32 *) thread;
     534  
     535    win32_check_for_error (WAIT_FAILED != WaitForSingleObject (wt->handle, INFINITE));
     536  }
     537  
     538  #define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388)
     539  
     540  #ifndef _MSC_VER
     541  static void *SetThreadName_VEH_handle = NULL;
     542  
     543  static LONG __stdcall
     544  SetThreadName_VEH (PEXCEPTION_POINTERS ExceptionInfo)
     545  {
     546    if (ExceptionInfo->ExceptionRecord != NULL &&
     547        ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME)
     548      return EXCEPTION_CONTINUE_EXECUTION;
     549  
     550    return EXCEPTION_CONTINUE_SEARCH;
     551  }
     552  #endif
     553  
     554  typedef struct _THREADNAME_INFO
     555  {
     556    DWORD  dwType;	/* must be 0x1000 */
     557    LPCSTR szName;	/* pointer to name (in user addr space) */
     558    DWORD  dwThreadID;	/* thread ID (-1=caller thread) */
     559    DWORD  dwFlags;	/* reserved for future use, must be zero */
     560  } THREADNAME_INFO;
     561  
     562  static void
     563  SetThreadName (DWORD  dwThreadID,
     564                 LPCSTR szThreadName)
     565  {
     566     THREADNAME_INFO info;
     567     DWORD infosize;
     568  
     569     info.dwType = 0x1000;
     570     info.szName = szThreadName;
     571     info.dwThreadID = dwThreadID;
     572     info.dwFlags = 0;
     573  
     574     infosize = sizeof (info) / sizeof (ULONG_PTR);
     575  
     576  #ifdef _MSC_VER
     577     __try
     578       {
     579         RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize,
     580                         (const ULONG_PTR *) &info);
     581       }
     582     __except (GetExceptionCode () == EXCEPTION_SET_THREAD_NAME ?
     583               EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
     584       {
     585       }
     586  #else
     587     if ((!IsDebuggerPresent ()) || (SetThreadName_VEH_handle == NULL))
     588       return;
     589  
     590     RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (const ULONG_PTR *) &info);
     591  #endif
     592  }
     593  
     594  typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread,
     595                                                   PCWSTR lpThreadDescription);
     596  static pSetThreadDescription SetThreadDescriptionFunc = NULL;
     597  static HMODULE kernel32_module = NULL;
     598  
     599  static gboolean
     600  g_thread_win32_load_library (void)
     601  {
     602    /* FIXME: Add support for UWP app */
     603  #if !defined(G_WINAPI_ONLY_APP)
     604    static gsize _init_once = 0;
     605    if (g_once_init_enter (&_init_once))
     606      {
     607        kernel32_module = LoadLibraryW (L"kernel32.dll");
     608        if (kernel32_module)
     609          {
     610            SetThreadDescriptionFunc =
     611                (pSetThreadDescription) GetProcAddress (kernel32_module,
     612                                                        "SetThreadDescription");
     613            if (!SetThreadDescriptionFunc)
     614              FreeLibrary (kernel32_module);
     615          }
     616        g_once_init_leave (&_init_once, 1);
     617      }
     618  #endif
     619  
     620    return !!SetThreadDescriptionFunc;
     621  }
     622  
     623  static gboolean
     624  g_thread_win32_set_thread_desc (const gchar *name)
     625  {
     626    HRESULT hr;
     627    wchar_t *namew;
     628  
     629    if (!g_thread_win32_load_library () || !name)
     630      return FALSE;
     631  
     632    namew = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL);
     633    if (!namew)
     634      return FALSE;
     635  
     636    hr = SetThreadDescriptionFunc (GetCurrentThread (), namew);
     637  
     638    g_free (namew);
     639    return SUCCEEDED (hr);
     640  }
     641  
     642  void
     643  g_system_thread_set_name (const gchar *name)
     644  {
     645    /* Prefer SetThreadDescription over exception based way if available,
     646     * since thread description set by SetThreadDescription will be preserved
     647     * in dump file */
     648    if (!g_thread_win32_set_thread_desc (name))
     649      SetThreadName ((DWORD) -1, name);
     650  }
     651  
     652  /* {{{1 Epilogue */
     653  
     654  void
     655  g_thread_win32_init (void)
     656  {
     657    InitializeCriticalSection (&g_private_lock);
     658  
     659  #ifndef _MSC_VER
     660    /* Set the handler as last to not interfere with ASAN runtimes.
     661     * Many ASAN implementations (currently all three of GCC, CLANG
     662     * and MSVC) install a Vectored Exception Handler that must be
     663     * first in the sequence to work well
     664     */
     665    SetThreadName_VEH_handle = AddVectoredExceptionHandler (0, &SetThreadName_VEH);
     666    if (SetThreadName_VEH_handle == NULL)
     667      g_critical ("%s failed with error code %u",
     668                  "AddVectoredExceptionHandler", (unsigned int) GetLastError ());
     669  #endif
     670  }
     671  
     672  void
     673  g_thread_win32_thread_detach (void)
     674  {
     675    gboolean dtors_called;
     676  
     677    do
     678      {
     679        GPrivateDestructor *dtor;
     680  
     681        /* We go by the POSIX book on this one.
     682         *
     683         * If we call a destructor then there is a chance that some new
     684         * TLS variables got set by code called in that destructor.
     685         *
     686         * Loop until nothing is left.
     687         */
     688        dtors_called = FALSE;
     689  
     690        for (dtor = g_atomic_pointer_get (&g_private_destructors); dtor; dtor = dtor->next)
     691          {
     692            gpointer value;
     693  
     694            value = TlsGetValue (dtor->index);
     695            if (value != NULL && dtor->notify != NULL)
     696              {
     697                /* POSIX says to clear this before the call */
     698                TlsSetValue (dtor->index, NULL);
     699                dtor->notify (value);
     700                dtors_called = TRUE;
     701              }
     702          }
     703      }
     704    while (dtors_called);
     705  }
     706  
     707  void
     708  g_thread_win32_process_detach (void)
     709  {
     710  #ifndef _MSC_VER
     711    if (SetThreadName_VEH_handle != NULL)
     712      {
     713        RemoveVectoredExceptionHandler (SetThreadName_VEH_handle);
     714        SetThreadName_VEH_handle = NULL;
     715      }
     716  #endif
     717  }
     718  
     719  /* vim:set foldmethod=marker: */