PageRenderTime 526ms CodeModel.GetById 180ms app.highlight 107ms RepoModel.GetById 235ms app.codeStats 1ms

/indra/llplugin/llpluginmessagepipe.cpp

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