mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1036 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1036 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* -*- c-basic-offset: 2 -*- */
 | 
						|
/*
 | 
						|
  Copyright(C) 2009-2017 Brazil
 | 
						|
 | 
						|
  This library is free software; you can redistribute it and/or
 | 
						|
  modify it under the terms of the GNU Lesser General Public
 | 
						|
  License version 2.1 as published by the Free Software Foundation.
 | 
						|
 | 
						|
  This library is distributed in the hope that it will be useful,
 | 
						|
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
  Lesser General Public License for more details.
 | 
						|
 | 
						|
  You should have received a copy of the GNU Lesser General Public
 | 
						|
  License along with this library; if not, write to the Free Software
 | 
						|
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
 | 
						|
*/
 | 
						|
 | 
						|
#include "grn_cache.h"
 | 
						|
#include "grn_ctx.h"
 | 
						|
#include "grn_ctx_impl.h"
 | 
						|
#include "grn_hash.h"
 | 
						|
#include "grn_pat.h"
 | 
						|
#include "grn_store.h"
 | 
						|
#include "grn_db.h"
 | 
						|
#include "grn_file_lock.h"
 | 
						|
 | 
						|
#include <sys/stat.h>
 | 
						|
 | 
						|
typedef struct _grn_cache_entry_memory grn_cache_entry_memory;
 | 
						|
 | 
						|
struct _grn_cache_entry_memory {
 | 
						|
  grn_cache_entry_memory *next;
 | 
						|
  grn_cache_entry_memory *prev;
 | 
						|
  grn_obj *value;
 | 
						|
  grn_timeval tv;
 | 
						|
  grn_id id;
 | 
						|
};
 | 
						|
 | 
						|
typedef struct _grn_cache_entry_persistent_data {
 | 
						|
  grn_id next;
 | 
						|
  grn_id prev;
 | 
						|
  grn_timeval modified_time;
 | 
						|
} grn_cache_entry_persistent_data;
 | 
						|
 | 
						|
/*
 | 
						|
  sizeof(grn_cache_entry_persistent_metadata) should be equal or smaller
 | 
						|
  than sizeof(grn_cache_entry_persistent_data).
 | 
						|
 */
 | 
						|
typedef struct _grn_cache_entry_persistent_metadata {
 | 
						|
  uint32_t max_nentries;
 | 
						|
  uint32_t nfetches;
 | 
						|
  uint32_t nhits;
 | 
						|
} grn_cache_entry_persistent_metadata;
 | 
						|
 | 
						|
typedef union _grn_cache_entry_persistent {
 | 
						|
  grn_cache_entry_persistent_data data;
 | 
						|
  grn_cache_entry_persistent_metadata metadata;
 | 
						|
} grn_cache_entry_persistent;
 | 
						|
 | 
						|
struct _grn_cache {
 | 
						|
  union {
 | 
						|
    struct {
 | 
						|
      grn_cache_entry_memory *next;
 | 
						|
      grn_cache_entry_memory *prev;
 | 
						|
      grn_hash *hash;
 | 
						|
      grn_mutex mutex;
 | 
						|
      uint32_t max_nentries;
 | 
						|
      uint32_t nfetches;
 | 
						|
      uint32_t nhits;
 | 
						|
    } memory;
 | 
						|
    struct {
 | 
						|
      grn_hash *keys;
 | 
						|
      grn_ja *values;
 | 
						|
      int timeout;
 | 
						|
    } persistent;
 | 
						|
  } impl;
 | 
						|
  grn_bool is_memory;
 | 
						|
  grn_ctx *ctx;
 | 
						|
};
 | 
						|
 | 
						|
#define GRN_CACHE_PERSISTENT_ROOT_ID 1
 | 
						|
#define GRN_CACHE_PERSISTENT_ROOT_KEY "\0"
 | 
						|
#define GRN_CACHE_PERSISTENT_ROOT_KEY_LEN \
 | 
						|
  (sizeof(GRN_CACHE_PERSISTENT_ROOT_KEY) - 1)
 | 
						|
#define GRN_CACHE_PERSISTENT_METADATA_ID 2
 | 
						|
#define GRN_CACHE_PERSISTENT_METADATA_KEY "\1"
 | 
						|
#define GRN_CACHE_PERSISTENT_METADATA_KEY_LEN \
 | 
						|
  (sizeof(GRN_CACHE_PERSISTENT_METADATA_KEY) - 1)
 | 
						|
 | 
						|
static grn_ctx grn_cache_ctx;
 | 
						|
static grn_cache *grn_cache_current = NULL;
 | 
						|
static grn_cache *grn_cache_default = NULL;
 | 
						|
static char grn_cache_default_base_path[PATH_MAX];
 | 
						|
 | 
						|
void
 | 
						|
