/indra/llplugin/llpluginmessagepipe.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 397 lines · 267 code · 61 blank · 69 comment · 49 complexity · 9ee41f6363fce8ee73499954780314f2 MD5 · raw file

  1. /**
  2. * @file llpluginmessagepipe.cpp
  3. * @brief Classes that implement connections from the plugin system to pipes/pumps.
  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 "llpluginmessagepipe.h"
  30. #include "llbufferstream.h"
  31. #include "llapr.h"
  32. static const char MESSAGE_DELIMITER = '\0';
  33. LLPluginMessagePipeOwner::LLPluginMessagePipeOwner() :
  34. mMessagePipe(NULL),
  35. mSocketError(APR_SUCCESS)
  36. {
  37. }
  38. // virtual
  39. LLPluginMessagePipeOwner::~LLPluginMessagePipeOwner()
  40. {
  41. killMessagePipe();
  42. }
  43. // virtual
  44. apr_status_t LLPluginMessagePipeOwner::socketError(apr_status_t error)
  45. {
  46. mSocketError = error;
  47. return error;
  48. };
  49. //virtual
  50. void LLPluginMessagePipeOwner::setMessagePipe(LLPluginMessagePipe *read_pipe)
  51. {
  52. // Save a reference to this pipe
  53. mMessagePipe = read_pipe;
  54. }
  55. bool LLPluginMessagePipeOwner::canSendMessage(void)
  56. {
  57. return (mMessagePipe != NULL);
  58. }
  59. bool LLPluginMessagePipeOwner::writeMessageRaw(const std::string &message)
  60. {
  61. bool result = true;
  62. if(mMessagePipe != NULL)
  63. {
  64. result = mMessagePipe->addMessage(message);
  65. }
  66. else
  67. {
  68. LL_WARNS("Plugin") << "dropping message: " << message << LL_ENDL;
  69. result = false;
  70. }
  71. return result;
  72. }
  73. void LLPluginMessagePipeOwner::killMessagePipe(void)
  74. {
  75. if(mMessagePipe != NULL)
  76. {
  77. delete mMessagePipe;
  78. mMessagePipe = NULL;
  79. }
  80. }
  81. LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket):
  82. mInputMutex(gAPRPoolp),
  83. mOutputMutex(gAPRPoolp),
  84. mOutputStartIndex(0),
  85. mOwner(owner),
  86. mSocket(socket)
  87. {
  88. mOwner->setMessagePipe(this);
  89. }
  90. LLPluginMessagePipe::~LLPluginMessagePipe()
  91. {
  92. if(mOwner != NULL)
  93. {
  94. mOwner->setMessagePipe(NULL);
  95. }
  96. }
  97. bool LLPluginMessagePipe::addMessage(const std::string &message)
  98. {
  99. // queue the message for later output
  100. LLMutexLock lock(&mOutputMutex);
  101. // If we're starting to use up too much memory, clear
  102. if (mOutputStartIndex > 1024 * 1024)
  103. {
  104. mOutput = mOutput.substr(mOutputStartIndex);
  105. mOutputStartIndex = 0;
  106. }
  107. mOutput += message;
  108. mOutput += MESSAGE_DELIMITER; // message separator
  109. return true;
  110. }
  111. void LLPluginMessagePipe::clearOwner(void)
  112. {
  113. // The owner is done with this pipe. The next call to process_impl should send any remaining data and exit.
  114. mOwner = NULL;
  115. }
  116. void LLPluginMessagePipe::setSocketTimeout(apr_interval_time_t timeout_usec)
  117. {
  118. // We never want to sleep forever, so force negative timeouts to become non-blocking.
  119. // according to this page: http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-13.html
  120. // blocking/non-blocking with apr sockets is somewhat non-portable.
  121. if(timeout_usec <= 0)
  122. {
  123. // Make the socket non-blocking
  124. apr_socket_opt_set(mSocket->getSocket(), APR_SO_NONBLOCK, 1);
  125. apr_socket_timeout_set(mSocket->getSocket(), 0);
  126. }
  127. else
  128. {
  129. // Make the socket blocking-with-timeout
  130. apr_socket_opt_set(mSocket->getSocket(), APR_SO_NONBLOCK, 1);
  131. apr_socket_timeout_set(mSocket->getSocket(), timeout_usec);
  132. }
  133. }
  134. bool LLPluginMessagePipe::pump(F64 timeout)
  135. {
  136. bool result = pumpOutput();
  137. if(result)
  138. {
  139. result = pumpInput(timeout);
  140. }
  141. return result;
  142. }
  143. bool LLPluginMessagePipe::pumpOutput()
  144. {
  145. bool result = true;
  146. if(mSocket)
  147. {
  148. apr_status_t status;
  149. apr_size_t in_size, out_size;
  150. LLMutexLock lock(&mOutputMutex);
  151. const char * output_data = &(mOutput.data()[mOutputStartIndex]);
  152. if(*output_data != '\0')
  153. {
  154. // write any outgoing messages
  155. in_size = (apr_size_t) (mOutput.size() - mOutputStartIndex);
  156. out_size = in_size;
  157. setSocketTimeout(0);
  158. // LL_INFOS("Plugin") << "before apr_socket_send, size = " << size << LL_ENDL;
  159. status = apr_socket_send(mSocket->getSocket(),
  160. output_data,
  161. &out_size);
  162. // LL_INFOS("Plugin") << "after apr_socket_send, size = " << size << LL_ENDL;
  163. if((status == APR_SUCCESS) || APR_STATUS_IS_EAGAIN(status))
  164. {
  165. // Success or Socket buffer is full...
  166. // If we've pumped the entire string, clear it
  167. if (out_size == in_size)
  168. {
  169. mOutputStartIndex = 0;
  170. mOutput.clear();
  171. }
  172. else
  173. {
  174. llassert(in_size > out_size);
  175. // Remove the written part from the buffer and try again later.
  176. mOutputStartIndex += out_size;
  177. }
  178. }
  179. else if(APR_STATUS_IS_EOF(status))
  180. {
  181. // This is what we normally expect when a plugin exits.
  182. llinfos << "Got EOF from plugin socket. " << llendl;
  183. if(mOwner)
  184. {
  185. mOwner->socketError(status);
  186. }
  187. result = false;
  188. }
  189. else
  190. {
  191. // some other error
  192. // Treat this as fatal.
  193. ll_apr_warn_status(status);
  194. if(mOwner)
  195. {
  196. mOwner->socketError(status);
  197. }
  198. result = false;
  199. }
  200. }
  201. }
  202. return result;
  203. }
  204. bool LLPluginMessagePipe::pumpInput(F64 timeout)
  205. {
  206. bool result = true;
  207. if(mSocket)
  208. {
  209. apr_status_t status;
  210. apr_size_t size;
  211. // FIXME: For some reason, the apr timeout stuff isn't working properly on windows.
  212. // Until such time as we figure out why, don't try to use the socket timeout -- just sleep here instead.
  213. #if LL_WINDOWS
  214. if(result)
  215. {
  216. if(timeout != 0.0f)
  217. {
  218. ms_sleep((int)(timeout * 1000.0f));
  219. timeout = 0.0f;
  220. }
  221. }
  222. #endif
  223. // Check for incoming messages
  224. if(result)
  225. {
  226. char input_buf[1024];
  227. apr_size_t request_size;
  228. if(timeout == 0.0f)
  229. {
  230. // If we have no timeout, start out with a full read.
  231. request_size = sizeof(input_buf);
  232. }
  233. else
  234. {
  235. // Start out by reading one byte, so that any data received will wake us up.
  236. request_size = 1;
  237. }
  238. // and use the timeout so we'll sleep if no data is available.
  239. setSocketTimeout((apr_interval_time_t)(timeout * 1000000));
  240. while(1)
  241. {
  242. size = request_size;
  243. // LL_INFOS("Plugin") << "before apr_socket_recv, size = " << size << LL_ENDL;
  244. status = apr_socket_recv(
  245. mSocket->getSocket(),
  246. input_buf,
  247. &size);
  248. // LL_INFOS("Plugin") << "after apr_socket_recv, size = " << size << LL_ENDL;
  249. if(size > 0)
  250. {
  251. LLMutexLock lock(&mInputMutex);
  252. mInput.append(input_buf, size);
  253. }
  254. if(status == APR_SUCCESS)
  255. {
  256. LL_DEBUGS("PluginSocket") << "success, read " << size << LL_ENDL;
  257. if(size != request_size)
  258. {
  259. // This was a short read, so we're done.
  260. break;
  261. }
  262. }
  263. else if(APR_STATUS_IS_TIMEUP(status))
  264. {
  265. LL_DEBUGS("PluginSocket") << "TIMEUP, read " << size << LL_ENDL;
  266. // Timeout was hit. Since the initial read is 1 byte, this should never be a partial read.
  267. break;
  268. }
  269. else if(APR_STATUS_IS_EAGAIN(status))
  270. {
  271. LL_DEBUGS("PluginSocket") << "EAGAIN, read " << size << LL_ENDL;
  272. // Non-blocking read returned immediately.
  273. break;
  274. }
  275. else if(APR_STATUS_IS_EOF(status))
  276. {
  277. // This is what we normally expect when a plugin exits.
  278. LL_INFOS("PluginSocket") << "Got EOF from plugin socket. " << LL_ENDL;
  279. if(mOwner)
  280. {
  281. mOwner->socketError(status);
  282. }
  283. result = false;
  284. break;
  285. }
  286. else
  287. {
  288. // some other error
  289. // Treat this as fatal.
  290. ll_apr_warn_status(status);
  291. if(mOwner)
  292. {
  293. mOwner->socketError(status);
  294. }
  295. result = false;
  296. break;
  297. }
  298. if(timeout != 0.0f)
  299. {
  300. // Second and subsequent reads should not use the timeout
  301. setSocketTimeout(0);
  302. // and should try to fill the input buffer
  303. request_size = sizeof(input_buf);
  304. }
  305. }
  306. processInput();
  307. }
  308. }
  309. return result;
  310. }
  311. void LLPluginMessagePipe::processInput(void)
  312. {
  313. // Look for input delimiter(s) in the input buffer.
  314. int delim;
  315. mInputMutex.lock();
  316. while((delim = mInput.find(MESSAGE_DELIMITER)) != std::string::npos)
  317. {
  318. // Let the owner process this message
  319. if (mOwner)
  320. {
  321. // Pull the message out of the input buffer before calling receiveMessageRaw.
  322. // It's now possible for this function to get called recursively (in the case where the plugin makes a blocking request)
  323. // and this guarantees that the messages will get dequeued correctly.
  324. std::string message(mInput, 0, delim);
  325. mInput.erase(0, delim + 1);
  326. mInputMutex.unlock();
  327. mOwner->receiveMessageRaw(message);
  328. mInputMutex.lock();
  329. }
  330. else
  331. {
  332. LL_WARNS("Plugin") << "!mOwner" << LL_ENDL;
  333. }
  334. }
  335. mInputMutex.unlock();
  336. }