- move construct into the buffer - directly invoke the payload constructor through PlantingHandle - reconsider type signature and size constraint - extend the unit test - document a corner case of c++ "perfect forwarding", which caused me some grief here
355 lines
10 KiB
C
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 too 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:
|
|
*/
|