Skip to content

Commit

Permalink
[libhybris] Add cache for socket based property lookups.
Browse files Browse the repository at this point in the history
Android properties are looked up over socket connection by default,
which incurs considerable overhead if property lookup is very frequent.
This commit adds similar property cache to socket based lookups as is
used for direct file lookups. The cache invalidates its contents with
a user definable timeout, making sure property changes are eventually
propagated to all processes.

By default the cache is not enabled, but can be enabled with
--enable-property-cache configure switch.
  • Loading branch information
Mikko Harju authored and morphis committed Jan 22, 2016
1 parent 931912b commit 3cc98a6
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 2 deletions.
7 changes: 7 additions & 0 deletions hybris/configure.ac
Expand Up @@ -73,6 +73,13 @@ AC_ARG_ENABLE(wayland_serverside_buffers,
[wayland_serverside_buffers=yes])
AM_CONDITIONAL( [WANT_WL_SERVERSIDE_BUFFERS], [test x"$wayland_serverside_buffers" = x"yes"])

AC_ARG_ENABLE(property_cache,
[ --enable-property-cache Enable runtime android property cache (default=disabled)],
[property_cache=$enableval],
[property_cache=no])
AM_CONDITIONAL( [WANT_RUNTIME_PROPERTY_CACHE], [test x"$property_cache" = x"yes"])


