LUMIERA.clone/src/common/interface.c
Ichthyostega 806db414dd Copyright: clarify and simplify the file headers
* 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.
2024-11-17 23:42:55 +01:00

346 lines
9.7 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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:
*/