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 : }
|