threads.c 16 KB

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