aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2007-04-26 15:06:49 +0000
committerMike Buland <eichlan@xagasoft.com>2007-04-26 15:06:49 +0000
commit530014a3cce53e86dce8917e98a4e86d02f176aa (patch)
treec667c996fb91692b101f75296206b8420f19bf73
parent066282ae6de25cf92780dbdaa2fd70a033e95659 (diff)
downloadlibbu++-530014a3cce53e86dce8917e98a4e86d02f176aa.tar.gz
libbu++-530014a3cce53e86dce8917e98a4e86d02f176aa.tar.bz2
libbu++-530014a3cce53e86dce8917e98a4e86d02f176aa.tar.xz
libbu++-530014a3cce53e86dce8917e98a4e86d02f176aa.zip
Merged Ito and put it in the BU namespace. I should probably clean up the
formatting on the comments, some of the lines wrap, but I'm not too worried about it right now. I also fixed up the doxygen config and build.conf files so that everything is building nice and smooth now.
-rw-r--r--Doxyfile4
-rw-r--r--build.conf26
-rwxr-xr-xmkincs.sh5
-rw-r--r--src/exceptions.cpp1
-rw-r--r--src/exceptions.h1
-rw-r--r--src/ito.cpp40
-rw-r--r--src/ito.h98
-rw-r--r--src/itoatom.h56
-rw-r--r--src/itocondition.cpp42
-rw-r--r--src/itocondition.h81
-rw-r--r--src/itomutex.cpp27
-rw-r--r--src/itomutex.h61
-rw-r--r--src/itoqueue.h231
-rw-r--r--src/main.dox14
-rw-r--r--src/serversocket.cpp148
-rw-r--r--src/serversocket.h31
-rw-r--r--src/socket.cpp16
-rw-r--r--src/socket.h4
-rw-r--r--src/tests/itoqueue1.cpp110
-rw-r--r--src/tests/itoqueue2.cpp83
20 files changed, 1059 insertions, 20 deletions
diff --git a/Doxyfile b/Doxyfile
index 6c7412e..c3168fa 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -74,9 +74,9 @@ QUIET = NO
74WARNINGS = YES 74WARNINGS = YES
75WARN_IF_UNDOCUMENTED = YES 75WARN_IF_UNDOCUMENTED = YES
76WARN_IF_DOC_ERROR = YES 76WARN_IF_DOC_ERROR = YES
77WARN_NO_PARAMDOC = NO 77WARN_NO_PARAMDOC = YES
78WARN_FORMAT = "$file:$line: $text" 78WARN_FORMAT = "$file:$line: $text"
79WARN_LOGFILE = 79WARN_LOGFILE = Doxywarn
80#--------------------------------------------------------------------------- 80#---------------------------------------------------------------------------
81# configuration options related to the input files 81# configuration options related to the input files
82#--------------------------------------------------------------------------- 82#---------------------------------------------------------------------------
diff --git a/build.conf b/build.conf
index bc186f9..c289205 100644
--- a/build.conf
+++ b/build.conf
@@ -1,13 +1,17 @@
1# This is a build file for libbu++ 1# This is a build file for libbu++
2 2
3default action: check "libbu++.a" 3default action: check group "lnhdrs", check "libbu++.a"
4"clean" action: clean targets() 4"tests" action: check group "lnhdrs", check group "tests"
5"tests" action: check targets() filter regexp("^tests/.*$") 5"all" action: check group "lnhdrs", check targets()
6"all" action: check targets()
7"fstring" action: check "tests/fstring"
8 6
9set "CXXFLAGS" += "-ggdb -Wall" 7set "CXXFLAGS" += "-ggdb -Wall"
10 8
9filesIn("src") filter regexp("^src/(.*)\\.h$", "src/bu/{re:1}.h"):
10 rule "hln",
11 group "lnhdrs",
12 target file,
13 input "src/{re:1}.h"
14
11"libbu++.a": 15"libbu++.a":
12 rule "lib", 16 rule "lib",
13 target file, 17 target file,
@@ -17,6 +21,7 @@ set "CXXFLAGS" += "-ggdb -Wall"
17directoriesIn("src/tests","tests/"): 21directoriesIn("src/tests","tests/"):
18 rule "exe", 22 rule "exe",
19 target file, 23 target file,
24 group "tests",
20 requires "libbu++.a", 25 requires "libbu++.a",
21 set "CXXFLAGS" += "-Isrc", 26 set "CXXFLAGS" += "-Isrc",
22 set "LDFLAGS" += "-L. -lbu++", 27 set "LDFLAGS" += "-L. -lbu++",
@@ -25,14 +30,18 @@ directoriesIn("src/tests","tests/"):
25filesIn("src/tests") filter regexp("^src/tests/(.*)\\.cpp$", "tests/{re:1}"): 30filesIn("src/tests") filter regexp("^src/tests/(.*)\\.cpp$", "tests/{re:1}"):
26 rule "exe", 31 rule "exe",
27 target file, 32 target file,
33 group "tests",
28 requires "libbu++.a", 34 requires "libbu++.a",
29 set "CXXFLAGS" += "-Isrc", 35 set "CXXFLAGS" += "-Isrc",
30 set "LDFLAGS" += "-L. -lbu++", 36 set "LDFLAGS" += "-L. -lbu++",
31 input "src/{target}.cpp" 37 input "src/{target}.cpp"
32 38
39["tests/itoqueue1", "tests/itoqueue2"]: set "LDFLAGS" += "-lpthread"
40
33directoriesIn("src/unit","unit/"): 41directoriesIn("src/unit","unit/"):
34 rule "exe", 42 rule "exe",
35 target file, 43 target file,
44 group "tests",
36 requires "libbu++.a", 45 requires "libbu++.a",
37 set "CXXFLAGS" += "-Isrc", 46 set "CXXFLAGS" += "-Isrc",
38 set "LDFLAGS" += "-L. -lbu++", 47 set "LDFLAGS" += "-L. -lbu++",
@@ -41,6 +50,7 @@ directoriesIn("src/unit","unit/"):
41filesIn("src/unit") filter regexp("^src/unit/(.*)\\.cpp$", "unit/{re:1}"): 50filesIn("src/unit") filter regexp("^src/unit/(.*)\\.cpp$", "unit/{re:1}"):
42 rule "exe", 51 rule "exe",
43 target file, 52 target file,
53 group "tests",
44 requires "libbu++.a", 54 requires "libbu++.a",
45 set "CXXFLAGS" += "-Isrc", 55 set "CXXFLAGS" += "-Isrc",
46 set "LDFLAGS" += "-L. -lbu++", 56 set "LDFLAGS" += "-L. -lbu++",
@@ -63,3 +73,9 @@ rule "cpp":
63 produces "{re:1}.o", 73 produces "{re:1}.o",
64 requires commandToList("g++ -M {CXXFLAGS} {match}", "make"), 74 requires commandToList("g++ -M {CXXFLAGS} {match}", "make"),
65 perform command("g++ {CXXFLAGS} -c -o {target} {match}") 75 perform command("g++ {CXXFLAGS} -c -o {target} {match}")
76
77rule "hln":
78 matches regexp("src/(.*)\\.h"),
79 produces "src/bu/{re:1}.h",
80 perform command("ln -s ../{re:1}.h {target}")
81
diff --git a/mkincs.sh b/mkincs.sh
new file mode 100755
index 0000000..6f72f89
--- /dev/null
+++ b/mkincs.sh
@@ -0,0 +1,5 @@
1#!/bin/bash
2
3cd src/bu
4rm *
5for i in ../*.h; do ln -s $i; done
diff --git a/src/exceptions.cpp b/src/exceptions.cpp
index a512105..d9f4e70 100644
--- a/src/exceptions.cpp
+++ b/src/exceptions.cpp
@@ -5,6 +5,7 @@ namespace Bu
5{ 5{
6 subExceptionDef( XmlException ) 6 subExceptionDef( XmlException )
7 subExceptionDef( FileException ) 7 subExceptionDef( FileException )
8 subExceptionDef( SocketException )
8 subExceptionDef( ConnectionException ) 9 subExceptionDef( ConnectionException )
9 subExceptionDef( PluginException ) 10 subExceptionDef( PluginException )
10 subExceptionDef( UnsupportedException ) 11 subExceptionDef( UnsupportedException )
diff --git a/src/exceptions.h b/src/exceptions.h
index 3efa19f..f146b73 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -8,6 +8,7 @@ namespace Bu
8{ 8{
9 subExceptionDecl( XmlException ) 9 subExceptionDecl( XmlException )
10 subExceptionDecl( FileException ) 10 subExceptionDecl( FileException )
11 subExceptionDecl( SocketException )
11 subExceptionDecl( ConnectionException ) 12 subExceptionDecl( ConnectionException )
12 subExceptionDecl( PluginException ) 13 subExceptionDecl( PluginException )
13 subExceptionDecl( UnsupportedException ) 14 subExceptionDecl( UnsupportedException )
diff --git a/src/ito.cpp b/src/ito.cpp
new file mode 100644
index 0000000..001ca06
--- /dev/null
+++ b/src/ito.cpp
@@ -0,0 +1,40 @@
1#include "ito.h"
2
3Bu::Ito::Ito()
4{
5}
6
7Bu::Ito::~Ito()
8{
9}
10
11bool Bu::Ito::start()
12{
13 nHandle = pthread_create( &ptHandle, NULL, threadRunner, this );
14
15 return true;
16}
17
18bool Bu::Ito::stop()
19{
20 pthread_exit( &ptHandle );
21
22 return true;
23}
24
25void *Bu::Ito::threadRunner( void *pThread )
26{
27 return ((Ito *)pThread)->run();
28}
29
30bool Bu::Ito::join()
31{
32 pthread_join( ptHandle, NULL );
33 return true;
34}
35
36void Bu::Ito::yield()
37{
38 pthread_yield();
39}
40
diff --git a/src/ito.h b/src/ito.h
new file mode 100644
index 0000000..01253f5
--- /dev/null
+++ b/src/ito.h
@@ -0,0 +1,98 @@
1#ifndef ITO_H
2#define ITO_H
3
4#include <pthread.h>
5
6namespace Bu
7{
8 /**
9 * Simple thread class. This wraps the basic pthread (posix threads) system in
10 * an object oriented sort of way. It allows you to create a class with
11 * standard member variables and callable functions that can be run in it's own
12 * thread, one per class instance.
13 *@author Mike Buland
14 */
15 class Ito
16 {
17 public:
18 /**
19 * Construct an Ito thread.
20 */
21 Ito();
22
23 /**
24 * Destroy an Ito thread.
25 */
26 virtual ~Ito();
27
28 /**
29 * Begin thread execution. This will call the overridden run function,
30 * which will simply execute in it's own thread until the function exits,
31 * the thread is killed, or the thread is cancelled (optionally). The
32 * thread started in this manner has access to all of it's class variables,
33 * but be sure to protect possible multiple-access with ItoMutex objects.
34 *@returns True if starting the thread was successful. False if something
35 * went wrong and the thread has not started.
36 */
37 bool start();
38
39 /**
40 * Forcibly kill a thread. This is not generally considered a good thing to
41 * do, but in those rare cases you need it, it's invaluable. The problem
42 * with stopping (or killing) a thread is that it stops it the moment you
43 * call stop, no matter what it's doing. The object oriented approach to
44 * this will help clean up any class variables that were used, but anything
45 * not managed as a member variable will probably create a memory leak type
46 * of situation. Instead of stop, consider using cancel, which can be
47 * handled by the running thread in a graceful manner.
48 *@returns True if the thread was stopped, false otherwise. When this
49 * function returns the thread may not have stopped, to ensure that the
50 * thread has really stopped, call join.
51 */
52 bool stop();
53
54 /**
55 * The workhorse of the Ito class. This is the function that will run in
56 * the thread, when this function exits the thread dies and is cleaned up
57 * by the system. Make sure to read up on ItoMutex, ItoCondition, and
58 * cancel to see how to control and protect everything you do in a safe way
59 * within this function.
60 *@returns I'm not sure right now, but this is the posix standard form.
61 */
62 virtual void *run()=0;
63
64 /**
65 * Join the thread in action. This function performs what is commonly
66 * called a thread join. That is that it effectively makes the calling
67 * thread an the Ito thread contained in the called object one in the same,
68 * and pauses the calling thread until the called thread exits. That is,
69 * when called from, say, your main(), mythread.join() will not return until
70 * the thread mythread has exited. This is very handy at the end of
71 * programs to ensure all of your data was cleaned up.
72 *@returns True if the thread was joined, false if the thread couldn't be
73 * joined, usually because it isn't running to begin with.
74 */
75 bool join();
76
77 private:
78 pthread_t ptHandle; /**< Internal handle to the posix thread. */
79 int nHandle; /**< Numeric handle to the posix thread. */
80
81 protected:
82 /**
83 * This is the hidden-heard of the thread system. While run is what the
84 * user gets to override, and everything said about it is true, this is
85 * the function that actually makes up the thread, it simply calls the
86 * run member function in an OO-friendly way. This is what allows us to
87 * use member variables from within the thread itself.
88 *@param Should always be this.
89 *@returns This is specified by posix, I'm not sure yet.
90 */
91 static void *threadRunner( void *pThread );
92
93 void yield();
94
95 };
96}
97
98#endif
diff --git a/src/itoatom.h b/src/itoatom.h
new file mode 100644
index 0000000..96090f2
--- /dev/null
+++ b/src/itoatom.h
@@ -0,0 +1,56 @@
1#ifndef ITO_QUEUE_H
2#define ITO_QUEUE_H
3
4#include <pthread.h>
5
6#include "itomutex.h"
7#include "itocondition.h"
8
9/**
10 * A thread-safe wrapper class.
11 *@author Mike Buland
12 */
13template <class T>
14class ItoAtom
15{
16public:
17 /**
18 * Construct an empty queue.
19 */
20 ItoAtom()
21 {
22 }
23
24 ItoAtom( const T &src ) :
25 data( src )
26 {
27 }
28
29 ~ItoQueue()
30 {
31 }
32
33 T get()
34 {
35 mOperate.lock();
36 mOperate.unlock();
37 return data;
38 }
39
40 void set( const T &val )
41 {
42 mOperate.lock();
43 data = val;
44 cBlock.signal();
45 mOperate.unlock();
46 }
47
48private:
49 Item *pStart; /**< The start of the queue, the next element to dequeue. */
50 Item *pEnd; /**< The end of the queue, the last element to dequeue. */
51
52 ItoMutex mOperate; /**< The master mutex, used on all operations. */
53 ItoCondition cBlock; /**< The condition for blocking dequeues. */
54};
55
56#endif
diff --git a/src/itocondition.cpp b/src/itocondition.cpp
new file mode 100644
index 0000000..d8f5375
--- /dev/null
+++ b/src/itocondition.cpp
@@ -0,0 +1,42 @@
1#include <sys/time.h>
2
3#include "itocondition.h"
4
5Bu::ItoCondition::ItoCondition()
6{
7 pthread_cond_init( &cond, NULL );
8}
9
10Bu::ItoCondition::~ItoCondition()
11{
12 pthread_cond_destroy( &cond );
13}
14
15int Bu::ItoCondition::wait()
16{
17 return pthread_cond_wait( &cond, &mutex );
18}
19
20int Bu::ItoCondition::wait( int nSec, int nUSec )
21{
22 struct timeval now;
23 struct timespec timeout;
24 struct timezone tz;
25
26 gettimeofday( &now, &tz );
27 timeout.tv_sec = now.tv_sec + nSec + ((now.tv_usec + nUSec)/1000000);
28 timeout.tv_nsec = ((now.tv_usec + nUSec)%1000000)*1000;
29
30 return pthread_cond_timedwait( &cond, &mutex, &timeout );
31}
32
33int Bu::ItoCondition::signal()
34{
35 return pthread_cond_signal( &cond );
36}
37
38int Bu::ItoCondition::broadcast()
39{
40 return pthread_cond_broadcast( &cond );
41}
42
diff --git a/src/itocondition.h b/src/itocondition.h
new file mode 100644
index 0000000..4771b22
--- /dev/null
+++ b/src/itocondition.h
@@ -0,0 +1,81 @@
1#ifndef ITO_CONDITION_H
2#define ITO_CONDITION_H
3
4#include <pthread.h>
5
6#include "itomutex.h"
7
8namespace Bu
9{
10 /**
11 * Ito condition. This is a fairly simple condition mechanism. As you may
12 * notice this class inherits from the ItoMutex class, this is because all
13 * conditions must be within a locked block. The standard usage of a condition
14 * is to pause one thread, perhaps indefinately, until another thread signals
15 * that it is alright to procede.
16 * <br>
17 * Standard usage for the thread that wants to wait is as follows:
18 * <pre>
19 * ItoCondition cond;
20 * ... // Perform setup and enter your run loop
21 * cond.lock();
22 * while( !isFinished() ) // Could be anything you're waiting for
23 * cond.wait();
24 * ... // Take care of what you have to.
25 * cond.unlock();
26 * </pre>
27 * The usage for the triggering thread is much simpler, when it needs to tell
28 * the others that it's time to grab some data it calls either signal or
29 * broadcast. See both of those functions for the difference.
30 *@author Mike Buland
31 */
32 class ItoCondition : public ItoMutex
33 {
34 public:
35 /**
36 * Create a condition.
37 */
38 ItoCondition();
39
40 /**
41 * Destroy a condition.
42 */
43 ~ItoCondition();
44
45 /**
46 * Wait forever, or until signalled. This has to be called from within a
47 * locked section, i.e. before calling this this object's lock function
48 * should be called.
49 */
50 int wait();
51
52 /**
53 * Wait for a maximum of nSec seconds and nUSec micro-seconds or until
54 * signalled. This is a little more friendly function if you want to
55 * perform other operations in the thrad loop that calls this function.
56 * Like the other wait function, this must be inside a locked section.
57 *@param nSec The seconds to wait.
58 *@param nUSec the micro-seconds to wait.
59 */
60 int wait( int nSec, int nUSec );
61
62 /**
63 * Notify the next thread waiting on this condition that they can go ahead.
64 * This only signals one thread, the next one in the condition queue, that
65 * it is safe to procede with whatever operation was being waited on.
66 */
67 int signal();
68
69 /**
70 * Notify all threads waiting on this condition that they can go ahead now.
71 * This function is slower than signal, but more effective in certain
72 * situations where you may not know how many threads should be activated.
73 */
74 int broadcast();
75
76 private:
77 pthread_cond_t cond; /**< Internal condition reference. */
78 };
79}
80
81#endif
diff --git a/src/itomutex.cpp b/src/itomutex.cpp
new file mode 100644
index 0000000..dc51af9
--- /dev/null
+++ b/src/itomutex.cpp
@@ -0,0 +1,27 @@
1#include "itomutex.h"
2
3Bu::ItoMutex::ItoMutex()
4{
5 pthread_mutex_init( &mutex, NULL );
6}
7
8Bu::ItoMutex::~ItoMutex()
9{
10 pthread_mutex_destroy( &mutex );
11}
12
13int Bu::ItoMutex::lock()
14{
15 return pthread_mutex_lock( &mutex );
16}
17
18int Bu::ItoMutex::unlock()
19{
20 return pthread_mutex_unlock( &mutex );
21}
22
23int Bu::ItoMutex::trylock()
24{
25 return pthread_mutex_trylock( &mutex );
26}
27
diff --git a/src/itomutex.h b/src/itomutex.h
new file mode 100644
index 0000000..80956b8
--- /dev/null
+++ b/src/itomutex.h
@@ -0,0 +1,61 @@
1#ifndef ITO_MUTEX_H
2#define ITO_MUTEX_H
3
4#include <pthread.h>
5
6namespace Bu
7{
8 /**
9 * Simple mutex wrapper. Currently this doesn't do anything extra for you
10 * except keep all of the functionality together in an OO sorta' way and keep
11 * you from having to worry about cleaning up your mutexes properly, or initing
12 * them.
13 *@author Mike Buland
14 */
15 class ItoMutex
16 {
17 public:
18 /**
19 * Create an unlocked mutex.
20 */
21 ItoMutex();
22
23 /**
24 * Destroy a mutex. This can only be done when a mutex is unlocked.
25 * Failure to unlock before destroying a mutex object could cause it to
26 * wait for the mutex to unlock, the odds of which are usually farily low
27 * at deconstruction time.
28 */
29 ~ItoMutex();
30
31 /**
32 * Lock the mutex. This causes all future calls to lock on this instance
33 * of mutex to block until the first thread that called mutex unlocks it.
34 * At that point the next thread that called lock will get a chance to go
35 * to work. Because of the nature of a mutex lock it is a very bad idea to
36 * do any kind of serious or rather time consuming computation within a
37 * locked section. This can cause thread-deadlock and your program may
38 * hang.
39 */
40 int lock();
41
42 /**
43 * Unlock the mutex. This allows the next thread that asked for a lock to
44 * lock the mutex and continue with execution.
45 */
46 int unlock();
47
48 /**
49 * Try to lock the mutex. This is the option to go with if you cannot avoid
50 * putting lengthy operations within a locked section. trylock will attempt
51 * to lock the mutex, if the mutex is already locked this function returns
52 * immediately with an error code.
53 */
54 int trylock();
55
56 protected:
57 pthread_mutex_t mutex; /**< The internal mutex reference. */
58 };
59}
60
61#endif
diff --git a/src/itoqueue.h b/src/itoqueue.h
new file mode 100644
index 0000000..322698d
--- /dev/null
+++ b/src/itoqueue.h
@@ -0,0 +1,231 @@
1#ifndef ITO_QUEUE_H
2#define ITO_QUEUE_H
3
4#include <pthread.h>
5
6#include "itomutex.h"
7#include "itocondition.h"
8
9namespace Bu
10{
11 /**
12 * A thread-safe queue class. This class is a very simple queue with some cool
13 * extra functionality for use with the Ito system. The main extra that it
14 * provides is the option to either dequeue without blocking, with infinite
15 * blocking, or with timed blocking, which will return a value if something is
16 * enqueued within the specified time limit, or NULL if the time limit is
17 * exceded.
18 *@author Mike Buland
19 */
20 template <class T>
21 class ItoQueue
22 {
23 private:
24 /**
25 * Helper struct. Keeps track of linked-list items for the queue data.
26 */
27 typedef struct Item
28 {
29 T pData;
30 Item *pNext;
31 } Item;
32
33 public:
34 /**
35 * Construct an empty queue.
36 */
37 ItoQueue() :
38 pStart( NULL ),
39 pEnd( NULL ),
40 nSize( 0 )
41 {
42 }
43
44 /**
45 * Destroy the queue. This function will simply free all contained
46 * structures. If you stored pointers in the queue, this will lose the
47 * pointers without cleaning up the memory they pointed to. Make sure
48 * you're queue is empty before allowing it to be destroyed!
49 */
50 ~ItoQueue()
51 {
52 Item *pCur = pStart;
53 while( pCur )
54 {
55 Item *pTmp = pCur->pNext;
56 delete pCur;
57 pCur = pTmp;
58 }
59 }
60
61 /**
62 * Enqueue a pieces of data. The new data will go at the end of the queue,
63 * and unless another piece of data is enqueued, will be the last piece of
64 * data to be dequeued.
65 *@param pData The data to enqueue. If this is not a primitive data type
66 * it's probably best to use a pointer type.
67 */
68 void enqueue( T pData )
69 {
70 mOperate.lock();
71
72 if( pStart == NULL )
73 {
74 pStart = pEnd = new Item;
75 pStart->pData = pData;
76 pStart->pNext = NULL;
77 nSize++;
78 }
79 else
80 {
81 pEnd->pNext = new Item;
82 pEnd = pEnd->pNext;
83 pEnd->pData = pData;
84 pEnd->pNext = NULL;
85 nSize++;
86 }
87
88 cBlock.signal();
89
90 mOperate.unlock();
91 }
92
93 /**
94 * Dequeue the first item from the queue. This function can operate in two
95 * different modes, blocking and non-blocking. In non-blocking mode it will
96 * return immediately weather there was data in the queue or not. If there
97 * was data it will remove it from the queue and return it to the caller.
98 * In blocking mode it will block forever wating for data to be enqueued.
99 * When data finally is enqueued this function will return immediately with
100 * the new data. The only way this function should ever return a null in
101 * blocking mode is if the calling thread was cancelled. It's probably a
102 * good idea to check for NULL return values even if you use blocking, just
103 * to be on the safe side.
104 *@param bBlock Set to true to enable blocking, leave as false to work in
105 * non-blocking mode.
106 *@returns The next piece of data in the queue, or NULL if no data was in
107 * the queue.
108 */
109 T dequeue( bool bBlock=false )
110 {
111 mOperate.lock();
112 if( pStart == NULL )
113 {
114 mOperate.unlock();
115
116 if( bBlock )
117 {
118 cBlock.lock();
119
120 while( pStart == NULL )
121 cBlock.wait();
122
123 T tmp = dequeue( false );
124
125 cBlock.unlock();
126 return tmp;
127
128 }
129
130 return NULL;
131 }
132 else
133 {
134 T pTmp = pStart->pData;
135 Item *pDel = pStart;
136 pStart = pStart->pNext;
137 delete pDel;
138 nSize--;
139
140 mOperate.unlock();
141 return pTmp;
142 }
143 }
144
145 /**
146 * Operates just like the other dequeue function in blocking mode with one
147 * twist. This function will block for at most nSec seconds and nUSec
148 * micro-seconds. If the timer is up and no data is available, this will
149 * just return NULL. If data is enqueued before the timeout expires, it
150 * will dequeue and exit immediately.
151 *@param nSec The number of seconds to wait, max.
152 *@param nUSec The number of micro-seconds to wait, max.
153 *@returns The next piece of data in the queue, or NULL if the timeout was
154 * exceeded.
155 */
156 T dequeue( int nSec, int nUSec )
157 {
158 mOperate.lock();
159 if( pStart == NULL )
160 {
161 mOperate.unlock();
162
163 cBlock.lock();
164
165 cBlock.wait( nSec, nUSec );
166
167 if( pStart == NULL )
168 {
169 cBlock.unlock();
170 return NULL;
171 }
172
173 mOperate.lock();
174 T pTmp = pStart->pData;
175 Item *pDel = pStart;
176 pStart = pStart->pNext;
177 delete pDel;
178 nSize--;
179 mOperate.unlock();
180
181 cBlock.unlock();
182 return pTmp;
183 }
184 else
185 {
186 T pTmp = pStart->pData;
187 Item *pDel = pStart;
188 pStart = pStart->pNext;
189 delete pDel;
190 nSize--;
191
192 mOperate.unlock();
193 return pTmp;
194 }
195 }
196
197 /**
198 * Checks to see if the queue has data in it or not. Note that there is no
199 * function to determine the length of the queue. This data isn't kept
200 * track of. If you really need to know, fix this.
201 *@returns True if the queue is empty, false if it has data in it.
202 */
203 bool isEmpty()
204 {
205 mOperate.lock();
206 bool bEmpty = (pStart == NULL );
207 mOperate.unlock();
208
209 return bEmpty;
210 }
211
212 long getSize()
213 {
214 mOperate.lock();
215 long nRet = nSize;
216 mOperate.unlock();
217
218 return nRet;
219 }
220
221 private:
222 Item *pStart; /**< The start of the queue, the next element to dequeue. */
223 Item *pEnd; /**< The end of the queue, the last element to dequeue. */
224 long nSize; /**< The number of items in the queue. */
225
226 ItoMutex mOperate; /**< The master mutex, used on all operations. */
227 ItoCondition cBlock; /**< The condition for blocking dequeues. */
228 };
229}
230
231#endif
diff --git a/src/main.dox b/src/main.dox
new file mode 100644
index 0000000..668d2e3
--- /dev/null
+++ b/src/main.dox
@@ -0,0 +1,14 @@
1/**
2 *@mainpage libbu++ utility library
3 *
4 *@section secIntro Introduction
5 *
6 * Libbu++ is a C++ library of general utility classes and functions. They
7 * cover a wide range of topics from streams and sockets to data structures to
8 * data serialization and xml handling to threading.
9 *
10 */
11
12/**
13 *@namespace Bu The core libbu++ namespace, to ensure things don't get muddied.
14 */
diff --git a/src/serversocket.cpp b/src/serversocket.cpp
new file mode 100644
index 0000000..c53c80d
--- /dev/null
+++ b/src/serversocket.cpp
@@ -0,0 +1,148 @@
1#include <time.h>
2#include <string.h>
3#include <stdio.h>
4#include <errno.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <sys/types.h>
8#include <sys/socket.h>
9#include <termios.h>
10#include <netinet/in.h>
11#include <netdb.h>
12#include <arpa/inet.h>
13#include <fcntl.h>
14#include "serversocket.h"
15#include "exceptions.h"
16
17Bu::ServerSocket::ServerSocket( int nPort, int nPoolSize ) :
18 nPort( nPort )
19{
20 /* Create the socket and set it up to accept connections. */
21 struct sockaddr_in name;
22
23 /* Give the socket a name. */
24 name.sin_family = AF_INET;
25 name.sin_port = htons( nPort );
26
27 // I think this specifies who we will accept connections from,
28 // a good thing to make configurable later on
29 name.sin_addr.s_addr = htonl( INADDR_ANY );
30
31 startServer( name, nPoolSize );
32}
33
34Bu::ServerSocket::ServerSocket(const FString &sAddr,int nPort, int nPoolSize) :
35 nPort( nPort )
36{
37 /* Create the socket and set it up to accept connections. */
38 struct sockaddr_in name;
39
40 /* Give the socket a name. */
41 name.sin_family = AF_INET;
42 name.sin_port = htons( nPort );
43
44 inet_aton( sAddr.getStr(), &name.sin_addr );
45
46 startServer( name, nPoolSize );
47}
48
49Bu::ServerSocket::~ServerSocket()
50{
51}
52
53void Bu::ServerSocket::startServer( struct sockaddr_in &name, int nPoolSize )
54{
55 /* Create the socket. */
56 nServer = socket( PF_INET, SOCK_STREAM, 0 );
57 if( nServer < 0 )
58 {
59 throw Bu::SocketException("Couldn't create a listen socket.");
60 }
61
62 int opt = 1;
63 setsockopt(
64 nServer,
65 SOL_SOCKET,
66 SO_REUSEADDR,
67 (char *)&opt,
68 sizeof( opt )
69 );
70
71 if( bind( nServer, (struct sockaddr *) &name, sizeof(name) ) < 0 )
72 {
73 throw Bu::SocketException("Couldn't bind to the listen socket.");
74 }
75
76 if( listen( nServer, nPoolSize ) < 0 )
77 {
78 throw Bu::SocketException(
79 "Couldn't begin listening to the server socket."
80 );
81 }
82
83 FD_ZERO( &fdActive );
84 /* Initialize the set of active sockets. */
85 FD_SET( nServer, &fdActive );
86}
87
88int Bu::ServerSocket::accept( int nTimeoutSec, int nTimeoutUSec )
89{
90 fd_set fdRead = fdActive;
91
92 struct timeval xT;
93
94 xT.tv_sec = nTimeoutSec;
95 xT.tv_usec = nTimeoutUSec;
96
97 if( TEMP_FAILURE_RETRY(select( nServer+1, &fdRead, NULL, NULL, &xT )) < 0 )
98 {
99 throw SocketException(
100 "Error scanning for new connections: %s", strerror( errno )
101 );
102 }
103
104 if( FD_ISSET( nServer, &fdRead ) )
105 {
106 struct sockaddr_in clientname;
107 size_t size;
108 int nClient;
109
110 size = sizeof( clientname );
111#ifdef __CYGWIN__
112 nClient = ::accept( nServer, (struct sockaddr *)&clientname,
113 (int *)&size
114 );
115#else
116 nClient = ::accept( nServer, (struct sockaddr *)&clientname, &size );
117#endif
118 if( nClient < 0 )
119 {
120 throw SocketException(
121 "Error accepting a new connection: %s", strerror( errno )
122 );
123 }
124 char tmpa[20];
125 inet_ntop( AF_INET, (void *)&clientname.sin_addr, tmpa, 20 );
126 //"New connection from host %s, port %hd.",
127 // tmpa, ntohs (clientname.sin_port) );
128
129 {
130 int flags;
131
132 flags = fcntl( nClient, F_GETFL, 0 );
133 flags |= O_NONBLOCK;
134 if( fcntl( nClient, F_SETFL, flags ) < 0)
135 {
136 throw SocketException(
137 "Error setting option on client socket: %s",
138 strerror( errno )
139 );
140 }
141 }
142
143 return nClient;
144 }
145
146 return -1;
147}
148
diff --git a/src/serversocket.h b/src/serversocket.h
new file mode 100644
index 0000000..9a26e2d
--- /dev/null
+++ b/src/serversocket.h
@@ -0,0 +1,31 @@
1#ifndef SERVER_SOCKET_H
2#define SERVER_SOCKET_H
3
4#include <stdint.h>
5#include "fstring.h"
6#include "socket.h"
7
8namespace Bu
9{
10 /**
11 *
12 */
13 class ServerSocket
14 {
15 public:
16 ServerSocket( int nPort, int nPoolSize=40 );
17 ServerSocket( const FString &sAddr, int nPort, int nPoolSize=40 );
18 virtual ~ServerSocket();
19
20 int accept( int nTimeoutSec, int nTimeoutUSec );
21
22 private:
23 void startServer( struct sockaddr_in &name, int nPoolSize );
24
25 fd_set fdActive;
26 int nServer;
27 int nPort;
28 };
29}
30
31#endif
diff --git a/src/socket.cpp b/src/socket.cpp
index c4f914b..455b5c8 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -118,6 +118,7 @@ void Bu::Socket::close()
118 //} 118 //}
119} 119}
120 120
121/*
121void Bu::Socket::read() 122void Bu::Socket::read()
122{ 123{
123 char buffer[RBS]; 124 char buffer[RBS];
@@ -132,7 +133,6 @@ void Bu::Socket::read()
132 if( nbytes < 0 && errno != 0 && errno != EAGAIN ) 133 if( nbytes < 0 && errno != 0 && errno != EAGAIN )
133 { 134 {
134 //printf("errno: %d, %s\n", errno, strerror( errno ) ); 135 //printf("errno: %d, %s\n", errno, strerror( errno ) );
135 /* Read error. */
136 //perror("readInput"); 136 //perror("readInput");
137 throw ConnectionException( 137 throw ConnectionException(
138 excodeReadError, 138 excodeReadError,
@@ -146,15 +146,13 @@ void Bu::Socket::read()
146 break; 146 break;
147 nTotalRead += nbytes; 147 nTotalRead += nbytes;
148 sReadBuf.append( buffer, nbytes ); 148 sReadBuf.append( buffer, nbytes );
149 /* Data read. */
150 if( nbytes < RBS ) 149 if( nbytes < RBS )
151 { 150 {
152 break; 151 break;
153 } 152 }
154 153
155 /* New test, if data is divisible by RBS bytes on some libs the 154 // New test, if data is divisible by RBS bytes on some libs the
156 * read could block, this keeps it from happening. 155 // read could block, this keeps it from happening.
157 */
158 { 156 {
159 fd_set rfds; 157 fd_set rfds;
160 FD_ZERO(&rfds); 158 FD_ZERO(&rfds);
@@ -171,13 +169,7 @@ void Bu::Socket::read()
171 } 169 }
172 } 170 }
173 } 171 }
174 172}*/
175 /*
176 if( pProtocol != NULL && nTotalRead > 0 )
177 {
178 pProtocol->onNewData();
179 }*/
180}
181 173
182size_t Bu::Socket::read( void *pBuf, size_t nBytes ) 174size_t Bu::Socket::read( void *pBuf, size_t nBytes )
183{ 175{
diff --git a/src/socket.h b/src/socket.h
index 3d0125d..568cad6 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -19,7 +19,7 @@ namespace Bu
19 virtual ~Socket(); 19 virtual ~Socket();
20 20
21 virtual void close(); 21 virtual void close();
22 virtual void read(); 22 //virtual void read();
23 virtual size_t read( void *pBuf, size_t nBytes ); 23 virtual size_t read( void *pBuf, size_t nBytes );
24 virtual size_t write( const void *pBuf, size_t nBytes ); 24 virtual size_t write( const void *pBuf, size_t nBytes );
25 25
@@ -33,6 +33,8 @@ namespace Bu
33 virtual bool canWrite(); 33 virtual bool canWrite();
34 virtual bool canSeek(); 34 virtual bool canSeek();
35 35
36
37
36 private: 38 private:
37 int nSocket; 39 int nSocket;
38 bool bActive; 40 bool bActive;
diff --git a/src/tests/itoqueue1.cpp b/src/tests/itoqueue1.cpp
new file mode 100644
index 0000000..f73f4d3
--- /dev/null
+++ b/src/tests/itoqueue1.cpp
@@ -0,0 +1,110 @@
1#include <string>
2#include "bu/ito.h"
3#include "bu/itoqueue.h"
4
5class Reader : public Bu::Ito
6{
7public:
8 Reader( Bu::ItoQueue<std::string *> &q, int id ) :
9 q( q ),
10 id( id )
11 {
12 }
13
14 void *run()
15 {
16 for( int i = 0; i < 10; i++ )
17 {
18 std::string *pStr = q.dequeue( true );
19 if( pStr == NULL )
20 {
21 printf("Null received...\n");
22 }
23 else
24 {
25 printf("[%d] read: %s\n", id, pStr->c_str() );
26 delete pStr;
27 }
28 usleep( (int)(((double)rand())/((double)RAND_MAX)*2000000.0) );
29 }
30
31 return NULL;
32 }
33
34private:
35 Bu::ItoQueue<std::string *> &q;
36 int id;
37};
38
39class Writer : public Bu::Ito
40{
41public:
42 Writer( Bu::ItoQueue<std::string *> &q, int id, const char *strbase ) :
43 q( q ),
44 strbase( strbase ),
45 id( id )
46 {
47 }
48
49 void *run()
50 {
51 for( int i = 0; i < 11; i++ )
52 {
53 usleep( (int)(((double)rand())/((double)RAND_MAX)*2000000.0) );
54 q.enqueue( new std::string( strbase ) );
55 printf("[%d] write: %s\n", id, strbase );
56 }
57
58 return NULL;
59 }
60
61private:
62 Bu::ItoQueue<std::string *> &q;
63 const char *strbase;
64 int id;
65};
66
67int main()
68{
69 Writer *wr[5];
70 Reader *rd[5];
71 const char bob[][7]={
72 {"Test 1"},
73 {"Test 2"},
74 {"Test 3"},
75 {"Test 4"},
76 {"Test 5"}
77 };
78
79 Bu::ItoQueue<std::string *> q;
80
81 for( int j = 0; j < 5; j++ )
82 {
83 wr[j] = new Writer( q, j, bob[j] );
84 rd[j] = new Reader( q, j );
85 }
86
87 for( int j = 0; j < 5; j++ )
88 {
89 rd[j]->start();
90 }
91
92 for( int j = 0; j < 5; j++ )
93 {
94 wr[j]->start();
95 }
96
97 for( int j = 0; j < 5; j++ )
98 {
99 rd[j]->join();
100 }
101
102 for( int j = 0; j < 5; j++ )
103 {
104 delete wr[j];
105 delete rd[j];
106 }
107
108 return 0;
109}
110
diff --git a/src/tests/itoqueue2.cpp b/src/tests/itoqueue2.cpp
new file mode 100644
index 0000000..f4b5e19
--- /dev/null
+++ b/src/tests/itoqueue2.cpp
@@ -0,0 +1,83 @@
1#include <string>
2#include "bu/ito.h"
3#include "bu/itoqueue.h"
4#include <errno.h>
5
6class Reader : public Bu::Ito
7{
8public:
9 Reader( Bu::ItoQueue<std::string *> &q, int id ) :
10 q( q ),
11 id( id )
12 {
13 }
14
15 void *run()
16 {
17 for( int i = 0; i < 10; i++ )
18 {
19 std::string *pStr = q.dequeue( 0, 500000 );
20 if( pStr == NULL )
21 {
22 printf("Null received...\n");
23 i--;
24 }
25 else
26 {
27 printf("[%d] read: %s\n", id, pStr->c_str() );
28 delete pStr;
29 }
30 }
31
32 return NULL;
33 }
34
35private:
36 Bu::ItoQueue<std::string *> &q;
37 int id;
38};
39
40class Writer : public Bu::Ito
41{
42public:
43 Writer( Bu::ItoQueue<std::string *> &q, int id, const char *strbase ) :
44 q( q ),
45 strbase( strbase ),
46 id( id )
47 {
48 }
49
50 void *run()
51 {
52 for( int i = 0; i < 11; i++ )
53 {
54 sleep( 2 );
55 printf("[%d] write: %s\n", id, strbase );
56 q.enqueue( new std::string( strbase ) );
57 }
58
59 return NULL;
60 }
61
62private:
63 Bu::ItoQueue<std::string *> &q;
64 const char *strbase;
65 int id;
66};
67
68int main()
69{
70 printf("ETIMEDOUT: %d\n", ETIMEDOUT );
71 Bu::ItoQueue<std::string *> q;
72 Writer wr( q, 0, "writer" );
73 Reader rd( q, 0 );
74
75 rd.start();
76 wr.start();
77
78 rd.join();
79 wr.join();
80
81 return 0;
82}
83