diff options
author | Armin Weatherwax | 2010-06-14 12:04:49 +0200 |
---|---|---|
committer | Armin Weatherwax | 2010-09-23 15:38:25 +0200 |
commit | 35df5441d3e2789663532c948731aff3a1e04728 (patch) | |
tree | ac7674289784a5f96106ea507637055a8dada78a /linden/indra/llplugin/llpluginprocessparent.cpp | |
parent | Changed version to Experimental 2010.09.18 (diff) | |
download | meta-impy-35df5441d3e2789663532c948731aff3a1e04728.zip meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.gz meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.bz2 meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.xz |
llmediaplugins first step
Diffstat (limited to '')
-rw-r--r-- | linden/indra/llplugin/llpluginprocessparent.cpp | 714 |
1 files changed, 714 insertions, 0 deletions
diff --git a/linden/indra/llplugin/llpluginprocessparent.cpp b/linden/indra/llplugin/llpluginprocessparent.cpp new file mode 100644 index 0000000..bd36d11 --- /dev/null +++ b/linden/indra/llplugin/llpluginprocessparent.cpp | |||
@@ -0,0 +1,714 @@ | |||
1 | /** | ||
2 | * @file llpluginprocessparent.cpp | ||
3 | * @brief LLPluginProcessParent handles the parent side of the external-process plugin API. | ||
4 | * | ||
5 | * $LicenseInfo:firstyear=2008&license=viewergpl$ | ||
6 | * | ||
7 | * Copyright (c) 2008-2009, Linden Research, Inc. | ||
8 | * | ||
9 | * Second Life Viewer Source Code | ||
10 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
11 | * to you under the terms of the GNU General Public License, version 2.0 | ||
12 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
13 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
15 | * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 | ||
16 | * | ||
17 | * There are special exceptions to the terms and conditions of the GPL as | ||
18 | * it is applied to this Source Code. View the full text of the exception | ||
19 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
20 | * online at | ||
21 | * http://secondlifegrid.net/programs/open_source/licensing/flossexception | ||
22 | * | ||
23 | * By copying, modifying or distributing this software, you acknowledge | ||
24 | * that you have read and understood your obligations described above, | ||
25 | * and agree to abide by those obligations. | ||
26 | * | ||
27 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
28 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
29 | * COMPLETENESS OR PERFORMANCE. | ||
30 | * $/LicenseInfo$ | ||
31 | */ | ||
32 | |||
33 | #include "linden_common.h" | ||
34 | |||
35 | #include "llpluginprocessparent.h" | ||
36 | #include "llpluginmessagepipe.h" | ||
37 | #include "llpluginmessageclasses.h" | ||
38 | |||
39 | #include "llapr.h" | ||
40 | |||
41 | //virtual | ||
42 | LLPluginProcessParentOwner::~LLPluginProcessParentOwner() | ||
43 | { | ||
44 | |||
45 | } | ||
46 | |||
47 | LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner) | ||
48 | { | ||
49 | mOwner = owner; | ||
50 | mBoundPort = 0; | ||
51 | mState = STATE_UNINITIALIZED; | ||
52 | mDisableTimeout = false; | ||
53 | mDebug = false; | ||
54 | |||
55 | mPluginLaunchTimeout = 60.0f; | ||
56 | mPluginLockupTimeout = 30.0f; | ||
57 | |||
58 | // Don't start the timer here -- start it when we actually launch the plugin process. | ||
59 | mHeartbeat.stop(); | ||
60 | } | ||
61 | |||
62 | LLPluginProcessParent::~LLPluginProcessParent() | ||
63 | { | ||
64 | LL_DEBUGS("Plugin") << "destructor" << LL_ENDL; | ||
65 | |||
66 | // Destroy any remaining shared memory regions | ||
67 | sharedMemoryRegionsType::iterator iter; | ||
68 | while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end()) | ||
69 | { | ||
70 | // destroy the shared memory region | ||
71 | iter->second->destroy(); | ||
72 | |||
73 | // and remove it from our map | ||
74 | mSharedMemoryRegions.erase(iter); | ||
75 | } | ||
76 | |||
77 | // orphaning the process means it won't be killed when the LLProcessLauncher is destructed. | ||
78 | // This is what we want -- it should exit cleanly once it notices the sockets have been closed. | ||
79 | mProcess.orphan(); | ||
80 | killSockets(); | ||
81 | } | ||
82 | |||
83 | void LLPluginProcessParent::killSockets(void) | ||
84 | { | ||
85 | killMessagePipe(); | ||
86 | mListenSocket.reset(); | ||
87 | mSocket.reset(); | ||
88 | } | ||
89 | |||
90 | void LLPluginProcessParent::errorState(void) | ||
91 | { | ||
92 | if(mState < STATE_RUNNING) | ||
93 | setState(STATE_LAUNCH_FAILURE); | ||
94 | else | ||
95 | setState(STATE_ERROR); | ||
96 | } | ||
97 | |||
98 | void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path) | ||
99 | { | ||
100 | mProcess.setExecutable(launcher_filename); | ||
101 | mPluginFile = plugin_filename; | ||
102 | mCPUUsage = 0.0f; | ||
103 | mDebug = debug; | ||
104 | mUserDataPath = user_data_path; | ||
105 | |||
106 | setState(STATE_INITIALIZED); | ||
107 | } | ||
108 | |||
109 | bool LLPluginProcessParent::accept() | ||
110 | { | ||
111 | bool result = false; | ||
112 | |||
113 | apr_status_t status = APR_EGENERAL; | ||
114 | apr_socket_t *new_socket = NULL; | ||
115 | |||
116 | status = apr_socket_accept( | ||
117 | &new_socket, | ||
118 | mListenSocket->getSocket(), | ||
119 | gAPRPoolp); | ||
120 | |||
121 | |||
122 | if(status == APR_SUCCESS) | ||
123 | { | ||
124 | // llinfos << "SUCCESS" << llendl; | ||
125 | // Success. Create a message pipe on the new socket | ||
126 | |||
127 | // we MUST create a new pool for the LLSocket, since it will take ownership of it and delete it in its destructor! | ||
128 | apr_pool_t* new_pool = NULL; | ||
129 | status = apr_pool_create(&new_pool, gAPRPoolp); | ||
130 | |||
131 | mSocket = LLSocket::create(new_socket, new_pool); | ||
132 | new LLPluginMessagePipe(this, mSocket); | ||
133 | |||
134 | result = true; | ||
135 | } | ||
136 | else if(APR_STATUS_IS_EAGAIN(status)) | ||
137 | { | ||
138 | // llinfos << "EAGAIN" << llendl; | ||
139 | |||
140 | // No incoming connections. This is not an error. | ||
141 | status = APR_SUCCESS; | ||
142 | } | ||
143 | else | ||
144 | { | ||
145 | // llinfos << "Error:" << llendl; | ||
146 | ll_apr_warn_status(status); | ||
147 | |||
148 | // Some other error. | ||
149 | errorState(); | ||
150 | } | ||
151 | |||
152 | return result; | ||
153 | } | ||
154 | |||
155 | void LLPluginProcessParent::idle(void) | ||
156 | { | ||
157 | bool idle_again; | ||
158 | |||
159 | do | ||
160 | { | ||
161 | // Give time to network processing | ||
162 | if(mMessagePipe) | ||
163 | { | ||
164 | if(!mMessagePipe->pump()) | ||
165 | { | ||
166 | // LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL; | ||
167 | errorState(); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | if((mSocketError != APR_SUCCESS) && (mState <= STATE_RUNNING)) | ||
172 | { | ||
173 | // The socket is in an error state -- the plugin is gone. | ||
174 | LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; | ||
175 | errorState(); | ||
176 | } | ||
177 | |||
178 | // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). | ||
179 | // 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. | ||
180 | // When in doubt, don't do it. | ||
181 | idle_again = false; | ||
182 | switch(mState) | ||
183 | { | ||
184 | case STATE_UNINITIALIZED: | ||
185 | break; | ||
186 | |||
187 | case STATE_INITIALIZED: | ||
188 | { | ||
189 | |||
190 | apr_status_t status = APR_SUCCESS; | ||
191 | apr_sockaddr_t* addr = NULL; | ||
192 | mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); | ||
193 | mBoundPort = 0; | ||
194 | |||
195 | // This code is based on parts of LLSocket::create() in lliosocket.cpp. | ||
196 | |||
197 | status = apr_sockaddr_info_get( | ||
198 | &addr, | ||
199 | "127.0.0.1", | ||
200 | APR_INET, | ||
201 | 0, // port 0 = ephemeral ("find me a port") | ||
202 | 0, | ||
203 | gAPRPoolp); | ||
204 | |||
205 | if(ll_apr_warn_status(status)) | ||
206 | { | ||
207 | killSockets(); | ||
208 | errorState(); | ||
209 | break; | ||
210 | } | ||
211 | |||
212 | // This allows us to reuse the address on quick down/up. This is unlikely to create problems. | ||
213 | ll_apr_warn_status(apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_REUSEADDR, 1)); | ||
214 | |||
215 | status = apr_socket_bind(mListenSocket->getSocket(), addr); | ||
216 | if(ll_apr_warn_status(status)) | ||
217 | { | ||
218 | killSockets(); | ||
219 | errorState(); | ||
220 | break; | ||
221 | } | ||
222 | |||
223 | // Get the actual port the socket was bound to | ||
224 | { | ||
225 | apr_sockaddr_t* bound_addr = NULL; | ||
226 | if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) | ||
227 | { | ||
228 | killSockets(); | ||
229 | errorState(); | ||
230 | break; | ||
231 | } | ||
232 | mBoundPort = bound_addr->port; | ||
233 | |||
234 | if(mBoundPort == 0) | ||
235 | { | ||
236 | LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; | ||
237 | |||
238 | killSockets(); | ||
239 | errorState(); | ||
240 | break; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | LL_DEBUGS("Plugin") << "Bound tcp socket to port: " << addr->port << LL_ENDL; | ||
245 | |||
246 | // Make the listen socket non-blocking | ||
247 | status = apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_NONBLOCK, 1); | ||
248 | if(ll_apr_warn_status(status)) | ||
249 | { | ||
250 | killSockets(); | ||
251 | errorState(); | ||
252 | break; | ||
253 | } | ||
254 | |||
255 | apr_socket_timeout_set(mListenSocket->getSocket(), 0); | ||
256 | if(ll_apr_warn_status(status)) | ||
257 | { | ||
258 | killSockets(); | ||
259 | errorState(); | ||
260 | break; | ||
261 | } | ||
262 | |||
263 | // If it's a stream based socket, we need to tell the OS | ||
264 | // to keep a queue of incoming connections for ACCEPT. | ||
265 | status = apr_socket_listen( | ||
266 | mListenSocket->getSocket(), | ||
267 | 10); // FIXME: Magic number for queue size | ||
268 | |||
269 | if(ll_apr_warn_status(status)) | ||
270 | { | ||
271 | killSockets(); | ||
272 | errorState(); | ||
273 | break; | ||
274 | } | ||
275 | |||
276 | // If we got here, we're listening. | ||
277 | setState(STATE_LISTENING); | ||
278 | } | ||
279 | break; | ||
280 | |||
281 | case STATE_LISTENING: | ||
282 | { | ||
283 | // Launch the plugin process. | ||
284 | |||
285 | // Only argument to the launcher is the port number we're listening on | ||
286 | std::stringstream stream; | ||
287 | stream << mBoundPort; | ||
288 | mProcess.addArgument(stream.str()); | ||
289 | if(mProcess.launch() != 0) | ||
290 | { | ||
291 | errorState(); | ||
292 | } | ||
293 | else | ||
294 | { | ||
295 | if(mDebug) | ||
296 | { | ||
297 | #if LL_DARWIN | ||
298 | // 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. | ||
299 | |||
300 | // The command we're constructing would look like this on the command line: | ||
301 | // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' | ||
302 | |||
303 | std::stringstream cmd; | ||
304 | |||
305 | mDebugger.setExecutable("/usr/bin/osascript"); | ||
306 | mDebugger.addArgument("-e"); | ||
307 | mDebugger.addArgument("tell application \"Terminal\""); | ||
308 | mDebugger.addArgument("-e"); | ||
309 | cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\""; | ||
310 | mDebugger.addArgument(cmd.str()); | ||
311 | mDebugger.addArgument("-e"); | ||
312 | mDebugger.addArgument("do script \"continue\" in win"); | ||
313 | mDebugger.addArgument("-e"); | ||
314 | mDebugger.addArgument("end tell"); | ||
315 | mDebugger.launch(); | ||
316 | |||
317 | #endif | ||
318 | } | ||
319 | |||
320 | // This will allow us to time out if the process never starts. | ||
321 | mHeartbeat.start(); | ||
322 | mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); | ||
323 | setState(STATE_LAUNCHED); | ||
324 | } | ||
325 | } | ||
326 | break; | ||
327 | |||
328 | case STATE_LAUNCHED: | ||
329 | // waiting for the plugin to connect | ||
330 | if(pluginLockedUpOrQuit()) | ||
331 | { | ||
332 | errorState(); | ||
333 | } | ||
334 | else | ||
335 | { | ||
336 | // Check for the incoming connection. | ||
337 | if(accept()) | ||
338 | { | ||
339 | // Stop listening on the server port | ||
340 | mListenSocket.reset(); | ||
341 | setState(STATE_CONNECTED); | ||
342 | } | ||
343 | } | ||
344 | break; | ||
345 | |||
346 | case STATE_CONNECTED: | ||
347 | // waiting for hello message from the plugin | ||
348 | |||
349 | if(pluginLockedUpOrQuit()) | ||
350 | { | ||
351 | errorState(); | ||
352 | } | ||
353 | break; | ||
354 | |||
355 | case STATE_HELLO: | ||
356 | LL_DEBUGS("Plugin") << "received hello message" << llendl; | ||
357 | |||
358 | // Send the message to load the plugin | ||
359 | { | ||
360 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin"); | ||
361 | message.setValue("file", mPluginFile); | ||
362 | message.setValue("user_data_path", mUserDataPath); | ||
363 | sendMessage(message); | ||
364 | } | ||
365 | |||
366 | setState(STATE_LOADING); | ||
367 | break; | ||
368 | |||
369 | case STATE_LOADING: | ||
370 | // The load_plugin_response message will kick us from here into STATE_RUNNING | ||
371 | if(pluginLockedUpOrQuit()) | ||
372 | { | ||
373 | errorState(); | ||
374 | } | ||
375 | break; | ||
376 | |||
377 | case STATE_RUNNING: | ||
378 | if(pluginLockedUpOrQuit()) | ||
379 | { | ||
380 | errorState(); | ||
381 | } | ||
382 | break; | ||
383 | |||
384 | case STATE_EXITING: | ||
385 | if(!mProcess.isRunning()) | ||
386 | { | ||
387 | setState(STATE_CLEANUP); | ||
388 | } | ||
389 | else if(pluginLockedUp()) | ||
390 | { | ||
391 | LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl; | ||
392 | errorState(); | ||
393 | } | ||
394 | break; | ||
395 | |||
396 | case STATE_LAUNCH_FAILURE: | ||
397 | if(mOwner != NULL) | ||
398 | { | ||
399 | mOwner->pluginLaunchFailed(); | ||
400 | } | ||
401 | setState(STATE_CLEANUP); | ||
402 | break; | ||
403 | |||
404 | case STATE_ERROR: | ||
405 | if(mOwner != NULL) | ||
406 | { | ||
407 | mOwner->pluginDied(); | ||
408 | } | ||
409 | setState(STATE_CLEANUP); | ||
410 | break; | ||
411 | |||
412 | case STATE_CLEANUP: | ||
413 | // Don't do a kill here anymore -- closing the sockets is the new 'kill'. | ||
414 | mProcess.orphan(); | ||
415 | killSockets(); | ||
416 | setState(STATE_DONE); | ||
417 | break; | ||
418 | |||
419 | |||
420 | case STATE_DONE: | ||
421 | // just sit here. | ||
422 | break; | ||
423 | |||
424 | } | ||
425 | |||
426 | } while (idle_again); | ||
427 | } | ||
428 | |||
429 | bool LLPluginProcessParent::isLoading(void) | ||
430 | { | ||
431 | bool result = false; | ||
432 | |||
433 | if(mState <= STATE_LOADING) | ||
434 | result = true; | ||
435 | |||
436 | return result; | ||
437 | } | ||
438 | |||
439 | bool LLPluginProcessParent::isRunning(void) | ||
440 | { | ||
441 | bool result = false; | ||
442 | |||
443 | if(mState == STATE_RUNNING) | ||
444 | result = true; | ||
445 | |||
446 | return result; | ||
447 | } | ||
448 | |||
449 | bool LLPluginProcessParent::isDone(void) | ||
450 | { | ||
451 | bool result = false; | ||
452 | |||
453 | if(mState == STATE_DONE) | ||
454 | result = true; | ||
455 | |||
456 | return result; | ||
457 | } | ||
458 | |||
459 | void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send) | ||
460 | { | ||
461 | if(force_send || (sleep_time != mSleepTime)) | ||
462 | { | ||
463 | // Cache the time locally | ||
464 | mSleepTime = sleep_time; | ||
465 | |||
466 | if(canSendMessage()) | ||
467 | { | ||
468 | // and send to the plugin. | ||
469 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "sleep_time"); | ||
470 | message.setValueReal("time", mSleepTime); | ||
471 | sendMessage(message); | ||
472 | } | ||
473 | else | ||
474 | { | ||
475 | // Too early to send -- the load_plugin_response message will trigger us to send mSleepTime later. | ||
476 | } | ||
477 | } | ||
478 | } | ||
479 | |||
480 | void LLPluginProcessParent::sendMessage(const LLPluginMessage &message) | ||
481 | { | ||
482 | |||
483 | std::string buffer = message.generate(); | ||
484 | LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL; | ||
485 | writeMessageRaw(buffer); | ||
486 | } | ||
487 | |||
488 | |||
489 | void LLPluginProcessParent::receiveMessageRaw(const std::string &message) | ||
490 | { | ||
491 | LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL; | ||
492 | |||
493 | // FIXME: should this go into a queue instead? | ||
494 | |||
495 | LLPluginMessage parsed; | ||
496 | if(parsed.parse(message) != -1) | ||
497 | { | ||
498 | receiveMessage(parsed); | ||
499 | } | ||
500 | } | ||
501 | |||
502 | void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) | ||
503 | { | ||
504 | std::string message_class = message.getClass(); | ||
505 | if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) | ||
506 | { | ||
507 | // internal messages should be handled here | ||
508 | std::string message_name = message.getName(); | ||
509 | if(message_name == "hello") | ||
510 | { | ||
511 | if(mState == STATE_CONNECTED) | ||
512 | { | ||
513 | // Plugin host has launched. Tell it which plugin to load. | ||
514 | setState(STATE_HELLO); | ||
515 | } | ||
516 | else | ||
517 | { | ||
518 | LL_WARNS("Plugin") << "received hello message in wrong state -- bailing out" << LL_ENDL; | ||
519 | errorState(); | ||
520 | } | ||
521 | |||
522 | } | ||
523 | else if(message_name == "load_plugin_response") | ||
524 | { | ||
525 | if(mState == STATE_LOADING) | ||
526 | { | ||
527 | // Plugin has been loaded. | ||
528 | |||
529 | mPluginVersionString = message.getValue("plugin_version"); | ||
530 | LL_INFOS("Plugin") << "plugin version string: " << mPluginVersionString << LL_ENDL; | ||
531 | |||
532 | // Check which message classes/versions the plugin supports. | ||
533 | // TODO: check against current versions | ||
534 | // TODO: kill plugin on major mismatches? | ||
535 | mMessageClassVersions = message.getValueLLSD("versions"); | ||
536 | LLSD::map_iterator iter; | ||
537 | for(iter = mMessageClassVersions.beginMap(); iter != mMessageClassVersions.endMap(); iter++) | ||
538 | { | ||
539 | LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL; | ||
540 | } | ||
541 | |||
542 | // Send initial sleep time | ||
543 | setSleepTime(mSleepTime, true); | ||
544 | |||
545 | setState(STATE_RUNNING); | ||
546 | } | ||
547 | else | ||
548 | { | ||
549 | LL_WARNS("Plugin") << "received load_plugin_response message in wrong state -- bailing out" << LL_ENDL; | ||
550 | errorState(); | ||
551 | } | ||
552 | } | ||
553 | else if(message_name == "heartbeat") | ||
554 | { | ||
555 | // this resets our timer. | ||
556 | mHeartbeat.setTimerExpirySec(mPluginLockupTimeout); | ||
557 | |||
558 | mCPUUsage = message.getValueReal("cpu_usage"); | ||
559 | |||
560 | LL_DEBUGS("Plugin") << "cpu usage reported as " << mCPUUsage << LL_ENDL; | ||
561 | |||
562 | } | ||
563 | else if(message_name == "shm_add_response") | ||
564 | { | ||
565 | // Nothing to do here. | ||
566 | } | ||
567 | else if(message_name == "shm_remove_response") | ||
568 | { | ||
569 | std::string name = message.getValue("name"); | ||
570 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
571 | |||
572 | if(iter != mSharedMemoryRegions.end()) | ||
573 | { | ||
574 | // destroy the shared memory region | ||
575 | iter->second->destroy(); | ||
576 | |||
577 | // and remove it from our map | ||
578 | mSharedMemoryRegions.erase(iter); | ||
579 | } | ||
580 | } | ||
581 | else | ||
582 | { | ||
583 | LL_WARNS("Plugin") << "Unknown internal message from child: " << message_name << LL_ENDL; | ||
584 | } | ||
585 | } | ||
586 | else | ||
587 | { | ||
588 | if(mOwner != NULL) | ||
589 | { | ||
590 | mOwner->receivePluginMessage(message); | ||
591 | } | ||
592 | } | ||
593 | } | ||
594 | |||
595 | std::string LLPluginProcessParent::addSharedMemory(size_t size) | ||
596 | { | ||
597 | std::string name; | ||
598 | |||
599 | LLPluginSharedMemory *region = new LLPluginSharedMemory; | ||
600 | |||
601 | // This is a new region | ||
602 | if(region->create(size)) | ||
603 | { | ||
604 | name = region->getName(); | ||
605 | |||
606 | mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region)); | ||
607 | |||
608 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add"); | ||
609 | message.setValue("name", name); | ||
610 | message.setValueS32("size", (S32)size); | ||
611 | sendMessage(message); | ||
612 | } | ||
613 | else | ||
614 | { | ||
615 | LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL; | ||
616 | |||
617 | // Don't leak | ||
618 | delete region; | ||
619 | } | ||
620 | |||
621 | return name; | ||
622 | } | ||
623 | |||
624 | void LLPluginProcessParent::removeSharedMemory(const std::string &name) | ||
625 | { | ||
626 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
627 | |||
628 | if(iter != mSharedMemoryRegions.end()) | ||
629 | { | ||
630 | // This segment exists. Send the message to the child to unmap it. The response will cause the parent to unmap our end. | ||
631 | LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove"); | ||
632 | message.setValue("name", name); | ||
633 | sendMessage(message); | ||
634 | } | ||
635 | else | ||
636 | { | ||
637 | LL_WARNS("Plugin") << "Request to remove an unknown shared memory segment." << LL_ENDL; | ||
638 | } | ||
639 | } | ||
640 | size_t LLPluginProcessParent::getSharedMemorySize(const std::string &name) | ||
641 | { | ||
642 | size_t result = 0; | ||
643 | |||
644 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
645 | if(iter != mSharedMemoryRegions.end()) | ||
646 | { | ||
647 | result = iter->second->getSize(); | ||
648 | } | ||
649 | |||
650 | return result; | ||
651 | } | ||
652 | void *LLPluginProcessParent::getSharedMemoryAddress(const std::string &name) | ||
653 | { | ||
654 | void *result = NULL; | ||
655 | |||
656 | sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); | ||
657 | if(iter != mSharedMemoryRegions.end()) | ||
658 | { | ||
659 | result = iter->second->getMappedAddress(); | ||
660 | } | ||
661 | |||
662 | return result; | ||
663 | } | ||
664 | |||
665 | std::string LLPluginProcessParent::getMessageClassVersion(const std::string &message_class) | ||
666 | { | ||
667 | std::string result; | ||
668 | |||
669 | if(mMessageClassVersions.has(message_class)) | ||
670 | { | ||
671 | result = mMessageClassVersions[message_class].asString(); | ||
672 | } | ||
673 | |||
674 | return result; | ||
675 | } | ||
676 | |||
677 | std::string LLPluginProcessParent::getPluginVersion(void) | ||
678 | { | ||
679 | return mPluginVersionString; | ||
680 | } | ||
681 | |||
682 | void LLPluginProcessParent::setState(EState state) | ||
683 | { | ||
684 | LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL; | ||
685 | mState = state; | ||
686 | }; | ||
687 | |||
688 | bool LLPluginProcessParent::pluginLockedUpOrQuit() | ||
689 | { | ||
690 | bool result = false; | ||
691 | |||
692 | if(!mDisableTimeout && !mDebug) | ||
693 | { | ||
694 | if(!mProcess.isRunning()) | ||
695 | { | ||
696 | LL_WARNS("Plugin") << "child exited" << llendl; | ||
697 | result = true; | ||
698 | } | ||
699 | else if(pluginLockedUp()) | ||
700 | { | ||
701 | LL_WARNS("Plugin") << "timeout" << llendl; | ||
702 | result = true; | ||
703 | } | ||
704 | } | ||
705 | |||
706 | return result; | ||
707 | } | ||
708 | |||
709 | bool LLPluginProcessParent::pluginLockedUp() | ||
710 | { | ||
711 | // If the timer is running and has expired, the plugin has locked up. | ||
712 | return (mHeartbeat.getStarted() && mHeartbeat.hasExpired()); | ||
713 | } | ||
714 | |||