diff -r 000000000000 -r 2b3e5ec03512 sim/Simulator.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sim/Simulator.cc Thu Apr 21 14:57:45 2011 +0100 @@ -0,0 +1,320 @@ +/* + * Copyright 2004-2006 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "Simulator.h" +#include "Node.h" +#include "Topology.h" +#include "SimLog.h" +#include "bundling/BundleTimestamp.h" + +using namespace dtn; + +namespace dtnsim { + +template<> +Simulator* oasys::Singleton::instance_ = NULL; + +//---------------------------------------------------------------------- + +double Simulator::time_ = 0; +bool Simulator::interrupted_ = false; +double Simulator::runtill_ = -1; + +//---------------------------------------------------------------------- +Simulator::Simulator() + : Logger("Simulator", "/dtnsim"), + eventq_(), + exit_event_(NULL) +{ +} + +//---------------------------------------------------------------------- +void +Simulator::post(SimEvent* e) +{ + instance_->eventq_.push(e); +} + +//---------------------------------------------------------------------- +void +Simulator::exit() +{ + ::exit(0); +} + +//---------------------------------------------------------------------- +int +Simulator::run_node_events() +{ + bool done; + int next_timer; + do { + done = true; + next_timer = -1; + + Topology::NodeTable::iterator iter; + for (iter = Topology::node_table()->begin(); + iter != Topology::node_table()->end(); + ++iter) + { + check_interrupt(); + + Node* node = iter->second; + node->set_active(); + + int next = oasys::TimerSystem::instance()->run_expired_timers(); + if (next != -1) { + if (next_timer == -1) { + next_timer = next; + } else { + next_timer = std::min(next_timer, next); + } + } + + log_debug("processing all bundle events for node %s", node->name()); + if (node->process_one_bundle_event()) { + done = false; + while (node->process_one_bundle_event()) { + check_interrupt(); + } + } + } + } while (!done); + + return next_timer; +} + +//---------------------------------------------------------------------- +void +Simulator::log_inqueue_stats() +{ + Topology::NodeTable::iterator node_iter; + for (node_iter = Topology::node_table()->begin(); + node_iter != Topology::node_table()->end(); + ++node_iter) + { + Node* node = node_iter->second; + + oasys::ScopeLock l(node->pending_bundles()->lock(), "log_inqueue_stats"); + BundleList::iterator bundle_iter; + for (bundle_iter = node->pending_bundles()->begin(); + bundle_iter != node->pending_bundles()->end(); + ++bundle_iter) + { + Bundle* bundle = *bundle_iter; + SimLog::instance()->log_inqueue(node, bundle); + } + } +} + +//---------------------------------------------------------------------- +void +Simulator::run() +{ + oasys::Log* log = oasys::Log::instance(); + log->set_prefix("--"); + + log_debug("Configuring all nodes"); + Topology::NodeTable::iterator iter; + for (iter = Topology::node_table()->begin(); + iter != Topology::node_table()->end(); + ++iter) + { + iter->second->configure(); + } + + log_debug("Setting up interrupt handler"); + signal(SIGINT, handle_interrupt); + + log_debug("Starting Simulator event loop..."); + + while (1) { + check_interrupt(); + + int next_timer_ms = run_node_events(); + double next_timer = (next_timer_ms == -1) ? INT_MAX : + time_ + (((double)next_timer_ms) / 1000); + double next_event = INT_MAX; + log->set_prefix("--"); + + SimEvent* e = NULL; + if (! eventq_.empty()) { + e = eventq_.top(); + next_event = e->time(); + } + + if ((next_timer_ms == -1) && (e == NULL)) { + break; + } + else if (next_timer < next_event) { + time_ = next_timer; + log_debug("advancing time by %u ms to %f for next timer", + next_timer_ms, time_); + } + else { + ASSERT(e != NULL); + eventq_.pop(); + time_ = e->time(); + + if (e->is_valid()) { + ASSERT(e->handler() != NULL); + /* Process the event */ + log_debug("Event:%p type %s at time %f", + e, e->type_str(), time_); + e->handler()->process(e); + } + } + + if ((Simulator::runtill_ != -1) && + (time_ > Simulator::runtill_)) { + log_info("Exiting simulation. " + "Current time (%f) > Max time (%f)", + time_, Simulator::runtill_); + goto done; + } + } + + log_info("Simulator loop done -- no pending events or timers (time is %f)", + time_); + + if (exit_event_) { + run_at_event(exit_event_); + } + +done: + log_inqueue_stats(); + SimLog::instance()->flush(); +} + +//---------------------------------------------------------------------- +void +Simulator::pause() +{ + oasys::StaticStringBuffer<128> cmd; + cmd.appendf("puts \"Simulator paused at time %f...\"", time_); + oasys::TclCommandInterp::instance()->exec_command(cmd.c_str()); + + run_console(false); +} + +//---------------------------------------------------------------------- +void +Simulator::run_console(bool complete) +{ + Node* cur_active = Node::active_node(); + interrupted_ = true; + + if (complete) { + oasys::TclCommandInterp::instance()->command_loop("dtnsim% "); + } else { + // we can't re-enter tclreadline more than once so if it's not + // complete we need to use the simple command loop + oasys::TclCommandInterp::instance()->exec_command( + "simple_command_loop \"dtnsim% \""); + } + + interrupted_ = false; + cur_active->set_active(); +} + +//---------------------------------------------------------------------- +void +Simulator::handle_interrupt(int sig) +{ + (void)sig; + + if (interrupted_) { + instance()->exit(); + } else { + interrupted_ = true; + } +} + +//---------------------------------------------------------------------- +void +Simulator::check_interrupt() +{ + if (interrupted_) { + oasys::StaticStringBuffer<128> cmd; + cmd.appendf("puts \"Simulator interrupted at time %f...\"", time_); + oasys::TclCommandInterp::instance()->exec_command(cmd.c_str()); + run_console(false); + } +} + +//---------------------------------------------------------------------- +/** + * Override gettimeofday to return the simulator time. + */ +extern "C" int +gettimeofday(struct timeval *tv, struct timezone *tz) +{ + (void)tz; + double now = Simulator::time(); + DOUBLE_TO_TIMEVAL(now, *tv); + + // Sometimes converting a double like 100.2 into an timeval + // results in a value like of 100.199999 so we fudge it a bit here + // to make the output look prettier + if ((tv->tv_usec % 1000) == 999) { + tv->tv_usec += 1; + } + return 0; +} + +//---------------------------------------------------------------------- +void +Simulator::process(SimEvent *e) +{ + switch (e->type()) { + case SIM_AT_EVENT: { + run_at_event((SimAtEvent*)e); + break; + } + default: + NOTREACHED; + } +} + +//---------------------------------------------------------------------- +void +Simulator::set_exit_event(SimAtEvent* evt) +{ + ASSERTF(exit_event_ == NULL, "cannot set multiple exit events"); + exit_event_ = evt; +} + +//---------------------------------------------------------------------- +void +Simulator::run_at_event(SimAtEvent* evt) +{ + int err = oasys::TclCommandInterp::instance()-> + exec_command(evt->objc_, evt->objv_); + if (err != 0) { + oasys::StringBuffer cmd; + cmd.appendf("puts \"ERROR in at command, pausing simulation\""); + oasys::TclCommandInterp::instance()->exec_command(cmd.c_str()); + pause(); + } +} + +} // namespace dtnsim