00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "clihandler.h"
00013
00014 #include <boost/interprocess/managed_shared_memory.hpp>
00015 #include <boost/program_options.hpp>
00016
00017 #include <log4cpp/Category.hh>
00018 #include <log4cpp/OstreamAppender.hh>
00019 #include <log4cpp/PatternLayout.hh>
00020
00021 #include <vector>
00022 #include <string>
00023 #include <functional>
00024
00025 #include "sharedmemtypes.h"
00026 #include "byzantinegeneralthread.h"
00027 #include "mailbox.h"
00028
00029 const char* CLIHandler::kSharedMemoryName_ = "ByzGeneralsSharedMemory";
00030 const char* CLIHandler::kMailboxVectorName_ = "ByzGeneralsMailboxVector";
00031 const char* CLIHandler::kTraitorSetName_ = "ByzGeneralsTraitorSet";
00032 const char* CLIHandler::kLogCategoryName_ = "ByzGeneralsLogCategory";
00033
00034 CLIHandler::CLIHandler()
00035 : log_(InitializeLogger()),
00036 number_rounds_(0),
00037 number_traitor_generals_(0),
00038 number_loyal_generals_(0),
00039 generate_dot_graph_(false),
00040 total_number_generals_(0),
00041 total_messages_per_lt_general_(0),
00042 byzantine_generals_(),
00043 segment_(NULL),
00044 traitor_set_(NULL),
00045 mailbox_vector_(NULL) {
00046 }
00047
00048 CLIHandler::~CLIHandler() {
00049 }
00050
00051 ByzGeneralsReturnCodes::type CLIHandler::Exec(int argc, char* argv[]) {
00052
00053 const ByzGeneralsReturnCodes::type result =
00054 ParseAndValidateArguments(argc, argv);
00055
00056 switch (result) {
00057 case ByzGeneralsReturnCodes::kNoError:
00058
00059 break;
00060
00061 case ByzGeneralsReturnCodes::kHelpExitNoError:
00062
00063 return ByzGeneralsReturnCodes::kNoError;
00064 break;
00065
00066 case ByzGeneralsReturnCodes::kInvalidInput:
00067 throw std::invalid_argument("Invalid Input");
00068 break;
00069
00070 case ByzGeneralsReturnCodes::kIPCError:
00071 throw std::invalid_argument("IPC Error");
00072 break;
00073
00074 case ByzGeneralsReturnCodes::kUnknown:
00075 default:
00076
00077 throw std::exception();
00078 break;
00079 }
00080
00081
00082
00083
00084 CalculateOrdersAndRounds();
00085
00086
00087 CreateSharedMemoryItems();
00088
00089
00090 CreateGeneralThreads();
00091
00092
00093 assert(byzantine_generals_.size() == total_number_generals_);
00094 for (unsigned int i = 0; i < total_number_generals_; ++i) {
00095 byzantine_generals_.at(i)->Start();
00096 }
00097
00098
00099 for (unsigned int i = 0; i < total_number_generals_; ++i) {
00100 byzantine_generals_.at(i)->Stop();
00101 }
00102
00103
00104 DestroyGeneralThreads();
00105
00106
00107 DestroySharedMemoryItems();
00108
00109 return result;
00110 }
00111
00112 log4cpp::Category& CLIHandler::InitializeLogger() {
00113
00114 log4cpp::OstreamAppender* appender =
00115 new log4cpp::OstreamAppender("out", &std::cout);
00116 assert(appender != NULL);
00117
00118 log4cpp::PatternLayout* layout = new log4cpp::PatternLayout();
00119 assert(layout != NULL);
00120
00121 log4cpp::Category& log = log4cpp::Category::getInstance(kLogCategoryName_);
00122
00123
00124 layout->setConversionPattern("%d{%H:%M:%S.%l} %p : %m%n");
00125 appender->setLayout(layout);
00126 log.setAppender(appender);
00127
00128
00129
00130 log.setPriority(log4cpp::Priority::NOTICE);
00131
00132 return log;
00133 }
00134
00135 ByzGeneralsReturnCodes::type
00136 CLIHandler::ParseAndValidateArguments(int argc, char* argv[]) {
00137
00138
00139
00140
00141 namespace po = boost::program_options;
00142
00143 po::options_description description("Allowed options");
00144 description.add_options()
00145 ("help", "Produce help message")
00146 ("graph", po::value<bool>()->default_value(false),
00147 "Generate a DOT graph for the first general.")
00148 ("loyalists", po::value<int>()->default_value(3),
00149 "Number of loyal generals.")
00150 ("traitors", po::value<int>()->default_value(1),
00151 "Number of traitor generals.")
00152 ("verbose", po::value<bool>()->default_value(false),
00153 "Verbose debugging information.");
00154
00155 po::variables_map variables_map;
00156 po::store(po::parse_command_line(argc, argv, description), variables_map);
00157 po::notify(variables_map);
00158
00159 if (variables_map.count("help")) {
00160
00161 log_.noticeStream() << description;
00162 return ByzGeneralsReturnCodes::kHelpExitNoError;
00163 }
00164
00165 if (variables_map.count("traitors")) {
00166
00167 number_traitor_generals_ = variables_map["traitors"].as<int>();
00168 log_.noticeStream() << "Number of traitor generals set to "
00169 << number_traitor_generals_ << ".";
00170 }
00171
00172 if (variables_map.count("loyalists")) {
00173
00174 number_loyal_generals_ = variables_map["loyalists"].as<int>();
00175 log_.noticeStream() << "Number of loyal generals set to "
00176 << number_loyal_generals_ << ".";
00177 }
00178
00179 if (variables_map.count("graph")) {
00180
00181 generate_dot_graph_ = variables_map["graph"].as<bool>();
00182 log_.noticeStream() << "Graph output is "
00183 << (generate_dot_graph_ ?
00184 std::string("enabled.") :
00185 std::string("disabled."));
00186 }
00187
00188 if (variables_map.count("verbose")) {
00189
00190 bool verbose = variables_map["verbose"].as<bool>();
00191 log_.noticeStream() << "Verbose debugging is "
00192 << (verbose ?
00193 std::string("enabled.") :
00194 std::string("disabled."));
00195
00196 if (verbose == true) {
00197 log_.setPriority(log4cpp::Priority::INFO);
00198 }
00199
00200 }
00201
00202 if ((number_loyal_generals_ + number_traitor_generals_) <
00203 ((3 * number_traitor_generals_) + 1)) {
00204
00205
00206
00207
00208
00209
00210 log_.error("We need at least (3n + 1) total generals, where n"
00211 "is the number of traitor generals.");
00212
00213 return ByzGeneralsReturnCodes::kInvalidInput;
00214 }
00215
00216 total_number_generals_ = number_loyal_generals_ + number_traitor_generals_;
00217
00218 if (total_number_generals_ <= 0) {
00219 log_.error("We need at least 1 loyal general to begin.");
00220
00221 return ByzGeneralsReturnCodes::kInvalidInput;
00222 }
00223
00224 return ByzGeneralsReturnCodes::kNoError;
00225 }
00226
00227 void CLIHandler::CalculateOrdersAndRounds() {
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244 assert(total_number_generals_ > 0);
00245
00246 number_rounds_ = number_traitor_generals_ + 1;
00247
00248
00249 unsigned int curr_round_number_messages = 1;
00250 total_messages_per_lt_general_ = curr_round_number_messages;
00251
00252 for (unsigned int round = 1; round < number_rounds_; ++round) {
00253
00254
00255 curr_round_number_messages =
00256 (curr_round_number_messages * (total_number_generals_ - round));
00257
00258
00259
00260 total_messages_per_lt_general_ += curr_round_number_messages;
00261 }
00262
00263 log_.infoStream()
00264 << "Each general should receive "
00265 << total_messages_per_lt_general_ << " messages in all.";
00266 }
00267
00268 void CLIHandler::CreateSharedMemoryItems() {
00269 namespace bi = boost::interprocess;
00270
00271
00272
00273
00274 bi::shared_memory_object::remove(kSharedMemoryName_);
00275
00276
00277 assert(segment_ == NULL);
00278 segment_ = new bi::managed_shared_memory(bi::create_only,
00279 kSharedMemoryName_,
00280 kSharedMemoryMaxSize_);
00281 assert(segment_ != NULL);
00282
00283 SharedMemoryMailboxAllocator
00284 mailbox_allocator(segment_->get_segment_manager());
00285
00286
00287
00288
00289 assert(mailbox_vector_ == NULL);
00290 mailbox_vector_ = segment_->construct<SharedMemoryMailboxVector>
00291 (kMailboxVectorName_)(mailbox_allocator);
00292 assert(mailbox_vector_ != NULL);
00293
00294 OrderAllocator order_allocator(segment_->get_segment_manager());
00295
00296 for (unsigned int i = 0; i < total_number_generals_; ++i) {
00297 Mailbox* inbox = segment_->construct<Mailbox>
00298 (bi::anonymous_instance)(order_allocator);
00299 assert(inbox != NULL);
00300 mailbox_vector_->push_back(inbox);
00301 }
00302
00303
00304
00305 SharedMemoryUIntAllocator int_allocator(segment_->get_segment_manager());
00306
00307 assert(traitor_set_ == NULL);
00308 traitor_set_ = segment_->construct<SharedMemoryUIntSet>
00309 (kTraitorSetName_)(std::less<unsigned int>(), int_allocator);
00310 assert(traitor_set_ != NULL);
00311 }
00312
00313 void CLIHandler::DestroySharedMemoryItems() {
00314 namespace bi = boost::interprocess;
00315
00316
00317 assert(segment_ != NULL);
00318 assert(mailbox_vector_ != NULL);
00319
00320
00321 while (mailbox_vector_->size() > 0) {
00322 segment_->destroy_ptr(mailbox_vector_->back());
00323 mailbox_vector_->pop_back();
00324 }
00325
00326 assert(segment_->destroy<SharedMemoryMailboxVector>(kMailboxVectorName_) ==
00327 true);
00328 mailbox_vector_ = NULL;
00329
00330 delete segment_;
00331 segment_ = NULL;
00332
00333
00334 bi::shared_memory_object::remove(kSharedMemoryName_);
00335 }
00336
00337 void CLIHandler::CreateGeneralThreads() {
00338
00339 unsigned int seed = time(0);
00340
00341
00342 int traitors_needed = number_traitor_generals_;
00343
00344
00345 assert(traitor_set_ != NULL);
00346
00347
00348
00349 assert(byzantine_generals_.size() == 0);
00350
00351 for (unsigned int i = 0; i < total_number_generals_; ++i) {
00352 bool is_loyal = true;
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362 if (traitors_needed > 0) {
00363 double number_generals_remaining = total_number_generals_ - i;
00364 double random_double =
00365 static_cast<double>(rand_r(&seed)) / RAND_MAX;
00366 double probability_of_selection =
00367 (static_cast<double>(traitors_needed)) /
00368 number_generals_remaining;
00369
00370 if (random_double < probability_of_selection) {
00371
00372 --traitors_needed;
00373 is_loyal = false;
00374 log_.noticeStream()
00375 << "General " << i << " randomly chosen to be disloyal.";
00376 traitor_set_->insert(i);
00377 }
00378 }
00379
00380
00381 ByzantineGeneralThread* nextGeneral =
00382 new ByzantineGeneralThread(i,
00383 kSharedMemoryName_,
00384 kMailboxVectorName_,
00385 is_loyal,
00386 total_number_generals_,
00387 total_messages_per_lt_general_,
00388 number_rounds_,
00389 kLogCategoryName_,
00390 generate_dot_graph_,
00391 kTraitorSetName_);
00392
00393
00394 assert(nextGeneral != NULL);
00395
00396
00397 byzantine_generals_.push_back(nextGeneral);
00398 }
00399
00400
00401 assert(traitors_needed == 0);
00402 }
00403
00404 void CLIHandler::DestroyGeneralThreads() {
00405 while (byzantine_generals_.size() > 0) {
00406 delete (byzantine_generals_.back());
00407 byzantine_generals_.pop_back();
00408 }
00409 }
00410