From 369c644ab47097edf2fea8a75b70a0a1b0f8c9fa Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Thu, 11 Dec 2008 23:10:33 +0100 Subject: [PATCH] Basic resourcecollector implementation Lets one register callback functions which to incrementally cleanup unused resources. --- src/backend/Makefile.am | 2 + src/backend/resourcecollector.c | 187 +++++++++++++++++++++++++ src/backend/resourcecollector.h | 169 ++++++++++++++++++++++ tests/20resourcecollector.tests | 22 +++ tests/Makefile.am | 5 + tests/backend/test-resourcecollector.c | 74 ++++++++++ 6 files changed, 459 insertions(+) create mode 100644 src/backend/resourcecollector.c create mode 100644 src/backend/resourcecollector.h create mode 100644 tests/20resourcecollector.tests create mode 100644 tests/backend/test-resourcecollector.c diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index e918e9d0f..ec103286b 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -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 diff --git a/src/backend/resourcecollector.c b/src/backend/resourcecollector.c new file mode 100644 index 000000000..2e31c41c3 --- /dev/null +++ b/src/backend/resourcecollector.c @@ -0,0 +1,187 @@ +/* + resourcecollector.c - manage/collect resources when they get short + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 + +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: +*/ diff --git a/src/backend/resourcecollector.h b/src/backend/resourcecollector.h new file mode 100644 index 000000000..703da8b7b --- /dev/null +++ b/src/backend/resourcecollector.h @@ -0,0 +1,169 @@ +/* + resourcecollector.h - manage/collect resources when they get short + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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_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: +*/ diff --git a/tests/20resourcecollector.tests b/tests/20resourcecollector.tests new file mode 100644 index 000000000..bab637815 --- /dev/null +++ b/tests/20resourcecollector.tests @@ -0,0 +1,22 @@ +TESTING "Resourcecollector" ./test-resourcecollector + + +TEST "basic register, destroy" basic < + + 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 +#include + +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