diff options
Diffstat (limited to 'doc/html/pcre2api.html')
-rw-r--r-- | doc/html/pcre2api.html | 46 |
1 files changed, 38 insertions, 8 deletions
diff --git a/doc/html/pcre2api.html b/doc/html/pcre2api.html index 5cd7ae0..e456647 100644 --- a/doc/html/pcre2api.html +++ b/doc/html/pcre2api.html @@ -626,14 +626,15 @@ documentation for more details. <P> In a more complicated situation, where patterns are compiled only when they are first needed, but are still shared between threads, pointers to compiled -patterns must be protected from simultaneous writing by multiple threads, at -least until a pattern has been compiled. The logic can be something like this: +patterns must be protected from simultaneous writing by multiple threads. This +is somewhat tricky to do correctly. If you know that writing to a pointer is +atomic in your environment, you can use logic like this: <pre> Get a read-only (shared) lock (mutex) for pointer if (pointer == NULL) { Get a write (unique) lock for pointer - pointer = pcre2_compile(... + if (pointer == NULL) pointer = pcre2_compile(... } Release the lock Use pointer in pcre2_match() @@ -641,10 +642,39 @@ least until a pattern has been compiled. The logic can be something like this: Of course, testing for compilation errors should also be included in the code. </P> <P> -If JIT is being used, but the JIT compilation is not being done immediately, -(perhaps waiting to see if the pattern is used often enough) similar logic is -required. JIT compilation updates a pointer within the compiled code block, so -a thread must gain unique write access to the pointer before calling +The reason for checking the pointer a second time is as follows: Several +threads may have acquired the shared lock and tested the pointer for being +NULL, but only one of them will be given the write lock, with the rest kept +waiting. The winning thread will compile the pattern and store the result. +After this thread releases the write lock, another thread will get it, and if +it does not retest pointer for being NULL, will recompile the pattern and +overwrite the pointer, creating a memory leak and possibly causing other +issues. +</P> +<P> +In an environment where writing to a pointer may not be atomic, the above logic +is not sufficient. The thread that is doing the compiling may be descheduled +after writing only part of the pointer, which could cause other threads to use +an invalid value. Instead of checking the pointer itself, a separate "pointer +is valid" flag (that can be updated atomically) must be used: +<pre> + Get a read-only (shared) lock (mutex) for pointer + if (!pointer_is_valid) + { + Get a write (unique) lock for pointer + if (!pointer_is_valid) + { + pointer = pcre2_compile(... + pointer_is_valid = TRUE + } + } + Release the lock + Use pointer in pcre2_match() +</pre> +If JIT is being used, but the JIT compilation is not being done immediately +(perhaps waiting to see if the pattern is used often enough), similar logic is +required. JIT compilation updates a value within the compiled code block, so a +thread must gain unique write access to the pointer before calling <b>pcre2_jit_compile()</b>. Alternatively, <b>pcre2_code_copy()</b> or <b>pcre2_code_copy_with_tables()</b> can be used to obtain a private copy of the compiled code before calling the JIT compiler. @@ -3959,7 +3989,7 @@ Cambridge, England. </P> <br><a name="SEC42" href="#TOC1">REVISION</a><br> <P> -Last updated: 05 October 2020 +Last updated: 04 November 2020 <br> Copyright © 1997-2020 University of Cambridge. <br> |