diff options
author | Michael Jennings <mej@kainx.org> | 2006-07-13 23:03:45 +0000 |
---|---|---|
committer | Michael Jennings <mej@kainx.org> | 2006-07-13 23:03:45 +0000 |
commit | 1f7ded81f9871bb18c7bb0eefa9668f223a31899 (patch) | |
tree | 57762f0b958d501980c6aa7485952d8c9bad24bc | |
parent | f6fc5e71127f6648bcc4106eea6ecfe40dadcd16 (diff) | |
download | libast-1f7ded81f9871bb18c7bb0eefa9668f223a31899.tar.gz |
Thu Jul 13 14:02:08 2006 Michael Jennings (mej)
Added thread, mutex, and condition interfaces along with their
corresponding pthreads-based implementations. Not quite done yet, and
not tested.
Added dynamically-loadable module object class along with
corresponding unit tests. Appears to work fine.
----------------------------------------------------------------------
SVN revision: 23882
-rw-r--r-- | ChangeLog | 9 | ||||
-rwxr-xr-x | autogen.sh | 4 | ||||
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | include/libast.h | 12 | ||||
-rw-r--r-- | include/libast/Makefile.am | 6 | ||||
-rw-r--r-- | include/libast/condition_if.h | 73 | ||||
-rw-r--r-- | include/libast/list_if.h | 4 | ||||
-rw-r--r-- | include/libast/module.h | 83 | ||||
-rw-r--r-- | include/libast/mutex_if.h | 71 | ||||
-rw-r--r-- | include/libast/pthreads.h | 121 | ||||
-rw-r--r-- | include/libast/thread_if.h | 96 | ||||
-rw-r--r-- | libast.m4 | 200 | ||||
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/array.c | 1 | ||||
-rw-r--r-- | src/module.c | 307 | ||||
-rw-r--r-- | src/msgs.c | 30 | ||||
-rw-r--r-- | src/pthreads.c | 733 | ||||
-rw-r--r-- | test/test.c | 490 |
18 files changed, 2014 insertions, 239 deletions
@@ -787,3 +787,12 @@ Added sprintf() routines for string classes. Began work on UTF-8/Unicode strings. ---------------------------------------------------------------------- +Thu Jul 13 14:02:08 2006 Michael Jennings (mej) + +Added thread, mutex, and condition interfaces along with their +corresponding pthreads-based implementations. Not quite done yet, and +not tested. + +Added dynamically-loadable module object class along with +corresponding unit tests. Appears to work fine. +---------------------------------------------------------------------- @@ -55,9 +55,9 @@ done export LIBTOOLIZE ACLOCAL AUTOCONF AUTOHEADER AUTOMAKE # Check for existing libast.m4 we can use. Use the local one if not. -if test ! -f "`$ACLOCAL --print-ac-dir`/libast.m4"; then +#if test ! -f "`$ACLOCAL --print-ac-dir`/libast.m4"; then ACLOCAL_FLAGS="-I . $ACLOCAL_FLAGS" -fi +#fi # Run the stuff. (set -x && $LIBTOOLIZE -c -f) || abort libtool diff --git a/configure.in b/configure.in index d95bd55..623c8ba 100644 --- a/configure.in +++ b/configure.in @@ -96,6 +96,7 @@ AST_IMLIB2_SUPPORT() AST_MMX_SUPPORT() AST_ARG_REGEXP(REGEXP) AST_ARG_BACKQUOTE_EXEC(ALLOW_BACKQUOTE_EXEC) +AST_PTHREADS() AST_FLAGS() AC_SUBST(CC) diff --git a/include/libast.h b/include/libast.h index d397328..3df49d8 100644 --- a/include/libast.h +++ b/include/libast.h @@ -107,10 +107,13 @@ extern int re_exec(); # endif #endif +/* Type and object headers that must go first. */ #include <libast/types.h> #include <libast/obj.h> +/* Basic objects */ #include <libast/mbuff.h> +#include <libast/module.h> #include <libast/objpair.h> #include <libast/regexp.h> #include <libast/socket.h> @@ -119,15 +122,23 @@ extern int re_exec(); #include <libast/url.h> #include <libast/ustr.h> +/* Interface classes */ +#include <libast/condition_if.h> #include <libast/iterator_if.h> #include <libast/list_if.h> #include <libast/map_if.h> +#include <libast/mutex_if.h> +#include <libast/thread_if.h> #include <libast/vector_if.h> +/* List/vector/map implementations */ #include <libast/array.h> #include <libast/linked_list.h> #include <libast/dlinked_list.h> +/* Thread/condition/mutex implementations */ +#include <libast/pthreads.h> + #include <libast/avl_tree.h> /******************************* GENERIC GOOP *********************************/ @@ -2690,6 +2701,7 @@ typedef spif_uint32_t (*spifhash_func_t)(spif_uint8_t *, spif_uint32_t, spif_uin /* msgs.c */ extern void libast_set_program_name(const char *); extern void libast_set_program_version(const char *); +extern spif_bool_t libast_set_silent(spif_bool_t); extern int libast_dprintf(const char *, ...); extern void libast_print_error(const char *fmt, ...); extern void libast_print_warning(const char *fmt, ...); diff --git a/include/libast/Makefile.am b/include/libast/Makefile.am index f0e26c7..1052bda 100644 --- a/include/libast/Makefile.am +++ b/include/libast/Makefile.am @@ -1,8 +1,9 @@ # $Id: Makefile.am,v 1.6 2001/09/22 16:25:29 mej Exp $ EXTRA_HEADERS = array.h avl_tree.h dlinked_list.h iterator_if.h \ -linked_list.h list_if.h map_if.h mbuff.h obj.h objpair.h regexp.h \ -socket.h str.h sysdefs.h tok.h types.h url.h ustr.h vector_if.h +linked_list.h list_if.h map_if.h mbuff.h module.h obj.h objpair.h \ +regexp.h socket.h str.h sysdefs.h tok.h types.h url.h ustr.h \ +vector_if.h install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(includedir)/$(PACKAGE) @@ -22,4 +23,5 @@ types.h: types.h.in sysdefs.h: sysdefs.h.in (cd $(top_srcdir) && ./config.status) + MAINTAINERCLEANFILES = Makefile.in diff --git a/include/libast/condition_if.h b/include/libast/condition_if.h new file mode 100644 index 0000000..c3f847c --- /dev/null +++ b/include/libast/condition_if.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 1997-2004, Michael Jennings + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software, its documentation and marketing & publicity + * materials, and acknowledgment shall be given in the documentation, materials + * and software packages that this Software was used. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBAST_CONDITION_IF_H_ +#define _LIBAST_CONDITION_IF_H_ + +/* + * interface goop + */ + +/* Standard typecast macros.... */ +#define SPIF_CONDITION(o) (SPIF_CAST(condition) (o)) +#define SPIF_CONDITION_CLASS(o) (SPIF_CAST(conditionclass) SPIF_OBJ_CLASS(o)) + +/* Name of class variable associated with condition interface */ +#define SPIF_CONDITIONCLASS_VAR(type) spif_ ## type ## _conditionclass + +/* Check if a condition is NULL */ +#define SPIF_CONDITION_ISNULL(o) (SPIF_CONDITION(o) == SPIF_NULL_TYPE(condition)) + +/* Check if an object is a condition */ +#define SPIF_OBJ_IS_CONDITION(o) SPIF_OBJ_IS_TYPE(o, condition) + +/* Call a method on an instance of an implementation class */ +#define SPIF_CONDITION_CALL_METHOD(o, meth) SPIF_CONDITION_CLASS(o)->meth + +/* Calls to the basic functions. */ +#define SPIF_CONDITION_NEW(type) SPIF_CONDITION((SPIF_CLASS(SPIF_CONDITIONCLASS_VAR(type)))->noo()) +#define SPIF_CONDITION_INIT(o) SPIF_OBJ_INIT(o) +#define SPIF_CONDITION_DONE(o) SPIF_OBJ_DONE(o) +#define SPIF_CONDITION_DEL(o) SPIF_OBJ_DEL(o) +#define SPIF_CONDITION_SHOW(o, b, i) SPIF_OBJ_SHOW(o, b, i) +#define SPIF_CONDITION_COMP(o1, o2) SPIF_OBJ_COMP(o1, o2) +#define SPIF_CONDITION_DUP(o) SPIF_OBJ_DUP(o) +#define SPIF_CONDITION_TYPE(o) SPIF_OBJ_TYPE(o) + +#define SPIF_CONDITION_BROADCAST(o) SPIF_CAST(bool) ((SPIF_CONDITION_CALL_METHOD((o), broadcast))(o)) +#define SPIF_CONDITION_SIGNAL(o) SPIF_CAST(bool) ((SPIF_CONDITION_CALL_METHOD((o), signal))(o)) +#define SPIF_CONDITION_WAIT(o) SPIF_CAST(bool) ((SPIF_CONDITION_CALL_METHOD((o), wait))(o)) +#define SPIF_CONDITION_WAIT_TIMED(o, t) SPIF_CAST(bool) ((SPIF_CONDITION_CALL_METHOD((o), wait_timed))((o), (t))) + +typedef spif_obj_t spif_condition_t; + +SPIF_DECL_OBJ(conditionclass) { + SPIF_DECL_PARENT_TYPE(class); + + spif_func_t broadcast; + spif_func_t signal; + spif_func_t wait; + spif_func_t wait_timed; +}; + +#endif /* _LIBAST_CONDITION_IF_H_ */ diff --git a/include/libast/list_if.h b/include/libast/list_if.h index 5a07a6a..7c3a4dc 100644 --- a/include/libast/list_if.h +++ b/include/libast/list_if.h @@ -56,10 +56,10 @@ #define SPIF_LIST_APPEND(o, item) SPIF_CAST(bool) ((SPIF_LIST_CALL_METHOD((o), append))(o, item)) #define SPIF_LIST_CONTAINS(o, item) SPIF_CAST(bool) ((SPIF_LIST_CALL_METHOD((o), contains))(o, item)) -#define SPIF_LIST_COUNT(o) SPIF_CAST_C(size_t) ((SPIF_LIST_CALL_METHOD((o), count))(o)) +#define SPIF_LIST_COUNT(o) SPIF_CAST(listidx) ((SPIF_LIST_CALL_METHOD((o), count))(o)) #define SPIF_LIST_FIND(o, item) SPIF_CAST(obj) ((SPIF_LIST_CALL_METHOD((o), find))(o, item)) #define SPIF_LIST_GET(o, index) SPIF_CAST(obj) ((SPIF_LIST_CALL_METHOD((o), get))(o, index)) -#define SPIF_LIST_INDEX(o, item) SPIF_CAST_C(size_t) ((SPIF_LIST_CALL_METHOD((o), index))(o, item)) +#define SPIF_LIST_INDEX(o, item) SPIF_CAST(listidx) ((SPIF_LIST_CALL_METHOD((o), index))(o, item)) #define SPIF_LIST_INSERT(o, item) SPIF_CAST(bool) ((SPIF_LIST_CALL_METHOD((o), insert))(o, item)) #define SPIF_LIST_INSERT_AT(o, item, index) SPIF_CAST(bool) ((SPIF_LIST_CALL_METHOD((o), insert_at))(o, item, index)) #define SPIF_LIST_ITERATOR(o) SPIF_CAST(iterator) ((SPIF_LIST_CALL_METHOD((o), iterator))(o)) diff --git a/include/libast/module.h b/include/libast/module.h new file mode 100644 index 0000000..52eac80 --- /dev/null +++ b/include/libast/module.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 1997-2004, Michael Jennings + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software, its documentation and marketing & publicity + * materials, and acknowledgment shall be given in the documentation, materials + * and software packages that this Software was used. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBAST_MODULE_H_ +#define _LIBAST_MODULE_H_ + +#define SPIF_MODULE(obj) (SPIF_CAST(module) (obj)) +#define SPIF_MODULE_CLASS(o) (SPIF_CAST(moduleclass) SPIF_OBJ_CLASS(o)) +#define SPIF_OBJ_IS_MODULE(o) (SPIF_OBJ_IS_TYPE(o, module)) +#define SPIF_MODULE_ISNULL(s) SPIF_OBJ_ISNULL(SPIF_OBJ(s)) +#define SPIF_MODULECLASS_VAR(type) spif_ ## type ## _moduleclass +#define SPIF_MODULE_CALL_METHOD(o, meth) SPIF_MODULE_CLASS(o)->meth + +#define SPIF_MODULE_NEW(type) SPIF_MODULE((SPIF_CLASS(SPIF_CLASS_VAR(type)))->noo()) +#define SPIF_MODULE_INIT(obj) SPIF_OBJ_INIT(obj) +#define SPIF_MODULE_DONE(obj) SPIF_OBJ_DONE(obj) +#define SPIF_MODULE_DEL(obj) SPIF_OBJ_DEL(obj) +#define SPIF_MODULE_SHOW(obj, b, i) SPIF_OBJ_SHOW(obj, b, i) +#define SPIF_MODULE_COMP(o1, o2) SPIF_OBJ_COMP(o1, o2) +#define SPIF_MODULE_DUP(obj) SPIF_OBJ_DUP(obj) +#define SPIF_MODULE_TYPE(obj) SPIF_OBJ_TYPE(obj) + +typedef spif_ptr_t (spif_getsym_func_t)(spif_charptr_t); + +SPIF_DECL_OBJ(module) { + SPIF_DECL_PARENT_TYPE(obj); + SPIF_DECL_PROPERTY(str, name); + SPIF_DECL_PROPERTY(str, path); + SPIF_DECL_PROPERTY(ptr, module_handle); + SPIF_DECL_PROPERTY(ptr, main_handle); +}; + +SPIF_DECL_OBJ(moduleclass) { + SPIF_DECL_PARENT_TYPE(class); + + spif_func_t call; + spif_func_t getsym; + spif_func_t load; + spif_func_t run; + spif_func_t unload; +}; + +extern spif_class_t SPIF_CLASS_VAR(module); +extern spif_moduleclass_t SPIF_MODULECLASS_VAR(module); +extern spif_module_t spif_module_new(void); +extern spif_bool_t spif_module_del(spif_module_t); +extern spif_bool_t spif_module_init(spif_module_t); +extern spif_bool_t spif_module_done(spif_module_t); +extern spif_str_t spif_module_show(spif_module_t, spif_charptr_t, spif_str_t, size_t); +extern spif_cmp_t spif_module_comp(spif_module_t, spif_module_t); +extern spif_module_t spif_module_dup(spif_module_t); +extern spif_classname_t spif_module_type(spif_module_t); +extern spif_ptr_t spif_module_call(spif_module_t self, spif_charptr_t fname, spif_ptr_t data); +extern spif_ptr_t spif_module_getsym(spif_module_t self, spif_charptr_t sym); +extern spif_bool_t spif_module_load(spif_module_t self); +extern spif_bool_t spif_module_run(spif_module_t self); +extern spif_bool_t spif_module_unload(spif_module_t self); +SPIF_DECL_PROPERTY_FUNC(module, str, name); +SPIF_DECL_PROPERTY_FUNC(module, str, path); +SPIF_DECL_PROPERTY_FUNC(module, ptr, module_handle); +SPIF_DECL_PROPERTY_FUNC(module, ptr, main_handle); + +#endif /* _LIBAST_MODULE_H_ */ diff --git a/include/libast/mutex_if.h b/include/libast/mutex_if.h new file mode 100644 index 0000000..c948e16 --- /dev/null +++ b/include/libast/mutex_if.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1997-2004, Michael Jennings + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software, its documentation and marketing & publicity + * materials, and acknowledgment shall be given in the documentation, materials + * and software packages that this Software was used. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBAST_MUTEX_IF_H_ +#define _LIBAST_MUTEX_IF_H_ + +/* + * interface goop + */ + +/* Standard typecast macros.... */ +#define SPIF_MUTEX(o) (SPIF_CAST(mutex) (o)) +#define SPIF_MUTEX_CLASS(o) (SPIF_CAST(mutexclass) SPIF_OBJ_CLASS(o)) + +/* Name of class variable associated with mutex interface */ +#define SPIF_MUTEXCLASS_VAR(type) spif_ ## type ## _mutexclass + +/* Check if a mutex is NULL */ +#define SPIF_MUTEX_ISNULL(o) (SPIF_MUTEX(o) == SPIF_NULL_TYPE(mutex)) + +/* Check if an object is a mutex */ +#define SPIF_OBJ_IS_MUTEX(o) SPIF_OBJ_IS_TYPE(o, mutex) + +/* Call a method on an instance of an implementation class */ +#define SPIF_MUTEX_CALL_METHOD(o, meth) SPIF_MUTEX_CLASS(o)->meth + +/* Calls to the basic functions. */ +#define SPIF_MUTEX_NEW(type) SPIF_MUTEX((SPIF_CLASS(SPIF_MUTEXCLASS_VAR(type)))->noo()) +#define SPIF_MUTEX_INIT(o) SPIF_OBJ_INIT(o) +#define SPIF_MUTEX_DONE(o) SPIF_OBJ_DONE(o) +#define SPIF_MUTEX_DEL(o) SPIF_OBJ_DEL(o) +#define SPIF_MUTEX_SHOW(o, b, i) SPIF_OBJ_SHOW(o, b, i) +#define SPIF_MUTEX_COMP(o1, o2) SPIF_OBJ_COMP(o1, o2) +#define SPIF_MUTEX_DUP(o) SPIF_OBJ_DUP(o) +#define SPIF_MUTEX_TYPE(o) SPIF_OBJ_TYPE(o) + +#define SPIF_MUTEX_LOCK() SPIF_CAST(bool) ((SPIF_MUTEX_CALL_METHOD((o), lock))(o)) +#define SPIF_MUTEX_LOCK_NOWAIT() SPIF_CAST(bool) ((SPIF_MUTEX_CALL_METHOD((o), lock_nowait))(o)) +#define SPIF_MUTEX_UNLOCK() SPIF_CAST(bool) ((SPIF_MUTEX_CALL_METHOD((o), unlock))(o)) + +typedef spif_obj_t spif_mutex_t; + +SPIF_DECL_OBJ(mutexclass) { + SPIF_DECL_PARENT_TYPE(class); + + spif_func_t lock; + spif_func_t lock_nowait; + spif_func_t unlock; +}; + +#endif /* _LIBAST_MUTEX_IF_H_ */ diff --git a/include/libast/pthreads.h b/include/libast/pthreads.h new file mode 100644 index 0000000..2b8f59b --- /dev/null +++ b/include/libast/pthreads.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 1997-2004, Michael Jennings + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software, its documentation and marketing & publicity + * materials, and acknowledgment shall be given in the documentation, materials + * and software packages that this Software was used. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBAST_PTHREADS_H_ +#define _LIBAST_PTHREADS_H_ + +#define SPIF_PTHREADS(obj) (SPIF_CAST(pthreads) (obj)) +#define SPIF_OBJ_IS_PTHREADS(o) (SPIF_OBJ_IS_TYPE(o, pthreads)) +#define SPIF_PTHREADS_ISNULL(s) SPIF_OBJ_ISNULL(SPIF_OBJ(s)) + +SPIF_DECL_OBJ(pthreads) { + SPIF_DECL_PARENT_TYPE(obj); + SPIF_DECL_PROPERTY_C(pthread_t, handle); + SPIF_DECL_PROPERTY_C(pthread_t, creator); + SPIF_DECL_PROPERTY_C(pthread_attr_t, attr); + SPIF_DECL_PROPERTY(thread_func, main_func); + SPIF_DECL_PROPERTY(thread_data, data); + SPIF_DECL_PROPERTY(list, tls_keys); +}; + +#define SPIF_PTHREADS_MUTEX(obj) (SPIF_CAST(pthreads_mutex) (obj)) +#define SPIF_OBJ_IS_PTHREADS_MUTEX(o) (SPIF_OBJ_IS_TYPE(o, pthreads_mutex)) +#define SPIF_PTHREADS_MUTEX_ISNULL(s) SPIF_OBJ_ISNULL(SPIF_OBJ(s)) + +SPIF_DECL_OBJ(pthreads_mutex) { + SPIF_DECL_PARENT_TYPE(obj); + SPIF_DECL_PROPERTY(thread, creator); + SPIF_DECL_PROPERTY_C(pthread_mutex_t, mutex); +}; + +#define SPIF_PTHREADS_CONDITION(obj) (SPIF_CAST(pthreads_condition) (obj)) +#define SPIF_OBJ_IS_PTHREADS_CONDITION(o) (SPIF_OBJ_IS_TYPE(o, pthreads_condition)) +#define SPIF_PTHREADS_CONDITION_ISNULL(s) SPIF_OBJ_ISNULL(SPIF_OBJ(s)) + +SPIF_DECL_OBJ(pthreads_condition) { + SPIF_DECL_PARENT_TYPE(pthreads_mutex); + SPIF_DECL_PROPERTY_C(pthread_cond_t, cond); +}; + +extern SPIF_TYPE(class) SPIF_CLASS_VAR(pthreads); +extern SPIF_TYPE(threadclass) SPIF_THREADCLASS_VAR(pthreads); +extern spif_pthreads_t spif_pthreads_new(void); +extern spif_pthreads_t spif_pthreads_new_with_func(spif_thread_func_t, spif_thread_data_t); +extern spif_bool_t spif_pthreads_init(spif_pthreads_t); +extern spif_bool_t spif_pthreads_init_with_func(spif_pthreads_t, spif_thread_func_t, spif_thread_data_t); +extern spif_bool_t spif_pthreads_done(spif_pthreads_t); +extern spif_bool_t spif_pthreads_del(spif_pthreads_t); +extern spif_str_t spif_pthreads_show(spif_pthreads_t, spif_charptr_t, spif_str_t, size_t); +extern spif_cmp_t spif_pthreads_comp(spif_pthreads_t, spif_pthreads_t); +extern spif_pthreads_t spif_pthreads_dup(spif_pthreads_t); +extern spif_classname_t spif_pthreads_type(spif_pthreads_t); +extern spif_bool_t spif_pthreads_detach(spif_pthreads_t self); +extern spif_condition_t spif_pthreads_get_condition(spif_pthreads_t self); +extern spif_mutex_t spif_pthreads_get_mutex(spif_pthreads_t self); +extern spif_bool_t spif_pthreads_kill(spif_pthreads_t self, int sig); +extern spif_bool_t spif_pthreads_run(spif_pthreads_t self); +extern spif_tls_handle_t spif_pthreads_tls_calloc(spif_pthreads_t self, size_t count, size_t size); +extern spif_bool_t spif_pthreads_tls_free(spif_pthreads_t self, spif_tls_handle_t handle); +extern spif_ptr_t spif_pthreads_tls_get(spif_pthreads_t self, spif_tls_handle_t handle); +extern spif_tls_handle_t spif_pthreads_tls_malloc(spif_pthreads_t self, spif_memidx_t size); +extern spif_bool_t spif_pthreads_tls_realloc(spif_pthreads_t self, spif_tls_handle_t handle, spif_memidx_t size); +extern spif_bool_t spif_pthreads_wait(spif_pthreads_t self, spif_condition_t cond); +extern spif_bool_t spif_pthreads_wait_for(spif_pthreads_t self, spif_pthreads_t other); +SPIF_DECL_PROPERTY_FUNC_C(pthreads, pthread_t, handle); +SPIF_DECL_PROPERTY_FUNC_C(pthreads, pthread_t, creator); +SPIF_DECL_PROPERTY_FUNC_C(pthreads, pthread_attr_t, attr); +SPIF_DECL_PROPERTY_FUNC(pthreads, thread_func, main_func); +SPIF_DECL_PROPERTY_FUNC(pthreads, thread_data, data); +SPIF_DECL_PROPERTY_FUNC(pthreads, list, tls_keys); + +extern SPIF_TYPE(mutexclass) SPIF_MUTEXCLASS_VAR(pthreads_mutex); +extern spif_pthreads_mutex_t spif_pthreads_mutex_new(void); +extern spif_bool_t spif_pthreads_mutex_init(spif_pthreads_mutex_t); +extern spif_bool_t spif_pthreads_mutex_done(spif_pthreads_mutex_t); +extern spif_bool_t spif_pthreads_mutex_del(spif_pthreads_mutex_t); +extern spif_str_t spif_pthreads_mutex_show(spif_pthreads_mutex_t, spif_charptr_t, spif_str_t, size_t); +extern spif_cmp_t spif_pthreads_mutex_comp(spif_pthreads_mutex_t, spif_pthreads_mutex_t); +extern spif_pthreads_mutex_t spif_pthreads_mutex_dup(spif_pthreads_mutex_t); +extern spif_classname_t spif_pthreads_mutex_type(spif_pthreads_mutex_t); +extern spif_bool_t spif_pthreads_mutex_lock(spif_pthreads_mutex_t); +extern spif_bool_t spif_pthreads_mutex_lock_nowait(spif_pthreads_mutex_t); +extern spif_bool_t spif_pthreads_mutex_unlock(spif_pthreads_mutex_t); +SPIF_DECL_PROPERTY_FUNC(pthreads_mutex, thread, creator); +SPIF_DECL_PROPERTY_FUNC_C(pthreads_mutex, pthread_mutex_t, mutex); + +extern SPIF_TYPE(conditionclass) SPIF_CONDITIONCLASS_VAR(pthreads_condition); +extern spif_pthreads_condition_t spif_pthreads_condition_new(void); +extern spif_bool_t spif_pthreads_condition_init(spif_pthreads_condition_t); +extern spif_bool_t spif_pthreads_condition_done(spif_pthreads_condition_t); +extern spif_bool_t spif_pthreads_condition_del(spif_pthreads_condition_t); +extern spif_str_t spif_pthreads_condition_show(spif_pthreads_condition_t, spif_charptr_t, spif_str_t, size_t); +extern spif_cmp_t spif_pthreads_condition_comp(spif_pthreads_condition_t, spif_pthreads_condition_t); +extern spif_pthreads_condition_t spif_pthreads_condition_dup(spif_pthreads_condition_t); +extern spif_classname_t spif_pthreads_condition_type(spif_pthreads_condition_t); +extern spif_bool_t spif_pthreads_condition_broadcast(spif_pthreads_condition_t); +extern spif_bool_t spif_pthreads_condition_signal(spif_pthreads_condition_t); +extern spif_bool_t spif_pthreads_condition_wait(spif_pthreads_condition_t); +extern spif_bool_t spif_pthreads_condition_wait_timed(spif_pthreads_condition_t, spif_int32_t); +SPIF_DECL_PROPERTY_FUNC_C(pthreads_condition, pthread_cond_t, cond); + +#endif /* _LIBAST_PTHREADS_H_ */ diff --git a/include/libast/thread_if.h b/include/libast/thread_if.h new file mode 100644 index 0000000..86654b0 --- /dev/null +++ b/include/libast/thread_if.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1997-2004, Michael Jennings + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software, its documentation and marketing & publicity + * materials, and acknowledgment shall be given in the documentation, materials + * and software packages that this Software was used. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBAST_THREAD_IF_H_ +#define _LIBAST_THREAD_IF_H_ + +/* + * interface goop + */ + +/* Standard typecast macros.... */ +#define SPIF_THREAD(o) (SPIF_CAST(thread) (o)) +#define SPIF_THREAD_CLASS(o) (SPIF_CAST(threadclass) SPIF_OBJ_CLASS(o)) + +/* Name of class variable associated with thread interface */ +#define SPIF_THREADCLASS_VAR(type) spif_ ## type ## _threadclass + +/* Check if a thread is NULL */ +#define SPIF_THREAD_ISNULL(o) (SPIF_THREAD(o) == SPIF_NULL_TYPE(thread)) + +/* Check if an object is a thread */ +#define SPIF_OBJ_IS_THREAD(o) SPIF_OBJ_IS_TYPE(o, thread) + +/* Call a method on an instance of an implementation class */ +#define SPIF_THREAD_CALL_METHOD(o, meth) SPIF_THREAD_CLASS(o)->meth + +/* Calls to the basic functions. */ +#define SPIF_THREAD_NEW(type) SPIF_THREAD((SPIF_CLASS(SPIF_THREADCLASS_VAR(type)))->noo()) +#define SPIF_THREAD_INIT(o) SPIF_OBJ_INIT(o) +#define SPIF_THREAD_DONE(o) SPIF_OBJ_DONE(o) +#define SPIF_THREAD_DEL(o) SPIF_OBJ_DEL(o) +#define SPIF_THREAD_SHOW(o, b, i) SPIF_OBJ_SHOW(o, b, i) +#define SPIF_THREAD_COMP(o1, o2) SPIF_OBJ_COMP(o1, o2) +#define SPIF_THREAD_DUP(o) SPIF_OBJ_DUP(o) +#define SPIF_THREAD_TYPE(o) SPIF_OBJ_TYPE(o) + +#define SPIF_THREAD_NEW_WITH_FUNC(type, f, d) SPIF_THREAD((SPIF_CLASS(SPIF_THREADCLASS_VAR(type)))->new_with_func((f), (d))) +#define SPIF_THREAD_INIT_WITH_FUNC(o, f, d) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), init_with_func))((o), (f), (d))) +#define SPIF_THREAD_DETACH(o) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), detach))(o)) +#define SPIF_THREAD_GET_CONDITION(o) SPIF_CAST(condition) ((SPIF_THREAD_CALL_METHOD((o), get_condition))()) +#define SPIF_THREAD_GET_MUTEX(o) SPIF_CAST(mutex) ((SPIF_THREAD_CALL_METHOD((o), get_mutex))()) +#define SPIF_THREAD_KILL(o, s) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), kill))((o), (s))) +#define SPIF_THREAD_RUN(o) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), run))(o)) +#define SPIF_THREAD_TLS_CALLOC(o, c, s) SPIF_CAST(tls_handle) ((SPIF_THREAD_CALL_METHOD((o), tls_calloc))((o), (c), (s))) +#define SPIF_THREAD_TLS_FREE(o, i) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), tls_free))((o), (i))) +#define SPIF_THREAD_TLS_GET(o, i) SPIF_CAST(ptr) ((SPIF_THREAD_CALL_METHOD((o), tls_get))((o), (i))) +#define SPIF_THREAD_TLS_MALLOC(o, s) SPIF_CAST(tls_handle) ((SPIF_THREAD_CALL_METHOD((o), tls_malloc))((o), (s))) +#define SPIF_THREAD_TLS_REALLOC(o, i, s) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), tls_realloc))((o), (i), (s))) +#define SPIF_THREAD_WAIT(o, c) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), wait))((o), (c))) +#define SPIF_THREAD_WAIT_FOR(o, t) SPIF_CAST(bool) ((SPIF_THREAD_CALL_METHOD((o), wait_for))((o), (t))) + +typedef spif_obj_t spif_thread_t; +typedef spif_ptr_t spif_thread_data_t; +typedef spif_thread_data_t (*spif_thread_func_t)(spif_thread_data_t); +typedef spif_listidx_t spif_tls_handle_t; + +SPIF_DECL_OBJ(threadclass) { + SPIF_DECL_PARENT_TYPE(class); + + spif_func_t new_with_func; + spif_func_t init_with_func; + spif_func_t detach; + spif_func_t get_condition; + spif_func_t get_mutex; + spif_func_t kill; + spif_func_t run; + spif_func_t tls_calloc; + spif_func_t tls_free; + spif_func_t tls_get; + spif_func_t tls_malloc; + spif_func_t tls_realloc; + spif_func_t wait; + spif_func_t wait_for; +}; + +#endif /* _LIBAST_THREAD_IF_H_ */ @@ -182,7 +182,7 @@ dnl# LibAST macro for Imlib2 support dnl# AC_DEFUN([AST_IMLIB2_SUPPORT], [ AC_ARG_WITH(imlib, - [ --with-imlib[=DIR] compile with Imlib2 support (default)], + [ --with-imlib[=DIR] compile with Imlib2 support (default)], [ if test "$withval" != "no"; then if test "$withval" != "yes"; then @@ -347,7 +347,7 @@ dnl# - arg 1 is the name of the env var to use dnl# AC_DEFUN([AST_ARG_DEBUG], [ AC_MSG_CHECKING(for debugging level) - AC_ARG_WITH(debugging, [ --with-debugging[=num] compile in debugging support. num >= 0], [ + AC_ARG_WITH(debugging, [ --with-debugging[=num] compile in debugging support. num >= 0], [ if test "$withval" = "yes"; then withval=4 fi @@ -368,7 +368,7 @@ AC_DEFUN([AST_ARG_DEBUG], [ ]) AC_DEFUN([AST_ARG_REGEXP], [ AC_ARG_WITH(regexp, - [ --with-regexp[=TYPE] specify the type of regular expression support (bsd, posix, pcre)], + [ --with-regexp[=TYPE] specify the type of regular expression support (bsd, posix, pcre)], [$1=$withval], [$1=yes]) AST_REGEXP_SUPPORT($1) AC_MSG_CHECKING(for regular expression support) @@ -377,7 +377,7 @@ AC_DEFUN([AST_ARG_REGEXP], [ AC_DEFUN([AST_ARG_BACKQUOTE_EXEC], [ AC_MSG_CHECKING(if backquote execution support should be enabled) AC_ARG_WITH(backquote-exec, - [ --without-backquote-exec disables the execution of commands from inside config files], + [ --without-backquote-exec disables the execution of commands from inside config files], [ if test "$withval" = "no"; then AC_MSG_RESULT(no) @@ -688,3 +688,195 @@ exit(1); AC_MSG_RESULT([unknown, assuming none]) fi ]) + +dnl# Taken from http://autoconf-archive.cryp.to/acx_pthread.html +AC_DEFUN([AST_PTHREADS], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +ast_pthreads_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, ast_pthreads_ok=yes) + AC_MSG_RESULT($ast_pthreads_ok) + if test x"$ast_pthreads_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ast_pthreads_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ast_pthreads_flags="-pthreads pthread -mt -pthread $ast_pthreads_flags" + ;; +esac + +if test x"$ast_pthreads_ok" = xno; then +for flag in $ast_pthreads_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(ast_pthreads_config, pthread-config, yes, no) + if test x"$ast_pthreads_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include <pthread.h>], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [ast_pthreads_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($ast_pthreads_ok) + if test "x$ast_pthreads_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ast_pthreads_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ast_pthreads_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREADS,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + ast_pthreads_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl AST_PTHREADS diff --git a/src/Makefile.am b/src/Makefile.am index 583cd55..c8ecfaf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,11 +3,13 @@ lib_LTLIBRARIES = libast.la INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/include/$(PACKAGE) +AM_CFLAGS = $(PTHREAD_CFLAGS) +AM_LDFLAGS = $(PTHREAD_LIBS) -libast_la_SOURCES = \ - array.c builtin_hashes.c conf.c debug.c dlinked_list.c file.c \ - linked_list.c mbuff.c mem.c msgs.c obj.c objpair.c options.c \ - regexp.c socket.c str.c strings.c snprintf.c tok.c url.c ustr.c +libast_la_SOURCES = array.c builtin_hashes.c conf.c debug.c \ +dlinked_list.c file.c linked_list.c mbuff.c mem.c module.c msgs.c \ +obj.c objpair.c options.c pthreads.c regexp.c socket.c str.c strings.c \ +snprintf.c tok.c url.c ustr.c -libast_la_LDFLAGS = -version-info 2:1:0 +libast_la_LDFLAGS = -version-info 2:2:0 MAINTAINERCLEANFILES = Makefile.in diff --git a/src/array.c b/src/array.c index 09fb59e..c432efd 100644 --- a/src/array.c +++ b/src/array.c @@ -728,7 +728,6 @@ spif_array_remove(spif_array_t self, spif_obj_t item) } left = self->len - i - 1; - printf("%d %d %d\n", i, left, self->len); tmp = self->items[i]; memmove(self->items + i, self->items + i + 1, sizeof(spif_obj_t) * left); self->len--; diff --git a/src/module.c b/src/module.c new file mode 100644 index 0000000..19819ca --- /dev/null +++ b/src/module.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 1997-2004, Michael Jennings + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software, its documentation and marketing & publicity + * materials, and acknowledgment shall be given in the documentation, materials + * and software packages that this Software was used. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +static const char __attribute__((unused)) cvs_ident[] = "$Id$"; + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <libast_internal.h> +#include <dlfcn.h> + +/* *INDENT-OFF* */ +static SPIF_CONST_TYPE(moduleclass) s_class = { + { + SPIF_DECL_CLASSNAME(module), + (spif_func_t) spif_module_new, + (spif_func_t) spif_module_init, + (spif_func_t) spif_module_done, + (spif_func_t) spif_module_del, + (spif_func_t) spif_module_show, + (spif_func_t) spif_module_comp, + (spif_func_t) spif_module_dup, + (spif_func_t) spif_module_type + }, + (spif_func_t) spif_module_call, + (spif_func_t) spif_module_getsym, + (spif_func_t) spif_module_load, + (spif_func_t) spif_module_run, + (spif_func_t) spif_module_unload, +}; +SPIF_TYPE(class) SPIF_CLASS_VAR(module) = SPIF_CAST(class) &s_class; +SPIF_TYPE(moduleclass) SPIF_MODULECLASS_VAR(module) = &s_class; +/* *INDENT-ON* */ + +static const size_t buff_inc = 4096; + +spif_module_t +spif_module_new(void) +{ + spif_module_t self; + + self = SPIF_ALLOC(module); + if (!spif_module_init(self)) { + SPIF_DEALLOC(self); + self = SPIF_NULL_TYPE(module); + } + return self; +} + +spif_bool_t +spif_module_init(spif_module_t self) +{ + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), FALSE); + /* ***NOT NEEDED*** spif_obj_init(SPIF_OBJ(self)); */ + spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS(SPIF_MODULECLASS_VAR(module))); + self->name = SPIF_NULL_TYPE(str); + self->path = SPIF_NULL_TYPE(str); + self->module_handle = SPIF_NULL_TYPE(ptr); + self->main_handle = dlopen(NULL, RTLD_LAZY); + return TRUE; +} + +spif_bool_t +spif_module_done(spif_module_t self) +{ + spif_bool_t ret = TRUE; + + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), FALSE); + if (self->module_handle) { + ret = spif_module_unload(self); + self->module_handle = SPIF_NULL_TYPE(ptr); + } + if (!SPIF_STR_ISNULL(self->name)) { + spif_str_del(self->name); + self->name = SPIF_NULL_TYPE(str); + } + if (!SPIF_STR_ISNULL(self->path)) { + spif_str_del(self->path); + self->path = SPIF_NULL_TYPE(str); + } + return ret; +} + +spif_bool_t +spif_module_del(spif_module_t self) +{ + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), FALSE); + spif_module_done(self); + dlclose(self->main_handle); + SPIF_DEALLOC(self); + return TRUE; +} + +spif_str_t +spif_module_show(spif_module_t self, spif_charptr_t name, spif_str_t buff, size_t indent) +{ + spif_char_t tmp[4096]; + + if (SPIF_MODULE_ISNULL(self)) { + SPIF_OBJ_SHOW_NULL(module, name, buff, indent, tmp); + return buff; + } + + memset(tmp, ' ', indent); + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, + "(spif_module_t) %s: %10p { \"", + name, SPIF_CAST(ptr) self); + if (SPIF_STR_ISNULL(buff)) { + buff = spif_str_new_from_ptr(tmp); + } else { + spif_str_append_from_ptr(buff, tmp); + } + + indent += 2; + if (indent < sizeof(tmp)) { + memset(tmp, ' ', indent); + } + buff = spif_str_show(self->name, SPIF_CHARPTR("name"), buff, indent); + buff = spif_str_show(self->path, SPIF_CHARPTR("path"), buff, indent); + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_ptr_t) module_handle: 0x%p\n", self->module_handle); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_ptr_t) main_handle: 0x%p\n", self->main_handle); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp), sizeof(tmp), "}\n"); + spif_str_append_from_ptr(buff, tmp); + return buff; +} + +spif_cmp_t +spif_module_comp(spif_module_t self, spif_module_t other) +{ + spif_cmp_t ret; + + SPIF_OBJ_COMP_CHECK_NULL(self, other); + SPIF_OBJ_COMP_CHECK_NULL(self->name, other->name); + if (!SPIF_CMP_IS_EQUAL(ret = spif_str_comp(self->name, other->name))) { + return ret; + } + SPIF_OBJ_COMP_CHECK_NULL(self->path, other->path); + if (!SPIF_CMP_IS_EQUAL(ret = spif_str_comp(self->path, other->path))) { + return ret; + } + return SPIF_CMP_FROM_INT((int) self->module_handle - (int) other->module_handle); +} + +spif_module_t +spif_module_dup(spif_module_t self) +{ + spif_module_t tmp; + + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), SPIF_NULL_TYPE(module)); + tmp = SPIF_ALLOC(module); + memcpy(tmp, self, SPIF_SIZEOF_TYPE(module)); + tmp->name = spif_str_dup(self->name); + tmp->path = spif_str_dup(self->path); + return tmp; +} + +spif_classname_t +spif_module_type(spif_module_t self) +{ + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), SPIF_CAST(classname) SPIF_NULLSTR_TYPE(classname)); + return SPIF_OBJ_CLASSNAME(self); +} + +spif_ptr_t +spif_module_call(spif_module_t self, spif_charptr_t fname, spif_ptr_t data) +{ + spif_func_t fp; + spif_charptr_t err; + + fp = SPIF_CAST(func) spif_module_getsym(self, fname); + if (SPIF_PTR_ISNULL(err)) { + /* No error. Proceed. */ + return (fp)(data); + } else { + libast_print_warning("Unable to call function %s() from module \"%s\" -- %s\n", fname, SPIF_STR_STR(self->name), err); + } + return SPIF_NULL_TYPE(ptr); +} + +spif_ptr_t +spif_module_getsym(spif_module_t self, spif_charptr_t sym) +{ + spif_ptr_t psym; + spif_charptr_t err; + + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), SPIF_NULL_TYPE(ptr)); + ASSERT_RVAL(!SPIF_PTR_ISNULL(sym), SPIF_NULL_TYPE(ptr)); + REQUIRE_RVAL(!SPIF_PTR_ISNULL(self->main_handle), SPIF_NULL_TYPE(ptr)); + dlerror(); + psym = SPIF_CAST(ptr) dlsym(self->module_handle, (const char *) sym); + err = SPIF_CAST(charptr) dlerror(); + if (SPIF_PTR_ISNULL(err)) { + /* No error. Proceed. */ + return psym; + } else { + psym = SPIF_CAST(ptr) dlsym(self->main_handle, (const char *) sym); + err = SPIF_CAST(charptr) dlerror(); + if (SPIF_PTR_ISNULL(err)) { + /* No error. Proceed. */ + return psym; + } else { +#ifdef RTLD_DEFAULT + psym = SPIF_CAST(ptr) dlsym(RTLD_DEFAULT, (const char *) sym); + err = SPIF_CAST(charptr) dlerror(); + if (SPIF_PTR_ISNULL(err)) { + /* No error. Proceed. */ + return psym; + } else +#endif + libast_print_warning("Unable to resolve symbol \"%s\" from module \"%s\" -- %s\n", sym, + SPIF_STR_STR(self->name), err); + } + } + return SPIF_NULL_TYPE(ptr); +} + +spif_bool_t +spif_module_load(spif_module_t self) +{ + spif_func_t fp; + + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), FALSE); + REQUIRE_RVAL(!SPIF_STR_ISNULL(self->path), FALSE); + + if (SPIF_STR_ISNULL(self->name)) { + spif_stridx_t idx; + + idx = spif_str_rindex(self->path, '/'); + if (idx == spif_str_get_len(self->path)) { + self->name = spif_str_dup(self->path); + } else { + self->name = spif_str_substr(self->path, idx, 0); + } + } + + self->module_handle = dlopen(SPIF_STR_STR(self->path), RTLD_LAZY | RTLD_GLOBAL); + if (SPIF_PTR_ISNULL(self->module_handle)) { + libast_print_error("Unable to dlopen() \"%s\" -- %s\n", SPIF_STR_STR(self->path), dlerror()); + return FALSE; + } + fp = spif_module_getsym(self, "init"); + if (fp) { + return (((fp)(self)) ? (TRUE) : (FALSE)); + } else { + return TRUE; + } +} + +spif_bool_t +spif_module_run(spif_module_t self) +{ + return ((spif_module_call(self, "run", NULL)) ? (TRUE) : (FALSE)); +} + +spif_bool_t +spif_module_unload(spif_module_t self) +{ + spif_func_t fp; + + ASSERT_RVAL(!SPIF_MODULE_ISNULL(self), FALSE); + REQUIRE_RVAL(!SPIF_PTR_ISNULL(self->module_handle), FALSE); + + fp = spif_module_getsym(self, "done"); + if (fp) { + if (!(fp)()) { + return FALSE; + } + } + + dlerror(); + if (dlclose(self->module_handle)) { + libast_print_warning("Unable to dlclose() \"%s\" -- %s\n", SPIF_STR_STR(self->path), dlerror()); + return FALSE; + } + self->module_handle = SPIF_NULL_TYPE(ptr); + return TRUE; +} + +SPIF_DEFINE_PROPERTY_FUNC(module, str, name); +SPIF_DEFINE_PROPERTY_FUNC(module, str, path); +SPIF_DEFINE_PROPERTY_FUNC(module, ptr, module_handle); +SPIF_DEFINE_PROPERTY_FUNC(module, ptr, main_handle); @@ -61,6 +61,14 @@ spif_charptr_t libast_program_name = SPIF_CAST(charptr) PACKAGE; spif_charptr_t libast_program_version = SPIF_CAST(charptr) VERSION; /** + * Silence mode + * + * This variable determines whether or not error, warning, and + * debugging messages may be printed. + */ +static spif_bool_t silent = FALSE; + +/** * Sets the program name. * * This function is provided for safe and sane setting of the @@ -119,6 +127,22 @@ libast_set_program_version(const char *progversion) } /** + * Sets silent mode. + * + * This function turns on/off error, warning, and debugging output. + * + * @param flag Boolean value to set silent flag + * @return The new value + * + * @see silent + */ +spif_bool_t +libast_set_silent(spif_bool_t flag) +{ + return (silent = flag); +} + +/** * Prints debugging output. * * This function is the guts behing the D_*() and DPRINTF() families of @@ -140,6 +164,7 @@ libast_dprintf(const char *format, ...) int n; ASSERT_RVAL(!SPIF_PTR_ISNULL(format), SPIF_CAST_C(int) -1); + REQUIRE_RVAL(!silent, 0); REQUIRE_RVAL(libast_program_name != NULL, 0); va_start(args, format); n = vfprintf(LIBAST_DEBUG_FD, format, args); @@ -168,6 +193,7 @@ libast_print_error(const char *fmt, ...) va_list arg_ptr; ASSERT(!SPIF_PTR_ISNULL(fmt)); + REQUIRE(!silent); REQUIRE(libast_program_name != NULL); va_start(arg_ptr, fmt); fprintf(stderr, "%s: Error: ", libast_program_name); @@ -195,6 +221,7 @@ libast_print_warning(const char *fmt, ...) va_list arg_ptr; ASSERT(!SPIF_PTR_ISNULL(fmt)); + REQUIRE(!silent); REQUIRE(libast_program_name != NULL); va_start(arg_ptr, fmt); fprintf(stderr, "%s: Warning: ", libast_program_name); @@ -222,7 +249,7 @@ libast_fatal_error(const char *fmt, ...) va_list arg_ptr; ASSERT(!SPIF_PTR_ISNULL(fmt)); - if (libast_program_name != NULL) { + if ((!silent) && (libast_program_name != NULL)) { va_start(arg_ptr, fmt); fprintf(stderr, "%s: FATAL: ", libast_program_name); vfprintf(stderr, fmt, arg_ptr); @@ -230,3 +257,4 @@ libast_fatal_error(const char *fmt, ...) } exit(-1); } + diff --git a/src/pthreads.c b/src/pthreads.c new file mode 100644 index 0000000..5cebf09 --- /dev/null +++ b/src/pthreads.c @@ -0,0 +1,733 @@ +/* + * Copyright (C) 1997-2004, Michael Jennings + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software, its documentation and marketing & publicity + * materials, and acknowledgment shall be given in the documentation, materials + * and software packages that this Software was used. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +static const char __attribute__((unused)) cvs_ident[] = "$Id$"; + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <libast_internal.h> +#include <pthread.h> + +/* *INDENT-OFF* */ +static SPIF_CONST_TYPE(threadclass) pt_class = { + { + SPIF_DECL_CLASSNAME(pthreads), + (spif_func_t) spif_pthreads_new, + (spif_func_t) spif_pthreads_init, + (spif_func_t) spif_pthreads_done, + (spif_func_t) spif_pthreads_del, + (spif_func_t) spif_pthreads_show, + (spif_func_t) spif_pthreads_comp, + (spif_func_t) spif_pthreads_dup, + (spif_func_t) spif_pthreads_type + }, + (spif_func_t) spif_pthreads_new_with_func, + (spif_func_t) spif_pthreads_init_with_func, + (spif_func_t) spif_pthreads_detach, + (spif_func_t) spif_pthreads_get_condition, + (spif_func_t) spif_pthreads_get_mutex, + (spif_func_t) spif_pthreads_kill, + (spif_func_t) spif_pthreads_run, + (spif_func_t) spif_pthreads_tls_calloc, + (spif_func_t) spif_pthreads_tls_free, + (spif_func_t) spif_pthreads_tls_get, + (spif_func_t) spif_pthreads_tls_malloc, + (spif_func_t) spif_pthreads_tls_realloc, + (spif_func_t) spif_pthreads_wait, + (spif_func_t) spif_pthreads_wait_for +}; +SPIF_TYPE(class) SPIF_CLASS_VAR(pthreads) = SPIF_CAST(class) &pt_class; +SPIF_TYPE(threadclass) SPIF_THREADCLASS_VAR(pthreads) = &pt_class; + +static SPIF_CONST_TYPE(mutexclass) ptm_class = { + { + SPIF_DECL_CLASSNAME(pthreads_mutex), + (spif_func_t) spif_pthreads_mutex_new, + (spif_func_t) spif_pthreads_mutex_init, + (spif_func_t) spif_pthreads_mutex_done, + (spif_func_t) spif_pthreads_mutex_del, + (spif_func_t) spif_pthreads_mutex_show, + (spif_func_t) spif_pthreads_mutex_comp, + (spif_func_t) spif_pthreads_mutex_dup, + (spif_func_t) spif_pthreads_mutex_type + }, + (spif_func_t) spif_pthreads_mutex_lock, + (spif_func_t) spif_pthreads_mutex_lock_nowait, + (spif_func_t) spif_pthreads_mutex_unlock +}; +SPIF_TYPE(class) SPIF_CLASS_VAR(pthreads_mutex) = SPIF_CAST(class) &ptm_class; +SPIF_TYPE(mutexclass) SPIF_MUTEXCLASS_VAR(pthreads_mutex) = &ptm_class; + +static SPIF_CONST_TYPE(conditionclass) ptc_class = { + { + SPIF_DECL_CLASSNAME(pthreads_condition), + (spif_func_t) spif_pthreads_condition_new, + (spif_func_t) spif_pthreads_condition_init, + (spif_func_t) spif_pthreads_condition_done, + (spif_func_t) spif_pthreads_condition_del, + (spif_func_t) spif_pthreads_condition_show, + (spif_func_t) spif_pthreads_condition_comp, + (spif_func_t) spif_pthreads_condition_dup, + (spif_func_t) spif_pthreads_condition_type + }, + (spif_func_t) spif_pthreads_condition_broadcast, + (spif_func_t) spif_pthreads_condition_signal, + (spif_func_t) spif_pthreads_condition_wait, + (spif_func_t) spif_pthreads_condition_wait_timed +}; +SPIF_TYPE(class) SPIF_CLASS_VAR(pthreads_condition) = SPIF_CAST(class) &ptc_class; +SPIF_TYPE(conditionclass) SPIF_CONDITIONCLASS_VAR(pthreads_condition) = &ptc_class; +/* *INDENT-ON* */ + +static void spif_pthreads_tls_destructor(void *ptr); + +spif_pthreads_t +spif_pthreads_new(void) +{ + spif_pthreads_t self; + + self = SPIF_ALLOC(pthreads); + if (!spif_pthreads_init(self)) { + SPIF_DEALLOC(self); + self = SPIF_NULL_TYPE(pthreads); + } + return self; +} + +spif_pthreads_t +spif_pthreads_new_with_func(spif_thread_func_t func, spif_thread_data_t data) +{ + spif_pthreads_t self; + + self = SPIF_ALLOC(pthreads); + if (!spif_pthreads_init_with_func(self, func, data)) { + SPIF_DEALLOC(self); + self = SPIF_NULL_TYPE(pthreads); + } + return self; +} + +spif_bool_t +spif_pthreads_init(spif_pthreads_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + /* ***NOT NEEDED*** spif_obj_init(SPIF_OBJ(self)); */ + spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS(SPIF_THREADCLASS_VAR(pthreads))); + self->handle = (pthread_t) 0; + self->creator = (pthread_t) 0; + pthread_attr_init(&self->attr); + self->main_func = SPIF_NULL_TYPE(thread_func); + self->data = SPIF_NULL_TYPE(thread_data); + self->tls_keys = SPIF_NULL_TYPE(list); + return TRUE; +} + +spif_bool_t +spif_pthreads_init_with_func(spif_pthreads_t self, spif_thread_func_t func, spif_thread_data_t data) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + /* ***NOT NEEDED*** spif_obj_init(SPIF_OBJ(self)); */ + spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS(SPIF_THREADCLASS_VAR(pthreads))); + self->handle = (pthread_t) 0; + self->creator = (pthread_t) 0; + pthread_attr_init(&self->attr); + self->main_func = func; + self->data = data; + self->tls_keys = SPIF_NULL_TYPE(list); + return TRUE; +} + +spif_bool_t +spif_pthreads_done(spif_pthreads_t self) +{ + spif_bool_t ret = TRUE; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + if (self->handle) { + spif_pthreads_kill(self, SIGTERM); + self->handle = (pthread_t) 0; + } + self->creator = pthread_self(); + pthread_attr_destroy(&self->attr); + pthread_attr_init(&self->attr); + self->main_func = SPIF_NULL_TYPE(thread_func); + if (self->tls_keys) { + SPIF_LIST_DEL(self->tls_keys); + self->tls_keys = SPIF_NULL_TYPE(list); + } + return ret; +} + +spif_bool_t +spif_pthreads_del(spif_pthreads_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + spif_pthreads_done(self); + SPIF_DEALLOC(self); + return TRUE; +} + +spif_str_t +spif_pthreads_show(spif_pthreads_t self, spif_charptr_t name, spif_str_t buff, size_t indent) +{ + spif_char_t tmp[4096]; + + if (SPIF_PTHREADS_ISNULL(self)) { + SPIF_OBJ_SHOW_NULL(pthreads, name, buff, indent, tmp); + return buff; + } + + memset(tmp, ' ', indent); + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, + "(spif_pthreads_t) %s: %10p { \"", + name, SPIF_CAST(ptr) self); + if (SPIF_STR_ISNULL(buff)) { + buff = spif_str_new_from_ptr(tmp); + } else { + spif_str_append_from_ptr(buff, tmp); + } + + indent += 2; + if (indent < sizeof(tmp)) { + memset(tmp, ' ', indent); + } + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(pthread_t) handle: %ld\n", (long) self->handle); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(pthread_t) creator: %ld\n", (long) self->creator); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(pthread_attr_t) attr: %10p {...}\n", &self->attr); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_thread_func_t) main_func: %10p\n", self->main_func); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(spif_thread_data_t) data: %10p\n", self->data); + spif_str_append_from_ptr(buff, tmp); + + if (SPIF_LIST_ISNULL(self->tls_keys)) { + SPIF_OBJ_SHOW_NULL(list, "tls_keys", buff, indent, tmp); + } else { + buff = SPIF_LIST_SHOW(self->tls_keys, buff, indent); + } + + snprintf(SPIF_CHARPTR_C(tmp), sizeof(tmp), "}\n"); + spif_str_append_from_ptr(buff, tmp); + return buff; +} + +spif_cmp_t +spif_pthreads_comp(spif_pthreads_t self, spif_pthreads_t other) +{ + SPIF_OBJ_COMP_CHECK_NULL(self, other); + if (pthread_equal(self->handle, other->handle)) { + return SPIF_CMP_EQUAL; + } + return SPIF_OBJ_COMP(self, other); +} + +spif_pthreads_t +spif_pthreads_dup(spif_pthreads_t self) +{ + spif_pthreads_t tmp; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), SPIF_NULL_TYPE(pthreads)); + tmp = SPIF_ALLOC(pthreads); + memcpy(tmp, self, SPIF_SIZEOF_TYPE(pthreads)); + tmp->tls_keys = SPIF_LIST_DUP(self->tls_keys); + return tmp; +} + +spif_classname_t +spif_pthreads_type(spif_pthreads_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), SPIF_CAST(classname) SPIF_NULLSTR_TYPE(classname)); + return SPIF_OBJ_CLASSNAME(self); +} + +spif_bool_t +spif_pthreads_detach(spif_pthreads_t self) +{ + int ret; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + REQUIRE_RVAL(self->handle, FALSE); + + ret = pthread_detach(self->handle); + if (!ret) { + return TRUE; + } else if ((errno == EINVAL) || (ret == EINVAL)) { + return TRUE; + } else { + return FALSE; + } +} + +spif_condition_t +spif_pthreads_get_condition(spif_pthreads_t self) +{ + spif_pthreads_condition_t cond; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), SPIF_NULL_TYPE(condition)); + cond = spif_pthreads_condition_new(); + spif_pthreads_mutex_set_creator(SPIF_PTHREADS_MUTEX(cond), SPIF_THREAD(self)); + return SPIF_CAST(condition) cond; +} + +spif_mutex_t +spif_pthreads_get_mutex(spif_pthreads_t self) +{ + spif_pthreads_mutex_t mutex; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), SPIF_NULL_TYPE(mutex)); + mutex = spif_pthreads_mutex_new(); + spif_pthreads_mutex_set_creator(mutex, SPIF_THREAD(self)); + return SPIF_CAST(mutex) mutex; +} + +spif_bool_t +spif_pthreads_kill(spif_pthreads_t self, int sig) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + REQUIRE_RVAL(self->handle, FALSE); + + return ((pthread_kill(self->handle, sig)) ? (FALSE) : (TRUE)); +} + +spif_bool_t +spif_pthreads_run(spif_pthreads_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + REQUIRE_RVAL(self->main_func != SPIF_NULL_TYPE(thread_func), FALSE); + REQUIRE_RVAL(self->handle == (pthread_t) 0, FALSE); + + if (!pthread_create(&self->handle, &self->attr, self->main_func, self)) { + return TRUE; + } else { + return FALSE; + } +} + +spif_tls_handle_t +spif_pthreads_tls_calloc(spif_pthreads_t self, size_t count, size_t size) +{ + spif_tls_handle_t handle; + spif_ptr_t data; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), SPIF_CAST(tls_handle) -1); + REQUIRE_RVAL(count, SPIF_CAST(tls_handle) -1); + REQUIRE_RVAL(size, SPIF_CAST(tls_handle) -1); + + handle = spif_pthreads_tls_malloc(self, count * size); + data = spif_pthreads_tls_get(self, handle); + MEMSET(data, 0, count * size); + return handle; +} + +spif_bool_t +spif_pthreads_tls_free(spif_pthreads_t self, spif_tls_handle_t handle) +{ + spif_mbuff_t key; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + REQUIRE_RVAL(!SPIF_LIST_ISNULL(self->tls_keys), FALSE); + + key = SPIF_CAST(mbuff) SPIF_LIST_REMOVE_AT(self->tls_keys, handle); + if (handle != SPIF_LIST_COUNT(self->tls_keys)) { + /* If this isn't the last item in our list, insert an empty placeholder + so that numeric indexes which have already been given out will remain valid. */ + SPIF_LIST_INSERT_AT(self->tls_keys, SPIF_NULL_TYPE(obj), handle); + } + if (!SPIF_MBUFF_ISNULL(key)) { + spif_ptr_t ptr; + + ptr = SPIF_CAST(ptr) pthread_getspecific(*((pthread_key_t *) SPIF_MBUFF_BUFF(key))); + if (ptr && !pthread_key_delete(*((pthread_key_t *) SPIF_MBUFF_BUFF(key)))) { + FREE(ptr); + return TRUE; + } + } + return FALSE; +} + +spif_ptr_t +spif_pthreads_tls_get(spif_pthreads_t self, spif_tls_handle_t handle) +{ + spif_mbuff_t key; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), SPIF_NULL_TYPE(ptr)); + REQUIRE_RVAL(!SPIF_LIST_ISNULL(self->tls_keys), FALSE); + + key = SPIF_CAST(mbuff) SPIF_LIST_GET(self->tls_keys, handle); + if (!SPIF_MBUFF_ISNULL(key)) { + return (SPIF_CAST(ptr) pthread_getspecific(*((pthread_key_t *) SPIF_MBUFF_BUFF(key)))); + } + return SPIF_NULL_TYPE(ptr); +} + +spif_tls_handle_t +spif_pthreads_tls_malloc(spif_pthreads_t self, spif_memidx_t size) +{ + pthread_key_t key; + spif_ptr_t ptr; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), SPIF_CAST(tls_handle) -1); + + if (SPIF_LIST_ISNULL(self->tls_keys)) { + self->tls_keys = SPIF_LIST_NEW(array); + } + ptr = MALLOC(size); + + if (!pthread_key_create(&key, spif_pthreads_tls_destructor)) { + spif_mbuff_t tls_key; + + /* Store the pointer. */ + pthread_setspecific(key, ptr); + + /* The key is actually copied into the mbuff buffer. */ + tls_key = spif_mbuff_new_from_ptr(SPIF_CAST(byteptr) &key, sizeof(pthread_key_t)); + + /* Since the pointer returned by MALLOC() is only stored locally in + this function and as keyed in the TLS data store, it cannot be + retrieved by any other thread. Thus, thread-local storage. Yay! */ + if (SPIF_LIST_APPEND(self->tls_keys, tls_key)) { + return (SPIF_LIST_COUNT(self->tls_keys) - 1); + } + } + return (SPIF_CAST(tls_handle) -1); +} + +static void +spif_pthreads_tls_destructor(void *ptr) +{ + FREE(ptr); +} + +spif_bool_t +spif_pthreads_tls_realloc(spif_pthreads_t self, spif_tls_handle_t handle, spif_memidx_t size) +{ + spif_mbuff_t key; + + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + REQUIRE_RVAL(!SPIF_LIST_ISNULL(self->tls_keys), FALSE); + + key = SPIF_CAST(mbuff) SPIF_LIST_GET(self->tls_keys, handle); + if (!SPIF_MBUFF_ISNULL(key)) { + spif_ptr_t ptr; + + ptr = SPIF_CAST(ptr) pthread_getspecific(*((pthread_key_t *) SPIF_MBUFF_BUFF(key))); + if (ptr) { + ptr = REALLOC(ptr, size); + if (ptr) { + pthread_setspecific(*((pthread_key_t *) SPIF_MBUFF_BUFF(key)), ptr); + return TRUE; + } + } + } + return FALSE; +} + +spif_bool_t +spif_pthreads_wait(spif_pthreads_t self, spif_condition_t cond) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + ASSERT_RVAL(!SPIF_PTHREADS_CONDITION_ISNULL(SPIF_PTHREADS_CONDITION(cond)), FALSE); +} + +spif_bool_t +spif_pthreads_wait_for(spif_pthreads_t self, spif_pthreads_t other) +{ + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(self), FALSE); + ASSERT_RVAL(!SPIF_PTHREADS_ISNULL(other), FALSE); +} + +SPIF_DEFINE_PROPERTY_FUNC_C(pthreads, pthread_t, handle); +SPIF_DEFINE_PROPERTY_FUNC_C(pthreads, pthread_t, creator); +SPIF_DEFINE_PROPERTY_FUNC_C(pthreads, pthread_attr_t, attr); +SPIF_DEFINE_PROPERTY_FUNC(pthreads, thread_func, main_func); +SPIF_DEFINE_PROPERTY_FUNC(pthreads, thread_data, data); +SPIF_DEFINE_PROPERTY_FUNC(pthreads, list, tls_keys); + +spif_pthreads_mutex_t +spif_pthreads_mutex_new(void) +{ + spif_pthreads_mutex_t self; + + self = SPIF_ALLOC(pthreads_mutex); + if (!spif_pthreads_mutex_init(self)) { + SPIF_DEALLOC(self); + self = SPIF_NULL_TYPE(pthreads_mutex); + } + return self; +} + +spif_bool_t +spif_pthreads_mutex_init(spif_pthreads_mutex_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_MUTEX_ISNULL(self), FALSE); + /* ***NOT NEEDED*** spif_obj_init(SPIF_OBJ(self)); */ + spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS(SPIF_MUTEXCLASS_VAR(pthreads_mutex))); + self->creator = SPIF_NULL_TYPE(thread); + pthread_mutex_init(&self->mutex, NULL); + return TRUE; +} + +spif_bool_t +spif_pthreads_mutex_done(spif_pthreads_mutex_t self) +{ + spif_bool_t ret = TRUE; + + ASSERT_RVAL(!SPIF_PTHREADS_MUTEX_ISNULL(self), FALSE); + self->creator = SPIF_NULL_TYPE(thread); + if (!pthread_mutex_unlock(&self->mutex)) { + /* It was locked. Destroy it. */ + pthread_mutex_destroy(&self->mutex); + } + pthread_mutex_init(&self->mutex, NULL); + return ret; +} + +spif_bool_t +spif_pthreads_mutex_del(spif_pthreads_mutex_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_MUTEX_ISNULL(self), FALSE); + spif_pthreads_mutex_done(self); + SPIF_DEALLOC(self); + return TRUE; +} + +spif_str_t +spif_pthreads_mutex_show(spif_pthreads_mutex_t self, spif_charptr_t name, spif_str_t buff, size_t indent) +{ + spif_char_t tmp[4096]; + + if (SPIF_PTHREADS_MUTEX_ISNULL(self)) { + SPIF_OBJ_SHOW_NULL(pthreads_mutex, name, buff, indent, tmp); + return buff; + } + + memset(tmp, ' ', indent); + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, + "(spif_pthreads_mutex_t) %s: %10p { \"", + name, SPIF_CAST(ptr) self); + if (SPIF_STR_ISNULL(buff)) { + buff = spif_str_new_from_ptr(tmp); + } else { + spif_str_append_from_ptr(buff, tmp); + } + + indent += 2; + if (indent < sizeof(tmp)) { + memset(tmp, ' ', indent); + } + if (SPIF_THREAD_ISNULL(self->creator)) { + SPIF_OBJ_SHOW_NULL(thread, "creator", buff, indent, tmp); + } else { + buff = SPIF_THREAD_SHOW(self->creator, buff, indent); + } + + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(pthread_mutex_t) mutex: %10p {...}\n", &self->mutex); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp), sizeof(tmp), "}\n"); + spif_str_append_from_ptr(buff, tmp); + return buff; +} + +spif_cmp_t +spif_pthreads_mutex_comp(spif_pthreads_mutex_t self, spif_pthreads_mutex_t other) +{ + SPIF_OBJ_COMP_CHECK_NULL(self, other); + return SPIF_OBJ_COMP(self, other); +} + +spif_pthreads_mutex_t +spif_pthreads_mutex_dup(spif_pthreads_mutex_t self) +{ + spif_pthreads_mutex_t tmp; + + ASSERT_RVAL(!SPIF_PTHREADS_MUTEX_ISNULL(self), SPIF_NULL_TYPE(pthreads_mutex)); + tmp = SPIF_ALLOC(pthreads_mutex); + memcpy(tmp, self, SPIF_SIZEOF_TYPE(pthreads_mutex)); + return tmp; +} + +spif_classname_t +spif_pthreads_mutex_type(spif_pthreads_mutex_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_MUTEX_ISNULL(self), SPIF_CAST(classname) SPIF_NULLSTR_TYPE(classname)); + return SPIF_OBJ_CLASSNAME(self); +} + +spif_bool_t +spif_pthreads_mutex_lock(spif_pthreads_mutex_t self) +{ + +} + +spif_bool_t +spif_pthreads_mutex_lock_nowait(spif_pthreads_mutex_t self) +{ + +} + +spif_bool_t +spif_pthreads_mutex_unlock(spif_pthreads_mutex_t self) +{ + +} + +SPIF_DEFINE_PROPERTY_FUNC(pthreads_mutex, thread, creator); +SPIF_DEFINE_PROPERTY_FUNC_C(pthreads_mutex, pthread_mutex_t, mutex); + +spif_pthreads_condition_t +spif_pthreads_condition_new(void) +{ + spif_pthreads_condition_t self; + + self = SPIF_ALLOC(pthreads_condition); + if (!spif_pthreads_condition_init(self)) { + SPIF_DEALLOC(self); + self = SPIF_NULL_TYPE(pthreads_condition); + } + return self; +} + +spif_bool_t +spif_pthreads_condition_init(spif_pthreads_condition_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_CONDITION_ISNULL(self), FALSE); + spif_pthreads_mutex_init(SPIF_PTHREADS_MUTEX(self)); + spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS(SPIF_CONDITIONCLASS_VAR(pthreads_condition))); + pthread_cond_init(&self->cond, NULL); + return TRUE; +} + +spif_bool_t +spif_pthreads_condition_done(spif_pthreads_condition_t self) +{ + spif_bool_t ret = TRUE; + + ASSERT_RVAL(!SPIF_PTHREADS_CONDITION_ISNULL(self), FALSE); + ret = spif_pthreads_mutex_done(SPIF_PTHREADS_MUTEX(self)); + pthread_cond_destroy(&self->cond); + pthread_cond_init(&self->cond, NULL); + return ret; +} + +spif_bool_t +spif_pthreads_condition_del(spif_pthreads_condition_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_CONDITION_ISNULL(self), FALSE); + spif_pthreads_condition_done(self); + SPIF_DEALLOC(self); + return TRUE; +} + +spif_str_t +spif_pthreads_condition_show(spif_pthreads_condition_t self, spif_charptr_t name, spif_str_t buff, size_t indent) +{ + spif_char_t tmp[4096]; + + if (SPIF_PTHREADS_CONDITION_ISNULL(self)) { + SPIF_OBJ_SHOW_NULL(pthreads_condition, name, buff, indent, tmp); + return buff; + } + + memset(tmp, ' ', indent); + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, + "(spif_pthreads_condition_t) %s: %10p { \"", + name, SPIF_CAST(ptr) self); + if (SPIF_STR_ISNULL(buff)) { + buff = spif_str_new_from_ptr(tmp); + } else { + spif_str_append_from_ptr(buff, tmp); + } + + indent += 2; + if (indent < sizeof(tmp)) { + memset(tmp, ' ', indent); + } + buff = spif_pthreads_mutex_show(SPIF_PTHREADS_MUTEX(self), "self", buff, indent); + + snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "(pthread_cond_t) cond: %10p {...}\n", &self->cond); + spif_str_append_from_ptr(buff, tmp); + + snprintf(SPIF_CHARPTR_C(tmp), sizeof(tmp), "}\n"); + spif_str_append_from_ptr(buff, tmp); + return buff; +} + +spif_cmp_t +spif_pthreads_condition_comp(spif_pthreads_condition_t self, spif_pthreads_condition_t other) +{ + SPIF_OBJ_COMP_CHECK_NULL(self, other); + return SPIF_OBJ_COMP(self, other); +} + +spif_pthreads_condition_t +spif_pthreads_condition_dup(spif_pthreads_condition_t self) +{ + spif_pthreads_condition_t tmp; + + ASSERT_RVAL(!SPIF_PTHREADS_CONDITION_ISNULL(self), SPIF_NULL_TYPE(pthreads_condition)); + tmp = SPIF_ALLOC(pthreads_condition); + memcpy(tmp, self, SPIF_SIZEOF_TYPE(pthreads_condition)); + return tmp; +} + +spif_classname_t +spif_pthreads_condition_type(spif_pthreads_condition_t self) +{ + ASSERT_RVAL(!SPIF_PTHREADS_CONDITION_ISNULL(self), SPIF_CAST(classname) SPIF_NULLSTR_TYPE(classname)); + return SPIF_OBJ_CLASSNAME(self); +} + +spif_bool_t +spif_pthreads_condition_broadcast(spif_pthreads_condition_t self) +{ + +} + +spif_bool_t +spif_pthreads_condition_signal(spif_pthreads_condition_t self) +{ + +} + +spif_bool_t +spif_pthreads_condition_wait(spif_pthreads_condition_t self) +{ + +} + +spif_bool_t +spif_pthreads_condition_wait_timed(spif_pthreads_condition_t self, spif_int32_t delay) +{ + +} + +SPIF_DEFINE_PROPERTY_FUNC_C(pthreads_condition, pthread_cond_t, cond); diff --git a/test/test.c b/test/test.c index 2fa1764..57658d9 100644 --- a/test/test.c +++ b/test/test.c @@ -35,7 +35,6 @@ unsigned short tnum = 0; int test_macros(void); int test_mem(void); int test_strings(void); -int test_hash_functions(void); int test_snprintf(void); int test_options(void); int test_obj(void); @@ -49,6 +48,8 @@ int test_vector(void); int test_map(void); int test_socket(void); int test_regexp(void); +int test_module(void); +int test_hash_functions(void); int test_macros(void) @@ -263,227 +264,6 @@ test_strings(void) return 0; } -#define MAXPAIR 80 -#define MAXLEN 80 -int -test_hash_functions(void) -{ - spif_uint8_t i; - - for (i = 0; i < 6; i++) { - spifhash_func_t hash_func; - spif_uint32_t key_length, input_byte, trials, successes = 0, hash, ref_hash; - spif_uint8_t buff[MAXLEN + 20], *pbuff; - spif_uint8_t align1[] = "This has got the amazing aroma of bovine fecal matter..."; - spif_uint8_t align2[] = "xThis has got the amazing aroma of bovine fecal matter..."; - spif_uint8_t align3[] = "xxThis has got the amazing aroma of bovine fecal matter..."; - spif_uint8_t align4[] = "xxxThis has got the amazing aroma of bovine fecal matter..."; - spif_uint32_t j; - - if (i == 0) { - TEST_NOTICE("*** Testing Jenkins hash:"); - hash_func = spifhash_jenkins; - } else if (i == 1) { - TEST_NOTICE("*** Testing Jenkins32 hash:"); - hash_func = spifhash_jenkins32; - } else if (i == 2) { -#if WORDS_BIGENDIAN - continue; -#else - TEST_NOTICE("*** Testing JenkinsLE hash:"); - hash_func = spifhash_jenkinsLE; -#endif - } else if (i == 3) { - TEST_NOTICE("*** Testing rotating hash:"); - hash_func = spifhash_rotating; - } else if (i == 4) { - TEST_NOTICE("*** Testing one-at-a-time hash:"); - hash_func = spifhash_one_at_a_time; - } else if (i == 5) { - TEST_NOTICE("*** Testing FNV hash:"); - hash_func = spifhash_fnv; - } - - TEST_BEGIN("effect of every input bit on every output bit"); - for (key_length = 0; key_length < MAXLEN; key_length++) { - /* For each key length up to 70 bytes... */ - - if ((hash_func == spifhash_jenkins32) - && (key_length % 4)) { - successes++; - continue; - } - trials = 0; - for (input_byte = 0; input_byte < key_length; input_byte++) { - /* ...for each input byte... */ - spif_uint32_t input_bit; - - for (input_bit = 0; input_bit < 8; input_bit++) { - /* ...for each input bit... */ - spif_uint32_t seed; - - for (seed = 1; seed < 8; seed++) { - /* ...use several possible seeds... */ - spif_uint32_t e, f, g, h, x, y; - spif_uint32_t bit_pair; - - /* Initialize to ~0 (0xffffffff). */ - e = f = g = h = x = y = ~(SPIF_CAST(uint32) 0); - - /* ...to make sure every output bit is affected by every input bit. */ - for (bit_pair = 0; bit_pair < MAXPAIR; bit_pair += 2) { - spif_uint8_t buff1[MAXLEN + 1], buff2[MAXLEN + 2]; - spif_uint8_t *pbuff1 = &buff1[0], *pbuff2 = &buff2[1]; - spif_uint32_t hash1, hash2; - - for (j = 0; j < key_length + 1; j++) { - /* Initialize keys to all zeros. */ - pbuff1[j] = pbuff2[j] = (spif_uint8_t) 0; - } - - /* Then make them differ by exactly one bit, the input_bit. - bit_pair will always end in 0, so bit_pair + 1 will always - end in 1. It's then shifted by input_bit to test the - current bit to test all 8 of the lowest bits in sequence. */ - pbuff1[input_byte] ^= (bit_pair << input_bit); - pbuff1[input_byte] ^= (bit_pair >> (8 - input_bit)); - pbuff2[input_byte] ^= ((bit_pair + 1) << input_bit); - pbuff2[input_byte] ^= ((bit_pair + 1) >> (8 - input_bit)); - - /* Hash them. */ - if (hash_func == spifhash_jenkins32) { - hash1 = hash_func(pbuff1, key_length / 4, seed); - hash2 = hash_func(pbuff2, key_length / 4, seed); - } else { - hash1 = hash_func(pbuff1, key_length, seed); - hash2 = hash_func(pbuff2, key_length, seed); - } - - /* Make sure every bit is 1 or 0 at least once. */ - e &= (hash1 ^ hash2); f &= ~(hash1 ^ hash2); - g &= hash1; h &= ~hash1; - x &= hash2; y &= ~hash2; - if (!(e | f | g | h | x | y)) { - /* They're all 0. That means they've all changed at least once. */ - break; - } - } - if (bit_pair > trials) { - trials = bit_pair; - } - if (bit_pair == MAXPAIR) { -#if UNUSED_BLOCK - printf("Some bit didn't change: "); - printf("%.8lx %.8lx %.8lx %.8lx %.8lx %.8lx ", - SPIF_CAST_C(unsigned long) e, - SPIF_CAST_C(unsigned long) f, - SPIF_CAST_C(unsigned long) g, - SPIF_CAST_C(unsigned long) h, - SPIF_CAST_C(unsigned long) x, - SPIF_CAST_C(unsigned long) y); - printf("input_byte %lu input_bit %lu seed %lu key length %lu\n", - SPIF_CAST_C(unsigned long) input_byte, - SPIF_CAST_C(unsigned long) input_bit, - SPIF_CAST_C(unsigned long) seed, - SPIF_CAST_C(unsigned long) key_length); -#endif - } - if (trials == MAXPAIR) { - /* Easy way to break out of a crapload of for loops. */ - goto done; - } - } - } - } - done: - if (trials < MAXPAIR) { - successes++; -#if UNUSED_BLOCK - printf("Mix success: %2lu-byte key required %2lu trials (%lu so far).\n", - SPIF_CAST_C(unsigned long) input_byte, - SPIF_CAST_C(unsigned long) trials / 2, - SPIF_CAST_C(unsigned long) successes); -#endif - } - } - printf("%.2f%% mix success rate in %d key lengths...", - (100.0 * successes / key_length), key_length); - TEST_FAIL_IF(successes == 0); - TEST_PASS(); - - /* Make sure nothing but the key is hashed, regardless of alignment. */ - TEST_BEGIN("endian cleanliness"); - key_length = CONST_STRLEN(align1); - if (hash_func == spifhash_jenkins32) { - if (key_length % 4) { - TEST_FAIL_IF(key_length); - } else { - key_length /= 4; - } - } - ref_hash = hash_func(align1, key_length, 0); - hash = hash_func(align2 + 1, key_length, 0); - /*printf("Reference hash 0x%08x, hash 0x%08x for length %lu\n", ref_hash, hash, key_length);*/ - TEST_FAIL_IF(hash != ref_hash); - hash = hash_func(align3 + 2, key_length, 0); - TEST_FAIL_IF(hash != ref_hash); - hash = hash_func(align4 + 3, key_length, 0); - TEST_FAIL_IF(hash != ref_hash); - - for (j = 0, pbuff = buff + 1; j < 8; j++, pbuff++) { - for (key_length = 0; key_length < MAXLEN; key_length++) { - if ((hash_func == spifhash_jenkins32) - && (key_length % 4)) { - continue; - } - MEMSET(buff, 0, sizeof(buff)); - - if (hash_func == spifhash_jenkins32) { - ref_hash = hash_func(pbuff, key_length / 4, 1); - } else { - ref_hash = hash_func(pbuff, key_length, 1); - } - *(pbuff + key_length) = ~(SPIF_CAST(uint8) 0); - *(pbuff - 1) = ~(SPIF_CAST(uint8) 0); - if (hash_func == spifhash_jenkins32) { - hash = hash_func(pbuff, key_length / 4, 1); - } else { - hash = hash_func(pbuff, key_length, 1); - } - /*printf("Reference hash 0x%08x, hash 0x%08x for length %lu\n", ref_hash, hash, key_length);*/ - TEST_FAIL_IF(hash != ref_hash); - } - } - TEST_PASS(); - - /* We cannot test the rotating hash or the FNV hash here. The - rotating hash repeats after 4 zero-length keys. The FNV - hash generates constant hash values for zero-length keys. */ - if ((hash_func != spifhash_rotating) - && (hash_func != spifhash_fnv)) { - spif_uint32_t null_hashes[8]; - spif_uint8_t one_byte; - - TEST_BEGIN("hashes of empty strings"); - one_byte = ~0; - for (j = 0, hash = 0; j < 8; j++) { - spif_uint32_t k; - - hash = hash_func(&one_byte, SPIF_CAST(uint32) 0, hash); - null_hashes[j] = hash; - /*printf("Empty string hash %lu is 0x%08x\n", j, hash);*/ - for (k = j - 1; k < 8; k--) { - TEST_FAIL_IF(null_hashes[j] == null_hashes[k]); - } - } - TEST_PASS(); - } - } - - TEST_PASSED("hash functions"); - return 0; -} - int test_snprintf(void) { @@ -2715,6 +2495,269 @@ test_regexp(void) } int +test_module(void) +{ + spif_module_t test_mod; + double (*dynamic_tan)(double); + + TEST_BEGIN("spif_module_new() function"); + test_mod = spif_module_new(); + TEST_FAIL_IF(SPIF_MODULE_ISNULL(test_mod)); + TEST_PASS(); + + TEST_BEGIN("spif_module_t properties"); + spif_module_set_name(test_mod, spif_str_new_from_ptr("math library")); + spif_module_set_path(test_mod, spif_str_new_from_ptr("libm.so")); + TEST_FAIL_IF(!SPIF_CMP_IS_EQUAL(spif_str_cmp_with_ptr(test_mod->name, "math library"))); + TEST_FAIL_IF(!SPIF_CMP_IS_EQUAL(spif_str_cmp_with_ptr(test_mod->path, "libm.so"))); + TEST_PASS(); + + libast_set_silent(1); + TEST_BEGIN("spif_module_load() function"); + TEST_FAIL_IF(!spif_module_load(test_mod)); + TEST_PASS(); + + TEST_BEGIN("spif_module_getsym() function"); + TEST_FAIL_IF(SPIF_PTR_ISNULL(spif_module_getsym(test_mod, "sqrt"))); + TEST_FAIL_IF(!SPIF_PTR_ISNULL(spif_module_getsym(test_mod, "lkjweoiclkjsdfoihwerbglkjsdf"))); + dynamic_tan = spif_module_getsym(test_mod, "tan"); + TEST_FAIL_IF((*dynamic_tan)(2.0) - tan(2.0) > 0.00001); + TEST_PASS(); + + TEST_BEGIN("spif_module_call() function"); + TEST_PASS(); + + TEST_BEGIN("spif_module_unload() function"); + TEST_FAIL_IF(!spif_module_unload(test_mod)); + TEST_PASS(); + libast_set_silent(0); + + TEST_PASSED("spif_module_t"); + return 0; +} + +#define MAXPAIR 80 +#define MAXLEN 80 +int +test_hash_functions(void) +{ + spif_uint8_t i; + + for (i = 0; i < 6; i++) { + spifhash_func_t hash_func; + spif_uint32_t key_length, input_byte, trials, successes = 0, hash, ref_hash; + spif_uint8_t buff[MAXLEN + 20], *pbuff; + spif_uint8_t align1[] = "This has got the amazing aroma of bovine fecal matter..."; + spif_uint8_t align2[] = "xThis has got the amazing aroma of bovine fecal matter..."; + spif_uint8_t align3[] = "xxThis has got the amazing aroma of bovine fecal matter..."; + spif_uint8_t align4[] = "xxxThis has got the amazing aroma of bovine fecal matter..."; + spif_uint32_t j; + + if (i == 0) { + TEST_NOTICE("*** Testing Jenkins hash:"); + hash_func = spifhash_jenkins; + } else if (i == 1) { + TEST_NOTICE("*** Testing Jenkins32 hash:"); + hash_func = spifhash_jenkins32; + } else if (i == 2) { +#if WORDS_BIGENDIAN + continue; +#else + TEST_NOTICE("*** Testing JenkinsLE hash:"); + hash_func = spifhash_jenkinsLE; +#endif + } else if (i == 3) { + TEST_NOTICE("*** Testing rotating hash:"); + hash_func = spifhash_rotating; + } else if (i == 4) { + TEST_NOTICE("*** Testing one-at-a-time hash:"); + hash_func = spifhash_one_at_a_time; + } else if (i == 5) { + TEST_NOTICE("*** Testing FNV hash:"); + hash_func = spifhash_fnv; + } + + TEST_BEGIN("effect of every input bit on every output bit"); + for (key_length = 0; key_length < MAXLEN; key_length++) { + /* For each key length up to 70 bytes... */ + + if ((hash_func == spifhash_jenkins32) + && (key_length % 4)) { + successes++; + continue; + } + trials = 0; + for (input_byte = 0; input_byte < key_length; input_byte++) { + /* ...for each input byte... */ + spif_uint32_t input_bit; + + for (input_bit = 0; input_bit < 8; input_bit++) { + /* ...for each input bit... */ + spif_uint32_t seed; + + for (seed = 1; seed < 8; seed++) { + /* ...use several possible seeds... */ + spif_uint32_t e, f, g, h, x, y; + spif_uint32_t bit_pair; + + /* Initialize to ~0 (0xffffffff). */ + e = f = g = h = x = y = ~(SPIF_CAST(uint32) 0); + + /* ...to make sure every output bit is affected by every input bit. */ + for (bit_pair = 0; bit_pair < MAXPAIR; bit_pair += 2) { + spif_uint8_t buff1[MAXLEN + 1], buff2[MAXLEN + 2]; + spif_uint8_t *pbuff1 = &buff1[0], *pbuff2 = &buff2[1]; + spif_uint32_t hash1, hash2; + + for (j = 0; j < key_length + 1; j++) { + /* Initialize keys to all zeros. */ + pbuff1[j] = pbuff2[j] = (spif_uint8_t) 0; + } + + /* Then make them differ by exactly one bit, the input_bit. + bit_pair will always end in 0, so bit_pair + 1 will always + end in 1. It's then shifted by input_bit to test the + current bit to test all 8 of the lowest bits in sequence. */ + pbuff1[input_byte] ^= (bit_pair << input_bit); + pbuff1[input_byte] ^= (bit_pair >> (8 - input_bit)); + pbuff2[input_byte] ^= ((bit_pair + 1) << input_bit); + pbuff2[input_byte] ^= ((bit_pair + 1) >> (8 - input_bit)); + + /* Hash them. */ + if (hash_func == spifhash_jenkins32) { + hash1 = hash_func(pbuff1, key_length / 4, seed); + hash2 = hash_func(pbuff2, key_length / 4, seed); + } else { + hash1 = hash_func(pbuff1, key_length, seed); + hash2 = hash_func(pbuff2, key_length, seed); + } + + /* Make sure every bit is 1 or 0 at least once. */ + e &= (hash1 ^ hash2); f &= ~(hash1 ^ hash2); + g &= hash1; h &= ~hash1; + x &= hash2; y &= ~hash2; + if (!(e | f | g | h | x | y)) { + /* They're all 0. That means they've all changed at least once. */ + break; + } + } + if (bit_pair > trials) { + trials = bit_pair; + } + if (bit_pair == MAXPAIR) { +#if UNUSED_BLOCK + printf("Some bit didn't change: "); + printf("%.8lx %.8lx %.8lx %.8lx %.8lx %.8lx ", + SPIF_CAST_C(unsigned long) e, + SPIF_CAST_C(unsigned long) f, + SPIF_CAST_C(unsigned long) g, + SPIF_CAST_C(unsigned long) h, + SPIF_CAST_C(unsigned long) x, + SPIF_CAST_C(unsigned long) y); + printf("input_byte %lu input_bit %lu seed %lu key length %lu\n", + SPIF_CAST_C(unsigned long) input_byte, + SPIF_CAST_C(unsigned long) input_bit, + SPIF_CAST_C(unsigned long) seed, + SPIF_CAST_C(unsigned long) key_length); +#endif + } + if (trials == MAXPAIR) { + /* Easy way to break out of a crapload of for loops. */ + goto done; + } + } + } + } + done: + if (trials < MAXPAIR) { + successes++; +#if UNUSED_BLOCK + printf("Mix success: %2lu-byte key required %2lu trials (%lu so far).\n", + SPIF_CAST_C(unsigned long) input_byte, + SPIF_CAST_C(unsigned long) trials / 2, + SPIF_CAST_C(unsigned long) successes); +#endif + } + } + printf("%.2f%% mix success rate in %d key lengths...", + (100.0 * successes / key_length), key_length); + TEST_FAIL_IF(successes == 0); + TEST_PASS(); + + /* Make sure nothing but the key is hashed, regardless of alignment. */ + TEST_BEGIN("endian cleanliness"); + key_length = CONST_STRLEN(align1); + if (hash_func == spifhash_jenkins32) { + if (key_length % 4) { + TEST_FAIL_IF(key_length); + } else { + key_length /= 4; + } + } + ref_hash = hash_func(align1, key_length, 0); + hash = hash_func(align2 + 1, key_length, 0); + /*printf("Reference hash 0x%08x, hash 0x%08x for length %lu\n", ref_hash, hash, key_length);*/ + TEST_FAIL_IF(hash != ref_hash); + hash = hash_func(align3 + 2, key_length, 0); + TEST_FAIL_IF(hash != ref_hash); + hash = hash_func(align4 + 3, key_length, 0); + TEST_FAIL_IF(hash != ref_hash); + + for (j = 0, pbuff = buff + 1; j < 8; j++, pbuff++) { + for (key_length = 0; key_length < MAXLEN; key_length++) { + if ((hash_func == spifhash_jenkins32) + && (key_length % 4)) { + continue; + } + MEMSET(buff, 0, sizeof(buff)); + + if (hash_func == spifhash_jenkins32) { + ref_hash = hash_func(pbuff, key_length / 4, 1); + } else { + ref_hash = hash_func(pbuff, key_length, 1); + } + *(pbuff + key_length) = ~(SPIF_CAST(uint8) 0); + *(pbuff - 1) = ~(SPIF_CAST(uint8) 0); + if (hash_func == spifhash_jenkins32) { + hash = hash_func(pbuff, key_length / 4, 1); + } else { + hash = hash_func(pbuff, key_length, 1); + } + /*printf("Reference hash 0x%08x, hash 0x%08x for length %lu\n", ref_hash, hash, key_length);*/ + TEST_FAIL_IF(hash != ref_hash); + } + } + TEST_PASS(); + + /* We cannot test the rotating hash or the FNV hash here. The + rotating hash repeats after 4 zero-length keys. The FNV + hash generates constant hash values for zero-length keys. */ + if ((hash_func != spifhash_rotating) + && (hash_func != spifhash_fnv)) { + spif_uint32_t null_hashes[8]; + spif_uint8_t one_byte; + + TEST_BEGIN("hashes of empty strings"); + one_byte = ~0; + for (j = 0, hash = 0; j < 8; j++) { + spif_uint32_t k; + + hash = hash_func(&one_byte, SPIF_CAST(uint32) 0, hash); + null_hashes[j] = hash; + /*printf("Empty string hash %lu is 0x%08x\n", j, hash);*/ + for (k = j - 1; k < 8; k--) { + TEST_FAIL_IF(null_hashes[j] == null_hashes[k]); + } + } + TEST_PASS(); + } + } + + TEST_PASSED("hash functions"); + return 0; +} + +int main(int argc, char *argv[]) { int ret = 0; @@ -2772,6 +2815,9 @@ main(int argc, char *argv[]) if ((ret = test_regexp()) != 0) { return ret; } + if ((ret = test_module()) != 0) { + return ret; + } if ((ret = test_hash_functions()) != 0) { return ret; } |