(root)/
libxml2-2.12.3/
threads.c
       1  /**
       2   * threads.c: set of generic threading related routines
       3   *
       4   * See Copyright for the status of this software.
       5   *
       6   * Gary Pennington <Gary.Pennington@uk.sun.com>
       7   * daniel@veillard.com
       8   */
       9  
      10  #define IN_LIBXML
      11  #include "libxml.h"
      12  
      13  #include <string.h>
      14  #include <stdlib.h>
      15  
      16  #include <libxml/threads.h>
      17  #include <libxml/parser.h>
      18  #ifdef LIBXML_CATALOG_ENABLED
      19  #include <libxml/catalog.h>
      20  #endif
      21  #ifdef LIBXML_SCHEMAS_ENABLED
      22  #include <libxml/xmlschemastypes.h>
      23  #include <libxml/relaxng.h>
      24  #endif
      25  
      26  #if defined(SOLARIS)
      27  #include <note.h>
      28  #endif
      29  
      30  #include "private/dict.h"
      31  #include "private/enc.h"
      32  #include "private/globals.h"
      33  #include "private/memory.h"
      34  #include "private/threads.h"
      35  #include "private/xpath.h"
      36  
      37  #if defined(HAVE_POSIX_THREADS) && \
      38      defined(__GLIBC__) && \
      39      __GLIBC__ * 100 + __GLIBC_MINOR__ >= 234
      40  
      41  /*
      42   * The modern way available since glibc 2.32.
      43   *
      44   * The check above is for glibc 2.34 which merged the pthread symbols into
      45   * libc. Since we still allow linking without pthread symbols (see below),
      46   * this only works if pthread symbols are guaranteed to be available.
      47   */
      48  
      49  #include <sys/single_threaded.h>
      50  
      51  #define XML_IS_THREADED() (!__libc_single_threaded)
      52  #define XML_IS_NEVER_THREADED() 0
      53  
      54  #elif defined(HAVE_POSIX_THREADS) && \
      55        defined(__GLIBC__) && \
      56        defined(__GNUC__)
      57  
      58  /*
      59   * The traditional way to check for single-threaded applications with
      60   * glibc was to check whether the separate libpthread library is
      61   * linked in. This works by not linking libxml2 with libpthread (see
      62   * BASE_THREAD_LIBS in configure.ac and Makefile.am) and declaring
      63   * pthread functions as weak symbols.
      64   *
      65   * In glibc 2.34, the pthread symbols were moved from libpthread to libc,
      66   * so this doesn't work anymore.
      67   *
      68   * At some point, this legacy code and the BASE_THREAD_LIBS hack in
      69   * configure.ac can probably be removed.
      70   */
      71  
      72  #pragma weak pthread_mutex_init
      73  #pragma weak pthread_mutex_destroy
      74  #pragma weak pthread_mutex_lock
      75  #pragma weak pthread_mutex_unlock
      76  #pragma weak pthread_cond_init
      77  #pragma weak pthread_cond_destroy
      78  #pragma weak pthread_cond_wait
      79  #pragma weak pthread_equal
      80  #pragma weak pthread_self
      81  #pragma weak pthread_cond_signal
      82  
      83  #define XML_PTHREAD_WEAK
      84  #define XML_IS_THREADED() libxml_is_threaded
      85  #define XML_IS_NEVER_THREADED() (!libxml_is_threaded)
      86  
      87  static int libxml_is_threaded = -1;
      88  
      89  #else /* other POSIX platforms */
      90  
      91  #define XML_IS_THREADED() 1
      92  #define XML_IS_NEVER_THREADED() 0
      93  
      94  #endif
      95  
      96  /*
      97   * TODO: this module still uses malloc/free and not xmlMalloc/xmlFree
      98   *       to avoid some craziness since xmlMalloc/xmlFree may actually
      99   *       be hosted on allocated blocks needing them for the allocation ...
     100   */
     101  
     102  /*
     103   * xmlRMutex are reentrant mutual exception locks
     104   */
     105  struct _xmlRMutex {
     106  #ifdef HAVE_POSIX_THREADS
     107      pthread_mutex_t lock;
     108      unsigned int held;
     109      unsigned int waiters;
     110      pthread_t tid;
     111      pthread_cond_t cv;
     112  #elif defined HAVE_WIN32_THREADS
     113      CRITICAL_SECTION cs;
     114  #else
     115      int empty;
     116  #endif
     117  };
     118  
     119  static xmlRMutexPtr xmlLibraryLock = NULL;
     120  
     121  /**
     122   * xmlInitMutex:
     123   * @mutex:  the mutex
     124   *
     125   * Initialize a mutex.
     126   */
     127  void
     128  xmlInitMutex(xmlMutexPtr mutex)
     129  {
     130  #ifdef HAVE_POSIX_THREADS
     131      if (XML_IS_NEVER_THREADED() == 0)
     132          pthread_mutex_init(&mutex->lock, NULL);
     133  #elif defined HAVE_WIN32_THREADS
     134      InitializeCriticalSection(&mutex->cs);
     135  #else
     136      (void) mutex;
     137  #endif
     138  }
     139  
     140  /**
     141   * xmlNewMutex:
     142   *
     143   * xmlNewMutex() is used to allocate a libxml2 token struct for use in
     144   * synchronizing access to data.
     145   *
     146   * Returns a new simple mutex pointer or NULL in case of error
     147   */
     148  xmlMutexPtr
     149  xmlNewMutex(void)
     150  {
     151      xmlMutexPtr tok;
     152  
     153      if ((tok = malloc(sizeof(xmlMutex))) == NULL)
     154          return (NULL);
     155      xmlInitMutex(tok);
     156      return (tok);
     157  }
     158  
     159  /**
     160   * xmlCleanupMutex:
     161   * @mutex:  the simple mutex
     162   *
     163   * Reclaim resources associated with a mutex.
     164   */
     165  void
     166  xmlCleanupMutex(xmlMutexPtr mutex)
     167  {
     168  #ifdef HAVE_POSIX_THREADS
     169      if (XML_IS_NEVER_THREADED() == 0)
     170          pthread_mutex_destroy(&mutex->lock);
     171  #elif defined HAVE_WIN32_THREADS
     172      DeleteCriticalSection(&mutex->cs);
     173  #else
     174      (void) mutex;
     175  #endif
     176  }
     177  
     178  /**
     179   * xmlFreeMutex:
     180   * @tok:  the simple mutex
     181   *
     182   * Free a mutex.
     183   */
     184  void
     185  xmlFreeMutex(xmlMutexPtr tok)
     186  {
     187      if (tok == NULL)
     188          return;
     189  
     190      xmlCleanupMutex(tok);
     191      free(tok);
     192  }
     193  
     194  /**
     195   * xmlMutexLock:
     196   * @tok:  the simple mutex
     197   *
     198   * xmlMutexLock() is used to lock a libxml2 token.
     199   */
     200  void
     201  xmlMutexLock(xmlMutexPtr tok)
     202  {
     203      if (tok == NULL)
     204          return;
     205  #ifdef HAVE_POSIX_THREADS
     206      /*
     207       * This assumes that __libc_single_threaded won't change while the
     208       * lock is held.
     209       */
     210      if (XML_IS_THREADED() != 0)
     211          pthread_mutex_lock(&tok->lock);
     212  #elif defined HAVE_WIN32_THREADS
     213      EnterCriticalSection(&tok->cs);
     214  #endif
     215  
     216  }
     217  
     218  /**
     219   * xmlMutexUnlock:
     220   * @tok:  the simple mutex
     221   *
     222   * xmlMutexUnlock() is used to unlock a libxml2 token.
     223   */
     224  void
     225  xmlMutexUnlock(xmlMutexPtr tok)
     226  {
     227      if (tok == NULL)
     228          return;
     229  #ifdef HAVE_POSIX_THREADS
     230      if (XML_IS_THREADED() != 0)
     231          pthread_mutex_unlock(&tok->lock);
     232  #elif defined HAVE_WIN32_THREADS
     233      LeaveCriticalSection(&tok->cs);
     234  #endif
     235  }
     236  
     237  /**
     238   * xmlNewRMutex:
     239   *
     240   * xmlRNewMutex() is used to allocate a reentrant mutex for use in
     241   * synchronizing access to data. token_r is a re-entrant lock and thus useful
     242   * for synchronizing access to data structures that may be manipulated in a
     243   * recursive fashion.
     244   *
     245   * Returns the new reentrant mutex pointer or NULL in case of error
     246   */
     247  xmlRMutexPtr
     248  xmlNewRMutex(void)
     249  {
     250      xmlRMutexPtr tok;
     251  
     252      if ((tok = malloc(sizeof(xmlRMutex))) == NULL)
     253          return (NULL);
     254  #ifdef HAVE_POSIX_THREADS
     255      if (XML_IS_NEVER_THREADED() == 0) {
     256          pthread_mutex_init(&tok->lock, NULL);
     257          tok->held = 0;
     258          tok->waiters = 0;
     259          pthread_cond_init(&tok->cv, NULL);
     260      }
     261  #elif defined HAVE_WIN32_THREADS
     262      InitializeCriticalSection(&tok->cs);
     263  #endif
     264      return (tok);
     265  }
     266  
     267  /**
     268   * xmlFreeRMutex:
     269   * @tok:  the reentrant mutex
     270   *
     271   * xmlRFreeMutex() is used to reclaim resources associated with a
     272   * reentrant mutex.
     273   */
     274  void
     275  xmlFreeRMutex(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
     276  {
     277      if (tok == NULL)
     278          return;
     279  #ifdef HAVE_POSIX_THREADS
     280      if (XML_IS_NEVER_THREADED() == 0) {
     281          pthread_mutex_destroy(&tok->lock);
     282          pthread_cond_destroy(&tok->cv);
     283      }
     284  #elif defined HAVE_WIN32_THREADS
     285      DeleteCriticalSection(&tok->cs);
     286  #endif
     287      free(tok);
     288  }
     289  
     290  /**
     291   * xmlRMutexLock:
     292   * @tok:  the reentrant mutex
     293   *
     294   * xmlRMutexLock() is used to lock a libxml2 token_r.
     295   */
     296  void
     297  xmlRMutexLock(xmlRMutexPtr tok)
     298  {
     299      if (tok == NULL)
     300          return;
     301  #ifdef HAVE_POSIX_THREADS
     302      if (XML_IS_THREADED() == 0)
     303          return;
     304  
     305      pthread_mutex_lock(&tok->lock);
     306      if (tok->held) {
     307          if (pthread_equal(tok->tid, pthread_self())) {
     308              tok->held++;
     309              pthread_mutex_unlock(&tok->lock);
     310              return;
     311          } else {
     312              tok->waiters++;
     313              while (tok->held)
     314                  pthread_cond_wait(&tok->cv, &tok->lock);
     315              tok->waiters--;
     316          }
     317      }
     318      tok->tid = pthread_self();
     319      tok->held = 1;
     320      pthread_mutex_unlock(&tok->lock);
     321  #elif defined HAVE_WIN32_THREADS
     322      EnterCriticalSection(&tok->cs);
     323  #endif
     324  }
     325  
     326  /**
     327   * xmlRMutexUnlock:
     328   * @tok:  the reentrant mutex
     329   *
     330   * xmlRMutexUnlock() is used to unlock a libxml2 token_r.
     331   */
     332  void
     333  xmlRMutexUnlock(xmlRMutexPtr tok ATTRIBUTE_UNUSED)
     334  {
     335      if (tok == NULL)
     336          return;
     337  #ifdef HAVE_POSIX_THREADS
     338      if (XML_IS_THREADED() == 0)
     339          return;
     340  
     341      pthread_mutex_lock(&tok->lock);
     342      tok->held--;
     343      if (tok->held == 0) {
     344          if (tok->waiters)
     345              pthread_cond_signal(&tok->cv);
     346          memset(&tok->tid, 0, sizeof(tok->tid));
     347      }
     348      pthread_mutex_unlock(&tok->lock);
     349  #elif defined HAVE_WIN32_THREADS
     350      LeaveCriticalSection(&tok->cs);
     351  #endif
     352  }
     353  
     354  /************************************************************************
     355   *									*
     356   *			Library wide thread interfaces			*
     357   *									*
     358   ************************************************************************/
     359  
     360  /**
     361   * xmlGetThreadId:
     362   *
     363   * DEPRECATED: Internal function, do not use.
     364   *
     365   * xmlGetThreadId() find the current thread ID number
     366   * Note that this is likely to be broken on some platforms using pthreads
     367   * as the specification doesn't mandate pthread_t to be an integer type
     368   *
     369   * Returns the current thread ID number
     370   */
     371  int
     372  xmlGetThreadId(void)
     373  {
     374  #ifdef HAVE_POSIX_THREADS
     375      pthread_t id;
     376      int ret;
     377  
     378      if (XML_IS_THREADED() == 0)
     379          return (0);
     380      id = pthread_self();
     381      /* horrible but preserves compat, see warning above */
     382      memcpy(&ret, &id, sizeof(ret));
     383      return (ret);
     384  #elif defined HAVE_WIN32_THREADS
     385      return GetCurrentThreadId();
     386  #else
     387      return ((int) 0);
     388  #endif
     389  }
     390  
     391  /**
     392   * xmlLockLibrary:
     393   *
     394   * xmlLockLibrary() is used to take out a re-entrant lock on the libxml2
     395   * library.
     396   */
     397  void
     398  xmlLockLibrary(void)
     399  {
     400      xmlRMutexLock(xmlLibraryLock);
     401  }
     402  
     403  /**
     404   * xmlUnlockLibrary:
     405   *
     406   * xmlUnlockLibrary() is used to release a re-entrant lock on the libxml2
     407   * library.
     408   */
     409  void
     410  xmlUnlockLibrary(void)
     411  {
     412      xmlRMutexUnlock(xmlLibraryLock);
     413  }
     414  
     415  /**
     416   * xmlInitThreads:
     417   *
     418   * DEPRECATED: Alias for xmlInitParser.
     419   */
     420  void
     421  xmlInitThreads(void)
     422  {
     423      xmlInitParser();
     424  }
     425  
     426  /**
     427   * xmlCleanupThreads:
     428   *
     429   * DEPRECATED: This function is a no-op. Call xmlCleanupParser
     430   * to free global state but see the warnings there. xmlCleanupParser
     431   * should be only called once at program exit. In most cases, you don't
     432   * have call cleanup functions at all.
     433   */
     434  void
     435  xmlCleanupThreads(void)
     436  {
     437  }
     438  
     439  /************************************************************************
     440   *									*
     441   *			Library wide initialization			*
     442   *									*
     443   ************************************************************************/
     444  
     445  static int xmlParserInitialized = 0;
     446  static int xmlParserInnerInitialized = 0;
     447  
     448  
     449  #ifdef HAVE_POSIX_THREADS
     450  static pthread_mutex_t global_init_lock = PTHREAD_MUTEX_INITIALIZER;
     451  #elif defined HAVE_WIN32_THREADS
     452  static volatile LPCRITICAL_SECTION global_init_lock = NULL;
     453  #endif
     454  
     455  /**
     456   * xmlGlobalInitMutexLock
     457   *
     458   * Makes sure that the global initialization mutex is initialized and
     459   * locks it.
     460   */
     461  static void
     462  xmlGlobalInitMutexLock(void) {
     463  #ifdef HAVE_POSIX_THREADS
     464  
     465  #ifdef XML_PTHREAD_WEAK
     466      /*
     467       * This is somewhat unreliable since libpthread could be loaded
     468       * later with dlopen() and threads could be created. But it's
     469       * long-standing behavior and hard to work around.
     470       */
     471      if (libxml_is_threaded == -1)
     472          libxml_is_threaded =
     473              (pthread_mutex_init != NULL) &&
     474              (pthread_mutex_destroy != NULL) &&
     475              (pthread_mutex_lock != NULL) &&
     476              (pthread_mutex_unlock != NULL) &&
     477              (pthread_cond_init != NULL) &&
     478              (pthread_cond_destroy != NULL) &&
     479              (pthread_cond_wait != NULL) &&
     480              /*
     481               * pthread_equal can be inline, resuting in -Waddress warnings.
     482               * Let's assume it's available if all the other functions are.
     483               */
     484              /* (pthread_equal != NULL) && */
     485              (pthread_self != NULL) &&
     486              (pthread_cond_signal != NULL);
     487  #endif
     488  
     489      /* The mutex is statically initialized, so we just lock it. */
     490      if (XML_IS_THREADED() != 0)
     491          pthread_mutex_lock(&global_init_lock);
     492  
     493  #elif defined HAVE_WIN32_THREADS
     494  
     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  
     528  #endif
     529  }
     530  
     531  static void
     532  xmlGlobalInitMutexUnlock(void) {
     533  #ifdef HAVE_POSIX_THREADS
     534      if (XML_IS_THREADED() != 0)
     535          pthread_mutex_unlock(&global_init_lock);
     536  #elif defined HAVE_WIN32_THREADS
     537      if (global_init_lock != NULL)
     538  	LeaveCriticalSection(global_init_lock);
     539  #endif
     540  }
     541  
     542  /**
     543   * xmlGlobalInitMutexDestroy
     544   *
     545   * Makes sure that the global initialization mutex is destroyed before
     546   * application termination.
     547   */
     548  static void
     549  xmlGlobalInitMutexDestroy(void) {
     550  #ifdef HAVE_POSIX_THREADS
     551  #elif defined HAVE_WIN32_THREADS
     552      if (global_init_lock != NULL) {
     553          DeleteCriticalSection(global_init_lock);
     554          free(global_init_lock);
     555          global_init_lock = NULL;
     556      }
     557  #endif
     558  }
     559  
     560  /**
     561   * xmlInitParser:
     562   *
     563   * Initialization function for the XML parser.
     564   *
     565   * Call once from the main thread before using the library in
     566   * multithreaded programs.
     567   */
     568  void
     569  xmlInitParser(void) {
     570      /*
     571       * Note that the initialization code must not make memory allocations.
     572       */
     573      if (xmlParserInitialized != 0)
     574          return;
     575  
     576      xmlGlobalInitMutexLock();
     577  
     578      if (xmlParserInnerInitialized == 0) {
     579  #if defined(_WIN32) && \
     580      !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
     581      (!defined(LIBXML_STATIC) || defined(LIBXML_STATIC_FOR_DLL))
     582          if (xmlFree == free)
     583              atexit(xmlCleanupParser);
     584  #endif
     585  
     586          xmlInitMemoryInternal(); /* Should come second */
     587          xmlInitGlobalsInternal();
     588          xmlInitRandom();
     589          xmlInitDictInternal();
     590          xmlInitEncodingInternal();
     591  #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
     592          xmlInitXPathInternal();
     593  #endif
     594  
     595          xmlRegisterDefaultInputCallbacks();
     596  #ifdef LIBXML_OUTPUT_ENABLED
     597          xmlRegisterDefaultOutputCallbacks();
     598  #endif /* LIBXML_OUTPUT_ENABLED */
     599  
     600          xmlParserInnerInitialized = 1;
     601      }
     602  
     603      xmlGlobalInitMutexUnlock();
     604  
     605      xmlParserInitialized = 1;
     606  }
     607  
     608  /**
     609   * xmlCleanupParser:
     610   *
     611   * This function name is somewhat misleading. It does not clean up
     612   * parser state, it cleans up memory allocated by the library itself.
     613   * It is a cleanup function for the XML library. It tries to reclaim all
     614   * related global memory allocated for the library processing.
     615   * It doesn't deallocate any document related memory. One should
     616   * call xmlCleanupParser() only when the process has finished using
     617   * the library and all XML/HTML documents built with it.
     618   * See also xmlInitParser() which has the opposite function of preparing
     619   * the library for operations.
     620   *
     621   * WARNING: if your application is multithreaded or has plugin support
     622   *          calling this may crash the application if another thread or
     623   *          a plugin is still using libxml2. It's sometimes very hard to
     624   *          guess if libxml2 is in use in the application, some libraries
     625   *          or plugins may use it without notice. In case of doubt abstain
     626   *          from calling this function or do it just before calling exit()
     627   *          to avoid leak reports from valgrind !
     628   */
     629  void
     630  xmlCleanupParser(void) {
     631      if (!xmlParserInitialized)
     632          return;
     633  
     634      /* These functions can call xmlFree. */
     635  
     636      xmlCleanupCharEncodingHandlers();
     637  #ifdef LIBXML_CATALOG_ENABLED
     638      xmlCatalogCleanup();
     639  #endif
     640  #ifdef LIBXML_SCHEMAS_ENABLED
     641      xmlSchemaCleanupTypes();
     642      xmlRelaxNGCleanupTypes();
     643  #endif
     644  
     645      /* These functions should never call xmlFree. */
     646  
     647      xmlCleanupInputCallbacks();
     648  #ifdef LIBXML_OUTPUT_ENABLED
     649      xmlCleanupOutputCallbacks();
     650  #endif
     651  
     652      xmlCleanupDictInternal();
     653      xmlCleanupRandom();
     654      xmlCleanupGlobalsInternal();
     655      /*
     656       * Must come last. On Windows, xmlCleanupGlobalsInternal can call
     657       * xmlFree which uses xmlMemMutex in debug mode.
     658       */
     659      xmlCleanupMemoryInternal();
     660  
     661      xmlGlobalInitMutexDestroy();
     662  
     663      xmlParserInitialized = 0;
     664      xmlParserInnerInitialized = 0;
     665  }
     666  
     667  #if defined(HAVE_ATTRIBUTE_DESTRUCTOR) && \
     668      !defined(LIBXML_THREAD_ALLOC_ENABLED) && \
     669      !defined(LIBXML_STATIC) && \
     670      !defined(_WIN32)
     671  static void
     672  ATTRIBUTE_DESTRUCTOR
     673  xmlDestructor(void) {
     674      /*
     675       * Calling custom deallocation functions in a destructor can cause
     676       * problems, for example with Nokogiri.
     677       */
     678      if (xmlFree == free)
     679          xmlCleanupParser();
     680  }
     681  #endif
     682