Code Search for Developers
 
 
  

aging.c from Gtk-Gnutella at Krugle


Show aging.c syntax highlighted

/*
 * $Id: aging.c 13502 2007-05-05 18:24:07Z cbiere $
 *
 * Copyright (c) 2004, Raphael Manfredi
 *
 *----------------------------------------------------------------------
 * This file is part of gtk-gnutella.
 *
 *  gtk-gnutella is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  gtk-gnutella 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with gtk-gnutella; if not, write to the Free Software
 *  Foundation, Inc.:
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *----------------------------------------------------------------------
 */

/**
 * @ingroup lib
 * @file
 *
 * Hash table with aging values, removed automatically after
 * some time has elapsed.
 *
 * @author Raphael Manfredi
 * @date 2004
 */

#include "common.h"

RCSID("$Id: aging.c 13502 2007-05-05 18:24:07Z cbiere $")

#include "aging.h"
#include "cq.h"
#include "misc.h"
#include "tm.h"
#include "walloc.h"
#include "override.h"		/* Must be the last header included */

/**
 * The hash table is the central piece, but we also have a `value freeing'
 * callback, since the values can expire automatically.
 */

enum aging_magic {
	AGING_MAGIC	= 0xb8e2fac3U
};

struct aging {
	enum aging_magic magic;			/**< Magic number */
	GHashTable *table;		/**< The table holding values */
	aging_free_t kfree;		/**< The freeing callback for keys */
	gint delay;				/**< Initial aging delay, in seconds */
};

/**
 * We wrap the values we insert in the table, since each value must keep
 * track of its insertion time, and cleanup event.
 */
struct aging_value {
	gpointer value;			/**< The value they inserted in the table */
	gpointer key;			/**< The associated key object */
	cevent_t *cq_ev;		/**< Scheduled cleanup event */
	struct aging *ag;		/**< Holding container */
	time_t last_insert;		/**< Last insertion time */
	gint ttl;				/**< Time to live */
};

/**
 * Create new aging container.
 *
 * @param delay		the aging delay, in seconds, for entries
 * @param hash		the hashing function for the keys in the hash table
 * @param eq		the equality function for the keys in the hash table
 * @param kfree		the key freeing callback, NULL if none.
 *
 * @return opaque handle to the container.
 */
struct aging *
aging_make(gint delay, GHashFunc hash, GEqualFunc eq, aging_free_t kfree)
{
	struct aging *ag;

	ag = walloc(sizeof *ag);
	ag->magic = AGING_MAGIC;
	ag->table = g_hash_table_new(hash, eq);
	ag->kfree = kfree;
	ag->delay = delay;

	return ag;
}

/**
 * Free keys and values from the aging table.
 */
static void
aging_free_kv(gpointer key, gpointer value, gpointer udata)
{
	struct aging *ag = udata;
	struct aging_value *aval = value;

	g_assert(aval->ag == ag);

	if (ag->kfree != NULL)
		(*ag->kfree)(key, aval->value);

	cq_cancel(callout_queue, &aval->cq_ev);

	wfree(aval, sizeof *aval);
}

/**
 * Destroy container, freeing all keys and values.
 */
void
aging_destroy(struct aging *ag)
{
	g_assert(ag->magic == AGING_MAGIC);

	g_hash_table_foreach(ag->table, aging_free_kv, ag);
	g_hash_table_destroy(ag->table);
	ag->magic = 0;
	wfree(ag, sizeof *ag);
}

/**
 * Expire value entry.
 */
static void
aging_expire(cqueue_t *unused_cq, gpointer obj)
{
	struct aging_value *aval = obj;
	struct aging *ag = aval->ag;

	(void) unused_cq;
	aval->cq_ev = NULL;

	g_hash_table_remove(ag->table, aval->key);
	aging_free_kv(aval->key, aval, ag);
}

/**
 * Lookup value in table.
 */
gpointer
aging_lookup(struct aging *ag, gpointer key)
{
	struct aging_value *aval;

	g_assert(ag->magic == AGING_MAGIC);

	aval = g_hash_table_lookup(ag->table, key);
	return aval == NULL ? NULL : aval->value;
}

/**
 * Remove value associated with key, dispose of items (key and value)
 * stored in the hash table, but leave the function parameter alone.
 */
