(cache_add): Take additional parameter specifying
[kopensolaris-gnu/glibc.git] / nscd / cache.c
index ea79c38..2faaf34 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 1998, 1999, 2003-2006, 2007 Free Software Foundation, Inc.
+/* Copyright (c) 1998, 1999, 2003-2007, 2008 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
 #include "dbg_log.h"
 
 
+/* Wrapper functions with error checking for standard functions.  */
+extern void *xcalloc (size_t n, size_t s);
+
+
 /* Number of times a value is reloaded without being used.  UINT_MAX
    means unlimited.  */
 unsigned int reload_count = DEFAULT_RELOAD_LIMIT;
@@ -131,7 +135,7 @@ cache_search (request_type type, void *key, size_t len,
 int
 cache_add (int type, const void *key, size_t len, struct datahead *packet,
           bool first, struct database_dyn *table,
-          uid_t owner)
+          uid_t owner, bool prune_wakeup)
 {
   if (__builtin_expect (debug_level >= 2, 0))
     {
@@ -151,11 +155,21 @@ cache_add (int type, const void *key, size_t len, struct datahead *packet,
   unsigned long int hash = __nis_hash (key, len) % table->head->module;
   struct hashentry *newp;
 
-  newp = mempool_alloc (table, sizeof (struct hashentry));
+  newp = mempool_alloc (table, sizeof (struct hashentry), IDX_record_data);
   /* If we cannot allocate memory, just do not do anything.  */
   if (newp == NULL)
     {
       ++table->head->addfailed;
+
+      /* If necessary mark the entry as unusable so that lookups will
+        not use it.  */
+      if (first)
+       packet->usable = false;
+
+      /* Mark the in-flight memory as unused.  */
+      for (enum in_flight idx = 0; idx < IDX_record_data; ++idx)
+       mem_in_flight.block[idx].dbidx = -1;
+
       return -1;
     }
 
@@ -166,6 +180,7 @@ cache_add (int type, const void *key, size_t len, struct datahead *packet,
   assert (newp->key + newp->len <= table->head->first_free);
   newp->owner = owner;
   newp->packet = (char *) packet - table->data;
+  assert ((newp->packet & BLOCK_ALIGN_M1) == 0);
 
   /* Put the new entry in the first position.  */
   do
@@ -197,19 +212,31 @@ cache_add (int type, const void *key, size_t len, struct datahead *packet,
           (char *) &table->head->array[hash] - (char *) table->head
           + sizeof (ref_t), MS_ASYNC);
 
-  /* Perhaps the prune thread for the data is not running in a long
-     time.  Wake it if necessary.  */
-  time_t next_wakeup = table->wakeup_time;
-  while (next_wakeup + CACHE_PRUNE_INTERVAL > packet->timeout)
-    if (atomic_compare_and_exchange_bool_acq (&table->wakeup_time,
-                                             packet->timeout,
-                                             next_wakeup) == 0)
-      {
+  /* We do not have to worry about the pruning thread if we are
+     re-adding the data since this is done by the pruning thread.  We
+     also do not have to do anything in case this is not the first
+     time the data is entered since different data heads all have the
+     same timeout.  */
+  if (first && prune_wakeup)
+    {
+      /* Perhaps the prune thread for the table is not running in a long
+        time.  Wake it if necessary.  */
+      pthread_mutex_lock (&table->prune_lock);
+      time_t next_wakeup = table->wakeup_time;
+      bool do_wakeup = false;
+      if (next_wakeup > packet->timeout + CACHE_PRUNE_INTERVAL)
+       {
+         table->wakeup_time = packet->timeout;
+         do_wakeup = true;
+       }
+      pthread_mutex_unlock (&table->prune_lock);
+      if (do_wakeup)
        pthread_cond_signal (&table->prune_cond);
-       break;
-      }
-    else
-      next_wakeup = table->wakeup_time;
+    }
+
+  /* Mark the in-flight memory as unused.  */
+  for (enum in_flight idx = 0; idx < IDX_last; ++idx)
+    mem_in_flight.block[idx].dbidx = -1;
 
   return 0;
 }
@@ -278,7 +305,20 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
      we don't need to get any lock.  It is at all timed assured that the
      linked lists are set up correctly and that no second thread prunes
      the cache.  */
-  bool mark[cnt];
+  bool *mark;
+  size_t memory_needed = cnt * sizeof (bool);
+  bool mark_use_alloca;
+  if (__builtin_expect (memory_needed <= MAX_STACK_USE, 1))
+    {
+      mark = alloca (cnt * sizeof (bool));
+      memset (mark, '\0', memory_needed);
+      mark_use_alloca = true;
+    }
+  else
+    {
+      mark = xcalloc (1, memory_needed);
+      mark_use_alloca = false;
+    }
   size_t first = cnt + 1;
   size_t last = 0;
   char *const data = table->data;
@@ -288,7 +328,8 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
     dbg_log (_("pruning %s cache; time %ld"),
             dbnames[table - dbs], (long int) now);
 
-  time_t next_timeout = LONG_MAX;
+#define NO_TIMEOUT LONG_MAX
+  time_t next_timeout = NO_TIMEOUT;
   do
     {
       ref_t run = table->head->array[--cnt];
@@ -404,7 +445,8 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
              ref_t *old = &table->head->array[first];
              ref_t run = table->head->array[first];
 
-             while (run != ENDREF)
+             assert (run != ENDREF);
+             do
                {
                  struct hashentry *runp = (struct hashentry *) (data + run);
                  struct datahead *dh
@@ -430,6 +472,7 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
                      run = runp->next;
                    }
                }
+             while (run != ENDREF);
            }
 
          ++first;
@@ -470,9 +513,14 @@ prune_cache (struct database_dyn *table, time_t now, int fd)
        }
     }
 
+  if (__builtin_expect (! mark_use_alloca, 0))
+    free (mark);
+
   /* Run garbage collection if any entry has been removed or replaced.  */
   if (any)
     gc (table);
 
-  return next_timeout - now;
+  /* If there is no entry in the database and we therefore have no new
+     timeout value, tell the caller to wake up in 24 hours.  */
+  return next_timeout == NO_TIMEOUT ? 24 * 60 * 60 : next_timeout - now;
 }