1  /* libxml2 - Library for parsing XML documents
       2   * Copyright (C) 2006-2019 Free Software Foundation, Inc.
       3   *
       4   * This file is not part of the GNU gettext program, but is used with
       5   * GNU gettext.
       6   *
       7   * The original copyright notice is as follows:
       8   */
       9  
      10  /*
      11   * Copyright (C) 1998-2012 Daniel Veillard.  All Rights Reserved.
      12   *
      13   * Permission is hereby granted, free of charge, to any person obtaining a copy
      14   * of this software and associated documentation files (the "Software"), to deal
      15   * in the Software without restriction, including without limitation the rights
      16   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      17   * copies of the Software, and to permit persons to whom the Software is fur-
      18   * nished to do so, subject to the following conditions:
      19   *
      20   * The above copyright notice and this permission notice shall be included in
      21   * all copies or substantial portions of the Software.
      22   *
      23   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      24   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
      25   * NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
      26   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      27   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      28   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      29   * THE SOFTWARE.
      30   *
      31   * Gary Pennington <Gary.Pennington@uk.sun.com>
      32   * daniel@veillard.com
      33   */
      34  
      35  /**
      36   * threads.c: set of generic threading related routines
      37   */
      38  
      39  #define IN_LIBXML
      40  #include "libxml.h"
      41  
      42  #include <string.h>
      43  
      44  #include <libxml/threads.h>
      45  #include <libxml/globals.h>
      46  
      47  #ifdef HAVE_SYS_TYPES_H
      48  #include <sys/types.h>
      49  #endif
      50  #ifdef HAVE_UNISTD_H
      51  #include <unistd.h>
      52  #endif
      53  #ifdef HAVE_STDLIB_H
      54  #include <stdlib.h>
      55  #endif
      56  #ifdef HAVE_PTHREAD_H
      57  #include <pthread.h>
      58  #elif defined HAVE_WIN32_THREADS
      59  #define WIN32_LEAN_AND_MEAN
      60  #include <windows.h>
      61  #ifndef HAVE_COMPILER_TLS
      62  #include <process.h>
      63  #endif
      64  #endif
      65  
      66  #ifdef HAVE_BEOS_THREADS
      67  #include <OS.h>
      68  #include <TLS.h>
      69  #endif
      70  
      71  #if defined(SOLARIS)
      72  #include <note.h>
      73  #endif
      74  
      75  /* #define DEBUG_THREADS */
      76  
      77  #ifdef LIBXML_THREAD_ENABLED
      78  #ifdef HAVE_PTHREAD_H
      79  
      80  #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 303) && \
      81      defined(__GLIBC__) && defined(__linux__)
      82  
      83  static int libxml_is_threaded = -1;
      84  
      85  #define XML_PTHREAD_WEAK
      86  
      87  #pragma weak pthread_once
      88  #pragma weak pthread_getspecific
      89  #pragma weak pthread_setspecific
      90  #pragma weak pthread_key_create
      91  #pragma weak pthread_key_delete
      92  #pragma weak pthread_mutex_init
      93  #pragma weak pthread_mutex_destroy
      94  #pragma weak pthread_mutex_lock
      95  #pragma weak pthread_mutex_unlock
      96  #pragma weak pthread_cond_init
      97  #pragma weak pthread_cond_destroy
      98  #pragma weak pthread_cond_wait
      99  #pragma weak pthread_equal
     100  #pragma weak pthread_self
     101  #pragma weak pthread_key_create
     102  #pragma weak pthread_key_delete
     103  #pragma weak pthread_cond_signal
     104  
     105  #else /* __GNUC__, __GLIBC__, __linux__ */
     106  
     107  static int libxml_is_threaded = 1;
     108  
     109  #endif /* __GNUC__, __GLIBC__, __linux__ */
     110  
     111  #endif /* HAVE_PTHREAD_H */
     112  #endif /* LIBXML_THREAD_ENABLED */
     113  
     114  /*
     115   * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
     116   *       to avoid some crazyness since xmlMalloc/xmlFree may actually
     117   *       be hosted on allocated blocks needing them for the allocation ...
     118   */
     119  
     120  /*
     121   * xmlMutex are a simple mutual exception locks
     122   */
     123  struct _xmlMutex {
     124  #ifdef LIBXML_THREAD_ENABLED
     125  #ifdef HAVE_PTHREAD_H
     126      pthread_mutex_t lock;
     127  #elif defined HAVE_WIN32_THREADS
     128      HANDLE mutex;
     129  #elif defined HAVE_BEOS_THREADS
     130      sem_id sem;
     131      thread_id tid;
     132  #else
     133      int empty;
     134  #endif
     135  #else
     136      int empty;
     137  #endif
     138  };
     139  
     140  /*
     141   * xmlRMutex are reentrant mutual exception locks
     142   */
     143  struct _xmlRMutex {
     144  #ifdef LIBXML_THREAD_ENABLED
     145  #ifdef HAVE_PTHREAD_H
     146      pthread_mutex_t lock;
     147      unsigned int held;
     148      unsigned int waiters;
     149      pthread_t tid;
     150      pthread_cond_t cv;
     151  #elif defined HAVE_WIN32_THREADS
     152      CRITICAL_SECTION cs;
     153      unsigned int count;
     154  #elif defined HAVE_BEOS_THREADS
     155      xmlMutexPtr lock;
     156      thread_id tid;
     157      int32 count;
     158  #else
     159      int empty;
     160  #endif
     161  #else
     162      int empty;
     163  #endif
     164  };
     165  
     166  /*
     167   * This module still has some internal static data.
     168   *   - xmlLibraryLock a global lock
     169   *   - globalkey used for per-thread data
     170   */
     171  
     172  #ifdef LIBXML_THREAD_ENABLED
     173  #ifdef HAVE_PTHREAD_H
     174  static pthread_key_t globalkey;
     175  static pthread_t mainthread;
     176  static pthread_once_t once_control = PTHREAD_ONCE_INIT;
     177  static pthread_once_t once_control_init = PTHREAD_ONCE_INIT;
     178  static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
     179  #elif defined HAVE_WIN32_THREADS
     180  #if defined(HAVE_COMPILER_TLS)
     181  static __declspec(thread) xmlGlobalState tlstate;
     182  static __declspec(thread) int tlstate_inited = 0;
     183  #else /* HAVE_COMPILER_TLS */
     184  static DWORD globalkey = TLS_OUT_OF_INDEXES;
     185  #endif /* HAVE_COMPILER_TLS */
     186  static DWORD mainthread;
     187  static struct {
     188      DWORD done;
     189      LONG control;
     190  } run_once = { 0, 0};
     191  static volatile LPCRITICAL_SECTION global_init_lock = NULL;
     192  
     193  /* endif HAVE_WIN32_THREADS */
     194  #elif defined HAVE_BEOS_THREADS
     195  int32 globalkey = 0;
     196  thread_id mainthread = 0;
     197  int32 run_once_init = 0;
     198  static int32 global_init_lock = -1;
     199  static vint32 global_init_count = 0;
     200  #endif
     201  #endif
     202  
     203  static xmlRMutexPtr xmlLibraryLock = NULL;
     204  
     205  #ifdef LIBXML_THREAD_ENABLED
     206  static void xmlOnceInit(void);
     207  #endif
     208  
     209  /**
     210   * xmlNewMutex:
     211   *
     212   * xmlNewMutex() is used to allocate a libxml2 token struct for use in
     213   * synchronizing access to data.
     214   *
     215   * Returns a new simple mutex pointer or NULL in case of error
     216   */
     217  xmlMutexPtr
     218  xmlNewMutex(void)
     219  {
     220      xmlMutexPtr tok;
     221  
     222      if ((tok = malloc(sizeof(xmlMutex))) == NULL)
     223          return (NULL);
     224  #ifdef LIBXML_THREAD_ENABLED
     225  #ifdef HAVE_PTHREAD_H
     226      if (libxml_is_threaded != 0)
     227          pthread_mutex_init(&tok->lock, NULL);
     228  #elif defined HAVE_WIN32_THREADS
     229      tok->mutex = CreateMutex(NULL, FALSE, NULL);
     230  #elif defined HAVE_BEOS_THREADS
     231      if ((tok->sem = create_sem(1, "xmlMutex")) < B_OK) {
     232          free(tok);
     233          return NULL;
     234      }
     235      tok->tid = -1;
     236  #endif
     237  #endif
     238      return (tok);
     239  }
     240  
     241  /**
     242   * xmlFreeMutex:
     243   * @tok:  the simple mutex
     244   *
     245   * xmlFreeMutex() is used to reclaim resources associated with a libxml2 token
     246   * struct.
     247   */
     248  void
     249  xmlFreeMutex(xmlMutexPtr tok)
     250  {
     251      if (tok == NULL)
     252          return;
     253  
     254  #ifdef LIBXML_THREAD_ENABLED
     255  #ifdef HAVE_PTHREAD_H
     256      if (libxml_is_threaded != 0)
     257          pthread_mutex_destroy(&tok->lock);
     258  #elif defined HAVE_WIN32_THREADS
     259      CloseHandle(tok->mutex);
     260  #elif defined HAVE_BEOS_THREADS
     261      delete_sem(tok->sem);
     262  #endif
     263  #endif
     264      free(tok);
     265  }
     266  
     267  /**
     268   * xmlMutexLock:
     269   * @tok:  the simple mutex
     270   *
     271   * xmlMutexLock() is used to lock a libxml2 token.
     272   */
     273  void
     274  xmlMutexLock(xmlMutexPtr tok)
     275  {
     276      if (tok == NULL)
     277          return;
     278  #ifdef LIBXML_THREAD_ENABLED
     279  #ifdef HAVE_PTHREAD_H
     280      if (libxml_is_threaded != 0)
     281          pthread_mutex_lock(&tok->lock);
     282  #elif defined HAVE_WIN32_THREADS
     283      WaitForSingleObject(tok->mutex, INFINITE);
     284  #elif defined HAVE_BEOS_THREADS
     285      if (acquire_sem(tok->sem) != B_NO_ERROR) {
     286  #ifdef DEBUG_THREADS
     287          xmlGenericError(xmlGenericErrorContext,
     288                          "xmlMutexLock():BeOS:Couldn't aquire semaphore\n");
     289  #endif
     290      }
     291      tok->tid = find_thread(NULL);
     292  #endif
     293  #endif
     294  }
     295  
     296  /**
     297   * xmlMutexUnlock:
     298   * @tok:  the simple mutex
     299   *
     300   * xmlMutexUnlock() is used to unlock a libxml2 token.
     301   */
     302  void
     303  xmlMutexUnlock(xmlMutexPtr tok)
     304  {
     305      if (tok == NULL)
     306          return;
     307  #ifdef LIBXML_THREAD_ENABLED
     308  #ifdef HAVE_PTHREAD_H
     309      if (libxml_is_threaded != 0)
     310          pthread_mutex_unlock(&tok->lock);
     311  #elif defined HAVE_WIN32_THREADS
     312      ReleaseMutex(tok->mutex);
     313  #elif defined HAVE_BEOS_THREADS
     314      if (tok->tid == find_thread(NULL)) {
     315          tok->tid = -1;
     316          release_sem(tok->sem);
     317      }
     318  #endif
     319  #endif
     320  }
     321  
     322  /**
     323   * xmlNewRMutex:
     324   *
     325   * xmlRNewMutex() is used to allocate a reentrant mutex for use in
     326   * synchronizing access to data. token_r is a re-entrant lock and thus useful
     327   * for synchronizing access to data structures that may be manipulated in a
     328   * recursive fashion.
     329   *
     330   * Returns the new reentrant mutex pointer or NULL in case of error
     331   */
     332  xmlRMutexPtr
     333  xmlNewRMutex(void)
     334  {
     335      xmlRMutexPtr tok;
     336  
     337      if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
     338          return (NULL);
     339  #ifdef LIBXML_THREAD_ENABLED
     340  #ifdef HAVE_PTHREAD_H
     341      if (libxml_is_threaded != 0) {
     342          pthread_mutex_init(&tok->lock, NULL);
     343          tok->held = 0;
     344          tok->waiters = 0;
     345          pthread_cond_init(&tok->cv, NULL);
     346      }
     347  #elif defined HAVE_WIN32_THREADS
     348      InitializeCriticalSection(&tok->cs);
     349      tok->count = 0;
     350  #elif defined HAVE_BEOS_THREADS
     351      if ((tok->lock = xmlNewMutex()) == NULL) {
     352          free(tok);
     353          return NULL;
     354      }
     355      tok->count = 0;
     356  #endif
     357  #endif
     358      return (tok);
     359  }
     360  
     361  /**
     362   * xmlFreeRMutex:
     363   * @tok:  the reentrant mutex
     364   *
     365   * xmlRFreeMutex() is used to reclaim resources associated with a
     366   * reentrant mutex.
     367   */
     368  void
     369  xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
     370  {
     371      if (tok == NULL)
     372          return;
     373  #ifdef LIBXML_THREAD_ENABLED
     374  #ifdef HAVE_PTHREAD_H
     375      if (libxml_is_threaded != 0) {
     376          pthread_mutex_destroy(&tok->lock);
     377          pthread_cond_destroy(&tok->cv);
     378      }
     379  #elif defined HAVE_WIN32_THREADS
     380      DeleteCriticalSection(&tok->cs);
     381  #elif defined HAVE_BEOS_THREADS
     382      xmlFreeMutex(tok->lock);
     383  #endif
     384  #endif
     385      free(tok);
     386  }
     387  
     388  /**
     389   * xmlRMutexLock:
     390   * @tok:  the reentrant mutex
     391   *
     392   * xmlRMutexLock() is used to lock a libxml2 token_r.
     393   */
     394  void
     395  xmlRMutexLock(xmlRMutexPtr tok)
     396  {
     397      if (tok == NULL)
     398          return;
     399  #ifdef LIBXML_THREAD_ENABLED
     400  #ifdef HAVE_PTHREAD_H
     401      if (libxml_is_threaded == 0)
     402          return;
     403  
     404      pthread_mutex_lock(&tok->lock);
     405      if (tok->held) {
     406          if (pthread_equal(tok->tid, pthread_self())) {
     407              tok->held++;
     408              pthread_mutex_unlock(&tok->lock);
     409              return;
     410          } else {
     411              tok->waiters++;
     412              while (tok->held)
     413                  pthread_cond_wait(&tok->cv, &tok->lock);
     414              tok->waiters--;
     415          }
     416      }
     417      tok->tid = pthread_self();
     418      tok->held = 1;
     419      pthread_mutex_unlock(&tok->lock);
     420  #elif defined HAVE_WIN32_THREADS
     421      EnterCriticalSection(&tok->cs);
     422      tok->count++;
     423  #elif defined HAVE_BEOS_THREADS
     424      if (tok->lock->tid == find_thread(NULL)) {
     425          tok->count++;
     426          return;
     427      } else {
     428          xmlMutexLock(tok->lock);
     429          tok->count = 1;
     430      }
     431  #endif
     432  #endif
     433  }
     434  
     435  /**
     436   * xmlRMutexUnlock:
     437   * @tok:  the reentrant mutex
     438   *
     439   * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
     440   */
     441  void
     442  xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
     443  {
     444      if (tok == NULL)
     445          return;
     446  #ifdef LIBXML_THREAD_ENABLED
     447  #ifdef HAVE_PTHREAD_H
     448      if (libxml_is_threaded == 0)
     449          return;
     450  
     451      pthread_mutex_lock(&tok->lock);
     452      tok->held--;
     453      if (tok->held == 0) {
     454          if (tok->waiters)
     455              pthread_cond_signal(&tok->cv);
     456          memset(&tok->tid, 0, sizeof(tok->tid));
     457      }
     458      pthread_mutex_unlock(&tok->lock);
     459  #elif defined HAVE_WIN32_THREADS
     460      if (tok->count > 0) {
     461  	tok->count--;
     462          LeaveCriticalSection(&tok->cs);
     463      }
     464  #elif defined HAVE_BEOS_THREADS
     465      if (tok->lock->tid == find_thread(NULL)) {
     466          tok->count--;
     467          if (tok->count == 0) {
     468              xmlMutexUnlock(tok->lock);
     469          }
     470          return;
     471      }
     472  #endif
     473  #endif
     474  }
     475  
     476  /**
     477   * xmlGlobalInitMutexLock
     478   *
     479   * Makes sure that the global initialization mutex is initialized and
     480   * locks it.
     481   */
     482  void
     483  __xmlGlobalInitMutexLock(void)
     484  {
     485      /* Make sure the global init lock is initialized and then lock it. */
     486  #ifdef LIBXML_THREAD_ENABLED
     487  #ifdef HAVE_PTHREAD_H
     488      /* The mutex is statically initialized, so we just lock it. */
     489  #ifdef XML_PTHREAD_WEAK
     490      if (pthread_mutex_lock == NULL)
     491          return;
     492  #endif /* XML_PTHREAD_WEAK */
     493      pthread_mutex_lock(&global_init_lock);
     494  #elif defined HAVE_WIN32_THREADS
     495      LPCRITICAL_SECTION cs;
     496  
     497      /* Create a new critical section */
     498      if (global_init_lock == NULL) {
     499          cs = malloc(sizeof(CRITICAL_SECTION));
     500          if (cs == NULL) {
     501              xmlGenericError(xmlGenericErrorContext,
     502                              "xmlGlobalInitMutexLock: out of memory\n");
     503              return;
     504          }
     505          InitializeCriticalSection(cs);
     506  
     507          /* Swap it into the global_init_lock */
     508  #ifdef InterlockedCompareExchangePointer
     509          InterlockedCompareExchangePointer((void **) &global_init_lock,
     510                                            cs, NULL);
     511  #else /* Use older void* version */
     512          InterlockedCompareExchange((void **) &global_init_lock,
     513                                     (void *) cs, NULL);
     514  #endif /* InterlockedCompareExchangePointer */
     515  
     516          /* If another thread successfully recorded its critical
     517           * section in the global_init_lock then discard the one
     518           * allocated by this thread. */
     519          if (global_init_lock != cs) {
     520              DeleteCriticalSection(cs);
     521              free(cs);
     522          }
     523      }
     524  
     525      /* Lock the chosen critical section */
     526      EnterCriticalSection(global_init_lock);
     527  #elif defined HAVE_BEOS_THREADS
     528      int32 sem;
     529  
     530      /* Allocate a new semaphore */
     531      sem = create_sem(1, "xmlGlobalinitMutex");
     532  
     533      while (global_init_lock == -1) {
     534          if (atomic_add(&global_init_count, 1) == 0) {
     535              global_init_lock = sem;
     536          } else {
     537              snooze(1);
     538              atomic_add(&global_init_count, -1);
     539          }
     540      }
     541  
     542      /* If another thread successfully recorded its critical
     543       * section in the global_init_lock then discard the one
     544       * allocated by this thread. */
     545      if (global_init_lock != sem)
     546          delete_sem(sem);
     547  
     548      /* Acquire the chosen semaphore */
     549      if (acquire_sem(global_init_lock) != B_NO_ERROR) {
     550  #ifdef DEBUG_THREADS
     551          xmlGenericError(xmlGenericErrorContext,
     552                          "xmlGlobalInitMutexLock():BeOS:Couldn't acquire semaphore\n");
     553  #endif
     554      }
     555  #endif
     556  #endif
     557  }
     558  
     559  void
     560  __xmlGlobalInitMutexUnlock(void)
     561  {
     562  #ifdef LIBXML_THREAD_ENABLED
     563  #ifdef HAVE_PTHREAD_H
     564  #ifdef XML_PTHREAD_WEAK
     565      if (pthread_mutex_unlock == NULL)
     566          return;
     567  #endif /* XML_PTHREAD_WEAK */
     568      pthread_mutex_unlock(&global_init_lock);
     569  #elif defined HAVE_WIN32_THREADS
     570      if (global_init_lock != NULL) {
     571  	LeaveCriticalSection(global_init_lock);
     572      }
     573  #elif defined HAVE_BEOS_THREADS
     574      release_sem(global_init_lock);
     575  #endif
     576  #endif
     577  }
     578  
     579  /**
     580   * xmlGlobalInitMutexDestroy
     581   *
     582   * Makes sure that the global initialization mutex is destroyed before
     583   * application termination.
     584   */
     585  void
     586  __xmlGlobalInitMutexDestroy(void)
     587  {
     588  #ifdef LIBXML_THREAD_ENABLED
     589  #ifdef HAVE_PTHREAD_H
     590  #elif defined HAVE_WIN32_THREADS
     591      if (global_init_lock != NULL) {
     592          DeleteCriticalSection(global_init_lock);
     593          free(global_init_lock);
     594          global_init_lock = NULL;
     595      }
     596  #endif
     597  #endif
     598  }
     599  
     600  /************************************************************************
     601   *									*
     602   *			Per thread global state handling		*
     603   *									*
     604   ************************************************************************/
     605  
     606  #ifdef LIBXML_THREAD_ENABLED
     607  #ifdef xmlLastError
     608  #undef xmlLastError
     609  #endif
     610  
     611  /**
     612   * xmlFreeGlobalState:
     613   * @state:  a thread global state
     614   *
     615   * xmlFreeGlobalState() is called when a thread terminates with a non-NULL
     616   * global state. It is is used here to reclaim memory resources.
     617   */
     618  static void
     619  xmlFreeGlobalState(void *state)
     620  {
     621      xmlGlobalState *gs = (xmlGlobalState *) state;
     622  
     623      /* free any memory allocated in the thread's xmlLastError */
     624      xmlResetError(&(gs->xmlLastError));
     625      free(state);
     626  }
     627  
     628  /**
     629   * xmlNewGlobalState:
     630   *
     631   * xmlNewGlobalState() allocates a global state. This structure is used to
     632   * hold all data for use by a thread when supporting backwards compatibility
     633   * of libxml2 to pre-thread-safe behaviour.
     634   *
     635   * Returns the newly allocated xmlGlobalStatePtr or NULL in case of error
     636   */
     637  static xmlGlobalStatePtr
     638  xmlNewGlobalState(void)
     639  {
     640      xmlGlobalState *gs;
     641  
     642      gs = malloc(sizeof(xmlGlobalState));
     643      if (gs == NULL) {
     644  	xmlGenericError(xmlGenericErrorContext,
     645  			"xmlGetGlobalState: out of memory\n");
     646          return (NULL);
     647      }
     648  
     649      memset(gs, 0, sizeof(xmlGlobalState));
     650      xmlInitializeGlobalState(gs);
     651      return (gs);
     652  }
     653  #endif /* LIBXML_THREAD_ENABLED */
     654  
     655  #ifdef LIBXML_THREAD_ENABLED
     656  
     657  #ifdef HAVE_PTHREAD_H
     658  #elif defined HAVE_WIN32_THREADS
     659  #if !defined(HAVE_COMPILER_TLS)
     660  #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
     661  typedef struct _xmlGlobalStateCleanupHelperParams {
     662      HANDLE thread;
     663      void *memory;
     664  } xmlGlobalStateCleanupHelperParams;
     665  
     666  static void XMLCDECL
     667  xmlGlobalStateCleanupHelper(void *p)
     668  {
     669      xmlGlobalStateCleanupHelperParams *params =
     670          (xmlGlobalStateCleanupHelperParams *) p;
     671      WaitForSingleObject(params->thread, INFINITE);
     672      CloseHandle(params->thread);
     673      xmlFreeGlobalState(params->memory);
     674      free(params);
     675      _endthread();
     676  }
     677  #else /* LIBXML_STATIC && !LIBXML_STATIC_FOR_DLL */
     678  
     679  typedef struct _xmlGlobalStateCleanupHelperParams {
     680      void *memory;
     681      struct _xmlGlobalStateCleanupHelperParams *prev;
     682      struct _xmlGlobalStateCleanupHelperParams *next;
     683  } xmlGlobalStateCleanupHelperParams;
     684  
     685  static xmlGlobalStateCleanupHelperParams *cleanup_helpers_head = NULL;
     686  static CRITICAL_SECTION cleanup_helpers_cs;
     687  
     688  #endif /* LIBXMLSTATIC && !LIBXML_STATIC_FOR_DLL */
     689  #endif /* HAVE_COMPILER_TLS */
     690  #endif /* HAVE_WIN32_THREADS */
     691  
     692  #if defined HAVE_BEOS_THREADS
     693  
     694  /**
     695   * xmlGlobalStateCleanup:
     696   * @data: unused parameter
     697   *
     698   * Used for Beos only
     699   */
     700  void
     701  xmlGlobalStateCleanup(void *data)
     702  {
     703      void *globalval = tls_get(globalkey);
     704  
     705      if (globalval != NULL)
     706          xmlFreeGlobalState(globalval);
     707  }
     708  #endif
     709  
     710  #endif /* LIBXML_THREAD_ENABLED */
     711  
     712  /**
     713   * xmlGetGlobalState:
     714   *
     715   * xmlGetGlobalState() is called to retrieve the global state for a thread.
     716   *
     717   * Returns the thread global state or NULL in case of error
     718   */
     719  xmlGlobalStatePtr
     720  xmlGetGlobalState(void)
     721  {
     722  #ifdef LIBXML_THREAD_ENABLED
     723  #ifdef HAVE_PTHREAD_H
     724      xmlGlobalState *globalval;
     725  
     726      if (libxml_is_threaded == 0)
     727          return (NULL);
     728  
     729      pthread_once(&once_control, xmlOnceInit);
     730  
     731      if ((globalval = (xmlGlobalState *)
     732           pthread_getspecific(globalkey)) == NULL) {
     733          xmlGlobalState *tsd = xmlNewGlobalState();
     734  	if (tsd == NULL)
     735  	    return(NULL);
     736  
     737          pthread_setspecific(globalkey, tsd);
     738          return (tsd);
     739      }
     740      return (globalval);
     741  #elif defined HAVE_WIN32_THREADS
     742  #if defined(HAVE_COMPILER_TLS)
     743      if (!tlstate_inited) {
     744          tlstate_inited = 1;
     745          xmlInitializeGlobalState(&tlstate);
     746      }
     747      return &tlstate;
     748  #else /* HAVE_COMPILER_TLS */
     749      xmlGlobalState *globalval;
     750      xmlGlobalStateCleanupHelperParams *p;
     751  
     752      xmlOnceInit();
     753  #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
     754      globalval = (xmlGlobalState *) TlsGetValue(globalkey);
     755  #else
     756      p = (xmlGlobalStateCleanupHelperParams *) TlsGetValue(globalkey);
     757      globalval = (xmlGlobalState *) (p ? p->memory : NULL);
     758  #endif
     759      if (globalval == NULL) {
     760          xmlGlobalState *tsd = xmlNewGlobalState();
     761  
     762          if (tsd == NULL)
     763  	    return(NULL);
     764          p = (xmlGlobalStateCleanupHelperParams *)
     765              malloc(sizeof(xmlGlobalStateCleanupHelperParams));
     766  	if (p == NULL) {
     767              xmlGenericError(xmlGenericErrorContext,
     768                              "xmlGetGlobalState: out of memory\n");
     769              xmlFreeGlobalState(tsd);
     770  	    return(NULL);
     771  	}
     772          p->memory = tsd;
     773  #if defined(LIBXML_STATIC) && !defined(LIBXML_STATIC_FOR_DLL)
     774          DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
     775                          GetCurrentProcess(), &p->thread, 0, TRUE,
     776                          DUPLICATE_SAME_ACCESS);
     777          TlsSetValue(globalkey, tsd);
     778          _beginthread(xmlGlobalStateCleanupHelper, 0, p);
     779  #else
     780          EnterCriticalSection(&cleanup_helpers_cs);
     781          if (cleanup_helpers_head != NULL) {
     782              cleanup_helpers_head->prev = p;
     783          }
     784          p->next = cleanup_helpers_head;
     785          p->prev = NULL;
     786          cleanup_helpers_head = p;
     787          TlsSetValue(globalkey, p);
     788          LeaveCriticalSection(&cleanup_helpers_cs);
     789  #endif
     790  
     791          return (tsd);
     792      }
     793      return (globalval);
     794  #endif /* HAVE_COMPILER_TLS */
     795  #elif defined HAVE_BEOS_THREADS
     796      xmlGlobalState *globalval;
     797  
     798      xmlOnceInit();
     799  
     800      if ((globalval = (xmlGlobalState *) tls_get(globalkey)) == NULL) {
     801          xmlGlobalState *tsd = xmlNewGlobalState();
     802  	if (tsd == NULL)
     803  	    return (NULL);
     804  
     805          tls_set(globalkey, tsd);
     806          on_exit_thread(xmlGlobalStateCleanup, NULL);
     807          return (tsd);
     808      }
     809      return (globalval);
     810  #else
     811      return (NULL);
     812  #endif
     813  #else
     814      return (NULL);
     815  #endif
     816  }
     817  
     818  /************************************************************************
     819   *									*
     820   *			Library wide thread interfaces			*
     821   *									*
     822   ************************************************************************/
     823  
     824  /**
     825   * xmlGetThreadId:
     826   *
     827   * xmlGetThreadId() find the current thread ID number
     828   * Note that this is likely to be broken on some platforms using pthreads
     829   * as the specification doesn't mandate pthread_t to be an integer type
     830   *
     831   * Returns the current thread ID number
     832   */
     833  int
     834  xmlGetThreadId(void)
     835  {
     836  #ifdef LIBXML_THREAD_ENABLED
     837  #ifdef HAVE_PTHREAD_H
     838      pthread_t id;
     839      int ret;
     840  
     841      if (libxml_is_threaded == 0)
     842          return (0);
     843      id = pthread_self();
     844      /* horrible but preserves compat, see warning above */
     845      memcpy(&ret, &id, sizeof(ret));
     846      return (ret);
     847  #elif defined HAVE_WIN32_THREADS
     848      return GetCurrentThreadId();
     849  #elif defined HAVE_BEOS_THREADS
     850      return find_thread(NULL);
     851  #else
     852      return ((int) 0);
     853  #endif
     854  #else
     855      return ((int) 0);
     856  #endif
     857  }
     858  
     859  /**
     860   * xmlIsMainThread:
     861   *
     862   * xmlIsMainThread() check whether the current thread is the main thread.
     863   *
     864   * Returns 1 if the current thread is the main thread, 0 otherwise
     865   */
     866  int
     867  xmlIsMainThread(void)
     868  {
     869  #ifdef LIBXML_THREAD_ENABLED
     870  #ifdef HAVE_PTHREAD_H
     871      if (libxml_is_threaded == -1)
     872          xmlInitThreads();
     873      if (libxml_is_threaded == 0)
     874          return (1);
     875      pthread_once(&once_control, xmlOnceInit);
     876  #elif defined HAVE_WIN32_THREADS
     877      xmlOnceInit();
     878  #elif defined HAVE_BEOS_THREADS
     879      xmlOnceInit();
     880  #endif
     881  
     882  #ifdef DEBUG_THREADS
     883      xmlGenericError(xmlGenericErrorContext, "xmlIsMainThread()\n");
     884  #endif
     885  #ifdef HAVE_PTHREAD_H
     886      return (pthread_equal(mainthread,pthread_self()));
     887  #elif defined HAVE_WIN32_THREADS
     888      return (mainthread == GetCurrentThreadId());
     889  #elif defined HAVE_BEOS_THREADS
     890      return (mainthread == find_thread(NULL));
     891  #else
     892      return (1);
     893  #endif
     894  #else
     895      return (1);
     896  #endif
     897  }
     898  
     899  /**
     900   * xmlLockLibrary:
     901   *
     902   * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
     903   * library.
     904   */
     905  void
     906  xmlLockLibrary(void)
     907  {
     908  #ifdef DEBUG_THREADS
     909      xmlGenericError(xmlGenericErrorContext, "xmlLockLibrary()\n");
     910  #endif
     911      xmlRMutexLock(xmlLibraryLock);
     912  }
     913  
     914  /**
     915   * xmlUnlockLibrary:
     916   *
     917   * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
     918   * library.
     919   */
     920  void
     921  xmlUnlockLibrary(void)
     922  {
     923  #ifdef DEBUG_THREADS
     924      xmlGenericError(xmlGenericErrorContext, "xmlUnlockLibrary()\n");
     925  #endif
     926      xmlRMutexUnlock(xmlLibraryLock);
     927  }
     928  
     929  /**
     930   * xmlInitThreads:
     931   *
     932   * xmlInitThreads() is used to to initialize all the thread related
     933   * data of the libxml2 library.
     934   */
     935  void
     936  xmlInitThreads(void)
     937  {
     938  #ifdef LIBXML_THREAD_ENABLED
     939  #ifdef HAVE_PTHREAD_H
     940  #ifdef XML_PTHREAD_WEAK
     941      if (libxml_is_threaded == -1) {
     942          if ((pthread_once != NULL) &&
     943              (pthread_getspecific != NULL) &&
     944              (pthread_setspecific != NULL) &&
     945              (pthread_key_create != NULL) &&
     946              (pthread_key_delete != NULL) &&
     947              (pthread_mutex_init != NULL) &&
     948              (pthread_mutex_destroy != NULL) &&
     949              (pthread_mutex_lock != NULL) &&
     950              (pthread_mutex_unlock != NULL) &&
     951              (pthread_cond_init != NULL) &&
     952              (pthread_cond_destroy != NULL) &&
     953              (pthread_cond_wait != NULL) &&
     954              (pthread_equal != NULL) &&
     955              (pthread_self != NULL) &&
     956              (pthread_cond_signal != NULL)) {
     957              libxml_is_threaded = 1;
     958  
     959  /* fprintf(stderr, "Running multithreaded\n"); */
     960          } else {
     961  
     962  /* fprintf(stderr, "Running without multithread\n"); */
     963              libxml_is_threaded = 0;
     964          }
     965      }
     966  #endif /* XML_PTHREAD_WEAK */
     967  #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
     968      InitializeCriticalSection(&cleanup_helpers_cs);
     969  #endif
     970  #endif
     971  }
     972  
     973  /**
     974   * xmlCleanupThreads:
     975   *
     976   * xmlCleanupThreads() is used to to cleanup all the thread related
     977   * data of the libxml2 library once processing has ended.
     978   *
     979   * WARNING: if your application is multithreaded or has plugin support
     980   *          calling this may crash the application if another thread or
     981   *          a plugin is still using libxml2. It's sometimes very hard to
     982   *          guess if libxml2 is in use in the application, some libraries
     983   *          or plugins may use it without notice. In case of doubt abstain
     984   *          from calling this function or do it just before calling exit()
     985   *          to avoid leak reports from valgrind !
     986   */
     987  void
     988  xmlCleanupThreads(void)
     989  {
     990  #ifdef DEBUG_THREADS
     991      xmlGenericError(xmlGenericErrorContext, "xmlCleanupThreads()\n");
     992  #endif
     993  #ifdef LIBXML_THREAD_ENABLED
     994  #ifdef HAVE_PTHREAD_H
     995      if (libxml_is_threaded != 0)
     996          pthread_key_delete(globalkey);
     997      once_control = once_control_init;
     998  #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
     999      if (globalkey != TLS_OUT_OF_INDEXES) {
    1000          xmlGlobalStateCleanupHelperParams *p;
    1001  
    1002          EnterCriticalSection(&cleanup_helpers_cs);
    1003          p = cleanup_helpers_head;
    1004          while (p != NULL) {
    1005              xmlGlobalStateCleanupHelperParams *temp = p;
    1006  
    1007              p = p->next;
    1008              xmlFreeGlobalState(temp->memory);
    1009              free(temp);
    1010          }
    1011          cleanup_helpers_head = 0;
    1012          LeaveCriticalSection(&cleanup_helpers_cs);
    1013          TlsFree(globalkey);
    1014          globalkey = TLS_OUT_OF_INDEXES;
    1015      }
    1016      DeleteCriticalSection(&cleanup_helpers_cs);
    1017  #endif
    1018  #endif
    1019  }
    1020  
    1021  #ifdef LIBXML_THREAD_ENABLED
    1022  
    1023  /**
    1024   * xmlOnceInit
    1025   *
    1026   * xmlOnceInit() is used to initialize the value of mainthread for use
    1027   * in other routines. This function should only be called using
    1028   * pthread_once() in association with the once_control variable to ensure
    1029   * that the function is only called once. See man pthread_once for more
    1030   * details.
    1031   */
    1032  static void
    1033  xmlOnceInit(void)
    1034  {
    1035  #ifdef HAVE_PTHREAD_H
    1036      (void) pthread_key_create(&globalkey, xmlFreeGlobalState);
    1037      mainthread = pthread_self();
    1038      __xmlInitializeDict();
    1039  #elif defined(HAVE_WIN32_THREADS)
    1040      if (!run_once.done) {
    1041          if (InterlockedIncrement(&run_once.control) == 1) {
    1042  #if !defined(HAVE_COMPILER_TLS)
    1043              globalkey = TlsAlloc();
    1044  #endif
    1045              mainthread = GetCurrentThreadId();
    1046  	    __xmlInitializeDict();
    1047              run_once.done = 1;
    1048          } else {
    1049              /* Another thread is working; give up our slice and
    1050               * wait until they're done. */
    1051              while (!run_once.done)
    1052                  Sleep(0);
    1053          }
    1054      }
    1055  #elif defined HAVE_BEOS_THREADS
    1056      if (atomic_add(&run_once_init, 1) == 0) {
    1057          globalkey = tls_allocate();
    1058          tls_set(globalkey, NULL);
    1059          mainthread = find_thread(NULL);
    1060  	__xmlInitializeDict();
    1061      } else
    1062          atomic_add(&run_once_init, -1);
    1063  #endif
    1064  }
    1065  #endif
    1066  
    1067  /**
    1068   * DllMain:
    1069   * @hinstDLL: handle to DLL instance
    1070   * @fdwReason: Reason code for entry
    1071   * @lpvReserved: generic pointer (depends upon reason code)
    1072   *
    1073   * Entry point for Windows library. It is being used to free thread-specific
    1074   * storage.
    1075   *
    1076   * Returns TRUE always
    1077   */
    1078  #ifdef HAVE_PTHREAD_H
    1079  #elif defined(HAVE_WIN32_THREADS) && !defined(HAVE_COMPILER_TLS) && (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
    1080  #if defined(LIBXML_STATIC_FOR_DLL)
    1081  int XMLCALL
    1082  xmlDllMain(ATTRIBUTE_UNUSED void *hinstDLL, unsigned long fdwReason,
    1083             ATTRIBUTE_UNUSED void *lpvReserved)
    1084  #else
    1085  /* declare to avoid "no previous prototype for 'DllMain'" warning */
    1086  /* Note that we do NOT want to include this function declaration in
    1087     a public header because it's meant to be called by Windows itself,
    1088     not a program that uses this library.  This also has to be exported. */
    1089  
    1090  XMLPUBFUN BOOL WINAPI
    1091  DllMain (HINSTANCE hinstDLL,
    1092           DWORD     fdwReason,
    1093           LPVOID    lpvReserved);
    1094  
    1095  BOOL WINAPI
    1096  DllMain(ATTRIBUTE_UNUSED HINSTANCE hinstDLL, DWORD fdwReason,
    1097          ATTRIBUTE_UNUSED LPVOID lpvReserved)
    1098  #endif
    1099  {
    1100      switch (fdwReason) {
    1101          case DLL_THREAD_DETACH:
    1102              if (globalkey != TLS_OUT_OF_INDEXES) {
    1103                  xmlGlobalState *globalval = NULL;
    1104                  xmlGlobalStateCleanupHelperParams *p =
    1105                      (xmlGlobalStateCleanupHelperParams *)
    1106                      TlsGetValue(globalkey);
    1107                  globalval = (xmlGlobalState *) (p ? p->memory : NULL);
    1108                  if (globalval) {
    1109                      xmlFreeGlobalState(globalval);
    1110                      TlsSetValue(globalkey, NULL);
    1111                  }
    1112                  if (p) {
    1113                      EnterCriticalSection(&cleanup_helpers_cs);
    1114                      if (p == cleanup_helpers_head)
    1115                          cleanup_helpers_head = p->next;
    1116                      else
    1117                          p->prev->next = p->next;
    1118                      if (p->next != NULL)
    1119                          p->next->prev = p->prev;
    1120                      LeaveCriticalSection(&cleanup_helpers_cs);
    1121                      free(p);
    1122                  }
    1123              }
    1124              break;
    1125      }
    1126      return TRUE;
    1127  }
    1128  #endif
    1129  #define bottom_threads
    1130  #include "elfgcchack.h"