diff options
Diffstat (limited to '')
-rw-r--r-- | Doxyfile | 4 | ||||
-rw-r--r-- | build.conf | 26 | ||||
-rwxr-xr-x | mkincs.sh | 5 | ||||
-rw-r--r-- | src/exceptions.cpp | 1 | ||||
-rw-r--r-- | src/exceptions.h | 1 | ||||
-rw-r--r-- | src/ito.cpp | 40 | ||||
-rw-r--r-- | src/ito.h | 98 | ||||
-rw-r--r-- | src/itoatom.h | 56 | ||||
-rw-r--r-- | src/itocondition.cpp | 42 | ||||
-rw-r--r-- | src/itocondition.h | 81 | ||||
-rw-r--r-- | src/itomutex.cpp | 27 | ||||
-rw-r--r-- | src/itomutex.h | 61 | ||||
-rw-r--r-- | src/itoqueue.h | 231 | ||||
-rw-r--r-- | src/main.dox | 14 | ||||
-rw-r--r-- | src/serversocket.cpp | 148 | ||||
-rw-r--r-- | src/serversocket.h | 31 | ||||
-rw-r--r-- | src/socket.cpp | 16 | ||||
-rw-r--r-- | src/socket.h | 4 | ||||
-rw-r--r-- | src/tests/itoqueue1.cpp | 110 | ||||
-rw-r--r-- | src/tests/itoqueue2.cpp | 83 |
20 files changed, 1059 insertions, 20 deletions
@@ -74,9 +74,9 @@ QUIET = NO | |||
74 | WARNINGS = YES | 74 | WARNINGS = YES |
75 | WARN_IF_UNDOCUMENTED = YES | 75 | WARN_IF_UNDOCUMENTED = YES |
76 | WARN_IF_DOC_ERROR = YES | 76 | WARN_IF_DOC_ERROR = YES |
77 | WARN_NO_PARAMDOC = NO | 77 | WARN_NO_PARAMDOC = YES |
78 | WARN_FORMAT = "$file:$line: $text" | 78 | WARN_FORMAT = "$file:$line: $text" |
79 | WARN_LOGFILE = | 79 | WARN_LOGFILE = Doxywarn |
80 | #--------------------------------------------------------------------------- | 80 | #--------------------------------------------------------------------------- |
81 | # configuration options related to the input files | 81 | # configuration options related to the input files |
82 | #--------------------------------------------------------------------------- | 82 | #--------------------------------------------------------------------------- |
@@ -1,13 +1,17 @@ | |||
1 | # This is a build file for libbu++ | 1 | # This is a build file for libbu++ |
2 | 2 | ||
3 | default action: check "libbu++.a" | 3 | default 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 | ||
9 | set "CXXFLAGS" += "-ggdb -Wall" | 7 | set "CXXFLAGS" += "-ggdb -Wall" |
10 | 8 | ||
9 | filesIn("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" | |||
17 | directoriesIn("src/tests","tests/"): | 21 | directoriesIn("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/"): | |||
25 | filesIn("src/tests") filter regexp("^src/tests/(.*)\\.cpp$", "tests/{re:1}"): | 30 | filesIn("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 | |||
33 | directoriesIn("src/unit","unit/"): | 41 | directoriesIn("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/"): | |||
41 | filesIn("src/unit") filter regexp("^src/unit/(.*)\\.cpp$", "unit/{re:1}"): | 50 | filesIn("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 | |||
77 | rule "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 | |||
3 | cd src/bu | ||
4 | rm * | ||
5 | for 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 | |||
3 | Bu::Ito::Ito() | ||
4 | { | ||
5 | } | ||
6 | |||
7 | Bu::Ito::~Ito() | ||
8 | { | ||
9 | } | ||
10 | |||
11 | bool Bu::Ito::start() | ||
12 | { | ||
13 | nHandle = pthread_create( &ptHandle, NULL, threadRunner, this ); | ||
14 | |||
15 | return true; | ||
16 | } | ||
17 | |||
18 | bool Bu::Ito::stop() | ||
19 | { | ||
20 | pthread_exit( &ptHandle ); | ||
21 | |||
22 | return true; | ||
23 | } | ||
24 | |||
25 | void *Bu::Ito::threadRunner( void *pThread ) | ||
26 | { | ||
27 | return ((Ito *)pThread)->run(); | ||
28 | } | ||
29 | |||
30 | bool Bu::Ito::join() | ||
31 | { | ||
32 | pthread_join( ptHandle, NULL ); | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | void 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 | |||
6 | namespace 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 | */ | ||
13 | template <class T> | ||
14 | class ItoAtom | ||
15 | { | ||
16 | public: | ||
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 | |||
48 | private: | ||
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 | |||
5 | Bu::ItoCondition::ItoCondition() | ||
6 | { | ||
7 | pthread_cond_init( &cond, NULL ); | ||
8 | } | ||
9 | |||
10 | Bu::ItoCondition::~ItoCondition() | ||
11 | { | ||
12 | pthread_cond_destroy( &cond ); | ||
13 | } | ||
14 | |||
15 | int Bu::ItoCondition::wait() | ||
16 | { | ||
17 | return pthread_cond_wait( &cond, &mutex ); | ||
18 | } | ||
19 | |||
20 | int 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 | |||
33 | int Bu::ItoCondition::signal() | ||
34 | { | ||
35 | return pthread_cond_signal( &cond ); | ||
36 | } | ||
37 | |||
38 | int 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 | |||
8 | namespace 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 | |||
3 | Bu::ItoMutex::ItoMutex() | ||
4 | { | ||
5 | pthread_mutex_init( &mutex, NULL ); | ||
6 | } | ||
7 | |||
8 | Bu::ItoMutex::~ItoMutex() | ||
9 | { | ||
10 | pthread_mutex_destroy( &mutex ); | ||
11 | } | ||
12 | |||
13 | int Bu::ItoMutex::lock() | ||
14 | { | ||
15 | return pthread_mutex_lock( &mutex ); | ||
16 | } | ||
17 | |||
18 | int Bu::ItoMutex::unlock() | ||
19 | { | ||
20 | return pthread_mutex_unlock( &mutex ); | ||
21 | } | ||
22 | |||
23 | int 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 | |||
6 | namespace 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 | |||
9 | namespace 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 | |||
17 | Bu::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 | |||
34 | Bu::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 | |||
49 | Bu::ServerSocket::~ServerSocket() | ||
50 | { | ||
51 | } | ||
52 | |||
53 | void 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 | |||
88 | int 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 | |||
8 | namespace 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 | /* | ||
121 | void Bu::Socket::read() | 122 | void 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 | ||
182 | size_t Bu::Socket::read( void *pBuf, size_t nBytes ) | 174 | size_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 | |||
5 | class Reader : public Bu::Ito | ||
6 | { | ||
7 | public: | ||
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 | |||
34 | private: | ||
35 | Bu::ItoQueue<std::string *> &q; | ||
36 | int id; | ||
37 | }; | ||
38 | |||
39 | class Writer : public Bu::Ito | ||
40 | { | ||
41 | public: | ||
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 | |||
61 | private: | ||
62 | Bu::ItoQueue<std::string *> &q; | ||
63 | const char *strbase; | ||
64 | int id; | ||
65 | }; | ||
66 | |||
67 | int 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 | |||
6 | class Reader : public Bu::Ito | ||
7 | { | ||
8 | public: | ||
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 | |||
35 | private: | ||
36 | Bu::ItoQueue<std::string *> &q; | ||
37 | int id; | ||
38 | }; | ||
39 | |||
40 | class Writer : public Bu::Ito | ||
41 | { | ||
42 | public: | ||
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 | |||
62 | private: | ||
63 | Bu::ItoQueue<std::string *> &q; | ||
64 | const char *strbase; | ||
65 | int id; | ||
66 | }; | ||
67 | |||
68 | int 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 | |||