nrtb-core team mailing list archive
-
nrtb-core team
-
Mailing list archive
-
Message #00208
[Merge] lp:~fpstovall/nrtb/cpp_common into lp:nrtb
Rick Stovall has proposed merging lp:~fpstovall/nrtb/cpp_common into lp:nrtb.
Requested reviews:
DougPiranha (dougpirahna): code
Related bugs:
Bug #723272 in New Real Time Battle: "Todo: Review the NRTB C++ common classes"
https://bugs.launchpad.net/nrtb/+bug/723272
Bug #723277 in New Real Time Battle: "Todo: Create an archive for common lib"
https://bugs.launchpad.net/nrtb/+bug/723277
Bug #723503 in New Real Time Battle: "Todo: triad template lacks an automatic unit test."
https://bugs.launchpad.net/nrtb/+bug/723503
Bug #813860 in New Real Time Battle: "Todo: common_rl needs a unit test created."
https://bugs.launchpad.net/nrtb/+bug/813860
Bug #814217 in New Real Time Battle: "Todo: Add dot_product and vector_product methods to triad.h"
https://bugs.launchpad.net/nrtb/+bug/814217
Bug #814392 in New Real Time Battle: "Todo: C++ GPB build process"
https://bugs.launchpad.net/nrtb/+bug/814392
Bug #815503 in New Real Time Battle: "Todo: C++ unit tests for GPB classes"
https://bugs.launchpad.net/nrtb/+bug/815503
Bug #815525 in New Real Time Battle: "Todo: Implement the transceiver class."
https://bugs.launchpad.net/nrtb/+bug/815525
Bug #825614 in New Real Time Battle: "TODO: Port ricklibIII threads and sockets classes to NRTB"
https://bugs.launchpad.net/nrtb/+bug/825614
For more details, see:
https://code.launchpad.net/~fpstovall/nrtb/cpp_common/+merge/72827
I believe the expectations for all the C++ common libraries for the Alpha phase have been met at this time. This branch is ready for code review for merge to the alpha main branch.
--
https://code.launchpad.net/~fpstovall/nrtb/cpp_common/+merge/72827
Your team NRTB Core is subscribed to branch lp:nrtb.
=== modified file 'GPB_proto/ack_nak.proto'
--- GPB_proto/ack_nak.proto 2010-01-14 02:24:25 +0000
+++ GPB_proto/ack_nak.proto 2011-08-25 04:57:16 +0000
@@ -9,6 +9,9 @@
// in a channel wrapper message.
// return on of these to ack the recept of each error free message.
+
+package nrtb_msg;
+
message message_ack {
required uint32 msg_uid = 1;
}
=== modified file 'GPB_proto/physics_common.proto'
--- GPB_proto/physics_common.proto 2010-01-14 02:24:25 +0000
+++ GPB_proto/physics_common.proto 2011-08-25 04:57:16 +0000
@@ -11,6 +11,8 @@
// meters for distance, meters/sec for velocity, radians for attitude,
// and radians/sec for rotational velocity.
+package nrtb_msg;
+
message triplet {
required double x = 1;
required double y = 2;
=== modified file 'GPB_proto/sim_obj_tq_update.proto'
--- GPB_proto/sim_obj_tq_update.proto 2010-01-14 02:24:25 +0000
+++ GPB_proto/sim_obj_tq_update.proto 2011-08-25 04:57:16 +0000
@@ -8,6 +8,8 @@
// Note: these are never sent bare.. they are always payloads
// in a channel wrapper message.
+package nrtb_msg;
+
import "physics_common.proto";
// This message contains all the information required to report the
=== modified file 'GPB_proto/sim_to_db_wrapper.proto'
--- GPB_proto/sim_to_db_wrapper.proto 2010-01-14 02:24:25 +0000
+++ GPB_proto/sim_to_db_wrapper.proto 2011-08-25 04:57:16 +0000
@@ -7,6 +7,8 @@
// Note: The message defined here is a "channel wrapper", a container for
// all messages from the simulation engine to the data broker.
+package nrtb_msg;
+
import "ack_nak.proto";
import "sim_obj_tq_update.proto";
=== added directory 'common/GPB'
=== added file 'common/GPB/Makefile'
--- common/GPB/Makefile 1970-01-01 00:00:00 +0000
+++ common/GPB/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,34 @@
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+proto=../../GPB_proto
+
+lib: lib/nrtb_gpb.a
+ @echo "GPB lib build complete."
+
+lib/nrtb_gpb.a:
+ @protoc -I=${proto} --cpp_out=cpp_src/ ${proto}/*.proto
+ @cd obj; for file in ../cpp_src/*.cc; do g++ -c $$file; done
+ @cp -v cpp_src/*h ../include
+ @ar -r ../lib/nrtb_gpb.a obj/*.o
+
+clean:
+ @cd cpp_src; for file in *h; do rm -vf ../../include/$$file; done
+ @rm -vf ../lib/nrtb_gpb.a
+ @rm -f obj/* cpp_src/*
+ @echo "GPB cleanup complete."
=== added directory 'common/GPB/cpp_src'
=== added directory 'common/GPB/obj'
=== modified file 'common/Makefile'
--- common/Makefile 2010-01-14 02:24:25 +0000
+++ common/Makefile 2011-08-25 04:57:16 +0000
@@ -1,6 +1,27 @@
-lib:
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: ./lib/nrtb_common.a
+
+./lib/nrtb_common.a:
@echo "============= building common libs ==============="
@make action=lib doit
+ @ar -r ./lib/nrtb_common.a ./obj/*.o
@echo "============= common libs complete ==============="
modules:
@@ -11,12 +32,18 @@
clean:
@echo "============= cleaning common libs ==============="
@make action=clean doit
- @rm -fv ./obj/*
+ @rm -fv ./obj/* ./lib/*
@echo "========== common lib cleanup complete ==========="
doit:
+ @cd common_rl; make ${action}
@cd point; make ${action}
@cd timer; make ${action}
- @cd work_queue_thread; make ${action}
+ @cd threads; make ${action}
+ @cd sockets; make ${action}
+ @cd serializer; make ${action}
@cd singleton; make ${action}
- @cd plugin_loader; make ${action}
+ @cd logger; make ${action}
+ @cd confreader; make ${action}
+ @cd GPB; make ${action}
+ @cd transceiver; make ${action}
=== added directory 'common/common_rl'
=== added file 'common/common_rl/Makefile'
--- common/common_rl/Makefile 1970-01-01 00:00:00 +0000
+++ common/common_rl/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,36 @@
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: common_rl_test
+ @./common_rl_test
+ @cp -v common.h ../include
+ @cp -v common.o ../obj
+ @echo build complete
+
+common.o: common.h common.cpp Makefile
+ @rm -f common.o
+ g++ -c -O3 common.cpp
+
+common_rl_test: common.o common_rl_test.cpp
+ @rm -f common_rl_test
+ g++ -c common_rl_test.cpp
+ g++ -o common_rl_test common_rl_test.o common.o
+
+clean:
+ @rm -rvf *.o ../include/common.h ../obj/common.o common_rl_test
+ @echo all objects and executables have been erased.
=== added file 'common/common_rl/common.cpp'
--- common/common_rl/common.cpp 1970-01-01 00:00:00 +0000
+++ common/common_rl/common.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,274 @@
+/***********************************************
+ T his file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include <stdlib.h>
+#include <iostream>
+#include <math.h>
+#include <time.h>
+#include "common.h"
+
+using namespace std;
+
+namespace nrtb
+{
+
+base_exception::base_exception()
+{
+ ctime = time(NULL);
+};
+
+base_exception::base_exception(const string & text)
+{
+ ctime = time(NULL);
+ _text = text;
+};
+
+void base_exception::store(const string & s)
+{
+ _text = s;
+};
+
+string base_exception::comment()
+{
+ return _text;
+};
+
+unsigned long int base_exception::creation_time()
+{
+ return ctime;
+};
+
+const string __ricks_handy_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const string __ricks_handy_lower = "abcdefghijklmnopqrstuvwxyz";
+
+string changecase(const string &s,const string &upper, const string &lower)
+{
+ try
+ {
+ string returnme = "";
+ for (int i = 0; i < s.size(); i++)
+ {
+ unsigned long int loc = lower.find(s[i]);
+ if (loc == string::npos)
+ {
+ returnme += s[i];
+ }
+ else
+ {
+ returnme += upper[loc];
+ };
+ };
+ return returnme;
+ }
+ catch (...)
+ {
+ throw translate_exception();
+ };
+};
+
+string upcase(const string &s)
+{
+ return changecase(s,__ricks_handy_upper,__ricks_handy_lower);
+};
+
+string downcase(const string &s)
+{
+ return changecase(s,__ricks_handy_lower,__ricks_handy_upper);
+};
+
+
+const string __ricks_handy_hexits = "0123456789ABCDEF";
+
+string gsub(const string & target, const string & findme, const string & putme)
+{
+ // error checking
+ if (findme.empty() || target.empty())
+ {
+ throw gsub_exception();
+ };
+ string returnme = "";
+ unsigned long int okay_through = 0;
+ unsigned long int step = findme.length();
+ unsigned long int where = target.find(findme,okay_through);
+ while (where != string::npos)
+ {
+ returnme += target.substr(okay_through,where-okay_through) + putme;
+ okay_through = where + step;
+ where = target.find(findme,okay_through);
+ };
+ returnme += target.substr(okay_through);
+ return returnme;
+};
+
+strlist split(const string & source, const char token)
+{
+ strlist returnme;
+ returnme.clear();
+ if (source.size() > 0)
+ {
+ unsigned long int loc;
+ unsigned long int processed = 0;
+ try
+ {
+ loc = source.find(token,processed);
+ while (loc != string::npos)
+ {
+ returnme.push_back(source.substr(processed,loc-processed));
+ processed = loc + 1;
+ loc = source.find(token,processed);
+ }
+ returnme.push_back(source.substr(processed));
+ }
+ catch (...)
+ {
+ throw split_exception();
+ };
+ };
+ return returnme;
+};
+
+string trim(std::string s)
+{
+ const string ws = "\t\n ";
+ unsigned long int where = s.find_first_not_of(ws);
+ if (where != string::npos)
+ {
+ s.erase(0,where);
+ };
+ where = s.find_last_not_of(ws);
+ if (where != string::npos)
+ {
+ s.erase(where+1);
+ };
+ return s;
+};
+
+string mconvert(const string &s)
+{
+ /* This function is designed to escape strings destined for
+ * use in SQL queries and the like. Specifically, the following
+ * is done to the string passed in:
+ * 1. "\" is replaced with "\\"
+ * 2. "'" is replaced with "\'"
+ * 3. """ (double quote) is replaced with "\""
+ * 4. 0x00 is replaced with "\"+0x00.
+ */
+ string returnme = "";
+ if (s.length() > 0)
+ {
+ returnme = gsub(s,"\\","\\\\");
+ returnme = gsub(returnme,"'", "\\'");
+ returnme = gsub(returnme,"\"","\\\"");
+ // special handling is required to make strings including
+ // 0x00; this is the kludge I found to work at 2200 tonight.
+ string findme = " "; findme[0] = 0;
+ returnme = gsub(returnme,findme,"\\0");
+ };
+ return returnme;
+};
+
+string dateflip(string date, const string & sep)
+{
+ string out;
+ try
+ {
+ strlist temp = split(date,'-');
+ out = temp[1]+ sep + temp[2] + sep + temp[0];
+ }
+ catch (...)
+ {
+ throw dateflip_exception();
+ };
+ return out;
+}; // string dateflip
+
+string http_hextochar(string s)
+{
+ try
+ {
+ s = upcase(s);
+ unsigned char v = (16 * __ricks_handy_hexits.find(s[0]))
+ + __ricks_handy_hexits.find(s[1]);
+ return s = v;
+ }
+ catch (...)
+ {
+ throw hextrans_exception();
+ };
+}; // string hextochar
+
+string http_chartohex(const string &s)
+{
+ string out;
+ try
+ {
+ for (long int i=0; i < s.length() ; i++ ) {
+ unsigned char v = s[i];
+ div_t hexval = div(v,16);
+ out += __ricks_handy_hexits[hexval.quot];
+ out += __ricks_handy_hexits[hexval.rem];
+ }; /* endfor */
+ }
+ catch (...)
+ {
+ throw hextrans_exception();
+ };
+ return out;
+};// string chartohex()
+
+string http_enhex(const string & s)
+{
+ string out;
+ try
+ {
+ for (long int i=0; i < s.length() ; i++ ) {
+ unsigned char v = s[i];
+ div_t hexval = div(v,16);
+ out += "%";
+ out += __ricks_handy_hexits[hexval.quot];
+ out += __ricks_handy_hexits[hexval.rem];
+ }; /* endfor */
+ }
+ catch (...)
+ {
+ throw hextrans_exception();
+ };
+ return out;
+}; // string enhex()
+
+string http_unhex(string s)
+{
+ try
+ {
+ while (s.find('%') != string::npos)
+ {
+ int where = s.find('%');
+ string hexchar = s.substr(where+1,2);
+ s.erase(where,3);
+ s.insert(where,http_hextochar(hexchar));
+ }; /* endwhile */
+ }
+ catch (...)
+ {
+ throw hextrans_exception();
+ };
+ return s;
+};// string unhex()
+
+
+} // namespace nrtb
=== added file 'common/common_rl/common.h'
--- common/common_rl/common.h 1970-01-01 00:00:00 +0000
+++ common/common_rl/common.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,171 @@
+/***********************************************
+ T his file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+// prevent multiple definitions
+#ifndef __ga_common_h
+#define __ga_common_h 1
+
+//#include <stream.h>
+#include <string>
+#include <map>
+#include <vector>
+
+namespace nrtb
+{
+
+// throwable exception classes
+
+/** Provides a base exception class for use in HCS developed programs.
+ **
+ ** Usage: Inherit your exception classes from this class. When throwing a
+ ** derived exception you'll have the option of either instanciating the
+ ** exception with a comment or not; in any case the time the exception was
+ ** instanciated will be recorded. Later, when catching the exception you can
+ ** get the time the exception was created and any text the thrower may have
+ ** provided.
+ **/
+ class base_exception: public std::exception
+ {
+ protected:
+ unsigned long int ctime;
+ std::string _text;
+ public:
+ /** Default constructor.
+ **
+ ** Creates an nrtb_exception recording it's creation time but without
+ ** a comment string.
+ **/
+ base_exception();
+ /** Constructs with a comment string.
+ **
+ ** Creates an nrtb_exception recording it's creation time and storing the
+ ** provided string "text" for recall later via the comment method.
+ **
+ ** This version takes an ISO standard C++ string.
+ **/
+ base_exception(const std::string & text);
+ /// NOP virtual distructor for safe inheritance
+ virtual ~base_exception() throw() {};
+ /** Stores a comment string.
+ **/
+ void store(const std::string & s);
+ /** Returns the value stored at exception creation.
+ **/
+ std::string comment();
+ /** Returns the unix time the exception was created.
+ **/
+ unsigned long int creation_time();
+};
+
+// Thrown by gsub() in cases of unexpected error.
+class gsub_exception: public nrtb::base_exception {};
+// Thrown by split() in cases of unexpected error.
+class split_exception: public nrtb::base_exception {};
+// Thrown by dateflip in cases of unexpected error.
+class dateflip_exception: public nrtb::base_exception {};
+// Thrown by upcase() and downcase() in cases of unexpected error.
+class translate_exception: public nrtb::base_exception {};
+// Thrown by http_* functions in cases of unexpected error.
+class hextrans_exception: public nrtb::base_exception {};
+// Thrown by the biased_rand* functions in cases of unexpected error.
+class rand_exception: public nrtb::base_exception {};
+
+// handy commonly used maps
+/// A map of ints keyed by ints.
+typedef std::map<int,int> num_map;
+/// A map of ints keyed by strings.
+typedef std::map<std::string,int> string_map;
+
+/// the return type for split.
+typedef std::vector<std::string> strlist;
+
+/** Converts the alpha components of s to all upper case.
+ **
+ ** May throw a nrtb::translate_exception if there is a problem.
+ **/
+std::string upcase(const std::string &s);
+
+/** Converts the alpha components of s to all lower case.
+ **
+ ** May throw a nrtb::translate_exception if there is a problem.
+ **/
+std::string downcase(const std::string &s);
+
+/** Replaces all instances of findme in target with putme.
+ **
+ ** May throw a nrtb::gsub_exception if there is a problem.
+ **/
+std::string gsub(const std::string & target, const std::string & findme,
+ const std::string & putme);
+
+/** Splits the string source using token as a delimiter and returns
+ ** the results in vector of strings.
+ **
+ ** May throw a nrtb::split_exception if there is a problem.
+ **/
+strlist split(const std::string & source, const char token);
+
+/// Performs SQL escaping on the string.
+std::string mconvert(const std::string & s);
+
+/// Removes white space from both ends of a string.
+std::string trim(std::string s);
+
+/** Takes an ISO standard date (YYYY-MM-DD) and returns MM-DD-YYYY.
+ **
+ ** May throw a nrtb::dateflip_exception if there is a problem.
+ **/
+std::string dateflip(std::string date, const std::string & sep="-");
+
+/** Takes a string representing one HTTP hex-encoded byte and returns
+ ** the appropriate character. This will only operate on the first two
+ ** bytes of s, and will throw a nrtb_hextrans_exception if less
+ ** than two bytes are supplied.
+ **
+ ** May throw a nrtb::hextrans_exception if there is a problem.
+ **/
+std::string http_hextochar(std::string s);
+
+/** Takes a string and returns it hex-encoded (i.e. " " returns "20").
+ **
+ ** This function is somewhat misnamed due to hysterical reasons. In
+ ** truth it simply returns the hex equivilent of the input. Use
+ ** http_enhex() when you actually need valid HTTP hex encoding.
+ ** On the other hand, this function is quite
+ ** useful if you need to hex up a series of bytes and don't need the
+ ** overhead of the extra byte per character of true HTTP hex encoding.
+ **
+ ** May throw a nrtb::hextrans_exception if there is a problem.
+*/
+std::string http_chartohex(const std::string &s);
+
+/** Takes a string and returns it in HTTP hex-encoded format (" " returns "%20").
+ **
+ ** May throw a nrtb::hextrans_exception if there is a problem.
+ **/
+std::string http_enhex(const std::string &s);
+
+/** Takes a string and returns it with all HTTP hex-encoded bytes changed to
+ ** their normal single byte representation.
+ **
+ ** May throw a nrtb::hextrans_exception if there is a problem.
+ **/
+std::string http_unhex(std::string s);
+
+} // namespace nrtb
+#endif /* __ga_common_h */
=== added file 'common/common_rl/common_rl_test.cpp'
--- common/common_rl/common_rl_test.cpp 1970-01-01 00:00:00 +0000
+++ common/common_rl/common_rl_test.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,101 @@
+/***********************************************
+ T his file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "common.h"
+#include <iostream>
+
+using namespace std;
+
+int report_test(const string banner, bool test)
+{
+ string result;
+ int returnme = 0;
+ if (test)
+ {
+ result = "PASSED";
+ }
+ else
+ {
+ result = "FAILED";
+ returnme = 1;
+ };
+ cout << "\t" << banner << ": " << result << endl;
+ return returnme;
+};
+
+int main()
+{
+ cout << "=== Starting nrtb::common_rl unit test ===" << endl;
+ int returnme = 0;
+ string tstr = "MiXeDcAsE";
+
+ returnme += report_test(
+ "nrtb::upcase()",
+ nrtb::upcase(tstr) == "MIXEDCASE");
+
+ returnme += report_test(
+ "nrtb::downcase()",
+ nrtb::downcase(tstr) == "mixedcase");
+
+ returnme += report_test(
+ "nrtb::gsub()",
+ nrtb::gsub(tstr, "cAsE", " frogs") == "MiXeD frogs");
+
+ // split() testing
+ nrtb::strlist tokens = nrtb::split("this is a test",' ');
+ bool faults = tokens.size() == 4;
+ faults = faults or (tokens[0] == "this");
+ faults = faults or (tokens[0] == "is");
+ faults = faults or (tokens[0] == "a");
+ faults = faults or (tokens[0] == "test");
+ returnme += report_test(
+ "nrtb::split()",
+ faults);
+
+ string tchar = " "; tchar[0] = 0;
+ returnme += report_test(
+ "nrtb::mconvert()",
+ nrtb::mconvert("\\ \" '"+tchar) == "\\\\ \\\" \\\'\\0");
+
+ returnme += report_test(
+ "nrtb::trim()",
+ nrtb::trim("\t"+tstr+" ") == tstr);
+
+ returnme += report_test(
+ "nrtb::dateflip()",
+ nrtb::dateflip("2011-07-21") == "07-21-2011");
+
+ returnme += report_test(
+ "nrtb::http_hextochar()",
+ nrtb::http_hextochar("20") == " ");
+
+ returnme += report_test(
+ "nrtb::http_chartohex()",
+ nrtb::http_chartohex(tstr) == "4D6958654463417345");
+
+ returnme += report_test(
+ "nrtb::http_enhex()",
+ nrtb::http_enhex(tstr) == "%4D%69%58%65%44%63%41%73%45");
+
+ returnme += report_test(
+ "nrtb::http_unhex()",
+ nrtb::http_unhex("%4D%69%58%65%44%63%41%73%45") == tstr);
+
+ cout << "=== nrtb::common_rl unit test complete ===" << endl;
+ return returnme;
+};
\ No newline at end of file
=== added directory 'common/confreader'
=== added file 'common/confreader/Makefile'
--- common/confreader/Makefile 1970-01-01 00:00:00 +0000
+++ common/confreader/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,38 @@
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: conftest
+ @rm -f conf_test.log
+ @./conftest test=1 test2-2 test2=71.837486 test3="jack danials" --doit
+ @grep "Run Complete" conf_test.log
+ @cp -v confreader.h ../include
+ @cp -v confreader.o ../obj
+ @echo build complete
+
+confreader.o: confreader.h confreader.cpp Makefile
+ @rm -f confreader.o
+ g++ -c confreader.cpp -I../include
+
+conftest: confreader.o conftest.cpp
+ @rm -f conftest
+ g++ -c conftest.cpp -I../include
+ g++ -o conftest conftest.o confreader.o ../obj/common.o ../obj/log_setup.o ../obj/base_thread.o -lpthread -lPocoFoundation -lPocoUtil
+
+clean:
+ @rm -rvf *.o conftest ../include/confreader.h ../obj/confreader.o conf_test.log
+ @echo all objects and executables have been erased.
=== added file 'common/confreader/confreader.cpp'
--- common/confreader/confreader.cpp 1970-01-01 00:00:00 +0000
+++ common/confreader/confreader.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,226 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+**********************************************/
+
+// see confreader.h for documentation
+
+#include "confreader.h"
+#include <fstream>
+#include <iostream>
+#include <common.h>
+#include "Poco/Logger.h"
+
+using namespace std;
+using namespace nrtb;
+
+const std::string logname = "conf_reader";
+
+namespace nrtb
+{
+
+conf_reader::conf_reader()
+{
+ Poco::Logger& logger = Poco::Logger::get(logname);
+ logger.information("conf_reader instanciated.");
+};
+
+conf_reader::~conf_reader() {};
+
+unsigned int conf_reader::read(const std::string & _filename, bool _clear)
+{
+ Poco::Logger& logger = Poco::Logger::get(logname);
+ logger.information("Reading from \"" + _filename + "\".");
+ if (_filename != "") { filename = _filename; };
+ if (filename != "")
+ {
+ try
+ {
+ ifstream cfile(filename.c_str());
+ if (!cfile) throw base_exception();
+ if (_clear) values.clear();
+ // read the file line by line, ignoring comments.
+ char inbuff[16*1024];
+ while (cfile)
+ {
+ cfile.getline(inbuff, 16*1024);
+ string in = inbuff;
+ // truncate at the start of a comment.
+ unsigned long int where = in.find("#");
+ while (where != string::npos)
+ {
+ if (in[where-1] == '\\')
+ {
+ // comment char was escaped.. ignore.
+ where = in.find("#",where+1);
+ }
+ else
+ {
+ // truncate the string at this point.
+ in.erase(where);
+ where = string::npos;
+ };
+ };
+ // see if we can parse what's left.
+ in = trim(in);
+ unsigned long int split_point = in.find_first_of("\t ");
+ if (split_point != string::npos)
+ {
+ // okay.. get the fields.
+ pair arg;
+ arg.first = gsub(trim(in.substr(0,split_point)),"\\#","#");
+ arg.second = gsub(trim(in.substr(split_point)),"\\#","#");
+ // is this an include directive?
+ if (arg.first == "*INCLUDE")
+ {
+ read(arg.second,false);
+ }
+ else if (arg.first != "")
+ {
+ values.insert(arg);
+ };
+ };
+ };
+ }
+ catch (...)
+ {
+ logger.warning("Problems reading configuration file \""
+ + filename + "\"; data may be incomplete.");
+ }
+ };
+ return values.size();
+};
+
+unsigned int conf_reader::read(int argc, char * argv[],
+ const string & _filename)
+{
+ Poco::Logger& logger = Poco::Logger::get(logname);
+ logger.information("Reading from command line.");
+ clear();
+ filename = _filename;
+ value_list_type cvars;
+ // read values from the command line first.
+ for (int i = 0; i < argc; i++)
+ {
+ string instring = argv[i];
+ if (i == 0)
+ {
+ instring = "__exec_name="+instring;
+ };
+ strlist t = split(instring,'=');
+ if (t.size() > 0)
+ {
+ // build the new insert pair;
+ pair newval;
+ newval.first = t[0];
+ // assemble the value
+ // (allows for including "=" in the argument)
+ for (unsigned int l = 1; l < t.size(); l++)
+ {
+ newval.second += t[l];
+ if (l < (t.size() -1) )
+ {
+ newval.second += "=";
+ };
+ };
+ // store this in the list
+ trim(newval.first);
+ trim(newval.second);
+ cvars.insert(newval);
+ // is this a config file name?
+ if (newval.first == "configfile")
+ {
+ filename = newval.second;
+ };
+ };
+ }; // read the command line arguments.
+ // read the file args if any.
+ read(filename,false);
+ // override the first instance of any value found in configs
+ // or insert as appropriate.
+ iterator c = cvars.begin();
+ iterator e = cvars.end();
+ while (c != e)
+ {
+ iterator here = values.find(c->first);
+ if (here != values.end())
+ {
+ here->second = c->second;
+ }
+ else
+ {
+ values.insert(*c);
+ };
+ c++;
+ };
+ std::stringstream message;
+ message << "Read " << values.size() << " parameters.";
+ logger.information(message.str());
+ return values.size();
+};
+
+void conf_reader::clear()
+{
+ values.clear();
+};
+
+conf_reader::iterator conf_reader::begin()
+{
+ return values.begin();
+};
+
+conf_reader::iterator conf_reader::end()
+{
+ return values.end();
+};
+
+bool conf_reader::empty()
+{
+ return values.empty();
+};
+
+unsigned int conf_reader::size()
+{
+ return values.size();
+};
+
+strlist conf_reader::all(const std::string & key)
+{
+ strlist returnme;
+ iterator current = values.find(key);
+ iterator e = values.end();
+ while ((current != e) && (current->first == key))
+ {
+ returnme.push_back(current->second);
+ current++;
+ };
+ return returnme;
+};
+
+string conf_reader::operator [] (const std::string & key)
+{
+ iterator res = values.find(key);
+ string returnme = "";
+ if (res != values.end()) returnme = res->second;
+ return returnme;
+};
+
+bool conf_reader::exists(const std::string & key)
+{
+ return (values.find(key) != values.end());
+};
+
+} // namespace nrtb
=== added file 'common/confreader/confreader.h'
--- common/confreader/confreader.h 1970-01-01 00:00:00 +0000
+++ common/confreader/confreader.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,287 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef ricklib_confreader_h
+#define ricklib_confreader_h
+
+#include <string.h>
+#include <map>
+#include <vector>
+#include <string>
+#include <fstream>
+#include <boost/lexical_cast.hpp>
+#include <singleton.h>
+
+namespace nrtb
+{
+
+typedef std::vector<std::string> strlist;
+
+/** Reads command line and configuration file information.
+ **
+ ** For this NRTB implementation, this class is implemented as
+ ** a singleton.
+ **
+ ** Values read are stored in a multimap of name/value pairs and
+ ** may be accessed via provided iterators, or directly via the
+ ** [] operator, all(), get<T>() and getall<T>() methods. Automatic
+ ** handling of include files and comments is provided as well. The
+ ** get<>() and getall<>() template methods provide easy and complete
+ ** access to all types that have the ">>" stream operator
+ ** defined, including all the C++ standard types.
+ **/
+class conf_reader
+{
+ private:
+ typedef std::multimap<std::string,std::string> value_list_type;
+ value_list_type values;
+ std::string filename;
+ protected:
+ /** Reads and parses a configuration file.
+ **
+ ** Returns the number of values stored.
+ **
+ ** The optional parameter _clear when true clears the currently
+ ** stored list of values. if false the new values read will be added
+ ** to the existing list.
+ **
+ ** If the file named can not be processed completely, a warning
+ ** message is published to cerr.
+ **
+ ** Configuration files are defined as follows.
+ **
+ ** 1. Files are read line by line. No value can cross a newline
+ ** (and at least in this version) or include one. The good
+ ** news is that you are allowed up to 16k for each line.
+ **
+ ** 2. values are specifed in name/value pairs, one per line, with
+ ** the name first, followed by whitespace, followed by the
+ ** value. Values may include whitespace. All leading and trailing
+ ** whitespace is removed from both the name and the value.
+ **
+ ** 3. Comments start with a \# symbol; all text following a \# is
+ ** ignored. If you need to use \# in a name or value, escape
+ ** it with a backslash instead.
+ **
+ ** 4. Duplicate names are allowed.
+ **
+ ** 5. Names without values will be stored with "" as the value.
+ **
+ ** 6. A configuration file may include another configuration file
+ ** automatically by the use of the "*INCLUDE" reserved name.
+ ** When this name is found, it's value is used as the name of
+ ** the new file to be read.
+ **/
+ unsigned int read(const std::string & _filename = "", bool _clear=true);
+ public:
+ /// conf_reader iterator, points to a conf_reader::pair
+ typedef value_list_type::iterator iterator;
+ /// conf_reader::pair is a std::pair<string,string>
+ typedef std::pair<std::string,std::string> pair;
+ /** No argument constructor; actually calls read() without a filename.
+ **/
+ conf_reader();
+ /** NOP virtual destructor to allow safe inheritance.
+ **/
+ virtual ~conf_reader();
+ /** Reads and parses the command line and the provided file.
+ **
+ ** Returns the number of values stored. argc and argv are, of
+ ** course the same names you used as arguments to main(). Therefore
+ ** you can easily read all command line and configuration file
+ ** values like this:
+ **
+ ** int main(int argc, char* argv[])
+ **
+ ** {
+ **
+ ** conf_reader config;
+ **
+ ** config.read(argc,argv,"my_conf_file");
+ **
+ ** ...
+ **
+ ** }
+ **
+ ** See read(filename,_clear) for a discription of how configuration
+ ** files are structured and parsed. This method unconditionally
+ ** clears the existing value list before starting. For command line
+ ** arguments the following rules are true.
+ **
+ ** 1. The executable name (argv[0]) is stored as a value with the
+ ** name "__exec_name".
+ **
+ ** 2. Command line arguments of the form "name=value" are parsed
+ ** stored as named values.
+ **
+ ** 3. All other command line arguments are stored as names with
+ ** value = "".
+ **
+ ** 4. A command line argument of form "configfile=filename" will
+ ** override the filename supplied as an argument to this method.
+ **
+ ** 5. In the case of duplicate command line arguments, the last one
+ ** specified wins.
+ **
+ ** 6. In the case of names in the configuration file duplicating
+ ** names from the command line, the values from the command line
+ ** dominate. If there were multiple values for a given name
+ ** specified in the file, only the first one (the one returned by
+ ** the "[]" operator or get<>() method) is overridden.
+ **/
+ unsigned int read(int argc, char* argv[],
+ const std::string & _filename);
+ /// clears the name/value list.
+ void clear();
+ /// returns an iterator to the first item in the list.
+ iterator begin();
+ /// returns an iterator one past the end of the list.
+ iterator end();
+ /// True if there are no values, false otherwise.
+ bool empty();
+ /// Returns the number of values stored.
+ unsigned int size();
+ /** Returns the string value matching the supplied name.
+ **
+ ** NOTE: the use of get<T>(key) or get<T>(key,default) is preferred.
+ ** This method is public to allow for specialized handling in the
+ ** rare cases where it may be required.
+ **
+ ** If there are no matching names, "" is returned. Be aware that
+ ** "" is a valid value, so you can not use this to verify that
+ ** a given name was defined. Use exists() for that.
+ **/
+ std::string operator [] (const std::string & key);
+ /** Returns all values associated with the supplied name.
+ **
+ ** NOTE: the use of getall<T>(key) is preferred. This method is
+ ** public to allow specialized handling in rare case where it
+ ** may be required.
+ **
+ ** If there are no values defined the strlist will be empty.
+ **/
+ strlist all(const std::string & key);
+ /// True if the name exists, false otherwise.
+ bool exists(const std::string & key);
+ /** Use this to get all matching values.
+ **
+ ** Usage:
+ **
+ ** vector<type> mylist = conf_reader_object.getall<type>(key);
+ **
+ ** type can be any standard type (string, int, double, etc.) or any
+ ** type for which the ">>" stream operator is defined. All values
+ ** with matching names that can map to the requested type will be
+ ** returned. Any that do not map will not be returned.
+ **/
+ template < class T >
+ typename std::vector<T> getall(const std::string & key);
+ /** Use this to get the matching value.
+ **
+ ** Useage:
+ **
+ ** type myvar = get<type>(key);
+ **
+ ** type can be any standard type (string, int, double, etc.) or any
+ ** type for which the ">>" stream operator is defined. The return
+ ** value is initialized to all zeros if no matching name is found or
+ ** if the first value with that name does not map to the requested
+ ** type. For the numeric types that results in zero being returned,
+ ** but be aware that more complex object may be in non-sensible
+ ** states if they were not found or could not map. Use exists() to
+ ** verify the existance of a given name if needed.
+ **/
+ template < class T >
+ T get(const std::string & key);
+ /** Returns the value for the requested key, or the supplied default.
+ **
+ ** Works exactly like get<>() with the exception that if the key
+ ** is not found in the list, the user supplied default value is
+ ** returned instead of 0 or an empty string. If the value exists but
+ ** can not map to the requested type, 0 or an empty string is
+ ** returned for the standard types.
+ **/
+ template <class T>
+ T get(const std::string & key, const T & def);
+};
+
+typedef singleton<conf_reader> global_conf_reader;
+
+template < class T >
+ typename std::vector<T> conf_reader::getall(const std::string & key)
+{
+ strlist tvals = all(key);
+ std::vector<T> returnme;
+ if (typeid(T) == typeid(std::string))
+ {
+ // T is a std::string.. we can do this quickly.
+ strlist * wl = (strlist *) &returnme;
+ *wl = tvals;
+ }
+ else
+ {
+ // T is non-string.. will require more playing around.
+ unsigned int limit = tvals.size();
+ for (unsigned int i = 0; i < limit; i++)
+ {
+ try
+ {
+ returnme.push_back(boost::lexical_cast<T>(tvals[i]));
+ }
+ catch (...) {};
+ };
+ };
+ return returnme;
+};
+
+template < class T >
+ T conf_reader::get(const std::string & key)
+{
+ conf_reader & me = *this;
+ std::string tval = me[key];
+ T returnme;
+ // initialize the return value to nulls
+ // Needed for the numeric types, but bad for strings.
+ if (typeid(T) != typeid(std::string))
+ {
+ // null out the working area (death for strings!)
+ memset(&returnme,0,sizeof(T));
+ };
+ // This does appear to work for all the standard types.
+ if (tval != "")
+ {
+ try
+ {
+ returnme = boost::lexical_cast<T>(tval);
+ }
+ catch (...) {};
+ };
+ return returnme;
+};
+
+template < class T >
+ T conf_reader::get(const std::string & key, const T & def)
+{
+ if (exists(key)) { return get<T>(key); }
+ else { return def; };
+};
+
+}; // namespace nrtb
+
+#endif // ricklib_confreader_h
+
=== added file 'common/confreader/conftest.cpp'
--- common/confreader/conftest.cpp 1970-01-01 00:00:00 +0000
+++ common/confreader/conftest.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,129 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+/* confreader test program */
+
+#include "confreader.h"
+#include <log_setup.h>
+#include <iostream>
+#include "Poco/Logger.h"
+#include "Poco/SimpleFileChannel.h"
+//#include "Poco/AutoPtr.h"
+
+using namespace nrtb;
+using namespace std;
+
+int main(int argc, char* argv[])
+{
+ bool set_if_failed = false;
+ setup_global_logging("conf_test.log");
+ Poco::Logger & log = Poco::Logger::get("conftest");
+ log.information("=-=-=-=-=-= conftest Init =-=-=-=-=-=-=");
+ global_conf_reader & config = global_conf_reader::get_instance();
+ try
+ {
+ log.information("Starting read");
+ config.read(argc,argv,"test.config");
+ }
+ catch (...)
+ {
+ set_if_failed = true;
+ cerr << "Failed reading the configuration." << endl;
+ };
+ if (config.size() != 12)
+ {
+ set_if_failed = true;
+ cerr << "Did not find 12 parameters." << endl;
+ };
+ // iterator test
+ try
+ {
+ conf_reader::iterator c = config.begin();
+ conf_reader::iterator e = config.end();
+ while (c != e)
+ {
+ cout << "\t\"" << c->first << "\"=\"" << c->second
+ << "\"" << endl;
+ c++;
+ };
+ }
+ catch (...)
+ {
+ set_if_failed = true;
+ cerr << "Iterator test failed." << endl;
+ };
+ // template test.
+ int test = config.get<int>("test",-1);
+ int test2 = config.get<int>("test2",-1);
+ string test3 = config.get<string>("test3","not specified");
+ double test4 = config.get<double>("test",-1);
+ double test5 = config.get<double>("test2",-1);
+ cout << "(int) test = " << test
+ << "\n(int) test2 = " << test2
+ << "\n(string) test3 = \"" << test3 << "\""
+ << "\n(double) test = " << test4
+ << "\n(double) test2 = " << test5
+ << endl;
+ if (
+ (test != 1) or (test2 != 0)
+ or (test3 != "jack danials")
+ or (test4 != 1.0) or (test5 != 71.837486)
+ )
+ {
+ set_if_failed = true;
+ cerr << "** Template test failed." << endl;
+ };
+ // exists test.
+ cout << "?var \"--doit\" exists? "
+ << (config.exists("--doit") ? "Yes" : "No")
+ << endl;
+ if (!config.exists("--doit"))
+ {
+ set_if_failed = true;
+ cerr << "exists() test failed." << endl;
+ };
+ vector<int> intlist = config.getall<int>("test");
+ cout << "valid int \"test\" values:" << endl;
+ for (unsigned int i=0; i < intlist.size(); i++)
+ {
+ cout << "\t" << i << ": " << intlist[i] << endl;
+ };
+ if (intlist.size() != 2)
+ {
+ set_if_failed = true;
+ cerr << "getall<int>() did not find 2 parameters." << endl;
+ };
+ strlist strings = config.getall<string>("test");
+ cout << "valid string \"test\" values:" << endl;
+ for (unsigned int i=0; i < strings.size(); i++)
+ {
+ cout << "\t" << i << ": " << strings[i] << endl;
+ };
+ if (strings.size() != 3)
+ {
+ set_if_failed = true;
+ cerr << "getall<string>() did not find 3 parameters." << endl;
+ };
+ if (set_if_failed)
+ {
+ cerr << "** ntrb::conf_reader UNIT TEST FAILED. **" << endl;
+ log.fatal("UNIT TEST FAILED");
+ };
+ log.information("Run Complete");
+ return set_if_failed;
+};
=== added file 'common/confreader/test.config'
--- common/confreader/test.config 1970-01-01 00:00:00 +0000
+++ common/confreader/test.config 2011-08-25 04:57:16 +0000
@@ -0,0 +1,27 @@
+###### config reader test file ######
+# use this command line to properly test this:
+#
+# ./conftest test=1 test2=2 test2=71.837486 test3="jack danials" --doit
+#
+# You should get a warning about problems reading dummyfile, and 12
+# variables listed. "test" should be 2 overridden by the command line.
+# You can test the ability to override the config file name with the
+# command:
+#
+# ./conftest configfile=dummy
+#
+# It should complain about problems reading dummy, and no variables
+# should be populated.
+#
+
+fileversion version 1.0
+hashtest \#surrounded\# \#by\# \#hashes\#
+test nonsense # should be overridden by commmand line
+test duh! # what happens here?
+\#starthash should start with hash (\#) symbol
+test 21 # should only be seen in getall<int>();
+test3 Working line.
+
+*INCLUDE dummyfile
+#*INCLUDE ../../salesman/tests/bc_bench.config
+lastline this should be complete
=== added directory 'common/lib'
=== added directory 'common/logger'
=== added file 'common/logger/Makefile'
--- common/logger/Makefile 1970-01-01 00:00:00 +0000
+++ common/logger/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,42 @@
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: log_test
+ @echo "Testing..."
+ @rm -f test_output.log
+ @./log_test
+ @grep "Program run complete." test_output.log
+ @cp -fv log_setup.h ../include
+ @cp -fv log_setup.o ../obj
+
+
+log_setup.o: log_setup.h log_setup.cpp Makefile
+ @rm -f log_setup.o
+ g++ -c log_setup.cpp -I ../include
+
+log_test: log_setup.o log_test.cpp Makefile
+ @rm -vf log_test
+ g++ -c log_test.cpp -I../include
+ g++ -o log_test log_setup.o log_test.o -lPocoFoundation
+# g++ -g common_test.cpp -idirafter . -o common_test
+
+clean:
+ @rm -vf *.o log_test ../include/log_setup.h ../obj/log_setup.o test_output.log
+
+
+
=== added file 'common/logger/log_setup.cpp'
--- common/logger/log_setup.cpp 1970-01-01 00:00:00 +0000
+++ common/logger/log_setup.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,43 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "log_setup.h"
+
+#include "Poco/SimpleFileChannel.h"
+#include "Poco/FormattingChannel.h"
+#include "Poco/PatternFormatter.h"
+#include "Poco/Logger.h"
+#include "Poco/AutoPtr.h"
+
+using Poco::SimpleFileChannel;
+using Poco::FormattingChannel;
+using Poco::PatternFormatter;
+using Poco::Logger;
+using Poco::AutoPtr;
+
+void nrtb::setup_global_logging(const std::string & logfilename)
+{
+ AutoPtr<SimpleFileChannel> pFile(new SimpleFileChannel);
+ pFile->setProperty("path", logfilename);
+ pFile->setProperty("rotation", "250 K");
+ AutoPtr<PatternFormatter> pPF(new PatternFormatter);
+ pPF->setProperty("pattern", "%Y-%m-%d %H:%M:%S [%s:%p] %t");
+ AutoPtr<FormattingChannel> pFC(new FormattingChannel(pPF, pFile));
+ Logger::root().setChannel(pFC);
+ Logger::root().notice("Logging system initialized");
+}
\ No newline at end of file
=== added file 'common/logger/log_setup.h'
--- common/logger/log_setup.h 1970-01-01 00:00:00 +0000
+++ common/logger/log_setup.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,30 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef logger_setup_h
+#define logger_setup_h
+
+#include <string>
+
+namespace nrtb
+{
+
+ void setup_global_logging(const std::string & logfilename);
+}
+
+#endif //logger_setup_h
\ No newline at end of file
=== added file 'common/logger/log_test.cpp'
--- common/logger/log_test.cpp 1970-01-01 00:00:00 +0000
+++ common/logger/log_test.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,46 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "log_setup.h"
+
+#include <string>
+#include <iostream>
+#include <Poco/Logger.h>
+#include "Poco/LogStream.h"
+
+using namespace std;
+
+int main()
+{
+ bool set_if_failed = false;
+ try
+ {
+ nrtb::setup_global_logging("test_output.log");
+ Poco::Logger & logger = Poco::Logger::get("log_test");
+ logger.notice("Logging should be set up now.");
+ Poco::LogStream log(logger);
+ log << "This message used the stream interface" << endl;
+ logger.notice("Program run complete.");
+ }
+ catch (...)
+ {
+ set_if_failed = true;
+ cout << "** UNIT TEST FAILED **" << endl;
+ };
+ return set_if_failed;
+}
=== removed directory 'common/plugin_loader'
=== removed file 'common/plugin_loader/Makefile'
--- common/plugin_loader/Makefile 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/Makefile 1970-01-01 00:00:00 +0000
@@ -1,29 +0,0 @@
-
-build: plugin_wrapper.o plugin_manager.o
- @echo build complete
-
-plugin_wrapper.o: plugin_wrapper.h plugin_wrapper.cpp Makefile
- @rm -f plugin_wrapper.o
- g++ -c -O3 plugin_wrapper.cpp -idirafter ../../include3
-# g++ -c -g plugin_wrapper.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
-
-plugin_manager.o: plugin_manager.h plugin_manager.cpp Makefile
- @rm -f plugin_manager.o
- g++ -c -O3 plugin_manager.cpp -idirafter ../../include3
-# g++ -c -g plugin_wrapper.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
-
-test: plugin_wrapper.o plugin_manager.o wrapper_test.cpp
- @rm -f plugin_test
- g++ -c plugin_test.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
-# g++ -c -g conftest.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
- g++ -o plugtest plugtest.o plugin_wrapper.o plugin_manager.o -l ricklib3 -L../../lib/
-
-clean:
- @rm -rvf *.o plugin_test ../../include3/plugin_wrapper.h ../../include3/plugin_manager.h
- ar -d ../../lib/libricklib3.a plugin_wrapper.o plugin_manager.o
- @echo all objects and executables have been erased.
-
-lib: test
- @ar -r ../../lib/libricklib3.a plugin_wrapper.o plugin_manager.o
- @echo the plugin_wrapper class has been added to the library
- @cp -fv plugin_wrapper.h plugin_manager.h ../../include3
=== removed directory 'common/plugin_loader/doc'
=== removed file 'common/plugin_loader/doc/plugins.xmi'
--- common/plugin_loader/doc/plugins.xmi 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/doc/plugins.xmi 1970-01-01 00:00:00 +0000
@@ -1,339 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XMI xmlns:UML="http://schema.omg.org/spec/UML/1.3" verified="false" timestamp="2008-07-12T19:11:33" xmi.version="1.2" >
- <XMI.header>
- <XMI.documentation>
- <XMI.exporter>umbrello uml modeller http://uml.sf.net</XMI.exporter>
- <XMI.exporterVersion>1.5.8</XMI.exporterVersion>
- <XMI.exporterEncoding>UnicodeUTF8</XMI.exporterEncoding>
- </XMI.documentation>
- <XMI.metamodel xmi.name="UML" href="UML.xml" xmi.version="1.3" />
- </XMI.header>
- <XMI.content>
- <UML:Model isSpecification="false" isLeaf="false" isRoot="false" xmi.id="m1" isAbstract="false" name="UML Model" >
- <UML:Namespace.ownedElement>
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="folder" isRoot="false" isAbstract="false" name="folder" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="datatype" isRoot="false" isAbstract="false" name="datatype" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="singleton<work_queue_thread<message>>" isRoot="false" isAbstract="false" name="singleton<work_queue_thread<message>>" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="singleton" isRoot="false" isAbstract="false" name="singleton" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="thread::run" isRoot="false" isAbstract="false" name="thread::run" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="simObject, ricklib::thread" isRoot="false" isAbstract="false" name="simObject, ricklib::thread" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="map<string,plugin_list>" isRoot="false" isAbstract="false" name="map<string,plugin_list>" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="map<string,plugins_by_name>" isRoot="false" isAbstract="false" name="map<string,plugins_by_name>" />
- <UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="triad<double>" isRoot="false" isAbstract="false" name="triad<double>" />
- <UML:Model stereotype="folder" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="Logical View" isRoot="false" isAbstract="false" name="Logical View" >
- <UML:Namespace.ownedElement>
- <UML:Package stereotype="folder" isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="Datatypes" isRoot="false" isAbstract="false" name="Datatypes" >
- <UML:Namespace.ownedElement>
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="Ffh4kk3Ca5c0" isRoot="false" isAbstract="false" name="int" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="dwxIyKY9DGUT" isRoot="false" isAbstract="false" name="char" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="80aYqgH4Lz85" isRoot="false" isAbstract="false" name="bool" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="hBHFFrFqnD9j" isRoot="false" isAbstract="false" name="float" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="VfqivJ93JiZ9" isRoot="false" isAbstract="false" name="double" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="OrDjoJmVT3kg" isRoot="false" isAbstract="false" name="short" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="syWd9dNeIDLC" isRoot="false" isAbstract="false" name="long" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="WSFnAnxYc8FQ" isRoot="false" isAbstract="false" name="unsigned int" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="Q1tt2bZuZ5Yg" isRoot="false" isAbstract="false" name="unsigned short" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="htJz4eBZ4PMF" isRoot="false" isAbstract="false" name="unsigned long" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="CHlmQ1Co03h3" isRoot="false" isAbstract="false" name="string" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="z2PlKwwnqyeq" isRoot="false" isAbstract="false" name="void *" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="FWxLRQq7CGim" isRoot="false" isAbstract="false" name="abstract_weapon *" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="81JjStCiP83q" isRoot="false" isAbstract="false" name="abstract_sensor *" />
- <UML:DataType stereotype="datatype" isSpecification="false" isLeaf="false" visibility="public" namespace="Datatypes" xmi.id="qUlL5Ja8j1rY" isRoot="false" isAbstract="false" name="abstract_chassis *" />
- </UML:Namespace.ownedElement>
- </UML:Package>
- <UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="225YrWUg6CUk" isRoot="false" isAbstract="true" name="plugin_wrapper" >
- <UML:GeneralizableElement.generalization>
- <UML:Generalization xmi.idref="vIs8sXpoyxA8" />
- </UML:GeneralizableElement.generalization>
- <UML:Classifier.feature>
- <UML:Attribute comment="The type of plugin represented here (i.e. weapon, sensor, etc.), must match the types defined for sim engine use." isSpecification="false" visibility="protected" xmi.id="0kYAfH74JA7p" type="CHlmQ1Co03h3" name="plugin_type" />
- <UML:Attribute comment="The version number of this plugin." isSpecification="false" visibility="protected" xmi.id="ToJfQEMPzsZc" type="hBHFFrFqnD9j" name="version" />
- <UML:Attribute comment="The name of this plugin." isSpecification="false" visibility="protected" xmi.id="9egRonGpPYT7" type="CHlmQ1Co03h3" name="plugin_name" />
- <UML:Attribute comment="The path of the shared library implementing this plugin." isSpecification="false" visibility="protected" xmi.id="ZRl1ZW499wAW" type="YMxgqxXjjUHD" name="path" />
- <UML:Attribute isSpecification="false" visibility="protected" xmi.id="wOLstKsOU3xd" type="ZSpIFcMrTgYw" name="factory_address" />
- <UML:Operation comment="Called to load the library." isSpecification="false" isLeaf="false" visibility="public" xmi.id="evlKlgpUQJmb" isRoot="false" isAbstract="false" isQuery="false" name="load" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter comment="The path of shared library file implementing the plugin." isSpecification="false" visibility="private" xmi.id="95QVtX2h9xRU" value="" type="YMxgqxXjjUHD" name="path" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Called to get the raw address to the factory... you'll need to cast it to the correct type." isSpecification="false" isLeaf="false" visibility="public" xmi.id="j7HBVbv71scs" isRoot="false" isAbstract="false" isQuery="false" name="plugin_wrapper" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter comment="The path to the library to be loaded." isSpecification="false" visibility="private" xmi.id="SOCOuP71SVVL" value="" type="YMxgqxXjjUHD" name="loadpath" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Returns the plugin_type for the contained plugin." isSpecification="false" isLeaf="false" visibility="public" xmi.id="gx1dfJPPtVf7" isRoot="false" isAbstract="false" isQuery="false" name="getType" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter kind="return" xmi.id="hG7NjAual1aw" type="CHlmQ1Co03h3" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Returns the version information for the contained plugin." isSpecification="false" isLeaf="false" visibility="public" xmi.id="lh0Se8lUfT5X" isRoot="false" isAbstract="false" isQuery="false" name="getVersion" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter kind="return" xmi.id="jxjLJCew2LSN" type="hBHFFrFqnD9j" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Returns the name of the plugin associated with this wrapper." isSpecification="false" isLeaf="false" visibility="public" xmi.id="fqiWLNb62bXe" isRoot="false" isAbstract="false" isQuery="false" name="getName" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter kind="return" xmi.id="g1RhawLICSft" type="CHlmQ1Co03h3" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Returns the full file path for the plugin loaded." isSpecification="false" isLeaf="false" visibility="public" xmi.id="EADACWaXDZwu" isRoot="false" isAbstract="false" isQuery="false" name="getPath" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter kind="return" xmi.id="9tDR3e4vEZMa" type="YMxgqxXjjUHD" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation isSpecification="false" isLeaf="false" visibility="public" xmi.id="n1z8iZxdRl3R" isRoot="false" isAbstract="false" isQuery="false" name="newInstance" />
- </UML:Classifier.feature>
- </UML:Class>
- <UML:Package isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="1dJfkTAS2RQE" isRoot="false" isAbstract="false" name="std" >
- <UML:Namespace.ownedElement/>
- </UML:Package>
- <UML:Package isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="rsozaUfviJVQ" isRoot="false" isAbstract="false" name="boost" >
- <UML:Namespace.ownedElement>
- <UML:Package isSpecification="false" isLeaf="false" visibility="public" namespace="rsozaUfviJVQ" xmi.id="hQQSxzuiyRF1" isRoot="false" isAbstract="false" name="filesystem" >
- <UML:Namespace.ownedElement>
- <UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="hQQSxzuiyRF1" xmi.id="YMxgqxXjjUHD" isRoot="false" isAbstract="false" name="path" />
- </UML:Namespace.ownedElement>
- </UML:Package>
- </UML:Namespace.ownedElement>
- </UML:Package>
- <UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="ZSpIFcMrTgYw" isRoot="false" isAbstract="false" name="factory_ptr" />
- <UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="5RxrSpb7LxuI" isRoot="false" isAbstract="false" name="weapon_plugin" >
- <UML:GeneralizableElement.generalization>
- <UML:Generalization xmi.idref="FgcyZa21VrTL" />
- </UML:GeneralizableElement.generalization>
- <UML:Classifier.feature>
- <UML:Operation comment="Allocates and constructs the abstract_weapon descendent and returns a smart pointer to the new object." isSpecification="false" isLeaf="false" visibility="public" xmi.id="WStI1XSjbrPt" isRoot="false" isAbstract="false" isQuery="false" name="newWeapon" />
- </UML:Classifier.feature>
- </UML:Class>
- <UML:Generalization isSpecification="false" child="5RxrSpb7LxuI" visibility="public" namespace="Logical View" xmi.id="FgcyZa21VrTL" parent="225YrWUg6CUk" discriminator="" name="" />
- <UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="Y1oRrxMFYzBw" isRoot="false" isAbstract="false" name="sensor_plugin" >
- <UML:GeneralizableElement.generalization>
- <UML:Generalization xmi.idref="QAC4gEA4Cbfh" />
- </UML:GeneralizableElement.generalization>
- <UML:Classifier.feature>
- <UML:Operation comment="Allocates and constructs a descendent of abstract_sensor and returns a smart pointer to the new object." isSpecification="false" isLeaf="false" visibility="public" xmi.id="0zNHSOUVzjY3" isRoot="false" isAbstract="false" isQuery="false" name="newSensor" />
- </UML:Classifier.feature>
- </UML:Class>
- <UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="PyRdWPmTPloe" isRoot="false" isAbstract="false" name="etc_plugin" >
- <UML:GeneralizableElement.generalization>
- <UML:Generalization xmi.idref="nenipvZyQWG1" />
- </UML:GeneralizableElement.generalization>
- <UML:Classifier.feature>
- <UML:Operation comment="Returns a smart to a new object of the appropriate class." isSpecification="false" isLeaf="false" visibility="public" xmi.id="EC8j9pFAdKdx" isRoot="false" isAbstract="false" isQuery="false" name="newEtc" />
- </UML:Classifier.feature>
- </UML:Class>
- <UML:Generalization isSpecification="false" child="Y1oRrxMFYzBw" visibility="public" namespace="Logical View" xmi.id="QAC4gEA4Cbfh" parent="225YrWUg6CUk" discriminator="" name="" />
- <UML:Generalization isSpecification="false" child="225YrWUg6CUk" visibility="public" namespace="Logical View" xmi.id="vIs8sXpoyxA8" parent="PyRdWPmTPloe" discriminator="" name="" />
- <UML:Generalization isSpecification="false" child="PyRdWPmTPloe" visibility="public" namespace="Logical View" xmi.id="nenipvZyQWG1" parent="225YrWUg6CUk" discriminator="" name="" />
- <UML:Class stereotype="singleton" isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="WT0Ka8jF10jk" isRoot="false" isAbstract="false" name="plugin_manager" >
- <UML:Classifier.feature>
- <UML:Attribute isSpecification="false" visibility="protected" xmi.id="HPMtw9k0PQzT" type="ohv3mPtcgGzX" name="plugins" />
- <UML:Operation comment="Loads all plugins found in the default paths (defined by the configuration manager singleton not shown here)." isSpecification="false" isLeaf="false" visibility="public" xmi.id="46RtlzCdqiGw" isRoot="false" isAbstract="false" isQuery="false" name="load_all" />
- <UML:Operation comment="Returns a reference to the requested plugin_wrapper or throws if it is not found." isSpecification="false" isLeaf="false" visibility="public" xmi.id="YVcYycxZS0QM" isRoot="false" isAbstract="false" isQuery="false" name="create_instance" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter comment="The class of plugin requested" isSpecification="false" visibility="private" xmi.id="r12fGT7KZYDL" value="" type="CHlmQ1Co03h3" name="plugin_type" />
- <UML:Parameter comment="The name of the plugin requested." isSpecification="false" visibility="private" xmi.id="WNwM6CGchsAd" value="" type="CHlmQ1Co03h3" name="name" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Releases all plugins held by the manager." isSpecification="false" isLeaf="false" visibility="public" xmi.id="niINN8W0bcBT" isRoot="false" isAbstract="false" isQuery="false" name="release_all" />
- <UML:Operation comment="For debugging, returns a formatted string with a list of all plugins loaded including types, names and version number." isSpecification="false" isLeaf="false" visibility="public" xmi.id="HQVTuW3lNEMc" isRoot="false" isAbstract="false" isQuery="false" name="dump" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter kind="return" xmi.id="6dRTso5oWpKV" type="CHlmQ1Co03h3" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Loads one plugin. Will throw if the provided path is invalid or does not contain a valid plugin." isSpecification="false" isLeaf="false" visibility="public" xmi.id="yilX1rI1K1Ye" isRoot="false" isAbstract="false" isQuery="false" name="load_one" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter comment="Path to the shared libary to be loaded." isSpecification="false" visibility="private" xmi.id="RbQYsO69oaag" value="" type="YMxgqxXjjUHD" name="path" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Releases one plugin, specified by type and name. WIll throw if the plugin is not found." isSpecification="false" isLeaf="false" visibility="public" xmi.id="SOhcCFAtLoIm" isRoot="false" isAbstract="false" isQuery="false" name="release_one" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter comment="The type of the single plugin to be released." isSpecification="false" visibility="private" xmi.id="e6qSbfm4JPXG" value="" type="CHlmQ1Co03h3" name="plugin_type" />
- <UML:Parameter comment="The name of the single plugin to be released." isSpecification="false" visibility="private" xmi.id="1Zq4BqfcCMEM" value="" type="CHlmQ1Co03h3" name="plugin_name" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- <UML:Operation comment="Returns a ricklib::str_list contining all the class names currently loaded." isSpecification="false" isLeaf="false" visibility="public" xmi.id="cu3qBV5M3heD" isRoot="false" isAbstract="false" isQuery="false" name="get_class_names" />
- <UML:Operation comment="returns a ricklib::str_list containing all the plugin names which match a given class." isSpecification="false" isLeaf="false" visibility="public" xmi.id="D9eyZpw9jz0s" isRoot="false" isAbstract="false" isQuery="false" name="get_class_plugin_names" >
- <UML:BehavioralFeature.parameter>
- <UML:Parameter comment="The name of the plugin class to be returned." isSpecification="false" visibility="private" xmi.id="KaC8tCfxNnbM" value="" type="CHlmQ1Co03h3" name="class_name" />
- </UML:BehavioralFeature.parameter>
- </UML:Operation>
- </UML:Classifier.feature>
- <UML:Namespace.ownedElement>
- <UML:Class stereotype="map<string,plugins_by_name>" isSpecification="false" isLeaf="false" visibility="public" namespace="WT0Ka8jF10jk" xmi.id="qoJ98w4raNBZ" isRoot="false" isAbstract="false" name="plugin_lists_by_class" />
- </UML:Namespace.ownedElement>
- </UML:Class>
- <UML:Class stereotype="map<string,plugin_list>" isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="eLKy62PhoKwL" isRoot="false" isAbstract="false" name="plugins_by_name" />
- <UML:Association isSpecification="false" visibility="public" namespace="Logical View" xmi.id="TBw9yE3Jk8tz" name="" >
- <UML:Association.connection>
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="OBQB4JJHyejP" aggregation="aggregate" type="qoJ98w4raNBZ" name="" multiplicity="0..n" />
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="tUGb3kMc58zH" aggregation="none" type="eLKy62PhoKwL" name="" />
- </UML:Association.connection>
- </UML:Association>
- <UML:Association isSpecification="false" visibility="public" namespace="Logical View" xmi.id="xRtgdkgsvOEx" name="" >
- <UML:Association.connection>
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="Vq2iVy3r9S2B" aggregation="aggregate" type="225YrWUg6CUk" name="" multiplicity="1..n" />
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="lJSix95qbJQe" aggregation="none" type="eLKy62PhoKwL" name="" />
- </UML:Association.connection>
- </UML:Association>
- <UML:Association isSpecification="false" visibility="public" namespace="Logical View" xmi.id="iYP4MN0Qbl9T" name="" >
- <UML:Association.connection>
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="4mG9sDPO2P4V" aggregation="aggregate" type="eLKy62PhoKwL" name="" multiplicity="1..n" />
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="N7KVZMgJd7zb" aggregation="none" type="225YrWUg6CUk" name="" />
- </UML:Association.connection>
- </UML:Association>
- <UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="Logical View" xmi.id="ohv3mPtcgGzX" isRoot="false" isAbstract="false" name="plugin_lists_by_class" />
- <UML:Association isSpecification="false" visibility="public" namespace="Logical View" xmi.id="D6HDTiBxzfAu" name="" >
- <UML:Association.connection>
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="uwmwLqPgvj9T" aggregation="composite" type="WT0Ka8jF10jk" name="" />
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="5qkhvjG9ldx6" aggregation="none" type="WT0Ka8jF10jk" name="" />
- </UML:Association.connection>
- </UML:Association>
- <UML:Association isSpecification="false" visibility="public" namespace="Logical View" xmi.id="ozgldpf0rhYJ" name="" >
- <UML:Association.connection>
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="RSWw91M3x6yG" aggregation="composite" type="WT0Ka8jF10jk" name="" />
- <UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="wNuq74JAs3rn" aggregation="none" type="qoJ98w4raNBZ" name="" />
- </UML:Association.connection>
- </UML:Association>
- </UML:Namespace.ownedElement>
- <XMI.extension xmi.extender="umbrello" >
- <diagrams>
- <diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="808" snapy="10" showatts="1" xmi.id="DukLGrX7Qs7l" documentation="" type="1" showops="1" showpackage="0" name="Plugin Manager Overview" localid="" showstereotype="0" showscope="1" snapcsgrid="0" font="Sans,10,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="588" >
- <widgets>
- <classwidget usesdiagramfillcolor="0" width="352" showattsigs="601" x="445" fillcolor="#ffffc0" y="19" drawascircle="0" showopsigs="601" linewidth="none" height="234" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="225YrWUg6CUk" showoperations="1" showpackage="0" showscope="1" usesdiagramusefillcolor="0" font="Sans,10,-1,5,50,0,0,0,0,0" linecolor="#ff0000" />
- <classwidget usesdiagramfillcolor="0" width="378" showattsigs="601" x="6" fillcolor="#ffffc0" y="219" showopsigs="601" linewidth="none" height="198" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="WT0Ka8jF10jk" showoperations="1" showpackage="0" showscope="1" showstereotype="1" usesdiagramusefillcolor="0" font="Sans,10,-1,5,50,0,0,0,0,0" linecolor="#ff0000" />
- <classwidget usesdiagramfillcolor="0" width="200" showattsigs="600" x="48" fillcolor="#ffffc0" y="14" showopsigs="600" linewidth="none" height="54" usefillcolor="1" showpubliconly="0" showattributes="0" isinstance="0" xmi.id="eLKy62PhoKwL" showoperations="0" showpackage="0" showscope="1" showstereotype="1" usesdiagramusefillcolor="0" font="Sans,10,-1,5,50,0,0,0,0,0" linecolor="#ff0000" />
- <classwidget usesdiagramfillcolor="0" width="249" showattsigs="601" x="25" fillcolor="#ffffc0" y="114" showopsigs="601" linewidth="none" height="54" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="qoJ98w4raNBZ" showoperations="1" showpackage="0" showscope="1" showstereotype="1" usesdiagramusefillcolor="0" font="Sans,10,-1,5,50,0,0,0,0,0" linecolor="#ff0000" />
- <notewidget usesdiagramfillcolor="1" width="383" x="417" fillcolor="none" y="273" linewidth="none" height="251" usefillcolor="1" isinstance="0" xmi.id="cwXZkbyWc9J4" text="IMPORTANT NOTE:
-
-For this to work, plugin_wrapper must assume each plugin will export the following symbols:
-
-Variables:
- string PTYPE: the plugin type (weapon, sensor, etc.)
- string PNAME: the name of this particular plugin
- string PVER: the version number of this particular plugin
-
-Function
- abstract_? * PFACT(...) : the factory which creates the plugin (a c++ class instance). The precise signature of the fuction will vary between plugin types and is not the concern of the plugin manager, as it will not be called in that context. " usesdiagramusefillcolor="1" font="Sans,8,-1,5,50,0,0,0,0,0" linecolor="none" />
- </widgets>
- <messages/>
- <associations>
- <assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="eLKy62PhoKwL" widgetaid="qoJ98w4raNBZ" xmi.id="TBw9yE3Jk8tz" type="501" linecolor="none" >
- <linepath>
- <startpoint startx="149" starty="114" />
- <endpoint endx="148" endy="68" />
- </linepath>
- <floatingtext usesdiagramfillcolor="1" width="32" x="151" fillcolor="none" y="90" linewidth="none" posttext="" role="701" height="22" usefillcolor="1" pretext="" isinstance="0" xmi.id="k5uTjsZAI36P" text="0..n" usesdiagramusefillcolor="1" font="Sans,10,-1,5,50,0,0,0,0,0" linecolor="none" />
- </assocwidget>
- <assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="225YrWUg6CUk" widgetaid="eLKy62PhoKwL" xmi.id="iYP4MN0Qbl9T" type="501" linecolor="none" >
- <linepath>
- <startpoint startx="248" starty="41" />
- <endpoint endx="445" endy="136" />
- </linepath>
- <floatingtext usesdiagramfillcolor="1" width="32" x="246" fillcolor="none" y="24" linewidth="none" posttext="" role="701" height="22" usefillcolor="1" pretext="" isinstance="0" xmi.id="4RQzXODSk8Ir" text="1..n" usesdiagramusefillcolor="1" font="Sans,10,-1,5,50,0,0,0,0,0" linecolor="none" />
- </assocwidget>
- <assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="qoJ98w4raNBZ" widgetaid="WT0Ka8jF10jk" xmi.id="ozgldpf0rhYJ" type="510" linecolor="none" >
- <linepath>
- <startpoint startx="195" starty="219" />
- <endpoint endx="149" endy="168" />
- </linepath>
- </assocwidget>
- </associations>
- </diagram>
- </diagrams>
- </XMI.extension>
- </UML:Model>
- <UML:Model stereotype="folder" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="Use Case View" isRoot="false" isAbstract="false" name="Use Case View" >
- <UML:Namespace.ownedElement/>
- </UML:Model>
- <UML:Model stereotype="folder" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="Component View" isRoot="false" isAbstract="false" name="Component View" >
- <UML:Namespace.ownedElement/>
- </UML:Model>
- <UML:Model stereotype="folder" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="Deployment View" isRoot="false" isAbstract="false" name="Deployment View" >
- <UML:Namespace.ownedElement/>
- </UML:Model>
- <UML:Model stereotype="folder" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="Entity Relationship Model" isRoot="false" isAbstract="false" name="Entity Relationship Model" >
- <UML:Namespace.ownedElement/>
- </UML:Model>
- </UML:Namespace.ownedElement>
- </UML:Model>
- </XMI.content>
- <XMI.extensions xmi.extender="umbrello" >
- <docsettings viewid="DukLGrX7Qs7l" documentation="" uniqueid="KaC8tCfxNnbM" />
- <listview>
- <listitem open="1" type="800" label="Views" >
- <listitem open="1" type="801" id="Logical View" >
- <listitem open="0" type="807" id="DukLGrX7Qs7l" label="Plugin Manager Overview" />
- <listitem open="1" type="813" id="PyRdWPmTPloe" >
- <listitem open="0" type="815" id="EC8j9pFAdKdx" />
- </listitem>
- <listitem open="1" type="813" id="ZSpIFcMrTgYw" />
- <listitem open="1" type="813" id="ohv3mPtcgGzX" />
- <listitem open="1" type="813" id="WT0Ka8jF10jk" >
- <listitem open="0" type="813" id="qoJ98w4raNBZ" />
- <listitem open="0" type="814" id="HPMtw9k0PQzT" />
- <listitem open="0" type="815" id="46RtlzCdqiGw" />
- <listitem open="0" type="815" id="YVcYycxZS0QM" />
- <listitem open="0" type="815" id="niINN8W0bcBT" />
- <listitem open="0" type="815" id="HQVTuW3lNEMc" />
- <listitem open="0" type="815" id="yilX1rI1K1Ye" />
- <listitem open="0" type="815" id="SOhcCFAtLoIm" />
- <listitem open="0" type="815" id="cu3qBV5M3heD" />
- <listitem open="0" type="815" id="D9eyZpw9jz0s" />
- </listitem>
- <listitem open="1" type="813" id="225YrWUg6CUk" >
- <listitem open="0" type="814" id="0kYAfH74JA7p" />
- <listitem open="0" type="814" id="ToJfQEMPzsZc" />
- <listitem open="0" type="814" id="9egRonGpPYT7" />
- <listitem open="0" type="814" id="ZRl1ZW499wAW" />
- <listitem open="0" type="814" id="wOLstKsOU3xd" />
- <listitem open="0" type="815" id="evlKlgpUQJmb" />
- <listitem open="0" type="815" id="j7HBVbv71scs" />
- <listitem open="0" type="815" id="gx1dfJPPtVf7" />
- <listitem open="0" type="815" id="lh0Se8lUfT5X" />
- <listitem open="0" type="815" id="fqiWLNb62bXe" />
- <listitem open="0" type="815" id="EADACWaXDZwu" />
- <listitem open="0" type="815" id="n1z8iZxdRl3R" />
- </listitem>
- <listitem open="1" type="813" id="eLKy62PhoKwL" />
- <listitem open="1" type="813" id="Y1oRrxMFYzBw" >
- <listitem open="0" type="815" id="0zNHSOUVzjY3" />
- </listitem>
- <listitem open="1" type="813" id="5RxrSpb7LxuI" >
- <listitem open="0" type="815" id="WStI1XSjbrPt" />
- </listitem>
- <listitem open="1" type="818" id="rsozaUfviJVQ" >
- <listitem open="1" type="818" id="hQQSxzuiyRF1" >
- <listitem open="1" type="813" id="YMxgqxXjjUHD" />
- </listitem>
- </listitem>
- <listitem open="1" type="818" id="1dJfkTAS2RQE" />
- <listitem open="1" type="830" id="Datatypes" >
- <listitem open="1" type="829" id="qUlL5Ja8j1rY" />
- <listitem open="1" type="829" id="81JjStCiP83q" />
- <listitem open="1" type="829" id="FWxLRQq7CGim" />
- <listitem open="1" type="829" id="80aYqgH4Lz85" />
- <listitem open="1" type="829" id="dwxIyKY9DGUT" />
- <listitem open="1" type="829" id="VfqivJ93JiZ9" />
- <listitem open="1" type="829" id="hBHFFrFqnD9j" />
- <listitem open="1" type="829" id="Ffh4kk3Ca5c0" />
- <listitem open="1" type="829" id="syWd9dNeIDLC" />
- <listitem open="1" type="829" id="OrDjoJmVT3kg" />
- <listitem open="1" type="829" id="CHlmQ1Co03h3" />
- <listitem open="1" type="829" id="WSFnAnxYc8FQ" />
- <listitem open="1" type="829" id="htJz4eBZ4PMF" />
- <listitem open="1" type="829" id="Q1tt2bZuZ5Yg" />
- <listitem open="1" type="829" id="z2PlKwwnqyeq" />
- </listitem>
- </listitem>
- <listitem open="1" type="802" id="Use Case View" />
- <listitem open="1" type="821" id="Component View" />
- <listitem open="1" type="827" id="Deployment View" />
- <listitem open="1" type="836" id="Entity Relationship Model" />
- </listitem>
- </listview>
- <codegeneration>
- <codegenerator language="C++" />
- </codegeneration>
- </XMI.extensions>
-</XMI>
=== removed file 'common/plugin_loader/plugin_manager.cpp'
--- common/plugin_loader/plugin_manager.cpp 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/plugin_manager.cpp 1970-01-01 00:00:00 +0000
@@ -1,127 +0,0 @@
-//
-// C++ Implementation: plugin_manager
-//
-// Description:
-//
-//
-// Author: Rick Stovall <rstovall@gabbie>, (C) 2008
-//
-// Copyright: See COPYING file that comes with this distribution
-//
-//
-
-#include "plugin_manager.h"
-
-//use boost::filesystem;
-//use boost::filesystem::directory_iterator;
-
-
-// TODO: review to make sure all methods from the .h are implemented.
-namespace NRTB
-{
-
- plugin_manager::plugin_manager()
- {
- // nop in this version.
- };
-
- plugin_manager::plugin_manager(const boost::filesystem::path loadpath)
- {
- plugin_manager();
- load_all(loadpath);
- };
-
- plugin_manager::~plugin_manager()
- {
- release_all();
- };
-
- void plugin_manager::load_all(const boost::filesystem::path loadpath)
- {
- // check to if loadpath really exists
- if (!boost::filesystem::exists(loadpath))
- {
- path_not_found e;
- e.store(loadpath.native_directory_string());
- throw e;
- };
- if (!is_directory(loadpath))
- {
- not_a_directory e;
- e.store(loadpath.native_file_string());
- throw e;
- };
- // set up for the scan
- boost::filesystem::directory_iterator end; // points to the end of the list
- boost::filesystem::directory_iterator current(loadpath);
- while (current != end)
- {
- if (is_directory(*current))
- {
- // recursive call to process the directory.
- load_all(*current);
- }
- else
- {
- // try to load the plugin
- try
- {
- load_one(*current);
- }
- catch (plugin_wrapper::invalid_plugin & e)
- {
- // take no action in this caee...
- };
- };
- current++;
- };
- };
-
- void plugin_manager::load_one(const boost::filesystem::path loadpath)
- {
- // check to if loadpath really exists
- if (!boost::filesystem::exists(loadpath))
- {
- path_not_found e;
- e.store(loadpath.native_directory_string());
- throw e;
- };
- if (boost::filesystem::is_directory(loadpath))
- {
- plugin_file_not_found e;
- e.store(loadpath.native_file_string());
- throw e;
- };
- /* NOTE: since wrapper_p is a smart pointer, any memory
- allocated to it will be discarded if an exception
- is thrown in the following section.
- */
- // Create the plugin_wrapper.
- wrapper_p new_plugin(new plugin_wrapper);
- // Load it... will throw if somehing's not right.
- new_plugin->load(loadpath);
- // Store it in our list.
- plugins[new_plugin->get_type()][new_plugin->get_name()] = new_plugin;
- };
-
- void plugin_manager::release_all()
- {
- // this will call the destructor and deallocate all
- // plugin_wrappers currently loaded.
- plugins.clear();
- };
-
- void plugin_manager::release_one(const std::string pclass,
- const std::string name)
- {
- // TODO: more here.
- plugins[pclass].erase(name);
- };
-
- std::string plugin_manager::dump()
- {
- // TODO: More to do here.
- };
-
-}; // namespace NRTB
-
=== removed file 'common/plugin_loader/plugin_manager.h'
--- common/plugin_loader/plugin_manager.h 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/plugin_manager.h 1970-01-01 00:00:00 +0000
@@ -1,194 +0,0 @@
-//
-// C++ Interface: plugin_manager
-//
-// Description:
-//
-//
-// Author: Rick Stovall <rstovall@gabbie>, (C) 2008
-//
-// Copyright: See COPYING file that comes with this distribution
-//
-//
-
-#ifndef plugin_manager_h
-#define plugin_manager_h
-
-#include "plugin_wrapper.h"
-#include <map>
-
-namespace NRTB
-{
- /**
- * Provides a simple mechanism for an application to
- * load and manage plugins (shared objects in the unix lingo)
- * which provide the application with functionality not
- * not available at the time it was built.
- *
- * To be useable with this class a shared object must
- * meet certain requirements which are outlined in the
- * NRTB::plugin_wrapper class documentation. Example
- * plugins are also provided.
- */
- class plugin_manager
- {
- public:
- /// Typedef for return values
- typedef std::vector<std::string> strlist;
- /// parent for all exceptions thrown by this class
- class general_exception: public base_exception {};
- /// thrown if a specified plugin file was no found.
- class plugin_file_not_found: public general_exception {};
- /// thrown if a specified load path (directory) was not found.
- class path_not_found: public general_exception {};
- /// thown if a specified load path is not a directory.
- class not_a_directory: public general_exception {};
- /// thrown if a specifically requested plugin is not loaded.
- class plugin_not_found: public general_exception {};
- /**
- * Simple constructor.
- */
- plugin_manager();
- /**
- * Constructs the class and then attempts to load all
- * files found under the given path (recursively) as
- * plugins.Non-plugin files will be ignored, but
- * problems reading the given path (path not found,
- * insufficient permissions, etc.) will cause an
- * exception to be thrown.
- *
- * @param loadpath The top of the file tree to be searched.
- */
- plugin_manager(const boost::filesystem::path loadpath);
- /**
- * Unloads all managed plugins and destructs the
- * plugin_manager. Probably not a good thing to do
- * while any of the plugins are in use.
- */
- virtual ~plugin_manager();
- /**
- * Attempts to load all files found under the
- * given path (recursively) as plugins.Non-plugin
- * files will be ignored, but problems reading the
- * given path (path not found, insufficient
- * permissions, etc.) will cause an exception to be thrown.
- *
- * If two plugins have the same class and name, the one
- * with the highest version number will be kept and
- * the other(s) discarded.
- *
- * @param loadpath The top of the file tree to be searched.
- */
- void load_all(const boost::filesystem::path loadpath);
- /**
- * Attempts to load a plugin from the fully qualified
- * file path given. Exceptions will be thrown if there
- * are any problems reading the file, or if it is not a
- * valid plugin (by plugin_wrapper standards).
- *
- * If two plugins have the same class and name, the one
- * with the highest version number will be kept and
- * the other(s) discarded.
- *
- * @param loadpath A fully qualified file path.
- */
- void load_one(const boost::filesystem::path loadpath);
- /**
- * Releases all plugins currently being managed. Probably
- * not a good thing to do if any of them are being used.
- */
- void release_all();
- /**
- * Attempts to release the single plugin matching the
- * provided plugin class and name. An exception will be thrown
- * if the plugin is not found.
- *
- * @param pclass The class of the plugin to release.
- * @param name The name of the plugin to release.
- */
- void release_one(const std::string pclass, const std::string name);
- /**
- * Dumps a list of all loaded plugins, intended for
- * diagnostic purposes. The list is formatted:
- *
- * plugin_class::plugin_name<eol>
- *
- * with one line for each plugin loaded.
- *
- * @return A string contianing a formatted plugin listing.
- */
- std::string dump();
- /**
- * Returns a strlist (a vector<string>) containing
- * all unique plugin class names loaded, in alpha order.
- *
- * @return A list of all unique plugin classes loaded.
- */
- strlist get_class_names();
- /**
- * Returns a strlist (a vector<string>) containing
- * the names of all plugins loaded of the given plugin class.
- *
- * @param pclass The name of the plugin class to be searched.
- * @return A list of all plugins loaded for the given plugin class.
- */
- strlist get_class_plugin_names(const std::string pclass);
- /**
- * Returns true if a plugin with the given class
- * and name exists, or faise if not.
- *
- * @param pclass The class name of the plugin to be checked.
- * @param pname The name of the plugin to be checked
- * @return True if the plugin is laoded, false otherwise.
- */
- bool exists(const std::string pclass, const std::string pname);
- /**
- * Returns a boost::shared_ptr<T> to a new instance
- * of the class provided by the plugin specified by
- * the plugin_class and plugin_name provided.
- *
- * Usage example:
- * myTypePointer = create_plugin_instance<mytype>(class,name);
- *
- * This routine will succeed unconditionally or throw
- * one of several possible exceptions. In no case will a
- * null pointer or improperly constructed instance be
- * returned.
- *
- * @param plugin_class The class name of the plugin desired
- * @param plugin_name The name of the plugin desired.
- * @return A boost::shared_ptr<T> to a ready to use instance.
- */
- template <class T> boost::shared_ptr<T> create_plugin_instance
- (
- const std::string plugin_class,
- const std::string plugin_name
- )
- {
- typedef boost::shared_ptr<T> returntype;
- if (!exists(plugin_class, plugin_name))
- {
- plugin_not_found e;
- e.store(plugin_class+"::"+plugin_name);
- throw e;
- };
- returntype returnme =
- plugins[plugin_class][plugin_name]->create_instance<T>();
- return returnme;
- };
- protected:
- // smart pointer to a plugin_wrapper
- typedef boost::shared_ptr<plugin_wrapper> wrapper_p;
- // type used contain plugins of the same type
- typedef std::map<std::string, wrapper_p> plugins_by_name;
- // type used to contain lists of types of plugins.
- typedef std::map<std::string, plugins_by_name> plugins_list_by_class;
- // iterator providing access to "plugins_by_name" lists by class
- typedef plugins_list_by_class::iterator class_iterator;
- // iterator providing access to individual plugins by name
- typedef plugins_by_name::iterator plugin_iterator;
- // container for all plugins being managed.
- plugins_list_by_class plugins;
- };
-};
-
-#endif //plugin_manager_h
=== removed file 'common/plugin_loader/plugin_wrapper.cpp'
--- common/plugin_loader/plugin_wrapper.cpp 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/plugin_wrapper.cpp 1970-01-01 00:00:00 +0000
@@ -1,146 +0,0 @@
-//
-// C++ Implementation: plugin_wrapper
-//
-// Description:
-//
-//
-// Author: Rick Stovall <rstovall@gabbie>, (C) 2008
-//
-// Copyright: See COPYING file that comes with this distribution
-//
-//
-
-#include "plugin_wrapper.h"
-
-namespace NRTB
-{
-
- plugin_wrapper::plugin_wrapper()
- {
- // Load the "constants"
- std::string name_key = "PLUGIN_NAME";
- std::string type_key = "PLUGIN_TYPE";
- std::string version_key = "PLUGIN_VERSION";
- std::string factory_key = "PLUGIN_FACTORY";
- // clear all the associated fields.
- plugin_type = "";
- plugin_name = "";
- plugin_version = 0.0;
- plugin_path = "";
- // close the handle if it's already assigned.
- if (dl_handle)
- {
- dlclose(dl_handle);
- };
- dl_handle = 0;
- };
-
- plugin_wrapper::plugin_wrapper(boost::filesystem::path loadpath)
- {
- load(loadpath);
- };
-
- plugin_wrapper::~plugin_wrapper()
- {
- // close the handle if it's already assigned.
- if (dl_handle)
- {
- dlclose(dl_handle);
- };
- dl_handle = 0;
- };
-
- void plugin_wrapper::load(boost::filesystem::path loadpath)
- {
- // establish initial conditions.
- plugin_wrapper();
- if (loadpath.empty())
- {
- // an empty path could expose security holes.
- file_not_found e;
- e.store("Illegal null path provided");
- throw e;
- };
- dlerror();
- try
- {
- // call dlopen to open the lib
- dl_handle = dlopen(loadpath.string().c_str(), RTLD_LAZY);
- if (!dl_handle)
- {
- throw new file_not_found;
- };
- // load the fields
- plugin_type = get_value<std::string>(type_key);
- plugin_name = get_value<std::string>(name_key);
- plugin_version = get_value<float>(version_key);
- plugin_path = loadpath;
- }
- catch (file_not_found &e)
- {
- // is it a missing file or a bad file?
- if (!boost::filesystem::exists(loadpath))
- {
- e.store(loadpath.string());
- throw e;
- }
- else
- {
- invalid_plugin ie;
- ie.store(dlerror());
- throw ie;
- };
- }
- catch (invalid_plugin & e)
- {
- if (e.comment() == "")
- {
- e.store(dlerror());
- };
- throw e;
- }
- catch (std::exception &e)
- {
- general_exception ge;
- ge.store(e.what());
- throw ge;
- };
- };
-
- std::string plugin_wrapper::get_type()
- {
- if (!dl_handle)
- {
- throw new not_loaded;
- };
- return plugin_type;
- };
-
- std::string plugin_wrapper::get_name()
- {
- if (!dl_handle)
- {
- throw new not_loaded;
- };
- return plugin_name;
- };
-
- float plugin_wrapper::get_version()
- {
- if (!dl_handle)
- {
- throw new not_loaded;
- };
- return plugin_version;
- };
-
- boost::filesystem::path plugin_wrapper::get_path()
- {
- if (!dl_handle)
- {
- throw new not_loaded;
- };
- return plugin_path;
- };
-
-} // namespace NRTB
=== removed file 'common/plugin_loader/plugin_wrapper.h'
--- common/plugin_loader/plugin_wrapper.h 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/plugin_wrapper.h 1970-01-01 00:00:00 +0000
@@ -1,207 +0,0 @@
-//
-// C++ Interface: plugin_wrappr
-//
-// Description:
-//
-//
-// Author: Rick Stovall <rstovall@gabbie>, (C) 2008
-//
-// Copyright: See COPYING file that comes with this distribution
-//
-//
-
-#ifndef plugin_wrapper_h
-#define plugin_wrapper_h
-
-#include <dlfcn.h>
-#include <string>
-#include <ricks_handy.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/filesystem/convenience.hpp>
-
-namespace NRTB
-{
- /**
- * A container for a single plugin. A plugin is defined as a
- * shared object file which provides a class, an associated factory
- * function, and several housekeeping constants, as follows:
- *
- * 1. A std::string identifying the class factory named "PLUGIN_FACTORY"
- *
- * 2. A plugin name std::string named "PLUGIN_NAME"
- *
- * 3. A plugin type std::stirng named "PLUGIN_TYPE"
- *
- * 4. A plugin version number float named "PLUGIN_VERSION"
- *
- * This class is not designed to instanticated directly by user code.
- * Instead, it will be intanciated by the plugin_manager, which acts as
- * a container for many plugins and provides access to them.
- *
- * In call cases except load(), when an exception is thown from a method,
- * the instance is left the state it was in at the start of the method.
- **/
- class plugin_wrapper
- {
- public:
- /// Parent for all plugin_wrapper exceptions.
- class general_exception: public base_exception {};
- /// Thrown if a file is not found at the requested path.
- class file_not_found: public general_exception {};
- /// Thrown if a requested file does not contain a valid plugin.
- class invalid_plugin: public general_exception {};
- /// Thrown if queries are called when no data is present.
- class not_loaded: public general_exception {};
- /**
- * Default constructor; NOP.
- */
- plugin_wrapper();
- /**
- * This constructor attempts to load a plugin at
- * the argument provided. Actually calls load() to
- * to do the work.
- *
- * @param loadpath Should be point to a valid plugin file
- */
- plugin_wrapper(boost::filesystem::path loadpath);
- /**
- * Unloads any plugin, if contained.
- */
- virtual ~plugin_wrapper();
- /**
- * Discards the current contents of this wrapper, if any,
- * and then attempts to load the plugin file provided. If there
- * are any problems, an appropriate exception will be thrown.
- *
- * @param loadpath Should point to a valid plugin file.
- */
- virtual void load(boost::filesystem::path loadpath);
- /**
- * Returns the pligin type associated with the loaded plugin,
- * or throws if one is not loaded. The plugin type is a string
- * indicating the what services the plugin provides and in
- * most cases will have no meaning outside of the application
- * context.
- *
- * For example, a plugin providing printing functionality
- * may have a type of "printer", while one that provides
- * database access may have a type of "db_interface".
- *
- * @return A std::string indicating plugin type.
- */
- virtual std::string get_type();
- /**
- * Returns the name of the particular plugin loaded, or
- * throws if one is not loaded. The name is an arbitrary string
- * which uniquely identifies a particular plugin within a type.
- *
- * For example, a plugin providing printing functionality to
- * a PDF file may have a type of "printer" and a name of "PDF",
- * while another "printer" plugin providing output to doc
- * files may have the name "WordDoc".
- *
- * @return A std::string indicating the plugin name.
- */
- virtual std::string get_name();
- /**
- * Returns the version of the particular plugin loaded, or
- * throws if one is not loaded. The version is an arbitrary
- * float value which uniquely identifies a particular version
- * of a plugin within a name and type.
- *
- * @return A float indicating the plugin version.
- */
- virtual float get_version();
- /**
- * Returns the fully qualified path of the plugin loaded, or
- * throws if one is not loaded.
- *
- * @return A boost::filesystem::path to the plugin file.
- */
- boost::filesystem::path get_path();
- /**
- * Calls the class factory and returns shared_ptr to an
- * instance of the type requested. For reliable operation,
- * the type provided the template must be compatable with
- * that actually implemented in the plugin.
- *
- * An appropriate exception will be thrown if there are issues.
- *
- * Usage: mytype myvar createInstance<mytype>();
- *
- * @return A boost::shared_ptr to an instanace of
- * the type provided.
- */
- template <class T> boost::shared_ptr<T> create_instance()
- {
- if (!dl_handle)
- {
- not_loaded e;
- e.store("create_instance");
- throw e;
- };
- typedef T (*factory_type)();
- typedef boost::shared_ptr<T> my_pointer;
- my_pointer returnme;
- try
- {
- factory_type factory = get_value<factory_type>(factory_key);
- returnme = factory();
- }
- catch (std::exception & e)
- {
- // Let the caller sort it out.
- throw;
- }
- catch (...)
- {
- // most likely a bad problem in the factory function.
- invalid_plugin e;
- e.store("create_instance caught an unknown exception");
- throw e;
- };
- return returnme;
- };
-
- protected:
- // gets indivdual field values from the shared lib.
- template <class T> T get_value(const std::string & key)
- {
- typedef T * my_pointer;
- T returnme;
- dlerror();
- my_pointer value = (my_pointer) dlsym(dl_handle,key.c_str());
- std::string check_error = dlerror();
- if (!check_error.empty())
- {
- invalid_plugin e;
- e.store(check_error+" ("+key+")");
- throw e;
- };
- if (!value)
- {
- invalid_plugin e;
- e.store("("+key+") returned a null value");
- throw e;
- };
- returnme = *value;
- return returnme;
- };
-
- private:
- // The following are used to cache plugin data after load.
- void * dl_handle;
- std::string plugin_type;
- std::string plugin_name;
- float plugin_version;
- boost::filesystem::path plugin_path;
- // The following define what labels are expected to be defined.
- static const std::string name_key;
- static const std::string type_key;
- static const std::string version_key;
- static const std::string factory_key;
- };
-
-} // nameplace NRTB
-
-#endif // plugin_wrapper_h
=== removed directory 'common/plugin_loader/test'
=== removed file 'common/plugin_loader/test/Makefile'
--- common/plugin_loader/test/Makefile 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/test/Makefile 1970-01-01 00:00:00 +0000
@@ -1,13 +0,0 @@
-
-# makefile for the plugin test routine.
-
-test: ../plugin_wrapper.o ../plugin_manager.o plugintest.cpp
- @rm -f plugintest
- g++ -c plugintest.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
-# g++ -c -g conftest.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
- g++ -o plugintest plugintest.o ../plugin_wrapper.o ../plugin_manager.o -l ricklib3 -L../../lib/
- ./plugintest
-
-clean:
- @rm -rvf *.o plugintest
- @echo all objects and executables have been erased.
=== removed directory 'common/plugin_loader/test/plugins'
=== removed file 'common/plugin_loader/test/plugintest.cpp'
--- common/plugin_loader/test/plugintest.cpp 2010-01-14 02:24:25 +0000
+++ common/plugin_loader/test/plugintest.cpp 1970-01-01 00:00:00 +0000
@@ -1,12 +0,0 @@
-//
-// C++ Implementation: plugintest
-//
-// Description:
-//
-//
-// Author: Rick Stovall <rstovall@gabbie>, (C) 2008
-//
-// Copyright: See COPYING file that comes with this distribution
-//
-//
-
=== modified file 'common/point/Makefile'
--- common/point/Makefile 2010-01-14 02:24:25 +0000
+++ common/point/Makefile 2011-08-25 04:57:16 +0000
@@ -1,17 +1,34 @@
-build: common_test Makefile
- @echo build complete
-
-common_test: common_test.cpp triad.h Makefile
- @rm -vf triad.o
- g++ -O3 common_test.cpp -idirafter . -o common_test
-# g++ -g common_test.cpp -idirafter . -o common_test
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: common_test Makefile
+ @echo "(2,3.5,7)" | ./common_test
+ @cp -v triad.h ../include
+ @echo nrtb::triad build complete
+
+../include/common.h:
+ @cd ../common_rl; make lib
+
+common_test: common_test.cpp triad.h Makefile ../include/common.h
+ @rm -vf common_test
+ g++ -O3 common_test.cpp -I ../include ../obj/common.o -o common_test
clean:
- @rm -vf *.o common_test ../../include3/triad.h
-
-lib: common_test ../../include3/triad.h
-
-../../include3/triad.h: triad.h
- @cp -fv triad.h ../../include3
+ @rm -vf *.o common_test ../include/triad.h
=== modified file 'common/point/common_test.cpp'
--- common/point/common_test.cpp 2010-01-14 02:24:25 +0000
+++ common/point/common_test.cpp 2011-08-25 04:57:16 +0000
@@ -1,4 +1,22 @@
-/*** Test program for triad.h *****/
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+ /*** Test program for triad.h *****/
#include <unistd.h>
#include <iostream>
@@ -7,56 +25,93 @@
using namespace std;
-typedef NRTB::triad<long double> ld_triad;
+typedef nrtb::triad<long double> ld_triad;
+
+int test_triad(const std::string prompt, ld_triad val, ld_triad right, int ec)
+{
+ cout << "\t" << prompt << " = " << val << endl;
+ if (val != right)
+ {
+ ec++;
+ cerr << "\t\tTest Failed: Answer should be " << val << endl;
+ }
+ return ec;
+};
+
+int test_ld(const std::string prompt, long double val, long double right, int ec)
+{
+ cout << "\t" << prompt << " = " << val << endl;
+ if (val != right)
+ {
+ ec++;
+ cerr << "\t\tTest Failed: Answer should be " << val << endl;
+ }
+ return ec;
+};
int main()
{
ld_triad a(1,2,3);
ld_triad b(3,2,1);
+ int returnme = 0;
cout << setprecision(10);
- cout << "=== Triad Test ===" << endl;
- cout << "\ta = " << a << "; b = " << b << "\n" << endl;
-
- cout << "\ta + b = " << a + b << endl;
- cout << "\ta - b = " << a - b << endl;
- cout << "\ta * b = " << a * b << endl;
- cout << "\ta / b = " << a / b << endl;
- cout << "\ta += b; a = " << (a += b) << endl;
- cout << "\ta -= b; a = " << (a -= b) << endl;
- cout << "\ta *= b; a = " << (a *= b) << endl;
- cout << "\ta /= b; a = " << (a /= b) << endl;
- cout << "\n\ta.pow(b) = " << a.pow(b) << endl;
- cout << "\ta.range(b) = " << a.range(b) << endl;
- cout << "\ta.magnatude() = " << a.magnatude() << endl;
- cout << "\n\ta == a = " << (a == a) << endl;
- cout << "\ta == b = " << (a== b) << endl;
- cout << "\ta != b = " << (a != b) << endl;
- cout << "\ta != a = " << (a != a) << endl;
-
- cout << endl;
-
- cout << "\ta + 2 = " << a + 2 << endl;
- cout << "\ta - 2 = " << a - 2 << endl;
- cout << "\ta * 2 = " << a * 2 << endl;
- cout << "\ta / 2 = " << a / 2 << endl;
- cout << "\ta += 2; a = " << (a += 2) << endl;
- cout << "\ta -= 2; a = " << (a -= 2) << endl;
- cout << "\ta *= 2; a = " << (a *= 2) << endl;
- cout << "\ta /= 2; a = " << (a /= 2) << endl;
- cout << "\n\ta.pow(2) = " << a.pow(2) << endl;
- cout << "\ta.pow(0.5) = " << a.pow(0.5) << endl;
+ cout << "=== nrtb::triad Unit Test ===" << endl;
+ cout << "\ta = " << a << "; b = " << b << endl;
+ // basic operations tests
+ returnme = test_triad("a + b",a + b,ld_triad(4,4,4),returnme);
+ returnme = test_triad("a - b",a - b,ld_triad(-2,0,2),returnme);
+ returnme = test_triad("a * b",a * b,ld_triad(3,4,3),returnme);
+ returnme = test_triad("a / b",a / b,ld_triad(1.0d/(long double) 3.0,1,3),returnme);
+ returnme = test_triad("a += b; a",a += b,ld_triad(4,4,4),returnme);
+ returnme = test_triad("a -= b; a",a -= b,ld_triad(1,2,3),returnme);
+ returnme = test_triad("a *= b; a",a *= b,ld_triad(3,4,3),returnme);
+ returnme = test_triad("a /= b; a",a /= b,ld_triad(1,2,3),returnme);
+ // power test
+ returnme = test_triad("a.pow(b)",a.pow(b),ld_triad(1,4,3),returnme);
+ // range test
+ ld_triad t = b - a;
+ t *= t;
+ long double r = sqrt(t.x + t.y + t.z);
+ returnme = test_ld("a.range(b)",a.range(b),r,returnme);
+ // magnatude test
+ returnme = test_ld("a.magnatude()",a.magnatude(),a.range(0),returnme);
+ // boolean tests
+ returnme = test_ld("a == a",a == a,1,returnme);
+ returnme = test_ld("a == b",a == b,0,returnme);
+ returnme = test_ld("a != b",a != b,1,returnme);
+ returnme = test_ld("a != a",a != a,0,returnme);
+ // point/scalar operations
+ returnme = test_triad("a + 2",a + 2,ld_triad(3,4,5),returnme);
+ returnme = test_triad("a - 2",a - 2,ld_triad(-1,0,1),returnme);
+ returnme = test_triad("a * 2",a * 2,ld_triad(2,4,6),returnme);
+ returnme = test_triad("a / 2",a / 2,ld_triad(0.5,1,1.5),returnme);
+ returnme = test_triad("a += 2",a += 2,ld_triad(3,4,5),returnme);
+ returnme = test_triad("a -= 2",a -= 2,ld_triad(1,2,3),returnme);
+ returnme = test_triad("a *= 2",a *= 2,ld_triad(2,4,6),returnme);
+ returnme = test_triad("a /= 2",a /= 2,ld_triad(1,2,3),returnme);
+ returnme = test_triad("a.pow(2)",a.pow(2),ld_triad(1,4,9),returnme);
+ // normalization test
cout << "\ta.normalize() = " << a.normalize() << endl;
- cout << "\ta.normalize().magnatude() = "<< a.normalize().magnatude() << endl;
-
- cout << "\n\a\tInput a new value for b (x,y,z): " << flush;
- cin >> b;
- cout << "\t\tb = " << b << endl;
- cout << "\tb.to_str() =\"" << b.to_str() << "\"" << endl;
- cout << "\tb.from_str(b.to_str(10)) = " << b.from_str(b.to_str(10)) << endl;
-
-
- cout << "\n=== Test Complete ===" << endl;
- return 0;
+ returnme = test_ld("a.normalize().magnatude()",a.normalize().magnatude(),1.0,returnme);
+ // dot and vector product tests.
+ returnme = test_ld("a.dot_product(b)",a.dot_product(b),10,returnme);
+ returnme = test_triad("a.vector_product(b)",a.vector_product(b),
+ ld_triad(-4,8,-4),returnme);
+ // string i/o tests, assumes "2,3.5,7) is input.
+ cout << "\tInput a new value for b \"(2,3.5,7)\": " << flush;
+ cin >> b; cout << endl;
+ returnme = test_triad("b",b,ld_triad(2,3.5,7),returnme);
+ returnme = test_triad("b.from_str(b.to_str(10))",
+ b.from_str(b.to_str(10)),ld_triad(2,3.5,7),returnme);
+ // report errors, if any
+ if (returnme)
+ {
+ cerr << "There were " << returnme
+ << " error(s) found." << endl;
+ }
+ cout << "=== nrtb::triad Unit Test Complete ===" << endl;
+ // return the error count as the exit code
+ return returnme;
};
=== modified file 'common/point/triad.h'
--- common/point/triad.h 2010-01-14 02:24:25 +0000
+++ common/point/triad.h 2011-08-25 04:57:16 +0000
@@ -1,31 +1,40 @@
-/*************************************************************
-**** CODE REVIEW: triad.h, starting 2008-11-24 ***************
-* The triad template is fully implemented in this one file.
-* This class will be used for all vector and point operations
-* Please make any comments or suggestions directly in the file.
-*************************************************************/
-
-/**************************************************
-TODO: add methods for dot product, cross multiply,
- finding the surface normal, etc.
-/**************************************************
-
-#ifndef nrtb_triad_header
-#define nrtb_triad_header
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef triad_header
+#define triad_header
#include <iostream>
+#include <vector>
+#include <string>
#include <sstream>
#include <iomanip>
#include <math.h>
+#include <common.h>
-namespace NRTB
+namespace nrtb
{
/** Template defining a point or vector in cartesian 3d space. Should work
** with all floating point types.
**
** Implements the following operators: +,=,*,/,+=,-=,*=,/= for both
- ** triad<T> and <T> arguments; each returns a triad as the result.
+ ** triad<T> and <T> arguments; each returns an triad as the result.
** ==,!=,>>,<< are implemented for triad arguments. Additionally
** implements pow() with triad and T arguments. Finally implements range()
** which returns the distance between the triad and one supplied, and
@@ -76,13 +85,17 @@
** is not modified.
**
** Normalized triads are very useful when calcuating force or
- ** accelleration vectors and in many other cases.
+ ** accelleration vectors, for example.
**/
triad<T> normalize();
/// Returns the distance between *this and the supplied argument.
T range(const triad<T> & a);
/// Returns the magnatude of the vector.
T magnatude();
+ /// Returns the dot (scalar) product of two triads
+ T dot_product(const triad<T> & a);
+ /// Returns the vector product of two triads
+ triad<T> vector_product(const triad<T> & a);
bool operator == (const triad<T> & a);
bool operator != (const triad<T> & a);
/// Loads from a std::string.
@@ -292,6 +305,26 @@
};
template <class T>
+T triad<T>::dot_product(const triad<T> & a)
+{
+ T returnme;
+ returnme = x * a.x;
+ returnme += y * a.y;
+ returnme += z * a.z;
+ return returnme;
+};
+
+template <class T>
+triad<T> triad<T>::vector_product(const triad<T> & a)
+{
+ triad<T> rv;
+ rv.x = (y * a.z) - (z * a.y);
+ rv.y = (z * a.x) - (x * a.z);
+ rv.z = (x * a.y) - (y * a.x);
+ return rv;
+};
+
+template <class T>
bool triad<T>::operator == (const triad<T> & a)
{
return ((x == a.x) && (y == a.y) && (z == a.z));
@@ -329,16 +362,11 @@
return f;
};
- //?? Review question: should we have the read operator
- // throw instead of defaulting to (0,0,0) on a bad read?
- // A: No, because this is the same behavior the read
- // operator exhibits with other numeric types.
-
/** Reads the triad<T> from an input stream.
**
** The acceptable format is [(]x,y,z[)]. Any other format will result
** in the triad<T> being set to (0,0,0). Any numeric format acceptable
- ** to an iostream is valid for x, y and z. Any other represention of x,
+ ** to strtod() is valid for x, y and z. Any other represention of x,
** y or z will result in that particular value being set to 0.
**/
template <class T>
@@ -346,8 +374,7 @@
{
std::string element;
f >> element;
- //TODO: Need to replace split with boost equivelent
- std::vector<std::string> t = split(element,',');
+ strlist t = split(element,',');
if (t.size() != 3)
{
a = 0;
@@ -363,6 +390,6 @@
return f;
};
-} // namespace NRTB
+} // namespace nrtb
-#endif // nrtb_triad_header
+#endif // triad_header
=== added directory 'common/serializer'
=== added file 'common/serializer/Makefile'
--- common/serializer/Makefile 1970-01-01 00:00:00 +0000
+++ common/serializer/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,36 @@
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: serializer_test
+ @./serializer_test
+ @cp -v serializer.h ../include
+ @cp -v serializer.o ../obj
+ @echo build complete
+
+serializer.o: serializer.h serializer.cpp Makefile
+ @rm -f serializer.o
+ g++ -c serializer.cpp -I ../include
+
+serializer_test: serializer.o serializer_test.cpp
+ @rm -f serializer_test
+ g++ -c serializer_test.cpp -I../include
+ g++ -o serializer_test serializer_test.o serializer.o ../obj/base_thread.o -lpthread ../obj/common.o
+
+clean:
+ @rm -rvf *.o serializer_test ../include/serializer.h ../obj/serializer.o
+ @echo all objects and executables have been erased.
=== added file 'common/serializer/serializer.cpp'
--- common/serializer/serializer.cpp 1970-01-01 00:00:00 +0000
+++ common/serializer/serializer.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,43 @@
+/***********************************************
+ T his file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "serializer.h"
+#include <base_thread.h>
+
+using namespace nrtb;
+
+nrtb::serializer::serializer()
+{
+ counter = 0;
+};
+
+nrtb::serializer::serializer(unsigned long long start)
+{
+ counter = start;
+};
+
+serializer::~serializer()
+{
+ // nop destructor
+};
+
+unsigned long long serializer::operator()()
+{
+ nrtb::scope_lock mylock(lock);
+ return counter++;
+}
=== added file 'common/serializer/serializer.h'
--- common/serializer/serializer.h 1970-01-01 00:00:00 +0000
+++ common/serializer/serializer.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,50 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef nrtb_serializer_h
+#define nrtb_serializer_h
+#include <base_thread.h>
+
+namespace nrtb
+{
+ /******************************************************************
+ * nrtb::serializer provides a simple, thread safe functor which
+ * returns a series of long long ints increasing by one each time
+ * it's called. By default it starts counting from zero, but may
+ * be started from any arbitrary integer in the range
+ * 0 < x < max long long.
+ * ***************************************************************/
+ class serializer
+ {
+ public:
+ // default constructor; counter starts from zero.
+ serializer();
+ // constructor which sets the starting number.
+ serializer(unsigned long long start);
+ // NOP distructor for inheritance safety
+ ~serializer();
+ // functor method, returns the next value in the sequence.
+ unsigned long long operator ()();
+ private:
+ nrtb::mutex lock;
+ unsigned long long int counter;
+ };
+
+}
+
+#endif // nrtb_serializer_h
\ No newline at end of file
=== added file 'common/serializer/serializer_test.cpp'
--- common/serializer/serializer_test.cpp 1970-01-01 00:00:00 +0000
+++ common/serializer/serializer_test.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,56 @@
+/***********************************************
+ T his file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "serializer.h"
+#include <iostream>
+
+using namespace nrtb;
+using namespace std;
+
+int main()
+{
+ cout << "** nrtb::serializer unit test **" << endl;
+ bool set_if_failed = false;
+ serializer set_default;
+ serializer set_start(20);
+ serializer rollover((unsigned long long)(0 - 1));
+ cout << "Default initialization" << endl;
+ cout << " " << set_default();
+ cout << " " << set_default();
+ cout << " " << set_default();
+ cout << endl;
+ if (set_default() != 3)
+ set_if_failed = true;
+ cout << "Started from twenty" << endl;
+ cout << " " << set_start();
+ cout << " " << set_start();
+ cout << " " << set_start();
+ cout << endl;
+ if (set_start() != 23)
+ set_if_failed = true;
+ cout << "Rollover Demonstration" << endl;
+ cout << " " << rollover();
+ cout << " " << rollover();
+ cout << " " << rollover();
+ cout << endl;
+ if (rollover() != 2)
+ set_if_failed = true;
+ if (set_if_failed)
+ cout << "UNIT TEST FAILED" << endl;
+ return set_if_failed;
+};
\ No newline at end of file
=== added directory 'common/singleton'
=== removed directory 'common/singleton'
=== added file 'common/singleton/Makefile'
--- common/singleton/Makefile 1970-01-01 00:00:00 +0000
+++ common/singleton/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,33 @@
+#***********************************************
+# This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+
+lib: singleton_test
+ @./singleton_test
+ @cp -v singleton.h ../include
+ @echo build complete
+
+singleton_test: singleton_test.cpp Makefile
+ @rm -f singleton_test
+ g++ -c singleton_test.cpp -I ../include
+ g++ -o singleton_test singleton_test.o ../obj/base_thread.o ../obj/common.o ../obj/serializer.o -lpthread
+
+clean:
+ @rm -rvf *.o singleton_test ../include/singleton.h
+ @echo all objects and executables have been erased.
+
=== removed file 'common/singleton/Makefile'
--- common/singleton/Makefile 2010-01-14 02:24:25 +0000
+++ common/singleton/Makefile 1970-01-01 00:00:00 +0000
@@ -1,18 +0,0 @@
-
-build: singleton_test
- @echo build complete
-
-singleton_test: singleton_test.cpp Makefile
- @rm -f singleton_test
- g++ -c singleton_test.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
-# g++ -c -g singleton_test.cpp -idirafter ../../include3 -idirafter /usr/local/include/boost-1_31
- g++ -o singleton_test singleton_test.o -lricklib3 -lpthread -lssl -L../../lib/
-
-clean:
- @rm -rvf *.o singleton_test ../../include3/singleton.h
- @echo all objects and executables have been erased.
-
-lib: ../../include3/singleton.h
-
-../../include3/singleton.h: singleton.h
- @cp -fv singleton.h ../../include3
=== added file 'common/singleton/singleton.h'
--- common/singleton/singleton.h 1970-01-01 00:00:00 +0000
+++ common/singleton/singleton.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,123 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+/* implements a template that takes any class provided and
+ * creates a singleton for it.
+ *
+ * fps 2002-5-29
+ */
+
+#ifndef nrtb_singleton_h
+#define nrtb_singleton_h
+
+#include <base_thread.h>
+
+namespace nrtb
+{
+
+/** Wrapper template to create singleton classes.
+ **
+ ** Classic Singleton pattern template, with thread-safe
+ ** methods for getting an instance and destruction.
+ **
+ ** Singletons are classes that:
+ ** (1) Insure that there are no more than one instance of the
+ ** class at any time.
+ ** (2) Allocate themselves automagically when accessed for the
+ ** first time.
+ ** (3) Provide a globally available access method to allow any
+ ** scope to gain access and use the instance.
+ **
+ ** This template can create a singleton version of any class that has a
+ ** no-argument constructor. The resulting class will have the same
+ ** inteface as the object used to create the template with the constructor
+ ** and destructor made protected and the addition of the get_instance()
+ ** and delete_me() methods.
+ **
+ ** See the documentation on the get_instance() and delete_me() methods for
+ ** usage.
+ **/
+template <class T, int mytag=0>
+class singleton: public T
+{
+ private:
+ static mutex __mylock;
+ static singleton * __me;
+ protected:
+ singleton() : T() {};
+ virtual ~singleton() {};
+ singleton(const singleton &) {};
+ public:
+ /** Used to access the object.
+ **
+ ** Returns a reference to the instanciated singleton object. If
+ ** the object has not been accessed before, the object will be
+ ** instanciated automatically.
+ **
+ ** As is usual for the method that provides access to a singleton,
+ ** this method is static, allowing it to be called via the class
+ ** name as shown. Remember you can only assign the return value to
+ ** a reference:
+ **
+ ** mytype & a = mytype::get_instance();
+ **
+ ** Attempts to make a copy of the instance should throw a compiler
+ ** error.
+ **
+ ** This method is thread safe, but that does not imply that the
+ ** class returned is thread-safe overall; that would depend on the
+ ** implementation of the class used to instaciate the template.
+ **/
+ static singleton & get_instance()
+ {
+ // First test avoids expensive mutex cycle
+ // if the object is already allocated.
+ if (!__me)
+ {
+ scope_lock lock(__mylock);
+ // second test required in case multiple threads
+ // get past the first check.
+ if (!__me)
+ {
+ __me = new singleton;
+ };
+ };
+ return *__me;
+ };
+
+ /** Destructs and deallocates the singleton object.
+ **
+ ** After a call to this method, the singleton object will
+ ** be destructed and deallocated from memory. However, it
+ ** will be automatically reconstruted and allocated if
+ ** get_instance() called at any time afterword.
+ **/
+ void delete_me()
+ {
+ scope_lock lock(__mylock);
+ if (__me) delete __me;
+ __me = 0;
+ };
+};
+
+template <class T, int mytag> mutex singleton<T,mytag>::__mylock;
+template <class T, int mytag> singleton<T,mytag> * singleton<T,mytag>::__me = 0;
+
+} // namespace nrtb;
+
+#endif // nrtb_singleton_h
=== removed file 'common/singleton/singleton.h'
--- common/singleton/singleton.h 2010-01-14 02:24:25 +0000
+++ common/singleton/singleton.h 1970-01-01 00:00:00 +0000
@@ -1,110 +0,0 @@
-/* implements a template that takes any class provided and
- * creates a singleton for it.
- *
- * fps 2002-5-29
- */
-
-/*
- TODO: Alter to use boost::threads mutexes.
-*/
-
-
-#ifndef NRTB_singleton_h
-#define NRTB_singleton_h
-
-#include <boost/thread>
-
-namespace NRTB
-{
-
-/** Wrapper template to create singleton classes.
- **
- ** Classic Singleton pattern template, with thread-safe
- ** methods for getting an instance and destruction.
- **
- ** Singletons are classes that:
- ** (1) Insure that there are no more than one instance of the
- ** class at any time.
- ** (2) Allocate themselves automagically when accessed for the
- ** first time.
- ** (3) Provide a globally available access method to allow any
- ** scope to gain access and use the instance.
- **
- ** This template can create a singleton version of any class that has a
- ** no-argument constructor. The resulting class will have the same
- ** inteface as the object used to create the template with the constructor
- ** and destructor made protected and the addition of the get_instance()
- ** and delete_me() methods.
- **
- ** See the documentation on the get_instance() and delete_me() methods for
- ** usage.
- **/
-template <class T, int mytag=0>
-class singleton: public T
-{
- private:
- static mutex __mylock;
- static singleton * __me;
- protected:
- singleton() : T() {};
- virtual ~singleton() {};
- singleton(const singleton &) {};
- public:
- /** Used to access the object.
- **
- ** Returns a reference to the instanciated singleton object. If
- ** the object has not been accessed before, the object will be
- ** instanciated automatically.
- **
- ** As is usual for the method that provides access to a singleton,
- ** this method is static, allowing it to be called via the class
- ** name as shown. Remember you can only assign the return value to
- ** a reference:
- **
- ** mytype & a = mytype::get_instance();
- **
- ** Attempts to make a copy of the instance should throw a compiler
- ** error.
- **
- ** This method is thread safe, but that does not imply that the
- ** class returned is thread-safe overall; that would depend on the
- ** implementation of the class used to instaciate the template.
- **/
- static singleton & get_instance()
- {
- // First test avoids expensive mutex cycle
- // if the object is already allocated.
- if (!__me)
- {
- scope_lock lock(__mylock);
- // second test required in case multiple threads
- // get past the first check.
- if (!__me)
- {
- __me = new singleton;
- };
- };
- return *__me;
- };
-
- /** Destructs and deallocates the singleton object.
- **
- ** After a call to this method, the singleton object will
- ** be destructed and deallocated from memory. However, it
- ** will be automatically reconstruted and allocated if
- ** get_instance() called at any time afterword.
- **/
- void delete_me()
- {
- scope_lock lock(__mylock);
- if (__me) delete __me;
- __me = 0;
- };
-};
-
-template <class T, int mytag> mutex singleton<T,mytag>::__mylock;
-template <class T, int mytag> singleton<T,mytag> * singleton<T,mytag>::__me = 0;
-
-} // namespace NRTB;
-
-#endif // NRTB_singleton_h
=== added file 'common/singleton/singleton_test.cpp'
--- common/singleton/singleton_test.cpp 1970-01-01 00:00:00 +0000
+++ common/singleton/singleton_test.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,55 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+// singleton template test program
+
+#include "singleton.h"
+#include <serializer.h>
+#include <iostream>
+
+using namespace nrtb;
+using namespace std;
+
+typedef singleton<serializer> sequence_type;
+
+int main()
+{
+
+ cout << "============== singleton unit test ================"
+ << endl;
+ int er_count = 0;
+
+ sequence_type & a = sequence_type::get_instance();
+
+ for (int i=0; i<10; i++)
+ {
+ cout << a();
+ };
+
+ sequence_type & b = sequence_type::get_instance();
+
+ if ( b() != 10)
+ {
+ er_count++;
+ };
+
+ cout << "\n=========== singleton test " << (er_count ? "failed" : "passed")
+ << " =============" << endl;
+
+ return er_count;
+};
=== removed file 'common/singleton/singleton_test.cpp'
--- common/singleton/singleton_test.cpp 2010-01-14 02:24:25 +0000
+++ common/singleton/singleton_test.cpp 1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
-// singleton template test program
-
-
-#include <http_page.h>
-#include "singleton.h"
-#include <iostream>
-
-using namespace NRTB;
-using namespace std;
-
-int main()
-{
- typedef singleton<http_page> p_type;
-
- {
- p_type & getter = p_type::get_instance();
-
- cout << "newly initialized code: " << getter.result_code() << endl;
-
- getter.get("127.0.0.1:80","/");
- };
-
- cout << "first response code: " << p_type::get_instance().result_code()
- << endl;
-
- p_type::get_instance().get("127.0.0.1:80","/not_there.html");
-
- cout << "second response code: " << p_type::get_instance().result_code()
- << endl;
-
- p_type::get_instance().delete_me();
-};
=== added directory 'common/sockets'
=== added file 'common/sockets/Makefile'
--- common/sockets/Makefile 1970-01-01 00:00:00 +0000
+++ common/sockets/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,38 @@
+#***********************************************
+# This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: socket_test
+ @./socket_test
+ @cp -v base_socket.h ../include/
+ @cp -v base_socket.o ../obj/
+ @echo build complete
+
+socket_test: base_socket.o socket_test.cpp
+ @rm -f socket_test
+ g++ -c socket_test.cpp -I ../include
+ g++ -o socket_test socket_test.o base_socket.o ../obj/hires_timer.o ../obj/common.o ../obj/base_thread.o -lpthread
+
+
+base_socket.o: base_socket.cpp base_socket.h Makefile
+ @rm -f base_socket.o
+ g++ -c -O3 base_socket.cpp -I ../include
+
+clean:
+ @rm -vf *.o ../include/base_socket.h socket_test
+ @echo all objects and executables have been erased.
+
=== added file 'common/sockets/base_socket.cpp'
--- common/sockets/base_socket.cpp 1970-01-01 00:00:00 +0000
+++ common/sockets/base_socket.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,853 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+// see base_socket.h for documentation
+
+#include "base_socket.h"
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <boost/lexical_cast.hpp>
+
+using boost::lexical_cast;
+using std::string;
+
+namespace nrtb
+{
+
+// added for Solaris 2.6
+#ifdef no_inet_aton
+int inet_aton(const char * addr, in_addr * inp)
+{
+ // this workaround defines inet_aton in term in_addr. Drawbacks are
+ // that there is no useful error checking.
+ inp->s_addr = inet_addr(addr);
+ return (inp->s_addr != 0);
+};
+#endif // no_inet_aton
+
+// added for solaris 2.6
+#ifdef no_socklen_t
+typedef int socklen_t;
+#endif // no_sock_len_t
+
+int tcp_socket::transmit(const string & data)
+{
+ return send(mysock,data.c_str(),data.length(),MSG_DONTWAIT);
+};
+
+int tcp_socket::receive(std::string & data, int limit)
+{
+ errno = 0;
+ data = "";
+ // make more room in the input buffer if needed.
+ int bufsize = limit + 1;
+ if (bufsize > inbuff.size()) inbuff.resize(bufsize);
+ // get the data to inbuff
+ int returnme = recv(mysock,(void *) &(inbuff[0]),limit,0);
+ // did we get anything?
+ if (returnme > 0)
+ {
+ // yes.. store for the caller.
+ data.resize(returnme,0);
+ for (int i=0; i<returnme; i++)
+ {
+ data[i] = inbuff[i];
+ };
+ }
+ // ... or was there an error?
+ else if (errno)
+ {
+ // flag it for the caller.
+ returnme = -1;
+ };
+ return returnme;
+};
+
+tcp_socket::tcp_socket(bool autoclose)
+{
+ close_on_destruct = autoclose;
+ mysock = socket(AF_INET,SOCK_STREAM,0);
+ _status = sock_init;
+ _last_error = 0;
+};
+
+tcp_socket::tcp_socket(int existing_socket, bool autoclose)
+{
+ close_on_destruct = autoclose;
+ mysock = existing_socket;
+ _status = sock_connect;
+ _last_error = 0;
+};
+
+tcp_socket::~tcp_socket()
+{
+ if (close_on_destruct)
+ {
+ shutdown(mysock,SHUT_RDWR);
+ ::close(mysock);
+ _status = sock_undef;
+ };
+};
+
+void tcp_socket::reset()
+{
+ // close the old socket if we can.
+ try { close(); } catch (tcp_socket::general_exception) {};
+ // get a new one.
+ mysock = socket(AF_INET,SOCK_STREAM,0);
+ // set up the default conditions;
+ _status = sock_init;
+ _last_error = 0;
+};
+
+sockaddr_in tcp_socket::str_to_sockaddr(const string & address)
+{
+ sockaddr_in in_address;
+ // init our address structure.
+ memset(&in_address,0,sizeof(in_address));
+ //in_address.sin_len = 16;
+ in_address.sin_family = AF_INET;
+ // seperate the IP and port addresses.
+ const int IP = 0;
+ const int PORT = 1;
+ strlist addr;
+ addr = split(address,':');
+ if (addr.size() != 2)
+ {
+ throw bad_address_exception();
+ };
+ if (addr[IP] != "*")
+ {
+ // first attempt name resolution
+ hostent * name = gethostbyname(addr[IP].c_str());
+ if ((name != 0) && (name->h_length > 0))
+ {
+ in_address.sin_addr = *( (in_addr *) (name->h_addr_list[0]));
+ }
+ else if (!inet_aton(addr[IP].c_str(),&in_address.sin_addr))
+ {
+ throw bad_address_exception();
+ };
+ };
+ if (addr[PORT] != "*")
+ {
+ // get the good port;
+ uint16_t port = lexical_cast<uint16_t>(addr[PORT]);
+ in_address.sin_port = htons(port);
+ };
+ return in_address;
+};
+
+string tcp_socket::sockaddr_to_str(const sockaddr_in & address)
+{
+ // get the IP address.
+ string returnme = inet_ntoa(address.sin_addr);
+ // adjust for wild card if appropriate.
+ if (returnme == "0.0.0.0") { returnme = "*"; };
+ // get the port address
+ returnme += ":";
+ uint16_t myport = ntohs(address.sin_port);
+ if (myport > 0 )
+ {
+ returnme += lexical_cast<string>(myport);
+ }
+ else
+ {
+ returnme += "*";
+ };
+ return returnme;
+};
+
+void tcp_socket::bind(const string & address)
+{
+ if (address != "*:*")
+ {
+ sockaddr_in in_address = str_to_sockaddr(address);
+ socklen_t socklen = sizeof(in_address);
+ // try bind.
+ if (::bind(mysock, (sockaddr*) &in_address, socklen))
+ {
+ // failed in some way.
+ _last_error = errno;
+ cant_bind_exception e;
+ e.store(address);
+ throw e;
+ };
+ }; // nop if "*:*" is passed in.
+};
+
+void tcp_socket::connect(const string & address, int timeout)
+{
+ sockaddr_in in_address = str_to_sockaddr(address);
+ if (::connect(mysock, ( sockaddr *) &in_address, sizeof(in_address)))
+ {
+ _last_error = errno;
+ bad_connect_exception e;
+ e.store(address);
+ throw e;
+ }
+ else
+ {
+ _status = sock_connect;
+ };
+};
+
+void tcp_socket::close()
+{
+ if (::close(mysock))
+ {
+ _last_error = errno;
+ throw close_exception();
+ }
+ else
+ {
+ _status = sock_close;
+ };
+};
+
+int tcp_socket::put(string s, int timeout)
+{
+ // set up for the testing loop.
+ time_t endtime = time(NULL);
+ int orig_len = s.length();
+ endtime += timeout;
+ // Try to send.
+ bool done = false;
+ while (!done)
+ {
+ // make an attempt.
+ int results = transmit(s);
+ // remove the chars already sent from the buffer and set done
+ // if appropriate.
+ if (results > 0)
+ {
+ s.erase(0,results);
+ done = (s.length() == 0) ? true : false;
+ };
+ // decide what do do about any possible error results.
+ if (results == -1)
+ {
+ switch (errno)
+ {
+ case EBADF :
+ case ENOTSOCK:
+ {
+ done = true;
+ _last_error = errno;
+ _status = sock_close;
+ not_open_exception e;
+ e.store(s);
+ throw e;
+ break;
+ };
+ case ENOBUFS :
+ case ENOMEM :
+ {
+ done = true;
+ _last_error = errno;
+ buffer_full_exception e;
+ e.store(s);
+ throw e;
+ break;
+ };
+ case EINTR:
+ case EAGAIN :
+// case EWOULDBLOCK :
+ {
+ usleep(50);
+ break;
+ };
+ default :
+ {
+ done = true;
+ _last_error = errno;
+ general_exception e;
+ e.store(s);
+ throw e;
+ break;
+ };
+ }; // switch (errno)
+ }; // until error,
+ if ((timeout > 0) && ((time(NULL) >= endtime)))
+ {
+ done = true;
+ };
+ }; // continue sending until success or error or timeout.
+ // check for timeout.
+ int sent = orig_len - s.length();
+ if (sent != orig_len)
+ {
+ timeout_exception e;
+ e.store(s);
+ throw e;
+ };
+ return sent;
+};
+
+string tcp_socket::get(int maxlen, int timeout)
+{
+/*
+micro_timer run_time;
+run_time.start();
+std::cerr << "ENTER get(" << maxlen << "," << timeout << ")" << std::endl;
+*/
+ string returnme = "";
+ returnme.reserve(maxlen);
+ string in_buffer;
+ // set out timeout marker.
+ time_t endtime = time(NULL);
+ endtime += timeout;
+ timeval wait;
+ // input loop
+ bool done = false;
+ while (!done)
+ {
+ time_t waittime = endtime - time(NULL);
+ wait.tv_sec = (waittime > 0) ? waittime : 0;
+ wait.tv_usec = 10;
+ // set max timeout.
+ // the assert is used because some platforms may not properly support
+ // the SO_RCVTIMEO socket option.
+ setsockopt(mysock,SOL_SOCKET,SO_RCVTIMEO,&wait,sizeof(wait));
+ // is there any data waiting?
+ int results = receive(in_buffer,maxlen-returnme.size());
+ // was there an error?
+ switch (results)
+ {
+ case 0:
+ {
+ if (returnme == "")
+ {
+ throw not_open_exception();
+ }
+ else
+ {
+ done = true;
+ }
+ break;
+ }
+ case -1 :
+ {
+ switch (errno)
+ {
+ case EBADF :
+ case ENOTCONN :
+ case ENOTSOCK :
+ {
+ done = true;
+ _last_error = errno;
+ _status = sock_close;
+ if (returnme == "")
+ {
+ not_open_exception e;
+ e.store(returnme);
+ throw e;
+ };
+ break;
+ }
+ case EAGAIN : // shouldn't happen, but .....
+ case EINTR : // this too....
+ {
+ if (timeout == 0)
+ {
+ done = true;
+ };
+ usleep(50);
+ break;
+ }
+ default :
+ {
+ done = true;
+ _last_error = errno;
+ if (returnme == "")
+ {
+ general_exception e;
+ e.store(returnme);
+ throw e;
+ };
+ break;
+ }
+ }; // there was an error.
+ break;
+ }
+ default:
+ {
+ returnme += in_buffer;
+ break;
+ }
+ };
+ // check boundry conditions;
+ // -- maxlen only effective if maxlen > 0.
+ if ((maxlen > 0) && (returnme.length() >= maxlen)) { done = true; };
+ // -- timeout only effective if timeout > 0.
+ if ((timeout > 0) && (time(NULL) > endtime)) { done = true; };
+ if (timeout == 0) { done = true; };
+/*
+std::cerr << "\tstatus: " << returnme.size()
+<< ", done = " << done
+<< " (" << run_time.interval() << ")"
+<< std::endl;
+*/
+ }; // input loop.
+ // did we time out?
+ if ((timeout > 0) && (time(NULL) > endtime))
+ {
+ timeout_exception e;
+ e.store(returnme);
+ throw e;
+ };
+ // done!
+ return returnme;
+};
+
+
+string tcp_socket::getln(string eol, int maxlen, int timeout)
+{
+ // args check.
+ if (eol == "")
+ {
+ throw bad_args_exception();
+ return eol;
+ };
+ // setup.
+ string returnme = "";
+ returnme.reserve(maxlen);
+ int targetlen = eol.length();
+ int searchlen = targetlen - 1;
+ time_t endtime = time(NULL);
+ endtime += timeout;
+ bool done = false;
+ bool success = false;
+ // first, get at least as many characters as are in the eol string.
+ try
+ {
+ int wait_time = timeout;
+ if (timeout < 0) { wait_time = 0; };
+ returnme = get(targetlen,wait_time);
+ }
+ catch (timeout_exception e)
+ {
+ // if get timed out we may be done.
+ if (timeout >= 0) { done = true; };
+ returnme = e.comment();
+ };
+ // We only need to continue if we have not gotten our token yet.
+ if ((!done) && (returnme.length() >= targetlen))
+ {
+// success = returnme.substr(returnme.length()-targetlen,targetlen) == eol;
+ success = returnme.find(eol,returnme.length()-targetlen) != string::npos;
+ };
+ while (!done && !success)
+ {
+ // still more to get, if we have time.
+ int timeleft = endtime - time(NULL);
+ timeleft = (timeleft > 0) ? timeleft : 0;
+ // can we increase our get length?
+ int getlen = 1;
+ if (searchlen > 1)
+ {
+ // see if a token start is near.
+ if (returnme.find(eol[0],returnme.length()-searchlen) == string::npos)
+ {
+ // great!.. get just less than one token's worth.
+ getlen = searchlen;
+ }
+ };
+ // okay.. let's get the next chunk
+ try
+ {
+ returnme += get(getlen,timeleft);
+ }
+ catch (timeout_exception e)
+ {
+ if (timeout >= 0) { done = true; };
+ };
+ // check boundary conditions.
+ // -- did we get it?
+ if (returnme.length() >= targetlen)
+ {
+// success = returnme.substr(returnme.length()-targetlen,targetlen) == eol;
+ success = returnme.find(eol,returnme.length()-targetlen) != string::npos;
+ };
+ // -- maxlen only effective if maxlen > 0.
+ if ((maxlen > 0) && (returnme.length() >= maxlen)) { done = true; };
+ };
+ // did we error out?
+ if (!success)
+ {
+ // what type of error was it?
+ if ((maxlen > 0) && (returnme.length() >= maxlen))
+ {
+ overrun_exception e;
+ e.store(returnme);
+ throw e;
+ }
+ else
+ {
+ timeout_exception e;
+ e.store(returnme);
+ throw e;
+ };
+ };
+ return returnme;
+};
+
+string tcp_socket::get_local_address()
+{
+ sockaddr_in myaddr;
+ socklen_t len = sizeof(myaddr);
+ int results = getsockname(mysock,(sockaddr *) &myaddr,&len);
+ // check that getsockname was okay.
+ if (results == -1)
+ {
+ // some type of error occurred.
+ switch (errno)
+ {
+ case ENOBUFS :
+ {
+ throw buffer_full_exception();
+ break;
+ };
+ default :
+ {
+ throw general_exception();
+ break;
+ };
+ }; // switch errno
+ }
+ return sockaddr_to_str(myaddr);
+};
+
+string tcp_socket::get_remote_address()
+{
+ sockaddr_in myaddr;
+ socklen_t len = sizeof(myaddr);
+ int results = getpeername(mysock,(sockaddr *) &myaddr,&len);
+ // check that getsockname was okay.
+ if (results == -1)
+ {
+ // some type of error occurred.
+ switch (errno)
+ {
+ case ENOTCONN :
+ {
+ _status = sock_close;
+ throw not_open_exception();
+ break;
+ };
+ case ENOBUFS :
+ {
+ throw buffer_full_exception();
+ break;
+ };
+ default :
+ {
+ throw general_exception();
+ break;
+ };
+ }; // switch errno
+ }
+ return sockaddr_to_str(myaddr);
+};
+
+
+tcp_server_socket_factory::tcp_server_socket_factory(
+ const string & address, const unsigned short int & backlog)
+{
+ // does not attempt to set up the connection initially.
+ _address = address;
+ _backlog = backlog;
+ _last_thread_fault = 0;
+ thread_return = 0;
+ in_on_accept = false;
+ connect_sock = 0;
+};
+
+tcp_server_socket_factory::~tcp_server_socket_factory()
+{
+ // are we current handling requests?
+ if (is_running())
+ {
+ // yes, this is -rude- shutdown of the listening thread.
+ // Get over it.
+ stop();
+ try
+ {
+ if (listen_sock)
+ close(listen_sock);
+ }
+ catch (...) {};
+ };
+};
+
+void tcp_server_socket_factory::start_listen()
+{
+ // take no action if the listen thread is already running.
+ if (!is_running())
+ {
+ // load up the control flags
+ in_on_accept = false;
+ okay_to_continue = true;
+ thread_return = 0;
+ // start it up!
+ detach();
+ start();
+ };
+};
+
+void tcp_server_socket_factory::stop_listen()
+{
+ // take action only if the listen thread is running.
+ if (is_running())
+ {
+ bool okay_to_kill = false;
+ // set the flag that indicates the thread should not process any more.
+ {
+ scope_lock lock(thread_data);
+ bool okay_to_kill = !in_on_accept;
+ }
+ // close the listener socket.
+ close(listen_sock);
+ // is is okay to cancel the thread?
+ if (!okay_to_kill)
+ {
+ // we'll allow up to 30 seconds for on_accept() to finish.
+ time_t endtime = time(NULL);
+ endtime += 30;
+ bool done = false;
+ while (!done)
+ {
+ // wait a second...
+ sleep(1);
+ // check boundaries.
+ // -- are we out of on_accept()?
+ {
+ scope_lock lock(thread_data);
+ done = !in_on_accept;
+ }
+ // -- are we out of time?
+ okay_to_kill = done;
+ if (time(NULL) > endtime) { done = true; };
+ };
+ };
+ // kill the listener thread.
+ if (!okay_to_kill) { throw on_accept_bound_exception(); };
+ stop();
+ };
+};
+
+bool tcp_server_socket_factory::listening()
+{
+ bool running = is_running();
+ if (!running)
+ {
+ // check to be sure the thread did not die due to an error.
+ if (thread_return != 0)
+ {
+ // Clear the error state before throwing an exception.
+ _last_thread_fault = thread_return;
+ thread_return = 0;
+ // if thread_return was non-zero, it is assumed the thread died an
+ // evil and useless death. Scream in anger!
+ throw listen_terminated_exception();
+ };
+ };
+ return running;
+};
+
+unsigned short int tcp_server_socket_factory::backlog()
+{
+ return _backlog;
+};
+
+void tcp_server_socket_factory::run()
+{
+ /* Put this entire thing in a try block to protect the application.
+ * Without this, an untrapped exception thrown here or in the user supplied
+ * on_accept() method would abort the entire application instead of just this
+ * thread.
+ */
+ try
+ {
+ bool go = true;
+ // set up our listening socket.
+ listen_sock = socket(AF_INET,SOCK_STREAM,0);
+ sockaddr_in myaddr;
+ try
+ {
+ myaddr = tcp_socket::str_to_sockaddr(_address);
+ }
+ catch (...)
+ {
+ // probably a tcp_socket::bad_address_exception,
+ // but any reason will do.
+ go = false;
+ };
+ if (bind(listen_sock,(sockaddr *) &myaddr,sizeof(myaddr)))
+ {
+ // bind did not work.
+ go = false;
+ };
+ if (listen(listen_sock,_backlog))
+ {
+ // listen failed in some way.. I don't care which.
+ go = false;
+ };
+ // processing loop
+ while (go)
+ {
+ // are we okay to proceed?
+ {
+ scope_lock lock(thread_data);
+ in_on_accept = false;
+ go = okay_to_continue;
+ }
+ if (!go)
+ {
+ close(listen_sock);
+ exit(0);
+ };
+ // accept a new connection
+ bool good_connect = true;
+ int new_conn = accept(listen_sock,NULL,NULL);
+ // validate the accept return value.
+ if (new_conn == -1)
+ {
+ // accept returned an error.
+ switch (errno)
+ {
+ case ENETDOWN :
+ case EPROTO :
+ case ENOPROTOOPT :
+ case EHOSTDOWN :
+ case ENONET :
+ case EHOSTUNREACH :
+ case EOPNOTSUPP :
+ case ENETUNREACH :
+ case EAGAIN :
+// case EWOULDBLOCK :
+ case EPERM :
+ case ECONNABORTED :
+ {
+ good_connect = false;
+ break;
+ };
+ default :
+ {
+ // for any other error, we're going to shutdown the
+ // this listener thread.
+ {
+ scope_lock lock(thread_data);
+ // If the error was caused by an attempt to close the
+ // socket and shutdown the thread, don't store an
+ // error flag.
+ if (okay_to_continue)
+ {
+ thread_return = errno;
+ }
+ else
+ {
+ thread_return = 0;
+ };
+ };
+ exit(errno);
+ break;
+ };
+ }; // switch (errno)
+ }; // error thrown by accept.
+ // create connect_sock
+ connect_sock = new tcp_socket(new_conn);
+ // are we okay to proceed?
+ {
+ scope_lock lock(thread_data);
+ go = okay_to_continue;
+ // if we are go, then we'll be going to on_accept next.
+ // Therefore, this equality makes sense if you hold your
+ // head just so. :-P
+ in_on_accept = go;
+ }
+ if (!go)
+ {
+ delete connect_sock;
+ close(listen_sock);
+ exit(0);
+ };
+ // only call on_accept() if we have a good connection.
+ if (good_connect)
+ {
+ // make the thread easily cancelable.
+ set_cancel_anytime();
+ // call on_accept
+ on_accept();
+ // set back to cancel_deferred.
+ set_deferred_cancel();
+ };
+ }; // while go;
+ // if we get here then things are not going well...
+ {
+ scope_lock lock(thread_data);
+ thread_return = -2;
+ }
+ close(listen_sock);
+ exit(-2);
+ }
+ catch (...)
+ {
+ /* an untrapped exception was thrown by someone in this thread.
+ * We'll shutdown this thread and put -1 in the thread_return field
+ * to let the world know that we don't know what killed us.
+ */
+ thread_data.lock();
+ thread_return = -1;
+ thread_data.unlock();
+ close(listen_sock);
+ exit(-1);
+ };
+};
+
+} // namespace nrtb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
=== added file 'common/sockets/base_socket.h'
--- common/sockets/base_socket.h 1970-01-01 00:00:00 +0000
+++ common/sockets/base_socket.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,588 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef base_socket_header
+#define base_socket_header
+
+#include <base_thread.h>
+#include <boost/shared_ptr.hpp>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+namespace nrtb
+{
+
+/** Provides an easy to use TCP/IP bidirectional communciations socket.
+ **
+ ** tcp_socket contains all the functionality you'll need for 98% of
+ ** your normal, non-encrypted networking communications needs. Everything
+ ** from creating the socket, binding it if needed, name resolution,
+ ** connecting, sending and receiving, etc. is handled here.
+ **
+ ** Unless you override it, a tcp_socket closes automatically when it
+ ** goes out of scope, preventing hanging sockets.
+ **/
+class tcp_socket
+{
+ public:
+
+ typedef enum {sock_undef,sock_init,sock_connect,sock_close} state;
+
+ protected:
+
+ int mysock;
+ bool close_on_destruct;
+ state _status;
+ int _last_error;
+ std::vector<unsigned char> inbuff;
+
+ virtual int transmit(const std::string & data);
+ virtual int receive(std::string & data,int limit);
+
+ public:
+
+ /// Use to catch all socket exceptions.
+ class general_exception: public base_exception {};
+ /// Thrown by send or get* if the socket is not open for use.
+ class not_open_exception: public general_exception {};
+ /// Thrown by get* if more than maxlen chars are receieved.
+ class overrun_exception: public general_exception {};
+ /// Thrown if a timeout occured during an operation.
+ class timeout_exception: public general_exception {};
+ /// Thrown if the socket could not connect to the target.
+ class bad_connect_exception: public general_exception {};
+ /// Thrown if the address argument is not interpretable.
+ class bad_address_exception: public general_exception {};
+ /// Thrown by bind() if the address/port is not bindable.
+ class cant_bind_exception: public general_exception {};
+ /// Thrown by close() if an error is reported.
+ class close_exception: public general_exception {};
+ /// Thrown by send if the message is too large.
+ class msg_too_large_exception: public general_exception {};
+ /// Thrown by send if buffer overflows.
+ class buffer_full_exception: public general_exception {};
+ /// Thrown if the socket lib complains of bad args.
+ class bad_args_exception: public general_exception {};
+
+ /** Constructs a tcp_socket.
+ **
+ ** Prepares this socket for use. If autoclose=true the socket will be
+ ** closed when the object is distructed, otherwise it will be left in
+ ** whatever state it happened to be in at the time.
+ **
+ ** BTW, I _don't_ recommend you leave the socket open. If the tcp_socket
+ ** was instanciated via this constructor, there will not be any other
+ ** references to the socket save the one held privately, so a socket left
+ ** open will be there forever. This will almost certainly warrent an
+ ** unpleasent visit by one or more SAs of the box(es) hosting your
+ ** application over time.
+ **
+ ** autoclose=true; it's the only way to be sure. ;)
+ **/
+ tcp_socket(bool autoclose=true);
+
+ /** Constructs from an already existing socket.
+ **
+ ** Constructs the tcp_socket from an already existing socket. This
+ ** constructor is specifically for use with the traditional listen()
+ ** sockets function, and probably should not be used in other contexts.
+ **
+ ** If autoclose is true the socket will be closed on destruction of the
+ ** object, otherwise the socket will be left in whatever state it happened
+ ** to be in at the time. As a general rule I suspect you'll want leave
+ ** autoclose defaulted to true.
+ **
+ ** In most cases where you would use listen(), take a look at the
+ ** tcp_server_socket_factory class; it'll probably do everything you need
+ ** with a minimum of programming overhead.
+ **
+ ** BTW, I _don't_ recommend you leave the socket open. If the socket variable
+ ** used for instanciation was discarded, there will not be any other
+ ** references to the socket save the one held privately, so a socket left
+ ** open will be there forever. This will almost certainly warrent an
+ ** unpleasent visit by one or more SAs of the box(es) hosting your
+ ** application over time.
+ **
+ ** autoclose=true; it's the only way to be sure. ;)
+ **/
+ tcp_socket(int existing_socket, bool autoclose=true);
+
+ /** Destructs a tcp_socket.
+ **
+ ** If autoclose was set to true, the socket will be closed if it is open
+ ** at the time of destruction. If no, the socket will be left in whatever
+ ** state it happened to be in at the time.
+ **/
+ virtual ~tcp_socket();
+
+ /** Resets the socket after a hard error.
+ **
+ ** In cases where the socket was invlidated by the operating system
+ ** (typically cases where a SIGPIPE was raised by the TCP/IP stack),
+ ** use this function to discard the old socket id and aquire a new one.
+ ** Understand that this will make the tcp_socket useful again, but you
+ ** will have to reconnect before any traffic can be sent.
+ **/
+ virtual void reset();
+
+ /** Creates a sockaddr_in from a formatted string.
+ **
+ ** address is an "IP:port" formatted string; i.e. 129.168.1.1:80.
+ ** "*" may use used to substatute for the IP, port or both.
+ **
+ ** Returns a properly formatted sockaddr_in struct for use with connect,
+ ** bind, etc. Throws a bad_address_exception if the address argument is
+ ** mal-formed.
+ **/
+ static sockaddr_in str_to_sockaddr(const std::string & address);
+
+ /** Creates a string from a sockaddr_in.
+ **
+ ** The returned string is of the same format accepted as arguments to the
+ ** to the bind() and connect() methods.
+ **
+ ** May throw a bad_address_exception if the sock_addr_in is not
+ ** properly formatted.
+ **/
+ static std::string sockaddr_to_str(const sockaddr_in & address);
+
+ /** binds the tcp_socket to a local address/port.
+ **
+ ** You do _not_ need to use this method if you are setting up a tcp_socket
+ ** for client use. If you don't call bind an ephemeral port and the host's
+ ** IP number will be assigned, as is normal and preferred for client sockets.
+ **
+ ** In very rare cases where you have a multi-homed host and a real
+ ** requirement to specify which IP this connection uses as local, you can
+ ** use this method to select the port and address the client will use.
+ **
+ ** address: string of the format IP:port; example "255.255.255.255:91".
+ ** If you wish to accept input on any active IP address in this host,
+ ** use "*" for the IP address, as in "*:80". If you want to fix the address
+ ** but leave the port up to the host, use "*" for the port, as in
+ ** "10.101.78.33:*". Obviously, using "*:*" as the address gives the
+ ** same results as not calling this method at all.
+ **
+ ** If the address argument is mal-formed a bad_address_exception will be
+ ** thrown and no changes will be made to the tcp_socket.
+ **
+ ** If the address is not bindable (already in use is a common cause) a
+ ** cant_bind_exception will be thrown.
+ **/
+ void bind(const std::string & address);
+
+ /** Opens the connection.
+ **
+ ** address: string of the format IP:port; example "255.255.255.255:91".
+ ** If the address is mal-formed a bad_address_exception will be thrown.
+ **
+ ** If timeout is specified and >0, the system will not wait more than
+ ** timeout seconds before throwing a timeout_exception.
+ **
+ ** If the connection is not set up properly, a bad_connection_exception
+ ** will be thrown.
+ **/
+ void connect(const std::string & address, int timeout=0);
+
+ /** Closes an open connection.
+ **
+ ** If a problem is reported a close_exception will be thrown. Possible
+ ** causes include the socket not being open, unsent data pending in the
+ ** tcp stack, etc.
+ **/
+ virtual void close();
+
+ /** Returns the current status.
+ **
+ ** Possibles states are tcp_socket::undef, tcp_socket::init,
+ ** tcp_socket::connect, and tcp_socket::close.
+ **/
+ state status() { return _status; };
+
+ /** Returns the last socket error recieved.
+ **
+ ** Will contain zero if no errors have occured on the socket since
+ ** the tcp_socket was instanciated, or the errno returned on the most recent
+ ** error otherwise.
+ **
+ ** Check the man pages for bind(), connect(), close(), socket(), send(),
+ ** and recv() for possible values. They are defined in error.h.
+ **/
+ int last_error() { return _last_error; };
+
+ /** Sends the string to the target host.
+ **
+ ** If timeout is specified and >0, a timeout_exception will be thrown
+ ** if the send does not succeed within timeout seconds.
+ **
+ ** If socket is not connected or closes during sending a
+ ** not_open_exception will be thrown.
+ **
+ ** If an exception is thrown, the characters that were not
+ ** sent will be available via the exception's comment() method (as
+ ** a string, of course).
+ **/
+ int put(std::string s, int timeout=10);
+
+ /** gets a string of characters from the socket.
+ **
+ ** This method allows the reception of a string of characters
+ ** limited by length and time. Use this method when you are expecting a
+ ** stream if data that you either know the length of or where there is
+ ** no specific ending character. If the expected traffic is organized
+ ** into messages or lines ending with a specific character or string,
+ ** use the getln() method instead.
+ **
+ ** Examples:
+ **
+ ** get() or get(1) will return a string containing a single
+ ** character or throw a timeout in 10 seconds if one is not
+ ** recieved.
+ **
+ ** get(100,30) will return when 100 characters have been recieved or
+ ** throw a timeout in 30 seconds.
+ **
+ ** get(100,-1) will return after 100 characters have been recieved, and will
+ ** _never_ time out. I strongly recommend that at the very least use timeout
+ ** values >= 0 to this method to prevent program long term program stalls.
+ **
+ ** If maxlen==0, this method will return immediately with whatever characters
+ ** happen to be in the socket's input buffer at the time.
+ **
+ ** If maxlen>0 and timeout<0, this method will block until maxlen characters
+ ** have been recieved and then return. NOTE: this method could block
+ ** forever if the sending host does not send maxlen characters and timeout
+ ** is < 0.
+ **
+ ** If maxlen>0 and timeout>0 this method will return upon recieving
+ ** maxlen characters or throw a timeout_exception after timeout seconds
+ ** have expired in the worse case.
+ **
+ ** In all cases the method returns a string containing the characters
+ ** recieved, if any.
+ **
+ ** If the timeout expires before the required number of characters are
+ ** recieved a timeout_exception will be thrown.
+ **
+ ** If the socket is not connected or closes while being read a
+ ** not_open_exception will be thrown.
+ **
+ ** If an exception is thrown, any characters already recieved
+ ** will be available via the exception's comment() method.
+ **/
+ std::string get(int maxlen=1, int timeout=10);
+
+ /** gets a string of characters from the socket.
+ **
+ ** This method is for the reception of messages (or lines) that have a
+ ** specific ending string. As such it considers the recept of maxlen
+ ** characters or a timeout without the designated eol string to be
+ ** a problem and will throw an exception in those cases. This behavor
+ ** differs from that of the get() method, which simply returns when it
+ ** has received its limit.
+ **
+ ** If there is no specific eol or eom string in your expected data
+ ** stream use the get() method instead.
+ **
+ ** Examples:
+ **
+ ** getln() will return all characters recieved up to and including the
+ ** first carrage return or throw a timeout if a carrage return is not
+ ** recieved after 10 seconds.
+ **
+ ** getln("</alert>") will return all characters recieved up to and
+ ** including "</alert>" or throw a timeout if "</alert>" is not recieved
+ ** within 10 seconds.
+ **
+ ** getln(":",30) will return all characters recieved up to and including
+ ** ":" if ":" is recieved by the 30th character. If 30 characters are recieved
+ ** before ":", an overrun_exception would be thrown. A timeout would be
+ ** thrown if ":" is not recieved within 10 seconds.
+ **
+ ** getln(":",30,20) would react exactly like getln(":",30) except the timeout
+ ** period would be 20 seconds instead of the default 10.
+ **
+ ** getln(":",30,-1) will react exactly like getln(":",30) except that no
+ ** timeout will ever be thrown. In this case, the method may block
+ ** forever if no ":" is received and it never receives 30 characters.
+ ** I strongly recommend that at the very least that timeout be a
+ ** value >= 0 to this method to prevent program long term program stalls.
+ **
+ ** If maxlen==0 and timeout<0, this method will block until the eol
+ ** string is recieved from the host or maxlen characters
+ ** have been recieved. and then return. NOTE: this method could block
+ ** forever if the sending host does not send maxlen characters or the
+ ** eol string and timeout is < 0;
+ **
+ ** If maxlen>0 and timeout>0 this method will return upon recieving the
+ ** eol string, when maxlen characters have be recieved or when
+ ** timeout seconds have expired.
+ **
+ ** In all cases the method returns a string containing the characters
+ ** recieved, if any, including the eol string.
+ **
+ ** If the timeout expires before eol is recieved or maxlen characters are
+ ** recieved a timeout_exception will be thrown.
+ **
+ ** If maxlen characters are recieved before the eol string is recieved
+ ** an overrun_exception will be thown.
+ **
+ ** If the socket is not connected or closes while being read a
+ ** not_open_exception will be thrown.
+ **
+ ** If an exception is thrown, any characters already recieved
+ ** will be available via the exception's comment() method.
+ **/
+ std::string getln(std::string eol="\r", int maxlen=0, int timeout=10);
+
+ /** Returns the local address.
+ **
+ ** Returns the address in the same form that it needs to be in for the
+ ** bind and connect addess arguments.
+ **
+ ** May throw a buffer_full_exception if there are not enough resources,
+ ** or a general_exception if some other problem occurs.
+ **/
+ std::string get_local_address();
+
+ /** Returns the remote address.
+ **
+ ** Returns the address in the same form that it needs to be in for the
+ ** bind and connect addess arguments.
+ **
+ ** May throw a buffer_full_exception if there are not enough resources,
+ ** a not_open_exception if the socket has not been connected to a remote
+ ** host, or a general_exception if some other problem occurs.
+ **/
+ std::string get_remote_address();
+};
+
+/// smart pointer for use with tcp_sockets
+typedef boost::shared_ptr<nrtb::tcp_socket> tcp_socketp;
+
+/** Abstract "listener" TCP/IP socket for servers.
+ **
+ ** Simplifies the use of TCP/IP sockets for applications providing services.
+ ** base_sock implements a free running thread that listens for connections on
+ ** the address and port specified and on connection calls the abstract method
+ ** on_accept(). Upon return from on_accept the class returns to listening for
+ ** the next connection.
+ **
+ ** Normal useage: This is an abstract class, so you must make a descendent
+ ** class that at a minimum overrides on_accept() to provide connection handling.
+ ** That aside, a typical sequence for a descendent of this class would be:
+ **
+ ** (1) construct the object providing the address, port and backlog;
+ **
+ ** (2) when the application is ready to accept traffic it calls the
+ ** start_listen() method to start the listening on the socket;
+ **
+ ** (3) as each connection is accepted, a new connect_sock* is contructed and
+ ** then on_accept() is called;
+ **
+ ** (4) on_accept() processes the connection, using connect_sock* to receive and
+ ** send data. In most cases, on_accept will only place the new tcp_socket in
+ ** a queue for other threads to process so that on_accept() can return quickly;
+ **
+ ** (5) when on_accept() returns we start listening for the next connection.
+ **
+ ** (6) When the application wishes to stop accepting connections, it calls
+ ** the stop_listen() method, which will return when as soon any current calls
+ ** to on_accept() complete.
+ **
+ ** --WARNING--- This class is a tcp_socket factory in that a new tcp_socket is
+ ** created for every connection. It is the responsibility of the host
+ ** application to delete these when they are no longer needed, either in the
+ ** overridden on_accept() method or (more likely) later in the processing flow
+ ** when the transaction is complete.
+ **
+ ** Descendent classes must override on_accept() to provide the necessary
+ ** connection handling. See the documentation on on_accept for more details.
+ **/
+class tcp_server_socket_factory: private thread
+{
+
+ private:
+
+ int listen_sock;
+ bool in_on_accept;
+ bool okay_to_continue;
+ int thread_return;
+ int _last_thread_fault;
+ mutex thread_data;
+
+ // Provides the listener thread.
+ void run();
+
+ protected:
+
+ /// the address:port the listening socket will connect to.
+ std::string _address;
+ unsigned short int _backlog;
+
+ /** Pointer to the socket for the current connection. This is
+ ** only safe to use/manipulate after entry of the method on_accept();
+ ** at any other time this pointer may be altered without notice.
+ **
+ ** At entry to on_accept() this will point to a valid base_sock
+ ** object. It is up to the application to delete this object when
+ ** it is done with it; server_sock will not take any actions with or
+ ** on the object pointed to by listen_sock once on_accept() is called.
+ ** However, once on_accept() has returned, server_sock will create
+ ** another new base_sock and store it's address here on the next
+ ** connection.
+ **/
+ tcp_socket * connect_sock;
+
+ /** Abstract method to process connections. An on_accept() call is
+ ** made on every connection accepted immediately after constructing a
+ ** new base_sock socket for it (pointed to by connect_sock).
+ **
+ ** It is expected that a useful on_accept() method must either place
+ ** the connection information (presumably including conect_sock*) on
+ ** a queue for the application to process later, or process the connection
+ ** itself. It is desireable for on_accept() to return quickly to minimize
+ ** connection latency, so for most applications you'll want to queue
+ ** the connection for processing by another thread.
+ **
+ ** In either case, be sure the application deletes the tcp_socket object
+ ** at connect_sock when it's done with it, as you can be sure of memory
+ ** leaks if you don't. tcp_server_socket_factory never deletes any
+ ** tcp_socket object it created.
+ **
+ ** WARNING: While this class attempts to respect the integrity of any code
+ ** placed in on_accept(), you must be aware that it may be cancelled
+ ** quite rudely if any given call to on_accept() takes more than 30
+ ** seconds to process. on_accept() is run with the cancel_anytime attribute
+ ** set, and this can not be changed from within the method. Therefore,
+ ** take care that on_accept() either runs within 30 seconds or at the
+ ** very least that it does not hold any resource (mutex, etc.) locks
+ ** by the time it's run over 20 seconds or so. Failure to follow these
+ ** guidelines could result in program deadlocks should on_accept be
+ ** cancelled while holding a resource lock.
+ **/
+ virtual void on_accept() = 0;
+
+ public:
+
+ /// Use to catch all server_socket_factory exceptions.
+ class general_exception: public base_exception {};
+ /// Thrown if we can not allocate a new connect_sock* as needed.
+ class mem_exhasted_exception: public general_exception {};
+ /// Thrown by start_listen() if the IP/port could not be bound.
+ class bind_failure_exception: public general_exception {};
+ /** Thrown by stop_listen() if on_accept takes more than 30 seconds
+ ** to return.
+ **/
+ class on_accept_bound_exception: public general_exception {};
+ /// Thrown by by the listen thread in case of unexpected error.
+ class listen_terminated_exception: public general_exception {};
+
+ /** Construct a tcp_server_socket_factory.
+ **
+ ** address: std::string of the format IP:port; example "255.255.255.255:91".
+ ** if you wish to accept input on any active IP address in this host,
+ ** use "*" for the IP address, as in "*:80". The port number is
+ ** required.
+ **
+ ** backlog: The number of backlog connections (connections pending accept)
+ ** allowed for this socket. The limit is operating system dependent. If
+ ** the supplied value is 0 or less, the default value for the operating
+ ** system will be used.
+ **/
+ tcp_server_socket_factory(const std::string & address,
+ const unsigned short int & backlog);
+
+ /** Destructs a server_sock.
+ **
+ ** If the server_sock is currently listening the listener thread is
+ ** shut down and the socket released immediately, rudely if necessary.
+ **
+ ** It's preferable to call stop_listen() before desconstruction to allow
+ ** graceful termination of the listener thread and teardown of the port.
+ **/
+ virtual ~tcp_server_socket_factory();
+
+ /** Initiate listening for inbound connections.
+ **
+ ** This opens the port and spawns the listener thread, then returns
+ ** immediately. An exception will be thrown if a problem occurs.
+ **
+ ** If the listener thread is already running this method returns
+ ** immediately without taking any action.
+ **
+ ** Call this method when your application is ready to start recieving
+ ** connections. Once started, the server_sock will call on_accept()
+ ** for each connection recieved until stop_listen() is called.
+ **/
+ void start_listen();
+
+ /** Stop listening for inbound connections.
+ **
+ ** This shuts down the listener thread and tears down the TCP/IP port
+ ** before returning. An exception will be thrown if a problem occurs.
+ **
+ ** If a connection is being actively processed (defined as thread
+ ** execution being post accept() and prior to the return of on_accept()),
+ ** the return will be delayed until on_accept() is complete, though
+ ** the listening socket will be torn down immediately. If on_accept()
+ ** fails to return within 30 seconds, the listener thread will be
+ ** rudely murdered and an on_accept_bound_exception will be thrown. In the
+ ** worse case it may be that the listener thread will not die for some time,
+ ** though it should be cleanly cancelable at most any time.
+ **
+ ** If the listener thread is not running this method returns immediately
+ ** without taking any actions.
+ **
+ ** Call this method when your application wishes to stop recieving input,
+ ** even if you are going to restart reception later.
+ **/
+ void stop_listen();
+
+ /** Monitors listening status.
+ **
+ ** At it's simplist, this method returns true if the socket is listening
+ ** for new connections, and false if if it not. Additionally, this
+ ** method will throw a listen_terminated_exception if the listener thread
+ ** died unexpectedly. Therefore, calling this method periodically is an
+ ** easy way to monitor the health of the listener thread.
+ **/
+ bool listening();
+
+ /** Returns the last listen thread fault code.
+ **
+ ** Will be 0 if the listener has never faulted. If -1, an untrapped
+ ** exception was caught (likely thrown by on_accept()); if -2 the thread
+ ** did not manage to initialize the listening socket successfully, and
+ ** if > 0 then it'll be the error code of the socket error caught that
+ ** forced the thread shutdown.
+ **
+ ** check this if you catch a listen_terminated_exception to find out
+ ** what really happened.
+ **/
+ int last_fault();
+
+ /** Returns the max number of backlog connections.
+ **/
+ unsigned short int backlog();
+
+};
+
+} // namepace nrtb
+
+#endif // base_socket_header
=== added file 'common/sockets/socket_test.cpp'
--- common/sockets/socket_test.cpp 1970-01-01 00:00:00 +0000
+++ common/sockets/socket_test.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,200 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <boost/random.hpp>
+#include "base_socket.h"
+#include <boost/shared_ptr.hpp>
+
+using namespace nrtb;
+using namespace std;
+
+typedef boost::shared_ptr<tcp_socket> sockp;
+
+class myserver: public tcp_server_socket_factory
+{
+public:
+ int hits;
+ int errors;
+
+ // constructor
+ myserver(const string & a, const unsigned short int & b)
+ : tcp_server_socket_factory(a,b)
+ {
+ // Don't need to lock here because we know the
+ // listener thread is not running.
+ hits = 0;
+ errors = 0;
+ };
+
+protected:
+ // on_accept() is called on each connection.
+ void on_accept()
+ {
+ try
+ {
+ sockp c(connect_sock);
+ // just return what we've recieved.
+ string msg = c->getln();
+ c->put(msg);
+ // Close the socket.
+ c->close();
+ // Update our hit count.
+ hits++;
+ }
+ catch (base_exception & e)
+ {
+ errors++;
+ cerr << "Caught " << e.what() << endl;
+ }
+ catch (...)
+ {
+ errors++;
+ cerr << "Unexpected error in on_accept()" << endl;
+ };
+ };
+};
+
+string transceiver(const string address, const string sendme)
+{
+ string returnme;
+ tcp_socket sender;
+ sender.connect(address);
+ sender.put(sendme);
+ returnme = sender.getln();
+ return returnme;
+};
+
+string address = "127.0.0.1:";
+int port_base = 17000;
+
+int main()
+{
+ int er_count = 0;
+ //set up our port and address
+ boost::mt19937 rng;
+ rng.seed(time(0));
+ boost::uniform_int<> r(0,1000);
+ stringstream s;
+ s << address << port_base + r(rng);
+ address = s.str();
+ cout << "Using " << address << endl;
+
+ myserver test_server(address,5);
+
+ try
+ {
+ // start the receiver/server
+ test_server.start_listen();
+ usleep(5e5);
+
+ // Send test messages
+ for (int i = 0; i < 1000; i++)
+ {
+ stringstream msg;
+ msg << "test message " << i << "\r";
+ string checkme = msg.str();
+ string returned = transceiver(address, checkme);
+ if (returned != checkme)
+ {
+ er_count++;
+ };
+ cout << returned.substr(0,returned.size()-1) << ": "
+ << ((returned == checkme) ? "Passed" : "Failed")
+ << endl;
+ usleep(1000);
+ };
+ test_server.stop_listen();
+ if (test_server.listening())
+ {
+ er_count++;
+ cout << "Server failed to stop. " << endl;
+ };
+ }
+ catch (myserver::bind_failure_exception)
+ {
+ cout << "Could not bind port" << endl;
+ }
+ catch (myserver::mem_exhasted_exception)
+ {
+ cout << "myserver reports out of memory." << endl;
+ }
+ catch (myserver::listen_terminated_exception)
+ {
+ cout << "Listener terminated unexpectedly." << endl;
+ }
+ catch (myserver::on_accept_bound_exception)
+ {
+ cout << "myserver::on_accept() seems bound." << endl;
+ }
+ catch (tcp_socket::bad_connect_exception & e)
+ {
+ cout << "A bad_connect_exception was thrown.\n"
+ << e.comment() << endl;
+ }
+ catch (tcp_socket::general_exception & e)
+ {
+ cout << "A tcp_socket exception was caught.\n"
+ << e.comment() << endl;
+ }
+ catch (exception & e)
+ {
+ cout << "A unexpected " << e.what() << " exception was caught." << endl;
+ };
+
+ // final check.
+ if (test_server.hits != 1000)
+ {
+ er_count++;
+ cout << "Server does not report the proper number of hits.\n"
+ << "\tExpected 1000, got " << test_server.hits
+ << endl;
+ };
+ cout << "=========== tcp_socket test complete =============" << endl;
+
+ return er_count;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
=== added directory 'common/threads'
=== added file 'common/threads/Makefile'
--- common/threads/Makefile 1970-01-01 00:00:00 +0000
+++ common/threads/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,36 @@
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: thread_test
+ @./thread_test
+ @cp -v base_thread.h ../include
+ @cp -v base_thread.o ../obj
+ @echo build complete
+
+base_thread.o: base_thread.cpp base_thread.h Makefile
+ @rm -f base_thread.o
+ g++ -c -O3 base_thread.cpp -I ../include
+
+thread_test: base_thread.o thread_test.cpp
+ @rm -f thread_test
+ g++ -c thread_test.cpp -I../include
+ g++ -o thread_test thread_test.o base_thread.o ../obj/hires_timer.o ../obj/common.o -lpthread
+
+clean:
+ @rm -rvf *.o thread_test ../include/base_thread.h ../obj/base_thread.o
+ @echo all objects and executables have been erased.
=== added file 'common/threads/base_thread.cpp'
--- common/threads/base_thread.cpp 1970-01-01 00:00:00 +0000
+++ common/threads/base_thread.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,497 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "base_thread.h"
+#include <boost/lexical_cast.hpp>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <iostream>
+
+using namespace std;
+
+using boost::lexical_cast;
+using std::string;
+
+namespace nrtb
+{
+
+void runnable::exit(int value)
+{
+ pthread_exit(&value);
+};
+
+void runnable::set_not_cancelable()
+{
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+};
+
+void runnable::set_deferred_cancel()
+{
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+};
+
+void runnable::set_cancel_anytime()
+{
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+};
+
+void runnable::test_cancel()
+{
+ pthread_testcancel();
+};
+
+thread::thread()
+{
+ alt_run = 0;
+ mytid = 0;
+ start_detached = false;
+};
+
+void * thread::startpoint(void * _this)
+{
+ thread * Me = (thread *) _this;
+ //Me->mypid = getpid();
+ // Have we had a runnable object passed?
+ if (Me->alt_run)
+ {
+ // call the run() process from the runnable passed in.
+ Me->alt_run->run();
+ }
+ else
+ {
+ // call the overridden run() method from our class.
+ Me->run();
+ };
+ return NULL;
+};
+
+thread::thread(runnable & do_this)
+{
+ if (!is_running())
+ {
+ alt_run = & do_this;
+ start_detached = false;
+ mytid = 0;
+ }
+ else
+ {
+ throw already_running_exception();
+ };
+};
+
+thread::~thread()
+{
+ stop();
+};
+
+void thread::run()
+{
+ throw no_code_exception();
+};
+
+void thread::start(runnable & do_this)
+{
+ alt_run = & do_this;
+ start();
+};
+
+void thread::start()
+{
+ if (!is_running())
+ {
+ int results;
+ // Build the attributes we'll use for this
+ pthread_attr_t attribs;
+ pthread_attr_init(&attribs);
+ if (start_detached)
+ {
+ pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED);
+ }
+ else
+ {
+ pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_JOINABLE);
+ };
+ // start the thread by using our predefined entry point
+ results = pthread_create(&mytid,&attribs,startpoint,this);
+ // Could we start? Warning: pthread_create returns false if succesful.
+ if (results)
+ {
+ throw no_start_exception();
+ };
+ }
+ else
+ {
+ // not good; we are already active!
+ throw already_running_exception();
+ };
+};
+
+void thread::stop()
+{
+ // If we are not running this is a nop.
+ if (is_running())
+ {
+ // warning: pthread_cancel returns false if successful, true otherwise.
+ if (pthread_cancel(mytid))
+ {
+ // this is the only error pthread_cancel returns...
+ throw thread_id_not_found_exception();
+ };
+ mytid = 0;
+ };
+};
+
+bool thread::is_running()
+{
+ if ((mytid == 0) || (pthread_kill(mytid,0) != 0))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ };
+};
+
+int thread::join()
+{
+ void ** ret_val;
+ if (pthread_join(mytid, ret_val))
+ {
+ throw not_joinable_exception();
+ };
+ mytid = 0;
+ return 0;
+};
+
+void thread::detach(bool yes)
+{
+ if (yes)
+ {
+ if (is_running())
+ {
+ pthread_detach(mytid);
+ }
+ else
+ {
+ start_detached = true;
+ };
+ }
+ else
+ {
+ if (is_running())
+ {
+ throw attr_change_failed_exception();
+ }
+ else
+ {
+ start_detached = false;
+ };
+ };
+};
+
+int thread::max_priority(int policy)
+{
+ return sched_get_priority_max(policy);
+};
+
+int thread::min_priority(int policy)
+{
+ return sched_get_priority_min(policy);
+};
+
+int thread::get_priority()
+{
+ int returnme = 0;
+ if (is_running())
+ {
+ int temp;
+ sched_param val;
+ int ret = pthread_getschedparam(mytid,&temp,&val);
+ if(ret != 0)
+ {
+ cerr << "\n" << __FILE__ << "::" << __FUNCTION__
+ << ": pthread_getschedparam(" << mytid
+ << ",,) returned " << ret << "!" << endl;
+ switch (ret)
+ {
+/*
+ backed out hte ESRCH check because I can not find where ESRCH
+ is defined in the 2.6 kernel include files!
+
+ case ESRCH :
+ {
+ thread_id_not_found_exception e;
+ e.store("thread::get_priority()");
+ throw e;
+ }
+*/ default :
+ {
+ general_exception e;
+ e.store("thread::get_priority()");
+ throw e;
+ }
+ };
+ };
+ returnme = val.sched_priority;
+ }
+ else
+ {
+ not_running_exception e;
+ e.store("thread:get_priority()");
+ throw e;
+ };
+ return returnme;
+};
+
+void thread::set_priority(const int pri, const int policy)
+{
+ if (!is_running())
+ {
+ not_running_exception e;
+ e.store("thread::set_priority()");
+ throw e;
+ };
+ sched_param val;
+ val.sched_priority = pri;
+ if (pthread_setschedparam(mytid,policy,&val))
+ {
+ attr_change_failed_exception e;
+ string message = "thread::set_priority("
+ + lexical_cast<string>(pri)
+ + ")";
+ e.store(message);
+ throw e;
+ };
+};
+
+void thread::yield()
+{
+ timespec t;
+ t.tv_sec = 0;
+ t.tv_nsec = 0;
+ nanosleep(&t,&t);
+};
+
+mutex::mutex()
+{
+ pthread_mutexattr_t attrib;
+ pthread_mutexattr_init(&attrib);
+ #ifdef linux_pthreads
+ pthread_mutexattr_setkind_np(&attrib,PTHREAD_MUTEX_ERRORCHECK_NP);
+ #endif
+ pthread_mutex_init(&mymid,&attrib);
+// owner = 0;
+};
+
+mutex::~mutex()
+{
+ if (pthread_mutex_destroy(&mymid))
+ {
+ throw can_not_destruct_exception();
+ };
+};
+
+void mutex::lock()
+{
+ if (pthread_mutex_lock(&mymid))
+ {
+ throw already_locked_exception();
+ };
+};
+
+void mutex::unlock()
+{
+ if (pthread_mutex_unlock(&mymid))
+ {
+ throw not_owner_exception();
+ };
+};
+
+bool mutex::try_lock()
+{
+ if (pthread_mutex_trylock(&mymid))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ };
+};
+
+cond_variable::cond_variable()
+ : mutex()
+{
+ pthread_cond_init(&mycv,NULL);
+ waiting = 0;
+ owner = 0;
+};
+
+cond_variable::~cond_variable()
+{
+ try
+ {
+ // lock the mutex before we do anything here.
+ // is there anyone waiting on this cond_variable?
+ if (!try_lock() || (waiting > 0))
+ {
+ // not good, there are others waiting on us or we are locked.
+ can_not_destruct_exception e;
+ e.store(lexical_cast<string>(waiting));
+ unlock();
+ throw e;
+ }
+ else
+ {
+ pthread_cond_destroy(&mycv);
+ };
+ // unlock before we leave.
+ unlock();
+ }
+ catch (mutex::general_exception & e)
+ {
+ throw e;
+ };
+};
+
+void cond_variable::lock()
+{
+ if (pthread_equal(pthread_self(),owner))
+ {
+ throw already_locked_exception();
+ }
+ else
+ {
+ mutex::lock();
+ owner = pthread_self();
+ };
+};
+
+void cond_variable::unlock()
+{
+ if (pthread_equal(pthread_self(),owner))
+ {
+ owner = 0;
+ mutex::unlock();
+ }
+ else
+ {
+ not_owner_exception e;
+ e.store(lexical_cast<string>(owner));
+ throw e;
+ };
+};
+
+bool cond_variable::try_lock()
+{
+ if (pthread_equal(pthread_self(),owner))
+ {
+ return true;
+ }
+ else if (mutex::try_lock())
+ {
+ owner = pthread_self();
+ return true;
+ }
+ else
+ {
+ return false;
+ };
+};
+
+void cond_variable::wait()
+{
+ if (pthread_equal(pthread_self(),owner))
+ {
+ waiting++;
+ pthread_cond_wait(&mycv,&mymid);
+ waiting--;
+ owner = pthread_self();
+ }
+ else
+ {
+ throw not_locked_exception();
+ };
+};
+
+void cond_variable::timed_wait(const time_t & seconds)
+{
+ if (pthread_equal(pthread_self(),owner))
+ {
+ waiting++;
+ time_t endtime = time(NULL) + seconds;
+ timespec delay;
+ delay.tv_sec = endtime;
+ delay.tv_nsec = 0;
+ pthread_cond_timedwait(&mycv,&mymid,&delay);
+ // pthread_cond_timewait can return a couple of error conditions,
+ // but we don't care since we will treat any return the same way.
+ waiting--;
+ owner = pthread_self();
+ }
+ else
+ {
+ throw not_locked_exception();
+ };
+};
+
+void cond_variable::signal()
+{
+ if (pthread_equal(pthread_self(),owner))
+ {
+ pthread_cond_signal(&mycv);
+ }
+ else
+ {
+ throw not_locked_exception();
+ };
+};
+
+void cond_variable::broadcast_signal()
+{
+ if (pthread_equal(pthread_self(),owner))
+ {
+ pthread_cond_broadcast(&mycv);
+ }
+ else
+ {
+ throw not_locked_exception();
+ };
+};
+
+int cond_variable::num_waiting()
+{
+ lock();
+ int i = waiting;
+ unlock();
+ return i;
+};
+
+void safe_unlock(void * mylock)
+{
+ try { static_cast<mutex *>(mylock)->unlock(); }
+ catch (...) {};
+};
+
+
+} // namespace nrtb
=== added file 'common/threads/base_thread.h'
--- common/threads/base_thread.h 1970-01-01 00:00:00 +0000
+++ common/threads/base_thread.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,627 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef base_thread_h
+#define base_thread_h
+
+#include <common.h>
+//#define linux_pthreads
+#include <pthread.h>
+
+namespace nrtb
+{
+
+/** Abstract base class for independent runnable processes.
+ **/
+class runnable
+{
+ protected:
+
+ /** Ends the thread's execution.
+ **
+ ** This is a polite run termination, which must be requested by the
+ ** running thread itself instead of being imposed by by another process.
+ ** Making this method protected insures that the only code that can call it
+ ** is the code specifed in the run method. This prevents the inadvertant
+ ** shutdown of other threads.
+ **/
+ void exit(int value);
+
+ /** Prevents runtime cancellation.
+ **
+ ** After calling this method the thread not not be cancelled by
+ ** other threads. Use this with care; if set the only way to kill
+ ** the thread will be to kill the entire application.
+ **/
+ void set_not_cancelable();
+
+ /** Enables deferred cancellation.
+ **
+ ** After calling this method the a cancellation request will be deferred
+ ** until one of the pthreads defined cancellation points or until the
+ ** thread calls test_cancel().
+ **
+ ** This is the default thread cancellability state.
+ **/
+ void set_deferred_cancel();
+
+ /** Allows the thread to be cancelled at any time.
+ **
+ ** After calling this method the thread will be cancelled immediately
+ ** if another thread attempts to cancel it.
+ **/
+ void set_cancel_anytime();
+
+ /** Terminates the thread if another thread has requested cancellation.
+ **
+ ** The code in run() can call this to accept a cancel request if the
+ ** thread is currently in deferred_cancel status. Calling this method
+ ** if cancellation has not been requested has no effect on the threads
+ ** execution.
+ **
+ ** This should be called periodically in cancelable long run threads to
+ ** insure that cancel requests are accepted in a timely manner.
+ **/
+ void test_cancel();
+
+ public:
+
+ /** (Abstract) The code to be run.
+ **
+ ** Override this class to provide the code that will be run
+ ** in a descendent class.
+ **/
+ virtual void run() = 0;
+
+ /** Destructor.
+ **
+ ** Base destructor is a nop.
+ **/
+ virtual ~runnable() { };
+};
+
+/** Basic thread class.
+ **
+ ** Usage: You must override the run() method or pass in a descendent of
+ ** "runnable" to provide the code the thread will execute when started. Once
+ ** this is done, call start() to start the thread, and call stop to forcefully
+ ** end thread execution.
+ **
+ ** NOTE: Passing out of scope will kill the thread. This is a safety thing
+ ** because the private data the thread may be using becomes undefined in that
+ ** case.
+ **
+ **If you are overriding the run() method to provide the thread code, you are
+ ** free create new constructor(s) to allow easy initalization of any class
+ ** data elements you've added.
+ **
+ ** If you will be passing in a runnable via the thread(runnable &) constructor
+ ** see the notes with that constructor. In short, remember the actual execution
+ ** thread in that case will occur in the context of the runnable descendent and
+ ** not that of the thread class, so local data must be stored in that context.
+ **
+ ** NOTE: this class is pthreads specific. Many of the public methods should
+ ** not be overridden because they provide basic thread control functionality,
+ ** _unless_ you are adapting this class to us a different thread lib.
+ **/
+class thread: public runnable
+{
+ private:
+
+ runnable * alt_run;
+ bool start_detached;
+
+ protected:
+
+ pthread_t mytid;
+
+ /** Provides a startpoint for threads.
+ **
+ ** Do NOT override this method. It exists to provide a constant
+ ** start point for pthread_create() function and contains the logic to
+ ** determine which run method (local or one from a passed in
+ ** runnable) we are to call.
+ **/
+ static void * startpoint(void * _this);
+ /// Yields processing time to another process.
+ void yield();
+
+ public:
+
+ // Exceptions.
+ /// Use this to catch all possible thread exceptions.
+ class general_exception: public base_exception {};
+ /// Thrown by run() if no thread code is available to run.
+ class no_code_exception: public general_exception {};
+ /// Thrown by start() if the thread could not be started.
+ class no_start_exception: public general_exception {};
+ /** Thrown if a thread attribute change is requested while the
+ ** thread is executing and changes are locked out.
+ **/
+ class attr_change_failed_exception: public general_exception {};
+ /// Thown if an attempt is made to join a detached thread.
+ class not_joinable_exception: public general_exception {};
+ /// Thrown if the requested thread could not be found.
+ class thread_id_not_found_exception: public general_exception {};
+ /// Thrown if an attempt to start an already running thread is made.
+ class already_running_exception: public general_exception {};
+ /// Thrown if the thread is not running when needed.
+ class not_running_exception: public general_exception {};
+
+ /** Default constructor.
+ **
+ ** This is nearly a nop in the base class, but this is okay as long as the
+ ** run() is overridden and you don't need to initialize any local
+ ** data.
+ **
+ ** The only action taken by the default constructor is to to set the
+ ** private variable alt_run = 0 to prevent accidental calling of the
+ ** wrong run method.
+ **/
+ thread();
+
+ /** Constructs from a runnable.
+ **
+ ** Constructs the thread to use the code and data from the runnable
+ ** descendent class provided. If you want to provide "local" data for
+ ** the thread to use during execution it should be provided in the
+ ** runnable descendent instead the base_thread descendent, since the
+ ** thread will execute within the scoping context of the runnable
+ ** descendent. (whew!!)
+ **
+ ** Do not override this method because it does some special handling
+ ** to insure the runnable's run() method is executed instead of the
+ ** base_thread's.
+ **/
+ thread(runnable & dothis);
+
+ /** Kills and destructs the thread.
+ **
+ ** This will kill a running thread automagically. This is a safety
+ ** behavior to prevent the thread from attempting access to object
+ ** data that becomes undefined if the object moves out of scope.
+ **/
+ virtual ~thread();
+
+ /** Optionally provides the code executed by the thread.
+ **
+ ** Override this class to provide the actual code for the thread
+ ** unless you are going to be passing in a descendent of runnable
+ ** to the constructor. If you do not override this and you don't
+ ** instanciate via the base_thread(runnable) constructor, any call to
+ ** start() is going to throw an exception.
+ **/
+ virtual void run();
+
+ /** Start the thread's execution.
+ **
+ ** If you have not overridden run() or constructed the object by
+ ** passing in a runnable class, this method will throw a
+ ** thread_no_code_exception. If the thread can not start for some
+ ** other reason a thread_no_start_exception is thrown. Any other
+ ** exceptions that may be thrown by a successfully started thread
+ ** are not caught here and must be dealt with in the application code.
+ **
+ ** Don't override this method unless you are adapting base_thread to
+ ** use a thread library other than pthreads.
+ **/
+ void start();
+
+ /** Start a thread using a supplied runnable object
+ **
+ ** Simply registers the runnable with the thread and
+ ** then calls start(). It is critical that the runnable
+ ** not be altered by any other process while the thread is
+ ** running.
+ **
+ ** NOTE: The registered runnable object will remain
+ ** linked to the thread object until another is passed
+ ** in.
+ **/
+ void start(runnable & do_this);
+
+ /** Rudely stop the thread's execution.
+ **
+ ** This actually translates to call the pthead_cancel if the thread is running,
+ ** or a nop if no thread is running. Since cancel is a rude process by
+ ** definition, be aware that you may need to do some cleanup after calling
+ ** this from outside the run() method.
+ **
+ ** Take care to prevent possible deadlocks by writing your thread code
+ ** to prevent cancellation while the thread is holding mutexes that others
+ ** may need access to later. You can do this using the set_not_cancelable(),
+ ** set_deferred_cancel(), and set_cancel_anytime() methods inherited from
+ ** runnable.
+ **
+ ** Don't override this method unless you are adapting base_thread to use
+ ** a thread library other than pthreads.
+ **/
+ void stop();
+
+ /** True if the thread is running, false otherwise.
+ **/
+ bool is_running();
+
+ /** Blocks until the thread completes.
+ **
+ ** If the thread is running, this will block the calling process until
+ ** the thread (i.e. the run() method) is complete. If the thread is not
+ ** currently running this method will return immediately.
+ **
+ ** If the thread is detached (see detach(bool) below) a
+ ** not_joinable_exception will the thrown.
+ **/
+ int join();
+
+ /** Makes this a "daemon" thread.
+ **
+ ** If the thread is detached no other thread can use join() to wait
+ ** for it's completion. However, the threads library will be able to
+ ** release some assets it must maintain on a joinable process,
+ ** saving memory.
+ **
+ ** If this is called during thread run: If yes==true the thread is
+ ** immediately detached if it is not already and a "note" is
+ ** recorded to set detached state immediately if the thread is
+ ** restarted later. If yes == false and the thread is already
+ ** detached an attr_change_failed_exception will be thrown because
+ ** you can not re-attach a detached thread.
+ **
+ ** If the thread is not currently running the detached attribute is
+ ** set for use when the thread is started.
+ **
+ ** Generally, if you are creating a long run thread or one that does
+ ** not terminate during the application's run (an input processor,
+ ** for example) you'll save memory if you detach it.
+ **/
+ void detach(bool yes=true);
+ /// returns the max priority the thread can use.
+ static int max_priority(int policy = SCHED_OTHER);
+ /// returns the min priority the thread can use.
+ static int min_priority(int policy = SCHED_OTHER);
+ /// return the thread's current priority.
+ int get_priority();
+ /** Sets the thread's priority
+ **
+ ** May throw an attr_change_failed_exception if the priority is not
+ ** acceptable (for example, the process must be running as root to
+ ** set a priority higher (numericly less) than 0.
+ **
+ ** The available priority range varies with operating system,
+ ** thread library, program permissions and scheduling policy in
+ ** use. Because of these variables, it's best to use
+ ** min_priority() and max_priority() to find out the acceptable
+ ** range for sure.
+ **
+ ** For the record, all threads I've seen start with a priority of
+ ** 0 (normal).
+ **/
+ void set_priority(const int pri, int policy = SCHED_OTHER);
+};
+
+/** Simple mutex.
+ **
+ ** Provides a mutex (MUTual EXclusion) lock. Only one thread can hold the lock
+ ** at a time.
+ **
+ ** These are useful for protecting data and other resources that must be shared
+ ** between two or more threads. Using mutexes is outside the scope of this
+ ** documention; a nice place to start is Chapter 3 of "Pthreads Programming"
+ ** by Nichols, Buttlar & Farrell, published by O'Reilly.
+ **/
+class mutex
+{
+ protected:
+ pthread_mutex_t mymid;
+ public:
+ /// Use to catch all mutex exceptions.
+ class general_exception: public base_exception {};
+ /// Thrown when relocking a mutex already locked by the calling thread.
+ class already_locked_exception: public general_exception {};
+ /// Thrown when unlocking a mutex not locked by the calling thread.
+ class not_owner_exception: public general_exception {};
+ /// Thrown if the destructor is called on a locked mutex.
+ class can_not_destruct_exception: public general_exception {};
+
+ /** Constructor.
+ **/
+ mutex();
+
+ /** Destructor.
+ **
+ ** Care should be taken to insure that a mutex can not go out of scope
+ ** or otherwise be destructed while a thread may still expect it to be
+ ** present.
+ **/
+ virtual ~mutex();
+
+ /** Locks the mutex.
+ **
+ ** If the mutex is not currently held by another thread, this locks it and
+ ** returns immediately to the caller. If the mutex is already locked by
+ ** another thread, this call will block until the mutex is unlocked
+ ** ("becomes available") at which time it will be locked and control
+ ** returned to the caller.
+ **
+ ** If you really _must_ have mutexs that you can lock willy-nilly take a
+ ** look at the recursive_mutex class. recursive_mutex can be locked
+ ** multple times by the same thread, but require the same number of unlocks
+ ** as were previously locked to release the mutex for another thread to use.
+ **
+ ** NOTE: There is no time limit on how long this method will block while
+ ** attempting to gain a lock on the mutex. Therefore, once a thread locks
+ ** a mutex, it _MUST_ unlock it when the thread is done with the resource
+ ** the mutex is protecting. Failure to do so will almost certainly result
+ ** in program deadlocks.
+ **/
+ virtual void lock();
+
+ /** Unlocks the mutex.
+ **
+ ** NOTE: In some pthreads implementations it is possible for a thread
+ ** to unlock a mutex owned by a different thread. The dangers in such an
+ ** action should be self-evident. Don't do it!
+ **/
+ virtual void unlock();
+
+ /** Checks the mutex and if available locks it.
+ **
+ ** If the mutex is locked this method returns to the caller immediately
+ ** with a return of "false". If the mutex is not locked, this locks it and
+ ** returns "true" to the caller. In no case will this method block.
+ **/
+ virtual bool try_lock();
+};
+
+/** Used to signal state changes between threads.
+ **
+ ** Provides a condition variable and it's associated mutex. Condition variables
+ ** are used to signal a thread that something it's interested in has changed.
+ ** Details on the use of condition variables are out of the scope of this
+ ** documention; for a good discusion check out chapeter 3 of
+ ** the book "Pthreads Programming" by Nichols, Buttlar, and Farrell,
+ ** published by O'Reilly.
+ **
+ ** Do not use this class where a simple mutex will do. There is more overhead
+ ** involved to prevent deadlocks and to support safe usage requirements.
+ **/
+class cond_variable: public mutex
+{
+ private:
+ pthread_cond_t mycv;
+ pthread_t owner; // 0 or current lock owner.
+ int waiting; // number of threads waiting
+ public:
+
+ /// Use this to catch all cond_variable exceptions.
+ class general_exception: public base_exception {};
+ /// Thrown if the cond_variable can not be destructed.
+ class can_not_destruct_exception: public general_exception {};
+ /// Thrown by *wait() and *signal if the cond_variable is unlocked on entry.
+ class not_locked_exception: public general_exception {};
+ /// Thrown when relocking a cond_variable already locked by the calling thread.
+ class already_locked_exception: public general_exception {};
+ /// Thrown when unlocking cond_variable not locked by the calling thread.
+ class not_owner_exception: public general_exception {};
+
+ /** Constructs the cond_variable.
+ **
+ ** The cond_variable is initialized and left unlocked and ready
+ ** to use.
+ **/
+ cond_variable();
+
+ /** Destructs the cond_variable;
+ **
+ ** If the cond_variable is unlocked and not in a wait state all resources
+ ** are released. In any other case a cant_destruct_exception is thrown.
+ **/
+ virtual ~cond_variable();
+
+ /** Locks the cond_variable.
+ **
+ ** cond_variable has an internal mutex that is used to control access to the
+ ** cond_variable and optionally the data it may be interested in. If the
+ ** cond_variable is not currently held by another thread, this locks it and
+ ** returns immediately to the caller. If the cond_variable is already locked
+ ** by another thread, this call will block until the cond_variable is
+ ** unlocked ("becomes available") at which time it will be locked and control
+ ** returned to the caller.
+ **
+ ** The cond_variable must be locked before entering any of the *wait()
+ ** or *signal() methods and is of course locked on exit of the same. It is
+ ** atomically unlocked while blocked in the *wait() methods to allow
+ ** other threads to update the protected data and use the *signal()
+ ** methods.
+ **
+ ** NOTE: There is no time limit on how long this method will block while
+ ** attempting to gain a lock. Therefore, once a thread locks a
+ ** cond_variable, it _MUST_ unlock it when the thread is done with the
+ ** resource the cond_variable is protecting. Failure to do so will almost
+ ** certainly result in program deadlocks.
+ **/
+ virtual void lock();
+
+ /** Unlocks the cond_variable.
+ **
+ ** This is the inverse of the cond_variable::lock() method. Just as you
+ ** must call lock sometime before entering *wait() or *signal(), you
+ ** must call unlock() after exiting those methods as soon as it would be
+ ** safe for other threads to access the cond_variable.
+ **/
+ virtual void unlock();
+
+ /** Checks the cond_variable and if available locks it.
+ **
+ ** If the cond_variable is locked by another thread this method returns to
+ ** the caller immediately with a return of "false". If the cond_variable
+ ** is not locked, this locks it and returns "true" to the caller. If the
+ ** cond_variable is already locked by the calling thread true is returned
+ ** and no other action is taken. In no case will this method block.
+ **/
+ virtual bool try_lock();
+
+ /** Waits indefinately until signaled.
+ **
+ ** The cond_variable must be locked prior to calling this method. On entry
+ ** to this method the calling thread blocks and the cond_variable will
+ ** be unlocked so that other threads can modify the data we're protecting.
+ **
+ ** When another thread wakes us up (via a call to signal() or
+ ** broadcast_signal()) the cond_variable will again be locked and this method
+ ** will return to it's caller. It's the responsiblity of the calling
+ ** thread to call unlock() when it's safe for other threads to modify
+ ** the data again.
+ **
+ ** NOTE: If no thread ever calls this cond_variable's signal() or
+ ** broadcast_signal() methods the calling thread will _never_ wake up.
+ **
+ ** WARNING: It is not technically an error for either the wait() or
+ ** time_wait() methods to return control to the calling thread
+ ** spuriously, i.e. without having been signalled or timing out. It is
+ ** therefore the calling thread's responsiblity to insure the conditions
+ ** it was waiting on have actually been met and, if not, take the
+ ** appropriate action.
+ **/
+ void wait();
+
+ /** Waits until signaled or a timeout occurs.
+ **
+ ** The cond_variable must be locked prior to calling this method. On entry
+ ** to this method the calling thread blocks and the cond_variable will
+ ** be unlocked so that other threads can modify the data we're protecting.
+ **
+ ** When another thread wakes us up (via a call to signal() or
+ ** broadcast_signal() or after the specified number of seconds have
+ ** elapsed) the cond_variable will again be locked and this method
+ ** will return to it's caller. It's the responsiblity of the calling
+ ** thread to call unlock() when it's safe for other threads to modify
+ ** the data again.
+ **
+ ** WARNING: It is not technically an error for either the wait() or
+ ** time_wait() methods to return control to the calling thread
+ ** spuriously, i.e. without having been signalled or timing out. It is
+ ** therefore the calling thread's responsiblity to insure the conditions
+ ** it was waiting on have actually been met and, if not, take the
+ ** appropriate action.
+ **/
+ void timed_wait(const time_t & seconds);
+
+ /** Wakes up one thread blocked in wait() or timed_wait();
+ **
+ ** When called and if one or more threads are blocked on this
+ ** cond_variable's wait() or time_wait(), one of those threads will be
+ ** released and given the lock on the cond_variable. Only one thread
+ ** will be released, and if there are more than one thread blocked in
+ ** this cond_variable's wait() or time_wait() you have no direct control
+ ** over which one will be released. Indeed, for all intents and practical
+ ** purposes you might as well consider the selection to be random.
+ **
+ ** Only works if this cond_variable is actually blocking in wait() or
+ ** time_wait(). Otherwise a not_waiting_exception is thrown.
+ **/
+ void signal();
+
+ /** Wakes up all threads blocked in wait() or timed_wait();
+ **
+ ** When called and if one or more threads are blocked on this
+ ** cond_variables's wait() or time_wait(), all threads so blocked will be
+ ** released and given the lock on this cond_variable in turn. You have
+ ** no direct control over the order in which the blocked thread(s) are
+ ** allowed to run; for all intents and practical purposes you might as
+ ** well consider the run order of the released threads to be random.
+ **
+ ** Only works if this cond_variable is actually blocking in wait() or
+ ** time_wait(). Otherwise a not_waiting_exception is thrown.
+ **/
+ void broadcast_signal();
+
+ /** Returns the number of threads blocked on this cond_variable.
+ **/
+ int num_waiting();
+};
+
+/** Safely mutex locks the scope it's instanciated in.
+ **
+ ** scope_lock provides a safe and easy way to control 95% percent of mutex
+ ** usage. Instanciating a scope_lock in a given scope locks the supplied
+ ** mutex until the end of the scope. When the scope_lock is destructed upon
+ ** exiting the scope the mutex is unlocked, regardless of how the scope is
+ ** exited. Even in the case of unexpected exceptions this class will insure
+ ** your mutex is accidently left locked to cause problems later.
+ **/
+class scope_lock
+{
+ private:
+ mutex * mylock;
+ public:
+ /** Constructs the scope_lock and locks the supplied mutex.
+ **
+ ** lock is a mutex (defined in base_thread.h) and is assumed to be
+ ** ready to use at the time this constructor is called. The mutex will
+ ** be locked at the return of this method, so it may block until the mutex
+ ** is available if the mutex is held by another thread at the time of entry.
+ **
+ ** NOTE: Once a mutex is used to instanciate a scope_lock it should not
+ ** be accessed or modified in any way within the scope being protected
+ ** by the scope_lock. Failure to abide by this simple rule may cause
+ ** unexpected deadlocks or exceptions.
+ **/
+ scope_lock(mutex & lock)
+ {
+ mylock = &lock;
+ mylock->lock();
+ };
+ /** Destructs the scope_lock and unlocks the associated mutex.
+ **
+ ** You never call this directly, of course. Instead, it's called
+ ** automatically upon exit of the scope the scope_lock was instanciated
+ ** in.
+ **/
+ ~scope_lock()
+ {
+ mylock->unlock();
+ };
+};
+
+/** Unlocks the supplied mutex.
+ **
+ ** Use this function in conjunction with pthread_cleanup_push() and
+ ** pthread_cleanup_pop(0) to ensure a mutex is unlocked should the thread
+ ** be canceled which owns it. Example (where mylock is a mutex or
+ ** cond_variable):
+ **
+ ** scope_lock lock(mylock);
+ **
+ ** pthread_cleanup_push(safe_unlock, &mylock);
+ **
+ ** (something interesting happens here...)
+ **
+ ** pthread_cleanup_pop(0)
+ **
+ ** Sorry I can't hide the pthread_cleanup_* calls and make this neater..
+ ** unfortunately the pthread standard requires they appear in the same
+ ** lexical block.
+**/
+void safe_unlock(void * mylock);
+
+
+} // namespace nrtb
+
+#endif // base_thread_h
+
=== added directory 'common/threads/tests'
=== added file 'common/threads/tests/cond_var.h'
--- common/threads/tests/cond_var.h 1970-01-01 00:00:00 +0000
+++ common/threads/tests/cond_var.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,124 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef base_cond_var_test_h
+#define base_cond_var_test_h
+
+#include "../base_thread.h"
+#include "general.h"
+#include "../../include/hires_timer.h"
+#include <sstream>
+#include <boost/concept_check.hpp>
+
+using namespace nrtb;
+using namespace std;
+
+class seller: public runnable
+{
+public:
+ hirez_timer elapsed;
+ int hits;
+
+ seller()
+ {
+ message = "";
+ hits = 0;
+ };
+
+ void signal(const string & msg)
+ {
+ {
+ scope_lock lock(signal_lock);
+ message = msg;
+ signal_lock.signal();
+ }
+ {
+ scope_lock lock(ready_lock);
+ ready_lock.timed_wait(1);
+ }
+ };
+
+ void run()
+ {
+ elapsed.start(0);
+ bool done = false;
+ while (!done)
+ {
+ scope_lock lock(signal_lock);
+ while (message == "")
+ signal_lock.wait();
+ cout << elapsed.interval() << " Recieved "
+ << message << endl;
+ if (message == "done")
+ {
+ done = true;
+ };
+ hits++;
+ message = "";
+ {
+ scope_lock t(ready_lock);
+ ready_lock.signal();
+ }
+ };
+ };
+
+protected:
+ cond_variable signal_lock;
+ cond_variable ready_lock;
+ string message;
+};
+
+seller store;
+
+class cond_test: public runnable
+{
+public:
+ int ec;
+
+ cond_test()
+ {
+ ec = 0;
+ };
+
+ void run()
+ {
+ for (int i=0; i < 1000; i++)
+ {
+ stringstream message;
+ message << "Request_" << i;
+ store.signal(message.str());
+ };
+ store.signal("done");
+ };
+
+ string operator() ()
+ {
+ stringstream msg;
+ msg << "Condition Variable Test\n"
+ << "\tSent : 1001\n"
+ << "\tRecd : " << store.hits
+ << "\n\tPassed: " << ((store.hits == 1001)?"True":"False")
+ << endl;
+ ec = 1001 - store.hits;
+ return msg.str();
+ };
+
+};
+
+
+#endif // base_cond_var_test_h
\ No newline at end of file
=== added file 'common/threads/tests/general.h'
--- common/threads/tests/general.h 1970-01-01 00:00:00 +0000
+++ common/threads/tests/general.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,194 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef base_general_test_h
+#define base_general_test_h
+
+#include "../base_thread.h"
+#include "../../include/hires_timer.h"
+#include <sstream>
+#include <boost/smart_ptr.hpp>
+
+using namespace nrtb;
+using namespace std;
+
+class console_writer
+{
+public:
+ mutex mylock;
+ hirez_timer elapsed;
+
+ void write(const string & output)
+ {
+ scope_lock lock(mylock);
+ cout << elapsed.interval() << ": " << output << endl;
+ };
+};
+
+console_writer console;
+
+class gen_test_task: public runnable
+{
+ public:
+
+ int loop_counter;
+ int count_to;
+ string name;
+ int desired_priority;
+ hirez_timer runtime;
+
+ gen_test_task(
+ const string & text,
+ int target,
+ int priority
+ )
+ {
+ loop_counter = 0;
+ count_to = target;
+ name = text;
+ };
+
+ virtual ~gen_test_task() {};
+
+ void run()
+ {
+ runtime.start(0);
+ set_cancel_anytime();
+ stringstream msg;
+ msg.clear();
+ msg << name
+ << "(" << count_to << "," << desired_priority
+ << ") started.";
+ console.write(msg.str());
+ // loop till we die.
+ for (int i=0; i<count_to; i++)
+ {
+ loop_counter++;
+ // yeild the timeslice
+ sleep(0);
+ };
+ // report status and stop runtime counter.
+ msg.str("");
+ msg << name << " has counted to " << loop_counter;
+ console.write(msg.str());
+ runtime.stop();
+ };
+
+ string report()
+ {
+ stringstream maker;
+ maker << "Task " << name << " Report"
+ << "\n\tIterations : " << loop_counter
+ << "\n\tRun Time : " << runtime.interval()
+ << endl;
+ return maker.str();
+ };
+};
+
+class gen_tester
+{
+ public:
+ typedef boost::shared_ptr<gen_test_task> taskp;
+ typedef vector<taskp> task_list;
+ typedef vector<thread> thread_list;
+ task_list tasks;
+ thread_list threads;
+ hirez_timer runtime;
+ int ec;
+
+ gen_tester()
+ {
+ ec = 0;
+ for (int i=0; i<10; i++)
+ {
+ stringstream s;
+ s << "task_" << i;
+ taskp t(new gen_test_task(s.str(),1e5,i*10));
+ tasks.push_back(t);
+ thread p(*t);
+ threads.push_back(p);
+ };
+ };
+
+ void run()
+ {
+ stringstream msg;
+ msg.str("");
+ msg << "Starting " << threads.size() << " threads.";
+ console.write(msg.str());
+ // start all threads
+ thread_list::iterator s = threads.begin();
+ thread_list::iterator e = threads.end();
+ thread_list::iterator c = s;
+ while (c != e)
+ {
+ c->start();
+ c++;
+ };
+ msg.str("");
+ msg << "All threads started.";
+ console.write(msg.str());
+ // wait for the threads to complete.
+ c = s;
+ while (c != e)
+ {
+ c->join();
+ c++;
+ };
+ msg.str("");
+ msg << threads.size() << " threads complete.";
+ console.write(msg.str());
+ runtime.stop();
+ };
+
+ string operator() ()
+ {
+ stringstream msg;
+ task_list::iterator s = tasks.begin();
+ task_list::iterator e = tasks.end();
+ task_list::iterator c = s;
+ while (c != e)
+ {
+ taskp t = *c;
+ msg << t->report();
+ if (t->loop_counter != t->count_to)
+ {
+ ec++;
+ msg << "The counter does not appear correct." << endl;
+ };
+ c++;
+ };
+ thread_list::iterator ts = threads.begin();
+ thread_list::iterator te = threads.end();
+ thread_list::iterator tc = ts;
+ while (tc != te)
+ {
+ if (tc->is_running())
+ {
+ msg << "ERROR: A thread appears to be running." << endl;
+ ec++;
+ };
+ tc++;
+ };
+ msg << "\nGeneral test run time " << runtime.interval()
+ << " seconds." << endl;
+ return msg.str();
+ };
+};
+
+#endif // base_general_test_h
\ No newline at end of file
=== added file 'common/threads/tests/loop_counter.h'
--- common/threads/tests/loop_counter.h 1970-01-01 00:00:00 +0000
+++ common/threads/tests/loop_counter.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,76 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef base_thread_loop_test_h
+#define base_thread_loop_test_h
+
+#include "../base_thread.h"
+#include "../../include/hires_timer.h"
+#include <sstream>
+
+using namespace nrtb;
+using namespace std;
+
+class counter_work_packet: public runnable
+{
+ public:
+
+ int loop_counter;
+ int call_counter;
+ int count_to;
+ hirez_timer runtime;
+
+ counter_work_packet()
+ {
+ loop_counter = 0;
+ call_counter = 0;
+ count_to = 1000;
+ runtime.reset();
+ };
+
+ ~counter_work_packet() {};
+
+ void run()
+ {
+ runtime.start();
+ call_counter++;
+ for (int i = 0; i < count_to; i++)
+ {
+ loop_counter++;
+ };
+ runtime.stop();
+ };
+
+ string report()
+ {
+ stringstream maker;
+ maker << "\ncounter_work_packet Report\n"
+ << "\tInvoications: " << call_counter
+ << "\n\tIterations : " << loop_counter
+ << "\n\tRun Time : " << runtime.interval()
+ << endl;
+ return maker.str();
+ };
+
+ string operator() ()
+ {
+ return report();
+ };
+};
+
+#endif // base_thread_loop_test_h
\ No newline at end of file
=== added file 'common/threads/thread_test.cpp'
--- common/threads/thread_test.cpp 1970-01-01 00:00:00 +0000
+++ common/threads/thread_test.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,77 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "base_thread.h"
+#include <iostream>
+#include <sstream>
+#include "./tests/loop_counter.h"
+#include "./tests/general.h"
+#include "./tests/cond_var.h"
+
+using namespace std;
+using namespace nrtb;
+
+void runnable_pair_test(runnable & a, runnable & b)
+{
+ cerr << "Entering runnable_pair_test" << endl;
+ static thread ut1, ut2;
+ ut1.start(a);
+ ut2.start(b);
+ ut1.join();
+ ut2.join();
+};
+
+int main()
+{
+ int ec_count = 0;
+ // runnable work packet test.
+ counter_work_packet cwp1, cwp2;
+ cwp2.count_to = 1500;
+ runnable_pair_test(cwp1, cwp2);
+ runnable_pair_test(cwp2, cwp1);
+ cout << cwp1() << "\n" << cwp2() << endl;
+ // check the results.
+ if ((cwp1.loop_counter != 2000) or (cwp1.call_counter != 2))
+ {
+ ec_count++;
+ };
+ if ((cwp2.loop_counter != 3000) or (cwp2.call_counter != 2))
+ {
+ ec_count++;
+ };
+
+ // general thread test
+ gen_tester gen_test;
+ gen_test.run();
+ cout << "\nGeneral thread test\n" << gen_test() << endl;
+ ec_count += gen_test.ec;
+
+ // conditional variable tests
+ cond_test cv_test;
+ runnable_pair_test(store, cv_test);
+ cout << cv_test() << endl;
+ ec_count += cv_test.ec;
+
+ if (ec_count)
+ {
+ cerr << "*** thread_test failed with " << ec_count
+ << " errors" << endl;
+ };
+
+ return ec_count;
+};
\ No newline at end of file
=== modified file 'common/timer/Makefile'
--- common/timer/Makefile 2010-01-14 02:24:25 +0000
+++ common/timer/Makefile 2011-08-25 04:57:16 +0000
@@ -1,147 +1,36 @@
-# This file was automatically generated by makemake.
-# Do not edit it directly!
-# Any changes you make will be silently overwritten.
-
-# Edit this file to define constants and custom build targets.
-# Please refer to the makemake documentation for more information.
-#
-# To compile multiple versions of a program or library, please study
-# http://www.its.caltech.edu/~jafl/jcc/multi_version.html
-
-# Useful directories
-
-MYCODEDIR := .
-
-# Directories to search for header files
-
-SEARCHDIRS := -I- -I${MYCODEDIR}
-
-# makemake variables
-
-DEPENDFLAGS := -O3 -Wall -Werror ${SEARCHDIRS}
-
-# C preprocessor (C, C++, FORTRAN)
-
-CPPFLAGS =
-
-# C compiler
-
-CC := gcc
-CFLAGS = ${DEPENDFLAGS}
-
-%.o : %.c
- ${CC} ${CPPFLAGS} ${CFLAGS} -c $< -o $@
-
-# C++ compiler
-
-CXX := g++
-CXXFLAGS = ${DEPENDFLAGS}
-
-%.o : %.cc
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-%.o : %.C
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-%.o : %.cpp
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-%.o : %.cxx
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-# FORTRAN compiler
-
-FC := f77
-FFLAGS =
-
-# C/C++/Eiffel/FORTRAN linker
-
-LINKER := g++
-LDFLAGS =
-LOADLIBES := -lm
-
-# Java compiler
-
-JAVAC := javac
-JFLAGS =
-JAR := jar
-
-%.class : %.java
- ${JAVAC} ${JFLAGS} $<
-
-
-# This is what makemake added
-
-
-# timer_test
-
-timer_test : ./nrtb_timer.o ./timer_test.o
- ${LINKER} ${LDFLAGS} -o $@ ${filter-out %.a %.so, $^} ${LOADLIBES}
-
-# target for making everything
-
-.PHONY : all
-all: timer_test
-
-.PHONY : lib
-lib: nrtb_timer.o ../../include/nrtb_timer.h
- @ar -r ../../lib/libnrtb.a nrtb_timer.o
- @echo nrtb_timer has been added to the library
-
-../../include/nrtb_timer.h: nrtb_timer.h
- @cp -fv nrtb_timer.h ../../include
-
-# target for removing all object files
-
-.PHONY : tidy
-tidy::
- @rm -fv core ./nrtb_timer.o ./timer_test.o
-
-# target for removing all object files
-
-.PHONY : clean
-clean:: tidy
- @rm -rf timer_test ../../include/nrtb_timer.h
- @ar -d ../../lib/libnrtb.a nrtb_timer.o
- @echo "nrtb_timers has been removed from the library"
-
-
-# list of all source files
-
-MM_ALL_SOURCES := ./nrtb_timer.cpp ./timer_test.cpp
-
-
-# target for checking a source file
-
-CHECKSYNTAXFILE := ${basename ${filter %${CHECKSTRING}, ${MM_ALL_SOURCES}}}
-
-.PHONY : checksyntax
-checksyntax:
- ifneq (${CHECKSYNTAXFILE},)
- @${MAKE} ${addsuffix .o, ${CHECKSYNTAXFILE}}
- else
- @echo No target to make ${CHECKSTRING}
- endif
-
-
-# target for touching appropriate source files
-
-.PHONY : touch
-touch::
- @list=$$(grep -l ${TOUCHSTRING} ${MM_ALL_SOURCES}); \
- for file in $$list; do { echo $$file; touch $$file; } done
-
-
-# target for calculating dependencies (MAKEMAKE)
-
-.PHONY : jdepend
-jdepend:
- @${MAKEMAKE} --depend Makefile -- ${DEPENDFLAGS} -- ./nrtb_timers.cpp ./nrtb_timer.o ./timer_test.cpp ./timer_test.o
-
-
-# DO NOT DELETE THIS LINE -- makemake depends on it.
-
-./nrtb_timer.o: ./nrtb_timer.h /usr/include/math.h /usr/include/sys/time.h
-
-./timer_test.o: ./nrtb_timer.h /usr/include/sys/time.h /usr/include/unistd.h
-
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: timer_test
+ @./timer_test
+ @cp -v hires_timer.h ../include
+ @cp -v hires_timer.o ../obj
+ @echo build complete
+
+hires_timer.o: hires_timer.h hires_timer.cpp Makefile
+ @rm -f hires_timer.o
+ g++ -c -O3 hires_timer.cpp
+
+timer_test: hires_timer.o timer_test.cpp
+ @rm -f timer_test
+ g++ -c timer_test.cpp
+ g++ -o timer_test timer_test.o hires_timer.o
+
+clean:
+ @rm -rvf *.o timer_test ../include/hires_timer.h ../obj/hires_timer.o
+ @echo all objects and executables have been erased.
=== added file 'common/timer/hires_timer.cpp'
--- common/timer/hires_timer.cpp 1970-01-01 00:00:00 +0000
+++ common/timer/hires_timer.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,174 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+/* see ricklib_timers.h for whatever documentation you can find */
+#include "hires_timer.h"
+#include <math.h>
+#include <sstream>
+
+namespace nrtb
+{
+
+hirez_timer::hirez_timer(const double init)
+{
+ reset();
+ start(init);
+};
+
+hirez_timer::hirez_timer(const timeval & init)
+{
+ reset();
+ start(init);
+};
+
+void hirez_timer::reset()
+{
+ running = false;
+ starttime = 0;
+ elapsed = 0;
+};
+
+void hirez_timer::start(const double init)
+{
+ if (!running)
+ {
+ timeval startt;
+ gettimeofday(&startt,0);
+ starttime = tv_to_ll(startt);
+ if (init > 0)
+ {
+ starttime -= double_to_ll(init);
+ };
+ };
+ running = true;
+};
+
+void hirez_timer::start(const timeval & init)
+{
+ starttime = tv_to_ll(init);
+ running = true;
+};
+
+double hirez_timer::stop()
+{
+ stop_timer();
+ return ll_to_double(elapsed);
+};
+
+unsigned long long hirez_timer::stop_as_usec()
+{
+ stop_timer();
+ return elapsed;
+};
+
+double hirez_timer::interval()
+{
+ return ll_to_double(interval_time());
+};
+
+unsigned long long hirez_timer::interval_as_usec()
+{
+ return interval_time();
+};
+
+tm hirez_timer::interval_as_tm()
+{
+ time_t temp = (interval_time()+500000)/1000000;
+ return *gmtime(&temp);
+};
+
+double hirez_timer::ll_to_double(const unsigned long long int ll)
+{
+ return ll / (double) 1e6;
+};
+
+timeval hirez_timer::ll_to_tv(const unsigned long long int ll)
+{
+ timeval t;
+ t.tv_sec = ll / (unsigned long long int) 1e6;
+ t.tv_usec = ll % (unsigned long long int) 1e6;
+ return t;
+};
+
+unsigned long long int hirez_timer::tv_to_ll(const timeval & tv)
+{
+ return ((unsigned long long) tv.tv_sec * (unsigned long long) 1e6)
+ + (unsigned long long) tv.tv_usec;
+};
+
+unsigned long long int hirez_timer::double_to_ll(const double d)
+{
+ return (unsigned long long int) floor(d * 1e6);
+};
+
+void hirez_timer::stop_timer()
+{
+ if (running)
+ {
+ timeval endtime;
+ gettimeofday(&endtime,0);
+ elapsed += (tv_to_ll(endtime) - starttime);
+ };
+ running = false;
+};
+
+unsigned long long int hirez_timer::interval_time()
+{
+ if (running)
+ {
+ timeval endtime;
+ gettimeofday(&endtime,0);
+ return elapsed + (tv_to_ll(endtime) - starttime);
+ }
+ else
+ {
+ return elapsed;
+ };
+};
+
+std::string hirez_timer::interval_as_HMS(const bool days)
+{
+ // local working vars.
+ std::stringstream output;
+ double t1;
+ long int t2;
+ t1 = interval();
+ // Days (only if needed)
+ if (days)
+ {
+ t2 = long(t1) / 86400l;
+ if (t2 > 0)
+ {
+ output << t2 << "d, ";
+ t1 -= t2 * 86400l;
+ };
+ };
+ // hours
+ t2 = long(t1) / 3600;
+ output << t2;
+ t1 -= t2 * 3600;
+ // minutes
+ t2 = long(t1) / 60;
+ output << ((t2 < 10) ? ":0" : ":") << t2;
+ t1 -= t2 * 60;
+ // seconds
+ output << ((t1 < 10) ? ":0" : ":") << t1;
+ return output.str();
+};
+
+} // namespace ricklib;
+
=== added file 'common/timer/hires_timer.h'
--- common/timer/hires_timer.h 1970-01-01 00:00:00 +0000
+++ common/timer/hires_timer.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,131 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+#ifndef nrtb_timer_h
+#define nrtb_timer_h
+
+#include <sys/time.h>
+#include <time.h>
+#include <string>
+
+namespace nrtb
+{
+
+/** Provides a timer with microsecond (1e-6 second) resolution.
+ **
+ ** Actual resolution on x86 machines is probably closer to 1e-4 or
+ ** -5, but it's still pretty good.
+ **
+ ** Usage notes: instanciating this class will start the timer
+ ** automagically.
+ **/
+class hirez_timer
+{
+ public:
+ /** initializes the timer and starts it.
+ **
+ ** Sets the the timer's elapsed time to init number of seconds and
+ ** then starts the timer.
+ **/
+ hirez_timer(const double init = 0);
+ /** initalizes the timer and starts it.
+ **
+ ** The timer's elapsed time is set to match the value provided in
+ ** init, and the timer is started.
+ **/
+ hirez_timer(const timeval & init);
+
+ /** Clears the timer.
+ **
+ ** If the timer is running, this stops it as well.
+ **/
+ void reset();
+ /** Starts the timer.
+ **
+ ** Starts the timer, and optionally sets the elapsed time at
+ ** startup to the value provided in init.
+ **/
+ void start(const double init = 0);
+ /** Starts the timer.
+ **
+ ** Sets the elapsed time equal to the value provided in init
+ ** and then starts the timer.
+ **/
+ void start(const timeval & init);
+ /** Stops the timer.
+ **
+ ** Unconditionally stops the timer, preserving the current
+ ** values. It may be restarted using the start() method,
+ ** acummulating from where is stopped.
+ **
+ ** Returns the elapsed time in seconds.
+ **/
+ double stop();
+ /** Stops the timer.
+ ** Unconditionally stops the timer, preserving the current
+ ** values. It may be restarted using the start() method,
+ ** acummulating from where is stopped.
+ **
+ ** The return value is the elapsed time in microseconds
+ ** (1e-6 seconds).
+ **/
+ unsigned long long stop_as_usec();
+ /** Returns the current elapsed time.
+ **
+ ** Returns the number of elapsed seconds on the timer. No change
+ ** is made to the timer's status.
+ **/
+ double interval();
+ /** Returns the current elapsed time.
+ **
+ ** Returns the elapsed time in microseconds (1e-6 seconds). No
+ ** change is made to the timer's status.
+ **/
+ unsigned long long interval_as_usec();
+ /** Returns the elapsed time in a tm struct.
+ **
+ ** See "man gmtime" for the structure of the tm struct. In short
+ ** though, this method provides the current timer value in a
+ ** structure where it's broken down into hours, minutes, seconds and
+ ** the like. No change is made to the timer's status.
+ **/
+ tm interval_as_tm();
+ /** Returns the elapsed time in H:MM:SS format
+ **
+ ** Returns the elapsed time in a std::string. The formate is
+ ** H:MM:SS.sssss. No change is made to the timer's status.
+ ** If the optional days parameter is set to true, the number
+ ** of days will be displayed and hours will be < 24, otherwise
+ ** no days are shown and hours will grow as needed (default).
+ **/
+ std::string interval_as_HMS(const bool days = false);
+ private:
+ unsigned long long int starttime;
+ unsigned long long int elapsed;
+ bool running;
+
+ inline double ll_to_double(const unsigned long long int ll);
+ inline timeval ll_to_tv(const unsigned long long int ll);
+ inline unsigned long long int double_to_ll(const double d);
+ inline unsigned long long int tv_to_ll(const timeval & tv);
+ inline void stop_timer();
+ inline unsigned long long int interval_time();
+};
+
+}; // namespace nrtb
+
+#endif // nrtb_timer_h
=== removed file 'common/timer/nrtb_timer.cpp'
--- common/timer/nrtb_timer.cpp 2010-01-14 02:24:25 +0000
+++ common/timer/nrtb_timer.cpp 1970-01-01 00:00:00 +0000
@@ -1,158 +0,0 @@
-/* see nrtb_timer.h for whatever documentation you can find */
-
-#include <nrtb_timer.h>
-#include <math.h>
-#include <sstream>
-
-namespace NRTB
-{
-
-hirez_timer::hirez_timer(const double init)
-{
- reset();
- start(init);
-};
-
-hirez_timer::hirez_timer(const timeval & init)
-{
- reset();
- start(init);
-};
-
-void hirez_timer::reset()
-{
- running = false;
- starttime = 0;
- elapsed = 0;
-};
-
-void hirez_timer::start(const double init)
-{
- if (!running)
- {
- timeval startt;
- gettimeofday(&startt,0);
- starttime = tv_to_ll(startt);
- if (init > 0)
- {
- starttime -= double_to_ll(init);
- };
- };
- running = true;
-};
-
-void hirez_timer::start(const timeval & init)
-{
- starttime = tv_to_ll(init);
- running = true;
-};
-
-double hirez_timer::stop()
-{
- stop_timer();
- return ll_to_double(elapsed);
-};
-
-unsigned long long hirez_timer::stop_as_usec()
-{
- stop_timer();
- return elapsed;
-};
-
-double hirez_timer::interval()
-{
- return ll_to_double(interval_time());
-};
-
-unsigned long long hirez_timer::interval_as_usec()
-{
- return interval_time();
-};
-
-tm hirez_timer::interval_as_tm()
-{
- time_t temp = (interval_time()+500000)/1000000;
- return *gmtime(&temp);
-};
-
-double hirez_timer::ll_to_double(const unsigned long long int ll)
-{
- return ll / (double) 1e6;
-};
-
-timeval hirez_timer::ll_to_tv(const unsigned long long int ll)
-{
- timeval t;
- t.tv_sec = ll / (unsigned long long int) 1e6;
- t.tv_usec = ll % (unsigned long long int) 1e6;
- return t;
-};
-
-unsigned long long int hirez_timer::tv_to_ll(const timeval & tv)
-{
- return ((unsigned long long) tv.tv_sec * (unsigned long long) 1e6)
- + (unsigned long long) tv.tv_usec;
-};
-
-unsigned long long int hirez_timer::double_to_ll(const double d)
-{
- return (unsigned long long int) floor(d * 1e6);
-};
-
-void hirez_timer::stop_timer()
-{
- if (running)
- {
- timeval endtime;
- gettimeofday(&endtime,0);
- elapsed += (tv_to_ll(endtime) - starttime);
- };
- running = false;
-};
-
-unsigned long long int hirez_timer::interval_time()
-{
- if (running)
- {
- timeval endtime;
- gettimeofday(&endtime,0);
- return elapsed + (tv_to_ll(endtime) - starttime);
- }
- else
- {
- return elapsed;
- };
-};
-
-std::string hirez_timer::interval_as_HMS(const bool days)
-{
- // local working vars.
- std::stringstream output;
- double t1;
- long int t2;
- t1 = interval();
- // Days (only if needed)
- if (days)
- {
- t2 = long(t1) / 86400l;
- if (t2 > 0)
- {
- output << t2 << "d, ";
- t1 -= t2 * 86400l;
- };
- };
- // hours
- t2 = long(t1) / 3600;
- output << t2;
- t1 -= t2 * 3600;
- // minutes
- t2 = long(t1) / 60;
- output << ((t2 < 10) ? ":0" : ":") << t2;
- t1 -= t2 * 60;
- // seconds
- output << ((t1 < 10) ? ":0" : ":") << t1;
- return output.str();
-};
-
-} // namespace NRTB;
-
=== removed file 'common/timer/nrtb_timer.h'
--- common/timer/nrtb_timer.h 2010-01-14 02:24:25 +0000
+++ common/timer/nrtb_timer.h 1970-01-01 00:00:00 +0000
@@ -1,115 +0,0 @@
-
-#ifndef nrtb_timer_h
-#define nrtb_timer_h
-
-#include <sys/time.h>
-#include <time.h>
-#include <string>
-
-namespace NRTB
-{
-
-/** Provides a timer with microsecond (1e-6 second) resolution.
- **
- ** Actual resolution on x86 machines is probably closer to 1e-4 or
- ** -5, but it's still pretty good.
- **
- ** Usage notes: instanciating this class will start the timer
- ** automagically.
- **/
-class hirez_timer
-{
- public:
- /** initializes the timer and starts it.
- **
- ** Sets the the timer's elapsed time to init number of seconds and
- ** then starts the timer.
- **/
- hirez_timer(const double init = 0);
- /** initalizes the timer and starts it.
- **
- ** The timer's elapsed time is set to match the value provided in
- ** init, and the timer is started.
- **/
- hirez_timer(const timeval & init);
-
- /** Clears the timer.
- **
- ** If the timer is running, this stops it as well.
- **/
- void reset();
- /** Starts the timer.
- **
- ** Starts the timer, and optionally sets the elapsed time at
- ** startup to the value provided in init.
- **/
- void start(const double init = 0);
- /** Starts the timer.
- **
- ** Sets the elapsed time equal to the value provided in init
- ** and then starts the timer.
- **/
- void start(const timeval & init);
- /** Stops the timer.
- **
- ** Unconditionally stops the timer, preserving the current
- ** values. It may be restarted using the start() method,
- ** acummulating from where is stopped.
- **
- ** Returns the elapsed time in seconds.
- **/
- double stop();
- /** Stops the timer.
- ** Unconditionally stops the timer, preserving the current
- ** values. It may be restarted using the start() method,
- ** acummulating from where is stopped.
- **
- ** The return value is the elapsed time in microseconds
- ** (1e-6 seconds).
- **/
- unsigned long long stop_as_usec();
- /** Returns the current elapsed time.
- **
- ** Returns the number of elapsed seconds on the timer. No change
- ** is made to the timer's status.
- **/
- double interval();
- /** Returns the current elapsed time.
- **
- ** Returns the elapsed time in microseconds (1e-6 seconds). No
- ** change is made to the timer's status.
- **/
- unsigned long long interval_as_usec();
- /** Returns the elapsed time in a tm struct.
- **
- ** See "man gmtime" for the structure of the tm struct. In short
- ** though, this method provides the current timer value in a
- ** structure where it's broken down into hours, minutes, seconds and
- ** the like. No change is made to the timer's status.
- **/
- tm interval_as_tm();
- /** Returns the elapsed time in H:MM:SS format
- **
- ** Returns the elapsed time in a std::string. The formate is
- ** H:MM:SS.sssss. No change is made to the timer's status.
- ** If the optional days parameter is set to true, the number
- ** of days will be displayed and hours will be < 24, otherwise
- ** no days are shown and hours will grow as needed (default).
- **/
- std::string interval_as_HMS(const bool days = false);
- private:
- unsigned long long int starttime;
- unsigned long long int elapsed;
- bool running;
-
- inline double ll_to_double(const unsigned long long int ll);
- inline timeval ll_to_tv(const unsigned long long int ll);
- inline unsigned long long int double_to_ll(const double d);
- inline unsigned long long int tv_to_ll(const timeval & tv);
- inline void stop_timer();
- inline unsigned long long int interval_time();
-};
-
-}; // namespace NRTB
-
-#endif // nrtb_timer_h
=== modified file 'common/timer/timer_test.cpp'
--- common/timer/timer_test.cpp 2010-01-14 02:24:25 +0000
+++ common/timer/timer_test.cpp 2011-08-25 04:57:16 +0000
@@ -1,40 +1,72 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
/* hirez_timer test program */
#include <iostream>
#include <unistd.h>
-#include <nrtb_timer.h>
+#include "hires_timer.h"
-using namespace NRTB;
+using namespace nrtb;
using namespace std;
int main()
{
hirez_timer overall;
hirez_timer interval;
- overall.start();
- interval.start();
- cout << "sleep 1 second" << endl;
- sleep(1);
- cout << overall.interval() << " | " << interval.stop() << " (stop)" << endl;
- cout << "sleep 3 seconds" << endl;
- sleep(3);
- cout << overall.interval() << " | " << interval.stop() << " (start)" << endl;
- interval.start();
- cout << "sleep 2 seconds" << endl;
- sleep(2);
- cout << overall.interval() << " | " << interval.stop() << " (reset)" << endl;
- interval.reset();
- interval.start();
- cout << "sleep 500000 useconds" << endl;
- usleep(500000);
- cout << overall.interval() << " | " << interval.stop() << endl;
- // test the advanced formationg function
- interval.start(109472.34);
- interval.stop();
- cout << "Extended interval_as_HMS() test: \""
- << interval.interval_as_HMS(true)
- << "\" or \"" << interval.interval_as_HMS() << "\"" << endl;
- cout << "Total run time: " << overall.interval() << " seconds." << endl;
- return 0;
+ int returnme = 0;
+ try
+ {
+ overall.start();
+ interval.start();
+ cout << "sleep 0.1 second" << endl;
+ usleep(1e5);
+ cout << overall.interval() << " | " << interval.stop() << " (stop)" << endl;
+ cout << "sleep 0.3 seconds" << endl;
+ usleep(3e5);
+ cout << overall.interval() << " | " << interval.stop() << " (start)" << endl;
+ interval.start();
+ cout << "sleep 0.2 seconds" << endl;
+ usleep(2e5);
+ cout << overall.interval() << " | " << interval.stop() << " (reset)" << endl;
+ interval.reset();
+ interval.start();
+ cout << "sleep 0.5 seconds" << endl;
+ usleep(5e5);
+ cout << overall.interval() << " | " << interval.stop() << endl;
+ // test the advanced formationg function
+ interval.start(109472.34);
+ interval.stop();
+ cout << "Extended interval_as_HMS() test: \""
+ << interval.interval_as_HMS(true)
+ << "\" or \"" << interval.interval_as_HMS() << "\"" << endl;
+ cout << "Total run time: " << overall.stop() << " seconds." << endl;
+ if ((overall.interval() < 1.1) or (overall.interval() > 1.105))
+ {
+ cerr << "Measured runtime "
+ << overall.interval()
+ << " is outside of expected limits, failed test" << endl;
+ returnme = 1;
+ };
+ }
+ catch (...)
+ {
+ returnme = 1;
+ };
+ return returnme;
};
=== added directory 'common/transceiver'
=== added file 'common/transceiver/Makefile'
--- common/transceiver/Makefile 1970-01-01 00:00:00 +0000
+++ common/transceiver/Makefile 2011-08-25 04:57:16 +0000
@@ -0,0 +1,31 @@
+#***********************************************
+#This file is part of the NRTB project (https://launchpad.net/nrtb).
+#
+# NRTB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+#
+#***********************************************
+
+lib: transceiver_test
+ @./transceiver_test
+ @cp -v transceiver.h ../include
+ @echo build complete
+
+transceiver_test: transceiver.h transceiver_test.cpp
+ @rm -f transceiver_test
+ g++ -c transceiver_test.cpp -I../include
+ g++ -o transceiver_test transceiver_test.o ../obj/common.o ../obj/log_setup.o ../obj/serializer.o ../obj/base_thread.o ../obj/base_socket.o ../obj/confreader.o -lpthread -lprotobuf ../lib/nrtb_gpb.a -lPocoFoundation
+
+clean:
+ @rm -rvf *.o transceiver_test ../include/transceiver.h *.log ../obj/transceiver.o
+ @echo all objects and executables have been erased.
=== added file 'common/transceiver/transceiver.cpp'
--- common/transceiver/transceiver.cpp 1970-01-01 00:00:00 +0000
+++ common/transceiver/transceiver.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,140 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include "transceiver.h"
+#include <confreader.h>
+#include <serializer.h>
+#include <string>
+#include <sstream>
+
+using namespace nrtb;
+using namespace std;
+
+serializer tscvr_sequence(0);
+
+template <class out, class in, class outp, class inp>
+transceiver<out,in,outp,inp>::transceiver(tcp_socketp socket)
+{
+ // get the configuration parameters.
+ global_conf_reader & config = global_conf_reader::get_instance();
+ send_time_limit = config.get<unsigned int>("transceiver.send_timeout",2);
+ attempt_recovery = config.get<bool>("transceiver.allow_recovery",true);
+ error_run_limit =
+ config.get<unsigned int>("transceiver.max_consecutive_errors",10);
+ sent_messages.resize(config.get<int>("transceiver.history_size",50));
+ // set up logging
+ std::stringstream s;
+ s << "transceiver:_" << tscvr_sequence();
+ log = Poco::Logger::get(s.str());
+ // set up the socket.
+ sock(socket);
+ // annouce ourselves...
+ log->trace("Instanciated.");
+ s.str("");
+ s << "history_size=" << sent_messages.size()
+ << ", send_timeout=" << send_time_limit
+ << ", attempt_recovery=" << attempt_recovery
+ << ", error_run_limit=" << error_run_limit
+ << ", remote_address=" << sock->get_remote_address()
+ << ", local_address=" << sock->get_local_address();
+ log->trace(s.str());
+};
+
+template <class out, class in, class outp, class inp>
+transceiver<out,in,outp,inp>::~transceiver()
+{
+ log->trace("In ~transciever");
+ // shutdown the socket.
+ sock->close();
+ // discard the sent messages list.
+ sent_messages.clear();
+ log->trace("shutdown complete.");
+};
+
+template <class out, class in, class outp, class inp>
+inp transceiver<out,in,outp,inp>::get()
+{
+ inp returnme(new in);
+ string input = sock->getln();
+ returnme->ParseFromString(input);
+ // for the first messsge any number is
+ // accepted.
+ if (last_inbound == 0)
+ {
+ last_inbound = returnme->msg_uid();
+ }
+ else
+ {
+ last_inbound++;
+ int temp = returnme->msg_uid();
+ if (temp != last_inbound)
+ {
+ inbound_seq_error e();
+ stringstream message;
+ message << "Expected " << last_inbound
+ << " received " << temp;
+ e.store(message.str());
+ throw e;
+ };
+ };
+ return returnme;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::send(outp sendme)
+{
+ sendme->set_msg_uid(out_msg_num());
+ string output;
+ output = sendme->SerializeAsString() + "\r";
+ sock->put(output);
+ sent_messages.push_back(sendme);
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::nak_invalid_context(const unsigned long int msg_number)
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::nak_invalid_context not implemented.");
+ throw e;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::nak_validation_error(const unsigned long int msg_number)
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::nak_validation_error not implemented.");
+ throw e;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::handle_inbound_nak()
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::handle_inbound_nak not implemented.");
+ throw e;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::handle_outbound_nak()
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::handle_outbound_nak not implemented.");
+ throw e;
+};
+
+
=== added file 'common/transceiver/transceiver.h'
--- common/transceiver/transceiver.h 1970-01-01 00:00:00 +0000
+++ common/transceiver/transceiver.h 2011-08-25 04:57:16 +0000
@@ -0,0 +1,276 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#ifndef nrtb_transceiver_h
+#define nrtb_transceiver_h
+
+#include <string>
+#include <sstream>
+#include <common.h>
+#include <base_socket.h>
+#include <serializer.h>
+#include <confreader.h>
+#include <Poco/Logger.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/circular_buffer.hpp>
+
+namespace nrtb
+{
+ /*******************************************************************
+ * transceiver is used to manage GBP (google protocol buffers)
+ * traffic between NRTB components. This is the only form of
+ * IPC used by NRTB.
+ *
+ * out is the channel wrapper for the outbound channel.
+ * in is the channel wrapper for the inbound channel.
+ *
+ * The nrtb::confreader singleton will be queried for the
+ * following parameters:
+ *
+ * transceiver.history_size (int)
+ * transceiver.send_timeout (int)
+ * transceiver.allow_recovery (bool)
+ * transceiver.max_consecutive_errors (int)
+ *
+ * See https://blueprints.launchpad.net/nrtb/+spec/icp-spec for
+ * specification this class implements.
+ * ***************************************************************/
+ template <class out, class in,
+ class outp = boost::shared_ptr<out>,
+ class inp = boost::shared_ptr<in> >
+ class transceiver
+ {
+ public:
+ typedef inp in_ptr;
+ typedef outp out_ptr;
+
+ union msg_num_t
+ {
+ uint32_t number;
+ unsigned char bytes[4];
+ };
+
+ /*************************************************************
+ * Creates the transceiver and associates it with a provided
+ * socket. Once created this class assumes it uniquely owns the
+ * socket and will close it upon distruction.
+ * ***********************************************************/
+ transceiver(tcp_socketp socket);
+ /*************************************************************
+ * Closes the socket and releases all mmemory associated with
+ * this class.
+ * ***********************************************************/
+ ~transceiver();
+ /**************************************************************
+ * gets the next message from the socket. If no messages are
+ * ready, blocks util one arrives.
+ * ***********************************************************/
+ inp get();
+ /**************************************************************
+ * Sends a message over the socket and adds it to the
+ * sent_messages buffer in case it's needed for error recovery.
+ * ***********************************************************/
+ void send(outp sendme);
+ /**q************************************************************
+ * Called by the data consumer when an inbound message was
+ * not valid in the current application context. msg_number
+ * is the sequence number of the offending message.
+ * ***********************************************************/
+ void nak_invalid_context(const unsigned long int msg_number);
+ /**************************************************************
+ * Called by the data consumer when an inbound message's
+ * data failed validation checks. msg_number is the sequence
+ * number of the offending message.
+ * ***********************************************************/
+ void nak_validation_error(const unsigned long int msg_number);
+ /**************************************************************
+ * Exceptions which may be thrown for external resulution.
+ * ***********************************************************/
+ /// parent of all transceiver exceptions
+ class general_exception : public nrtb::base_exception {};
+ /// thrown if the a unexpected msg_uid is received
+ class inbound_seq_error : public general_exception {};
+ /// thrown the connection is unexpectedly lost
+ class connection_lost: public general_exception {};
+ /// thrown if too many sequencial errors occur.
+ class too_many_errors: public general_exception {};
+
+ protected:
+ unsigned int send_time_limit;
+ bool attempt_recovery;
+ unsigned int error_run_limit;
+ /// the name used for logging
+ std::string logname;
+ /// The socket used for communcation.
+ tcp_socketp sock;
+ /// serializer used for message numbers
+ serializer out_msg_num;
+ /// last received message number
+ unsigned long long last_inbound;
+ /// buffer to hold previously sent messages; required for
+ /// error recovery.
+ boost::circular_buffer<outp> sent_messages;
+ /// fence post for recovery efforts, zero if none in play
+ unsigned long long nak_fence_post;
+ /// These methods implment actual nak recovery.
+ void handle_inbound_nak();
+ void handle_outbound_nak();
+ };
+
+
+serializer tscvr_sequence(0);
+
+template <class out, class in, class outp, class inp>
+transceiver<out,in,outp,inp>::transceiver(tcp_socketp socket)
+{
+ // get the configuration parameters.
+ global_conf_reader & config = global_conf_reader::get_instance();
+ send_time_limit = config.get<unsigned int>("transceiver.send_timeout",2);
+ attempt_recovery = config.get<bool>("transceiver.allow_recovery",true);
+ error_run_limit =
+ config.get<unsigned int>("transceiver.max_consecutive_errors",10);
+ sent_messages.resize(config.get<int>("transceiver.history_size",50));
+ last_inbound = 0;
+ // set up logging
+ std::stringstream s;
+ s << "transceiver:_" << tscvr_sequence();
+ logname = s.str();
+ Poco::Logger & log = Poco::Logger::get(logname);
+ // set up the socket.
+ sock = socket;
+ // annouce ourselves...
+ log.trace("Instanciated.");
+ s.str("");
+ s << "history_size=" << sent_messages.size()
+ << ", send_timeout=" << send_time_limit
+ << ", attempt_recovery=" << attempt_recovery
+ << ", error_run_limit=" << error_run_limit
+ << ", remote_address=" << sock->get_remote_address()
+ << ", local_address=" << sock->get_local_address();
+ log.trace(s.str());
+};
+
+template <class out, class in, class outp, class inp>
+transceiver<out,in,outp,inp>::~transceiver()
+{
+ Poco::Logger & log = Poco::Logger::get(logname);
+ log.trace("In ~transciever");
+ // shutdown the socket.
+ sock->close();
+ // discard the sent messages list.
+ sent_messages.clear();
+ log.trace("shutdown complete.");
+};
+
+template <class out, class in, class outp, class inp>
+inp transceiver<out,in,outp,inp>::get()
+{
+ // get the message length first.
+ std::string len_field = sock->get(4,10);
+//std::cout << "len_field=" << http_chartohex(len_field) << std::endl;
+ msg_num_t msg_len;
+ for (int i=0; i<4; i++)
+ {
+ msg_len.bytes[i] = len_field[i];
+ };
+//std::cout << ":len=" << msg_len.number << std::endl;
+ // get the rest of the message.
+ inp returnme(new in);
+ std::string input = sock->get(msg_len.number);
+//std::cout << ":received=" << http_chartohex(input) << std::endl;
+ returnme->ParseFromString(input);
+ // for the first messsge any number is
+ // accepted.
+ if (last_inbound == 0)
+ {
+ last_inbound = returnme->msg_uid();
+ }
+ else
+ {
+ last_inbound++;
+ int temp = returnme->msg_uid();
+ if (temp != last_inbound)
+ {
+ inbound_seq_error e;
+ std::stringstream message;
+ message << "Expected " << last_inbound
+ << " received " << temp;
+ e.store(message.str());
+ throw e;
+ };
+ };
+ return returnme;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::send(outp sendme)
+{
+ sendme->set_msg_uid(out_msg_num());
+ std::string output;
+ output = sendme->SerializeAsString();
+ msg_num_t msg_len;
+ msg_len.number = output.size();
+//std::cout << "num:len" << msg_len.number << ":" << output.length() << //std::endl;
+ std::string num_field = " ";
+ for (int i=0; i<4; i++)
+ {
+ num_field[i] = msg_len.bytes[i];
+//std::cout << int(num_field[i]) << "," ;
+ };
+//std::cout << " = " << msg_len.number << std::endl;
+ output = num_field + output;
+//std::cout << "out msg=" << http_chartohex(output) << std::endl;
+ sock->put(output);
+ sent_messages.push_back(sendme);
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::nak_invalid_context(const unsigned long int msg_number)
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::nak_invalid_context not implemented.");
+ throw e;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::nak_validation_error(const unsigned long int msg_number)
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::nak_validation_error not implemented.");
+ throw e;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::handle_inbound_nak()
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::handle_inbound_nak not implemented.");
+ throw e;
+};
+
+template <class out, class in, class outp, class inp>
+void transceiver<out,in,outp,inp>::handle_outbound_nak()
+{
+ general_exception e;
+ e.store("transceiver<outp,inp>::handle_outbound_nak not implemented.");
+ throw e;
+};
+
+} // namespace nrtb
+
+#endif //nrtb_transceiver_h//
\ No newline at end of file
=== added file 'common/transceiver/transceiver_test.cpp'
--- common/transceiver/transceiver_test.cpp 1970-01-01 00:00:00 +0000
+++ common/transceiver/transceiver_test.cpp 2011-08-25 04:57:16 +0000
@@ -0,0 +1,145 @@
+/***********************************************
+ This file is part of the NRTB project (https://*launchpad.net/nrtb).
+
+ NRTB is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NRTB 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 NRTB. If not, see <http://www.gnu.org/licenses/>.
+
+ **********************************************/
+
+#include <string>
+#include <iostream>
+#include <base_thread.h>
+#include <base_socket.h>
+#include "transceiver.h"
+#include <log_setup.h>
+#include <sim_to_db_wrapper.pb.h>
+#include <boost/random.hpp>
+
+using namespace nrtb;
+using namespace std;
+
+typedef nrtb_msg::sim_to_db my_msg;
+typedef boost::shared_ptr<my_msg> my_msgp;
+typedef transceiver<my_msg,my_msg> linkt;
+
+int er_count = 0;
+
+class server_work_thread: public runnable
+{
+public:
+
+ tcp_socketp sock;
+ unsigned long long last_inbound;
+
+ void run()
+ {
+ set_cancel_anytime();
+ linkt link(sock);
+ while (sock->status() == tcp_socket::sock_connect)
+ {
+ try
+ {
+ linkt::out_ptr inbound = link.get();
+ last_inbound = inbound->msg_uid();
+ cout << "\tReceived #" << last_inbound << endl;
+ if (last_inbound == 99)
+ exit(0);
+ }
+ catch (linkt::general_exception & e)
+ {
+ cerr << "Server work thread caught " << e.what()
+ << "\n\tComment: " << e.comment() << endl;
+ er_count++;
+ }
+ catch (tcp_socket::general_exception & e)
+ {
+ cerr << "Server work thread caught " << e.what()
+ << "\n\tComment: " << e.comment() << endl;
+ er_count++;
+ }
+ catch (std::exception & e)
+ {
+ cerr << "Server work thread caught " << e.what()
+ << endl;
+ er_count++;
+ };
+ };
+ };
+};
+
+thread process;
+server_work_thread task;
+
+class listener: public tcp_server_socket_factory
+{
+public:
+ listener(const string & add, const int & back)
+ : tcp_server_socket_factory(add, back) {};
+
+ void on_accept()
+ {
+ if (!process.is_running())
+ {
+ task.last_inbound = 0;
+ task.sock.reset(connect_sock);
+ process.start(task);
+ cout << "server thread running." << endl;
+ }
+ else
+ {
+ tcp_socketp s(connect_sock);
+ s->close();
+ cerr << "Multiple attempts to connect to server"
+ << endl;
+ };
+ };
+};
+
+string address = "127.0.0.1:";
+int port_base = 12334;
+
+int main()
+{
+ setup_global_logging("transceiver.log");
+
+ //set up our port and address
+ boost::mt19937 rng;
+ rng.seed(time(0));
+ boost::uniform_int<> r(0,1000);
+ stringstream s;
+ s << address << port_base + r(rng);
+ address = s.str();
+ cout << "Using " << address << endl;
+
+ // kick off the listener thread.
+ listener server(address,5);
+ server.start_listen();
+ usleep(5e5);
+
+ // set up our sender
+ tcp_socketp sock(new tcp_socket);
+ sock->connect(address);
+ linkt sender(sock);
+
+ // Let's send a few things.
+ for (int i=0; i<100; i++)
+ {
+ my_msgp msg(new my_msg);
+ sender.send(msg);
+ cout << "Sent " << msg->msg_uid() << endl;
+ usleep(1e4);
+ };
+
+ usleep(5e5);
+ return er_count;
+};
\ No newline at end of file
=== removed directory 'common/work_queue_thread'
=== removed file 'common/work_queue_thread/Makefile'
--- common/work_queue_thread/Makefile 2010-01-14 02:24:25 +0000
+++ common/work_queue_thread/Makefile 1970-01-01 00:00:00 +0000
@@ -1,144 +0,0 @@
-# This file was automatically generated by makemake.
-# Do not edit it directly!
-# Any changes you make will be silently overwritten.
-
-# Edit this file to define constants and custom build targets.
-# Please refer to the makemake documentation for more information.
-#
-# To compile multiple versions of a program or library, please study
-# http://www.its.caltech.edu/~jafl/jcc/multi_version.html
-
-# Useful directories
-
-MYCODEDIR := .
-
-# Directories to search for header files
-
-SEARCHDIRS := -I- -I${MYCODEDIR} -I../ricks_handy -I../threads -I../free_thread
-
-# makemake variables
-
-DEPENDFLAGS := -g -Wall -Werror ${SEARCHDIRS}
-
-# C preprocessor (C, C++, FORTRAN)
-
-CPPFLAGS =
-
-# C compiler
-
-CC := gcc
-CFLAGS = ${DEPENDFLAGS}
-
-%.o : %.c
- ${CC} ${CPPFLAGS} ${CFLAGS} -c $< -o $@
-
-# C++ compiler
-
-CXX := g++
-CXXFLAGS = ${DEPENDFLAGS}
-
-%.o : %.cc
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-%.o : %.C
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-%.o : %.cpp
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-%.o : %.cxx
- ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@
-
-# FORTRAN compiler
-
-FC := f77
-FFLAGS =
-
-# C/C++/Eiffel/FORTRAN linker
-
-LINKER := g++
-LDFLAGS = ../ricks_handy/ricks_handy.o ../threads/base_thread.o ../free_thread/free_thread.o
-LOADLIBES := -lm -lpthread
-
-# Java compiler
-
-JAVAC := javac
-JFLAGS =
-JAR := jar
-
-%.class : %.java
- ${JAVAC} ${JFLAGS} $<
-
-
-# This is what makemake added
-
-
-# work_queue_tester
-
-work_queue_tester : ./wqt_tester.o
- ${LINKER} ${LDFLAGS} -o $@ ${filter-out %.a %.so, $^} ${LOADLIBES}
-
-# target for making everything
-
-.PHONY : all
-all: work_queue_tester
-
-
-# target for removing all object files
-
-.PHONY : tidy
-tidy::
- @rm -fv core ./wqt_tester.o
-
-# target for removing all object files
-
-.PHONY : clean
-clean:: tidy
- @rm -fv work_queue_tester ../../include3/work_queue_thread.h ../../include3/controlled_wqt.h
-
-.PHONY : lib
-lib : ./wqt_tester.o ../../include3/work_queue_thread.h ../../include3/controlled_wqt.h
-
-../../include3/work_queue_thread.h: work_queue_thread.h
- @cp -fv work_queue_thread.h ../../include3
-
-../../include3/controlled_wqt.h: controlled_wqt.h
- @cp -fv controlled_wqt.h ../../include3
-
-# list of all source files
-
-MM_ALL_SOURCES := ./wqt_tester.cpp
-
-
-# target for checking a source file
-
-CHECKSYNTAXFILE := ${basename ${filter %${CHECKSTRING}, ${MM_ALL_SOURCES}}}
-
-.PHONY : checksyntax
-checksyntax:
- ifneq (${CHECKSYNTAXFILE},)
- @${MAKE} ${addsuffix .o, ${CHECKSYNTAXFILE}}
- else
- @echo No target to make ${CHECKSTRING}
- endif
-
-
-# target for touching appropriate source files
-
-.PHONY : touch
-touch::
- @list=$$(grep -l ${TOUCHSTRING} ${MM_ALL_SOURCES}); \
- for file in $$list; do { echo $$file; touch $$file; } done
-
-
-# target for calculating dependencies (MAKEMAKE)
-
-.PHONY : jdepend
-jdepend:
- @${MAKEMAKE} --depend Makefile -- ${DEPENDFLAGS} -- ./wqt_tester.cpp ./wqt_tester.o
-
-
-# DO NOT DELETE THIS LINE -- makemake depends on it.
-
-./wqt_tester.o: ../free_thread/free_thread.h ../ricks_handy/ricks_handy.h ../threads/base_thread.h ./work_queue_thread.h /usr/include/pthread.h /usr/include/time.h ./controlled_wqt.h
-
=== removed file 'common/work_queue_thread/controlled_wqt.h'
--- common/work_queue_thread/controlled_wqt.h 2010-01-14 02:24:25 +0000
+++ common/work_queue_thread/controlled_wqt.h 1970-01-01 00:00:00 +0000
@@ -1,202 +0,0 @@
-#include <work_queue_thread.h>
-#include <free_thread.h>
-#include <list>
-
-namespace NRTB
-{
-
-/** A work_queue_thread with a global control queue.
- **
- ** First, read the documentation on work_queue_thread if you are not
- ** comfortable with it. A controlled_work_queue_thread differs from a
- ** work_queue_thread in that it mechanism for sending "control" messages
- ** to all of the threads servicing a given queue.
- **
- ** Control messages are sent once, but are presented to each worker thread
- ** servicing the queue. They are processed on a priority basis, before
- ** messages in the normal work queue, and are always processed in the
- ** order they were sent by each thread. Control messages may be a
- ** completely different data type than the work queue messages and are
- ** proceessed by a different method.
- **
- ** In addition to overridding the do_work() abstract method, you must
- ** override the do_control() method to define the actions you'll take on
- ** any given control message. In addition to the new required template
- ** parameter cltmsg, there is a new optional ctl_seq parameter that allows
- ** you to override the control queue type; it's subject to the same rules
- ** and restrictions as noted for the seq parameter, save that it must
- ** store control messages.
- **
- ** The default thread type for controlled_work_queue_threads is
- ** ricklib::free_thread. This is a good choice, since the control queue
- ** allows easy manipulalation of free running threads. However, this
- ** template should work fine with any clean descendent of ricklib::thread.
- **
- ** NOTE: run() and closeout() provide critical functionality for the
- ** automatic maintainence of the thread list and control queue. Don't
- ** override them unless you grok them completely. In practice, I don't
- ** expect you'll ever need to.
-**/
-template <class workpacket, class ctlmsg,
- class threadclass=ricklib::free_thread,
- int q_uid = 0, class seq = std::queue<workpacket>,
- class ctl_seq = std::queue<ctlmsg> >
-class controlled_work_queue_thread
- : public work_queue_thread<workpacket,threadclass,q_uid,seq>
-{
- public:
- /** Sends a control message to all the threads.
- **
- ** The ctlmsg is copied into the control queue of all the
- ** worker threads operating off of the same work queue.
- **/
- static void control(const ctlmsg & msg);
- protected:
- typedef work_queue_thread<workpacket,threadclass,q_uid,seq> parent_type;
- // static control variables.
- typedef controlled_work_queue_thread * cwqt_ptr;
- typedef std::list<cwqt_ptr> tlist_type;
- static tlist_type tlist;
- // local control variables;
- ctl_seq ctl_queue;
- bool control_pending;
- ctlmsg my_ctl;
- // methods
- /** Provides control message handling.
- **
- ** You must override this method to provide the control
- ** message processing logic. On entry, the control message
- ** will be in the my_ctl data element. You are free to take
- ** any action here, including terminating the thread run.
- **/
- virtual void do_control() = 0;
- /** called when the thread had data to process.
- **
- ** Override this to do the work you need to do. You'll find the data
- ** in my_data, which will be of whatever type you specified to make
- ** the template. This is where you'll actually get the work on the
- ** workpacket done.
- **/
- virtual void do_work() = 0;
- // must override park() and run()
- virtual void park();
- virtual void run();
- // insures thread is pulled from tlist
- static void closeout(void * arg);
- public:
- // used to add control message to the private queue
- // it is not intended to be called directly by user code.
- // Due to the lack of mutex locking here, calling this
- // can result in _EVIL_ behaviour.
- virtual void add_to_control(const ctlmsg & msg);
-};
-
-template <class workpacket, class ctlmsg,
- class threadclass, int q_uid, class seq, class ctl_seq>
-typename controlled_work_queue_thread<workpacket,ctlmsg,threadclass,
- q_uid,seq,ctl_seq>::tlist_type
- controlled_work_queue_thread<workpacket,ctlmsg,threadclass,
- q_uid,seq,ctl_seq>::tlist;
-
-template <class workpacket, class ctlmsg,
- class threadclass, int q_uid, class seq, class ctl_seq>
-void controlled_work_queue_thread<workpacket,ctlmsg,threadclass,
- q_uid,seq,ctl_seq>::control(const ctlmsg & msg)
-{
- scope_lock lock(parent_type::latch);
- typename tlist_type::iterator it = tlist.begin();
- typename tlist_type::iterator end = tlist.end();
- while (it != end)
- {
- if (*it)
- {
- (*it)->add_to_control(msg);
- };
- it++;
- };
- parent_type::latch.broadcast_signal();
-};
-
-template <class workpacket, class ctlmsg,
- class threadclass, int q_uid , class seq, class ctl_seq>
-void controlled_work_queue_thread<workpacket,ctlmsg,threadclass,
- q_uid,seq,ctl_seq>::park()
-{
- scope_lock lock(parent_type::latch);
- pthread_cleanup_push(ricklib::safe_unlock,&parent_type::latch);
- // increment the number of threads available.
- parent_type::available++;
- control_pending = false;
- // wait until there is data to work with.
- // NOTE: this latch.wait() is a thread cancelation point.
- while (parent_type::pending.empty() && ctl_queue.empty()) {parent_type::latch.wait(); };
- // take the message as our own and clear the buffer.
- if (ctl_queue.empty())
- {
- parent_type::my_data = parent_type::pending.front();
- parent_type::pending.pop();
- }
- else
- {
- my_ctl = ctl_queue.front();
- ctl_queue.pop();
- control_pending = true;
- };
- // decrement the number of threads available.
- parent_type::available--;
- pthread_cleanup_pop(0);
-};
-
-template <class workpacket, class ctlmsg,
- class threadclass, int q_uid, class seq, class ctl_seq>
-void controlled_work_queue_thread<workpacket,ctlmsg,threadclass,
- q_uid,seq,ctl_seq>::run()
-{
- {
- scope_lock lock(parent_type::latch);
- tlist.push_front(this);
- }
- pthread_cleanup_push(controlled_work_queue_thread::closeout,this);
- while (true)
- {
- try
- {
- park(); // this is a cancellation point
- if (control_pending)
- {
- do_control();
- }
- else
- {
- do_work();
- };
- }
- catch (std::exception & e)
- {
- std::clog << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__
- << ": WARNING!! unexpected exception \"" << e.what() << "\" caught." << std::endl;
- };
- };
- pthread_cleanup_pop(1);
-};
-
-template <class workpacket, class ctlmsg,
- class threadclass, int q_uid, class seq, class ctl_seq>
-void controlled_work_queue_thread<workpacket,ctlmsg,threadclass,
- q_uid,seq,ctl_seq>::add_to_control(const ctlmsg & msg)
-{
- // can not lock latch here, because it's locked by control();
- // ___NEVER___ call this method directly
- ctl_queue.push(msg);
-};
-
-template <class workpacket, class ctlmsg,
- class threadclass, int q_uid, class seq, class ctl_seq>
-void controlled_work_queue_thread<workpacket,ctlmsg,threadclass,
- q_uid,seq,ctl_seq>::closeout(void * arg)
-{
- scope_lock lock(parent_type::latch);
- tlist.remove(static_cast<cwqt_ptr>(arg));
-};
-
-} // namespace NRTB
=== removed file 'common/work_queue_thread/work_queue_thread.h'
--- common/work_queue_thread/work_queue_thread.h 2010-01-14 02:24:25 +0000
+++ common/work_queue_thread/work_queue_thread.h 1970-01-01 00:00:00 +0000
@@ -1,248 +0,0 @@
-/* provides the basic thread for writing to a database.
-
- TODO: Needs to be adapted to us boost threads.
-
-*/
-
-#ifndef work_queue_thread_h
-#define work_queue_thread_h
-
-#include <base_thread.h>
-#include <fstream>
-#include <queue>
-#include <iostream>
-
-namespace ricklib
-{
-
-/** Implements a general purpose queue driven worker thread framework.
- **
- ** work_queue_threads are useful where you need one or more threads to
- ** process data asyncronously from the feed rate. work_queue_threads can
- ** operate singlely or in teams working the same input queue. Example
- ** applictions could be as a connection handler on a web server, or any
- ** place where you need to business while allowing a "slow" task (i.e.
- ** remote communications) to complete without affecting the primary task.
- **
- ** This template takes two class arguments to instanciate. The first,
- ** workpacket, is the data type that will be passed in and queued for the
- ** threads to work on. The second, threadclass, must implement the same
- ** interface as ricklib::thread, and is in fact generally expected to be
- ** ricklib::thread or one of descendents such as ricklib::free_thread.
- **
- ** Things to remember about classes instanciated from work_queue_thread:
- **
- ** 1. All instances of a class based on work_queue_thread with the
- ** same parameters share the same work queue, and will share in
- ** processing the data placed in that queue by the store() method. As
- ** long as there are one or more instances of a given class running,
- ** the data will processed as soon as thread can get to it.
- **
- ** 2. If you need different classes descended from work_queue_thread
- ** with the same workpacket and thread types, the optional q_uid
- ** template parameter (the third one) when instanciating the
- ** work_queue_threads. q_uid defaults to 0 if not specified, but can be
- ** specified to any valid int value, allowing you have many different
- ** queues that were instanciated with the same data types. If you don't
- ** use q_uid, all work_queue_thread descendents instanciated with the
- ** same workpacket and threadclass types _will_ share the same work
- ** queue, even if they have different do_run() methods.
- **
- ** 3. If you need a different queue type than the default
- ** queue<workpacket> you can use the optional template parameter seq
- ** (the fourth parameter) to specify a different queue type. It will
- ** expected to provide push(), front() and pop() methods, and store and
- ** return the same datatype specified in the first template parameter.
- ** Use this if you need a priority queue for example. Of course, if
- ** you want to specify the queue type, you'll need to specify the q_uid
- ** parameter discussed above.
- **
- ** 4. Data passed to the class using the store() method will be queued,
- ** even if the there are no instances running at that time (both the
- ** data queue and store method are static). processing will start at
- ** the front of the queue in FIFO order once an instance is started.
- **
- ** 5. Please note that there is no way provided in this template
- ** to get data back from the thread once it is processed. If you need
- ** feedback, you'll need to build the mechanism yourself.
- **
- ** 6. As with all derivitives of ricklib::runnable, you can not safely
- ** put a work_queue_thread in most STL constainers because they reserve
- ** the right to move things around at whim. Instead, derive a
- ** boost::shared_ptr for your class, create your instances on the heap,
- ** and put the shared_ptrs in the container instead. Don't use auto_ptr
- ** because it's definately not safe in containers; in fact Containers
- ** Of AutoPtrs (COAPs) are explictitly disallowed by the current C++
- ** standard.
- **
- ** Usage Notes:
- **
- ** You'll need to create a descendent of work_queue_thread and override
- ** do_work() at a minimum to use this. If you need to keep track of
- ** threads after they are created keep smart pointers to them in a
- ** container as noted in #6 above. Don't override run() as you would with
- ** other descendents of ricklib::runable as it controls the park,do_work
- ** cycle. Instead put code to accomplish the needed task in do_work(),
- ** which is abstract in the template anyway.
-**/
-template <class workpacket, class threadclass=boost::thread,
- int q_uid = 0, class seq = std::queue<workpacket> >
-class work_queue_thread : public threadclass
-{
- public:
- /** Stores the workpacket in the pending work queue.
- **
- ** Returns immediately and the message is processed asyncronously.
- ** Generally you will not want to override this method.
- **/
- static void store(const workpacket & message);
- /** Returns the number of threads awaiting work.
- **
- ** Another way to look at this that it represents the number of
- ** running threads the class that are currently idle awaiting work.
- ** When the threads are idle, they are not consuming CPU cycles.
- **/
- static unsigned int parked();
- /** Returns the number of workpackets awaiting processing.
- **
- ** Technically, this returns pending.size(), or the number of
- ** elements in the deque that stores work packets not yet picked up
- ** for processing by a thread. Assuming you have one or more running
- ** instances of your class, this should be zero most of the time
- ** unless you've managed to get all your threads busy at once.
- **
- ** If you occasionally check this value and keep finding it > 0, you
- ** might consider creating more instances of your class.
- **/
- static unsigned int work_pending();
- /** Releases a stalled thread.
- **
- ** Technically, this method signals the condition variable to force
- ** the thread to check the pending queue. Generally this should not
- ** be needed. It is provided to deal with the POSIX legal situation
- ** where a signal generated by the store() method could be ignored
- ** by the thread. If you are truely paranoid, have some other
- ** process in your application call this every few seconds just
- ** ensure that no one got stuck.
- **
- ** Note that while POSIX says it is acceptable to miss the
- ** occasional signal, I've never seen it happen on a modern system.
- ** If you have data hanging in the queue (work_pending() > 0 and
- ** parked() > 0) you can call this method to start processing again.
- ** I don't expect you'll need it.
- **/
- static void kick_start();
- protected:
- /** latch protects data and available as well as signalling when
- ** there is new data to process. Use work_queue_thread::store() to
- ** store the workpacket for later processing.
- **/
- static cond_variable latch;
- static seq pending;
- static int available;
- // thread local data.
- workpacket my_data;
- /** called when the thread had data to process.
- **
- ** Override this to do the work you need to do. You'll find the data
- ** in my_data, which will be of whatever type you specified to make
- ** the template. This is where you'll actually get the work on the
- ** workpacket done.
- **/
- virtual void do_work() = 0;
- /** called by the thread when it is waiting for data.
- **
- ** Avoid overriding this.
- **/
- virtual void park();
- /** the thread's main execution loop.
- **
- ** Avoid overriding this.
- **/
- virtual void run();
-};
-
-// initialize the static variables.
-template <class workpacket, class threadclass, int q_uid, class seq>
- cond_variable work_queue_thread<workpacket,threadclass,q_uid,seq>::latch;
-template <class workpacket, class threadclass, int q_uid, class seq>
- seq work_queue_thread<workpacket,threadclass,q_uid,seq>::pending;
-template <class workpacket, class threadclass, int q_uid, class seq>
- int work_queue_thread<workpacket,threadclass,q_uid,seq>::available = 0;
-
-template <class workpacket, class threadclass, int q_uid, class seq>
- void work_queue_thread<workpacket,threadclass,q_uid,seq>
- ::store(const workpacket & message)
-{
- scope_lock lock(latch);
- pending.push(message);
- latch.signal();
-};
-
-template <class workpacket, class threadclass, int q_uid, class seq>
- unsigned int work_queue_thread<workpacket,threadclass,q_uid,seq>
- ::parked()
-{
- scope_lock lock(latch);
- return available;
-};
-
-template <class workpacket, class threadclass, int q_uid, class seq>
- unsigned int work_queue_thread<workpacket,threadclass,q_uid,seq>
- ::work_pending()
-{
- scope_lock lock(latch);
- return pending.size();
-};
-
-template <class workpacket, class threadclass, int q_uid, class seq>
- void work_queue_thread<workpacket,threadclass,q_uid,seq>
- ::kick_start()
-{
- scope_lock lock(latch);
- latch.signal();
-};
-
-template <class workpacket, class threadclass, int q_uid, class seq>
- void work_queue_thread<workpacket,threadclass,q_uid,seq>
- ::park()
-{
- scope_lock lock(latch);
- pthread_cleanup_push(ricklib::safe_unlock,&latch);
- // increment the number of threads available.
- available++;
- // wait until there is data to work with.
- // NOTE: this latch.wait() is a thread cancelation point.
- while (pending.empty()) { latch.wait(); };
- // take the message as our own and clear the buffer.
- my_data = pending.front();
- pending.pop();
- // decrement the number of threads available.
- available--;
- pthread_cleanup_pop(0);
-};
-
-template <class workpacket, class threadclass, int q_uid, class seq>
- void work_queue_thread<workpacket,threadclass,q_uid,seq>
- ::run()
-{
- while (true)
- {
- try
- {
- park(); // this is a cancellation point
- do_work();
- }
- catch (std::exception & e)
- {
- std::clog << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__
- << ": WARNING!! unexpected exception caught." << std::endl
- << "\t" << e.what() << std::endl;
- throw e;
- };
- };
-};
-
-}; // namespace NRTB
-
-#endif // work_queue_thread_h
=== removed file 'common/work_queue_thread/wqt_tester.cpp'
--- common/work_queue_thread/wqt_tester.cpp 2010-01-14 02:24:25 +0000
+++ common/work_queue_thread/wqt_tester.cpp 1970-01-01 00:00:00 +0000
@@ -1,154 +0,0 @@
-/* work queue thread test program
-
- TODO: Needs to be adapted to boost::theads.
-
-*/
-
-//#include <free_thread.h>
-#include <work_queue_thread.h>
-#include <controlled_wqt.h>
-#include <time.h>
-#include <unistd.h>
-#include <iostream>
-#include <queue>
-
-using namespace NRTB;
-using namespace std;
-
-mutex c_lock;
-
-class worker : public work_queue_thread<int,free_thread>
-{
- public:
- worker()
- {
- scope_lock lock(wlock);
- iam = count++;
- start();
- scope_lock clock(c_lock);
- cout << "worker " << iam << " started" << std::endl;
- };
- ~worker()
- {
- scope_lock lock(c_lock);
- cout << "worker " << iam << " is being deleted." << std::endl;
- };
- protected:
-
- static mutex wlock;
- static int count;
- int iam;
-
- virtual void do_work()
- {
- if (my_data >= 0)
- {
- timespec delaytime;
- delaytime.tv_sec = 0;
- delaytime.tv_nsec = my_data * 1000;
- nanosleep(&delaytime,NULL);
- {
- scope_lock lock(c_lock);
- cout << "\tworker " << iam
- << " reports packet " << my_data << std::endl;
- }
- }
- else
- {
- pthread_exit(0);
- };
- };
-};
-
-mutex worker::wlock;
-int worker::count = 0;
-
-class workera : public controlled_work_queue_thread<int,int,free_thread,1>
-{
- public:
- workera()
- {
- scope_lock lock(wlock);
- iam = count++;
- start();
- scope_lock clock(c_lock);
- cout << "workera " << iam << " started" << std::endl;
- };
- ~workera()
- {
- scope_lock lock(c_lock);
- cout << "workera " << iam << " is being deleted." << std::endl;
- };
- protected:
-
- static mutex wlock;
- static int count;
- int iam;
-
- virtual void do_work()
- {
- timespec delaytime;
- delaytime.tv_sec = 0;
- delaytime.tv_nsec = my_data * 1000;
- nanosleep(&delaytime,NULL);
- {
- scope_lock lock(c_lock);
- cout << "\t\tworkera " << iam
- << " reports packet " << my_data << std::endl;
- }
- };
-
- virtual void do_control()
- {
- switch (my_ctl)
- {
- case 1: pthread_exit(0); break;
- default:
- {
- scope_lock lock(c_lock);
- cout << "\t\t\tworkera " << iam
- << " received control message " << my_ctl
- << std::endl;
- }
- };
- };
-};
-
-mutex workera::wlock;
-int workera::count = 0;
-
-int main()
-{
- // start five workers
- cout << "*** Starting workers ***" << endl;
- for (int i=0; i < 5; i++)
- {
- new worker;
- new workera;
- };
- cout << "*** sending control message 0 ***" << endl;
- workera::control(0);
- cout << "*** queuing 0-9 (0-90) ***" << endl;
- for (int i = 0; i < 10; i++)
- {
- worker::store(i);
- workera::store(i*10);
- };
- cout << "*** sending control message 2 ***" << endl;
- workera::control(2);
- cout << "*** sending 10 \"-1\" to workers ***" << endl;
- for (int i = 0; i < 10; i++)
- {
- worker::store(-1);
- };
- cout << "*** two second sleep here ***" << endl;
- sleep(2);
- cout << "*** Shutting down CWQ now. ***" << endl;
- workera::control(1);
- cout << "*** Starting 10 second sleep ***" << endl;
- sleep(10);
- cout << "worker has " << worker::parked() << " parked threads and "
- << worker::work_pending() << " elements in the queue." << endl;
- cout << "workera has " << workera::parked() << " parked threads and "
- << workera::work_pending() << " elements in the queue." << endl;
-};
Follow ups