AC_ARG_ENABLE(arch,
[ --enable-arch[=arch] Compile specific CPU target(default=arm)
arm: compile for ARM
Expand Down
6 changes: 6 additions & 0 deletions hybris/properties/Makefile.am
Expand Up @@ -3,6 +3,12 @@ lib_LTLIBRARIES = \

libandroid_properties_la_SOURCES = properties.c cache.c
libandroid_properties_la_CFLAGS = -I$(top_srcdir)/include $(ANDROID_HEADERS_CFLAGS)
if WANT_RUNTIME_PROPERTY_CACHE
libandroid_properties_la_SOURCES += runtime_cache.c
else
libandroid_properties_la_CFLAGS += -DNO_RUNTIME_PROPERTY_CACHE
endif

if WANT_DEBUG
libandroid_properties_la_CFLAGS += -ggdb -O0
endif
Expand Down
23 changes: 21 additions & 2 deletions hybris/properties/properties.c
Expand Up @@ -167,8 +167,23 @@ int property_get(const char *key, char *value, const char *default_value)
if ((key) && (strlen(key) >= PROP_NAME_MAX -1)) return -1;
if (value == NULL) return -1;

if (property_get_socket(key, value, default_value) == 0)
return strlen(value);

// Runtime cache will serialize property lookups within the process.
// This will increase latency if multiple threads are doing many
// parallel lookups to new properties, but the overhead should
// be offset with the caching eventually.
runtime_cache_lock();
if (runtime_cache_get(key, value) == 0) {
ret = value;
} else if (property_get_socket(key, value, default_value) == 0) {
runtime_cache_insert(key, value);
ret = value;
}
runtime_cache_unlock();

if (ret)
return strlen(ret);


/* In case the socket is not available, search the property file cache by hand */
ret = hybris_propcache_find(key);
Expand Down Expand Up @@ -196,6 +211,10 @@ int property_set(const char *key, const char *value)
if (strlen(key) >= PROP_NAME_MAX -1) return -1;
if (strlen(value) >= PROP_VALUE_MAX -1) return -1;

runtime_cache_lock();
runtime_cache_remove(key);
runtime_cache_unlock();

memset(&msg, 0, sizeof(msg));
msg.cmd = PROP_MSG_SETPROP;
strncpy(msg.name, key, sizeof(msg.name));
Expand Down
14 changes: 14 additions & 0 deletions hybris/properties/properties_p.h
Expand Up @@ -23,4 +23,18 @@ typedef void (*hybris_propcache_list_cb)(const char *key, const char *value, voi
void hybris_propcache_list(hybris_propcache_list_cb cb, void *cookie);
char *hybris_propcache_find(const char *key);

#ifndef NO_RUNTIME_PROPERTY_CACHE
void runtime_cache_lock();
void runtime_cache_unlock();
int runtime_cache_get(const char *key, char *value);
void runtime_cache_insert(const char *key, char *value);
void runtime_cache_remove(const char *key);
#else
#define runtime_cache_lock()
#define runtime_cache_unlock()
#define runtime_cache_get(K,V) (-1)
#define runtime_cache_insert(K,V)
#define runtime_cache_remove(K)
#endif

#endif
180 changes: 180 additions & 0 deletions hybris/properties/runtime_cache.c
@@ -0,0 +1,180 @@
/*
* Copyright (c) 2012 Carsten Munk <carsten.munk@gmail.com>
* Copyright (c) 2008 The Android Open Source Project
* Copyright (c) 2013 Simon Busch <morphis@gravedo.de>
* Copyright (c) 2013 Canonical Ltd
* Copyright (c) 2013 Jolla Ltd. <robin.burchell@jollamobile.com>
* Copyright (c) 2015 Jolla Ltd. <mikko.harju@jollamobile.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <pthread.h>


#define HYBRIS_PROPERTY_CACHE_DEFAULT_TIMEOUT_SECS 10

/** Maximum allowed time to return stale data from the cache. Override
with HYBRIS_PROPERTY_CACHE_TIMEOUT_SECS environment variable.
*/
static time_t runtime_cache_timeout_secs = HYBRIS_PROPERTY_CACHE_DEFAULT_TIMEOUT_SECS;


/** Key, value pair and the time of previous update (in seconds) */
struct hybris_prop_value
{
char *key;
char *value;
time_t last_update;
};

static struct hybris_prop_value * prop_array = 0;
static int num_prop = 0;
static int num_alloc = 0;

/** Protect access to statics */
static pthread_mutex_t array_mutex = PTHREAD_MUTEX_INITIALIZER;

/* private:
* compares two hybris_prop_value by key, so as to maintain a qsorted array of
* props, and search the array.
*/
static int prop_qcmp(const void *a, const void *b)
{
struct hybris_prop_value *aa = (struct hybris_prop_value *)a;
struct hybris_prop_value *bb = (struct hybris_prop_value *)b;

return strcmp(aa->key, bb->key);
}

static struct hybris_prop_value *cache_find_internal(const char *key)
{
struct hybris_prop_value prop_key;
prop_key.key = (char*)key;

return bsearch(&prop_key, prop_array, num_prop, sizeof(struct hybris_prop_value), prop_qcmp);
}

static void runtime_cache_init()
{
num_alloc = 8;
prop_array = malloc(num_alloc * sizeof(struct hybris_prop_value));

const char *timeout_str = getenv("HYBRIS_PROPERTY_CACHE_TIMEOUT_SECS");
if (timeout_str) {
runtime_cache_timeout_secs = atoi(timeout_str);
}
}

static void runtime_cache_ensure_initialized()
{
if (!prop_array) {
runtime_cache_init();
}
}

/** Invalidate an entry in the cache
*
* Cache will never shrink. Instead, assume that the same key
* will be queried soon after invalidation and reuse the entry.
*/
static void runtime_cache_invalidate_entry(struct hybris_prop_value *entry)
{
free(entry->value);
entry->value = NULL;
}

static int runtime_cache_get_impl(const char *key, char *value)
{
int ret = -ENOENT;

struct hybris_prop_value *entry = cache_find_internal(key);
if (entry != NULL && entry->value != NULL) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
time_t delta_secs = now.tv_sec - entry->last_update;
if (delta_secs > runtime_cache_timeout_secs) {
// assume the data in cache is stale, and force refresh
runtime_cache_invalidate_entry(entry);
} else {
// success, return value from cache
strcpy(value, entry->value);
ret = 0;
}
}

return ret;
}

static void runtime_cache_insert_impl(const char *key, char *value)
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC_COARSE, &now);

struct hybris_prop_value *entry = cache_find_internal(key);
if (entry) {
assert(entry->value == NULL);
// key,value pair was invalidated earlier,
// reuse entry in the property array
entry->value = strdup(value);
entry->last_update = now.tv_sec;
} else {
if (num_alloc == num_prop) {
num_alloc = 3 * num_alloc / 2;
prop_array = realloc(prop_array, num_alloc * sizeof(struct hybris_prop_value));
}

struct hybris_prop_value new_entry = { strdup(key), strdup(value), now.tv_sec };
prop_array[num_prop++] = new_entry;

qsort(prop_array, num_prop, sizeof(struct hybris_prop_value), prop_qcmp);
}
}


void runtime_cache_lock()
{
pthread_mutex_lock(&array_mutex);
}

void runtime_cache_unlock()
{
pthread_mutex_unlock(&array_mutex);
}

void runtime_cache_remove(const char *key)
{
runtime_cache_ensure_initialized();
struct hybris_prop_value *entry = cache_find_internal(key);
if (entry) {
runtime_cache_invalidate_entry(entry);
}
}

int runtime_cache_get(const char *key, char *value)
{
runtime_cache_ensure_initialized();
return runtime_cache_get_impl(key, value);
}

void runtime_cache_insert(const char *key, char *value)
{
runtime_cache_ensure_initialized();
runtime_cache_insert_impl(key, value);
}

0 comments on commit 3cc98a6

Please sign in to comment.