PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llplugin/llpluginprocesschild.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 563 lines | 397 code | 84 blank | 82 comment | 74 complexity | 95cd6bd7cb2d25d1bc36c02dcdc9ba4e MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llpluginprocesschild.cpp
  3. * @brief LLPluginProcessChild handles the child side of the external-process plugin API.
  4. *
  5. * @cond
  6. * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2010, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. * @endcond
  27. */
  28. #include "linden_common.h"
  29. #include "llpluginprocesschild.h"
  30. #include "llplugininstance.h"
  31. #include "llpluginmessagepipe.h"
  32. #include "llpluginmessageclasses.h"
  33. static const F32 HEARTBEAT_SECONDS = 1.0f;
  34. static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f; // Each call to idle will give the plugin this much time.
  35. LLPluginProcessChild::LLPluginProcessChild()
  36. {
  37. mState = STATE_UNINITIALIZED;
  38. mInstance = NULL;
  39. mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
  40. mSleepTime = PLUGIN_IDLE_SECONDS; // default: send idle messages at 100Hz
  41. mCPUElapsed = 0.0f;
  42. mBlockingRequest = false;
  43. mBlockingResponseReceived = false;
  44. }
  45. LLPluginProcessChild::~LLPluginProcessChild()
  46. {
  47. if(mInstance != NULL)
  48. {
  49. sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
  50. // IMPORTANT: under some (unknown) circumstances the apr_dso_unload() triggered when mInstance is deleted
  51. // appears to fail and lock up which means that a given instance of the slplugin process never exits.
  52. // This is bad, especially when users try to update their version of SL - it fails because the slplugin
  53. // process as well as a bunch of plugin specific files are locked and cannot be overwritten.
  54. exit( 0 );
  55. //delete mInstance;
  56. //mInstance = NULL;
  57. }
  58. }
  59. void LLPluginProcessChild::killSockets(void)
  60. {
  61. killMessagePipe();
  62. mSocket.reset();
  63. }
  64. void LLPluginProcessChild::init(U32 launcher_port)
  65. {
  66. mLauncherHost = LLHost("127.0.0.1", launcher_port);
  67. setState(STATE_INITIALIZED);
  68. }
  69. void LLPluginProcessChild::idle(void)
  70. {
  71. bool idle_again;
  72. do
  73. {
  74. if(APR_STATUS_IS_EOF(mSocketError))
  75. {
  76. // Plugin socket was closed. This covers both normal plugin termination and host crashes.
  77. setState(STATE_ERROR);
  78. }
  79. else if(mSocketError != APR_SUCCESS)
  80. {
  81. LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR"<< LL_ENDL;
  82. setState(STATE_ERROR);
  83. }
  84. if((mState > STATE_INITIALIZED) && (mMessagePipe == NULL))
  85. {
  86. // The pipe has been closed -- we're done.
  87. // TODO: This could be slightly more subtle, but I'm not sure it needs to be.
  88. LL_INFOS("Plugin") << "message pipe went away, moving to STATE_ERROR"<< LL_ENDL;
  89. setState(STATE_ERROR);
  90. }
  91. // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState().
  92. // 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.
  93. // When in doubt, don't do it.
  94. idle_again = false;
  95. if(mInstance != NULL)
  96. {
  97. // Provide some time to the plugin
  98. mInstance->idle();
  99. }
  100. switch(mState)
  101. {
  102. case STATE_UNINITIALIZED:
  103. break;
  104. case STATE_INITIALIZED:
  105. if(mSocket->blockingConnect(mLauncherHost))
  106. {
  107. // This automatically sets mMessagePipe
  108. new LLPluginMessagePipe(this, mSocket);
  109. setState(STATE_CONNECTED);
  110. }
  111. else
  112. {
  113. // connect failed
  114. setState(STATE_ERROR);
  115. }
  116. break;
  117. case STATE_CONNECTED:
  118. sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hello"));
  119. setState(STATE_PLUGIN_LOADING);
  120. break;
  121. case STATE_PLUGIN_LOADING:
  122. if(!mPluginFile.empty())
  123. {
  124. mInstance = new LLPluginInstance(this);
  125. if(mInstance->load(mPluginDir, mPluginFile) == 0)
  126. {
  127. mHeartbeat.start();
  128. mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
  129. mCPUElapsed = 0.0f;
  130. setState(STATE_PLUGIN_LOADED);
  131. }
  132. else
  133. {
  134. setState(STATE_ERROR);
  135. }
  136. }
  137. break;
  138. case STATE_PLUGIN_LOADED:
  139. {
  140. setState(STATE_PLUGIN_INITIALIZING);
  141. LLPluginMessage message("base", "init");
  142. sendMessageToPlugin(message);
  143. }
  144. break;
  145. case STATE_PLUGIN_INITIALIZING:
  146. // waiting for init_response...
  147. break;
  148. case STATE_RUNNING:
  149. if(mInstance != NULL)
  150. {
  151. // Provide some time to the plugin
  152. LLPluginMessage message("base", "idle");
  153. message.setValueReal("time", PLUGIN_IDLE_SECONDS);
  154. sendMessageToPlugin(message);
  155. mInstance->idle();
  156. if(mHeartbeat.hasExpired())
  157. {
  158. // This just proves that we're not stuck down inside the plugin code.
  159. LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat");
  160. // Calculate the approximage CPU usage fraction (floating point value between 0 and 1) used by the plugin this heartbeat cycle.
  161. // Note that this will not take into account any threads or additional processes the plugin spawns, but it's a first approximation.
  162. // If we could write OS-specific functions to query the actual CPU usage of this process, that would be a better approximation.
  163. heartbeat.setValueReal("cpu_usage", mCPUElapsed / mHeartbeat.getElapsedTimeF64());
  164. sendMessageToParent(heartbeat);
  165. mHeartbeat.reset();
  166. mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
  167. mCPUElapsed = 0.0f;
  168. }
  169. }
  170. // receivePluginMessage will transition to STATE_UNLOADING
  171. break;
  172. case STATE_UNLOADING:
  173. if(mInstance != NULL)
  174. {
  175. sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
  176. delete mInstance;
  177. mInstance = NULL;
  178. }
  179. setState(STATE_UNLOADED);
  180. break;
  181. case STATE_UNLOADED:
  182. killSockets();
  183. setState(STATE_DONE);
  184. break;
  185. case STATE_ERROR:
  186. // Close the socket to the launcher
  187. killSockets();
  188. // TODO: Where do we go from here? Just exit()?
  189. setState(STATE_DONE);
  190. break;
  191. case STATE_DONE:
  192. // just sit here.
  193. break;
  194. }
  195. } while (idle_again);
  196. }
  197. void LLPluginProcessChild::sleep(F64 seconds)
  198. {
  199. deliverQueuedMessages();
  200. if(mMessagePipe)
  201. {
  202. mMessagePipe->pump(seconds);
  203. }
  204. else
  205. {
  206. ms_sleep((int)(seconds * 1000.0f));
  207. }
  208. }
  209. void LLPluginProcessChild::pump(void)
  210. {
  211. deliverQueuedMessages();
  212. if(mMessagePipe)
  213. {
  214. mMessagePipe->pump(0.0f);
  215. }
  216. else
  217. {
  218. // Should we warn here?
  219. }
  220. }
  221. bool LLPluginProcessChild::isRunning(void)
  222. {
  223. bool result = false;
  224. if(mState == STATE_RUNNING)
  225. result = true;
  226. return result;
  227. }
  228. bool LLPluginProcessChild::isDone(void)
  229. {
  230. bool result = false;
  231. switch(mState)
  232. {
  233. case STATE_DONE:
  234. result = true;
  235. break;
  236. default:
  237. break;
  238. }
  239. return result;
  240. }
  241. void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message)
  242. {
  243. if (mInstance)
  244. {
  245. std::string buffer = message.generate();
  246. LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
  247. LLTimer elapsed;
  248. mInstance->sendMessage(buffer);
  249. mCPUElapsed += elapsed.getElapsedTimeF64();
  250. }
  251. else
  252. {
  253. LL_WARNS("Plugin") << "mInstance == NULL" << LL_ENDL;
  254. }
  255. }
  256. void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message)
  257. {
  258. std::string buffer = message.generate();
  259. LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL;
  260. writeMessageRaw(buffer);
  261. }
  262. void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
  263. {
  264. // Incoming message from the TCP Socket
  265. LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL;
  266. // Decode this message
  267. LLPluginMessage parsed;
  268. parsed.parse(message);
  269. if(mBlockingRequest)
  270. {
  271. // We're blocking the plugin waiting for a response.
  272. if(parsed.hasValue("blocking_response"))
  273. {
  274. // This is the message we've been waiting for -- fall through and send it immediately.
  275. mBlockingResponseReceived = true;
  276. }
  277. else
  278. {
  279. // Still waiting. Queue this message and don't process it yet.
  280. mMessageQueue.push(message);
  281. return;
  282. }
  283. }
  284. bool passMessage = true;
  285. // FIXME: how should we handle queueing here?
  286. {
  287. std::string message_class = parsed.getClass();
  288. if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
  289. {
  290. passMessage = false;
  291. std::string message_name = parsed.getName();
  292. if(message_name == "load_plugin")
  293. {
  294. mPluginFile = parsed.getValue("file");
  295. mPluginDir = parsed.getValue("dir");
  296. }
  297. else if(message_name == "shm_add")
  298. {
  299. std::string name = parsed.getValue("name");
  300. size_t size = (size_t)parsed.getValueS32("size");
  301. sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
  302. if(iter != mSharedMemoryRegions.end())
  303. {
  304. // Need to remove the old region first
  305. LL_WARNS("Plugin") << "Adding a duplicate shared memory segment!" << LL_ENDL;
  306. }
  307. else
  308. {
  309. // This is a new region
  310. LLPluginSharedMemory *region = new LLPluginSharedMemory;
  311. if(region->attach(name, size))
  312. {
  313. mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region));
  314. std::stringstream addr;
  315. addr << region->getMappedAddress();
  316. // Send the add notification to the plugin
  317. LLPluginMessage message("base", "shm_added");
  318. message.setValue("name", name);
  319. message.setValueS32("size", (S32)size);
  320. message.setValuePointer("address", region->getMappedAddress());
  321. sendMessageToPlugin(message);
  322. // and send the response to the parent
  323. message.setMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add_response");
  324. message.setValue("name", name);
  325. sendMessageToParent(message);
  326. }
  327. else
  328. {
  329. LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL;
  330. delete region;
  331. }
  332. }
  333. }
  334. else if(message_name == "shm_remove")
  335. {
  336. std::string name = parsed.getValue("name");
  337. sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
  338. if(iter != mSharedMemoryRegions.end())
  339. {
  340. // forward the remove request to the plugin -- its response will trigger us to detach the segment.
  341. LLPluginMessage message("base", "shm_remove");
  342. message.setValue("name", name);
  343. sendMessageToPlugin(message);
  344. }
  345. else
  346. {
  347. LL_WARNS("Plugin") << "shm_remove for unknown memory segment!" << LL_ENDL;
  348. }
  349. }
  350. else if(message_name == "sleep_time")
  351. {
  352. mSleepTime = llmax(parsed.getValueReal("time"), 1.0 / 100.0); // clamp to maximum of 100Hz
  353. }
  354. else if(message_name == "crash")
  355. {
  356. // Crash the plugin
  357. LL_ERRS("Plugin") << "Plugin crash requested." << LL_ENDL;
  358. }
  359. else if(message_name == "hang")
  360. {
  361. // Hang the plugin
  362. LL_WARNS("Plugin") << "Plugin hang requested." << LL_ENDL;
  363. while(1)
  364. {
  365. // wheeeeeeeee......
  366. }
  367. }
  368. else
  369. {
  370. LL_WARNS("Plugin") << "Unknown internal message from parent: " << message_name << LL_ENDL;
  371. }
  372. }
  373. }
  374. if(passMessage && mInstance != NULL)
  375. {
  376. LLTimer elapsed;
  377. mInstance->sendMessage(message);
  378. mCPUElapsed += elapsed.getElapsedTimeF64();
  379. }
  380. }
  381. /* virtual */
  382. void LLPluginProcessChild::receivePluginMessage(const std::string &message)
  383. {
  384. LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL;
  385. if(mBlockingRequest)
  386. {
  387. //
  388. LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL;
  389. }
  390. // Incoming message from the plugin instance
  391. bool passMessage = true;
  392. // FIXME: how should we handle queueing here?
  393. // Intercept certain base messages (responses to ones sent by this class)
  394. {
  395. // Decode this message
  396. LLPluginMessage parsed;
  397. parsed.parse(message);
  398. if(parsed.hasValue("blocking_request"))
  399. {
  400. mBlockingRequest = true;
  401. }
  402. std::string message_class = parsed.getClass();
  403. if(message_class == "base")
  404. {
  405. std::string message_name = parsed.getName();
  406. if(message_name == "init_response")
  407. {
  408. // The plugin has finished initializing.
  409. setState(STATE_RUNNING);
  410. // Don't pass this message up to the parent
  411. passMessage = false;
  412. LLPluginMessage new_message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin_response");
  413. LLSD versions = parsed.getValueLLSD("versions");
  414. new_message.setValueLLSD("versions", versions);
  415. if(parsed.hasValue("plugin_version"))
  416. {
  417. std::string plugin_version = parsed.getValue("plugin_version");
  418. new_message.setValueLLSD("plugin_version", plugin_version);
  419. }
  420. // Let the parent know it's loaded and initialized.
  421. sendMessageToParent(new_message);
  422. }
  423. else if(message_name == "shm_remove_response")
  424. {
  425. // Don't pass this message up to the parent
  426. passMessage = false;
  427. std::string name = parsed.getValue("name");
  428. sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
  429. if(iter != mSharedMemoryRegions.end())
  430. {
  431. // detach the shared memory region
  432. iter->second->detach();
  433. // and remove it from our map
  434. mSharedMemoryRegions.erase(iter);
  435. // Finally, send the response to the parent.
  436. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove_response");
  437. message.setValue("name", name);
  438. sendMessageToParent(message);
  439. }
  440. else
  441. {
  442. LL_WARNS("Plugin") << "shm_remove_response for unknown memory segment!" << LL_ENDL;
  443. }
  444. }
  445. }
  446. }
  447. if(passMessage)
  448. {
  449. LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL;
  450. writeMessageRaw(message);
  451. }
  452. while(mBlockingRequest)
  453. {
  454. // The plugin wants to block and wait for a response to this message.
  455. sleep(mSleepTime); // this will pump the message pipe and process messages
  456. if(mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL))
  457. {
  458. // Response has been received, or we've hit an error state. Stop waiting.
  459. mBlockingRequest = false;
  460. mBlockingResponseReceived = false;
  461. }
  462. }
  463. }
  464. void LLPluginProcessChild::setState(EState state)
  465. {
  466. LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
  467. mState = state;
  468. };
  469. void LLPluginProcessChild::deliverQueuedMessages()
  470. {
  471. if(!mBlockingRequest)
  472. {
  473. while(!mMessageQueue.empty())
  474. {
  475. receiveMessageRaw(mMessageQueue.front());
  476. mMessageQueue.pop();
  477. }
  478. }
  479. }