diff --git a/Makefile.am b/Makefile.am index f3ae3ef41..d236fbfd8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,7 +62,6 @@ include $(top_srcdir)/icons/Makefile.am include $(top_srcdir)/tests/common/Makefile.am include $(top_srcdir)/tests/components/Makefile.am include $(top_srcdir)/tests/Makefile.am -include $(top_srcdir)/tests/plugin/Makefile.am #EXTRA_DIST += admin debian doc depcomp README.BUILD LICENSE \ # cinelerra-cvs-current.spec diff --git a/configure.ac b/configure.ac index 0ef7ffd00..4df50f245 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_INIT(lumiera, 0.1pre) -AC_CONFIG_SRCDIR(src/lib/plugin.c) +AC_CONFIG_SRCDIR(src/lib/luid.c) AC_CONFIG_AUX_DIR(scripts) AM_INIT_AUTOMAKE AC_PREREQ(2.59) diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index abb576021..da39e71c6 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -18,7 +18,7 @@ liblumibackend_a_srcdir = $(top_srcdir)/src/backend noinst_LIBRARIES += liblumibackend.a -liblumibackend_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror +liblumibackend_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wextra -Wall -Werror liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/mediaaccessfacade.cpp \ @@ -27,10 +27,13 @@ liblumibackend_a_SOURCES = \ $(liblumibackend_a_srcdir)/filehandle.c \ $(liblumibackend_a_srcdir)/filedescriptor.c \ $(liblumibackend_a_srcdir)/filehandlecache.c \ + $(liblumibackend_a_srcdir)/interface.c \ + $(liblumibackend_a_srcdir)/interfaceregistry.c \ $(liblumibackend_a_srcdir)/config.c \ $(liblumibackend_a_srcdir)/config_typed.c \ + $(liblumibackend_a_srcdir)/config_wordlist.c \ $(liblumibackend_a_srcdir)/configentry.c \ - $(liblumibackend_a_srcdir)/configitem.c \ + $(liblumibackend_a_srcdir)/configitem.c \ $(liblumibackend_a_srcdir)/config_lookup.c @@ -41,8 +44,11 @@ noinst_HEADERS += \ $(liblumibackend_a_srcdir)/filehandle.h \ $(liblumibackend_a_srcdir)/filedescriptor.h \ $(liblumibackend_a_srcdir)/filehandlecache.h \ + $(liblumibackend_a_srcdir)/interface.h \ + $(liblumibackend_a_srcdir)/interfaceregistry.h \ + $(liblumibackend_a_srcdir)/interfacedescriptor.h \ $(liblumibackend_a_srcdir)/config.h \ $(liblumibackend_a_srcdir)/configentry.h \ - $(liblumibackend_a_srcdir)/configitem.h \ + $(liblumibackend_a_srcdir)/configitem.h \ $(liblumibackend_a_srcdir)/config_lookup.h diff --git a/src/backend/config.c b/src/backend/config.c index 2e724be08..cec0dbfd8 100644 --- a/src/backend/config.c +++ b/src/backend/config.c @@ -111,7 +111,7 @@ lumiera_config_init (const char* path) lumiera_configitem_init (&lumiera_global_config->files); lumiera_configitem_init (&lumiera_global_config->TODO_unknown); - lumiera_rwlock_init (&lumiera_global_config->lock, "config rwlock", &NOBUG_FLAG (config)); + lumiera_mutex_init (&lumiera_global_config->lock, "config mutex", &NOBUG_FLAG (config)); lumiera_config_setdefault (lumiera_tmpbuf_snprintf (SIZE_MAX, "config.path = %s", path)); @@ -130,7 +130,7 @@ lumiera_config_destroy () TRACE (config); if (lumiera_global_config) { - lumiera_rwlock_destroy (&lumiera_global_config->lock, &NOBUG_FLAG (config)); + lumiera_mutex_destroy (&lumiera_global_config->lock, &NOBUG_FLAG (config)); lumiera_configitem_destroy (&lumiera_global_config->defaults, &lumiera_global_config->keys); lumiera_configitem_destroy (&lumiera_global_config->files, &lumiera_global_config->keys); lumiera_configitem_destroy (&lumiera_global_config->TODO_unknown, &lumiera_global_config->keys); @@ -146,6 +146,7 @@ lumiera_config_destroy () int lumiera_config_load (const char* file) { + (void) file; TRACE (config); UNIMPLEMENTED(); return -1; @@ -164,6 +165,7 @@ lumiera_config_save () int lumiera_config_purge (const char* filename) { + (void) filename; TRACE (config); UNIMPLEMENTED(); @@ -290,7 +292,7 @@ lumiera_config_setdefault (const char* line) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config, &lumiera_global_config->lock) { const char* key = line; while (*key && isspace (*key)) @@ -342,6 +344,7 @@ lumiera_config_dump (FILE* out) int lumiera_config_reset (const char* key) { + (void) key; TRACE (config); UNIMPLEMENTED(); return -1; @@ -351,6 +354,9 @@ lumiera_config_reset (const char* key) int lumiera_config_info (const char* key, const char** filename, unsigned* line) { + (void) key; + (void) filename; + (void) line; TRACE (config); UNIMPLEMENTED(); return -1; diff --git a/src/backend/config.h b/src/backend/config.h index 80f33a6d5..4e3acd9a9 100644 --- a/src/backend/config.h +++ b/src/backend/config.h @@ -24,7 +24,7 @@ //TODO: Support library includes// #include "lib/error.h" -#include "lib/rwlock.h" +#include "lib/mutex.h" //TODO: Forward declarations// struct lumiera_config_struct; @@ -74,14 +74,7 @@ struct lumiera_config_struct lumiera_configitem files; /* all loaded files */ lumiera_configitem TODO_unknown; /* all values which are not part of a file and not default TODO: this will be removed when file support is finished */ - /* - all access is protected with rwlock's. - We use rwlocks here since concurrent reads are likely common. - - So far this is a global config lock, if this is a problem we might granularize it by locking on a file level. - config access is not planned to be transactional yet, if this is a problem we need to expose the rwlock to a config_acquire/config_release function pair - */ - lumiera_rwlock lock; + lumiera_mutex lock; }; typedef struct lumiera_config_struct lumiera_config; @@ -96,6 +89,7 @@ typedef lumiera_config* LumieraConfig; LUMIERA_CONFIG_TYPE(number, signed long long) \ LUMIERA_CONFIG_TYPE(real, long double) \ LUMIERA_CONFIG_TYPE(string, const char*) \ + LUMIERA_CONFIG_TYPE(wordlist, const char*) \ LUMIERA_CONFIG_TYPE(word, const char*) \ LUMIERA_CONFIG_TYPE(bool, int) @@ -197,7 +191,7 @@ lumiera_config_set (const char* key, const char* delim_value); * Installs a default value for a config key. * Any key might have an associated default value which is used when * no other configuration is available, this can be set once. - * Any subsequent call will be a no-op. This function writelocks the config system. + * Any subsequent call will be a no-op. This function locks the config system. * @param line line with key, delimiter and value to store as default value * @return NULL in case of an error, else a pointer to the default configitem */ @@ -223,7 +217,57 @@ lumiera_config_setdefault (const char* line); LUMIERA_CONFIG_TYPES #undef LUMIERA_CONFIG_TYPE +/** + * Wordlists + * Wordlists are lists of single words delimited by any of " \t,;". + * They can be used to store groups of keys and other kinds of simple references into the config + * system. Here are some functions to manipulate single word entries in a wordlist. + */ +/** + * Get nth word of a wordlist. + * @param key key under which this wordlist is stored + * @param nth index of the word to get, starting with 0 + * @return pointer to a tempbuf holding the nth word or NULL in case of error + */ +const char* +lumiera_config_wordlist_get_nth (const char* key, unsigned nth); + + +/** + * Find the index of a word in a wordlist. + * @param key key under which this wordlist is stored + * @param value word to find + * @return index of the first occurence of the word or -1 in case of failure + */ +int +lumiera_config_wordlist_find (const char* key, const char* value); + + +/** + * Universal word replacement function. + * Replaces a word with up to two new words. This can be used to delete a word (no replacements), + * insert a new word before an existing word (giving the new word as subst1 and the old word as subst2) + * insert a new word after an existing word (giving the old word as subst1 and the new word as subst2) + * or simply give 2 new words. + * @param key key under which this wordlist is stored + * @param value word to be replaced + * @param subst1 first replacement word + * @param subst2 second replacement word + * @return internal representation of the wordlist in a tmpbuf or NULL in case of an error + */ +const char* +lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2); + + +/** + * Add a word to the end of a wordlist if it doesnt exist already + * @param key key under which this wordlist is stored + * @param value new word to add + * @return internal representation of the wordlist in a tmpbuf or NULL in case of an error + */ +const char* +lumiera_config_wordlist_add (const char* key, const char* value); // * {{{ lumiera_config_TYPE_set (const char* key, TYPE*value, const char* fmt) }}} // Highlevel interface for different types, fmt is a printf format specifier for the desired format, when NULL, defaults apply. diff --git a/src/backend/config_typed.c b/src/backend/config_typed.c index ae490ff2b..963090899 100644 --- a/src/backend/config_typed.c +++ b/src/backend/config_typed.c @@ -41,6 +41,7 @@ extern LumieraConfig lumiera_global_config; const char* lumiera_config_link_get (const char* key, const char** value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -49,6 +50,7 @@ lumiera_config_link_get (const char* key, const char** value) LumieraConfigitem lumiera_config_link_set (const char* key, const char** value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -66,7 +68,7 @@ lumiera_config_number_get (const char* key, long long* value) const char* raw_value = NULL; - LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &raw_value)) { @@ -94,7 +96,7 @@ lumiera_config_number_set (const char* key, long long* value) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { const char* fmt = "= %lld"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, *value)); @@ -111,6 +113,7 @@ lumiera_config_number_set (const char* key, long long* value) const char* lumiera_config_real_get (const char* key, long double* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -119,6 +122,7 @@ lumiera_config_real_get (const char* key, long double* value) LumieraConfigitem lumiera_config_real_set (const char* key, long double* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -195,7 +199,7 @@ lumiera_config_string_get (const char* key, const char** value) const char* raw_value = *value = NULL; - LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &raw_value)) { @@ -218,7 +222,7 @@ lumiera_config_string_set (const char* key, const char** value) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { const char* fmt = "= %s"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, *value)); @@ -228,6 +232,53 @@ lumiera_config_string_set (const char* key, const char** value) } +/** + * Wordlist + * words delimited by any of " \t,;" + */ + +const char* +lumiera_config_wordlist_get (const char* key, const char** value) +{ + TRACE (config_typed, "KEY %s", key); + + const char* raw_value = *value = NULL; + + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) + { + if (lumiera_config_get (key, &raw_value)) + { + if (raw_value) + { + *value = raw_value; + } + else + LUMIERA_ERROR_SET (config, CONFIG_NO_ENTRY); + + TODO ("remove the ERROR_SET because config_get sets it already? also in all other getters in this file"); + } + } + + return *value; +} + + +LumieraConfigitem +lumiera_config_wordlist_set (const char* key, const char** value) +{ + TRACE (config_typed); + + LumieraConfigitem item = NULL; + + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) + { + const char* fmt = "= %s"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); + item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, *value)); + } + + return item; +} + /** * Word @@ -262,7 +313,7 @@ lumiera_config_word_get (const char* key, const char** value) const char* raw_value = *value = NULL; - LUMIERA_RDLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { if (lumiera_config_get (key, &raw_value)) { @@ -285,7 +336,7 @@ lumiera_config_word_set (const char* key, const char** value) LumieraConfigitem item = NULL; - LUMIERA_WRLOCK_SECTION (config_typed, &lumiera_global_config->lock) + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) { const char* fmt = "= %s"; TODO ("use the config system (config.format*...) to deduce the desired format for this key"); item = lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, fmt, scan_word (*value))); @@ -302,6 +353,7 @@ lumiera_config_word_set (const char* key, const char** value) const char* lumiera_config_bool_get (const char* key, int* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; @@ -311,6 +363,7 @@ lumiera_config_bool_get (const char* key, int* value) LumieraConfigitem lumiera_config_bool_set (const char* key, int* value) { + (void) key; (void) value; TRACE (config_typed); UNIMPLEMENTED(); return 0; diff --git a/src/backend/config_wordlist.c b/src/backend/config_wordlist.c new file mode 100644 index 000000000..6cd0389a7 --- /dev/null +++ b/src/backend/config_wordlist.c @@ -0,0 +1,217 @@ +/* + config_wordlist.c - Lumiera wordlist access functions + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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. +*/ + +//TODO: Support library includes// +#include "lib/error.h" +#include "lib/safeclib.h" + + +//TODO: Lumiera header includes// +#include "backend/config.h" + +//TODO: internal/static forward declarations// +extern LumieraConfig lumiera_global_config; + + +//TODO: System includes// + +/** + * return nth word of a wordlist + */ +const char* +lumiera_config_wordlist_get_nth (const char* key, unsigned nth) +{ + const char* value; + size_t len; + + if (!lumiera_config_wordlist_get (key, &value)) + return NULL; + + for (;;) + { + value += strspn (value, " \t,;"); + len = strcspn (value, " \t,;"); + if (!nth && *value) + break; + + --nth; + value += len; + + if (!*value) + return NULL; + } + + return lumiera_tmpbuf_strndup (value, len); +} + + +int +lumiera_config_wordlist_find (const char* key, const char* value) +{ + const char* itr; + size_t vlen = strlen (value); + size_t len; + + if (!lumiera_config_wordlist_get (key, &itr)) + return -1; + + for (int idx = 0; *itr; itr += len, ++idx) + { + itr += strspn (itr, " \t,;"); + len = strcspn (itr, " \t,;"); + + if (len == vlen && !strncmp (itr, value, vlen)) + return idx; + } + + return -1; +} + + +const char* +lumiera_config_wordlist_replace (const char* key, const char* value, const char* subst1, const char* subst2) +{ + const char* wordlist; + const char* str = NULL; + size_t vlen = strlen (value); + size_t len; + + if (!value) + return NULL; + + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) + { + if (lumiera_config_get (key, &wordlist)) + { + const char* start = wordlist + strspn (wordlist, " \t,;"); + + for (const char* itr = start; *itr; itr += len) + { + const char* left_end = itr; + itr += strspn (itr, " \t,;"); + len = strcspn (itr, " \t,;"); + + if (len == vlen && !strncmp (itr, value, vlen)) + { + TODO ("figure delimiter from original string out"); + const char* delim = " "; + + /* step over the word */ + itr += len; + itr += strspn (itr, " \t,;"); + + /* getting the delimiters right for the corner cases looks ugly, want to refactor it? just do it */ + str = lumiera_tmpbuf_snprintf (SIZE_MAX, + "%.*s%.*s%s%s%s%s%s%s", + start - wordlist, wordlist, + left_end - start, start, + (left_end - start && subst1 && *subst1) ? delim : "", + (subst1 && *subst1) ? subst1 : "", + ((left_end - start || (subst1 && *subst1)) && subst2 && *subst2) ? delim : "", + (subst2 && *subst2) ? subst2 : "", + ((left_end - start || (subst1 && *subst1) || (subst2 && *subst2)) && *itr) ? delim : "", + itr + ); + + if (!lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, "=%s", str))) + str = NULL; + + break; + } + } + } + } + + return str; +} + + +const char* +lumiera_config_wordlist_add (const char* key, const char* value) +{ + const char* wordlist = NULL; + + if (value && *value) + { + LUMIERA_MUTEX_SECTION (config_typed, &lumiera_global_config->lock) + { + if (lumiera_config_get (key, &wordlist)) + { + size_t vlen = strlen (value); + size_t len; + + for (const char* itr = wordlist; *itr; itr += len) + { + itr += strspn (itr, " \t,;"); + len = strcspn (itr, " \t,;"); + + if (len == vlen && !strncmp (itr, value, vlen)) + goto end; + } + + TODO ("figure delimiter from original string out"); + const char* delim = " "; + + wordlist = lumiera_tmpbuf_snprintf (SIZE_MAX, "%s%s%s", + wordlist, + wordlist[strspn (wordlist, " \t,;")]?delim:"", + value); + + if (!lumiera_config_set (key, lumiera_tmpbuf_snprintf (SIZE_MAX, "=%s", wordlist))) + wordlist = NULL; + } + end:; + } + } + + return wordlist; +} + + +#if 0 + +const char* +lumiera_config_wordlist_remove_nth (const char* key, unsigned nth) +{ +} + + +LumieraConfigitem +lumiera_config_wordlist_append (const char* key, const char** value, unsigned nth) +{ +} + + +LumieraConfigitem +lumiera_config_wordlist_preprend (const char* key, const char** value, unsigned nth) +{ +} +#endif + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/configitem.c b/src/backend/configitem.c index f3550da38..8015a6e20 100644 --- a/src/backend/configitem.c +++ b/src/backend/configitem.c @@ -241,8 +241,8 @@ lumiera_configitem_parse (LumieraConfigitem self, const char* line) itr += self->key_size; /*we need a key with a length greather than zero and - * either the end of the line - * or a whitespace after the key */ + * either end of line + * or whitespace after key */ if ( self->key_size && ( !*itr || (*itr && isspace(*itr)) )) { diff --git a/src/backend/filedescriptor.c b/src/backend/filedescriptor.c index 4daa04f3e..5f6bb4f13 100644 --- a/src/backend/filedescriptor.c +++ b/src/backend/filedescriptor.c @@ -42,7 +42,7 @@ NOBUG_DEFINE_FLAG_PARENT (filedescriptor, file_all); This registry stores all acquired filedescriptors for lookup, they will be freed when not referenced anymore. */ static PSplay registry = NULL; -static lumiera_mutex registry_mutex = {PTHREAD_MUTEX_INITIALIZER}; +static lumiera_mutex registry_mutex = {PTHREAD_MUTEX_INITIALIZER NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; static int @@ -96,7 +96,9 @@ lumiera_filedescriptor_registry_init (void) if (!registry) LUMIERA_DIE (NO_MEMORY); - RESOURCE_HANDLE_INIT (registry_mutex.rh); + TODO ("LumieraMutex lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag);"); + + // RESOURCE_HANDLE_INIT (registry_mutex.rh); RESOURCE_ANNOUNCE (filedescriptor, "mutex", "filedescriptor registry", ®istry, registry_mutex.rh); } @@ -108,6 +110,8 @@ lumiera_filedescriptor_registry_destroy (void) RESOURCE_FORGET (filedescriptor, registry_mutex.rh); + TODO ("LumieraMutex lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag);"); + if (registry) psplay_destroy (registry); registry = NULL; diff --git a/src/backend/interface.c b/src/backend/interface.c new file mode 100644 index 000000000..58d0d0c0f --- /dev/null +++ b/src/backend/interface.c @@ -0,0 +1,293 @@ +/* + interface.c - Lumiera interface api + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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/mutex.h" +#include "lib/safeclib.h" + +#include "backend/interface.h" + +#include "backend/interfaceregistry.h" + +#include + +/** + * @file + * 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. + */ + + +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); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &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, allcoate or resize it on demand */ + TRACE (interface, "%s %s", parent->interface->name, child->interface->name); + + /* no dependencies recorded yet, alloc 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, "%s %d", self->interface->name, self->refcnt); + for (LumieraInterfacenode* dep = self->deps; *dep; ++dep) + { + TRACE (interface, "loop %s", (*dep)->interface->name); + int cycle = 0; + for (LumieraInterfacenode itr = *stack; itr; itr = itr->lnk) + { + if (itr == *dep) + { + TRACE (interface, "CYCLE"); + cycle = 1; + break; + } + } + + if (!cycle) + { + ++(*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, "%s %d (%s)", self->interface->name, self->refcnt, stack?stack->interface->name:""); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &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, "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) + { + ++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, "Acquire %s", self->interface->name); + collect_dependencies = self->deps?0:1; + self->interface = self->interface->acquire (self->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); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + lumiera_interfacenode_close ((LumieraInterfacenode)psplay_find (lumiera_interfaceregistry, self, 100)); + } +} + + +/* 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, "CYCLE"); + cycle = 1; + break; + } + } + + if (!cycle) + { + self->lnk = stack; + stack = self; + + if (self->refcnt == 1) + { + if (self->interface->release) + { + TRACE (interface, "Release %s", self->interface->name); + self->interface->release (self->interface); + } + } + else + { + if (self->deps) + { + TRACE (interface, "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; + --self->refcnt; + } +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/interface.h b/src/backend/interface.h new file mode 100644 index 000000000..ef13e40e1 --- /dev/null +++ b/src/backend/interface.h @@ -0,0 +1,408 @@ +/* + interface.h - Lumiera interface macros and structures + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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_INTERFACE_H +#define LUMIERA_INTERFACE_H + +#include "lib/luid.h" +#include "lib/ppmpl.h" +#include "lib/psplay.h" + +/* TODO better doxygen formating */ + +/** + * @file + * Lumiera interface macros and structures. + * + * Instead just simple function/library bindings, Lumiera uses a system of + * versioned interfaces. This interfaces are C-binding compatible and thus + * can be used by any language which can bind to C. This interfaces are versioned + * to provide exceptional forward and backward compatibility for both, source and + * binary deployment of modules. This interfaces play a central role on the Lumiera + * architecture, other facilities, like serializing sessions and distributed computing + * will use them extensively. + * + * Overview + * + * Interfaces are used for two purposes in Lumiera: + * 1. The core uses them internally and exports its functionality though them. + * 2. Plugins (effects,...) extend Lumiera by providing interfaces + * + * We define some macros here which ease the declaration and definition of interfaces. + * + * Declaration of an interface happens in a header and has the form: + * LUMIERA_INTERFACE_DECLARE(name, version, + * LUMIERA_INTERFACE_SLOT(ret, name, params), + * ... + * ) + * Any code which want to use this interface must then include its declaration. + * + * Basic definition of an interface is done by mapping functions to slots or giving + * inline definitions for slot functions: + * LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, + * LUMIERA_INTERFACE_MAP (slot, luid, function), + * LUMIERA_INTERFACE_INLINE (slot, luid, ret, params, {body}), + * ... + * ) + * + * There are 2 ways to define collections of interfaces: + * LUMIERA_EXPORT(queryfunc, + * LUMIERA_INTERFACE_DEFINE(...), + * ... + * ) + * to export interfaces from the core. + * + * LUMIERA_PLUGIN(descriptor, acquire, release, luid, + * LUMIERA_INTERFACE_DEFINE(...), + * ... + * ) + * is used to export interfaces from a plugin. + * + * Naming and Versioning + * Interfaces have unique names and a major and minor version. The name and the major version + * is used to construct a C identifier for the interface, the minor version is implicit defined + * by the number of functions a interface. Interface instances are not versioned by the + * interface system, versioning these shall be defined somewhere else. + * + * Slot names are normal C identifiers, how these shall be versioned has to be defined somewhere + * else and is not subject of the interface system. Each function can has its own unique uuid. + */ + + +/* + Interface declaration macros + */ + +/** + * Construct a type identifier for an interface + * @param name name of the interface + * @param version major version of this interface + */ +#define LUMIERA_INTERFACE_INAME(name, version) name##_##version + +/** + * Construct a definition identifier for an interface + * @param iname name of the interface + * @param version major version of the interface + * @param dname name for the instance + */ +#define LUMIERA_INTERFACE_DNAME(iname, version, dname) PPMPL_CAT (LUMIERA_INTERFACE_INAME(iname, version), _##dname) + + +/** + * Return a reference (pointer) to an interface implementation + * @param iname name of the interface + * @param version major version of the interface + * @param dname name for the instance + */ +#define LUMIERA_INTERFACE_REF(iname, version, dname) \ + (LumieraInterface)&LUMIERA_INTERFACE_DNAME(iname, version, dname) + + +/** + * Construct the type of the interface + * @param name name of the interface + * @param version major version of this interface + */ +#define LUMIERA_INTERFACE_TYPE(name, version) struct LUMIERA_INTERFACE_INAME(name, version) + + +/** + * Construct a cast to the target interface type + * Used to cast a generic LumieraInterface to the real type + * @param name name of the interface + * @param version major version of this interface + */ +#define LUMIERA_INTERFACE_CAST(name, version) (LUMIERA_INTERFACE_TYPE(name, version)*) + + +/** + * Declare an interface. + * @param name name of the interface + * @param version major version of this interface declaration. 0 denotes a experimental interface, + * otherwise this shall be counting from 1 upwards for each new (incompatible) change of an interface. + * The older interface declarations may still be maintained in parallel (backwards compatibility!). + * @param ... Slot declarations for the functions provided by this interface @see LUMIERA_INTERFACE_SLOT + * The number of Slots in an interface defines its 'minor' version. + * New slots must be added at the end. The prototype and order of existing slots must not be changed. + * Slots may be renamed, for example a slot 'foo' can be renamed to 'foo_old' when a new 'foo' slot is + * added. Binary modules will then still use the 'foo_old' slot which was the 'foo' slot at their + * compile time while compiling modules from source will use the new 'foo' slot. This may be + * intentionally used to break compilation and force the update of modules to a new api. + */ +#define LUMIERA_INTERFACE_DECLARE(name, version, ...) \ +LUMIERA_INTERFACE_TYPE(name, version) \ +{ \ + lumiera_interface interface_header_; \ + PPMPL_FOREACH(_, __VA_ARGS__) \ +} + +/** + * Declare function slot inside an interface. + * @param ret return type of the function + * @param name name of this slot + * @param params parentized list of parameters for the function + */ +#define PPMPL_FOREACH_LUMIERA_INTERFACE_SLOT(ret, name, params) ret (*name) params; lumiera_uid name##_uid; + + +/* + Interface definition macros + */ + +/** + * Define a interface instance. + * @param iname name of the interface to instance + * @param version major version of the interface to instance + * @param name name of the instance + * @param descriptor pointer to an interface instance which provides a description of this interface, might be NULL + * @param acquire a function which is called whenever this interface is opened for using, might be NULL + * @param release a function which is called whenever this interface is closed after use, might be NULL + * @param ... map functions to interface slots @see LUMIERA_INTERFACE_MAP + */ +#define LUMIERA_INTERFACE_INSTANCE(iname, version, name, descriptor, acquire, release, ...) \ +PPMPL_FOREACH(_P1_, __VA_ARGS__) \ +LUMIERA_INTERFACE_TYPE(iname, version) LUMIERA_INTERFACE_DNAME(iname, version, name) = \ +{ \ +{ \ + #iname, \ + version, \ + #name, \ + sizeof (LUMIERA_INTERFACE_TYPE(iname, version)), \ + descriptor, \ + acquire, \ + release \ +}, \ +PPMPL_FOREACH(_P2_, __VA_ARGS__) \ +} + + +/** + * Map a function to a interface slot + * @param slot name of the slot to be mapped + * @param luid unique identifier for this function, use the magic word LUIDGEN here and run the + * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * @param function name of the function to be mapped on slot + * + * @note C++ requires that all mappings are in the same order than defined in the interface declaration, + * this would be good style for C too anyways + */ +#define PPMPL_FOREACH_P1_LUMIERA_INTERFACE_MAP(slot, luid, function) +#ifdef __cplusplus +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_MAP(slot, luid, function) \ + function, LUMIERA_UID_INITIALIZER (luid), +#else +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_MAP(slot, luid, function) \ + .slot = function, .slot##_uid = LUMIERA_UID_INITIALIZER (luid), +#endif + + +/** + * Map a inline defined function to a interface slot + * @param slot name of the slot to be mapped + * @param luid unique identifier for this function, use the magic word LUIDGEN here and run the + * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * @param ret return type of the inline function + * @param params parentized list of parameters given to the function + * @param ... braced function body + * + * @note C++ requires that all mappings are in the same order than defined in the interface declaration, + * this would be good style for C too anyways + */ +#define PPMPL_FOREACH_P1_LUMIERA_INTERFACE_INLINE(slot, luid, ret, params, ...) \ + static ret \ + LUMIERA_INTERFACE_INLINE_NAME(slot) params \ + __VA_ARGS__ + +#ifdef __cplusplus +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_INLINE(slot, luid, ret, params, ...) \ + LUMIERA_INTERFACE_INLINE_NAME(slot), LUMIERA_UID_INITIALIZER (luid), +#else +#define PPMPL_FOREACH_P2_LUMIERA_INTERFACE_INLINE(slot, luid, ret, params, ...) \ + .slot = LUMIERA_INTERFACE_INLINE_NAME(slot), .slot##_uid = LUMIERA_UID_INITIALIZER (luid), +#endif + +#define LUMIERA_INTERFACE_INLINE_NAME(slot) PPMPL_CAT(lumiera_##slot##_l, __LINE__) + + +#define PPMPL_FOREACH_L1_P1_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, acquire, release, ...) \ +LUMIERA_INTERFACE_INSTANCE (iname, version, \ + name, \ + descriptor, \ + acquire, \ + release, \ + __VA_ARGS__ \ + ); + + +#define PPMPL_FOREACH_L1_P2_LUMIERA_INTERFACE_DEFINE(iname, version, name, descriptor, acquire, release, ...) \ + &LUMIERA_INTERFACE_DNAME(iname, version, name).interface_header_, + + +/** + * Generate interface container suitable for enumerating interfaces. + * This takes a list of interface definitions, instantiates them and places pointers to them + * into a zero terminated array which address is returned by the a created function. + * For interfaces generated by he core, the user is responsible to register these at the + * plugindb dynamically + * @param queryfunc name of the function to be created. + * @param ... list of LUMIERA_INTERFACE_DEFINE() for all interfaces this plugin provides. + */ +#define LUMIERA_EXPORT(queryfunc, ...) \ +PPMPL_FOREACH_L1(_P1_, __VA_ARGS__) \ +static LumieraInterface* \ +queryfunc (void) \ +{ \ + static LumieraInterface interfaces[] = \ + { \ + PPMPL_FOREACH_L1(_P2_, __VA_ARGS__) \ + NULL \ + }; \ + return interfaces; \ +} + + +/** + * Generate interface container suitable for a lumiera plugin. + * This takes a list of interface definitions and places pointers to them into a zero terminated array. Further + * it instances a local 'plugin interface' which will be picked up by the plugin loader to query the array of + * provided interfaces. + * @param descriptor pointer to an interface instance which provides a description of this plugin, might be NULL + * @param acquire a function which is called whenever the plugin interface is opened for using, might be NULL + * @param release a function which is called whenever this plugin interface is closed after use, might be NULL + * @param luid unique identifier for the this plugin interfaces query, use the magic word LUIDGEN here and run the + * lumiera uuid generator tool (to be written) over the source file to generate luid's automatically + * @param ... list of LUMIERA_INTERFACE_DEFINE() for all interfaces this plugin provides. + */ +#define LUMIERA_PLUGIN(descriptor, acquire, release, luid, ...) \ +LUMIERA_EXPORT(plugin_interfaces, __VA_ARGS__) \ +LUMIERA_INTERFACE_DEFINE (lumieraorg_plugin, 0, \ + lumieraorg_plugin_0, \ + NULL, \ + NULL, \ + NULL, \ + LUMIERA_INTERFACE_MAP (plugin_interfaces, plugin_interfaces, luid) \ + ) + + + +/** + * create a handle for a interface (WIP) + */ + +#define LUMIERA_INTERFACE_HANDLE(interface, version) \ + LUMIERA_INTERFACE_TYPE(interface, version)* + +#define LUMIERA_INTERFACE_OPEN(interface, version, minminor, name) \ + LUMIERA_INTERFACE_CAST(interface, version) lumiera_interface_open (#interface, version, minminor, #name) + + +typedef struct lumiera_interfaceslot_struct lumiera_interfaceslot; +typedef lumiera_interfaceslot* LumieraInterfaceslot; + +typedef struct lumiera_interface_struct lumiera_interface; +typedef lumiera_interface* LumieraInterface; + +/** + * This is just a placeholder for an entry in a interface table. + * It consists of one here generic, later correctly prototyped function pointer and + * a unique identifier which is associated with this function. + */ +struct lumiera_interfaceslot_struct +{ + void (*func)(void); + lumiera_uid uid; +}; + + +/** + * Header for an interface, just the absolute necessary metadata. + */ +struct lumiera_interface_struct +{ + /** name of the interface (type) */ + const char* interface; + + /** major version, 0 means experimental */ + unsigned version; + + /** name of this instance */ + const char* name; + + /** size of the whole interface structure (minor version) */ + size_t size; + + /** metadata descriptor, itself a interface (or NULL) */ + LumieraInterface descriptor; + + /** + * Must be called before this interface is used. + * might be nested. + * @param self pointer to the interface to be acquired + * @return pointer to the interface or NULL on error + */ + LumieraInterface (*acquire)(LumieraInterface self); + /** + * called when finished using this interface + * must match the acquire calls + * @param self pointer to the interface to be released + */ + void (*release)(LumieraInterface self); + +#ifndef __cplusplus + /** placeholder array for the following function slots, C++ doesn't support flexible arrays */ + lumiera_interfaceslot functions[]; +#endif +}; + +/* + API to handle interfaces + */ + + +/** + * Open an interface by version and name. + * Looks up the requested interface, possibly loading it from a plugin. + * @param interface name of the interface definition + * @param version major version of the interface definition + * @param minminorversion required minor version (structure size) + * @param name name of the interface implementation + * @return the queried interface handle on success, else NULL + */ +LumieraInterface +lumiera_interface_open (const char* interface, unsigned version, size_t minminorversion, const char* name); + +/** + * Close an interface after use. + * @param self interface to be closed + * consider 'self' to be invalidated after this call + */ +void +lumiera_interface_close (LumieraInterface iface); + + +#endif /* LUMIERA_INTERFACE_H */ +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/interfacedescriptor.h b/src/backend/interfacedescriptor.h new file mode 100644 index 000000000..468bd2e80 --- /dev/null +++ b/src/backend/interfacedescriptor.h @@ -0,0 +1,48 @@ +/* + interfacedescriptor.h - Metadata interface for Lumiera interfaces + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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_INTERFACEDESCRIPTOR_H +#define LUMIERA_INTERFACEDESCRIPTOR_H + +#include "backend/interface.h" + +/** + * WIP: interface descriptor, needs some generic metadata interface + */ +LUMIERA_INTERFACE_DECLARE (lumieraorg_interfacedescriptor, 0, + /* The following slots are some human-readable descriptions of certain properties */ + LUMIERA_INTERFACE_SLOT (const char*, name, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, version, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, author, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, copyright, (LumieraInterface)), + LUMIERA_INTERFACE_SLOT (const char*, license, (LumieraInterface)) + /* TODO add more things here, dependencies, provisions etc */ + ); + + + +#endif /* LUMIERA_INTERFACEDESCRIPTORS_H */ +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/interfaceregistry.c b/src/backend/interfaceregistry.c new file mode 100644 index 000000000..ffa979340 --- /dev/null +++ b/src/backend/interfaceregistry.c @@ -0,0 +1,255 @@ +/* + interfaceregistry.c - Lumiera interface registry + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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/mutex.h" +#include "lib/error.h" +#include "lib/psplay.h" +#include "lib/safeclib.h" + + +#include + + + +#include "backend/interfaceregistry.h" + +/** + * @file + * Interface instances are published and activated by registering them + * into a global registry, which is defined here. This instances are identified + * by their name and major version. + */ + +NOBUG_DEFINE_FLAG_PARENT (interface_all, backend); +NOBUG_DEFINE_FLAG_PARENT (interfaceregistry, interface_all); +NOBUG_DEFINE_FLAG_PARENT (interface, interface_all); + +PSplay lumiera_interfaceregistry; +lumiera_mutex lumiera_interface_mutex; + +static int +cmp_fn (const void* keya, const void* keyb); + +static const void* +key_fn (const PSplaynode node); + + +static LumieraInterfacenode +lumiera_interfacenode_new (LumieraInterface iface) +{ + LumieraInterfacenode self = lumiera_malloc (sizeof (*self)); + + psplaynode_init (&self->node); + self->interface = iface; + self->refcnt = 0; + self->lnk = NULL; + self->deps_size = 0; + self->deps = NULL; + + return self; +} + + +static void +lumiera_interfacenode_delete (LumieraInterfacenode self) +{ + if (self) + { + REQUIRE (self->refcnt == 0); + lumiera_free (self->deps); + lumiera_free (self); + } +} + + +/** + * Initialize the interface registry + */ +void +lumiera_interfaceregistry_init (void) +{ + NOBUG_INIT_FLAG (interface_all); + NOBUG_INIT_FLAG (interfaceregistry); + NOBUG_INIT_FLAG (interface); + TRACE (interfaceregistry); + REQUIRE (!lumiera_interfaceregistry); + + lumiera_interfaceregistry = psplay_new (cmp_fn, key_fn, NULL); + if (!lumiera_interfaceregistry) + LUMIERA_DIE (ERRNO); + + lumiera_recmutex_init (&lumiera_interface_mutex, "interfaceregistry", &NOBUG_FLAG(interfaceregistry)); +} + + +void +lumiera_interfaceregistry_destroy (void) +{ + TRACE (interfaceregistry); + REQUIRE (!psplay_nelements (lumiera_interfaceregistry)); + + lumiera_mutex_destroy (&lumiera_interface_mutex, &NOBUG_FLAG(interfaceregistry)); + + if (lumiera_interfaceregistry) + psplay_destroy (lumiera_interfaceregistry); + lumiera_interfaceregistry = NULL; +} + + +void +lumiera_interfaceregistry_register_interface (LumieraInterface self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + TRACE (interfaceregistry, "interface %s, version %d, instance %s", self->interface, self->version, self->name); + psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (self)->node, 100); + } +} + + +void +lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + while (*self) + { + TRACE (interfaceregistry, "interface %s, version %d, instance %s", (*self)->interface, (*self)->version, (*self)->name); + psplay_insert (lumiera_interfaceregistry, &lumiera_interfacenode_new (*self)->node, 100); + ++self; + } + } +} + + +void +lumiera_interfaceregistry_remove_interface (LumieraInterface self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + LumieraInterfacenode node = (LumieraInterfacenode) psplay_find (lumiera_interfaceregistry, self, 0); + REQUIRE (node->refcnt == 0, "but is %d", node->refcnt); + + lumiera_interfacenode_delete ((LumieraInterfacenode)psplay_remove (lumiera_interfaceregistry, &node->node)); + } +} + + +void +lumiera_interfaceregistry_bulkremove_interfaces (LumieraInterface* self) +{ + TRACE (interfaceregistry); + REQUIRE (self); + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + while (*self) + { + TRACE (interfaceregistry, "interface %s, version %d, instance %s", (*self)->interface, (*self)->version, (*self)->name); + + LumieraInterfacenode node = (LumieraInterfacenode) psplay_find (lumiera_interfaceregistry, *self, 0); + REQUIRE (node->refcnt == 0, "but is %d", node->refcnt); + + lumiera_interfacenode_delete ((LumieraInterfacenode) psplay_remove (lumiera_interfaceregistry, &node->node)); + + ++self; + } + } +} + + +LumieraInterfacenode +lumiera_interfaceregistry_interfacenode_find (const char* interface, unsigned version, const char* name) +{ + TRACE (interfaceregistry); + struct lumiera_interface_struct cmp; + cmp.interface = interface; + cmp.version = version; + cmp.name = name; + + LumieraInterfacenode ret = NULL; + + LUMIERA_RECMUTEX_SECTION (interfaceregistry, &lumiera_interface_mutex) + { + ret = (LumieraInterfacenode)psplay_find (lumiera_interfaceregistry, &cmp, 100); + } + + return ret; +} + + +LumieraInterface +lumiera_interfaceregistry_interface_find (const char* interface, unsigned version, const char* name) +{ + return lumiera_interfaceregistry_interfacenode_find (interface, version, name)->interface; +} + + +static int +cmp_fn (const void* keya, const void* keyb) +{ + const LumieraInterface a = (const LumieraInterface)keya; + const LumieraInterface b = (const LumieraInterface)keyb; + + int r = strcmp (a->interface, b->interface); + if (r<0) + return -1; + else if (r>0) + return 1; + + if (a->version < b->version) + return -1; + else if (a->version > b->version) + return 1; + + r = strcmp (a->name, b->name); + if (r<0) + return -1; + else if (r>0) + return 1; + + return 0; +} + + +static const void* +key_fn (const PSplaynode node) +{ + return ((LumieraInterfacenode)node)->interface; +} + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/backend/interfaceregistry.h b/src/backend/interfaceregistry.h new file mode 100644 index 000000000..02481072d --- /dev/null +++ b/src/backend/interfaceregistry.h @@ -0,0 +1,114 @@ +/* + interfaceregistry.h - Lumiera interface registry + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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_INTERFACEREGISTRY_H +#define LUMIERA_INTERFACEREGISTRY_H + +#include "lib/mutex.h" +#include "lib/psplay.h" + +#include "backend/interface.h" + +#include + + +/** + * @file + * Interface instances are published and activated by registering them + * into a gloabl registry, which is defined here. This instances are identified + * by their name and major version. + */ + +NOBUG_DECLARE_FLAG (interface_all); +NOBUG_DECLARE_FLAG (interfaceregistry); +NOBUG_DECLARE_FLAG (interface); + +extern PSplay lumiera_interfaceregistry; +extern lumiera_mutex lumiera_interface_mutex; + + +/** + * Interface management node. + * All active interfaces managed through this node which contains the dynamic data for + * dependency tracking and reference counting. + */ +typedef struct lumiera_interfacenode_struct lumiera_interfacenode; +typedef lumiera_interfacenode* LumieraInterfacenode; + +struct lumiera_interfacenode_struct +{ + /** all known interfaces are registered in a tree */ + psplaynode node; + + /** the interface itself */ + LumieraInterface interface; + + /** reference counters and link used for internal reference management */ + unsigned refcnt; + /** temporary used to stack interfaces when recursively opening/closing them */ + LumieraInterfacenode lnk; + /** allocated size of the following deps table */ + size_t deps_size; + /** NULL terminated table of all dependenncies (interfaces opened on initialization) */ + LumieraInterfacenode* deps; +}; + +extern lumiera_mutex lumiera_interface_mutex; + + +/** + * Initialize the interface registry + */ +void +lumiera_interfaceregistry_init (void); + +void +lumiera_interfaceregistry_destroy (void); + + +void +lumiera_interfaceregistry_register_interface (LumieraInterface self); + +void +lumiera_interfaceregistry_bulkregister_interfaces (LumieraInterface* self); + +void +lumiera_interfaceregistry_remove_interface (LumieraInterface self); + +void +lumiera_interfaceregistry_bulkremove_interfaces (LumieraInterface* self); + +LumieraInterfacenode +lumiera_interfaceregistry_interfacenode_find (const char* interface, unsigned version, const char* name); + +LumieraInterface +lumiera_interfaceregistry_interface_find (const char* interface, unsigned version, const char* name); + + + + +#endif /* LUMIERA_INTERFACEREGISTRY_H */ +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/plugin.c b/src/backend/plugin.c similarity index 99% rename from src/lib/plugin.c rename to src/backend/plugin.c index 44cf6abc5..f6ecd7649 100644 --- a/src/lib/plugin.c +++ b/src/backend/plugin.c @@ -27,8 +27,9 @@ #include #include -#include "plugin.h" -#include "safeclib.h" +#include "lib/safeclib.h" + +#include "backend/plugin.h" /** * @file diff --git a/src/lib/plugin.h b/src/backend/plugin.h similarity index 100% rename from src/lib/plugin.h rename to src/backend/plugin.h diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index e7c22abdb..55d88e71a 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -20,22 +20,20 @@ noinst_LIBRARIES += liblumi.a liblumi_a_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Werror -liblumi_a_SOURCES = \ - $(liblumi_a_srcdir)/plugin.c \ - $(liblumi_a_srcdir)/error.c \ - $(liblumi_a_srcdir)/mutex.c \ - $(liblumi_a_srcdir)/rwlock.c \ - $(liblumi_a_srcdir)/condition.c \ - $(liblumi_a_srcdir)/luid.c \ - $(liblumi_a_srcdir)/safeclib.c \ - $(liblumi_a_srcdir)/cuckoo.c \ - $(liblumi_a_srcdir)/psplay.c \ - $(liblumi_a_srcdir)/mrucache.c \ - $(liblumi_a_srcdir)/time.c \ +liblumi_a_SOURCES = \ + $(liblumi_a_srcdir)/error.c \ + $(liblumi_a_srcdir)/mutex.c \ + $(liblumi_a_srcdir)/rwlock.c \ + $(liblumi_a_srcdir)/condition.c \ + $(liblumi_a_srcdir)/luid.c \ + $(liblumi_a_srcdir)/safeclib.c \ + $(liblumi_a_srcdir)/cuckoo.c \ + $(liblumi_a_srcdir)/psplay.c \ + $(liblumi_a_srcdir)/mrucache.c \ + $(liblumi_a_srcdir)/time.c \ $(liblumi_a_srcdir)/appconfig.cpp noinst_HEADERS += \ - $(liblumi_a_srcdir)/plugin.h \ $(liblumi_a_srcdir)/error.h \ $(liblumi_a_srcdir)/mutex.h \ $(liblumi_a_srcdir)/rwlock.h \ @@ -46,6 +44,7 @@ noinst_HEADERS += \ $(liblumi_a_srcdir)/psplay.h \ $(liblumi_a_srcdir)/mrucache.h \ $(liblumi_a_srcdir)/time.h \ + $(liblumi_a_srcdir)/ppmpl.h \ $(liblumi_a_srcdir)/appconfig.hpp \ $(liblumi_a_srcdir)/lifecycleregistry.hpp diff --git a/src/lib/luid.h b/src/lib/luid.h index a93d004a3..553d22765 100644 --- a/src/lib/luid.h +++ b/src/lib/luid.h @@ -31,6 +31,22 @@ */ typedef unsigned char lumiera_uid[16]; +typedef lumiera_uid* LumieraUid; + +/* + C++ can't initialize arrays from string literals with the trailing \0 cropped + C can't initialize non constant members, + there we go +*/ +#ifdef __cplusplus +#define LUMIERA_UID_INITIALIZER(l) \ + { \ + l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], \ + l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] \ + } +#else +#define LUMIERA_UID_INITIALIZER(l) l +#endif /** * Retrieve a generic pointer stored in a luid diff --git a/src/lib/mutex.c b/src/lib/mutex.c index 99ef8897f..b228de746 100644 --- a/src/lib/mutex.c +++ b/src/lib/mutex.c @@ -44,6 +44,30 @@ lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* f } +static pthread_once_t recursive_mutexattr_once = PTHREAD_ONCE_INIT; +static pthread_mutexattr_t recursive_mutexattr; + +static void recursive_mutexattr_init() +{ + pthread_mutexattr_init (&recursive_mutexattr); + pthread_mutexattr_settype (&recursive_mutexattr, PTHREAD_MUTEX_RECURSIVE); +} + + +LumieraMutex +lumiera_recmutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag) +{ + if (self) + { + pthread_once(&recursive_mutexattr_once, recursive_mutexattr_init); + pthread_mutex_init (&self->mutex, &recursive_mutexattr); + NOBUG_RESOURCE_HANDLE_INIT (self->rh); + NOBUG_RESOURCE_ANNOUNCE_RAW (flag, "recmutex", purpose, self, self->rh); + } + return self; +} + + LumieraMutex lumiera_mutex_destroy (LumieraMutex self, struct nobug_flag* flag) { diff --git a/src/lib/mutex.h b/src/lib/mutex.h index 681d29432..86fb48944 100644 --- a/src/lib/mutex.h +++ b/src/lib/mutex.h @@ -39,15 +39,78 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); /** * Mutual exclusive section. */ -#define LUMIERA_MUTEX_SECTION(nobugflag, mtx) \ - for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1}; \ +#define LUMIERA_MUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ \ + = {(LumieraMutex)1 NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ + })) + +/** + * Mutual exclusion chainbuilder section. + * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_MUTEX_SECTION_CHAIN(b){run();}} + * calls lock(a); lock(b); unlock(a); run(); unlock(b); + * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be + * called on the correct mutexes, period. + */ +#define LUMIERA_MUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ + NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ + NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + if (lumiera_mutex_section_old_->mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ + lumiera_mutex_section_old_->mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ + } \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ + })) + + +/** + * Recursive Mutual exclusive section. + */ +#define LUMIERA_RECMUTEX_SECTION(nobugflag, mtx) \ + for (lumiera_mutexacquirer NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ + NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ lumiera_mutex_section_.mutex;) \ for ( \ ({ \ lumiera_mutex_section_.mutex = (mtx); \ NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ - RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire mutex", &lumiera_mutex_section_, \ - NOBUG_RESOURCE_EXCLUSIVE, lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ }); \ lumiera_mutex_section_.mutex; \ @@ -61,6 +124,43 @@ LUMIERA_ERROR_DECLARE (MUTEX_DESTROY); })) +/** + * Mutual exclusion chainbuilder section. + * Usage: LUMIERA_MUTEX_SECTION(a){LUMIERA_RECMUTEX_SECTION_CHAIN(b){run();}} + * calls lock(a); lock(b); unlock(a); run(); unlock(b); + * This macro should only be used inside LUMIERA_MUTEX_SECTION and should be + * called on the correct mutexes, period. + */ +#define LUMIERA_RECMUTEX_SECTION_CHAIN(nobugflag, mtx) \ + for (lumiera_mutexacquirer *lumiera_mutex_section_old_ = &lumiera_mutex_section_, \ + NOBUG_CLEANUP(lumiera_mutexacquirer_ensureunlocked) lumiera_mutex_section_ = {(LumieraMutex)1 \ + NOBUG_RESOURCE_HANDLE_COMMA_INITIALIZER}; \ + lumiera_mutex_section_.mutex;) \ + for ( \ + ({ \ + lumiera_mutex_section_.mutex = (mtx); \ + NOBUG_RESOURCE_HANDLE_INIT (lumiera_mutex_section_.rh); \ + RESOURCE_ENTER (nobugflag, (mtx)->rh, "acquire recmutex", &lumiera_mutex_section_, \ + NOBUG_RESOURCE_RECURSIVE, lumiera_mutex_section_.rh); \ + if (pthread_mutex_lock (&(mtx)->mutex)) LUMIERA_DIE (MUTEX_LOCK); \ + if (lumiera_mutex_section_old_->mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_old_->mutex->mutex); \ + lumiera_mutex_section_old_->mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_old_->rh); \ + } \ + }); \ + lumiera_mutex_section_.mutex; \ + ({ \ + if (lumiera_mutex_section_.mutex) \ + { \ + pthread_mutex_unlock (&lumiera_mutex_section_.mutex->mutex); \ + lumiera_mutex_section_.mutex = NULL; \ + RESOURCE_LEAVE(nobugflag, lumiera_mutex_section_.rh); \ + } \ + })) + + /** * Mutex. * @@ -76,12 +176,22 @@ typedef lumiera_mutex* LumieraMutex; /** * Initialize a mutex variable + * This initializes a 'fast' default mutex which must not be locked recursively from one thread. * @param self is a pointer to the mutex to be initialized * @return self as given */ LumieraMutex lumiera_mutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag); +/** + * Initialize a mutex variable + * Initializes a 'recursive' mutex which might be locked by the same thread multiple times. + * @param self is a pointer to the mutex to be initialized + * @return self as given + */ +LumieraMutex +lumiera_recmutex_init (LumieraMutex self, const char* purpose, struct nobug_flag* flag); + /** * Destroy a mutex variable diff --git a/src/lib/ppmpl.h b/src/lib/ppmpl.h new file mode 100644 index 000000000..edc7bec7c --- /dev/null +++ b/src/lib/ppmpl.h @@ -0,0 +1,158 @@ +/* + ppmpl.h - preprocessor meta programming library + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 PPMPL_H +#define PPMPL_H + +/** + * @file + * Preprocessor metaprogramming library. We define some useful preprocessor + * tricks here. + */ + + +/** + * Iterate over a list of macros. + * @param p used to disambiguate up to three passes, use _, _P1_ or _P2_ + * @param ... list of macros to be expanded. The user has to supply a definition + * in the form of PPMPL_FOREACH##p##macroname which shall expand to the desired text. + * + * This user defined macro shall be undefed after use. + * + * @example + * #define PPMPL_FOREACH_P1_FOO(arg) arg, + * + * {PPMPL_FOREACH(P1, FOO(1), FOO(2), FOO(3)), -1} + * + * #undef PPMPL_FOREACH_P1_FOO + * + * Would expand to the sequence: + * {1, 2, 3, -1} + * + * One can not recursively nest preprocessor macros. To allow this we define PPMPL_FOREACH_L1 + * to PPMPL_FOREACH_L2 with the same semantics as PPMPL_FOREACH, This allowes to nest the + * FOREACH loop up to three nesting levels. + */ +#define PPMPL_FOREACH(p, ...) PPMPL_FOREACH0(p, __VA_ARGS__, PPMPL_FOREACH_NIL)) + +/* internal */ +#define PPMPL_FOREACH0(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH1(p, __VA_ARGS__) +#define PPMPL_FOREACH1(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH2(p, __VA_ARGS__) +#define PPMPL_FOREACH2(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH3(p, __VA_ARGS__) +#define PPMPL_FOREACH3(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH4(p, __VA_ARGS__) +#define PPMPL_FOREACH4(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH5(p, __VA_ARGS__) +#define PPMPL_FOREACH5(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH6(p, __VA_ARGS__) +#define PPMPL_FOREACH6(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH7(p, __VA_ARGS__) +#define PPMPL_FOREACH7(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH8(p, __VA_ARGS__) +#define PPMPL_FOREACH8(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH9(p, __VA_ARGS__) +#define PPMPL_FOREACH9(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH10(p, __VA_ARGS__) +#define PPMPL_FOREACH10(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH11(p, __VA_ARGS__) +#define PPMPL_FOREACH11(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH12(p, __VA_ARGS__) +#define PPMPL_FOREACH12(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH13(p, __VA_ARGS__) +#define PPMPL_FOREACH13(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH14(p, __VA_ARGS__) +#define PPMPL_FOREACH14(p, h, ...) PPMPL_FOREACH##p##h PPMPL_FOREACH15(p, __VA_ARGS__) +#define PPMPL_FOREACH15(p, h, ...) PPMPL_FOREACH##p##h +#define PPMPL_FOREACH_ +#define PPMPL_FOREACH_P1_ +#define PPMPL_FOREACH_P2_ +#define PPMPL_FOREACH_PPMPL_FOREACH_NIL PPMPL_FOREACH_FINAL( +#define PPMPL_FOREACH_P1_PPMPL_FOREACH_NIL PPMPL_FOREACH_FINAL( +#define PPMPL_FOREACH_P2_PPMPL_FOREACH_NIL PPMPL_FOREACH_FINAL( +#define PPMPL_FOREACH_FINAL(...) + + + + +#define PPMPL_FOREACH_L1(p, ...) PPMPL_FOREACH_L1_0(p, __VA_ARGS__, PPMPL_FOREACH_L1_NIL)) + +/* internal */ +#define PPMPL_FOREACH_L1_0(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_1(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_1(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_2(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_2(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_3(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_3(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_4(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_4(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_5(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_5(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_6(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_6(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_7(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_7(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_8(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_8(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_9(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_9(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_10(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_10(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_11(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_11(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_12(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_12(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_13(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_13(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_14(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_14(p, h, ...) PPMPL_FOREACH_L1##p##h PPMPL_FOREACH_L1_15(p, __VA_ARGS__) +#define PPMPL_FOREACH_L1_15(p, h, ...) PPMPL_FOREACH_L1##p##h +#define PPMPL_FOREACH_L1_ +#define PPMPL_FOREACH_L1_P1_ +#define PPMPL_FOREACH_L1_P2_ +#define PPMPL_FOREACH_L1_PPMPL_FOREACH_L1_NIL PPMPL_FOREACH_L1_FINAL( +#define PPMPL_FOREACH_L1_P1_PPMPL_FOREACH_L1_NIL PPMPL_FOREACH_L1_FINAL( +#define PPMPL_FOREACH_L1_P2_PPMPL_FOREACH_L1_NIL PPMPL_FOREACH_L1_FINAL( +#define PPMPL_FOREACH_L1_FINAL(...) + + + + +#define PPMPL_FOREACH_L2(p, ...) PPMPL_FOREACH_L2_0(p, __VA_ARGS__, PPMPL_FOREACH_L2_NIL)) + +/* internal */ +#define PPMPL_FOREACH_L2_0(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_1(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_1(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_2(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_2(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_3(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_3(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_4(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_4(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_5(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_5(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_6(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_6(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_7(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_7(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_8(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_8(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_9(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_9(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_10(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_10(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_11(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_11(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_12(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_12(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_13(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_13(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_14(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_14(p, h, ...) PPMPL_FOREACH_L2##p##h PPMPL_FOREACH_L2_15(p, __VA_ARGS__) +#define PPMPL_FOREACH_L2_15(p, h, ...) PPMPL_FOREACH_L2##p##h +#define PPMPL_FOREACH_L2_ +#define PPMPL_FOREACH_L2_P1_ +#define PPMPL_FOREACH_L2_P2_ +#define PPMPL_FOREACH_L2_PPMPL_FOREACH_L2_NIL PPMPL_FOREACH_L2_FINAL( +#define PPMPL_FOREACH_L2_P1_PPMPL_FOREACH_L2_NIL PPMPL_FOREACH_L2_FINAL( +#define PPMPL_FOREACH_L2_P2_PPMPL_FOREACH_L2_NIL PPMPL_FOREACH_L2_FINAL( +#define PPMPL_FOREACH_L2_FINAL(...) + +/** + * Canonical preprocessor concat implementation which evaluates its arguments + */ +#define PPMPL_CAT(a,b) PPMPL_CAT_(a,b) +#define PPMPL_CAT_(a,b) a##b + + +#define PPMPL_STRINGIFY(a) PPMPL_STRINGIFY_(a) +#define PPMPL_STRINGIFY_(a) #a + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/psplay.h b/src/lib/psplay.h index 26639910a..b3c016c52 100644 --- a/src/lib/psplay.h +++ b/src/lib/psplay.h @@ -51,6 +51,7 @@ struct psplaynode_struct PSplaynode right; }; +#define PSPLAYNODE_INITIALIZER {NULL, NULL} /** * Function use to compare keys diff --git a/src/lib/safeclib.c b/src/lib/safeclib.c index 1c5cf28c6..7f8f79eb2 100644 --- a/src/lib/safeclib.c +++ b/src/lib/safeclib.c @@ -51,6 +51,17 @@ lumiera_calloc (size_t n, size_t size) } +void* +lumiera_realloc (void* ptr, size_t size) +{ + void* o = size ? realloc (ptr, size) : NULL; + if (!o) + LUMIERA_DIE (NO_MEMORY); + + return o; +} + + char* lumiera_strndup (const char* str, size_t len) { diff --git a/src/lib/safeclib.h b/src/lib/safeclib.h index 79f10d024..d949595f8 100644 --- a/src/lib/safeclib.h +++ b/src/lib/safeclib.h @@ -50,6 +50,16 @@ void* lumiera_calloc (size_t n, size_t size); +/** + * Change the size of a memory block. + * @param ptr pointer to the old memory block obtained by lumiera_malloc or lumiera_calloc + * @param size new size of the block + * @return address of new block + */ +void* +lumiera_realloc (void* ptr, size_t size); + + /** * Free previously allocated memory. * @param mem pointer to the memory block obtained by lumiera_malloc or lumiera_calloc diff --git a/tests/15locking.tests b/tests/15locking.tests index c7e8340e5..3420f3fa1 100644 --- a/tests/15locking.tests +++ b/tests/15locking.tests @@ -18,6 +18,18 @@ out: inner mutex locked section END +TEST "chained mutex section" chainedmutexsection <line = ' #comment bla' +END + +TEST "check content of configitem with section" configitem_simple_content_check $'[ key.foo suffix.bar ] ' << END +out: item->line = '[ key.foo suffix.bar ] ' +out: item->key_size = '7' +out: item->key = 'key.foo suffix.bar ] ' +out: item->delim = ' suffix.bar ] ' +END + +TEST "check content of configitem with directive (without argument)" configitem_simple_content_check $'\t @directive ' << END +out: item->line = ' @directive ' +out: item->key_size = '9' +out: item->key = '@directive ' +END + +TEST "check content of configitem with directive (with argument)" configitem_simple_content_check $'\t @directive \targument' << END +out: item->line = ' @directive argument' +out: item->key_size = '9' +out: item->key = '@directive argument' +out: item->delim = ' argument' +END + +TEST "check content of configitem with configentry" configitem_simple_content_check $' \t\t key.foo \t\t=\tbar' << END +out: item->line = ' key.foo = bar' +out: item->key_size = '7' +out: item->key = 'key.foo = bar' +out: item->delim = '= bar' +END + +TEST "check content of configitem with configentry (redirect)" configitem_simple_content_check $' \t\t key.foo \t\t<\tkey.bar' << END +out: item->line = ' key.foo < key.bar' +out: item->key_size = '7' +out: item->key = 'key.foo < key.bar' +out: item->delim = '< key.bar' +END + TEST "set a config and retrieve it" basic_set_get 'foo' '=bar' <line = ' #comment bla' +TEST "wordlist find, last" wordlist_find 'foo.bar' 'baz barf, gnarf' gnarf << END +out: '2' END -TEST "check content of configitem with section" configitem_simple_content_check $'[ key.foo suffix.bar ] ' << END -out: item->line = '[ key.foo suffix.bar ] ' -out: item->key_size = '7' -out: item->key = 'key.foo suffix.bar ] ' -out: item->delim = ' suffix.bar ] ' +TEST "wordlist replace, middle, insert after" wordlist_replace 'foo.bar' 'baz barf gnarf' barf barf foof << END +out: ' baz barf foof gnarf' END -TEST "check content of configitem with directive (without argument)" configitem_simple_content_check $'\t @directive ' << END -out: item->line = ' @directive ' -out: item->key_size = '9' -out: item->key = '@directive ' +TEST "wordlist replace, middle, insert before" wordlist_replace 'foo.bar' 'baz barf gnarf' barf foof barf << END +out: ' baz foof barf gnarf' END -TEST "check content of configitem with directive (with argument)" configitem_simple_content_check $'\t @directive \targument' << END -out: item->line = ' @directive argument' -out: item->key_size = '9' -out: item->key = '@directive argument' -out: item->delim = ' argument' +TEST "wordlist replace, middle, remove" wordlist_replace 'foo.bar' 'baz barf gnarf' barf '' '' << END +out: ' baz gnarf' END -TEST "check content of configitem with configentry" configitem_simple_content_check $' \t\t key.foo \t\t=\tbar' << END -out: item->line = ' key.foo = bar' -out: item->key_size = '7' -out: item->key = 'key.foo = bar' -out: item->delim = '= bar' +TEST "wordlist replace, middle, replace1" wordlist_replace 'foo.bar' 'baz barf gnarf' barf 'foof' '' << END +out: ' baz foof gnarf' END -TEST "check content of configitem with configentry (redirect)" configitem_simple_content_check $' \t\t key.foo \t\t<\tkey.bar' << END -out: item->line = ' key.foo < key.bar' -out: item->key_size = '7' -out: item->key = 'key.foo < key.bar' -out: item->delim = '< key.bar' +TEST "wordlist replace, middle, replace2" wordlist_replace 'foo.bar' 'baz barf gnarf' barf '' 'foof' << END +out: ' baz foof gnarf' END + +TEST "wordlist replace, first, insert before" wordlist_replace 'foo.bar' 'baz barf gnarf' baz 'first' 'baz' << END +out: ' first baz barf gnarf' +END + +TEST "wordlist replace, first, replace1" wordlist_replace 'foo.bar' 'baz barf gnarf' baz 'first' '' << END +out: ' first barf gnarf' +END + +TEST "wordlist replace, first, replace2" wordlist_replace 'foo.bar' 'baz barf gnarf' baz '' 'first' << END +out: ' first barf gnarf' +END + +TEST "wordlist replace, last, insert after" wordlist_replace 'foo.bar' 'baz barf gnarf' gnarf 'gnarf' 'last' << END +out: ' baz barf gnarf last' +END + +TEST "wordlist add 2 words" wordlist_add 'foo.bar' 'baz barf gnarf' first second << END +out: ' baz barf gnarf first' +out: ' baz barf gnarf first second' +END + +TEST "wordlist add same word" wordlist_add 'foo.bar' 'baz barf gnarf' same same << END +out: ' baz barf gnarf same' +out: ' baz barf gnarf same' +END + +TEST "wordlist add to empty list" wordlist_add 'foo.bar' '' first second << END +out: ' first' +out: ' first second' +END + +TEST "wordlist add to empty list, same" wordlist_add 'foo.bar' '' same same << END +out: ' same' +out: ' same' +END + + +TEST "wordlist get item from empty list should fail" wordlist_get_nth 'foo.bar' '' 0 << END +out: 'NULL' +END + +TEST "wordlist get item past end should fail" wordlist_get_nth 'foo.bar' 'baz barf gnarf' 3 << END +out: 'NULL' +END + +TEST "wordlist get first item" wordlist_get_nth 'foo.bar' 'baz barf gnarf' 0 << END +out: 'baz' +END + +TEST "wordlist get last item" wordlist_get_nth 'foo.bar' 'baz barf; gnarf' 2 << END +out: 'gnarf' +END + +TEST "wordlist get middle" wordlist_get_nth 'foo.bar' 'baz barf, gnarf' 1 << END +out: 'barf' +END + diff --git a/tests/Makefile.am b/tests/Makefile.am index fa28a115f..0c7f80f12 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -48,6 +48,11 @@ test_luid_SOURCES = $(tests_srcdir)/library/test-luid.c test_luid_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror test_luid_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm +check_PROGRAMS += test-interfaces +test_interfaces_SOURCES = $(tests_srcdir)/backend/test-interfaces.c +test_interfaces_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror +test_interfaces_LDADD = liblumibackend.a liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -lm + check_PROGRAMS += test-filedescriptors test_filedescriptors_SOURCES = $(tests_srcdir)/backend/test-filedescriptors.c test_filedescriptors_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror diff --git a/tests/plugin/example_plugin.c b/tests/backend/example_plugin.c similarity index 100% rename from tests/plugin/example_plugin.c rename to tests/backend/example_plugin.c diff --git a/tests/plugin/example_plugin.cpp b/tests/backend/example_plugin.cpp similarity index 100% rename from tests/plugin/example_plugin.cpp rename to tests/backend/example_plugin.cpp diff --git a/tests/plugin/hello_interface.h b/tests/backend/hello_interface.h similarity index 100% rename from tests/plugin/hello_interface.h rename to tests/backend/hello_interface.h diff --git a/tests/plugin/plugin_main.c b/tests/backend/plugin_main.c similarity index 100% rename from tests/plugin/plugin_main.c rename to tests/backend/plugin_main.c diff --git a/tests/backend/test-config.c b/tests/backend/test-config.c index ea312a429..506801454 100644 --- a/tests/backend/test-config.c +++ b/tests/backend/test-config.c @@ -83,6 +83,36 @@ TEST ("lookup") } +TEST ("change_value") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + + lumiera_config_init ("./"); + const char* value; + + if (!lumiera_config_set (argv[2], argv[3])) + printf ("failure setting first time '%s%s': %s\n", argv[2], argv[3], lumiera_error ()); + + if (lumiera_config_get (argv[2], &value)) + printf ("%s\n", value); + else + printf ("failure retrieving '%s': %s\n", argv[2], lumiera_error ()); + + if (!lumiera_config_set (argv[2], argv[4])) + printf ("failure setting second time '%s%s': %s\n", argv[2], argv[4], lumiera_error ()); + + if (lumiera_config_get (argv[2], &value)) + printf ("%s\n", value); + else + printf ("failure retrieving '%s': %s\n", argv[2], lumiera_error ()); + + lumiera_config_destroy (); +} + + + TEST ("basic_set_get") { REQUIRE (argv[2]); @@ -258,6 +288,7 @@ TEST ("configitem_simple_ctor_dtor") lumiera_config_destroy (); } + TEST ("configitem_simple_content_check") { REQUIRE (argv[2]); @@ -287,4 +318,95 @@ TEST ("configitem_simple_content_check") lumiera_config_destroy (); } + +TEST ("wordlist_get_nth") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + const char* word = lumiera_config_wordlist_get_nth (argv[2], atoi (argv[4])); + + printf ("'%s'\n", word?word:"NULL"); + + lumiera_config_destroy (); +} + + +TEST ("wordlist_find") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + int n = lumiera_config_wordlist_find (argv[2], argv[4]); + + printf ("'%d'\n", n); + + lumiera_config_destroy (); +} + + +TEST ("wordlist_replace") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + REQUIRE (argv[5]); + REQUIRE (argv[6]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + const char* wordlist = lumiera_config_wordlist_replace (argv[2], argv[4], *argv[5]?argv[5]:NULL, *argv[6]?argv[6]:NULL); + + if (wordlist) + printf ("'%s'\n", wordlist); + else + printf ("%s\n", lumiera_error ()); + + lumiera_config_destroy (); +} + + +TEST ("wordlist_add") +{ + REQUIRE (argv[2]); + REQUIRE (argv[3]); + REQUIRE (argv[4]); + REQUIRE (argv[5]); + + lumiera_config_init ("./"); + + if (!lumiera_config_wordlist_set (argv[2], &argv[3])) + printf ("failed setting word '%s=%s': %s\n", argv[2], argv[3], lumiera_error ()); + + const char* wordlist = lumiera_config_wordlist_add (argv[2], argv[4]); + if (wordlist) + printf ("'%s'\n", wordlist); + else + printf ("%s\n", lumiera_error ()); + + wordlist = lumiera_config_wordlist_add (argv[2], argv[5]); + if (wordlist) + printf ("'%s'\n", wordlist); + else + printf ("%s\n", lumiera_error ()); + + lumiera_config_destroy (); +} + + TESTS_END diff --git a/tests/backend/test-interfaces.c b/tests/backend/test-interfaces.c new file mode 100644 index 000000000..09fa25afa --- /dev/null +++ b/tests/backend/test-interfaces.c @@ -0,0 +1,452 @@ +/* + test-interfaces.c - test interfaces declaration and implementation + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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/interface.h" +#include "backend/interfaceregistry.h" +#include "backend/interfacedescriptor.h" +#include "tests/test.h" + +/* + define 2 example interfaces + */ + +LUMIERA_INTERFACE_DECLARE (lumieraorg_testexample_one, 0, + LUMIERA_INTERFACE_SLOT (void, foo1, (const char*)), + LUMIERA_INTERFACE_SLOT (void, bar1, (const char*)), +); + +LUMIERA_INTERFACE_DECLARE (lumieraorg_testexample_two, 0, + LUMIERA_INTERFACE_SLOT (void, foo2, (const char*)), + LUMIERA_INTERFACE_SLOT (void, bar2, (const char*)), +); + +LUMIERA_INTERFACE_DECLARE (lumieraorg_testexample_void, 0 +); + + +/* + now the functions we want to bind to them + */ + +void +testfunc (const char* message) +{ + printf ("Called as '%s'\n", message); +} + + + +LumieraInterface +testacquire (LumieraInterface self) +{ + printf ("Acquire %s_%d_%s\n", self->interface, self->version, self->name); + return self; +} + + +void +testrelease (LumieraInterface self) +{ + printf ("Release %s_%d_%s\n", self->interface, self->version, self->name); +} + +/* + implementation of some example interfaces + */ + +LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0, + lumieraorg_tests_descriptor, + /*self reference, yay*/ + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire, + testrelease, + LUMIERA_INTERFACE_INLINE (name, "\073\003\054\127\344\046\324\321\221\262\232\026\376\123\125\243", + const char*, (LumieraInterface iface), + {return "Lumiera Test suite examples";} + ), + LUMIERA_INTERFACE_INLINE (version, "\271\330\345\066\304\217\211\065\157\120\031\365\304\363\364\074", + const char*, (LumieraInterface iface), + {return "No Version";} + ), + LUMIERA_INTERFACE_INLINE (author, "\367\160\342\065\147\007\237\371\141\335\371\131\025\030\257\232", + const char*, (LumieraInterface iface), + {return "Christian Thaeter ";} + ), + LUMIERA_INTERFACE_INLINE (copyright, "\163\106\344\014\251\125\111\252\236\322\174\120\335\225\333\245", + const char*, (LumieraInterface iface), + { + return + "Copyright (C) Lumiera.org\n" + " 2008 Christian Thaeter "; + } + ), + LUMIERA_INTERFACE_INLINE (license, "\343\031\207\122\225\217\014\163\015\023\243\101\165\377\222\350", + const char*, (LumieraInterface iface), + { + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + ); + + + +LUMIERA_EXPORT (interfaces_defined_here, + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_one, 0, + lumieraorg_first_test, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire, + testrelease, + LUMIERA_INTERFACE_MAP (foo1, "\214\310\136\372\003\344\163\377\075\100\070\200\375\221\227\324", + testfunc), + LUMIERA_INTERFACE_MAP (bar1, "\262\253\067\211\157\052\212\140\114\334\231\250\340\075\214\030", + testfunc) + ), + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_two, 0, + lumieraorg_second_test, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire, + testrelease, + LUMIERA_INTERFACE_MAP (foo2, "\110\152\002\271\363\052\324\272\373\045\132\270\277\000\271\217", + testfunc), + LUMIERA_INTERFACE_MAP (bar2, "\376\042\027\336\355\113\132\233\350\312\170\077\377\370\356\167", + testfunc) + ) + ); + + +/* + Now we rig a cross dependency test + + we have 4 instances, the respective acquire/release operations set following up: + + one depends on two and three + two depends on one and four + three depends on two and four + four depends on one, two three + + These all are empty interfaces with no slots +*/ + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) one_keeps_two; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) one_keeps_three; + +LumieraInterface +testacquire_one (LumieraInterface self) +{ + TRACE (tests, "Acquire one %s_%d_%s", self->interface, self->version, self->name); + one_keeps_two = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + one_keeps_three = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + return self; +} + +void +testrelease_one (LumieraInterface self) +{ + TRACE (tests, "Release one %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)one_keeps_two); + lumiera_interface_close ((LumieraInterface)one_keeps_three); +} + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) two_keeps_one; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) two_keeps_four; + +LumieraInterface +testacquire_two (LumieraInterface self) +{ + TRACE (tests, "Acquire two %s_%d_%s", self->interface, self->version, self->name); + two_keeps_one = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + two_keeps_four = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + return self; +} + +void +testrelease_two (LumieraInterface self) +{ + TRACE (tests, "Release two %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)two_keeps_one); + lumiera_interface_close ((LumieraInterface)two_keeps_four); +} + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) three_keeps_two; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) three_keeps_four; + +LumieraInterface +testacquire_three (LumieraInterface self) +{ + TRACE (tests, "Acquire three %s_%d_%s", self->interface, self->version, self->name); + three_keeps_two = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + three_keeps_four = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + return self; +} + +void +testrelease_three (LumieraInterface self) +{ + TRACE (tests, "Release three %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)three_keeps_two); + lumiera_interface_close ((LumieraInterface)three_keeps_four); +} + +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) four_keeps_one; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) four_keeps_two; +static LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) four_keeps_three; + +LumieraInterface +testacquire_four (LumieraInterface self) +{ + TRACE (tests, "Acquire four %s_%d_%s", self->interface, self->version, self->name); + four_keeps_one = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + four_keeps_two = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + four_keeps_three = LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + return self; +} + +void +testrelease_four (LumieraInterface self) +{ + TRACE (tests, "Release four %s_%d_%s", self->interface, self->version, self->name); + lumiera_interface_close ((LumieraInterface)four_keeps_one); + lumiera_interface_close ((LumieraInterface)four_keeps_two); + lumiera_interface_close ((LumieraInterface)four_keeps_three); +} + + +LUMIERA_EXPORT (dependencytests, + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_one, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_one, + testrelease_one + ), + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_two, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_two, + testrelease_two + ), + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_three, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_three, + testrelease_three + ), + LUMIERA_INTERFACE_DEFINE (lumieraorg_testexample_void, 0, + lumieraorg_dependencytest_four, + LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_tests_descriptor), + testacquire_four, + testrelease_four + ) + ); + + +TESTS_BEGIN + +TEST ("basic") +{ + lumiera_interfaceregistry_init (); + + lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here()); + + + /* some ugly lowlevel handling tests */ + + LumieraInterface handle1 = + lumiera_interfaceregistry_interface_find ("lumieraorg_testexample_one", 0, "lumieraorg_first_test"); + + (LUMIERA_INTERFACE_CAST(lumieraorg_testexample_one, 0)handle1)->bar1 ("this is bar1"); + + + LUMIERA_INTERFACE_TYPE(lumieraorg_testexample_two, 0)* handle2 = LUMIERA_INTERFACE_CAST(lumieraorg_testexample_two, 0) + lumiera_interfaceregistry_interface_find ("lumieraorg_testexample_two", 0, "lumieraorg_second_test"); + + handle2->foo2 ("this is foo2"); + + lumiera_interfaceregistry_bulkremove_interfaces (interfaces_defined_here()); + lumiera_interfaceregistry_destroy (); +} + +TEST ("open_close") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (interfaces_defined_here()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_one, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_one, 0, 0, lumieraorg_first_test); + ENSURE (handle); + + handle->bar1 ("this is bar1"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (interfaces_defined_here()); + lumiera_interfaceregistry_destroy (); +} + +TEST ("dependencies_one") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + + +TEST ("dependencies_two") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + +TEST ("dependencies_three") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + + +TEST ("dependencies_four") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + ENSURE (handle); + + TRACE (tests, "Sucessfully opened"); + + lumiera_interface_close ((LumieraInterface)handle); + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + + + +TEST ("dependencies_all") +{ + lumiera_interfaceregistry_init (); + lumiera_interfaceregistry_bulkregister_interfaces (dependencytests()); + + TRACE (tests, "OPEN one"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_one = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_one); + ENSURE (handle_one); + + TRACE (tests, "OPEN three"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_three = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_three); + ENSURE (handle_three); + + TRACE (tests, "OPEN two"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_two = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_two); + ENSURE (handle_two); + + TRACE (tests, "OPEN four"); + LUMIERA_INTERFACE_HANDLE(lumieraorg_testexample_void, 0) handle_four = + LUMIERA_INTERFACE_OPEN (lumieraorg_testexample_void, 0, 0, lumieraorg_dependencytest_four); + ENSURE (handle_four); + + TRACE (tests, "Sucessfully OPENED"); + + TRACE (tests, "CLOSE four"); + lumiera_interface_close ((LumieraInterface)handle_four); + + TRACE (tests, "CLOSE two"); + lumiera_interface_close ((LumieraInterface)handle_two); + + TRACE (tests, "CLOSE three"); + lumiera_interface_close ((LumieraInterface)handle_three); + + TRACE (tests, "CLOSE one"); + lumiera_interface_close ((LumieraInterface)handle_one); + + + lumiera_interfaceregistry_bulkremove_interfaces (dependencytests()); + lumiera_interfaceregistry_destroy (); +} + + + +TEST ("highlevel, plugin") +{ + + +} + +TESTS_END + + + + + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/library/test-locking.c b/tests/library/test-locking.c index 55ea91cdb..aa2f6312c 100644 --- a/tests/library/test-locking.c +++ b/tests/library/test-locking.c @@ -85,7 +85,46 @@ TEST ("nestedmutexsection") lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); } +TEST ("chainedmutexsection") +{ + lumiera_mutex m; + lumiera_mutex_init (&m, "m_mutexsection", &NOBUG_FLAG(NOBUG_ON)); + lumiera_mutex n; + lumiera_mutex_init (&n, "n_mutexsection", &NOBUG_FLAG(NOBUG_ON)); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + printf ("outer mutex locked section\n"); + + LUMIERA_MUTEX_SECTION_CHAIN (NOBUG_ON, &n) + { + printf ("inner but not outer mutex locked section\n"); + } + } + + lumiera_mutex_destroy (&n, &NOBUG_FLAG(NOBUG_ON)); + lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); +} + +TEST ("recursivemutexsection") +{ + lumiera_mutex m; + lumiera_recmutex_init (&m, "m_mutexsection", &NOBUG_FLAG(NOBUG_ON)); + + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + printf ("mutex locked once\n"); + + LUMIERA_MUTEX_SECTION (NOBUG_ON, &m) + { + printf ("mutex locked twice\n"); + } + } + + lumiera_mutex_destroy (&m, &NOBUG_FLAG(NOBUG_ON)); +} TEST ("rwlocksection") { diff --git a/tests/plugin/DIR_INFO b/tests/plugin/DIR_INFO deleted file mode 100644 index c051168cc..000000000 --- a/tests/plugin/DIR_INFO +++ /dev/null @@ -1,3 +0,0 @@ -working example code for Lumiera's plugin system -This directory contains example code which shows how to use specific features. -All examples will be build and run as part of the testsuite diff --git a/tests/plugin/Makefile.am b/tests/plugin/Makefile.am deleted file mode 100644 index c8cf9a22f..000000000 --- a/tests/plugin/Makefile.am +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (C) Lumiera.org -# 2007, Christian Thaeter -# -# 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. - -examples_srcdir = $(top_srcdir)/tests/plugin -noinst_PROGRAMS += test-plugin - -test_plugin_CFLAGS = $(AM_CFLAGS) -std=gnu99 -Wall -Werror -test_plugin_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -test_plugin_LDADD = liblumi.a $(NOBUGMT_LUMIERA_LIBS) -ldl -test_plugin_SOURCES = $(examples_srcdir)/plugin_main.c - -noinst_HEADERS += $(examples_srcdir)/hello_interface.h - -check_LTLIBRARIES += example_plugin.la example_plugin_cpp.la -example_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -example_plugin_la_SOURCES = $(examples_srcdir)/example_plugin.c -# the -rpath option is required, prolly a automake bug? -example_plugin_la_LDFLAGS = -avoid-version -module -rpath $(shell pwd) - -example_plugin_cpp_la_CPPFLAGS = $(AM_CPPFLAGS) -Wall -Werror -example_plugin_cpp_la_SOURCES = $(examples_srcdir)/example_plugin.cpp -# the -rpath option is required, prolly a automake bug? -example_plugin_cpp_la_LDFLAGS = -avoid-version -module -rpath $(shell pwd)