void
aging_remove(struct aging *ag, gpointer key)
{
	struct aging_value *aval;
	gpointer okey, ovalue;

	g_assert(ag->magic == AGING_MAGIC);

	if (!g_hash_table_lookup_extended(ag->table, key, &okey, &ovalue))
		return;

	aval = ovalue;

	g_assert(aval->key == okey);
	g_assert(aval->ag == ag);

	g_hash_table_remove(ag->table, aval->key);
	aging_free_kv(aval->key, aval, ag);
}

/**
 * Add value to the table.
 *
 * If it was already present, its lifetime is augmented by the aging delay.
 *
 * The key argument is freed immediately if there is a free routine for
 * keys and the key was present in the table.
 *
 * The previous value is freed and replaced by the new one if there is
 * an insertion conflict and the value pointers are different.
 */
void
aging_insert(struct aging *ag, gpointer key, gpointer value)
{
	gboolean found;
	gpointer okey, ovalue;
	time_t now = tm_time();
	struct aging_value *aval;

	g_assert(ag->magic == AGING_MAGIC);

	found = g_hash_table_lookup_extended(ag->table, key, &okey, &ovalue);
	if (found) {
		aval = ovalue;

		g_assert(aval->key == okey);

		if (aval->key != key && ag->kfree != NULL) {
			/* We discard the new and keep the old key instead */
			(*ag->kfree)(key, aval->value);
		}

		g_assert(aval->cq_ev != NULL);

		/*
		 * Value existed for this key, prolonge its life.
		 */

		aval->value = value;
		aval->ttl -= delta_time(now, aval->last_insert);
		aval->ttl += ag->delay;
		aval->ttl = MAX(aval->ttl, 1);
		aval->ttl = MIN(aval->ttl, INT_MAX / 1000);
		aval->last_insert = now;

		cq_resched(callout_queue, aval->cq_ev, 1000 * aval->ttl);
	} else {
		aval = walloc(sizeof(*aval));

		aval->value = value;
		aval->key = key;
		aval->ttl = ag->delay;
		aval->ttl = MAX(aval->ttl, 1);
		aval->ttl = MIN(aval->ttl, INT_MAX / 1000);
		aval->last_insert = now;
		aval->ag = ag;
		aval->cq_ev = cq_insert(callout_queue,
			1000 * aval->ttl, aging_expire, aval);
		g_hash_table_insert(ag->table, key, aval);
	}
}

/* vi: set ts=4: sw=4 cindent: */




See more files for this project here

Gtk-Gnutella

A GTK+ Gnutella client for Unix, efficient, reliable and fast, written in C. It has been optimized for speed and scalability, with low-memory consumption. It is meant to be left running 24x7, using little CPU and only the configured bandwidth.

Project homepage: http://sourceforge.net/projects/gtk-gnutella
Programming language(s): C
License: other

  Jmakefile
  Makefile.SH
  adns.c
  adns.h
  aging.c
  aging.h
  array.h
  atoms.c
  atoms.h
  base16.c
  base16.h
  base32.c
  base32.h
  base64.c
  base64.h
  bg.c
  bg.h
  bit_array.h
  cobs.c
  cobs.h
  cq.c
  cq.h
  crash.c
  crash.h
  crc.c
  crc.h
  dbus_util.c
  dbus_util.h
  endian.h
  eval.c
  eval.h
  event.c
  event.h
  fast_assert.c
  fast_assert.h
  fifo.c
  fifo.h
  file.c
  file.h
  fragcheck.c
  fragcheck.h
  getdate.c
  getdate.h
  getdate.y
  getline.c
  getline.h
  getphysmemsize.c
  getphysmemsize.h
  glib-missing.c
  glib-missing.h
  halloc.c
  halloc.h
  hashlist.c
  hashlist.h
  hashtable.c
  hashtable.h
  header.c
  header.h
  host_addr.c
  host_addr.h
  html.c
  html.h
  html_entities.h
  idtable.c
  idtable.h
  inputevt.c
  inputevt.h
  iovec.h
  iprange.c
  iprange.h
  iso3166.c
  iso3166.h
  list.c
  list.h
  listener.h
  magnet.c
  magnet.h
  malloc.c
  malloc.h
  misc.c
  misc.h
  options.c
  options.h
  override.h
  pagetable.c
  pagetable.h
  palloc.c
  palloc.h
  pattern.c
  pattern.h
  prop.c
  prop.h
  sbool.h
  sha1.c
  sha1.h
  slist.c
  slist.h
  socket.c
  socket.h
  sorted_array.c
  sorted_array.h
  stats.c