diff options
Diffstat (limited to 'linden/indra/llplugin/llpluginprocessparent.cpp')
-rwxr-xr-x | linden/indra/llplugin/llpluginprocessparent.cpp | 1120 |
1 files changed, 1120 insertions, 0 deletions
diff --git a/linden/indra/llplugin/llpluginprocessparent.cpp b/linden/indra/llplugin/llpluginprocessparent.cpp new file mode 100755 index 0000000..8db6046 --- /dev/null +++ b/linden/indra/llplugin/llpluginprocessparent.cpp | |||
@@ -0,0 +1,1120 @@ | |||
1 | /** | ||
2 | * @file llpluginprocessparent.cpp | ||
3 | * @brief LLPluginProcessParent handles the parent side of the external-process plugin API. | ||
4 | * | ||
5 | * @cond | ||
6 | * $LicenseInfo:firstyear=2008&license=viewergpl$ | ||
7 | * | ||
8 | * Copyright (c) 2008-2010, Linden Research, Inc. | ||
9 | * | ||
10 | * Second Life Viewer Source Code | ||
11 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
12 | * to you under the terms of the GNU General Public License, version 2.0 | ||
13 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
14 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
15 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
16 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
17 | * | ||
18 | * There are special exceptions to the terms and conditions of the GPL as | ||
19 | * it is applied to this Source Code. View the full text of the exception | ||
20 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
21 | * online at | ||
22 | * http://secondlife.com/developers/opensource/flossexception | ||
23 | * | ||
24 | * By copying, modifying or distributing this software, you acknowledge | ||
25 | * that you have read and understood your obligations described above, | ||
26 | * and agree to abide by those obligations. | ||
27 | * | ||
28 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
29 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
30 | * COMPLETENESS OR PERFORMANCE. | ||
31 | * $/LicenseInfo$ | ||
32 | * | ||
33 | * @endcond | ||
34 | */ | ||
35 | |||
36 | #include "linden_common.h" | ||
37 | |||
38 | #include "llpluginprocessparent.h" | ||
39 | #include "llpluginmessagepipe.h" | ||
40 | #include "llpluginmessageclasses.h" | ||
41 | |||
42 | #include "llapr.h" | ||
43 | |||
44 | //virtual | ||
45 | LLPluginProcessParentOwner::~LLPluginProcessParentOwner() | ||
46 | { | ||
47 | |||
48 | } | ||
49 | |||
50 | bool LLPluginProcessParent::sUseReadThread = false; | ||
51 | apr_pollset_t *LLPluginProcessParent::sPollSet = NULL; | ||
52 | bool LLPluginProcessParent::sPollsetNeedsRebuild = false; | ||
53 | LLMutex *LLPluginProcessParent::sInstancesMutex; | ||
54 | std::list<LLPluginProcessParent*> LLPluginProcessParent::sInstances; | ||
55 | LLThread *LLPluginProcessParent::sReadThread = NULL; | ||
56 | |||
57 | |||
58 | class LLPluginProcessParentPollThread: public LLThread | ||
59 | { | ||
60 | public: | ||
61 | LLPluginProcessParentPollThread() : | ||
62 | LLThread("LLPluginProcessParentPollThread", gAPRPoolp) | ||
63 | { | ||
64 | } | ||
65 | protected: | ||
66 | // Inherited from LLThread | ||
67 | /*virtual*/ void run(void) | ||
68 | { | ||
69 | while(!isQuitting() && LLPluginProcessParent::getUseReadThread()) | ||
70 | { | ||
71 | LLPluginProcessParent::poll(0.1f); | ||
72 | checkPause(); | ||
73 | } | ||
74 | |||
75 | // Final poll to clean up the pollset, etc. | ||
76 | LLPluginProcessParent::poll(0.0f); | ||
77 | } | ||
78 | |||
79 | // Inherited from LLThread | ||
80 | /*virtual*/ bool runCondition(void) | ||
81 | { | ||
82 | return(LLPluginProcessParent::canPollThreadRun()); | ||
83 | } | ||
84 | |||
85 | }; | ||
86 | |||
87 | LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner): | ||
88 | mIncomingQueueMutex(gAPRPoolp) | ||
89 | { | ||
90 | if(!sInstancesMutex) | ||
91 | { | ||
92 | sInstancesMutex = new LLMutex(gAPRPoolp); | ||
93 | } | ||
94 | |||
95 | mOwner = owner; | ||
96 | mBoundPort = 0; | ||
97 | mState = STATE_UNINITIALIZED; | ||
98 | mSleepTime = 0.0; | ||
99 | mCPUUsage = 0.0; | ||
100 | mDisableTimeout = false; | ||
101 | mDebug = false; | ||
102 | mBlocked = false; | ||
103 | mPolledInput = false; | ||
104 | mPollFD.client_data = NULL; | ||
105 | |||
106 | mPluginLaunchTimeout = 60.0f; | ||
107 | mPluginLockupTimeout = 15.0f; | ||
108 | |||
109 | // Don't start the timer here -- start it when we actually launch the plugin process. | ||
110 | mHeartbeat.stop(); | ||
111 | |||
112 | |||
113 | // Don't add to the global list until fully constructed. | ||
114 | { | ||
115 | LLMutexLock lock(sInstancesMutex); | ||
116 | sInstances.push_back(this); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | LLPluginProcessParent::~LLPluginProcessParent() | ||
121 | { | ||
122 | LL_DEBUGS("Plugin") << "destructor" << LL_ENDL; | ||
123 | |||
124 | // Remove from the global list before beginning destruction. | ||
125 | { | ||
126 | // Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll() | ||
127 | LLMutexLock lock(sInstancesMutex); | ||
128 | { | ||
129 | LLMutexLock lock2(&mIncomingQueueMutex); | ||
130 | sInstances.remove(this); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | // Destroy any remaining shared memory regions | ||
135 | sharedMemoryRegionsType::iterator iter; | ||
136 | while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end()) | ||
137 | { | ||
138 | // destroy the shared memory region | ||
139 | iter->second->destroy(); | ||
140 | |||
141 | // and remove it from our map | ||
142 | mSharedMemoryRegions.erase(iter); | ||
143 | } | ||
144 | |||
145 | mProcess.kill(); | ||
146 | killSockets(); | ||
147 | } | ||
148 | |||
149 | void LLPluginProcessParent::killSockets(void) | ||
150 | { | ||
151 | { | ||
152 | LLMutexLock lock(&mIncomingQueueMutex); | ||
153 | killMessagePipe(); | ||
154 | } | ||
155 | |||
156 | mListenSocket.reset(); | ||
157 | mSocket.reset(); | ||
158 | } | ||
159 | |||
160 | void LLPluginProcessParent::errorState(void) | ||
161 | { | ||
162 | if(mState < STATE_RUNNING) | ||
163 | setState(STATE_LAUNCH_FAILURE); | ||
164 | else | ||
165 | setState(STATE_ERROR); | ||
166 | } | ||
167 | |||
168 | void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug) | ||
169 | { | ||
170 | mProcess.setExecutable(launcher_filename); | ||
171 | mPluginFile = plugin_filename; | ||
172 | mCPUUsage = 0.0f; | ||
173 | mDebug = debug; | ||
174 | setState(STATE_INITIALIZED); | ||
175 | } | ||
176 | |||
177 | bool LLPluginProcessParent::accept() | ||
178 | { | ||
179 | bool result = false; | ||
180 | |||
181 | apr_status_t status = APR_EGENERAL; | ||
182 | apr_socket_t *new_socket = NULL; | ||
183 | |||
184 | status = apr_socket_accept( | ||
185 | &new_socket, | ||
186 | mListenSocket->getSocket(), | ||
187 | gAPRPoolp); | ||
188 | |||
189 | |||
190 | if(status == APR_SUCCESS) | ||
191 | { | ||
192 | // llinfos << "SUCCESS" << llendl; | ||
193 | // Success. Create a message pipe on the new socket | ||
194 | |||
195 | // we MUST create a new pool for the LLSocket, since it will take ownership of it and delete it in its destructor! | ||
196 | apr_pool_t* new_pool = NULL; | ||
197 | status = apr_pool_create(&new_pool, gAPRPoolp); | ||
198 | |||
199 | mSocket = LLSocket::create(new_socket, new_pool); | ||
200 | new LLPluginMessagePipe(this, mSocket); | ||
201 | |||
202 | result = true; | ||
203 | } | ||
204 | else if(APR_STATUS_IS_EAGAIN(status)) | ||
205 | { | ||
206 | // llinfos << "EAGAIN" << llendl; | ||
207 | |||
208 | // No incoming connections. This is not an error. | ||
209 | status = APR_SUCCESS; | ||
210 | } | ||
211 | else | ||
212 | { | ||
213 | // llinfos << "Error:" << llendl; | ||
214 | ll_apr_warn_status(status); | ||
215 | |||
216 | // Some other error. | ||
217 | errorState(); | ||
218 | } | ||
219 | |||
220 | return result; | ||
221 | } | ||
222 | |||
223 | void LLPluginProcessParent::idle(void) | ||
224 | { | ||
225 | bool idle_again; | ||
226 | |||
227 | do | ||
228 | { | ||
229 | // process queued messages | ||
230 | mIncomingQueueMutex.lock(); | ||
231 | while(!mIncomingQueue.empty()) | ||
232 | { | ||
233 | LLPluginMessage message = mIncomingQueue.front(); | ||
234 | mIncomingQueue.pop(); | ||
235 | mIncomingQueueMutex.unlock(); | ||
236 | |||
237 | receiveMessage(message); | ||
238 | |||
239 | mIncomingQueueMutex.lock(); | ||
240 | } | ||
241 | |||
242 | mIncomingQueueMutex.unlock(); | ||
243 | |||
244 | // Give time to network processing | ||
245 | if(mMessagePipe) | ||
246 | { | ||
247 | // Drain any queued outgoing messages | ||
248 | mMessagePipe->pumpOutput(); | ||
249 | |||
250 | // Only do input processing here if this instance isn't in a pollset. | ||
251 | if(!mPolledInput) | ||
252 | { | ||
253 | mMessagePipe->pumpInput(); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | if(mState <= STATE_RUNNING) | ||
258 | { | ||
259 | if(APR_STATUS_IS_EOF(mSocketError)) | ||
260 | { | ||
261 | // Plugin socket was closed. This covers both normal plugin termination and plugin crashes. | ||
262 | errorState(); | ||
263 | } | ||
264 | else if(mSocketError != APR_SUCCESS) | ||
265 | { | ||
266 | // The socket is in an error state -- the plugin is gone. | ||
267 | LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; | ||
268 | errorState(); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). | ||
273 | // USE THIS CAREFULLY, since it can starve other code. Specifically make sure there's no way to get into a closed cycle and never return. | ||
274 | // When in doubt, don't do it. | ||
275 | idle_again = false; | ||
276 | switch(mState) | ||
277 | { | ||
278 | case STATE_UNINITIALIZED: | ||
279 | break; | ||
280 | |||
281 | case STATE_INITIALIZED: | ||
282 | { | ||
283 | |||
284 | apr_status_t status = APR_SUCCESS; | ||
285 | apr_sockaddr_t* addr = NULL; | ||
286 | mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); | ||
287 | mBoundPort = 0; | ||
288 | |||
289 | // This code is based on parts of LLSocket::create() in lliosocket.cpp. | ||
290 | |||
291 | status = apr_sockaddr_info_get( | ||
292 | &addr, | ||
293 | "127.0.0.1", | ||
294 | APR_INET, | ||
295 | 0, // port 0 = ephemeral ("find me a port") | ||
296 | 0, | ||
297 | gAPRPoolp); | ||
298 | |||
299 | if(ll_apr_warn_status(status)) | ||
300 | { | ||
301 | killSockets(); | ||
302 | errorState(); | ||
303 | break; | ||
304 | } | ||
305 | |||
306 | // This allows us to reuse the address on quick down/up. This is unlikely to create problems. | ||
307 | ll_apr_warn_status(apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_REUSEADDR, 1)); | ||
308 | |||
309 | status = apr_socket_bind(mListenSocket->getSocket(), addr); | ||
310 | if(ll_apr_warn_status(status)) | ||
311 | { | ||
312 | killSockets(); | ||
313 | errorState(); | ||
314 | break; | ||
315 | } | ||
316 | |||
317 | // Get the actual port the socket was bound to | ||
318 | { | ||
319 | apr_sockaddr_t* bound_addr = NULL; | ||
320 | if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) | ||
321 | { | ||
322 | killSockets(); | ||
323 | errorState(); | ||
324 | break; | ||
325 | } | ||
326 | mBoundPort = bound_addr->port; | ||
327 | |||
328 | if(mBoundPort == 0) | ||
329 | { | ||
330 | LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; | ||
331 | |||
332 | killSockets(); | ||
333 | errorState(); | ||
334 | break; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | LL_DEBUGS("Plugin") << "Bound tcp socket to port: " << addr->port << LL_ENDL; | ||
339 | |||
340 | // Make the listen socket non-blocking | ||
341 | status = apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_NONBLOCK, 1); | ||
342 | if(ll_apr_warn_status(status)) | ||
343 | { | ||
344 | killSockets(); | ||
345 | errorState(); | ||
346 | break; | ||
347 | } | ||
348 | |||
349 | apr_socket_timeout_set(mListenSocket->getSocket(), 0); | ||
350 | if(ll_apr_warn_status(status)) | ||
351 | { | ||
352 | killSockets(); | ||
353 | errorState(); | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | // If it's a stream based socket, we need to tell the OS | ||
358 | // to keep a queue of incoming connections for ACCEPT. | ||
359 | status = apr_socket_listen( | ||
360 | mListenSocket->getSocket(), | ||
361 | 10); // FIXME: Magic number for queue size | ||
362 | |||
363 | if(ll_apr_warn_status(status)) | ||
364 | { | ||
365 | killSockets(); | ||
366 | errorState(); | ||
367 | break; | ||
368 | } | ||
369 | |||
370 | // If we got here, we're listening. | ||
371 | setState(STATE_LISTENING); | ||
372 | } | ||
373 | break; | ||
374 | |||
375 | case STATE_LISTENING: | ||
376 | { | ||
377 | // Launch the plugin process. | ||
378 | |||
379 | // Only argument to the launcher is the port number we're listening on | ||
380 | std::stringstream stream; | ||
381 | stream << mBoundPort; | ||
382 | mProcess.addArgument(stream.str()); | ||
383 | if(mProcess.launch() != 0) | ||
384 | { | ||
385 | errorState(); | ||
386 | } | ||
387 | else | ||
388 | { | ||
389 | if(mDebug) | ||
390 | { | ||
391 | #if LL_DARWIN | ||
392 | // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. | ||
393 | |||
394 | // The command we're constructing would look like this on the command line: | ||
395 | // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' | ||
396 | |||
397 | std::stringstream cmd; | ||
398 | |||
399 | mDebugger.setExecutable("/usr/bin/osascript"); | ||
400 | mDebugger.addArgument("-e"); | ||
401 | mDebugger.addArgument("tell application \"Terminal\""); | ||
402 | mDebugger.addArgument("-e"); | ||
403 | cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\""; | ||
404 | mDebugger.addArgument(cmd.str()); | ||
405 | mDebugger.addArgument("-e"); | ||
406 | mDebugger.addArgument("do script \"continue\" in win"); | ||
407 | mDebugger.addArgument("-e"); | ||
408 | mDebugger.addArgument("end tell"); | ||
409 | mDebugger.launch(); | ||
410 | |||
411 | #elif LL_LINUX | ||
412 | |||
413 | std::stringstream cmd; | ||
414 | |||
415 | mDebugger.setExecutable("/usr/bin/gnome-terminal"); | ||
416 | mDebugger.addArgument("--geometry=165x24-0+0"); | ||
417 | mDebugger.addArgument("-e"); | ||
418 | cmd << "/usr/bin/gdb -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID(); | ||
419 | mDebugger.addArgument(cmd.str()); | ||
420 | mDebugger.launch(); | ||
421 | |||
422 | #endif | ||
423 | } | ||
424 | |||
425 | // This will allow us to time out if the process never starts. | ||
426 | mHeartbeat.start(); | ||
427 | mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); | ||
428 | setState(STATE_LAUNCHED); | ||
429 | } | ||
430 | } | ||
431 | break; | ||
432 | |||
433 | case STATE_LAUNCHED: | ||
434 | // waiting for the plugin to connect | ||
435 | if(pluginLockedUpOrQuit()) | ||
436 | { | ||
437 | errorState(); | ||
438 | } | ||
439 | else | ||
440 | { | ||
441 | // Check for the incoming connection. | ||
442 | if(accept()) | ||
443 | { | ||
444 | // Stop listening on the server port | ||
445 | mListenSocket.reset(); | ||
446 | setState(STATE_CONNECTED); | ||
447 | } | ||
448 | } | ||
449 | break; | ||
450 | |||
451 | case STATE_CONNECTED: | ||
452 | // waiting for hello message from the plugin | ||
453 | |||
454 | if(pluginLockedUpOrQuit()) | ||
455 | { | ||
456 | errorState(); | ||
457 | } | ||
458 | break; | ||
459 | |||
460 | case STATE_HELLO: | ||
461 | LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL; | ||
462 | |||
463 | // Send the message to load the plugin | ||
464 | { | ||
465 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin"); | ||
466 | message.setValue("file", mPluginFile); | ||
467 | sendMessage(message); | ||
468 | } | ||
469 | |||
470 | setState(STATE_LOADING); | ||
471 | break; | ||
472 | |||
473 | case STATE_LOADING: | ||
474 | // The load_plugin_response message will kick us from here into STATE_RUNNING | ||
475 | if(pluginLockedUpOrQuit()) | ||
476 | { | ||
477 | errorState(); | ||
478 | } | ||
479 | break; | ||
480 | |||
481 | case STATE_RUNNING: | ||
482 | if(pluginLockedUpOrQuit()) | ||
483 | { | ||
484 | errorState(); | ||
485 | } | ||
486 | break; | ||
487 | |||
488 | case STATE_EXITING: | ||
489 | if(!mProcess.isRunning()) | ||
490 | { | ||
491 | setState(STATE_CLEANUP); | ||
492 | } | ||
493 | else if(pluginLockedUp()) | ||
494 | { | ||
495 | LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL; | ||
496 | errorState(); | ||
497 | } | ||
498 | break; | ||
499 | |||
500 | case STATE_LAUNCH_FAILURE: | ||
501 | if(mOwner != NULL) | ||
502 | { | ||
503 | mOwner->pluginLaunchFailed(); | ||
504 | } | ||
505 | setState(STATE_CLEANUP); | ||
506 | break; | ||
507 | |||
508 | case STATE_ERROR: | ||
509 | if(mOwner != NULL) | ||
510 | { | ||
511 | mOwner->pluginDied(); | ||
512 | } | ||
513 | setState(STATE_CLEANUP); | ||
514 | break; | ||
515 | |||
516 | case STATE_CLEANUP: | ||
517 | mProcess.kill(); | ||
518 | killSockets(); | ||
519 | setState(STATE_DONE); | ||
520 | break; | ||
521 | |||
522 | |||
523 | case STATE_DONE: | ||
524 | // just sit here. | ||
525 | break; | ||
526 | |||
527 | } | ||
528 | |||
529 | } while (idle_again); | ||
530 | } | ||
531 | |||
532 | bool LLPluginProcessParent::isLoading(void) | ||
533 | { | ||
534 | bool result = false; | ||
535 | |||
536 | if(mState <= STATE_LOADING) | ||
537 | result = true; | ||
538 | |||
539 | return result; | ||
540 | } | ||
541 | |||
542 | bool LLPluginProcessParent::isRunning(void) | ||
543 | { | ||
544 | bool result = false; | ||
545 | |||
546 | if(mState == STATE_RUNNING) | ||
547 | result = true; | ||
548 | |||
549 | return result; | ||
550 | } | ||
551 | |||
552 | bool LLPluginProcessParent::isDone(void) | ||
553 | { | ||
554 | bool result = false; | ||
555 | |||
556 | if(mState == STATE_DONE) | ||
557 | result = true; | ||
558 | |||
559 | return result; | ||
560 | } | ||
561 | |||
562 | void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send) | ||
563 | { | ||
564 | if(force_send || (sleep_time != mSleepTime)) | ||
565 | { | ||
566 | // Cache the time locally | ||
567 | mSleepTime = sleep_time; | ||
568 | |||
569 | if(canSendMessage()) | ||
570 | { | ||
571 | // and send to the plugin. | ||
572 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "sleep_time"); | ||
573 | message.setValueReal("time", mSleepTime); | ||
574 | sendMessage(message); | ||
575 | } | ||
576 | else | ||
577 | { | ||
578 | // Too early to send -- the load_plugin_response message will trigger us to send mSleepTime later. | ||
579 | } | ||
580 | } | ||
581 | } | ||
582 | |||
583 | void LLPluginProcessParent::sendMessage(const LLPluginMessage &message) | ||
584 | { | ||
585 | if(message.hasValue("blocking_response")) | ||
586 | { | ||
587 | mBlocked = false; | ||
588 | |||
589 | // reset the heartbeat timer, since there will have been no heartbeats while the plugin was blocked. | ||
590 | mHeartbeat.setTimerExpirySec(mPluginLockupTimeout); | ||
591 | } | ||
592 | |||
593 | std::string buffer = message.generate(); | ||
594 | LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL; | ||
595 | writeMessageRaw(buffer); | ||
596 | |||
597 | // Try to send message immediately. | ||
598 | if(mMessagePipe) | ||
599 | { | ||
600 | mMessagePipe->pumpOutput(); | ||
601 | } | ||
602 | } | ||
603 | |||
604 | //virtual | ||
605 | void LLPluginProcessParent::setMessagePipe(LLPluginMessagePipe *message_pipe) | ||
606 | { | ||
607 | bool update_pollset = false; | ||
608 | |||
609 | if(mMessagePipe) | ||
610 | { | ||
611 | // Unsetting an existing message pipe -- remove from the pollset | ||
612 | mPollFD.client_data = NULL; | ||
613 | |||
614 | // pollset needs an update | ||
615 | update_pollset = true; | ||
616 | } | ||
617 | if(message_pipe != NULL) | ||
618 | { | ||
619 | // Set up the apr_pollfd_t | ||
620 | mPollFD.p = gAPRPoolp; | ||
621 | mPollFD.desc_type = APR_POLL_SOCKET; | ||
622 | mPollFD.reqevents = APR_POLLIN|APR_POLLERR|APR_POLLHUP; | ||
623 | mPollFD.rtnevents = 0; | ||
624 | mPollFD.desc.s = mSocket->getSocket(); | ||
625 | mPollFD.client_data = (void*)this; | ||
626 | |||
627 | // pollset needs an update | ||
628 | update_pollset = true; | ||
629 | } | ||
630 | |||
631 | mMessagePipe = message_pipe; | ||
632 | |||
633 | if(update_pollset) | ||
634 | { | ||
635 | dirtyPollSet(); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | //static | ||
640 | void LLPluginProcessParent::dirtyPollSet() | ||
641 | { | ||
642 | sPollsetNeedsRebuild = true; | ||
643 | |||
644 | if(sReadThread) | ||
645 | { | ||
646 | LL_DEBUGS("PluginPoll") << "unpausing read thread " << LL_ENDL; | ||
647 | sReadThread->unpause(); | ||
648 | } | ||
649 | } | ||
650 | |||
651 | void LLPluginProcessParent::updatePollset() | ||
652 | { | ||
653 | if(!sInstancesMutex) | ||
654 | { | ||
655 | // No instances have been created yet. There's no work to do. | ||
656 | return; | ||
657 | } | ||
658 | |||
659 | LLMutexLock lock(sInstancesMutex); | ||
660 | |||
661 | if(sPollSet) | ||
662 | { | ||
663 | LL_DEBUGS("PluginPoll") << "destroying pollset " << sPollSet << LL_ENDL; | ||
664 | // delete the existing pollset. | ||
665 | apr_pollset_destroy(sPollSet); | ||
666 | sPollSet = NULL; | ||
667 | } | ||
668 | |||
669 | std::list<LLPluginProcessParent*>::iterator iter; | ||
670 | int count = 0; | ||
671 | |||
672 | // Count the number of instances that want to be in the pollset | ||
673 | for(iter = sInstances.begin(); iter != sInstances.end(); iter++) | ||
674 | { | ||
675 | (*iter)->mPolledInput = false; | ||
676 | if((*iter)->mPollFD.client_data) | ||
677 | { | ||
678 | // This instance has a socket that needs to be polled. | ||
679 | ++count; | ||
680 | } | ||
681 | } | ||
682 | |||
683 | if(sUseReadThread && sReadThread && !sReadThread->isQuitting()) | ||
684 | { | ||
685 | if(!sPollSet && (count > 0)) | ||
686 | { | ||
687 | #ifdef APR_POLLSET_NOCOPY | ||
688 | // The pollset doesn't exist yet. Create it now. | ||
689 | apr_status_t status = apr_pollset_create(&sPollSet, count, gAPRPoolp, APR_POLLSET_NOCOPY); | ||
690 | if(status != APR_SUCCESS) | ||
691 | { | ||
692 | #endif // APR_POLLSET_NOCOPY | ||
693 | LL_WARNS("PluginPoll") << "Couldn't create pollset. Falling back to non-pollset mode." << LL_ENDL; | ||
694 | sPollSet = NULL; | ||
695 | #ifdef APR_POLLSET_NOCOPY | ||
696 | } | ||
697 | else | ||
698 | { | ||
699 | LL_DEBUGS("PluginPoll") << "created pollset " << sPollSet << LL_ENDL; | ||
700 | |||
701 | // Pollset was created, add all instances to it. | ||
702 | for(iter = sInstances.begin(); iter != sInstances.end(); iter++) | ||
703 | { | ||
704 | if((*iter)->mPollFD.client_data) | ||
705 | { | ||
706 | status = apr_pollset_add(sPollSet, &((*iter)->mPollFD)); | ||
707 | if(status == APR_SUCCESS) | ||
708 | { | ||
709 | (*iter)->mPolledInput = true; | ||
710 | } | ||
711 | else | ||
712 | { | ||
713 | LL_WARNS("PluginPoll") << "apr_pollset_add failed with status " << status << LL_ENDL; | ||
714 | } | ||
715 | } | ||
716 | } | ||
717 | } | ||
718 | #endif // APR_POLLSET_NOCOPY | ||
719 | } | ||
720 | } | ||
721 | } | ||
722 | |||
723 | void LLPluginProcessParent::setUseReadThread(bool use_read_thread) | ||
724 | { | ||
725 | if(sUseReadThread != use_read_thread) | ||
726 | { | ||
727 | sUseReadThread = use_read_thread; | ||
728 | |||
729 | if(sUseReadThread) | ||
730 | { | ||
731 | if(!sReadThread) | ||
732 | { | ||
733 | // start up the read thread | ||
734 | LL_INFOS("PluginPoll") << "creating read thread " << LL_ENDL; | ||
735 | |||
736 | // make sure the pollset gets rebuilt. | ||
737 | sPollsetNeedsRebuild = true; | ||
738 | |||
739 | sReadThread = new LLPluginProcessParentPollThread; | ||
740 | sReadThread->start(); | ||
741 | } | ||
742 | } | ||
743 | else | ||
744 | { | ||
745 | if(sReadThread) | ||
746 | { | ||
747 | // shut down the read thread | ||
748 | LL_INFOS("PluginPoll") << "destroying read thread " << LL_ENDL; | ||
749 | delete sReadThread; | ||
750 | sReadThread = NULL; | ||
751 | } | ||
752 | } | ||
753 | |||
754 | } | ||
755 | } | ||
756 | |||
757 | void LLPluginProcessParent::poll(F64 timeout) | ||
758 | { | ||
759 | if(sPollsetNeedsRebuild || !sUseReadThread) | ||
760 | { | ||
761 | sPollsetNeedsRebuild = false; | ||
762 | updatePollset(); | ||
763 | } | ||
764 | |||
765 | if(sPollSet) | ||
766 | { | ||
767 | apr_status_t status; | ||
768 | apr_int32_t count; | ||
769 | const apr_pollfd_t *descriptors; | ||
770 | status = apr_pollset_poll(sPollSet, (apr_interval_time_t)(timeout * 1000000), &count, &descriptors); | ||
771 | if(status == APR_SUCCESS) | ||
772 | { | ||
773 | // One or more of the descriptors signalled. Call them. | ||
774 | for(int i = 0; i < count; i++) | ||
775 | { | ||
776 | LLPluginProcessParent *self = (LLPluginProcessParent *)(descriptors[i].client_data); | ||
777 | // NOTE: the descriptor returned here is actually a COPY of the original (even though we create the pollset with APR_POLLSET_NOCOPY). | ||
778 | // This means that even if the parent has set its mPollFD.client_data to NULL, the old pointer may still there in this descriptor. | ||
779 | // It's even possible that the old pointer no longer points to a valid LLPluginProcessParent. | ||
780 | // This means that we can't safely dereference the 'self' pointer here without some extra steps... | ||
781 | if(self) | ||
782 | { | ||
783 | // Make sure this pointer is still in the instances list | ||
784 | bool valid = false; | ||
785 | { | ||
786 | LLMutexLock lock(sInstancesMutex); | ||
787 | for(std::list<LLPluginProcessParent*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) | ||
788 | { | ||
789 | if(*iter == self) | ||
790 | { | ||
791 | // Lock the instance's mutex before unlocking the global mutex. | ||
792 | // This avoids a possible race condition where the instance gets deleted between this check and the servicePoll() call. | ||
793 | self->mIncomingQueueMutex.lock(); | ||
794 | valid = true; | ||
795 | break; | ||
796 | } | ||
797 | } | ||
798 | } | ||
799 | |||
800 | if(valid) | ||
801 | { | ||
802 | // The instance is still valid. | ||
803 | // Pull incoming messages off the socket | ||
804 | self->servicePoll(); | ||
805 | self->mIncomingQueueMutex.unlock(); | ||
806 | } | ||
807 | else | ||
808 | { | ||
809 | LL_DEBUGS("PluginPoll") << "detected deleted instance " << self << LL_ENDL; | ||
810 | } | ||
811 | |||
812 | } | ||
813 | } | ||
814 | } | ||
815 | else if(APR_STATUS_IS_TIMEUP(status)) | ||
816 | { | ||
817 | // timed out with no incoming data. Just return. | ||
818 | } | ||
819 | else if(status == EBADF) | ||
820 | { | ||
821 | // This happens when one of the file descriptors in the pollset is destroyed, which happens whenever a plugin's socket is closed. | ||
822 | // The pollset has been or will be recreated, so just return. | ||
823 | LL_DEBUGS("PluginPoll") << "apr_pollset_poll returned EBADF" << LL_ENDL; | ||
824 | } | ||
825 | else if(status != APR_SUCCESS) | ||
826 | { | ||
827 | LL_WARNS("PluginPoll") << "apr_pollset_poll failed with status " << status << LL_ENDL; | ||
828 | } | ||
829 | } | ||
830 | } | ||
831 | |||
832 | void LLPluginProcessParent::servicePoll() | ||
833 | { | ||
834 | bool result = true; | ||
835 | |||
836 | // poll signalled on this object's socket. Try to process incoming messages. | ||
837 | if(mMessagePipe) | ||
838 | { | ||
839 | result = mMessagePipe->pumpInput(0.0f); | ||
840 | } | ||
841 | |||
842 | if(!result) | ||
843 | { | ||
844 | // If we got a read error on input, remove this pipe from the pollset | ||
845 | apr_pollset_remove(sPollSet, &mPollFD); | ||
846 | |||
847 | // and tell the code not to re-add it | ||
848 | mPollFD.client_data = NULL; | ||
849 | } | ||
850 | } | ||
851 | |||
852 | void LLPluginProcessParent::receiveMessageRaw(const std::string &message) | ||
853 | { | ||
854 | LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL; | ||
855 | |||
856 | LLPluginMessage parsed; | ||
857 | if(parsed.parse(message) != -1) | ||
858 | { | ||
859 | if(parsed.hasValue("blocking_request")) | ||
860 | { | ||
861 | mBlocked = true; | ||
862 | } | ||
863 | |||
864 | if(mPolledInput) | ||
865 | { | ||
866 | // This is being called on the polling thread -- only do minimal processing/queueing. | ||
867 | receiveMessageEarly(parsed); | ||
868 | } | ||
869 | else | ||
870 | { | ||
871 | // This is not being called on the polling thread -- do full message processing at this time. | ||
872 | receiveMessage(parsed); | ||
873 | } | ||
874 | } | ||
875 | } | ||
876 | |||
877 | void LLPluginProcessParent::receiveMessageEarly(const LLPluginMessage &message) | ||
878 | { | ||
879 | // NOTE: this function will be called from the polling thread. It will be called with mIncomingQueueMutex _already locked_. | ||
880 | |||
881 | bool handled = false; | ||
882 | |||
883 | std::string message_class = message.getClass(); | ||
884 | if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) | ||
885 | { | ||
886 | // no internal messages need to be handled early. | ||
887 | } | ||
888 | else | ||
889 | { | ||
890 | // Call out to the owner and see if they to reply | ||
891 | // TODO: Should this only happen when blocked? | ||
892 | if(mOwner != NULL) | ||
893 | { | ||
894 | handled = mOwner->receivePluginMessageEarly(message); | ||
895 | } | ||
896 | } | ||
897 | |||
898 | if(!handled) | ||
899 | { | ||
900 | // any message that wasn't handled early needs to be queued. | ||
901 | mIncomingQueue.push(message); | ||
902 | } | ||
903 | } | ||
904 | |||
905 | void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) | ||
906 | { | ||
907 | std::string message_class = message.getClass(); | ||
908 | if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) | ||
909 | { | ||
910 | // internal messages should be handled here | ||
911 | std::string message_name = message.getName(); | ||
912 | if(message_name == "hello") | ||
913 | { | ||
914 | if(mState == STATE_CONNECTED) | ||
915 | { | ||
916 | // Plugin host has launched. Tell it which plugin to load. | ||
917 | setState(STATE_HELLO); | ||
918 | } | ||
919 | else | ||
920 | { | ||
921 | LL_WARNS("Plugin") << "received hello message in wrong state -- bailing out" << LL_ENDL; | ||
922 | errorState(); | ||
923 | } | ||
924 | |||
925 | } | ||
926 | else if(message_name == "load_plugin_response") | ||
927 | { | ||
928 | if(mState == STATE_LOADING) | ||
929 | { | ||
930 | // Plugin has been loaded. | ||
931 | |||
932 | mPluginVersionString = message.getValue("plugin_version"); | ||
933 | LL_INFOS("Plugin") << "plugin version string: " << mPluginVersionString << LL_ENDL; | ||
934 | |||
935 | // Check which message classes/versions the plugin supports. | ||
936 | // TODO: check against current versions | ||
937 | // TODO: kill plugin on major mismatches? | ||
938 | mMessageClassVersions = message.getValueLLSD("versions"); | ||
939 | LLSD::map_iterator iter; | ||
940 | for(iter = mMessageClassVersions.beginMap(); iter != mMessageClassVersions.endMap(); iter++) | ||
941 | { | ||
942 | LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL; | ||
943 | } | ||
944 | |||
945 | // Send initial sleep time | ||
946 | setSleepTime(mSleepTime, true); | ||
947 | |||
948 | setState(STATE_RUNNING); | ||
949 | } | ||
950 | else | ||
951 | { | ||
952 | LL_WARNS("Plugin") << "received load_plugin_response message in wrong state -- bailing out" << LL_ENDL; | ||
953 | errorState(); | ||
954 | } | ||
955 | } | ||
956 | else if(message_name == "heartbeat") | ||
957 | { | ||
958 | // this resets our timer. | ||
959 | mHeartbeat.setTimerExpirySec(mPluginLockupTimeout); | ||
960 | |||
961 | mCPUUsage = message.getValueReal("cpu_usage"); | ||
962 | |||
963 | LL_DEBUGS("Plugin") << "cpu usage reported as " << mCPUUsage << LL_ENDL; | ||
964 | |||
965 | } | ||
966 | else if(message_name == "shm_add_response") | ||
967 | { | ||
968 | // Nothing to do here. | ||
969 | } | ||
970 | else if(message_name == "shm_remove_response") | ||
971 | { | ||
972 | std::string name = message.getValue("name"); | ||
973 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
974 | |||
975 | if(iter != mSharedMemoryRegions.end()) | ||
976 | { | ||
977 | // destroy the shared memory region | ||
978 | iter->second->destroy(); | ||
979 | |||
980 | // and remove it from our map | ||
981 | mSharedMemoryRegions.erase(iter); | ||
982 | } | ||
983 | } | ||
984 | else | ||
985 | { | ||
986 | LL_WARNS("Plugin") << "Unknown internal message from child: " << message_name << LL_ENDL; | ||
987 | } | ||
988 | } | ||
989 | else | ||
990 | { | ||
991 | if(mOwner != NULL) | ||
992 | { | ||
993 | mOwner->receivePluginMessage(message); | ||
994 | } | ||
995 | } | ||
996 | } | ||
997 | |||
998 | std::string LLPluginProcessParent::addSharedMemory(size_t size) | ||
999 | { | ||
1000 | std::string name; | ||
1001 | |||
1002 | LLPluginSharedMemory *region = new LLPluginSharedMemory; | ||
1003 | |||
1004 | // This is a new region | ||
1005 | if(region->create(size)) | ||
1006 | { | ||
1007 | name = region->getName(); | ||
1008 | |||
1009 | mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region)); | ||
1010 | |||
1011 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add"); | ||
1012 | message.setValue("name", name); | ||
1013 | message.setValueS32("size", (S32)size); | ||
1014 | sendMessage(message); | ||
1015 | } | ||
1016 | else | ||
1017 | { | ||
1018 | LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL; | ||
1019 | |||
1020 | // Don't leak | ||
1021 | delete region; | ||
1022 | } | ||
1023 | |||
1024 | return name; | ||
1025 | } | ||
1026 | |||
1027 | void LLPluginProcessParent::removeSharedMemory(const std::string &name) | ||
1028 | { | ||
1029 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
1030 | |||
1031 | if(iter != mSharedMemoryRegions.end()) | ||
1032 | { | ||
1033 | // This segment exists. Send the message to the child to unmap it. The response will cause the parent to unmap our end. | ||
1034 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove"); | ||
1035 | message.setValue("name", name); | ||
1036 | sendMessage(message); | ||
1037 | } | ||
1038 | else | ||
1039 | { | ||
1040 | LL_WARNS("Plugin") << "Request to remove an unknown shared memory segment." << LL_ENDL; | ||
1041 | } | ||
1042 | } | ||
1043 | size_t LLPluginProcessParent::getSharedMemorySize(const std::string &name) | ||
1044 | { | ||
1045 | size_t result = 0; | ||
1046 | |||
1047 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
1048 | if(iter != mSharedMemoryRegions.end()) | ||
1049 | { | ||
1050 | result = iter->second->getSize(); | ||
1051 | } | ||
1052 | |||
1053 | return result; | ||
1054 | } | ||
1055 | void *LLPluginProcessParent::getSharedMemoryAddress(const std::string &name) | ||
1056 | { | ||
1057 | void *result = NULL; | ||
1058 | |||
1059 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
1060 | if(iter != mSharedMemoryRegions.end()) | ||
1061 | { | ||
1062 | result = iter->second->getMappedAddress(); | ||
1063 | } | ||
1064 | |||
1065 | return result; | ||
1066 | } | ||
1067 | |||
1068 | std::string LLPluginProcessParent::getMessageClassVersion(const std::string &message_class) | ||
1069 | { | ||
1070 | std::string result; | ||
1071 | |||
1072 | if(mMessageClassVersions.has(message_class)) | ||
1073 | { | ||
1074 | result = mMessageClassVersions[message_class].asString(); | ||
1075 | } | ||
1076 | |||
1077 | return result; | ||
1078 | } | ||
1079 | |||
1080 | std::string LLPluginProcessParent::getPluginVersion(void) | ||
1081 | { | ||
1082 | return mPluginVersionString; | ||
1083 | } | ||
1084 | |||
1085 | void LLPluginProcessParent::setState(EState state) | ||
1086 | { | ||
1087 | LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL; | ||
1088 | mState = state; | ||
1089 | }; | ||
1090 | |||
1091 | bool LLPluginProcessParent::pluginLockedUpOrQuit() | ||
1092 | { | ||
1093 | bool result = false; | ||
1094 | |||
1095 | if(!mProcess.isRunning()) | ||
1096 | { | ||
1097 | LL_WARNS("Plugin") << "child exited" << LL_ENDL; | ||
1098 | result = true; | ||
1099 | } | ||
1100 | else if(pluginLockedUp()) | ||
1101 | { | ||
1102 | LL_WARNS("Plugin") << "timeout" << LL_ENDL; | ||
1103 | result = true; | ||
1104 | } | ||
1105 | |||
1106 | return result; | ||
1107 | } | ||
1108 | |||
1109 | bool LLPluginProcessParent::pluginLockedUp() | ||
1110 | { | ||
1111 | if(mDisableTimeout || mDebug || mBlocked) | ||
1112 | { | ||
1113 | // Never time out a plugin process in these cases. | ||
1114 | return false; | ||
1115 | } | ||
1116 | |||
1117 | // If the timer is running and has expired, the plugin has locked up. | ||
1118 | return (mHeartbeat.getStarted() && mHeartbeat.hasExpired()); | ||
1119 | } | ||
1120 | |||