Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion include/boost/regex/v5/mem_block_cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ struct mem_block_cache
~mem_block_cache()
{
for (size_t i = 0;i < BOOST_REGEX_MAX_CACHE_BLOCKS; ++i) {
if (cache[i].load()) ::operator delete(cache[i].load());
void* p = cache[i].exchange(nullptr);
if (p) ::operator delete(p);
}
}
void* get()
Expand All @@ -73,7 +74,11 @@ struct mem_block_cache

static mem_block_cache& instance()
{
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
static mem_block_cache block_cache = { { {nullptr} } };
#else
thread_local mem_block_cache block_cache = { { {nullptr} } };
#endif
return block_cache;
}
};
Expand Down Expand Up @@ -138,7 +143,11 @@ struct mem_block_cache
}
static mem_block_cache& instance()
{
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
static mem_block_cache block_cache;
#else
thread_local mem_block_cache block_cache;
#endif
return block_cache;
}
};
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ regex-test posix_api_check_cpp : c_compiler_checks/posix_api_check.cpp ;
regex-test wide_posix_api_check_cpp : c_compiler_checks/wide_posix_api_check.cpp ;
run pathology/bad_expression_test.cpp ;
run pathology/recursion_test.cpp ;
run pathology/concurrent_static_regex_test.cpp : : : <threading>multi ;
run named_subexpressions/named_subexpressions_test.cpp ;
run unicode/unicode_iterator_test.cpp : : : release <define>TEST_UTF8 : unicode_iterator_test_utf8 ;
run unicode/unicode_iterator_test.cpp : : : release <define>TEST_UTF16 : unicode_iterator_test_utf16 ;
Expand Down
114 changes: 114 additions & 0 deletions test/pathology/concurrent_static_regex_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
*
* Copyright (c) 2026
* Amaan Qureshi
*
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0. (See accompanying file
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*
*/

/*
* LOCATION: see http://www.boost.org for most recent version.
* FILE: concurrent_static_regex_test.cpp
* VERSION: see <boost/version.hpp>
* DESCRIPTION: Test for concurrent use of static regex objects.
* See https://github.com/boostorg/regex/issues/198
*/

#include "../test_macros.hpp"
#include <atomic>
#include <boost/detail/lightweight_main.hpp>
#include <boost/regex.hpp>
#include <string>
#include <thread>
#include <vector>

namespace {

std::atomic<unsigned> match_count{0};
std::atomic<unsigned> search_count{0};
std::atomic<unsigned> replace_count{0};

static const boost::regex digits_regex(R"(^\d+$)");
static const boost::regex
map_regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)");
static const boost::regex special_regex(R"([.^$\\*+?()\[\]{}|])");

void thread_regex_match(unsigned iterations) {
const char *samples[] = {"12345", "hello", "99999", "0", "abc123", "42"};
const unsigned n_samples = sizeof(samples) / sizeof(samples[0]);

for (unsigned i = 0; i < iterations; ++i) {
const char *s = samples[i % n_samples];
if (boost::regex_match(s, digits_regex))
match_count.fetch_add(1, std::memory_order_relaxed);
}
}

void thread_regex_search(unsigned iterations) {
const std::string lines[] = {
"08:00-17:00 r--p 00000000 08:01 12345 /usr/lib/libc.so.6",
"7f8a1000-7f8a2000 rw-p 00001000 08:01 67890 /usr/lib/ld-linux.so.2",
"this line does not match the pattern at all",
"00400000-00401000 r-xp 00000000 08:02 54321 /usr/bin/test",
};
const unsigned n_lines = sizeof(lines) / sizeof(lines[0]);

boost::smatch what;
for (unsigned i = 0; i < iterations; ++i) {
const std::string &line = lines[i % n_lines];
if (boost::regex_search(line, what, map_regex))
search_count.fetch_add(1, std::memory_order_relaxed);
}
}

void thread_regex_replace(unsigned iterations) {
const std::string inputs[] = {
"/nix/store/abc123",
"path.with"
"[brackets]",
"no+specials*here?",
"plain",
};
const unsigned n_inputs = sizeof(inputs) / sizeof(inputs[0]);

for (unsigned i = 0; i < iterations; ++i) {
const std::string &input = inputs[i % n_inputs];
std::string result = boost::regex_replace(input, special_regex, R"(\\$&)");
if (result != input)
replace_count.fetch_add(1, std::memory_order_relaxed);
}
}

} // anonymous namespace

int cpp_main(int, char *[]) {
const unsigned n_threads = 8;
const unsigned iterations = 50000;

for (unsigned round = 0; round < 3; ++round) {
match_count = 0;
search_count = 0;
replace_count = 0;

std::vector<std::thread> threads;
threads.reserve(n_threads * 3);

for (unsigned t = 0; t < n_threads; ++t) {
threads.emplace_back(thread_regex_match, iterations);
threads.emplace_back(thread_regex_search, iterations);
threads.emplace_back(thread_regex_replace, iterations);
}

for (auto &th : threads)
th.join();

BOOST_CHECK(match_count > 0);
BOOST_CHECK(search_count > 0);
BOOST_CHECK(replace_count > 0);
}

return 0;
}