version 3.0

This commit is contained in:
Bramfeld Team 2015-08-31 14:01:44 +02:00
commit d837490606
209 changed files with 19662 additions and 0 deletions

0
common/thread/Makefile Normal file
View file

15
common/thread/TODO Normal file
View file

@ -0,0 +1,15 @@
Threads need to pick a blocking mechanism and somehow stick to it reliably, so there can be a neat and tidy Thread::kill function. A signal should knock a thread out of anything, but that can still have races. It would be nice if Thread::kill didn't have to be virtual, and if it could be worked into a generalized thread-to-thread messaging paradigm, but that's problematic when one of the major purposes of having threads in the first place is to be able to use other polling mechanisms in separate threads, i.e. blocking for libpcap or X11 or whatever.
Maybe it would be best to remove virtual void main() and instead have a worker-thread model, where threads can perform work, wait, or be signalled. The work, wait and signal functions would be virtual, which would hide a lot of the complexity about whether it's okay to block, or whether we've been signalled. And then threads could determine their own needs for block and signal; most threads might use EventPoll to do that work, but some might use something like pcap or X11 instead. No inter-thread messaging at a low-level, but maybe some higher-level interfaces that could be implemented on top of them.
If moving to a worker model, why not provide a work unit, too, and the possibility of some messaging primitives? That also has the benefit of mirroring some of the hardware models it'd be nice for the event system and threading to be compatible with, like that of Octeon. And it allows for lossless messaging between threads, whereas signal/wait can have problems with races and spurious wakeups, right? Especially if there's any chance of the signal being consumed by something like a system call, rather than an instance of wait. But with some slightly-heavyweight locking, there's no chance of those kinds of races, because e.g. the thread state lock would be held during work processing. That nearly serializes processing, though. *Unless* we have a lockless work-queueing model in addition to a wakeup mechanism. Right? Then we can guarantee that a thread will acquire a lock, check for work, and only then block. There's still races, though, such as with pcap, or X11, which won't let us specify a lock to relinquish at wait time. Argh!
Maybe too much generalization is a curse, and it would be better to find a limited and reliable model to support, and make sure all the applications we care about in the immediate future are plausible.
Why not just register threads that need to generate callbacks as an "EventSource" with the EventThread/EventSystem? This is non-ideal for a world in which we would have multiple EventThreads but it's a start for now. But with this, the EventSource could include a pipe to read/write on, which would be very neat and tidy.
I tried moving towards making EventThread the normative model for threads, and parameterizing anything using callbacks on EventThread (as a CallbackScheduler mostly, but also for timeouts, although there's no reason to not have timeouts in a different thread, and merely schedule them back to EventThread.) This is OK, but mostly devolves into single-threading anyway.
If polling code is simplified a little further and moved to common/poll/..., it makes sense indeed to move polling into *every* thread, and to use pipes to communicate between threads (or, where possible, we could use user events like with kqueue.) That can all mostly be done under the hood after the fact for real if we begin with the EventSource model, in which new sources are registered with the EventSystem. Then we have one-direction communication, which is probably most of what we need anyway. Right?
Actually, to avoid races, the method of inter-thread communication must be a condvar so that we can savely use mutexes. Otherwise we can go to sleep with work waiting, right? Unless we get really fussy about the data on the inter-thread pipe, but why not just use condvars instead? Then we just need to have a WorkerThread which has block/signal/work abstractions, and make EventPoll one of the few that uses something other than the default. And have a single poll thread, of course. Yes, this doesn't solve the pcap race problem, but nothing will; pcap is thread hell.

95
common/thread/atomic.h Normal file
View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2010-2011 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef COMMON_THREAD_ATOMIC_H
#define COMMON_THREAD_ATOMIC_H
template<typename T>
class Atomic {
T val_;
public:
Atomic(void)
: val_()
{ }
template<typename Ta>
Atomic(Ta arg)
: val_(arg)
{ }
~Atomic()
{ }
T val () const { return val_; }
bool operator == (T v) const { return (v == val_); }
bool operator != (T v) const { return (v != val_); }
/*
* Note that these are deliberately not operator overloads, to force
* deliberate use of this class.
*/
#if defined(__GNUC__)
template<typename Ta>
T add(Ta arg)
{
return (__sync_add_and_fetch (&val_, arg));
}
template<typename Ta>
T subtract(Ta arg)
{
return (__sync_sub_and_fetch (&val_, arg));
}
template<typename Ta>
T set(Ta arg)
{
return (__sync_or_and_fetch (&val_, arg));
}
template<typename Ta>
T mask(Ta arg)
{
return (__sync_and_and_fetch (&val_, arg));
}
template<typename Ta>
T clear(Ta arg)
{
return (__sync_and_and_fetch (&val_, ~arg));
}
template<typename To, typename Tn>
bool cmpset(To oldval, Tn newval)
{
return (__sync_bool_compare_and_swap (&val_, oldval, newval));
}
#else
#error "No support for atomic operations for your compiler. Why not add some?"
#endif
};
#endif /* !COMMON_THREAD_ATOMIC_H */

6
common/thread/lib.mk Normal file
View file

@ -0,0 +1,6 @@
VPATH+= ${TOPDIR}/common/thread
SRCS+= thread.cc
LDADD+= -lpthread

71
common/thread/thread.cc Normal file
View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2010-2012 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <common/thread/thread.h>
////////////////////////////////////////////////////////////////////////////////
// //
// File: thread.cc //
// Description: basic structure for generic thread objects //
// Project: WANProxy XTech //
// Adapted by: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
namespace
{
static void* thread_posix_start (void* arg)
{
Thread* td = (Thread*) arg;
td->main ();
return 0;
}
}
Thread::Thread (const std::string& name) : name_(name), thread_id_(0), stop_(false)
{
}
bool Thread::start ()
{
int rv = pthread_create (&thread_id_, NULL, thread_posix_start, this);
if (rv != 0)
{
ERROR("/thread/posix") << "Unable to start thread.";
return false;
}
return true;
}
void Thread::stop ()
{
stop_ = true;
void* val;
int rv = pthread_join (thread_id_, &val);
if (rv == -1)
ERROR("/thread/posix") << "Thread join failed.";
}

59
common/thread/thread.h Normal file
View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2010-2011 Juli Mallett. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef COMMON_THREAD_THREAD_H
#define COMMON_THREAD_THREAD_H
#include <pthread.h>
////////////////////////////////////////////////////////////////////////////////
// //
// File: thread.h //
// Description: basic structure for generic thread objects //
// Project: WANProxy XTech //
// Adapted by: Andreu Vidal Bramfeld-Software //
// Last modified: 2015-04-01 //
// //
////////////////////////////////////////////////////////////////////////////////
class Thread
{
protected:
std::string name_;
pthread_t thread_id_;
bool stop_;
protected:
Thread (const std::string&);
public:
virtual ~Thread () {}
bool start ();
virtual void stop ();
virtual void main () = 0;
};
#endif /* !COMMON_THREAD_THREAD_H */