Background: An application that I came across uses a TRegEx
singleton from within several threads. The singleton is initialized to TRegEx.Create(Pattern, [roCompiled])
in a class constructor and the threads use it starting with RegEx.Match(Value).Groups
, and there appears to be no synchronization mechanism employed, however, the application runs fine. Though, this is just a small part of the TThread.Execute
override and there is little load on the threads. So this always might have worked just by chance, as the threads are unlikely to cross one another on the critical part.
Thoughts: On the one hand, thinking about it, it would make sense that a TRegEx
instance would only hold an immutable (compiled) pattern and work on parameter input directly, or keep this input around in (TMatch
) return values for a possible later continuation -- like with NextMatch
, for example, which is implemented on TMatch
and not on TRegEx
. And the underlying open source PCRE library seems to be thread-safe. All this fits the scenario above. On the other hand, I think a TRegEx
instance is generally not thread-safe because, for example, in the function TRegEx.Match(const Input: String): TMatch
(as used above) it looks like the string to be matched against the pattern is first stored to the instance before being matched. And the same nested TPerlRegEx
instance is passed on and kept alive throughout various function chains. A shared TRegEx
instance would seemingly need to be protected from uncoordinated access, with a critical section, for example.
That said, I suspect that TRegEx is not thread-safe, but I would like to ask someone knowledgeable at multithreading and at adjudicating thread-safety for confirmation. Hence, my question -- very much general and independent from the application from which it evolved:
Question: Is TRegEx thread-safe?
The PCRE library on which Delphi's regex class is built is threadsafe. See PCRE pcre_exec thread safe?
However, the Delphi wrapper TRegex
is not threadsafe. Consider the calls made to pcre_exec
in TPerlRegEx.Match
:
OffsetCount := pcre_exec(FPattern, FHints, @FSubject[0], FStop, 0, Opts, @Offsets[0],
High(Offsets));
Here FSubject
and Offsets
are members of TPerlRegEx
and so will be shared between different threads using this instance of TPerlRegEx
, which in turn is owned by the instance of TRegEx
.
For TRegEx
to be threadsafe in the sense that you desire (multiple threads performing matches on a shared compiled regex) these variables would need to be private to each invocation of a match function.