LUMIERA.clone/src/common/interface.c
Ichthyostega 41ad41d1f1 clean-up: sourcefile layout and spell checking
Uniform sequence at start of source files
- copyright claim
- license
- file comment
- header guard
- lumiera includes
- library / system includes

Lumiera uses Brittish spelling. Add an according note to the styleguide.
2014-10-23 23:04:35 +02:00

355 lines
10 KiB
C

/*
Interface - Lumiera interface handling
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.
* *****************************************************/
/** @file interface.c
** Implementation: handling of interfaces (extension points).
** From a programmers perspective interfaces only need to be opened when needed and closed
** when finished with them. There is no difference if the interface is internally provided
** by the core or provided by an external plugin.
** Interfaces can be opened multiple times and cross reference each other.
*/
#include "include/logging.h"
#include "lib/recmutex.h"
#include "lib/safeclib.h"
#include "common/plugin.h"
#include "common/interface.h"
#include "common/interfaceregistry.h"
#include <nobug.h>
/** the mother of all interfaces */
LumieraInterface lumiera_interface_interface;
static LumieraInterfacenode
lumiera_interface_open_interfacenode (LumieraInterfacenode self);
static void
lumiera_interfacenode_close (LumieraInterfacenode self);
LumieraInterface
lumiera_interface_open (const char* interface, unsigned version, size_t minminorversion, const char* name)
{
LumieraInterfacenode self = NULL;
TRACE (interface, "%s", name);
WARN_IF (version == 0, interface, "opening experimental interface: %s_%d_%s", interface, version, name);
LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
{
self = lumiera_interfaceregistry_interfacenode_find (interface, version, name);
if (!self)
{
UNIMPLEMENTED ("query plugindb and load plugin if exists");
}
if (self)
{
if (minminorversion > self->interface->size)
{
UNIMPLEMENTED ("set error");
self = NULL;
}
else
{
self = lumiera_interface_open_interfacenode (self);
}
}
}
return self->interface;
}
static void
push_dependency (LumieraInterfacenode parent, LumieraInterfacenode child)
{
/* push a dependency on the dependency array, allocate or resize it on demand */
TRACE (interface_dbg, "%s %s", parent->interface->name, child->interface->name);
/* no dependencies recorded yet, allocate a first block for 4 pointers */
if (!parent->deps_size)
parent->deps = lumiera_calloc (parent->deps_size = 4, sizeof (LumieraInterfacenode));
size_t sz = parent->deps_size;
LumieraInterfacenode* itr = parent->deps;
while (*itr)
{
--sz;
++itr;
if (sz == 1)
{
/* block to small, realloc it with twice its size, we keep the block NULL terminated */
sz = parent->deps_size + 1;
parent->deps_size *= 2;
parent->deps = lumiera_realloc (parent->deps, parent->deps_size * sizeof (LumieraInterface));
itr = parent->deps + sz - 2;
memset (itr, 0, sz * sizeof (LumieraInterface));
}
}
/* found free element, store self in dependencies */
*itr = child;
}
static void
depwalk (LumieraInterfacenode self, LumieraInterfacenode* stack)
{
/* increment refcount for all non-cyclic dependencies recursively */
if (self->deps)
{
TRACE (interface_dbg, "%s %d", self->interface->name, self->refcnt);
for (LumieraInterfacenode* dep = self->deps; *dep; ++dep)
{
TRACE (interface_dbg, "loop %s", (*dep)->interface->name);
int cycle = 0;
for (LumieraInterfacenode itr = *stack; itr; itr = itr->lnk)
{
if (itr == *dep)
{
TRACE (interface_dbg, "CYCLE");
cycle = 1;
break;
}
}
if (!cycle)
{
if ((*dep)->plugin)
lumiera_plugin_refinc ((*dep)->plugin);
++(*dep)->refcnt;
(*dep)->lnk = *stack;
*stack = *dep;
depwalk (*dep, stack);
*stack = (*dep)->lnk;
(*dep)->lnk = NULL;
}
}
}
}
static LumieraInterfacenode
lumiera_interface_open_interfacenode (LumieraInterfacenode self)
{
static unsigned collect_dependencies = 0;
static LumieraInterfacenode stack = NULL;
/*
OK, this got little more complicated than it should be,
but finally it handles any kind of cross dependencies between interfaces gracefully
*/
if (self)
{
TRACE (interface_dbg, "%s %d (%s)", self->interface->name, self->refcnt, stack?stack->interface->name:"");
LUMIERA_RECMUTEX_SECTION (mutex_sync, &lumiera_interface_mutex)
{
/* discover cycles, cycles don't refcount! */
int cycle = 0;
for (LumieraInterfacenode itr = stack; itr; itr = itr->lnk)
{
if (itr == self)
{
TRACE (interface_dbg, "CYCLE");
cycle = 1;
break;
}
}
/* 'stack' is ensured to be !NULL here because only a parent call can switch collect_dependencies on */
if (collect_dependencies)
push_dependency (stack, self);
if (!cycle)
{
if (self->plugin)
lumiera_plugin_refinc (self->plugin);
++self->refcnt;
self->lnk = stack;
stack = self;
int collect_dependencies_bak = collect_dependencies;
if (self->refcnt == 1)
{
/* first opening, run acquire, recursive opening shall record its dependencies here */
if (self->interface->acquire)
{
TRACE (interface_dbg, "Acquire %s", self->interface->name);
collect_dependencies = self->deps?0:1;
self->interface = self->interface->acquire (self->interface, lumiera_interface_interface);
}
}
else
{
/* opening again recurse dependencies */
collect_dependencies = 0;
depwalk (self, &stack);
}
collect_dependencies = collect_dependencies_bak;
stack = self->lnk;
self->lnk = NULL;
}
}
}
return self;
}
void
lumiera_interface_close (LumieraInterface self)
{
TRACE (interface_dbg);
LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex)
{
lumiera_interfacenode_close ((LumieraInterfacenode)psplay_find (lumiera_interfaceregistry, self, 100));
}
}
unsigned
lumiera_interface_version (LumieraInterface self, const char* iname)
{
if (self && iname && !strcmp (self->interface, iname))
return self->version;
return ~0;
}
/* internal function, does no locking! */
static void
lumiera_interfacenode_close (LumieraInterfacenode self)
{
static LumieraInterfacenode stack = NULL;
if (!self)
return;
TRACE (interface, "%s %d (%s)", self->interface->name, self->refcnt, stack?stack->interface->name:"");
REQUIRE (self->refcnt);
int cycle = 0;
for (LumieraInterfacenode itr = stack; itr; itr = itr->lnk)
{
if (itr == self)
{
TRACE (interface_dbg, "CYCLE");
cycle = 1;
break;
}
}
if (!cycle)
{
self->lnk = stack;
stack = self;
if (self->refcnt == 1)
{
if (self->interface->release)
{
TRACE (interface_dbg, "Release %s", self->interface->name);
self->interface->release (self->interface);
}
}
else
{
if (self->deps)
{
TRACE (interface_dbg, "Recurse %s %d", self->interface->name, self->refcnt);
for (LumieraInterfacenode* dep = self->deps; *dep; ++dep)
lumiera_interfacenode_close (*dep);
}
}
stack = self->lnk;
self->lnk = NULL;
if (self->plugin)
lumiera_plugin_refdec (self->plugin);
--self->refcnt;
}
}
/**
* Definition of 'the mother of all interfaces'
* since this interface is singleton and required for any component to open any other
* interface this should get a very stable interface and likely never change.
*/
LUMIERA_EXPORT(
LUMIERA_INTERFACE_DEFINE (lumieraorg_interface, 0,
lumieraorg_interface,
NULL,
NULL,
NULL,
LUMIERA_INTERFACE_MAP (open,
lumiera_interface_open),
LUMIERA_INTERFACE_MAP (close,
lumiera_interface_close),
LUMIERA_INTERFACE_MAP (version,
lumiera_interface_version),
)
)
void
lumiera_interface_init (void)
{
LUMIERA_INTERFACE_REGISTEREXPORTED;
lumiera_interface_interface =
lumiera_interface_open ("lumieraorg_interface", 0, 0, "lumieraorg_interface");
}
void
lumiera_interface_destroy (void)
{
lumiera_interface_close (lumiera_interface_interface);
LUMIERA_INTERFACE_UNREGISTEREXPORTED;
}
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/