Basic resourcecollector implementation

Lets one register callback functions which to incrementally cleanup unused
resources.
This commit is contained in:
Christian Thaeter 2008-12-11 23:10:33 +01:00
parent 67386a20eb
commit 369c644ab4
6 changed files with 459 additions and 0 deletions

View file

@ -38,6 +38,7 @@ liblumibackend_a_SOURCES = \
$(liblumibackend_a_srcdir)/configentry.c \
$(liblumibackend_a_srcdir)/configitem.c \
$(liblumibackend_a_srcdir)/config_lookup.c \
$(liblumibackend_a_srcdir)/resourcecollector.c \
$(liblumibackend_a_srcdir)/mmap.c \
$(liblumibackend_a_srcdir)/mmapings.c \
$(liblumibackend_a_srcdir)/mmapcache.c
@ -57,6 +58,7 @@ noinst_HEADERS += \
$(liblumibackend_a_srcdir)/configentry.h \
$(liblumibackend_a_srcdir)/configitem.h \
$(liblumibackend_a_srcdir)/config_lookup.h \
$(liblumibackend_a_srcdir)/resourcecollector.h \
$(liblumibackend_a_srcdir)/mmap.h \
$(liblumibackend_a_srcdir)/mmapings.h \
$(liblumibackend_a_srcdir)/mmapcache.h

View file

