aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-06-29 22:17:14 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-06-29 22:17:14 +0000
commit87ba667717a17773c1f9590a7369f86560e2ee61 (patch)
tree5d03a65aa922641cb116a34014e9759f2fbdf5bc
parent66cc8b93a7d3c649be8b2a5a8d2277a9fe5dc2a7 (diff)
downloadpaludis-87ba667717a17773c1f9590a7369f86560e2ee61.tar.gz
paludis-87ba667717a17773c1f9590a7369f86560e2ee61.tar.xz
r3662@snowflake: ciaranm | 2007-06-29 23:16:55 +0100
Start of experimental threading
-rw-r--r--configure.ac49
-rw-r--r--paludis/Makefile.am.m413
-rw-r--r--paludis/repositories/e/ebuild/Makefile.am9
-rw-r--r--paludis/util/Makefile.am.m411
-rw-r--r--paludis/util/action_queue.cc119
-rw-r--r--paludis/util/action_queue.hh40
-rw-r--r--paludis/util/action_queue_TEST.cc55
-rw-r--r--paludis/util/attributes.hh6
-rw-r--r--paludis/util/condition_variable.cc94
-rw-r--r--paludis/util/condition_variable.hh54
-rw-r--r--paludis/util/condition_variable_TEST.cc43
-rw-r--r--paludis/util/files.m44
-rw-r--r--paludis/util/instantiation_policy-impl.hh12
-rw-r--r--paludis/util/log.cc148
-rw-r--r--paludis/util/log.hh5
-rw-r--r--paludis/util/log_TEST.cc7
-rw-r--r--paludis/util/mutex.cc113
-rw-r--r--paludis/util/mutex.hh84
-rw-r--r--paludis/util/mutex_TEST.cc41
-rw-r--r--paludis/util/thread.cc58
-rw-r--r--paludis/util/thread.hh48
-rw-r--r--paludis/util/thread_TEST.cc51
22 files changed, 984 insertions, 80 deletions
diff --git a/configure.ac b/configure.ac
index 79a71f6..918a8d8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -558,6 +558,55 @@ else
fi
AC_SUBST([PALUDIS_ENABLE_VISIBILITY])
AC_SUBST([PALUDIS_CXXFLAGS_VISIBILITY])
+
+AC_MSG_CHECKING([whether to enable threads])
+PTHREAD_LIBS=""
+AC_ARG_ENABLE([threads],
+ AS_HELP_STRING([--enable-threads], [Enable threads (experimental)]),
+ [ENABLE_THREADS=$enableval],
+ [ENABLE_THREADS=no])
+AC_MSG_RESULT([$ENABLE_THREADS])
+AC_SUBST([ENABLE_THREADS])
+if test "x$ENABLE_THREADS" = "xyes" ; then
+ AC_MSG_CHECKING([for compiler __thread support])
+ AC_COMPILE_IFELSE([
+int main(int, char **)
+{
+ static __thread int x(0);
+ return x;
+}
+],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ AC_MSG_ERROR([--enable-threads requires compiler __thread support])])
+
+ AC_MSG_CHECKING([whether -pthread -lpthread gets us posix threads])
+ save_CXXFLAGS=$CXXFLAGS
+ save_LDFLAGS=$LDFLAGS
+ CXXFLAGS="$CXXFLAGS -pthread"
+ LDFLAGS="$LDFLAGS -pthread -lpthread"
+ AC_LINK_IFELSE([
+#include <pthread.h>
+int main(int, char **)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ return 0;
+}
+],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ AC_MSG_ERROR([don't know how to enable pthreads for your compiler])])
+ CXXFLAGS=$save_CXXFLAGS
+ LDFLAGS=$save_LDFLAGS
+ PALUDIS_CXXFLAGS="$PALUDIS_CXXFLAGS -DPALUDIS_ENABLE_THREADS=1 -pthread"
+ PTHREAD_LIBS="-pthread -lpthread"
+ PALUDIS_ENABLE_THREADS=1
+else
+ PALUDIS_ENABLE_THREADS=0
+fi
+AC_SUBST([PALUDIS_ENABLE_THREADS])
+AC_SUBST([PTHREAD_LIBS])
dnl }}}
dnl {{{ sandbox
diff --git a/paludis/Makefile.am.m4 b/paludis/Makefile.am.m4
index c1e4722..e9fc1c2 100644
--- a/paludis/Makefile.am.m4
+++ b/paludis/Makefile.am.m4
@@ -85,7 +85,7 @@ SUBDIRS = digests distributions eapis fetchers syncers util selinux . dep_list m
BUILT_SOURCES = srcleanlist secleanlist
libpaludis_la_SOURCES = filelist
-libpaludis_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0
+libpaludis_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0 $(PTHREAD_LIBS)
libpaludismanpagethings_la_SOURCES = name.cc
@@ -96,21 +96,14 @@ libpaludissohooks_TEST_la_LDFLAGS = -rpath /nowhere -version-info @VERSION_LIB_C
libpaludissohooks_TEST_la_LIBADD = $(top_builddir)/paludis/libpaludis.la
-if ! MONOLITHIC
-
libpaludis_la_LIBADD = \
$(top_builddir)/paludis/util/libpaludisutil.la \
- @DYNAMIC_LD_LIBS@
+ @DYNAMIC_LD_LIBS@ \
+ $(PTHREAD_LIBS)
libpaludismanpagethings_la_LIBADD = \
$(top_builddir)/paludis/util/libpaludisutil.la
-else
-
-libpaludis_la_LIBADD = @DYNAMIC_LD_LIBS@
-
-endif
-
TESTS = testlist
check_PROGRAMS = $(TESTS)
diff --git a/paludis/repositories/e/ebuild/Makefile.am b/paludis/repositories/e/ebuild/Makefile.am
index 4346427..cb4bbd9 100644
--- a/paludis/repositories/e/ebuild/Makefile.am
+++ b/paludis/repositories/e/ebuild/Makefile.am
@@ -67,12 +67,17 @@ merge_LDADD = \
$(top_builddir)/paludis/util/libpaludisutil.la \
$(top_builddir)/paludis/digests/libpaludisdigests.la \
$(top_builddir)/paludis/selinux/libpaludisselinux.la \
- $(DYNAMIC_LD_LIBS)
+ $(DYNAMIC_LD_LIBS) \
+ $(PTHREAD_LIBS)
+merge_LDFLAGS = $(PTHREAD_LIBS)
unmerge_SOURCES = unmerge.cc merge_common.cc merge_common.hh
unmerge_LDADD = \
$(top_builddir)/paludis/util/libpaludisutil.la \
- $(top_builddir)/paludis/digests/libpaludisdigests.la
+ $(top_builddir)/paludis/digests/libpaludisdigests.la \
+ $(DYNAMIC_LD_LIBS) \
+ $(PTHREAD_LIBS)
+unmerge_LDFLAGS = $(PTHREAD_LIBS)
check_SCRIPTS = $(TESTS) \
merge_TEST_setup.sh merge_TEST_cleanup.sh \
diff --git a/paludis/util/Makefile.am.m4 b/paludis/util/Makefile.am.m4
index f4de577..47f9a98 100644
--- a/paludis/util/Makefile.am.m4
+++ b/paludis/util/Makefile.am.m4
@@ -78,7 +78,8 @@ EXTRA_DIST = util.hh.m4 Makefile.am.m4 files.m4 srlist srcleanlist selist seclea
SUBDIRS = .
libpaludisutil_la_SOURCES = filelist
-libpaludisutil_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0
+libpaludisutil_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0 $(PTHREAD_LIBS)
+libpaludisutil_la_LIBADD = $(PTHREAD_LIBS)
TESTS = testlist
@@ -90,16 +91,8 @@ TESTS_ENVIRONMENT = env \
check_PROGRAMS = $(TESTS)
check_SCRIPTS = testscriptlist
-if MONOLITHIC
-
-noinst_LTLIBRARIES = libpaludisutil.la
-
-else
-
lib_LTLIBRARIES = libpaludisutil.la
-endif
-
paludis_util_includedir = $(includedir)/paludis-$(PALUDIS_PC_SLOT)/paludis/util/
paludis_util_include_HEADERS = headerlist srheaderlist seheaderlist
diff --git a/paludis/util/action_queue.cc b/paludis/util/action_queue.cc
new file mode 100644
index 0000000..2421ef9
--- /dev/null
+++ b/paludis/util/action_queue.cc
@@ -0,0 +1,119 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/action_queue.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+
+#ifdef PALUDIS_ENABLE_THREADS
+# include <paludis/util/mutex.hh>
+# include <paludis/util/condition_variable.hh>
+# include <paludis/util/thread.hh>
+# include <list>
+#endif
+
+using namespace paludis;
+
+namespace paludis
+{
+ template <>
+ struct Implementation<ActionQueue>
+ {
+#ifdef PALUDIS_ENABLE_THREADS
+ Mutex mutex;
+ ConditionVariable condition;
+ bool should_finish;
+ std::list<tr1::function<void () throw ()> > queue;
+ Thread thread;
+
+ void finish()
+ {
+ Lock l(mutex);
+ should_finish = true;
+ }
+
+ void thread_func()
+ {
+ while (true)
+ {
+ tr1::function<void () throw ()> func;
+ {
+ Lock l(mutex);
+ if (queue.empty())
+ {
+ if (should_finish)
+ break;
+
+ condition.wait(mutex);
+ continue;
+ }
+ else
+ {
+ func = *queue.begin();
+ queue.pop_front();
+ }
+ }
+
+ func();
+ }
+ }
+
+ Implementation() :
+ should_finish(false),
+ thread(tr1::bind(tr1::mem_fn(&Implementation::thread_func), this))
+ {
+ }
+#endif
+ };
+}
+
+ActionQueue::ActionQueue() :
+ PrivateImplementationPattern<ActionQueue>(new Implementation<ActionQueue>)
+{
+}
+
+ActionQueue::~ActionQueue()
+{
+ enqueue(tr1::bind(tr1::mem_fn(&Implementation<ActionQueue>::finish), _imp.get()));
+}
+
+void
+ActionQueue::enqueue(const tr1::function<void () throw ()> & f)
+{
+#ifdef PALUDIS_ENABLE_THREADS
+ Lock l(_imp->mutex);
+ _imp->queue.push_back(f);
+ _imp->condition.signal();
+#else
+ f();
+#endif
+}
+
+void
+ActionQueue::complete_pending()
+{
+#ifdef PALUDIS_ENABLE_THREADS
+ ConditionVariable c;
+ Mutex m;
+ Lock l(m);
+
+ enqueue(tr1::bind(tr1::mem_fn(&ConditionVariable::acquire_then_signal), &c, tr1::ref(m)));
+ c.wait(m);
+#endif
+}
+
diff --git a/paludis/util/action_queue.hh b/paludis/util/action_queue.hh
new file mode 100644
index 0000000..46c8d2a
--- /dev/null
+++ b/paludis/util/action_queue.hh
@@ -0,0 +1,40 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_ACTION_QUEUE_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_ACTION_QUEUE_HH 1
+
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/tr1_functional.hh>
+
+namespace paludis
+{
+ class PALUDIS_VISIBLE ActionQueue :
+ private PrivateImplementationPattern<ActionQueue>
+ {
+ public:
+ ActionQueue();
+ ~ActionQueue();
+
+ void enqueue(const tr1::function<void () throw ()> &);
+ void complete_pending();
+ };
+}
+
+#endif
diff --git a/paludis/util/action_queue_TEST.cc b/paludis/util/action_queue_TEST.cc
new file mode 100644
index 0000000..921e886
--- /dev/null
+++ b/paludis/util/action_queue_TEST.cc
@@ -0,0 +1,55 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/action_queue.hh>
+#include <paludis/util/join.hh>
+#include <test/test_runner.hh>
+#include <test/test_framework.hh>
+#include <list>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct ActionQueueTest : TestCase
+ {
+ ActionQueueTest() : TestCase("action queue") { }
+
+ void run()
+ {
+ std::list<int> l;
+ {
+ ActionQueue q;
+ for (int x(0) ; x < 100 ; ++x)
+ q.enqueue(tr1::bind(tr1::mem_fn(&std::list<int>::push_back), &l, x));
+ }
+
+ std::list<int>::const_iterator i(l.begin());
+ for (int x(0) ; x < 100 ; ++x)
+ {
+ TEST_CHECK(i != l.end());
+ TEST_CHECK(*i == x);
+ ++i;
+ }
+ TEST_CHECK(i == l.end());
+ }
+ } test_action_queue;
+}
+
diff --git a/paludis/util/attributes.hh b/paludis/util/attributes.hh
index f5fa2b4..a4ad7a5 100644
--- a/paludis/util/attributes.hh
+++ b/paludis/util/attributes.hh
@@ -75,4 +75,10 @@
# define PALUDIS_HIDDEN
#endif
+#ifdef PALUDIS_ENABLE_THREADS
+# define PALUDIS_TLS static __thread
+#else
+# define PALUDIS_TLS static
+#endif
+
#endif
diff --git a/paludis/util/condition_variable.cc b/paludis/util/condition_variable.cc
new file mode 100644
index 0000000..269ff43
--- /dev/null
+++ b/paludis/util/condition_variable.cc
@@ -0,0 +1,94 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/condition_variable.hh>
+
+using namespace paludis;
+
+#ifdef PALUDIS_ENABLE_THREADS
+
+ConditionVariable::ConditionVariable() :
+ _cond(new pthread_cond_t)
+{
+ pthread_cond_init(_cond, 0);
+}
+
+ConditionVariable::~ConditionVariable()
+{
+ pthread_cond_destroy(_cond);
+ delete _cond;
+}
+
+void
+ConditionVariable::broadcast()
+{
+ pthread_cond_broadcast(_cond);
+}
+
+void
+ConditionVariable::signal()
+{
+ pthread_cond_signal(_cond);
+}
+
+void
+ConditionVariable::acquire_then_signal(Mutex & m)
+{
+ Lock l(m);
+ signal();
+}
+
+void
+ConditionVariable::wait(Mutex & m)
+{
+ pthread_cond_wait(_cond, m.posix_mutex());
+}
+
+#else
+
+ConditionVariable::ConditionVariable()
+{
+}
+
+ConditionVariable::~ConditionVariable()
+{
+}
+
+void
+ConditionVariable::broadcast()
+{
+}
+
+void
+ConditionVariable::signal()
+{
+}
+
+void
+ConditionVariable::acquire_then_signal(Mutex &)
+{
+}
+
+void
+ConditionVariable::wait(Mutex &)
+{
+}
+
+#endif
+
diff --git a/paludis/util/condition_variable.hh b/paludis/util/condition_variable.hh
new file mode 100644
index 0000000..93751ba
--- /dev/null
+++ b/paludis/util/condition_variable.hh
@@ -0,0 +1,54 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_CONDITION_VARIABLE_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_CONDITION_VARIABLE_HH 1
+
+#include <paludis/util/attributes.hh>
+#include <paludis/util/mutex.hh>
+
+#ifdef PALUDIS_ENABLE_THREADS
+# include <pthread.h>
+#endif
+
+namespace paludis
+{
+ class PALUDIS_VISIBLE ConditionVariable
+ {
+ private:
+ ConditionVariable(const ConditionVariable &);
+ ConditionVariable & operator= (const ConditionVariable &);
+
+#ifdef PALUDIS_ENABLE_THREADS
+ pthread_cond_t * const _cond;
+#endif
+
+ public:
+ ConditionVariable();
+ ~ConditionVariable();
+
+ void broadcast();
+ void signal();
+ void acquire_then_signal(Mutex &);
+
+ void wait(Mutex &);
+ };
+}
+
+#endif
diff --git a/paludis/util/condition_variable_TEST.cc b/paludis/util/condition_variable_TEST.cc
new file mode 100644
index 0000000..bbf0060
--- /dev/null
+++ b/paludis/util/condition_variable_TEST.cc
@@ -0,0 +1,43 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "condition_variable.hh"
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace paludis;
+using namespace test;
+
+namespace test_cases
+{
+ struct ConditionTest : TestCase
+ {
+ ConditionTest() : TestCase("condition") { }
+
+ void run()
+ {
+ ConditionVariable c;
+ c.signal();
+ c.broadcast();
+ TEST_CHECK(true);
+ }
+ } test_condition;
+}
+
+
diff --git a/paludis/util/files.m4 b/paludis/util/files.m4
index 492ec49..debd87c 100644
--- a/paludis/util/files.m4
+++ b/paludis/util/files.m4
@@ -8,10 +8,12 @@ dnl the base filename with no extension; later parameters can be `hh', `cc',
dnl `test', `impl', `testscript'. Note that there isn't much error checking done
dnl on this file at present...
+add(`action_queue', `hh', `cc', `test')
add(`attributes', `hh')
add(`clone', `hh', `impl')
add(`collection', `hh', `fwd')
add(`collection_concrete', `hh')
+add(`condition_variable', `hh', `cc', `test')
add(`destringify', `hh', `cc', `test')
add(`dir_iterator', `hh', `cc', `test', `testscript')
add(`exception', `hh', `cc')
@@ -26,6 +28,7 @@ add(`is_file_with_extension', `hh', `cc', `se', `test', `testscript')
add(`join', `hh', `test')
add(`log', `hh', `cc', `se', `test')
add(`make_shared_ptr', `hh', `fwd')
+add(`mutex', `hh', `cc', `test')
add(`operators', `hh')
add(`options', `hh', `fwd', `cc', `test')
add(`output_wrapper', `test', `testscript')
@@ -38,6 +41,7 @@ add(`sr', `hh', `cc')
add(`stringify', `hh', `test')
add(`strip', `hh', `cc', `test')
add(`system', `hh', `cc', `test', `testscript')
+add(`thread', `hh', `cc', `test')
add(`tokeniser', `hh', `cc', `test')
add(`tr1_memory', `hh')
add(`tr1_type_traits', `hh')
diff --git a/paludis/util/instantiation_policy-impl.hh b/paludis/util/instantiation_policy-impl.hh
index 9cf808d..fc682a2 100644
--- a/paludis/util/instantiation_policy-impl.hh
+++ b/paludis/util/instantiation_policy-impl.hh
@@ -24,6 +24,7 @@
#include <paludis/util/exception.hh>
#include <paludis/util/save.hh>
#include <paludis/util/stringify.hh>
+#include <paludis/util/mutex.hh>
template <typename OurType_>
void
@@ -65,16 +66,21 @@ template<typename OurType_>
OurType_ *
paludis::InstantiationPolicy<OurType_, paludis::instantiation_method::SingletonTag>::get_instance()
{
- static bool recursive(false);
OurType_ * * i(_get_instance_ptr());
if (0 == *i)
{
+ PALUDIS_TLS bool recursive(false);
if (recursive)
throw paludis::InternalError(PALUDIS_HERE, "Recursive instantiation");
-
Save<bool> save_recursive(&recursive, true);
- *i = new OurType_;
+
+ static Mutex m;
+ Lock l(m);
+
+ i = _get_instance_ptr();
+ if (0 == *i)
+ *i = new OurType_;
}
return *i;
diff --git a/paludis/util/log.cc b/paludis/util/log.cc
index d0d7119..7e3a5a7 100644
--- a/paludis/util/log.cc
+++ b/paludis/util/log.cc
@@ -23,6 +23,7 @@
#include <paludis/util/private_implementation_pattern-impl.hh>
#include <paludis/util/instantiation_policy-impl.hh>
#include <paludis/util/exception.hh>
+#include <paludis/util/action_queue.hh>
/** \file
* Implementation for Log.
@@ -46,23 +47,91 @@ namespace paludis
template<>
struct Implementation<Log>
{
- /// Current log level
LogLevel log_level;
-
- /// Current output stream
std::ostream * stream;
-
- /// Program name
std::string program_name;
+
+ mutable ActionQueue action_queue;
+
+ Implementation() :
+ log_level(initial_ll),
+ stream(&std::cerr),
+ program_name("paludis")
+ {
+ }
+
+ void message(const LogLevel l, const LogContext c, const std::string & s)
+ {
+ if (l >= log_level)
+ {
+ *stream << program_name << "@" << ::time(0) << ": ";
+ do
+ {
+ switch (l)
+ {
+ case ll_debug:
+ *stream << "[DEBUG] ";
+ continue;
+
+ case ll_qa:
+ *stream << "[QA] ";
+ continue;
+
+ case ll_warning:
+ *stream << "[WARNING] ";
+ continue;
+
+ case ll_silent:
+ throw InternalError(PALUDIS_HERE, "ll_silent used for a message");
+
+ case last_ll:
+ break;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Bad value for log_level");
+
+ } while (false);
+
+ if (lc_context == c)
+ {
+ static std::string previous_context;
+ std::string context(Context::backtrace("\n ... "));
+ if (previous_context == context)
+ *stream << "(same context) " << s << std::endl;
+ else
+ *stream << Context::backtrace("\n ... ") << s << std::endl;
+ previous_context = context;
+ }
+ else
+ *stream << s << std::endl;
+ }
+ }
+
+ void set_log_level(const LogLevel l)
+ {
+ log_level = l;
+ }
+
+ void get_log_level(LogLevel & l) const
+ {
+ l = log_level;
+ }
+
+ void set_program_name(const std::string & s)
+ {
+ program_name = s;
+ }
+
+ void set_log_stream(std::ostream * const s)
+ {
+ stream = s;
+ }
};
}
Log::Log() :
PrivateImplementationPattern<Log>(new Implementation<Log>)
{
- _imp->log_level = initial_ll;
- _imp->stream = &std::cerr;
- _imp->program_name = "paludis";
}
Log::~Log()
@@ -72,61 +141,22 @@ Log::~Log()
void
Log::set_log_level(const LogLevel l)
{
- _imp->log_level = l;
+ _imp->action_queue.enqueue(tr1::bind(tr1::mem_fn(&Implementation<Log>::set_log_level), _imp.get(), l));
}
LogLevel
Log::log_level() const
{
- return _imp->log_level;
+ LogLevel result(static_cast<LogLevel>(1337));
+ _imp->action_queue.enqueue(tr1::bind(tr1::mem_fn(&Implementation<Log>::get_log_level), _imp.get(), tr1::ref(result)));
+ _imp->action_queue.complete_pending();
+ return result;
}
void
Log::_message(const LogLevel l, const LogContext c, const std::string & s)
{
- if (l >= _imp->log_level)
- {
- *_imp->stream << _imp->program_name << "@" << ::time(0) << ": ";
- do
- {
- switch (l)
- {
- case ll_debug:
- *_imp->stream << "[DEBUG] ";
- continue;
-
- case ll_qa:
- *_imp->stream << "[QA] ";
- continue;
-
- case ll_warning:
- *_imp->stream << "[WARNING] ";
- continue;
-
- case ll_silent:
- throw InternalError(PALUDIS_HERE, "ll_silent used for a message");
-
- case last_ll:
- break;
- }
-
- throw InternalError(PALUDIS_HERE, "Bad value for log_level");
-
- } while (false);
-
- if (lc_context == c)
- {
- static std::string previous_context;
- std::string context(Context::backtrace("\n ... "));
- if (previous_context == context)
- *_imp->stream << "(same context) " << s << std::endl;
- else
- *_imp->stream << Context::backtrace("\n ... ") << s << std::endl;
- previous_context = context;
- }
- else
- *_imp->stream << s << std::endl;
- }
+ _imp->action_queue.enqueue(tr1::bind(tr1::mem_fn(&Implementation<Log>::message), _imp.get(), l, c, s));
}
LogMessageHandler::LogMessageHandler(const LogMessageHandler & o) :
@@ -151,7 +181,13 @@ Log::message(const LogLevel l, const LogContext c)
void
Log::set_log_stream(std::ostream * const s)
{
- _imp->stream = s;
+ _imp->action_queue.enqueue(tr1::bind(tr1::mem_fn(&Implementation<Log>::set_log_stream), _imp.get(), s));
+}
+
+void
+Log::complete_pending() const
+{
+ _imp->action_queue.complete_pending();
}
std::ostream &
@@ -179,13 +215,13 @@ paludis::operator<< (std::ostream & s, const LogLevel & l)
;
};
- throw InternalError(PALUDIS_HERE, "Bad log level");
+ throw InternalError(PALUDIS_HERE, "Bad log level '" + stringify(static_cast<int>(l)) + "'");
}
void
Log::set_program_name(const std::string & s)
{
- _imp->program_name = s;
+ _imp->action_queue.enqueue(tr1::bind(tr1::mem_fn(&Implementation<Log>::set_program_name), _imp.get(), s));
}
LogMessageHandler::LogMessageHandler(Log * const ll, const LogLevel l, const LogContext c) :
diff --git a/paludis/util/log.hh b/paludis/util/log.hh
index fa80096..3a53309 100644
--- a/paludis/util/log.hh
+++ b/paludis/util/log.hh
@@ -117,6 +117,11 @@ namespace paludis
* Set our program name.
*/
void set_program_name(const std::string &);
+
+ /**
+ * Finish any pending writes.
+ */
+ void complete_pending() const;
};
/**
diff --git a/paludis/util/log_TEST.cc b/paludis/util/log_TEST.cc
index 7cfba21..c88f396 100644
--- a/paludis/util/log_TEST.cc
+++ b/paludis/util/log_TEST.cc
@@ -77,6 +77,7 @@ namespace test_cases
TEST_CHECK(s.str().empty());
Log::get_instance()->message(ll_debug, lc_no_context) << "one";
+ Log::get_instance()->complete_pending();
TEST_CHECK(! s.str().empty());
TEST_CHECK(std::string::npos != s.str().find("one"));
@@ -86,8 +87,10 @@ namespace test_cases
Log::get_instance()->set_log_level(ll_warning);
Log::get_instance()->message(ll_debug, lc_no_context) << "two";
+ Log::get_instance()->complete_pending();
TEST_CHECK(t.str().empty());
Log::get_instance()->message(ll_warning, lc_no_context) << "three" << "." << 14;
+ Log::get_instance()->complete_pending();
TEST_CHECK(! t.str().empty());
TEST_CHECK(std::string::npos == t.str().find("one"));
TEST_CHECK(std::string::npos == t.str().find("two"));
@@ -109,15 +112,19 @@ namespace test_cases
TEST_CHECK(s.str().empty());
TEST_CHECK_THROWS(Log::get_instance()->message(ll_debug, lc_no_context) << throws_a_monkey(), Monkey);
+ Log::get_instance()->complete_pending();
TEST_CHECK(s.str().empty());
TEST_CHECK_THROWS(Log::get_instance()->message(ll_debug, lc_no_context)
<< "one" << throws_a_monkey() << "two", Monkey);
+ Log::get_instance()->complete_pending();
TEST_CHECK(s.str().empty());
TEST_CHECK_THROWS(Log::get_instance()->message(ll_debug, lc_no_context) << throws_a_monkey_when_stringified(), Monkey);
+ Log::get_instance()->complete_pending();
TEST_CHECK(s.str().empty());
TEST_CHECK_THROWS(Log::get_instance()->message(ll_debug, lc_no_context)
<< "one" << throws_a_monkey_when_stringified() << "two", Monkey);
+ Log::get_instance()->complete_pending();
TEST_CHECK(s.str().empty());
}
} test_log_exception;
diff --git a/paludis/util/mutex.cc b/paludis/util/mutex.cc
new file mode 100644
index 0000000..315b091
--- /dev/null
+++ b/paludis/util/mutex.cc
@@ -0,0 +1,113 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/mutex.hh>
+
+using namespace paludis;
+
+#ifdef PALUDIS_ENABLE_THREADS
+
+Mutex::Mutex() :
+ _attr(new pthread_mutexattr_t),
+ _mutex(new pthread_mutex_t)
+{
+ pthread_mutexattr_init(_attr);
+ pthread_mutexattr_settype(_attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(_mutex, _attr);
+}
+
+Mutex::~Mutex()
+{
+ pthread_mutex_destroy(_mutex);
+ pthread_mutexattr_destroy(_attr);
+
+ delete _mutex;
+ delete _attr;
+}
+
+pthread_mutex_t * const
+Mutex::posix_mutex()
+{
+ return _mutex;
+}
+
+Lock::Lock(Mutex & m) :
+ _mutex(&m)
+{
+ pthread_mutex_lock(_mutex->posix_mutex());
+}
+
+Lock::~Lock()
+{
+ pthread_mutex_unlock(_mutex->posix_mutex());
+}
+
+TryLock::TryLock(Mutex & m) :
+ _mutex(&m)
+{
+ if (0 != pthread_mutex_trylock(_mutex->posix_mutex()))
+ _mutex = 0;
+}
+
+TryLock::~TryLock()
+{
+ if (_mutex)
+ pthread_mutex_unlock(_mutex->posix_mutex());
+}
+
+bool
+TryLock::operator() () const
+{
+ return _mutex;
+}
+
+#else
+
+Mutex::Mutex()
+{
+}
+
+Mutex::~Mutex()
+{
+}
+
+Lock::Lock(Mutex &)
+{
+}
+
+Lock::~Lock()
+{
+}
+
+TryLock::TryLock(Mutex &)
+{
+}
+
+TryLock::~TryLock()
+{
+}
+
+bool
+TryLock::operator() () const
+{
+ return true;
+}
+
+#endif
+
diff --git a/paludis/util/mutex.hh b/paludis/util/mutex.hh
new file mode 100644
index 0000000..bae7fa9
--- /dev/null
+++ b/paludis/util/mutex.hh
@@ -0,0 +1,84 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_MUTEX_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_MUTEX_HH 1
+
+#include <paludis/util/attributes.hh>
+
+#ifdef PALUDIS_ENABLE_THREADS
+# include <pthread.h>
+#endif
+
+namespace paludis
+{
+ class PALUDIS_VISIBLE Mutex
+ {
+ private:
+ Mutex(const Mutex &);
+ Mutex & operator= (const Mutex &);
+
+#ifdef PALUDIS_ENABLE_THREADS
+ pthread_mutexattr_t * const _attr;
+ pthread_mutex_t * const _mutex;
+#endif
+
+ public:
+ explicit Mutex();
+ ~Mutex();
+
+#ifdef PALUDIS_ENABLE_THREADS
+ pthread_mutex_t * const posix_mutex() PALUDIS_ATTRIBUTE((warn_unused_result));
+#endif
+ };
+
+ class PALUDIS_VISIBLE Lock
+ {
+ private:
+ Lock(const Lock &);
+ Lock & operator= (const Lock &);
+
+#ifdef PALUDIS_ENABLE_THREADS
+ Mutex * const _mutex;
+#endif
+
+ public:
+ explicit Lock(Mutex &);
+ ~Lock();
+ };
+
+ class PALUDIS_VISIBLE TryLock
+ {
+ private:
+ TryLock(const TryLock &);
+ TryLock & operator= (const TryLock &);
+
+#ifdef PALUDIS_ENABLE_THREADS
+ Mutex * _mutex;
+#endif
+
+ public:
+ explicit TryLock(Mutex &);
+ ~TryLock();
+
+ bool operator() () const PALUDIS_ATTRIBUTE((warn_unused_result));
+ };
+}
+
+#endif
diff --git a/paludis/util/mutex_TEST.cc b/paludis/util/mutex_TEST.cc
new file mode 100644
index 0000000..fcac67d
--- /dev/null
+++ b/paludis/util/mutex_TEST.cc
@@ -0,0 +1,41 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "mutex.hh"
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace paludis;
+using namespace test;
+
+namespace test_cases
+{
+ struct LockTest : TestCase
+ {
+ LockTest() : TestCase("lock") { }
+
+ void run()
+ {
+ Mutex m;
+ Lock l(m);
+ TEST_CHECK(true);
+ }
+ } test_lock;
+}
+
diff --git a/paludis/util/thread.cc b/paludis/util/thread.cc
new file mode 100644
index 0000000..dc896ef
--- /dev/null
+++ b/paludis/util/thread.cc
@@ -0,0 +1,58 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "thread.hh"
+
+using namespace paludis;
+
+#ifdef PALUDIS_ENABLE_THREADS
+
+Thread::Thread(const tr1::function<void () throw ()> & f) :
+ _thread(new pthread_t),
+ _func(f)
+{
+ pthread_create(_thread, 0, &thread_func, this);
+}
+
+void *
+Thread::thread_func(void * r)
+{
+ static_cast<Thread *>(r)->_func();
+ return 0;
+}
+
+Thread::~Thread()
+{
+ pthread_join(*_thread, 0);
+ delete _thread;
+}
+
+#else
+
+Thread::Thread(const tr1::function<void () throw ()> & f)
+{
+ f();
+}
+
+Thread::~Thread()
+{
+}
+
+#endif
+
diff --git a/paludis/util/thread.hh b/paludis/util/thread.hh
new file mode 100644
index 0000000..5729d88
--- /dev/null
+++ b/paludis/util/thread.hh
@@ -0,0 +1,48 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_THREAD_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_THREAD_HH 1
+
+#include <paludis/util/tr1_functional.hh>
+#include <paludis/util/attributes.hh>
+
+#ifdef PALUDIS_ENABLE_THREADS
+# include <pthread.h>
+#endif
+
+namespace paludis
+{
+ class PALUDIS_VISIBLE Thread
+ {
+ private:
+#ifdef PALUDIS_ENABLE_THREADS
+ pthread_t * const _thread;
+ const tr1::function<void () throw ()> _func;
+
+ static void * thread_func(void *);
+#endif
+
+ public:
+ Thread(const tr1::function<void () throw ()> &);
+ ~Thread();
+ };
+}
+
+#endif
diff --git a/paludis/util/thread_TEST.cc b/paludis/util/thread_TEST.cc
new file mode 100644
index 0000000..5a39119
--- /dev/null
+++ b/paludis/util/thread_TEST.cc
@@ -0,0 +1,51 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis 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., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/thread.hh>
+#include <test/test_runner.hh>
+#include <test/test_framework.hh>
+
+using namespace test;
+using namespace paludis;
+
+namespace
+{
+ void make_true(bool & b) throw ()
+ {
+ b = true;
+ }
+}
+
+namespace test_cases
+{
+ struct ThreadTest : TestCase
+ {
+ ThreadTest() : TestCase("thread") { }
+
+ void run()
+ {
+ bool x(false);
+ {
+ Thread t(tr1::bind(&make_true, tr1::ref(x)));
+ }
+ TEST_CHECK(x);
+ }
+ } test_thread;
+}
+