libselinux: improve performance with pcre matches am: 72806f3933 am: 0470684f9e am: 4d3b040b58
Original change: https://android-review.googlesource.com/c/platform/external/selinux/+/2411194
Change-Id: I73964710fcec1688fdb4fb1d1706428c4244d95d
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
index 16df679..54f2402 100644
--- a/libselinux/src/regex.c
+++ b/libselinux/src/regex.c
@@ -30,6 +30,11 @@
#endif
#ifdef USE_PCRE2
+static pthread_key_t match_data_key;
+static int match_data_key_initialized = -1;
+static pthread_mutex_t key_mutex = PTHREAD_MUTEX_INITIALIZER;
+static __thread char match_data_initialized;
+
char const *regex_arch_string(void)
{
static char arch_string_buffer[32];
@@ -60,14 +65,6 @@
struct regex_data {
pcre2_code *regex; /* compiled regular expression */
-#ifndef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
- /*
- * match data block required for the compiled
- * pattern in pcre2
- */
- pcre2_match_data *match_data;
-#endif
- pthread_mutex_t match_mutex;
};
int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
@@ -86,13 +83,6 @@
goto err;
}
-#ifndef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
- (*regex)->match_data =
- pcre2_match_data_create_from_pattern((*regex)->regex, NULL);
- if (!(*regex)->match_data) {
- goto err;
- }
-#endif
return 0;
err:
@@ -142,13 +132,6 @@
if (rc != 1)
goto err;
-#ifndef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
- (*regex)->match_data =
- pcre2_match_data_create_from_pattern((*regex)->regex, NULL);
- if (!(*regex)->match_data)
- goto err;
-#endif
-
*regex_compiled = true;
}
@@ -204,18 +187,32 @@
return rc;
}
+static void __attribute__((destructor)) match_data_thread_free(void *key)
+{
+ void *value;
+ pcre2_match_data *match_data;
+
+ if (match_data_key_initialized <= 0 || !match_data_initialized)
+ return;
+
+ value = __selinux_getspecific(match_data_key);
+ match_data = value ? value : key;
+
+ pcre2_match_data_free(match_data);
+
+ __pthread_mutex_lock(&key_mutex);
+ if (--match_data_key_initialized == 1) {
+ __selinux_key_delete(match_data_key);
+ match_data_key_initialized = -1;
+ }
+ __pthread_mutex_unlock(&key_mutex);
+}
+
void regex_data_free(struct regex_data *regex)
{
if (regex) {
if (regex->regex)
pcre2_code_free(regex->regex);
-
-#ifndef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
- if (regex->match_data)
- pcre2_match_data_free(regex->match_data);
-#endif
-
- __pthread_mutex_destroy(®ex->match_mutex);
free(regex);
}
}
@@ -223,32 +220,40 @@
int regex_match(struct regex_data *regex, char const *subject, int partial)
{
int rc;
- pcre2_match_data *match_data;
- __pthread_mutex_lock(®ex->match_mutex);
+ bool slow;
+ pcre2_match_data *match_data = NULL;
-#ifdef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
- match_data = pcre2_match_data_create_from_pattern(
- regex->regex, NULL);
- if (match_data == NULL) {
- __pthread_mutex_unlock(®ex->match_mutex);
- return REGEX_ERROR;
+ if (match_data_key_initialized > 0) {
+ if (match_data_initialized == 0) {
+ match_data = pcre2_match_data_create(1, NULL);
+ if (match_data) {
+ match_data_initialized = 1;
+ __selinux_setspecific(match_data_key,
+ match_data);
+ __pthread_mutex_lock(&key_mutex);
+ match_data_key_initialized++;
+ __pthread_mutex_unlock(&key_mutex);
+ }
+ } else
+ match_data = __selinux_getspecific(match_data_key);
}
-#else
- match_data = regex->match_data;
-#endif
+
+ slow = (match_data_key_initialized <= 0 || match_data == NULL);
+ if (slow) {
+ match_data = pcre2_match_data_create_from_pattern(regex->regex,
+ NULL);
+ if (!match_data)
+ return REGEX_ERROR;
+ }
rc = pcre2_match(
regex->regex, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0,
partial ? PCRE2_PARTIAL_SOFT : 0, match_data, NULL);
-#ifdef AGGRESSIVE_FREE_AFTER_REGEX_MATCH
- // pcre2_match allocates heap and it won't be freed until
- // pcre2_match_data_free, resulting in heap overhead.
- pcre2_match_data_free(match_data);
-#endif
+ if (slow)
+ pcre2_match_data_free(match_data);
- __pthread_mutex_unlock(®ex->match_mutex);
- if (rc > 0)
+ if (rc >= 0)
return REGEX_MATCH;
switch (rc) {
case PCRE2_ERROR_PARTIAL:
@@ -290,7 +295,14 @@
if (!regex_data)
return NULL;
- __pthread_mutex_init(®ex_data->match_mutex, NULL);
+ __pthread_mutex_lock(&key_mutex);
+ if (match_data_key_initialized < 0) {
+ match_data_key_initialized = !__selinux_key_create(
+ &match_data_key,
+ match_data_thread_free);
+ }
+ __pthread_mutex_unlock(&key_mutex);
+
return regex_data;
}
diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
index 06f2c03..d1e6c50 100644
--- a/libselinux/src/selinux_internal.h
+++ b/libselinux/src/selinux_internal.h
@@ -13,6 +13,7 @@
#pragma weak pthread_key_create
#pragma weak pthread_key_delete
#pragma weak pthread_setspecific
+#pragma weak pthread_getspecific
/* Call handler iff the first call. */
#define __selinux_once(ONCE_CONTROL, INIT_FUNCTION) \
@@ -41,6 +42,9 @@
pthread_setspecific(KEY, VALUE); \
} while (0)
+#define __selinux_getspecific(KEY) \
+ (pthread_getspecific != NULL ? pthread_getspecific(KEY) : NULL)
+
/* selabel_lookup() is only thread safe if we're compiled with pthreads */
#pragma weak pthread_mutex_init