@ -0,0 +1,187 @@
/*
resourcecollector.c - manage/collect resources when they get short
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "lib/llist.h"
#include "lib/mutex.h"
#include "lib/safeclib.h"
#include "resourcecollector.h"
#include <unistd.h>
NOBUG_DEFINE_FLAG_PARENT (resourcecollector, backend);
llist lumiera_resourcecollector_registry[LUMIERA_RESOURCE_END];
lumiera_mutex lumiera_resourcecollector_lock;
static pthread_once_t lumiera_resourcecollector_once = PTHREAD_ONCE_INIT;
struct lumiera_resourcehandler_struct
{
llist node;
lumiera_resource_handler_fn handler;
void* data;
};
static void
lumiera_resourcecollector_init_ (void)
{
NOBUG_INIT_FLAG (resourcecollector);
TRACE (resourcecollector);
for (int i = 0; i < LUMIERA_RESOURCE_END; ++i)
llist_init (&lumiera_resourcecollector_registry[i]);
lumiera_mutex_init (&lumiera_resourcecollector_lock, "resourcecollector", &NOBUG_FLAG(resourcecollector));
}
void
lumiera_resourcecollector_destroy (void)
{
TRACE (resourcecollector);
for (int i = 0; i < LUMIERA_RESOURCE_END; ++i)
LLIST_WHILE_HEAD (&lumiera_resourcecollector_registry[i], head)
lumiera_resourcehandler_unregister ((LumieraResourcehandler)head);
lumiera_mutex_destroy (&lumiera_resourcecollector_lock, &NOBUG_FLAG(resourcecollector));
}
int
lumiera_resourcecollector_run (enum lumiera_resource which, enum lumiera_resource_try* iteration, void* context)
{
TRACE (resourcecollector);
pthread_once (&lumiera_resourcecollector_once, lumiera_resourcecollector_init_);
LUMIERA_MUTEX_SECTION (resourcecollector, &lumiera_resourcecollector_lock)
{
for (enum lumiera_resource_try progress = LUMIERA_RESOURCE_NONE; progress < *iteration; ++*iteration)
{
if (*iteration < LUMIERA_RESOURCE_PANIC)
{
LLIST_FOREACH (&lumiera_resourcecollector_registry[which], node)
{
LumieraResourcehandler self = (LumieraResourcehandler) node;
progress = self->handler (*iteration, self->data, context);
if (*iteration < LUMIERA_RESOURCE_ALL)
{
if (progress >= *iteration)
{
llist_insert_next (node, &lumiera_resourcecollector_registry[which]);
break;
}
}
}
}
else
{
ERROR (resourcecollector, "PANIC, Not enough resources %d", which);
for (int i = 0; i < LUMIERA_RESOURCE_END; ++i)
LLIST_FOREACH (&lumiera_resourcecollector_registry[i], node)
{
LumieraResourcehandler self = (LumieraResourcehandler) node;
progress = self->handler (LUMIERA_RESOURCE_PANIC, self->data, NULL);
}
_exit (EXIT_FAILURE);
}
}
}
return 1;
}
LumieraResourcehandler
lumiera_resourcecollector_register_handler (enum lumiera_resource resource, lumiera_resource_handler_fn handler, void* data)
{
pthread_once (&lumiera_resourcecollector_once, lumiera_resourcecollector_init_);
TRACE (resourcecollector);
LumieraResourcehandler self = lumiera_malloc (sizeof (*self));
llist_init (&self->node);
self->handler = handler;
self->data = data;
LUMIERA_MUTEX_SECTION (resourcecollector, &lumiera_resourcecollector_lock)
{
llist_insert_tail (&lumiera_resourcecollector_registry[resource], &self->node);
}
return self;
}
void
lumiera_resourcehandler_unregister (LumieraResourcehandler self)
{
TRACE (resourcecollector);
if (self)
{
LUMIERA_MUTEX_SECTION (resourcecollector, &lumiera_resourcecollector_lock)
{
llist_unlink (&self->node);
self->handler (LUMIERA_RESOURCE_UNREGISTER, self->data, NULL);
}
lumiera_free (self);
}
}
LumieraResourcehandler
lumiera_resourcecollector_handler_find (enum lumiera_resource resource, lumiera_resource_handler_fn handler, void* data)
{
TRACE (resourcecollector);
LumieraResourcehandler self = NULL;
LUMIERA_MUTEX_SECTION (resourcecollector, &lumiera_resourcecollector_lock)
{
LLIST_FOREACH (&lumiera_resourcecollector_registry[resource], node)
{
self = (LumieraResourcehandler) node;
if (((LumieraResourcehandler)node)->handler == handler && ((LumieraResourcehandler)node)->data == data)
{
self = (LumieraResourcehandler)node;
break;
}
}
}
return self;
}
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/

View file

@ -0,0 +1,169 @@
/*
resourcecollector.h - manage/collect resources when they get short
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef LUMIERA_RESOURCECOLLECTOR_H
#define LUMIERA_RESOURCECOLLECTOR_H
#include <nobug.h>
NOBUG_DECLARE_FLAG (resourcecollector);
/**
* Resources known to the resource collector
*/
enum lumiera_resource
{
/** memory blocks, context is a pointer to the size_t required **/
LUMIERA_RESOURCE_MEMORY,
/** OS filehandles **/
LUMIERA_RESOURCE_FILEHANDLE,
/** mmaped regions **/
LUMIERA_RESOURCE_MMAP,
/** disk space for the storage area, context is a pointer to the filename indication the device **/
LUMIERA_RESOURCE_DISKSTORAGE,
/** disk bandwidth for the storage area, context is a pointer to the filename indication the device **/
LUMIERA_RESOURCE_STORAGEBANDWIDTH,
/** disk space for the caching area, context is a pointer to the filename indication the device **/
LUMIERA_RESOURCE_DISKCACHE,
/** disk bandwidth for the caching area, context is a pointer to the filename indication the device **/
LUMIERA_RESOURCE_CACHEBANDWIDTH,
LUMIERA_RESOURCE_END /* last entry */
};
/**
* Iteration indicator
* Resource collection works iteratively freeing more and more resources.
* Handlers do not need to obey the request and shall return LUMIERA_RESOURCE_NONE
* which will then continue with the next handler.
* This goes through all available handlers until one returns a higher or same value
* than the current iteration to indicate that it freed enough resources to continue the task.
* Then control is passed back to the calling loop which retries the resource allocation.
* LUMIERA_RESOURCE_PANIC is somewhat special since it will always call all registered handlers
* for all resources, not only the queried one and finally _exit() the application.
* The exact amounts of resources to be freed for ONE, SOME and MANY in intentionally
* kept vague the handlers are free to interpret this in some sensible way.
*/
enum lumiera_resource_try
{
/** No op, returned by a handler when it did nothing **/
LUMIERA_RESOURCE_NONE,
/** try to free one or really few of this resources **/
LUMIERA_RESOURCE_ONE,
/** try to free a small reasonable implementation defined amount of resources **/
LUMIERA_RESOURCE_SOME,
/** try to free a biggier implementation defined amount of resources **/
LUMIERA_RESOURCE_MANY,
/** free as much as possible **/
LUMIERA_RESOURCE_ALL,
/** die! **/
LUMIERA_RESOURCE_PANIC,
/** When a handler gets unregistered it wull be called with this value to give it a chance to clean up the user 'data' **/
LUMIERA_RESOURCE_UNREGISTER
};
/**
* The type for the resource collector handler functions.
* Handlers are always run with a global resourcecollector mutex locked, the user does not need to
* care about syncronization.
* @param itr the current iteration try in freeing resources
* @param data user supplied data at registration time for the handler
* @param context context pointer for this collection run, might be NULL (at least for UNREGISTER and PANIC)
* @return indication what the the handler really did (LUMIERA_RESOURCE_NONE when it didn't obey the request)
*/
typedef enum lumiera_resource_try (*lumiera_resource_handler_fn)(enum lumiera_resource_try itr, void* data, void* context);
typedef struct lumiera_resourcehandler_struct lumiera_resourcehandler;
typedef lumiera_resourcehandler* LumieraResourcehandler;
/**
* Destroy the resource collector registry.
* Unregisters and deletes all handlers.
* Note that there is no resourcecollector_init() function, initialization is automatic on first use.
*/
void
lumiera_resourcecollector_destroy (void);
/**
* Try to free resources.
*
* @param which The kind of resource to be acquired
* @param iteration a pointer to a local iterator, initialized with the start
* value for the loop
* @param context NULL or some context dependent data for the needed resource
* this is a pointer to a size_t for MEMORY and a pointer to a filename
* (to find out about the device) for STORAGE and CACHE resources
* @return either returns 1 or calls _exit()
*
* @example
* void* data;
* size_t size = 1000;
* enum lumiera_resource_try iteration = LUMIERA_RESOURCE_ONE;
* do {
* data = malloc (size);
* } while (!data && lumiera_resourcecollector_run (LUMIERA_RESOURCE_MEMORY, &iteration, &size));
*/
int
lumiera_resourcecollector_run (enum lumiera_resource which, enum lumiera_resource_try* iteration, void* context);
/**
* Registers a new collector handler
* @param resource resource for which this handler shall be registered
* @param handler pointer to the handler function
* @param data opaque user-data pointer which will be passed to the handler
* @return pointer to the internal handler structure. This can be used to unregister the handler.
*/
LumieraResourcehandler
lumiera_resourcecollector_register_handler (enum lumiera_resource resource, lumiera_resource_handler_fn handler, void* data);
/**
* Unregisters a collector handle
* Removes the handler from the registry and calls it once with LUMIERA_RESOURCE_UNREGISTER
* to give it a chance to free the user supplied data. Must not be called after lumiera_resourcecollector_destroy()
* @param self pointer to internal handler structure, obtained from register_handler() or handler_find(), might be NULL
*/
void
lumiera_resourcehandler_unregister (LumieraResourcehandler self);
/**
* Looks up a handler.
* Used to find a registered handler when the return value of register_handler() was unpractical to store.
* @param resource resource for which this handler was registered
* @param handler pointer to the handler function, same as used for registering
* @param data opaque user-data pointer, same as used for registering
* @return pointer to the internal handler structure or NULL if no handler was found
*/
LumieraResourcehandler
lumiera_resourcecollector_handler_find (enum lumiera_resource resource, lumiera_resource_handler_fn handler, void* data);
#endif
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/

