LCOV - code coverage report
Current view: top level - src - Trigger.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 6 214 2.8 %
Date: 2010-12-13 Functions: 3 27 11.1 %
Branches: 3 165 1.8 %

           Branch data     Line data    Source code
       1                 :            : // $Id: Trigger.cc 2359 2005-12-21 23:55:32Z vern $
       2                 :            : 
       3                 :            : #include <algorithm>
       4                 :            : 
       5                 :            : #include "Trigger.h"
       6                 :            : #include "Traverse.h"
       7                 :            : 
       8                 :            : // Callback class to traverse an expression, registering all relevant IDs and
       9                 :            : // Vals for change notifications.
      10                 :            : 
      11                 :            : class TriggerTraversalCallback : public TraversalCallback {
      12                 :            : public:
      13                 :          0 :         TriggerTraversalCallback(Trigger *arg_trigger)
      14         [ #  # ]:          0 :                 { Ref(arg_trigger); trigger = arg_trigger; }
      15                 :            : 
      16                 :          0 :         ~TriggerTraversalCallback()
      17 [ #  # ][ #  # ]:          0 :                 { Unref(trigger); }
         [ #  # ][ #  # ]
      18                 :            : 
      19                 :            :         virtual TraversalCode PreExpr(const Expr*);
      20                 :            : 
      21                 :            : private:
      22                 :            :         Trigger* trigger;
      23                 :            : };
      24                 :            : 
      25                 :          0 : TraversalCode TriggerTraversalCallback::PreExpr(const Expr* expr)
      26                 :            :         {
      27                 :            :         // We catch all expressions here which in some way reference global
      28                 :            :         // state.
      29                 :            : 
      30      [ #  #  # ]:          0 :         switch ( expr->Tag() ) {
      31                 :            :         case EXPR_NAME:
      32                 :            :                 {
      33                 :          0 :                 const NameExpr* e = static_cast<const NameExpr*>(expr);
      34         [ #  # ]:          0 :                 if ( e->Id()->IsGlobal() )
      35                 :          0 :                         trigger->Register(e->Id());
      36                 :            : 
      37                 :          0 :                 Val* v = e->Id()->ID_Val();
      38   [ #  #  #  # ]:          0 :                 if ( v && v->IsMutableVal() )
                 [ #  # ]
      39                 :          0 :                         trigger->Register(v);
      40                 :          0 :                 break;
      41                 :            :                 };
      42                 :            : 
      43                 :            :         case EXPR_INDEX:
      44                 :            :                 {
      45                 :          0 :                 const IndexExpr* e = static_cast<const IndexExpr*>(expr);
      46                 :          0 :                 BroObj::SuppressRunTimeErrors no_errors;
      47                 :          0 :                 Val* v = e->Eval(trigger->frame);
      48         [ #  # ]:          0 :                 if ( v )
      49                 :          0 :                         trigger->Register(v);
      50                 :          0 :                 break;
      51                 :            :                 }
      52                 :            : 
      53                 :            :         default:
      54                 :            :                 // All others are uninteresting.
      55                 :            :                 break;
      56                 :            :         }
      57                 :            : 
      58                 :          0 :         return TC_CONTINUE;
      59                 :            :         }
      60                 :            : 
      61                 :            : class TriggerTimer : public Timer {
      62                 :            : public:
      63                 :          0 :         TriggerTimer(double arg_timeout, Trigger* arg_trigger)
      64                 :          0 :         : Timer(network_time + arg_timeout, TIMER_TRIGGER)
      65                 :            :                 {
      66         [ #  # ]:          0 :                 Ref(arg_trigger);
      67                 :          0 :                 trigger = arg_trigger;
      68                 :          0 :                 timeout = arg_timeout;
      69                 :          0 :                 time = network_time;
      70                 :          0 :                 }
      71                 :            : 
      72                 :          0 :         ~TriggerTimer()
      73 [ #  # ][ #  # ]:          0 :                 { Unref(trigger); }
         [ #  # ][ #  # ]
      74                 :            : 
      75                 :          0 :         void Dispatch(double t, int is_expire)
      76                 :            :                 {
      77                 :            :                 // The network_time may still have been zero when the
      78                 :            :                 // timer was instantiated.  In this case, it fires
      79                 :            :                 // immediately and we simply restart it.
      80         [ #  # ]:          0 :                 if ( time )
      81                 :          0 :                         trigger->Timeout();
      82                 :            :                 else
      83                 :            :                         {
      84                 :          0 :                         TriggerTimer* timer = new TriggerTimer(timeout, trigger);
      85                 :          0 :                         timer_mgr->Add(timer);
      86                 :          0 :                         trigger->timer = timer;
      87                 :            :                         }
      88                 :          0 :                 }
      89                 :            : 
      90                 :            : protected:
      91                 :            :         Trigger* trigger;
      92                 :            :         double timeout;
      93                 :            :         double time;
      94                 :            : };
      95                 :            : 
      96                 :            : Trigger::Trigger(Expr* arg_cond, Stmt* arg_body, Stmt* arg_timeout_stmts,
      97                 :            :                         Expr* arg_timeout, Frame* arg_frame,
      98                 :          0 :                         bool arg_is_return, const Location* arg_location)
      99                 :            :         {
     100   [ #  #  #  # ]:          0 :         if ( ! pending )
     101                 :          0 :                 pending = new list<Trigger*>;
     102                 :            : 
     103                 :          0 :         cond = arg_cond;
     104                 :          0 :         body = arg_body;
     105                 :          0 :         timeout_stmts = arg_timeout_stmts;
     106                 :          0 :         timeout = arg_timeout;
     107                 :          0 :         frame = arg_frame->Clone();
     108                 :          0 :         timer = 0;
     109                 :          0 :         delayed = false;
     110                 :          0 :         disabled = false;
     111                 :          0 :         attached = 0;
     112                 :          0 :         is_return = arg_is_return;
     113                 :          0 :         location = arg_location;
     114                 :            : 
     115                 :          0 :         DBG_LOG(DBG_NOTIFIERS, "%s: instantiating", Name());
     116                 :            : 
     117   [ #  #  #  # ]:          0 :         if ( is_return )
     118                 :            :                 {
     119                 :          0 :                 Trigger* parent = frame->GetTrigger();
     120   [ #  #  #  # ]:          0 :                 if ( ! parent )
     121                 :            :                         {
     122                 :          0 :                         run_time("return trigger in context which does not allow delaying result");
     123                 :          0 :                         Unref(this);
     124                 :          0 :                         return;
     125                 :            :                         }
     126                 :            : 
     127                 :          0 :                 parent->Attach(this);
     128                 :          0 :                 arg_frame->SetDelayed();
     129                 :            :                 }
     130                 :            : 
     131 [ #  # ][ #  # ]:          0 :         Val* timeout = arg_timeout ? arg_timeout->ExprVal() : 0;
     132                 :            : 
     133 [ #  # ][ #  # ]:          0 :         if ( ! Eval() && timeout )
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     134                 :            :                 {
     135                 :          0 :                 timer = new TriggerTimer(timeout->AsInterval(), this);
     136                 :          0 :                 timer_mgr->Add(timer);
     137                 :            :                 }
     138                 :          0 :         }
     139                 :            : 
     140                 :          0 : Trigger::~Trigger()
     141                 :            :         {
     142                 :          0 :         DBG_LOG(DBG_NOTIFIERS, "%s: deleting", Name());
     143                 :            : 
     144 [ #  # ][ #  # ]:          0 :         for ( ValCache::iterator i = cache.begin(); i != cache.end(); ++i )
                 [ #  # ]
     145                 :          0 :                 Unref(i->second);
     146                 :            : 
     147                 :          0 :         Unref(frame);
     148                 :          0 :         UnregisterAll();
     149                 :            : 
     150 [ #  # ][ #  # ]:          0 :         Unref(attached);
                 [ #  # ]
     151                 :            :         // Due to ref'counting, "this" cannot be part of pending at this
     152                 :            :         // point.
     153 [ #  # ][ #  # ]:          0 :         }
                 [ #  # ]
     154                 :            : 
     155                 :          0 : void Trigger::Init()
     156                 :            :         {
     157         [ #  # ]:          0 :         assert(! disabled);
     158                 :          0 :         UnregisterAll();
     159                 :          0 :         TriggerTraversalCallback cb(this);
     160                 :          0 :         cond->Traverse(&cb);
     161                 :          0 :         }
     162                 :            : 
     163                 :            : Trigger::TriggerList* Trigger::pending = 0;
     164                 :            : 
     165                 :          0 : bool Trigger::Eval()
     166                 :            :         {
     167         [ #  # ]:          0 :         if ( disabled )
     168                 :          0 :                 return true;
     169                 :            : 
     170                 :          0 :         DBG_LOG(DBG_NOTIFIERS, "%s: evaluating", Name());
     171                 :            : 
     172         [ #  # ]:          0 :         if ( delayed )
     173                 :            :                 {
     174                 :          0 :                 DBG_LOG(DBG_NOTIFIERS, "%s: skipping eval due to delayed call",
     175                 :            :                                 Name());
     176                 :          0 :                 return false;
     177                 :            :                 }
     178                 :            : 
     179                 :            :         // It's unfortunate that we have to copy the frame again here but
     180                 :            :         // otherwise changes to any of the locals would propagate to later
     181                 :            :         // evaluations.
     182                 :            :         //
     183                 :            :         // An alternative approach to copying the frame would be to deep-copy
     184                 :            :         // the expression itself, replacing all references to locals with
     185                 :            :         // constants.
     186                 :          0 :         Frame* f = frame->Clone();
     187                 :          0 :         f->SetTrigger(this);
     188                 :          0 :         Val* v = cond->Eval(f);
     189                 :          0 :         f->ClearTrigger();
     190                 :            : 
     191         [ #  # ]:          0 :         if ( f->HasDelayed() )
     192                 :            :                 {
     193                 :          0 :                 DBG_LOG(DBG_NOTIFIERS, "%s: eval has delayed", Name());
     194         [ #  # ]:          0 :                 assert(!v);
     195                 :          0 :                 Unref(f);
     196                 :          0 :                 return false;
     197                 :            :                 }
     198                 :            : 
     199         [ #  # ]:          0 :         if ( v->IsZero() )
     200                 :            :                 {
     201                 :            :                 // Not true. Perhaps next time...
     202                 :          0 :                 DBG_LOG(DBG_NOTIFIERS, "%s: trigger condition is false", Name());
     203                 :          0 :                 Unref(v);
     204                 :          0 :                 Unref(f);
     205                 :          0 :                 Init();
     206                 :          0 :                 return false;
     207                 :            :                 }
     208                 :            : 
     209                 :          0 :         DBG_LOG(DBG_NOTIFIERS, "%s: trigger condition is true, executing",
     210                 :            :                         Name());
     211                 :            : 
     212                 :          0 :         Unref(v);
     213                 :            :         stmt_flow_type flow;
     214                 :          0 :         v = body->Exec(f, flow);
     215                 :            : 
     216         [ #  # ]:          0 :         if ( is_return )
     217                 :            :                 {
     218                 :          0 :                 Trigger* trigger = frame->GetTrigger();
     219         [ #  # ]:          0 :                 assert(trigger);
     220         [ #  # ]:          0 :                 assert(frame->GetCall());
     221         [ #  # ]:          0 :                 assert(trigger->attached == this);
     222                 :            : 
     223                 :            : #ifdef DEBUG
     224                 :          0 :                 const char* pname = copy_string(trigger->Name());
     225                 :          0 :                 DBG_LOG(DBG_NOTIFIERS, "%s: trigger has parent %s, caching result", Name(), pname);
     226         [ #  # ]:          0 :                 delete [] pname;
     227                 :            : #endif
     228                 :            : 
     229                 :          0 :                 trigger->Cache(frame->GetCall(), v);
     230                 :          0 :                 trigger->Release();
     231                 :            :                 }
     232                 :            : 
     233                 :          0 :         Unref(v);
     234                 :          0 :         Unref(f);
     235                 :            : 
     236         [ #  # ]:          0 :         if ( timer )
     237                 :          0 :                 timer_mgr->Cancel(timer);
     238                 :            : 
     239                 :          0 :         Disable();
     240                 :          0 :         Unref(this);
     241                 :            : 
     242                 :          0 :         return true;
     243                 :            :         }
     244                 :            : 
     245                 :          0 : void Trigger::QueueTrigger(Trigger* trigger)
     246                 :            :         {
     247         [ #  # ]:          0 :         assert(! trigger->disabled);
     248         [ #  # ]:          0 :         assert(pending);
     249         [ #  # ]:          0 :         if ( std::find(pending->begin(), pending->end(), trigger) == pending->end() )
     250                 :            :                 {
     251         [ #  # ]:          0 :                 Ref(trigger);
     252                 :          0 :                 pending->push_back(trigger);
     253                 :            :                 }
     254                 :          0 :         }
     255                 :            : 
     256                 :      42701 : void Trigger::EvaluatePending()
     257                 :            :         {
     258                 :      42701 :         DBG_LOG(DBG_NOTIFIERS, "evaluating all pending triggers");
     259                 :            : 
     260         [ -  + ]:      42701 :         if ( ! pending )
     261                 :      42701 :                 return;
     262                 :            : 
     263                 :            :         // While we iterate over the list, executing statements, we may
     264                 :            :         // in fact trigger new triggers and thereby modify the list.
     265                 :            :         // Therefore, we create a new temporary list which will receive
     266                 :            :         // triggers triggered during this time.
     267                 :          0 :         TriggerList* orig = pending;
     268                 :          0 :         TriggerList tmp;
     269                 :          0 :         pending = &tmp;
     270                 :            : 
     271         [ #  # ]:          0 :         for ( TriggerList::iterator i = orig->begin(); i != orig->end(); ++i )
     272                 :            :                 {
     273                 :          0 :                 Trigger* t = *i;
     274                 :          0 :                 (*i)->Eval();
     275         [ #  # ]:          0 :                 Unref(t);
     276                 :            :                 }
     277                 :            : 
     278                 :          0 :         pending = orig;
     279                 :          0 :         orig->clear();
     280                 :            : 
     281                 :            :         // Sigh... Is this really better than a for-loop?
     282                 :            :         std::copy(tmp.begin(), tmp.end(),
     283                 :          0 :                 insert_iterator<TriggerList>(*pending, pending->begin()));
     284                 :            :         }
     285                 :            : 
     286                 :          0 : void Trigger::Timeout()
     287                 :            :         {
     288         [ #  # ]:          0 :         if ( disabled )
     289                 :          0 :                 return;
     290                 :            : 
     291                 :          0 :         DBG_LOG(DBG_NOTIFIERS, "%s: timeout", Name());
     292         [ #  # ]:          0 :         if ( timeout_stmts )
     293                 :            :                 {
     294                 :            :                 stmt_flow_type flow;
     295                 :          0 :                 Frame* f = frame->Clone();
     296                 :          0 :                 Val* v = timeout_stmts->Exec(f, flow);
     297                 :            : 
     298         [ #  # ]:          0 :                 if ( is_return )
     299                 :            :                         {
     300                 :          0 :                         Trigger* trigger = frame->GetTrigger();
     301         [ #  # ]:          0 :                         assert(trigger);
     302         [ #  # ]:          0 :                         assert(frame->GetCall());
     303         [ #  # ]:          0 :                         assert(trigger->attached == this);
     304                 :            : 
     305                 :            : #ifdef DEBUG
     306                 :          0 :                         const char* pname = copy_string(trigger->Name());
     307                 :          0 :                         DBG_LOG(DBG_NOTIFIERS, "%s: trigger has parent %s, caching timeout result", Name(), pname);
     308         [ #  # ]:          0 :                         delete [] pname;
     309                 :            : #endif
     310                 :          0 :                         trigger->Cache(frame->GetCall(), v);
     311                 :          0 :                         trigger->Release();
     312                 :            :                         }
     313                 :            : 
     314                 :          0 :                 Unref(v);
     315                 :          0 :                 Unref(f);
     316                 :            :                 }
     317                 :            : 
     318                 :          0 :         Disable();
     319                 :          0 :         Unref(this);
     320                 :            :         }
     321                 :            : 
     322                 :          0 : void Trigger::Register(ID* id)
     323                 :            :         {
     324         [ #  # ]:          0 :         assert(! disabled);
     325                 :          0 :         notifiers.Register(id, this);
     326                 :            : 
     327                 :          0 :         Ref(id);
     328                 :          0 :         ids.insert(id);
     329                 :          0 :         }
     330                 :            : 
     331                 :          0 : void Trigger::Register(Val* val)
     332                 :            :         {
     333         [ #  # ]:          0 :         assert(! disabled);
     334                 :          0 :         notifiers.Register(val, this);
     335                 :            : 
     336                 :          0 :         Ref(val);
     337                 :          0 :         vals.insert(val);
     338                 :          0 :         }
     339                 :            : 
     340                 :          0 : void Trigger::UnregisterAll()
     341                 :            :         {
     342         [ #  # ]:          0 :         loop_over_list(ids, i)
     343                 :            :                 {
     344                 :          0 :                 notifiers.Unregister(ids[i], this);
     345                 :          0 :                 Unref(ids[i]);
     346                 :            :                 }
     347                 :            : 
     348                 :          0 :         ids.clear();
     349                 :            : 
     350         [ #  # ]:          0 :         loop_over_list(vals, j)
     351                 :            :                 {
     352                 :          0 :                 notifiers.Unregister(vals[j], this);
     353                 :          0 :                 Unref(vals[j]);
     354                 :            :                 }
     355                 :            : 
     356                 :          0 :         vals.clear();
     357                 :          0 :         }
     358                 :            : 
     359                 :          0 : void Trigger::Attach(Trigger *trigger)
     360                 :            :         {
     361         [ #  # ]:          0 :         assert(! disabled);
     362         [ #  # ]:          0 :         assert(! trigger->disabled);
     363         [ #  # ]:          0 :         assert(! trigger->delayed);
     364                 :            : 
     365                 :            : #ifdef DEBUG
     366                 :          0 :         const char* pname = copy_string(trigger->Name());
     367                 :          0 :         DBG_LOG(DBG_NOTIFIERS, "%s: attaching to %s", Name(), pname);
     368         [ #  # ]:          0 :         delete [] pname;
     369                 :            : #endif
     370                 :            : 
     371         [ #  # ]:          0 :         Ref(trigger);
     372                 :          0 :         attached = trigger;
     373                 :          0 :         Hold();
     374                 :          0 :         }
     375                 :            : 
     376                 :          0 : void Trigger::Cache(const CallExpr* expr, Val* v)
     377                 :            :         {
     378         [ #  # ]:          0 :         if ( disabled )
     379                 :          0 :                 return;
     380                 :            : 
     381                 :          0 :         ValCache::iterator i = cache.find(expr);
     382                 :            : 
     383         [ #  # ]:          0 :         if ( i != cache.end() )
     384                 :            :                 {
     385                 :          0 :                 Unref(i->second);
     386                 :          0 :                 i->second = v;
     387                 :            :                 }
     388                 :            : 
     389                 :            :         else
     390                 :          0 :                 cache.insert(ValCache::value_type(expr, v));
     391                 :            : 
     392                 :          0 :         Ref(v);
     393                 :            : 
     394                 :          0 :         QueueTrigger(this);
     395                 :            :         }
     396                 :            : 
     397                 :            : 
     398                 :          0 : Val* Trigger::Lookup(const CallExpr* expr)
     399                 :            :         {
     400         [ #  # ]:          0 :         assert(! disabled);
     401                 :            : 
     402                 :          0 :         ValCache::iterator i = cache.find(expr);
     403         [ #  # ]:          0 :         return (i != cache.end()) ? i->second : 0;
     404                 :            :         }
     405                 :            : 
     406                 :          0 : const char* Trigger::Name()
     407                 :            :         {
     408         [ #  # ]:          0 :         assert(location);
     409                 :            :         return fmt("%s:%d-%d", location->filename,
     410                 :          0 :                         location->first_line, location->last_line);
     411 [ +  - ][ +  - ]:          6 :         }

Generated by: LCOV version 1.8