* Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
346 lines
9.7 KiB
C
346 lines
9.7 KiB
C
/*
|
||
Interface - Lumiera interface handling
|
||
|
||
Copyright (C)
|
||
2008, Christian Thaeter <ct@pipapo.org>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
* *****************************************************************/
|
||
|
||
|
||
/** @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:
|
||
*/
|