View file

@ -0,0 +1,22 @@
TESTING "Resourcecollector" ./test-resourcecollector
TEST "basic register, destroy" basic <<END
out: unregistering memory handler
return: 0
END
TEST "memory success" memory_collection_mockup 1 <<END
out: unregistering memory handler
return: 0
END
TEST "memory success, 2nd try" memory_collection_mockup 2 <<END
out: memory handler got called
out: unregistering memory handler
return: 0
END
TEST "memory panic" memory_collection_mockup 10 <<END
return: 1
END

View file

@ -79,4 +79,9 @@ test_filemmap_SOURCES = $(tests_srcdir)/backend/test-filemmap.c
test_filemmap_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_filemmap_LDADD = liblumibackend.a liblumiera.a -lnobugmt -lpthread -ldl -lm
check_PROGRAMS += test-resourcecollector
test_resourcecollector_SOURCES = $(tests_srcdir)/backend/test-resourcecollector.c
test_resourcecollector_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_resourcecollector_LDADD = liblumibackend.a liblumiera.a -lnobugmt -lpthread -ldl -lm
TESTS = $(tests_srcdir)/test.sh

View file

@ -0,0 +1,74 @@
/*
test-resourcecollector.c - test the resource collector
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "backend/resourcecollector.h"
#include "tests/test.h"
#include <unistd.h>
#include <errno.h>
static enum lumiera_resource_try
test_memory_handler (enum lumiera_resource_try itr, void* data, void* context)
{
switch (itr)
{
case LUMIERA_RESOURCE_UNREGISTER:
printf ("unregistering memory handler\n");
break;
default:
printf ("memory handler got called\n");
return LUMIERA_RESOURCE_ALL;
}
return LUMIERA_RESOURCE_NONE;
}
TESTS_BEGIN
TEST ("basic")
{
lumiera_resourcecollector_register_handler (LUMIERA_RESOURCE_MEMORY, test_memory_handler, NULL);
lumiera_resourcecollector_destroy ();
}
TEST ("memory_collection_mockup")
{
REQUIRE (argv[2]);
lumiera_resourcecollector_register_handler (LUMIERA_RESOURCE_MEMORY, test_memory_handler, NULL);
size_t size = 1000;
enum lumiera_resource_try iteration = LUMIERA_RESOURCE_ONE;
int trying = atoi (argv[2]);
do {
--trying;
} while (trying && lumiera_resourcecollector_run (LUMIERA_RESOURCE_MEMORY, &iteration, &size));
lumiera_resourcecollector_destroy ();
}
TESTS_END