]>
Commit | Line | Data |
---|---|---|
98801e83 JR |
1 | Support libecap v1.0, allowing asynchronous adapters and eCAP version checks. |
2 | ||
3 | After these changes, Squid can support eCAP adapters built with libecap v1.0, | |
4 | but stops supporting adapters built with earlier libecap versions (due to API | |
5 | changes). The new libecap version allows Squid to better check the version of | |
6 | the eCAP adapter being loaded as well as the version of the eCAP library being | |
7 | used. This should help with migration to libecap v1.0. | |
8 | ||
9 | ||
10 | Expose [running] main event loop as a global so that modules can add engines. | |
11 | ||
12 | === modified file 'configure.ac' | |
13 | --- configure.ac 2013-07-09 11:15:51 +0000 | |
14 | +++ configure.ac 2013-09-12 23:08:18 +0000 | |
15 | @@ -1035,10 +1035,10 @@ | |
16 | ||
17 | if test -n "$PKG_CONFIG"; then | |
18 | dnl eCAP support requires libecap. | |
19 | - dnl This Squid supports libecap v0.2.x. | |
20 | + dnl This Squid supports libecap v1.0.x. | |
21 | dnl Use EXT_ prefix to distinguish external libecap (that we check for | |
22 | dnl here) from our own convenience ecap library in Makefiles. | |
23 | - PKG_CHECK_MODULES([EXT_LIBECAP],[libecap >= 0.2.0 libecap < 0.3]) | |
24 | + PKG_CHECK_MODULES([EXT_LIBECAP],[libecap >= 1.0 libecap < 1.1]) | |
25 | else | |
26 | AC_MSG_NOTICE([eCAP support requires pkg-config to verify the correct library version. Trouble may follow.]) | |
27 | fi | |
28 | ||
29 | === modified file 'src/EventLoop.cc' | |
30 | --- src/EventLoop.cc 2013-02-17 02:09:16 +0000 | |
31 | +++ src/EventLoop.cc 2013-09-16 00:07:51 +0000 | |
32 | @@ -37,6 +37,8 @@ | |
33 | #include "base/AsyncCallQueue.h" | |
34 | #include "SquidTime.h" | |
35 | ||
36 | +EventLoop *EventLoop::Running = NULL; | |
37 | + | |
38 | EventLoop::EventLoop() : errcount(0), last_loop(false), timeService(NULL), | |
39 | primaryEngine(NULL), | |
40 | loop_delay(EVENT_LOOP_TIMEOUT), | |
41 | @@ -96,7 +98,12 @@ | |
42 | { | |
43 | prepareToRun(); | |
44 | ||
45 | + assert(!Running); | |
46 | + Running = this; | |
47 | + | |
48 | while (!runOnce()); | |
49 | + | |
50 | + Running = NULL; | |
51 | } | |
52 | ||
53 | bool | |
54 | ||
55 | === modified file 'src/EventLoop.h' | |
56 | --- src/EventLoop.h 2013-05-04 11:50:26 +0000 | |
57 | +++ src/EventLoop.h 2013-09-16 00:07:51 +0000 | |
58 | @@ -89,6 +89,10 @@ | |
59 | ||
60 | int errcount; | |
61 | ||
62 | + /// the [main program] loop running now; may be nil | |
63 | + /// for simplicity, we assume there are no concurrent loops | |
64 | + static EventLoop *Running; | |
65 | + | |
66 | private: | |
67 | /** setup state variables prior to running */ | |
68 | void prepareToRun(); | |
69 | ||
70 | === modified file 'src/adaptation/ecap/Host.cc' | |
71 | --- src/adaptation/ecap/Host.cc 2012-08-28 13:00:30 +0000 | |
72 | +++ src/adaptation/ecap/Host.cc 2013-09-16 18:04:34 +0000 | |
73 | @@ -70,11 +70,55 @@ | |
74 | os << PACKAGE_NAME << " v" << PACKAGE_VERSION; | |
75 | } | |
76 | ||
77 | +/// Strips libecap version components not affecting compatibility decisions. | |
78 | +static std::string | |
79 | +EssentialVersion(const std::string &raw) | |
80 | +{ | |
81 | + // all libecap x.y.* releases are supposed to be compatible so we strip | |
82 | + // everything after the second period | |
83 | + const std::string::size_type minorPos = raw.find('.'); | |
84 | + const std::string::size_type microPos = minorPos == std::string::npos ? | |
85 | + std::string::npos : raw.find('.', minorPos+1); | |
86 | + return raw.substr(0, microPos); // becomes raw if microPos is npos | |
87 | +} | |
88 | + | |
89 | +/// If "their" libecap version is not compatible with what Squid has been built | |
90 | +/// with, then complain and return false. | |
91 | +static bool | |
92 | +SupportedVersion(const char *vTheir, const std::string &them) | |
93 | +{ | |
94 | + if (!vTheir || !*vTheir) { | |
95 | + debugs(93, DBG_CRITICAL, "ERROR: Cannot use " << them << | |
96 | + " with libecap prior to v1.0."); | |
97 | + return false; | |
98 | + } | |
99 | + | |
100 | + // we support what we are built with | |
101 | + const std::string vSupported(LIBECAP_VERSION); | |
102 | + debugs(93, 2, them << " with libecap v" << vTheir << "; us: v" << vSupported); | |
103 | + | |
104 | + if (EssentialVersion(vTheir) == EssentialVersion(vSupported)) | |
105 | + return true; // their version is supported | |
106 | + | |
107 | + debugs(93, DBG_CRITICAL, "ERROR: Cannot use " << them << | |
108 | + " with libecap v" << vTheir << | |
109 | + ": incompatible with supported libecap v" << vSupported); | |
110 | + return false; | |
111 | +} | |
112 | + | |
113 | void | |
114 | -Adaptation::Ecap::Host::noteService(const libecap::weak_ptr<libecap::adapter::Service> &weak) | |
115 | +Adaptation::Ecap::Host::noteVersionedService(const char *vGiven, const libecap::weak_ptr<libecap::adapter::Service> &weak) | |
116 | { | |
117 | - Must(!weak.expired()); | |
118 | - RegisterAdapterService(weak.lock()); | |
119 | + /* | |
120 | + * Check that libecap used to build the service is compatible with ours. | |
121 | + * This has to be done using vGiven string and not Service object itself | |
122 | + * because dereferencing a Service pointer coming from an unsupported | |
123 | + * version is unsafe. | |
124 | + */ | |
125 | + if (SupportedVersion(vGiven, "eCAP service built")) { | |
126 | + Must(!weak.expired()); | |
127 | + RegisterAdapterService(weak.lock()); | |
128 | + } | |
129 | } | |
130 | ||
131 | static int | |
132 | @@ -126,7 +170,8 @@ | |
133 | void | |
134 | Adaptation::Ecap::Host::Register() | |
135 | { | |
136 | - if (!TheHost) { | |
137 | + if (!TheHost && SupportedVersion(libecap::VersionString(), | |
138 | + "Squid executable dynamically linked")) { | |
139 | TheHost.reset(new Adaptation::Ecap::Host); | |
140 | libecap::RegisterHost(TheHost); | |
141 | } | |
142 | ||
143 | === modified file 'src/adaptation/ecap/Host.h' | |
144 | --- src/adaptation/ecap/Host.h 2012-10-04 11:10:17 +0000 | |
145 | +++ src/adaptation/ecap/Host.h 2013-09-16 18:04:34 +0000 | |
146 | @@ -19,7 +19,7 @@ | |
147 | /* libecap::host::Host API */ | |
148 | virtual std::string uri() const; // unique across all vendors | |
149 | virtual void describe(std::ostream &os) const; // free-format info | |
150 | - virtual void noteService(const libecap::weak_ptr<libecap::adapter::Service> &s); | |
151 | + virtual void noteVersionedService(const char *libEcapVersion, const libecap::weak_ptr<libecap::adapter::Service> &s); | |
152 | virtual std::ostream *openDebug(libecap::LogVerbosity lv); | |
153 | virtual void closeDebug(std::ostream *debug); | |
154 | typedef libecap::shared_ptr<libecap::Message> MessagePtr; | |
155 | ||
156 | === modified file 'src/adaptation/ecap/ServiceRep.cc' | |
157 | --- src/adaptation/ecap/ServiceRep.cc 2012-08-28 13:00:30 +0000 | |
158 | +++ src/adaptation/ecap/ServiceRep.cc 2013-09-16 18:04:34 +0000 | |
159 | @@ -2,20 +2,29 @@ | |
160 | * DEBUG: section 93 eCAP Interface | |
161 | */ | |
162 | #include "squid.h" | |
163 | +#include "adaptation/ecap/Config.h" | |
164 | +#include "adaptation/ecap/Host.h" | |
165 | +#include "adaptation/ecap/ServiceRep.h" | |
166 | +#include "adaptation/ecap/XactionRep.h" | |
167 | +#include "AsyncEngine.h" | |
168 | +#include "base/TextException.h" | |
169 | #include "Debug.h" | |
170 | -#include <list> | |
171 | +#include "EventLoop.h" | |
172 | #include <libecap/adapter/service.h> | |
173 | #include <libecap/common/options.h> | |
174 | #include <libecap/common/name.h> | |
175 | #include <libecap/common/named_values.h> | |
176 | -#include "adaptation/ecap/Config.h" | |
177 | -#include "adaptation/ecap/Host.h" | |
178 | -#include "adaptation/ecap/ServiceRep.h" | |
179 | -#include "adaptation/ecap/XactionRep.h" | |
180 | -#include "base/TextException.h" | |
181 | +#if HAVE_LIMITS | |
182 | +#include <limits> | |
183 | +#endif | |
184 | +#include <map> | |
185 | ||
186 | -// configured eCAP service wrappers | |
187 | -static std::list<Adaptation::Ecap::ServiceRep::AdapterService> TheServices; | |
188 | +/// libecap::adapter::services indexed by their URI | |
189 | +typedef std::map<std::string, Adaptation::Ecap::ServiceRep::AdapterService> AdapterServices; | |
190 | +/// all loaded services | |
191 | +static AdapterServices TheServices; | |
192 | +/// configured services producing async transactions | |
193 | +static AdapterServices AsyncServices; | |
194 | ||
195 | namespace Adaptation | |
196 | { | |
197 | @@ -39,6 +48,17 @@ | |
198 | const Master &master; ///< the configuration being wrapped | |
199 | }; | |
200 | ||
201 | +/// manages async eCAP transactions | |
202 | +class Engine: public AsyncEngine | |
203 | +{ | |
204 | +public: | |
205 | + /* AsyncEngine API */ | |
206 | + virtual int checkEvents(int timeout); | |
207 | + | |
208 | +private: | |
209 | + void kickAsyncServices(timeval &timeout); | |
210 | +}; | |
211 | + | |
212 | } // namespace Ecap | |
213 | } // namespace Adaptation | |
214 | ||
215 | @@ -76,6 +96,55 @@ | |
216 | visitor.visit(Name(i->first), Area::FromTempString(i->second)); | |
217 | } | |
218 | ||
219 | +/* Adaptation::Ecap::Engine */ | |
220 | + | |
221 | +int | |
222 | +Adaptation::Ecap::Engine::checkEvents(int) | |
223 | +{ | |
224 | + // Start with the default I/O loop timeout, convert from milliseconds. | |
225 | + static const struct timeval maxTimeout { | |
226 | + EVENT_LOOP_TIMEOUT/1000, // seconds | |
227 | + (EVENT_LOOP_TIMEOUT % 1000)*1000 | |
228 | + }; // microseconds | |
229 | + struct timeval timeout = maxTimeout; | |
230 | + | |
231 | + kickAsyncServices(timeout); | |
232 | + if (timeout.tv_sec == maxTimeout.tv_sec && timeout.tv_usec == maxTimeout.tv_usec) | |
233 | + return EVENT_IDLE; | |
234 | + | |
235 | + debugs(93, 7, "timeout: " << timeout.tv_sec << "s+" << timeout.tv_usec << "us"); | |
236 | + | |
237 | + // convert back to milliseconds, avoiding int overflows | |
238 | + if (timeout.tv_sec >= std::numeric_limits<int>::max()/1000 - 1000) | |
239 | + return std::numeric_limits<int>::max(); | |
240 | + else | |
241 | + return timeout.tv_sec*1000 + timeout.tv_usec/1000; | |
242 | +} | |
243 | + | |
244 | +/// resumes async transactions (if any) and returns true if they set a timeout | |
245 | +void | |
246 | +Adaptation::Ecap::Engine::kickAsyncServices(timeval &timeout) | |
247 | +{ | |
248 | + if (AsyncServices.empty()) | |
249 | + return; | |
250 | + | |
251 | + debugs(93, 3, "async services: " << AsyncServices.size()); | |
252 | + | |
253 | + // Activate waiting async transactions, if any. | |
254 | + typedef AdapterServices::iterator ASI; | |
255 | + for (ASI s = AsyncServices.begin(); s != AsyncServices.end(); ++s) { | |
256 | + assert(s->second); | |
257 | + s->second->resume(); // may call Ecap::Xaction::resume() | |
258 | + } | |
259 | + | |
260 | + // Give services a chance to decrease the default timeout. | |
261 | + for (ASI s = AsyncServices.begin(); s != AsyncServices.end(); ++s) { | |
262 | + s->second->suspend(timeout); | |
263 | + } | |
264 | +} | |
265 | + | |
266 | +/* Adaptation::Ecap::ServiceRep */ | |
267 | + | |
268 | Adaptation::Ecap::ServiceRep::ServiceRep(const ServiceConfigPointer &cfg): | |
269 | /*AsyncJob("Adaptation::Ecap::ServiceRep"),*/ Adaptation::Service(cfg), | |
270 | isDetached(false) | |
271 | @@ -123,6 +192,11 @@ | |
272 | ||
273 | debugs(93,DBG_IMPORTANT, "Starting eCAP service: " << theService->uri()); | |
274 | theService->start(); | |
275 | + | |
276 | + if (theService->makesAsyncXactions()) { | |
277 | + AsyncServices[theService->uri()] = theService; | |
278 | + debugs(93, 5, "asyncs: " << AsyncServices.size()); | |
279 | + } | |
280 | } | |
281 | ||
282 | /// handles failures while configuring or starting an eCAP service; | |
283 | @@ -168,6 +242,16 @@ | |
284 | HttpRequest *cause) | |
285 | { | |
286 | Must(up()); | |
287 | + | |
288 | + // register now because (a) we need EventLoop::Running and (b) we do not | |
289 | + // want to add more main loop overheads unless an async service is used. | |
290 | + static AsyncEngine *TheEngine = NULL; | |
291 | + if (AsyncServices.size() && !TheEngine && EventLoop::Running) { | |
292 | + TheEngine = new Engine; | |
293 | + EventLoop::Running->registerEngine(TheEngine); | |
294 | + debugs(93, 3, "asyncs: " << AsyncServices.size() << ' ' << TheEngine); | |
295 | + } | |
296 | + | |
297 | XactionRep *rep = new XactionRep(virgin, cause, Pointer(this)); | |
298 | XactionRep::AdapterXaction x(theService->makeXaction(rep)); | |
299 | rep->master(x); | |
300 | @@ -210,11 +294,10 @@ | |
301 | Adaptation::Ecap::ServiceRep::AdapterService | |
302 | Adaptation::Ecap::FindAdapterService(const String& serviceUri) | |
303 | { | |
304 | - typedef std::list<ServiceRep::AdapterService>::const_iterator ASCI; | |
305 | - for (ASCI s = TheServices.begin(); s != TheServices.end(); ++s) { | |
306 | - Must(*s); | |
307 | - if (serviceUri == (*s)->uri().c_str()) | |
308 | - return *s; | |
309 | + AdapterServices::const_iterator pos = TheServices.find(serviceUri.termedBuf()); | |
310 | + if (pos != TheServices.end()) { | |
311 | + Must(pos->second); | |
312 | + return pos->second; | |
313 | } | |
314 | return ServiceRep::AdapterService(); | |
315 | } | |
316 | @@ -222,30 +305,18 @@ | |
317 | void | |
318 | Adaptation::Ecap::RegisterAdapterService(const Adaptation::Ecap::ServiceRep::AdapterService& adapterService) | |
319 | { | |
320 | - typedef std::list<ServiceRep::AdapterService>::iterator ASI; | |
321 | - for (ASI s = TheServices.begin(); s != TheServices.end(); ++s) { | |
322 | - Must(*s); | |
323 | - if (adapterService->uri() == (*s)->uri()) { | |
324 | - *s = adapterService; | |
325 | - debugs(93, 3, "updated eCAP module service: " << | |
326 | - adapterService->uri()); | |
327 | - return; | |
328 | - } | |
329 | - } | |
330 | - TheServices.push_back(adapterService); | |
331 | - debugs(93, 3, "registered eCAP module service: " << adapterService->uri()); | |
332 | + TheServices[adapterService->uri()] = adapterService; // may update old one | |
333 | + debugs(93, 3, "stored eCAP module service: " << adapterService->uri()); | |
334 | + // We do not update AsyncServices here in case they are not configured. | |
335 | } | |
336 | ||
337 | void | |
338 | Adaptation::Ecap::UnregisterAdapterService(const String& serviceUri) | |
339 | { | |
340 | - typedef std::list<ServiceRep::AdapterService>::iterator ASI; | |
341 | - for (ASI s = TheServices.begin(); s != TheServices.end(); ++s) { | |
342 | - if (serviceUri == (*s)->uri().c_str()) { | |
343 | - TheServices.erase(s); | |
344 | - debugs(93, 3, "unregistered eCAP module service: " << serviceUri); | |
345 | - return; | |
346 | - } | |
347 | + if (TheServices.erase(serviceUri.termedBuf())) { | |
348 | + debugs(93, 3, "unregistered eCAP module service: " << serviceUri); | |
349 | + AsyncServices.erase(serviceUri.termedBuf()); // no-op for non-async | |
350 | + return; | |
351 | } | |
352 | debugs(93, 3, "failed to unregister eCAP module service: " << serviceUri); | |
353 | } | |
354 | @@ -253,16 +324,16 @@ | |
355 | void | |
356 | Adaptation::Ecap::CheckUnusedAdapterServices(const Adaptation::Services& cfgs) | |
357 | { | |
358 | - typedef std::list<ServiceRep::AdapterService>::const_iterator ASCI; | |
359 | + typedef AdapterServices::const_iterator ASCI; | |
360 | for (ASCI loaded = TheServices.begin(); loaded != TheServices.end(); | |
361 | ++loaded) { | |
362 | bool found = false; | |
363 | for (Services::const_iterator cfged = cfgs.begin(); | |
364 | cfged != cfgs.end() && !found; ++cfged) { | |
365 | - found = (*cfged)->cfg().uri == (*loaded)->uri().c_str(); | |
366 | + found = (*cfged)->cfg().uri == loaded->second->uri().c_str(); | |
367 | } | |
368 | if (!found) | |
369 | debugs(93, DBG_IMPORTANT, "Warning: loaded eCAP service has no matching " << | |
370 | - "ecap_service config option: " << (*loaded)->uri()); | |
371 | + "ecap_service config option: " << loaded->second->uri()); | |
372 | } | |
373 | } | |
374 | ||
375 | === modified file 'src/adaptation/ecap/XactionRep.cc' | |
376 | --- src/adaptation/ecap/XactionRep.cc 2013-06-03 14:05:16 +0000 | |
377 | +++ src/adaptation/ecap/XactionRep.cc 2013-09-23 16:22:57 +0000 | |
378 | @@ -2,19 +2,20 @@ | |
379 | * DEBUG: section 93 eCAP Interface | |
380 | */ | |
381 | #include "squid.h" | |
382 | +#include "adaptation/Answer.h" | |
383 | +#include "adaptation/ecap/XactionRep.h" | |
384 | +#include "adaptation/ecap/Config.h" | |
385 | +#include "adaptation/Initiator.h" | |
386 | +#include "base/AsyncJobCalls.h" | |
387 | +#include "base/TextException.h" | |
388 | +#include "HttpRequest.h" | |
389 | +#include "HttpReply.h" | |
390 | +#include "SquidTime.h" | |
391 | #include <libecap/common/area.h> | |
392 | #include <libecap/common/delay.h> | |
393 | #include <libecap/common/named_values.h> | |
394 | #include <libecap/common/names.h> | |
395 | #include <libecap/adapter/xaction.h> | |
396 | -#include "HttpRequest.h" | |
397 | -#include "HttpReply.h" | |
398 | -#include "SquidTime.h" | |
399 | -#include "adaptation/Answer.h" | |
400 | -#include "adaptation/ecap/XactionRep.h" | |
401 | -#include "adaptation/ecap/Config.h" | |
402 | -#include "adaptation/Initiator.h" | |
403 | -#include "base/TextException.h" | |
404 | ||
405 | CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep); | |
406 | ||
407 | @@ -273,6 +274,25 @@ | |
408 | Adaptation::Initiate::swanSong(); | |
409 | } | |
410 | ||
411 | +void | |
412 | +Adaptation::Ecap::XactionRep::resume() | |
413 | +{ | |
414 | + // go async to gain exception protection and done()-based job destruction | |
415 | + typedef NullaryMemFunT<Adaptation::Ecap::XactionRep> Dialer; | |
416 | + AsyncCall::Pointer call = asyncCall(93, 5, "Adaptation::Ecap::XactionRep::doResume", | |
417 | + Dialer(this, &Adaptation::Ecap::XactionRep::doResume)); | |
418 | + ScheduleCallHere(call); | |
419 | +} | |
420 | + | |
421 | +/// the guts of libecap::host::Xaction::resume() API implementation | |
422 | +/// which just goes async in Adaptation::Ecap::XactionRep::resume(). | |
423 | +void | |
424 | +Adaptation::Ecap::XactionRep::doResume() | |
425 | +{ | |
426 | + Must(theMaster); | |
427 | + theMaster->resume(); | |
428 | +} | |
429 | + | |
430 | libecap::Message & | |
431 | Adaptation::Ecap::XactionRep::virgin() | |
432 | { | |
433 | @@ -595,12 +615,6 @@ | |
434 | mustStop("adaptationAborted"); | |
435 | } | |
436 | ||
437 | -bool | |
438 | -Adaptation::Ecap::XactionRep::callable() const | |
439 | -{ | |
440 | - return !done(); | |
441 | -} | |
442 | - | |
443 | void | |
444 | Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp) | |
445 | { | |
446 | ||
447 | === modified file 'src/adaptation/ecap/XactionRep.h' | |
448 | --- src/adaptation/ecap/XactionRep.h 2012-10-04 11:10:17 +0000 | |
449 | +++ src/adaptation/ecap/XactionRep.h 2013-09-23 16:22:57 +0000 | |
450 | @@ -44,6 +44,7 @@ | |
451 | virtual void blockVirgin(); | |
452 | virtual void adaptationDelayed(const libecap::Delay &); | |
453 | virtual void adaptationAborted(); | |
454 | + virtual void resume(); | |
455 | virtual void vbDiscard(); | |
456 | virtual void vbMake(); | |
457 | virtual void vbStopMaking(); | |
458 | @@ -53,9 +54,6 @@ | |
459 | virtual void noteAbContentDone(bool atEnd); | |
460 | virtual void noteAbContentAvailable(); | |
461 | ||
462 | - // libecap::Callable API, via libecap::host::Xaction | |
463 | - virtual bool callable() const; | |
464 | - | |
465 | // BodyProducer API | |
466 | virtual void noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp); | |
467 | virtual void noteBodyConsumerAborted(RefCount<BodyPipe> bp); | |
468 | @@ -97,6 +95,8 @@ | |
469 | /// Return the adaptation meta headers and their values | |
470 | void visitEachMetaHeader(libecap::NamedValueVisitor &visitor) const; | |
471 | ||
472 | + void doResume(); | |
473 | + | |
474 | private: | |
475 | AdapterXaction theMaster; // the actual adaptation xaction we represent | |
476 | Adaptation::ServicePointer theService; ///< xaction's adaptation service | |
477 | ||
478 | === modified file 'src/main.cc' | |
479 | --- src/main.cc 2013-06-07 04:35:25 +0000 | |
480 | +++ src/main.cc 2013-09-16 00:12:40 +0000 | |
481 | @@ -225,17 +225,15 @@ | |
482 | { | |
483 | ||
484 | public: | |
485 | - SignalEngine(EventLoop &evtLoop) : loop(evtLoop) {} | |
486 | virtual int checkEvents(int timeout); | |
487 | ||
488 | private: | |
489 | - static void StopEventLoop(void * data) { | |
490 | - static_cast<SignalEngine *>(data)->loop.stop(); | |
491 | + static void StopEventLoop(void *) { | |
492 | + if (EventLoop::Running) | |
493 | + EventLoop::Running->stop(); | |
494 | } | |
495 | ||
496 | void doShutdown(time_t wait); | |
497 | - | |
498 | - EventLoop &loop; | |
499 | }; | |
500 | ||
501 | int | |
502 | @@ -1506,7 +1504,7 @@ | |
503 | /* main loop */ | |
504 | EventLoop mainLoop; | |
505 | ||
506 | - SignalEngine signalEngine(mainLoop); | |
507 | + SignalEngine signalEngine; | |
508 | ||
509 | mainLoop.registerEngine(&signalEngine); | |
510 | ||
511 | ||
512 |