Basic resourcecollector implementation
Lets one register callback functions which to incrementally cleanup unused resources.
This commit is contained in:
parent
67386a20eb
commit
369c644ab4
6 changed files with 459 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
187
src/backend/resourcecollector.c
Normal file
187
src/backend/resourcecollector.c
Normal 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:
|
||||
*/
|
||||
169
src/backend/resourcecollector.h
Normal file
169
src/backend/resourcecollector.h
Normal 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:
|
||||
*/
|
||||
22
tests/20resourcecollector.tests
Normal file
22
tests/20resourcecollector.tests
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
74
tests/backend/test-resourcecollector.c
Normal file
74
tests/backend/test-resourcecollector.c
Normal 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
|
||||
Loading…
Reference in a new issue