grn_set_default_cache_base_path(const char *base_path)
 | 
						|
{
 | 
						|
  if (base_path) {
 | 
						|
    grn_strcpy(grn_cache_default_base_path,
 | 
						|
               PATH_MAX,
 | 
						|
               base_path);
 | 
						|
  } else {
 | 
						|
    grn_cache_default_base_path[0] = '\0';
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
grn_get_default_cache_base_path(void)
 | 
						|
{
 | 
						|
  if (grn_cache_default_base_path[0] == '\0') {
 | 
						|
    return NULL;
 | 
						|
  } else {
 | 
						|
    return grn_cache_default_base_path;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_open_memory(grn_ctx *ctx, grn_cache *cache)
 | 
						|
{
 | 
						|
  cache->impl.memory.next = (grn_cache_entry_memory *)cache;
 | 
						|
  cache->impl.memory.prev = (grn_cache_entry_memory *)cache;
 | 
						|
  cache->impl.memory.hash = grn_hash_create(cache->ctx,
 | 
						|
                                            NULL,
 | 
						|
                                            GRN_CACHE_MAX_KEY_SIZE,
 | 
						|
                                            sizeof(grn_cache_entry_memory),
 | 
						|
                                            GRN_OBJ_KEY_VAR_SIZE);
 | 
						|
  if (!cache->impl.memory.hash) {
 | 
						|
    ERR(GRN_NO_MEMORY_AVAILABLE, "[cache] failed to create hash table");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  MUTEX_INIT(cache->impl.memory.mutex);
 | 
						|
 | 
						|
  cache->impl.memory.max_nentries = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
 | 
						|
  cache->impl.memory.nfetches = 0;
 | 
						|
  cache->impl.memory.nhits = 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_open_persistent(grn_ctx *ctx,
 | 
						|
                          grn_cache *cache,
 | 
						|
                          const char *base_path)
 | 
						|
{
 | 
						|
  grn_file_lock file_lock;
 | 
						|
  char *keys_path = NULL;
 | 
						|
  char *values_path = NULL;
 | 
						|
  char lock_path_buffer[PATH_MAX];
 | 
						|
  char keys_path_buffer[PATH_MAX];
 | 
						|
  char values_path_buffer[PATH_MAX];
 | 
						|
 | 
						|
  cache->impl.persistent.timeout = 1000;
 | 
						|
 | 
						|
  if (base_path) {
 | 
						|
    grn_snprintf(lock_path_buffer, PATH_MAX, PATH_MAX, "%s.lock", base_path);
 | 
						|
    grn_file_lock_init(ctx, &file_lock, lock_path_buffer);
 | 
						|
  } else {
 | 
						|
    grn_file_lock_init(ctx, &file_lock, NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  if (base_path) {
 | 
						|
    struct stat stat_buffer;
 | 
						|
 | 
						|
    grn_snprintf(keys_path_buffer, PATH_MAX, PATH_MAX, "%s.keys", base_path);
 | 
						|
    grn_snprintf(values_path_buffer, PATH_MAX, PATH_MAX, "%s.values", base_path);
 | 
						|
    keys_path = keys_path_buffer;
 | 
						|
    values_path = values_path_buffer;
 | 
						|
 | 
						|
    if (!grn_file_lock_acquire(ctx,
 | 
						|
                               &file_lock,
 | 
						|
                               cache->impl.persistent.timeout,
 | 
						|
                               "[cache][persistent][open]")) {
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (stat(keys_path, &stat_buffer) == 0) {
 | 
						|
      cache->impl.persistent.keys = grn_hash_open(ctx, keys_path);
 | 
						|
      if (cache->impl.persistent.keys) {
 | 
						|
        cache->impl.persistent.values = grn_ja_open(ctx, values_path);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (!cache->impl.persistent.keys) {
 | 
						|
      if (cache->impl.persistent.values) {
 | 
						|
        grn_ja_close(ctx, cache->impl.persistent.values);
 | 
						|
        cache->impl.persistent.values = NULL;
 | 
						|
      }
 | 
						|
      if (stat(keys_path, &stat_buffer) == 0) {
 | 
						|
        if (grn_hash_remove(ctx, keys_path) != GRN_SUCCESS) {
 | 
						|
          ERRNO_ERR("[cache][persistent] "
 | 
						|
                    "failed to remove path for cache keys: <%s>",
 | 
						|
                    keys_path);
 | 
						|
          goto exit;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (stat(values_path, &stat_buffer) == 0) {
 | 
						|
        if (grn_ja_remove(ctx, values_path) != GRN_SUCCESS) {
 | 
						|
          ERRNO_ERR("[cache][persistent] "
 | 
						|
                    "failed to remove path for cache values: <%s>",
 | 
						|
                    values_path);
 | 
						|
          goto exit;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!cache->impl.persistent.keys) {
 | 
						|
    cache->impl.persistent.keys =
 | 
						|
      grn_hash_create(ctx,
 | 
						|
                      keys_path,
 | 
						|
                      GRN_CACHE_MAX_KEY_SIZE,
 | 
						|
                      sizeof(grn_cache_entry_persistent),
 | 
						|
                      GRN_OBJ_KEY_VAR_SIZE);
 | 
						|
    if (!cache->impl.persistent.keys) {
 | 
						|
      ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | 
						|
          "[cache][persistent] failed to create cache keys storage: <%s>",
 | 
						|
          keys_path ? keys_path : "(memory)");
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
    cache->impl.persistent.values =
 | 
						|
      grn_ja_create(ctx,
 | 
						|
                    values_path,
 | 
						|
                    1 << 16,
 | 
						|
                    0);
 | 
						|
    if (!cache->impl.persistent.values) {
 | 
						|
      grn_hash_close(ctx, cache->impl.persistent.keys);
 | 
						|
      ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | 
						|
          "[cache][persistent] failed to create cache values storage: <%s>",
 | 
						|
          values_path ? values_path : "(memory)");
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    grn_cache_entry_persistent *entry;
 | 
						|
    grn_id root_id;
 | 
						|
    int added;
 | 
						|
 | 
						|
    root_id = grn_hash_add(ctx,
 | 
						|
                           cache->impl.persistent.keys,
 | 
						|
                           GRN_CACHE_PERSISTENT_ROOT_KEY,
 | 
						|
                           GRN_CACHE_PERSISTENT_ROOT_KEY_LEN,
 | 
						|
                           (void **)&entry,
 | 
						|
                           &added);
 | 
						|
    if (root_id != GRN_CACHE_PERSISTENT_ROOT_ID) {
 | 
						|
      grn_ja_close(ctx, cache->impl.persistent.values);
 | 
						|
      grn_hash_close(ctx, cache->impl.persistent.keys);
 | 
						|
      if (values_path) {
 | 
						|
        grn_ja_remove(ctx, values_path);
 | 
						|
      }
 | 
						|
      if (keys_path) {
 | 
						|
        grn_hash_remove(ctx, keys_path);
 | 
						|
      }
 | 
						|
      ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | 
						|
          "[cache][persistent] broken cache keys storage: broken root: <%s>",
 | 
						|
          keys_path ? keys_path : "(memory)");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (added) {
 | 
						|
      entry->data.next = root_id;
 | 
						|
      entry->data.prev = root_id;
 | 
						|
      entry->data.modified_time.tv_sec = 0;
 | 
						|
      entry->data.modified_time.tv_nsec = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    grn_cache_entry_persistent *entry;
 | 
						|
    grn_id metadata_id;
 | 
						|
    int added;
 | 
						|
 | 
						|
    metadata_id = grn_hash_add(ctx,
 | 
						|
                               cache->impl.persistent.keys,
 | 
						|
                               GRN_CACHE_PERSISTENT_METADATA_KEY,
 | 
						|
                               GRN_CACHE_PERSISTENT_METADATA_KEY_LEN,
 | 
						|
                               (void **)&entry,
 | 
						|
                               &added);
 | 
						|
    if (metadata_id != GRN_CACHE_PERSISTENT_METADATA_ID) {
 | 
						|
      grn_ja_close(ctx, cache->impl.persistent.values);
 | 
						|
      grn_hash_close(ctx, cache->impl.persistent.keys);
 | 
						|
      if (values_path) {
 | 
						|
        grn_ja_remove(ctx, values_path);
 | 
						|
      }
 | 
						|
      if (keys_path) {
 | 
						|
        grn_hash_remove(ctx, keys_path);
 | 
						|
      }
 | 
						|
      ERR(ctx->rc == GRN_SUCCESS ? GRN_FILE_CORRUPT : ctx->rc,
 | 
						|
          "[cache][persistent] broken cache keys storage: broken metadata: <%s>",
 | 
						|
          keys_path ? keys_path : "(memory)");
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
 | 
						|
    if (added) {
 | 
						|
      entry->metadata.max_nentries = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
 | 
						|
      entry->metadata.nfetches = 0;
 | 
						|
      entry->metadata.nhits = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
exit :
 | 
						|
  grn_file_lock_release(ctx, &file_lock);
 | 
						|
  grn_file_lock_fin(ctx, &file_lock);
 | 
						|
}
 | 
						|
 | 
						|
static grn_cache *
 | 
						|
grn_cache_open_raw(grn_ctx *ctx,
 | 
						|
                   grn_bool is_memory,
 | 
						|
                   const char *base_path)
 | 
						|
{
 | 
						|
  grn_cache *cache = NULL;
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
  cache = GRN_CALLOC(sizeof(grn_cache));
 | 
						|
  if (!cache) {
 | 
						|
    ERR(GRN_NO_MEMORY_AVAILABLE, "[cache] failed to allocate grn_cache");
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  cache->ctx = ctx;
 | 
						|
  cache->is_memory = is_memory;
 | 
						|
  if (cache->is_memory) {
 | 
						|
    grn_cache_open_memory(ctx, cache);
 | 
						|
  } else {
 | 
						|
    grn_cache_open_persistent(ctx, cache, base_path);
 | 
						|
  }
 | 
						|
  if (ctx->rc != GRN_SUCCESS) {
 | 
						|
    GRN_FREE(cache);
 | 
						|
    cache = NULL;
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
exit :
 | 
						|
  GRN_API_RETURN(cache);
 | 
						|
}
 | 
						|
 | 
						|
grn_cache *
 | 
						|
grn_cache_open(grn_ctx *ctx)
 | 
						|
{
 | 
						|
  const char *base_path = NULL;
 | 
						|
  grn_bool is_memory;
 | 
						|
 | 
						|
  if (grn_cache_default_base_path[0] != '\0') {
 | 
						|
    base_path = grn_cache_default_base_path;
 | 
						|
  }
 | 
						|
 | 
						|
  if (base_path) {
 | 
						|
    is_memory = GRN_FALSE;
 | 
						|
  } else {
 | 
						|
    char grn_cache_type_env[GRN_ENV_BUFFER_SIZE];
 | 
						|
    grn_getenv("GRN_CACHE_TYPE", grn_cache_type_env, GRN_ENV_BUFFER_SIZE);
 | 
						|
    if (strcmp(grn_cache_type_env, "persistent") == 0) {
 | 
						|
      is_memory = GRN_FALSE;
 | 
						|
    } else {
 | 
						|
      is_memory = GRN_TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return grn_cache_open_raw(ctx, is_memory, base_path);
 | 
						|
}
 | 
						|
 | 
						|
grn_cache *
 | 
						|
grn_persistent_cache_open(grn_ctx *ctx, const char *base_path)
 | 
						|
{
 | 
						|
  grn_bool is_memory = GRN_FALSE;
 | 
						|
  return grn_cache_open_raw(ctx, is_memory, base_path);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_close_memory(grn_ctx *ctx, grn_cache *cache)
 | 
						|
{
 | 
						|
  grn_cache_entry_memory *vp;
 | 
						|
 | 
						|
  GRN_HASH_EACH(ctx, cache->impl.memory.hash, id, NULL, NULL, &vp, {
 | 
						|
    grn_obj_close(ctx, vp->value);
 | 
						|
  });
 | 
						|
  grn_hash_close(ctx, cache->impl.memory.hash);
 | 
						|
  MUTEX_FIN(cache->impl.memory.mutex);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_close_persistent(grn_ctx *ctx, grn_cache *cache)
 | 
						|
{
 | 
						|
  grn_hash_close(ctx, cache->impl.persistent.keys);
 | 
						|
  grn_ja_close(ctx, cache->impl.persistent.values);
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_cache_close(grn_ctx *ctx_not_used, grn_cache *cache)
 | 
						|
{
 | 
						|
  grn_ctx *ctx = cache->ctx;
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
 | 
						|
  if (cache->is_memory) {
 | 
						|
    grn_cache_close_memory(ctx, cache);
 | 
						|
  } else {
 | 
						|
    grn_cache_close_persistent(ctx, cache);
 | 
						|
  }
 | 
						|
  GRN_FREE(cache);
 | 
						|
 | 
						|
  GRN_API_RETURN(ctx->rc);
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_cache_current_set(grn_ctx *ctx, grn_cache *cache)
 | 
						|
{
 | 
						|
  grn_cache_current = cache;
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
grn_cache *
 | 
						|
grn_cache_current_get(grn_ctx *ctx)
 | 
						|
{
 | 
						|
  return grn_cache_current;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_cache_init(void)
 | 
						|
{
 | 
						|
  grn_ctx *ctx = &grn_cache_ctx;
 | 
						|
 | 
						|
  grn_ctx_init(ctx, 0);
 | 
						|
 | 
						|
  grn_cache_default = grn_cache_open(ctx);
 | 
						|
  grn_cache_current_set(ctx, grn_cache_default);
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_cache_default_reopen(void)
 | 
						|
{
 | 
						|
  grn_ctx *ctx = &grn_cache_ctx;
 | 
						|
  grn_cache *new_default;
 | 
						|
  grn_bool default_is_current;
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
 | 
						|
  new_default = grn_cache_open(ctx);
 | 
						|
  if (!new_default) {
 | 
						|
    GRN_API_RETURN(ctx->rc);
 | 
						|
  }
 | 
						|
 | 
						|
  default_is_current = (grn_cache_default == grn_cache_current_get(ctx));
 | 
						|
  if (default_is_current) {
 | 
						|
    grn_cache_current_set(ctx, new_default);
 | 
						|
  }
 | 
						|
 | 
						|
  if (grn_cache_default) {
 | 
						|
    grn_cache_close(ctx, grn_cache_default);
 | 
						|
  }
 | 
						|
  grn_cache_default = new_default;
 | 
						|
 | 
						|
  GRN_API_RETURN(ctx->rc);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_expire_entry_memory(grn_cache *cache, grn_cache_entry_memory *ce)
 | 
						|
{
 | 
						|
  ce->prev->next = ce->next;
 | 
						|
  ce->next->prev = ce->prev;
 | 
						|
  grn_obj_close(cache->ctx, ce->value);
 | 
						|
  grn_hash_delete_by_id(cache->ctx, cache->impl.memory.hash, ce->id, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_entry_persistent_delete_link(grn_cache *cache,
 | 
						|
                                       grn_cache_entry_persistent *entry)
 | 
						|
{
 | 
						|
  grn_ctx *ctx = cache->ctx;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_cache_entry_persistent *prev_entry;
 | 
						|
  grn_cache_entry_persistent *next_entry;
 | 
						|
 | 
						|
  prev_entry =
 | 
						|
    (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                      keys,
 | 
						|
                                                      entry->data.prev,
 | 
						|
                                                      NULL);
 | 
						|
  next_entry =
 | 
						|
    (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                      keys,
 | 
						|
                                                      entry->data.next,
 | 
						|
                                                      NULL);
 | 
						|
  prev_entry->data.next = entry->data.next;
 | 
						|
  next_entry->data.prev = entry->data.prev;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_entry_persistent_prepend_link(grn_cache *cache,
 | 
						|
                                        grn_cache_entry_persistent *entry,
 | 
						|
                                        grn_id entry_id,
 | 
						|
                                        grn_cache_entry_persistent *head_entry,
 | 
						|
                                        grn_id head_entry_id)
 | 
						|
{
 | 
						|
  grn_ctx *ctx = cache->ctx;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_cache_entry_persistent *head_next_entry;
 | 
						|
 | 
						|
  entry->data.next = head_entry->data.next;
 | 
						|
  entry->data.prev = head_entry_id;
 | 
						|
  head_next_entry =
 | 
						|
    (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                      keys,
 | 
						|
                                                      head_entry->data.next,
 | 
						|
                                                      NULL);
 | 
						|
  head_next_entry->data.prev = entry_id;
 | 
						|
  head_entry->data.next = entry_id;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_expire_entry_persistent(grn_cache *cache,
 | 
						|
                                  grn_cache_entry_persistent *entry,
 | 
						|
                                  grn_id cache_id)
 | 
						|
{
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_ja *values = cache->impl.persistent.values;
 | 
						|
 | 
						|
  grn_cache_entry_persistent_delete_link(cache, entry);
 | 
						|
  grn_ja_put(cache->ctx, values, cache_id, NULL, 0, GRN_OBJ_SET, NULL);
 | 
						|
  grn_hash_delete_by_id(cache->ctx, keys, cache_id, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_expire_memory_without_lock(grn_cache *cache, int32_t size)
 | 
						|
{
 | 
						|
  grn_cache_entry_memory *ce0 =
 | 
						|
    (grn_cache_entry_memory *)(&(cache->impl.memory));
 | 
						|
  while (ce0 != ce0->prev && size--) {
 | 
						|
    grn_cache_expire_entry_memory(cache, ce0->prev);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_expire_persistent_without_lock(grn_cache *cache, int32_t size)
 | 
						|
{
 | 
						|
  grn_ctx *ctx = cache->ctx;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_cache_entry_persistent *head_entry;
 | 
						|
 | 
						|
  head_entry =
 | 
						|
    (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                      keys,
 | 
						|
                                                      GRN_CACHE_PERSISTENT_ROOT_ID,
 | 
						|
                                                      NULL);
 | 
						|
  while (head_entry->data.prev != GRN_CACHE_PERSISTENT_ROOT_ID &&
 | 
						|
         size > 0) {
 | 
						|
    grn_cache_entry_persistent *tail_entry;
 | 
						|
    tail_entry =
 | 
						|
      (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                        keys,
 | 
						|
                                                        head_entry->data.prev,
 | 
						|
                                                        NULL);
 | 
						|
    grn_cache_expire_entry_persistent(cache, tail_entry, head_entry->data.prev);
 | 
						|
    size--;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_cache_set_max_n_entries_memory(grn_ctx *ctx,
 | 
						|
                                   grn_cache *cache,
 | 
						|
                                   unsigned int n)
 | 
						|
{
 | 
						|
  uint32_t current_max_n_entries;
 | 
						|
 | 
						|
  MUTEX_LOCK(cache->impl.memory.mutex);
 | 
						|
  current_max_n_entries = cache->impl.memory.max_nentries;
 | 
						|
  cache->impl.memory.max_nentries = n;
 | 
						|
  if (n < current_max_n_entries) {
 | 
						|
    grn_cache_expire_memory_without_lock(cache, current_max_n_entries - n);
 | 
						|
  }
 | 
						|
  MUTEX_UNLOCK(cache->impl.memory.mutex);
 | 
						|
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_cache_set_max_n_entries_persistent(grn_ctx *ctx,
 | 
						|
                                       grn_cache *cache,
 | 
						|
                                       unsigned int n)
 | 
						|
{
 | 
						|
  grn_rc rc;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_cache_entry_persistent *metadata_entry;
 | 
						|
  uint32_t current_max_n_entries;
 | 
						|
 | 
						|
  rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | 
						|
  if (rc != GRN_SUCCESS) {
 | 
						|
    return rc;
 | 
						|
  }
 | 
						|
 | 
						|
  metadata_entry =
 | 
						|
      (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                        keys,
 | 
						|
                                                        GRN_CACHE_PERSISTENT_METADATA_ID,
 | 
						|
                                                        NULL);
 | 
						|
 | 
						|
  current_max_n_entries = metadata_entry->metadata.max_nentries;
 | 
						|
  metadata_entry->metadata.max_nentries = n;
 | 
						|
  if (n < current_max_n_entries) {
 | 
						|
    grn_cache_expire_persistent_without_lock(cache, current_max_n_entries - n);
 | 
						|
  }
 | 
						|
  grn_io_unlock(keys->io);
 | 
						|
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_cache_set_max_n_entries(grn_ctx *ctx, grn_cache *cache, unsigned int n)
 | 
						|
{
 | 
						|
  if (!cache) {
 | 
						|
    return GRN_INVALID_ARGUMENT;
 | 
						|
  }
 | 
						|
 | 
						|
  if (cache->is_memory) {
 | 
						|
    return grn_cache_set_max_n_entries_memory(cache->ctx, cache, n);
 | 
						|
  } else {
 | 
						|
    return grn_cache_set_max_n_entries_persistent(cache->ctx, cache, n);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t
 | 
						|
grn_cache_get_max_n_entries_memory(grn_ctx *ctx, grn_cache *cache)
 | 
						|
{
 | 
						|
  return cache->impl.memory.max_nentries;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t
 | 
						|
grn_cache_get_max_n_entries_persistent(grn_ctx *ctx, grn_cache *cache)
 | 
						|
{
 | 
						|
  grn_rc rc;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_cache_entry_persistent *metadata_entry;
 | 
						|
  uint32_t current_max_n_entries;
 | 
						|
 | 
						|
  rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | 
						|
  if (rc != GRN_SUCCESS) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  metadata_entry =
 | 
						|
      (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                        keys,
 | 
						|
                                                        GRN_CACHE_PERSISTENT_METADATA_ID,
 | 
						|
                                                        NULL);
 | 
						|
  current_max_n_entries = metadata_entry->metadata.max_nentries;
 | 
						|
  grn_io_unlock(keys->io);
 | 
						|
 | 
						|
  return current_max_n_entries;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t
 | 
						|
grn_cache_get_max_n_entries(grn_ctx *ctx, grn_cache *cache)
 | 
						|
{
 | 
						|
  if (!cache) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (cache->is_memory) {
 | 
						|
    return grn_cache_get_max_n_entries_memory(cache->ctx, cache);
 | 
						|
  } else {
 | 
						|
    return grn_cache_get_max_n_entries_persistent(cache->ctx, cache);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_get_statistics_memory(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                                grn_cache_statistics *statistics)
 | 
						|
{
 | 
						|
  MUTEX_LOCK(cache->impl.memory.mutex);
 | 
						|
  statistics->nentries = GRN_HASH_SIZE(cache->impl.memory.hash);
 | 
						|
  statistics->max_nentries = cache->impl.memory.max_nentries;
 | 
						|
  statistics->nfetches = cache->impl.memory.nfetches;
 | 
						|
  statistics->nhits = cache->impl.memory.nhits;
 | 
						|
  MUTEX_UNLOCK(cache->impl.memory.mutex);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_get_statistics_persistent(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                                    grn_cache_statistics *statistics)
 | 
						|
{
 | 
						|
  grn_rc rc = GRN_INVALID_ARGUMENT;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_cache_entry_persistent *metadata_entry;
 | 
						|
 | 
						|
  rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | 
						|
  if (rc != GRN_SUCCESS) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  metadata_entry =
 | 
						|
      (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                        keys,
 | 
						|
                                                        GRN_CACHE_PERSISTENT_METADATA_ID,
 | 
						|
                                                        NULL);
 | 
						|
 | 
						|
  statistics->nentries = GRN_HASH_SIZE(keys);
 | 
						|
  statistics->max_nentries = metadata_entry->metadata.max_nentries;
 | 
						|
  statistics->nfetches = metadata_entry->metadata.nfetches;
 | 
						|
  statistics->nhits = metadata_entry->metadata.nhits;
 | 
						|
 | 
						|
  grn_io_unlock(keys->io);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_cache_get_statistics(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                         grn_cache_statistics *statistics)
 | 
						|
{
 | 
						|
  if (cache->is_memory) {
 | 
						|
    return grn_cache_get_statistics_memory(ctx, cache, statistics);
 | 
						|
  } else {
 | 
						|
    return grn_cache_get_statistics_persistent(ctx, cache, statistics);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_cache_fetch_memory(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                       const char *key, uint32_t key_len,
 | 
						|
                       grn_obj *output)
 | 
						|
{
 | 
						|
  /* TODO: How about GRN_NOT_FOUND? */
 | 
						|
  grn_rc rc = GRN_INVALID_ARGUMENT;
 | 
						|
  grn_cache_entry_memory *ce;
 | 
						|
 | 
						|
  MUTEX_LOCK(cache->impl.memory.mutex);
 | 
						|
  cache->impl.memory.nfetches++;
 | 
						|
  if (grn_hash_get(cache->ctx, cache->impl.memory.hash, key, key_len,
 | 
						|
                   (void **)&ce)) {
 | 
						|
    if (ce->tv.tv_sec <= grn_db_get_last_modified(ctx, ctx->impl->db)) {
 | 
						|
      grn_cache_expire_entry_memory(cache, ce);
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
    rc = GRN_SUCCESS;
 | 
						|
    GRN_TEXT_PUT(ctx,
 | 
						|
                 output,
 | 
						|
                 GRN_TEXT_VALUE(ce->value),
 | 
						|
                 GRN_TEXT_LEN(ce->value));
 | 
						|
    ce->prev->next = ce->next;
 | 
						|
    ce->next->prev = ce->prev;
 | 
						|
    {
 | 
						|
      grn_cache_entry_memory *ce0 =
 | 
						|
        (grn_cache_entry_memory *)(&(cache->impl.memory));
 | 
						|
      ce->next = ce0->next;
 | 
						|
      ce->prev = ce0;
 | 
						|
      ce0->next->prev = ce;
 | 
						|
      ce0->next = ce;
 | 
						|
    }
 | 
						|
    cache->impl.memory.nhits++;
 | 
						|
  }
 | 
						|
exit :
 | 
						|
  MUTEX_UNLOCK(cache->impl.memory.mutex);
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_cache_fetch_persistent(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                           const char *key, uint32_t key_len,
 | 
						|
                           grn_obj *output)
 | 
						|
{
 | 
						|
  /* TODO: How about GRN_NOT_FOUND? */
 | 
						|
  grn_rc rc = GRN_INVALID_ARGUMENT;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_ja *values = cache->impl.persistent.values;
 | 
						|
  grn_id cache_id;
 | 
						|
  grn_cache_entry_persistent *entry;
 | 
						|
  grn_cache_entry_persistent *metadata_entry;
 | 
						|
 | 
						|
  if (key_len == GRN_CACHE_PERSISTENT_ROOT_KEY_LEN &&
 | 
						|
      memcmp(key,
 | 
						|
             GRN_CACHE_PERSISTENT_ROOT_KEY,
 | 
						|
             GRN_CACHE_PERSISTENT_ROOT_KEY_LEN) == 0) {
 | 
						|
    return rc;
 | 
						|
  }
 | 
						|
 | 
						|
  rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | 
						|
  if (rc != GRN_SUCCESS) {
 | 
						|
    return rc;
 | 
						|
  }
 | 
						|
 | 
						|
  /* TODO: How about GRN_NOT_FOUND? */
 | 
						|
  rc = GRN_INVALID_ARGUMENT;
 | 
						|
 | 
						|
  metadata_entry =
 | 
						|
      (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                        keys,
 | 
						|
                                                        GRN_CACHE_PERSISTENT_METADATA_ID,
 | 
						|
                                                        NULL);
 | 
						|
  metadata_entry->metadata.nfetches++;
 | 
						|
 | 
						|
  cache_id = grn_hash_get(cache->ctx, keys, key, key_len, (void **)&entry);
 | 
						|
  if (cache_id == GRN_ID_NIL) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  if (cache_id != GRN_ID_NIL) {
 | 
						|
    if (entry->data.modified_time.tv_sec <=
 | 
						|
        grn_db_get_last_modified(ctx, ctx->impl->db)) {
 | 
						|
      grn_cache_expire_entry_persistent(cache, entry, cache_id);
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
 | 
						|
    rc = GRN_SUCCESS;
 | 
						|
    grn_ja_get_value(ctx, values, cache_id, output);
 | 
						|
    grn_cache_entry_persistent_delete_link(cache, entry);
 | 
						|
    {
 | 
						|
      grn_cache_entry_persistent *head_entry;
 | 
						|
      head_entry =
 | 
						|
        (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                          keys,
 | 
						|
                                                          GRN_CACHE_PERSISTENT_ROOT_ID,
 | 
						|
                                                          NULL);
 | 
						|
      grn_cache_entry_persistent_prepend_link(cache,
 | 
						|
                                              entry,
 | 
						|
                                              cache_id,
 | 
						|
                                              head_entry,
 | 
						|
                                              GRN_CACHE_PERSISTENT_ROOT_ID);
 | 
						|
    }
 | 
						|
    metadata_entry->metadata.nhits++;
 | 
						|
  }
 | 
						|
 | 
						|
exit :
 | 
						|
  grn_io_unlock(keys->io);
 | 
						|
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_cache_fetch(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                const char *key, uint32_t key_len,
 | 
						|
                grn_obj *output)
 | 
						|
{
 | 
						|
  if (!ctx->impl || !ctx->impl->db) { return GRN_INVALID_ARGUMENT; }
 | 
						|
 | 
						|
  if (cache->is_memory) {
 | 
						|
    return grn_cache_fetch_memory(ctx, cache, key, key_len, output);
 | 
						|
  } else {
 | 
						|
    return grn_cache_fetch_persistent(ctx, cache, key, key_len, output);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_update_memory(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                        const char *key, uint32_t key_len,
 | 
						|
                        grn_obj *value)
 | 
						|
{
 | 
						|
  grn_id id;
 | 
						|
  int added = 0;
 | 
						|
  grn_cache_entry_memory *ce;
 | 
						|
  grn_rc rc = GRN_SUCCESS;
 | 
						|
  grn_obj *old = NULL;
 | 
						|
  grn_obj *obj = NULL;
 | 
						|
 | 
						|
  if (cache->impl.memory.max_nentries == 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  MUTEX_LOCK(cache->impl.memory.mutex);
 | 
						|
  obj = grn_obj_open(cache->ctx, GRN_BULK, 0, GRN_DB_TEXT);
 | 
						|
  if (!obj) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  GRN_TEXT_PUT(cache->ctx, obj, GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));
 | 
						|
  id = grn_hash_add(cache->ctx, cache->impl.memory.hash, key, key_len,
 | 
						|
                    (void **)&ce, &added);
 | 
						|
  if (id) {
 | 
						|
    if (!added) {
 | 
						|
      old = ce->value;
 | 
						|
      ce->prev->next = ce->next;
 | 
						|
      ce->next->prev = ce->prev;
 | 
						|
    }
 | 
						|
    ce->id = id;
 | 
						|
    ce->value = obj;
 | 
						|
    ce->tv = ctx->impl->tv;
 | 
						|
    {
 | 
						|
      grn_cache_entry_memory *ce0 =
 | 
						|
        (grn_cache_entry_memory *)(&(cache->impl.memory));
 | 
						|
      ce->next = ce0->next;
 | 
						|
      ce->prev = ce0;
 | 
						|
      ce0->next->prev = ce;
 | 
						|
      ce0->next = ce;
 | 
						|
    }
 | 
						|
    if (GRN_HASH_SIZE(cache->impl.memory.hash) >
 | 
						|
        cache->impl.memory.max_nentries) {
 | 
						|
      grn_cache_expire_entry_memory(cache, cache->impl.memory.prev);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    rc = GRN_NO_MEMORY_AVAILABLE;
 | 
						|
  }
 | 
						|
exit :
 | 
						|
  if (rc) { grn_obj_close(cache->ctx, obj); }
 | 
						|
  if (old) { grn_obj_close(cache->ctx, old); }
 | 
						|
  MUTEX_UNLOCK(cache->impl.memory.mutex);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_update_persistent(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                            const char *key, uint32_t key_len,
 | 
						|
                            grn_obj *value)
 | 
						|
{
 | 
						|
  grn_rc rc;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
  grn_ja *values = cache->impl.persistent.values;
 | 
						|
  grn_cache_entry_persistent *metadata_entry;
 | 
						|
  grn_id cache_id;
 | 
						|
  grn_cache_entry_persistent *entry;
 | 
						|
  int added;
 | 
						|
 | 
						|
  if (key_len == GRN_CACHE_PERSISTENT_ROOT_KEY_LEN &&
 | 
						|
      memcmp(key,
 | 
						|
             GRN_CACHE_PERSISTENT_ROOT_KEY,
 | 
						|
             GRN_CACHE_PERSISTENT_ROOT_KEY_LEN) == 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (key_len == GRN_CACHE_PERSISTENT_METADATA_KEY_LEN &&
 | 
						|
      memcmp(key,
 | 
						|
             GRN_CACHE_PERSISTENT_METADATA_KEY,
 | 
						|
             GRN_CACHE_PERSISTENT_METADATA_KEY_LEN) == 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | 
						|
  if (rc != GRN_SUCCESS) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  metadata_entry =
 | 
						|
    (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                      keys,
 | 
						|
                                                      GRN_CACHE_PERSISTENT_METADATA_ID,
 | 
						|
                                                      NULL);
 | 
						|
  if (metadata_entry->metadata.max_nentries == 0) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  cache_id = grn_hash_add(cache->ctx, keys, key, key_len, (void **)&entry,
 | 
						|
                          &added);
 | 
						|
  if (cache_id) {
 | 
						|
    grn_cache_entry_persistent *head_entry;
 | 
						|
 | 
						|
    if (!added) {
 | 
						|
      grn_cache_entry_persistent_delete_link(cache, entry);
 | 
						|
    }
 | 
						|
    entry->data.modified_time = ctx->impl->tv;
 | 
						|
 | 
						|
    grn_ja_put(cache->ctx, values, cache_id,
 | 
						|
               GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value),
 | 
						|
               GRN_OBJ_SET, NULL);
 | 
						|
 | 
						|
    head_entry =
 | 
						|
      (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                        keys,
 | 
						|
                                                        GRN_CACHE_PERSISTENT_ROOT_ID,
 | 
						|
                                                        NULL);
 | 
						|
    grn_cache_entry_persistent_prepend_link(cache,
 | 
						|
                                            entry,
 | 
						|
                                            cache_id,
 | 
						|
                                            head_entry,
 | 
						|
                                            GRN_CACHE_PERSISTENT_ROOT_ID);
 | 
						|
    if (GRN_HASH_SIZE(keys) > metadata_entry->metadata.max_nentries) {
 | 
						|
      grn_cache_entry_persistent *tail_entry;
 | 
						|
      tail_entry =
 | 
						|
        (grn_cache_entry_persistent *)grn_hash_get_value_(ctx,
 | 
						|
                                                          keys,
 | 
						|
                                                          head_entry->data.prev,
 | 
						|
                                                          NULL);
 | 
						|
      grn_cache_expire_entry_persistent(cache,
 | 
						|
                                        tail_entry,
 | 
						|
                                        head_entry->data.prev);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
exit :
 | 
						|
  grn_io_unlock(keys->io);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_cache_update(grn_ctx *ctx, grn_cache *cache,
 | 
						|
                 const char *key, uint32_t key_len, grn_obj *value)
 | 
						|
{
 | 
						|
  if (!ctx->impl) { return; }
 | 
						|
 | 
						|
  if (cache->is_memory) {
 | 
						|
    grn_cache_update_memory(ctx, cache, key, key_len, value);
 | 
						|
  } else {
 | 
						|
    grn_cache_update_persistent(ctx, cache, key, key_len, value);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_expire_memory(grn_cache *cache, int32_t size)
 | 
						|
{
 | 
						|
  MUTEX_LOCK(cache->impl.memory.mutex);
 | 
						|
  grn_cache_expire_memory_without_lock(cache, size);
 | 
						|
  MUTEX_UNLOCK(cache->impl.memory.mutex);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_cache_expire_persistent(grn_cache *cache, int32_t size)
 | 
						|
{
 | 
						|
  grn_rc rc;
 | 
						|
  grn_ctx *ctx = cache->ctx;
 | 
						|
  grn_hash *keys = cache->impl.persistent.keys;
 | 
						|
 | 
						|
  rc = grn_io_lock(ctx, keys->io, cache->impl.persistent.timeout);
 | 
						|
  if (rc != GRN_SUCCESS) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  grn_cache_expire_persistent_without_lock(cache, size);
 | 
						|
 | 
						|
  grn_io_unlock(keys->io);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_cache_expire(grn_cache *cache, int32_t size)
 | 
						|
{
 | 
						|
  if (cache->is_memory) {
 | 
						|
    grn_cache_expire_memory(cache, size);
 | 
						|
  } else {
 | 
						|
    grn_cache_expire_persistent(cache, size);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_cache_fin(void)
 | 
						|
{
 | 
						|
  grn_ctx *ctx = &grn_cache_ctx;
 | 
						|
 | 
						|
  grn_cache_current_set(ctx, NULL);
 | 
						|
 | 
						|
  if (grn_cache_default) {
 | 
						|
    grn_cache_close(ctx, grn_cache_default);
 | 
						|
    grn_cache_default = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  grn_ctx_fin(ctx);
 | 
						|
}
 |