diff options
| -rw-r--r-- | autobuild.xml | 14 | ||||
| -rw-r--r-- | indra/llplugin/llpluginmessagepipe.cpp | 2 | ||||
| -rw-r--r-- | indra/llplugin/llpluginprocesschild.cpp | 1171 | ||||
| -rw-r--r-- | indra/media_plugins/cef/media_plugin_cef.cpp | 18 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.cpp | 4 | 
5 files changed, 604 insertions, 605 deletions
| diff --git a/autobuild.xml b/autobuild.xml index 3a74a6832f..1ff5d8ade4 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -526,9 +526,9 @@              <key>archive</key>              <map>                <key>hash</key> -              <string>c3da21de753f30793a369c6a3dfaab07</string> +              <string>9005fcda30e6659790e2fdfc27d4e2ee</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4090/11448/dullahan-1.1.651_3.2987.1591.gd3e47f5-darwin64-504079.tar.bz2</string> +              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4384/12984/dullahan-1.1.764_3.2987.1591.gd3e47f5-darwin64-504373.tar.bz2</string>              </map>              <key>name</key>              <string>darwin64</string> @@ -538,9 +538,9 @@              <key>archive</key>              <map>                <key>hash</key> -              <string>3aea0733d1f18aa69420a02b9e455b40</string> +              <string>9c2abd5b606a5f0580c4cbb4e9aedfaa</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4091/11453/dullahan-1.1.651_3.2987.1591.gd3e47f5-windows-504079.tar.bz2</string> +              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4385/12990/dullahan-1.1.764_3.2987.1591.gd3e47f5-windows-504373.tar.bz2</string>              </map>              <key>name</key>              <string>windows</string> @@ -550,16 +550,16 @@              <key>archive</key>              <map>                <key>hash</key> -              <string>c52a90eaa74218ff954cd1f32d1960c9</string> +              <string>9e6714e4c3286a0b268577300043d998</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4092/11459/dullahan-1.1.651_3.2987.1591.gd3e47f5-windows64-504079.tar.bz2</string> +              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4386/12995/dullahan-1.1.764_3.2987.1591.gd3e47f5-windows64-504373.tar.bz2</string>              </map>              <key>name</key>              <string>windows64</string>            </map>          </map>          <key>version</key> -        <string>1.1.651_3.2987.1591.gd3e47f5</string> +        <string>1.1.764_3.2987.1591.gd3e47f5</string>        </map>        <key>elfio</key>        <map> diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp index 7e2bf90ad1..b7497ad781 100644 --- a/indra/llplugin/llpluginmessagepipe.cpp +++ b/indra/llplugin/llpluginmessagepipe.cpp @@ -215,7 +215,7 @@ bool LLPluginMessagePipe::pumpOutput()  			else if(APR_STATUS_IS_EOF(status))  			{  				// This is what we normally expect when a plugin exits. -				LL_INFOS() << "Got EOF from plugin socket. " << LL_ENDL; +				//LL_INFOS() << "Got EOF from plugin socket. " << LL_ENDL;  				if(mOwner)  				{ diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index be80d38305..3804ffadbb 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -1,582 +1,589 @@ -/**  - * @file llpluginprocesschild.cpp - * @brief LLPluginProcessChild handles the child side of the external-process plugin API.  - * - * @cond - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - * @endcond - */ - -#include "linden_common.h" - -#include "llpluginprocesschild.h" -#include "llplugininstance.h" -#include "llpluginmessagepipe.h" -#include "llpluginmessageclasses.h" - -static const F32 GOODBYE_SECONDS = 20.0f; -static const F32 HEARTBEAT_SECONDS = 1.0f; -static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f;  // Each call to idle will give the plugin this much time. - -LLPluginProcessChild::LLPluginProcessChild() -{ -	mState = STATE_UNINITIALIZED; -	mInstance = NULL; -	mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); -	mSleepTime = PLUGIN_IDLE_SECONDS;	// default: send idle messages at 100Hz -	mCPUElapsed = 0.0f; -	mBlockingRequest = false; -	mBlockingResponseReceived = false; -} - -LLPluginProcessChild::~LLPluginProcessChild() -{ -	if(mInstance != NULL) -	{ -		sendMessageToPlugin(LLPluginMessage("base", "cleanup")); - -		// IMPORTANT: under some (unknown) circumstances the apr_dso_unload() triggered when mInstance is deleted  -		// appears to fail and lock up which means that a given instance of the slplugin process never exits.  -		// This is bad, especially when users try to update their version of SL - it fails because the slplugin  -		// process as well as a bunch of plugin specific files are locked and cannot be overwritten. -		exit( 0 ); -		//delete mInstance; -		//mInstance = NULL; -	} -} - -void LLPluginProcessChild::killSockets(void) -{ -	killMessagePipe(); -	mSocket.reset(); -} - -void LLPluginProcessChild::init(U32 launcher_port) -{ -	mLauncherHost = LLHost("127.0.0.1", launcher_port); -	setState(STATE_INITIALIZED); -} - -void LLPluginProcessChild::idle(void) -{ -	bool idle_again; -	do -	{ -		if(APR_STATUS_IS_EOF(mSocketError)) -		{ -			// Plugin socket was closed.  This covers both normal plugin termination and host crashes. -			setState(STATE_ERROR); -		} -		else if(mSocketError != APR_SUCCESS) -		{ -			LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR"<< LL_ENDL; -			setState(STATE_ERROR); -		}	 - -		if((mState > STATE_INITIALIZED) && (mMessagePipe == NULL)) -		{ -			// The pipe has been closed -- we're done. -			// TODO: This could be slightly more subtle, but I'm not sure it needs to be. -			LL_INFOS("Plugin") << "message pipe went away, moving to STATE_ERROR"<< LL_ENDL; -			setState(STATE_ERROR); -		} -	 -		// If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). -		// 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. -		// When in doubt, don't do it. -		idle_again = false; -		 -		if(mInstance != NULL) -		{ -			// Provide some time to the plugin -			mInstance->idle(); -		} -		 -		switch(mState) -		{ -			case STATE_UNINITIALIZED: -			break; - -			case STATE_INITIALIZED: -				if(mSocket->blockingConnect(mLauncherHost)) -				{ -					// This automatically sets mMessagePipe -					new LLPluginMessagePipe(this, mSocket); -					 -					setState(STATE_CONNECTED); -				} -				else -				{ -					// connect failed -					setState(STATE_ERROR); -				} -			break; -			 -			case STATE_CONNECTED: -				sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hello")); -				setState(STATE_PLUGIN_LOADING); -			break; -						 -			case STATE_PLUGIN_LOADING: -				if(!mPluginFile.empty()) -				{ -					mInstance = new LLPluginInstance(this); -					if(mInstance->load(mPluginDir, mPluginFile) == 0) -					{ -						mHeartbeat.start(); -						mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS); -						mCPUElapsed = 0.0f; -						setState(STATE_PLUGIN_LOADED); -					} -					else -					{ -						setState(STATE_ERROR); -					} -				} -			break; -			 -			case STATE_PLUGIN_LOADED: -				{ -					setState(STATE_PLUGIN_INITIALIZING); -					LLPluginMessage message("base", "init"); -					sendMessageToPlugin(message); -				} -			break; -			 -			case STATE_PLUGIN_INITIALIZING: -				// waiting for init_response... -			break; -			 -			case STATE_RUNNING: -				if(mInstance != NULL) -				{ -					// Provide some time to the plugin -					LLPluginMessage message("base", "idle"); -					message.setValueReal("time", PLUGIN_IDLE_SECONDS); -					sendMessageToPlugin(message); -					 -					mInstance->idle(); -					 -					if(mHeartbeat.hasExpired()) -					{ -						 -						// This just proves that we're not stuck down inside the plugin code. -						LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat"); -						 -						// Calculate the approximage CPU usage fraction (floating point value between 0 and 1) used by the plugin this heartbeat cycle. -						// Note that this will not take into account any threads or additional processes the plugin spawns, but it's a first approximation. -						// If we could write OS-specific functions to query the actual CPU usage of this process, that would be a better approximation. -						heartbeat.setValueReal("cpu_usage", mCPUElapsed / mHeartbeat.getElapsedTimeF64()); -						 -						sendMessageToParent(heartbeat); - -						mHeartbeat.reset(); -						mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS); -						mCPUElapsed = 0.0f; -					} -				} -				// receivePluginMessage will transition to STATE_UNLOADING -			    break; - -            case STATE_SHUTDOWNREQ: -                if (mInstance != NULL) -                { -                    sendMessageToPlugin(LLPluginMessage("base", "cleanup")); -                    delete mInstance; -                    mInstance = NULL; -                } -                setState(STATE_UNLOADING); -                mWaitGoodbye.setTimerExpirySec(GOODBYE_SECONDS); -                break; - -			case STATE_UNLOADING: -                // waiting for goodbye from plugin. -                if (mWaitGoodbye.hasExpired()) -                { -                    LL_WARNS() << "Wait for goodbye expired.  Advancing to UNLOADED" << LL_ENDL; -                    setState(STATE_UNLOADED); -                } -			    break; -			 -			case STATE_UNLOADED: -				killSockets(); -				setState(STATE_DONE); -			    break; - -			case STATE_ERROR: -				// Close the socket to the launcher -				killSockets();				 -				// TODO: Where do we go from here?  Just exit()? -				setState(STATE_DONE); -			    break; -			 -			case STATE_DONE: -				// just sit here. -			    break; -		} -	 -	} while (idle_again); -} - -void LLPluginProcessChild::sleep(F64 seconds) -{ -	deliverQueuedMessages(); -	if(mMessagePipe) -	{ -		mMessagePipe->pump(seconds); -	} -	else -	{ -		ms_sleep((int)(seconds * 1000.0f)); -	} -} - -void LLPluginProcessChild::pump(void) -{ -	deliverQueuedMessages(); -	if(mMessagePipe) -	{ -		mMessagePipe->pump(0.0f); -	} -	else -	{ -		// Should we warn here? -	} -} - - -bool LLPluginProcessChild::isRunning(void) -{ -	bool result = false; -	 -	if(mState == STATE_RUNNING) -		result = true; -		 -	return result; -} - -bool LLPluginProcessChild::isDone(void) -{ -	bool result = false; -	 -	switch(mState) -	{ -		case STATE_DONE: -		result = true; -		break; -		default: -		break; -	} -		 -	return result; -} - -void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message) -{ -	if (mInstance) -	{ -		std::string buffer = message.generate(); -		 -		LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL; -		LLTimer elapsed; -		 -		mInstance->sendMessage(buffer); -		 -		mCPUElapsed += elapsed.getElapsedTimeF64(); -	} -	else -	{ -		LL_WARNS("Plugin") << "mInstance == NULL" << LL_ENDL; -	} -} - -void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message) -{ -	std::string buffer = message.generate(); - -	LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL; - -	writeMessageRaw(buffer); -} - -void LLPluginProcessChild::receiveMessageRaw(const std::string &message) -{ -	// Incoming message from the TCP Socket - -	LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL; - -	// Decode this message -	LLPluginMessage parsed; -	parsed.parse(message); - -	if(mBlockingRequest) -	{ -		// We're blocking the plugin waiting for a response. - -		if(parsed.hasValue("blocking_response")) -		{ -			// This is the message we've been waiting for -- fall through and send it immediately.  -			mBlockingResponseReceived = true; -		} -		else -		{ -			// Still waiting.  Queue this message and don't process it yet. -			mMessageQueue.push(message); -			return; -		} -	} -	 -	bool passMessage = true; -	 -	// FIXME: how should we handle queueing here? -	 -	{ -		std::string message_class = parsed.getClass(); -		if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) -		{ -			passMessage = false; -			 -			std::string message_name = parsed.getName(); -			if(message_name == "load_plugin") -			{ -				mPluginFile = parsed.getValue("file"); -				mPluginDir = parsed.getValue("dir"); -			} -            else if (message_name == "shutdown_plugin") -            { -                setState(STATE_SHUTDOWNREQ); -            } -			else if(message_name == "shm_add") -			{ -				std::string name = parsed.getValue("name"); -				size_t size = (size_t)parsed.getValueS32("size"); -				 -				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); -				if(iter != mSharedMemoryRegions.end()) -				{ -					// Need to remove the old region first -					LL_WARNS("Plugin") << "Adding a duplicate shared memory segment!" << LL_ENDL; -				} -				else -				{ -					// This is a new region -					LLPluginSharedMemory *region = new LLPluginSharedMemory; -					if(region->attach(name, size)) -					{ -						mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region)); -						 -						std::stringstream addr; -						addr << region->getMappedAddress(); -						 -						// Send the add notification to the plugin -						LLPluginMessage message("base", "shm_added"); -						message.setValue("name", name); -						message.setValueS32("size", (S32)size); -						message.setValuePointer("address", region->getMappedAddress()); -						sendMessageToPlugin(message); -						 -						// and send the response to the parent -						message.setMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add_response"); -						message.setValue("name", name); -						sendMessageToParent(message); -					} -					else -					{ -						LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL; -						delete region; -					} -				} -				 -			} -			else if(message_name == "shm_remove") -			{ -				std::string name = parsed.getValue("name"); -				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); -				if(iter != mSharedMemoryRegions.end()) -				{ -					// forward the remove request to the plugin -- its response will trigger us to detach the segment. -					LLPluginMessage message("base", "shm_remove"); -					message.setValue("name", name); -					sendMessageToPlugin(message); -				} -				else -				{ -					LL_WARNS("Plugin") << "shm_remove for unknown memory segment!" << LL_ENDL; -				} -			} -			else if(message_name == "sleep_time") -			{ -				mSleepTime = llmax(parsed.getValueReal("time"), 1.0 / 100.0); // clamp to maximum of 100Hz -			} -			else if(message_name == "crash") -			{ -				// Crash the plugin -				LL_ERRS("Plugin") << "Plugin crash requested." << LL_ENDL; -			} -			else if(message_name == "hang") -			{ -				// Hang the plugin -				LL_WARNS("Plugin") << "Plugin hang requested." << LL_ENDL; -				while(1) -				{ -					// wheeeeeeeee...... -				} -			} -			else -			{ -				LL_WARNS("Plugin") << "Unknown internal message from parent: " << message_name << LL_ENDL; -			} -		} -	} -	 -	if(passMessage && mInstance != NULL) -	{ -		LLTimer elapsed; - -		mInstance->sendMessage(message); - -		mCPUElapsed += elapsed.getElapsedTimeF64(); -	} -} - -/* virtual */  -void LLPluginProcessChild::receivePluginMessage(const std::string &message) -{ -	LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL; -	 -	if(mBlockingRequest) -	{ -		//  -		LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL; -	} -	 -	// Incoming message from the plugin instance -	bool passMessage = true; - -	// FIXME: how should we handle queueing here? -	 -	// Intercept certain base messages (responses to ones sent by this class) -	{ -		// Decode this message -		LLPluginMessage parsed; -		parsed.parse(message); -		 -		if(parsed.hasValue("blocking_request")) -		{ -			mBlockingRequest = true; -		} - -		std::string message_class = parsed.getClass(); -		if(message_class == "base") -		{ -			std::string message_name = parsed.getName(); -			if(message_name == "init_response") -			{ -				// The plugin has finished initializing. -				setState(STATE_RUNNING); - -				// Don't pass this message up to the parent -				passMessage = false; -				 -				LLPluginMessage new_message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin_response"); -				LLSD versions = parsed.getValueLLSD("versions"); -				new_message.setValueLLSD("versions", versions); -				 -				if(parsed.hasValue("plugin_version")) -				{ -					std::string plugin_version = parsed.getValue("plugin_version"); -					new_message.setValueLLSD("plugin_version", plugin_version); -				} - -				// Let the parent know it's loaded and initialized. -				sendMessageToParent(new_message); -			} -            else if (message_name == "goodbye") -            { -                setState(STATE_UNLOADED); -            } -			else if(message_name == "shm_remove_response") -			{ -				// Don't pass this message up to the parent -				passMessage = false; - -				std::string name = parsed.getValue("name"); -				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);				 -				if(iter != mSharedMemoryRegions.end()) -				{ -					// detach the shared memory region -					iter->second->detach(); -					 -					// and remove it from our map -					mSharedMemoryRegions.erase(iter); -					 -					// Finally, send the response to the parent. -					LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove_response"); -					message.setValue("name", name); -					sendMessageToParent(message); -				} -				else -				{ -					LL_WARNS("Plugin") << "shm_remove_response for unknown memory segment!" << LL_ENDL; -				} -			} -		} -	} -	 -	if(passMessage) -	{ -		LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL; -		writeMessageRaw(message); -	} -	 -	while(mBlockingRequest) -	{ -		// The plugin wants to block and wait for a response to this message. -		sleep(mSleepTime);	// this will pump the message pipe and process messages - -		if(mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL)) -		{ -			// Response has been received, or we've hit an error state.  Stop waiting. -			mBlockingRequest = false; -			mBlockingResponseReceived = false; -		} -	} -} - - -void LLPluginProcessChild::setState(EState state) -{ -	LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL; -	mState = state;  -}; - -void LLPluginProcessChild::deliverQueuedMessages() -{ -	if(!mBlockingRequest) -	{ -		while(!mMessageQueue.empty()) -		{ -			receiveMessageRaw(mMessageQueue.front()); -			mMessageQueue.pop(); -		} -	} -} +/**
 +* @file llpluginprocesschild.cpp
 +* @brief LLPluginProcessChild handles the child side of the external-process plugin API.
 +*
 +* @cond
 +* $LicenseInfo:firstyear=2008&license=viewerlgpl$
 +* Second Life Viewer Source Code
 +* Copyright (C) 2010, Linden Research, Inc.
 +*
 +* This library is free software; you can redistribute it and/or
 +* modify it under the terms of the GNU Lesser General Public
 +* License as published by the Free Software Foundation;
 +* version 2.1 of the License only.
 +*
 +* This library is distributed in the hope that it will be useful,
 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +* Lesser General Public License for more details.
 +*
 +* You should have received a copy of the GNU Lesser General Public
 +* License along with this library; if not, write to the Free Software
 +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 +*
 +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 +* $/LicenseInfo$
 +* @endcond
 +*/
 +
 +#include "linden_common.h"
 +
 +#include "llpluginprocesschild.h"
 +#include "llplugininstance.h"
 +#include "llpluginmessagepipe.h"
 +#include "llpluginmessageclasses.h"
 +
 +static const F32 GOODBYE_SECONDS = 20.0f;
 +static const F32 HEARTBEAT_SECONDS = 1.0f;
 +static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f;  // Each call to idle will give the plugin this much time.
 +
 +LLPluginProcessChild::LLPluginProcessChild()
 +{
 +	mState = STATE_UNINITIALIZED;
 +	mInstance = NULL;
 +	mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
 +	mSleepTime = PLUGIN_IDLE_SECONDS;	// default: send idle messages at 100Hz
 +	mCPUElapsed = 0.0f;
 +	mBlockingRequest = false;
 +	mBlockingResponseReceived = false;
 +}
 +
 +LLPluginProcessChild::~LLPluginProcessChild()
 +{
 +	if (mInstance != NULL)
 +	{
 +		sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
 +
 +		// IMPORTANT: under some (unknown) circumstances the apr_dso_unload() triggered when mInstance is deleted 
 +		// appears to fail and lock up which means that a given instance of the slplugin process never exits. 
 +		// This is bad, especially when users try to update their version of SL - it fails because the slplugin 
 +		// process as well as a bunch of plugin specific files are locked and cannot be overwritten.
 +		exit(0);
 +		//delete mInstance;
 +		//mInstance = NULL;
 +	}
 +}
 +
 +void LLPluginProcessChild::killSockets(void)
 +{
 +	killMessagePipe();
 +	mSocket.reset();
 +}
 +
 +void LLPluginProcessChild::init(U32 launcher_port)
 +{
 +	mLauncherHost = LLHost("127.0.0.1", launcher_port);
 +	setState(STATE_INITIALIZED);
 +}
 +
 +void LLPluginProcessChild::idle(void)
 +{
 +	bool idle_again;
 +	do
 +	{
 +		if (mState < STATE_SHUTDOWNREQ)
 +		{   // Once we have hit the shutdown request state checking for errors might put us in a spurious 
 +			// error state... don't do that.
 +
 +			if (APR_STATUS_IS_EOF(mSocketError))
 +			{
 +				// Plugin socket was closed.  This covers both normal plugin termination and host crashes.
 +				setState(STATE_ERROR);
 +			}
 +			else if (mSocketError != APR_SUCCESS)
 +			{
 +				LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR" << LL_ENDL;
 +				setState(STATE_ERROR);
 +			}
 +
 +			if ((mState > STATE_INITIALIZED) && (mMessagePipe == NULL))
 +			{
 +				// The pipe has been closed -- we're done.
 +				// TODO: This could be slightly more subtle, but I'm not sure it needs to be.
 +				LL_INFOS("Plugin") << "message pipe went away, moving to STATE_ERROR" << LL_ENDL;
 +				setState(STATE_ERROR);
 +			}
 +		}
 +
 +		// If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState().
 +		// 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.
 +		// When in doubt, don't do it.
 +		idle_again = false;
 +
 +		if (mInstance != NULL)
 +		{
 +			// Provide some time to the plugin
 +			mInstance->idle();
 +		}
 +
 +		switch (mState)
 +		{
 +		case STATE_UNINITIALIZED:
 +			break;
 +
 +		case STATE_INITIALIZED:
 +			if (mSocket->blockingConnect(mLauncherHost))
 +			{
 +				// This automatically sets mMessagePipe
 +				new LLPluginMessagePipe(this, mSocket);
 +
 +				setState(STATE_CONNECTED);
 +			}
 +			else
 +			{
 +				// connect failed
 +				setState(STATE_ERROR);
 +			}
 +			break;
 +
 +		case STATE_CONNECTED:
 +			sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hello"));
 +			setState(STATE_PLUGIN_LOADING);
 +			break;
 +
 +		case STATE_PLUGIN_LOADING:
 +			if (!mPluginFile.empty())
 +			{
 +				mInstance = new LLPluginInstance(this);
 +				if (mInstance->load(mPluginDir, mPluginFile) == 0)
 +				{
 +					mHeartbeat.start();
 +					mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
 +					mCPUElapsed = 0.0f;
 +					setState(STATE_PLUGIN_LOADED);
 +				}
 +				else
 +				{
 +					setState(STATE_ERROR);
 +				}
 +			}
 +			break;
 +
 +		case STATE_PLUGIN_LOADED:
 +		{
 +			setState(STATE_PLUGIN_INITIALIZING);
 +			LLPluginMessage message("base", "init");
 +			sendMessageToPlugin(message);
 +		}
 +		break;
 +
 +		case STATE_PLUGIN_INITIALIZING:
 +			// waiting for init_response...
 +			break;
 +
 +		case STATE_RUNNING:
 +			if (mInstance != NULL)
 +			{
 +				// Provide some time to the plugin
 +				LLPluginMessage message("base", "idle");
 +				message.setValueReal("time", PLUGIN_IDLE_SECONDS);
 +				sendMessageToPlugin(message);
 +
 +				mInstance->idle();
 +
 +				if (mHeartbeat.hasExpired())
 +				{
 +
 +					// This just proves that we're not stuck down inside the plugin code.
 +					LLPluginMessage heartbeat(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat");
 +
 +					// Calculate the approximage CPU usage fraction (floating point value between 0 and 1) used by the plugin this heartbeat cycle.
 +					// Note that this will not take into account any threads or additional processes the plugin spawns, but it's a first approximation.
 +					// If we could write OS-specific functions to query the actual CPU usage of this process, that would be a better approximation.
 +					heartbeat.setValueReal("cpu_usage", mCPUElapsed / mHeartbeat.getElapsedTimeF64());
 +
 +					sendMessageToParent(heartbeat);
 +
 +					mHeartbeat.reset();
 +					mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
 +					mCPUElapsed = 0.0f;
 +				}
 +			}
 +			// receivePluginMessage will transition to STATE_UNLOADING
 +			break;
 +
 +		case STATE_SHUTDOWNREQ:
 +			// set next state first thing in case "cleanup" message advances state.
 +			setState(STATE_UNLOADING);
 +			mWaitGoodbye.setTimerExpirySec(GOODBYE_SECONDS);
 +
 +			if (mInstance != NULL)
 +			{
 +				sendMessageToPlugin(LLPluginMessage("base", "cleanup"));
 +			}
 +			break;
 +
 +		case STATE_UNLOADING:
 +			// waiting for goodbye from plugin.
 +			if (mWaitGoodbye.hasExpired())
 +			{
 +				LL_WARNS() << "Wait for goodbye expired.  Advancing to UNLOADED" << LL_ENDL;
 +				setState(STATE_UNLOADED);
 +			}
 +			break;
 +
 +		case STATE_UNLOADED:
 +			killSockets();
 +			delete mInstance;
 +			mInstance = NULL;
 +			setState(STATE_DONE);
 +			break;
 +
 +		case STATE_ERROR:
 +			// Close the socket to the launcher
 +			killSockets();
 +			// TODO: Where do we go from here?  Just exit()?
 +			setState(STATE_DONE);
 +			break;
 +
 +		case STATE_DONE:
 +			// just sit here.
 +			break;
 +		}
 +
 +	} while (idle_again);
 +}
 +
 +void LLPluginProcessChild::sleep(F64 seconds)
 +{
 +	deliverQueuedMessages();
 +	if (mMessagePipe)
 +	{
 +		mMessagePipe->pump(seconds);
 +	}
 +	else
 +	{
 +		ms_sleep((int)(seconds * 1000.0f));
 +	}
 +}
 +
 +void LLPluginProcessChild::pump(void)
 +{
 +	deliverQueuedMessages();
 +	if (mMessagePipe)
 +	{
 +		mMessagePipe->pump(0.0f);
 +	}
 +	else
 +	{
 +		// Should we warn here?
 +	}
 +}
 +
 +
 +bool LLPluginProcessChild::isRunning(void)
 +{
 +	bool result = false;
 +
 +	if (mState == STATE_RUNNING)
 +		result = true;
 +
 +	return result;
 +}
 +
 +bool LLPluginProcessChild::isDone(void)
 +{
 +	bool result = false;
 +
 +	switch (mState)
 +	{
 +	case STATE_DONE:
 +		result = true;
 +		break;
 +	default:
 +		break;
 +	}
 +
 +	return result;
 +}
 +
 +void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message)
 +{
 +	if (mInstance)
 +	{
 +		std::string buffer = message.generate();
 +
 +		LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
 +		LLTimer elapsed;
 +
 +		mInstance->sendMessage(buffer);
 +
 +		mCPUElapsed += elapsed.getElapsedTimeF64();
 +	}
 +	else
 +	{
 +		LL_WARNS("Plugin") << "mInstance == NULL" << LL_ENDL;
 +	}
 +}
 +
 +void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message)
 +{
 +	std::string buffer = message.generate();
 +
 +	LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL;
 +
 +	writeMessageRaw(buffer);
 +}
 +
 +void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
 +{
 +	// Incoming message from the TCP Socket
 +
 +	LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL;
 +
 +	// Decode this message
 +	LLPluginMessage parsed;
 +	parsed.parse(message);
 +
 +	if (mBlockingRequest)
 +	{
 +		// We're blocking the plugin waiting for a response.
 +
 +		if (parsed.hasValue("blocking_response"))
 +		{
 +			// This is the message we've been waiting for -- fall through and send it immediately. 
 +			mBlockingResponseReceived = true;
 +		}
 +		else
 +		{
 +			// Still waiting.  Queue this message and don't process it yet.
 +			mMessageQueue.push(message);
 +			return;
 +		}
 +	}
 +
 +	bool passMessage = true;
 +
 +	// FIXME: how should we handle queueing here?
 +
 +	{
 +		std::string message_class = parsed.getClass();
 +		if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
 +		{
 +			passMessage = false;
 +
 +			std::string message_name = parsed.getName();
 +			if (message_name == "load_plugin")
 +			{
 +				mPluginFile = parsed.getValue("file");
 +				mPluginDir = parsed.getValue("dir");
 +			}
 +			else if (message_name == "shutdown_plugin")
 +			{
 +				setState(STATE_SHUTDOWNREQ);
 +			}
 +			else if (message_name == "shm_add")
 +			{
 +				std::string name = parsed.getValue("name");
 +				size_t size = (size_t)parsed.getValueS32("size");
 +
 +				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
 +				if (iter != mSharedMemoryRegions.end())
 +				{
 +					// Need to remove the old region first
 +					LL_WARNS("Plugin") << "Adding a duplicate shared memory segment!" << LL_ENDL;
 +				}
 +				else
 +				{
 +					// This is a new region
 +					LLPluginSharedMemory *region = new LLPluginSharedMemory;
 +					if (region->attach(name, size))
 +					{
 +						mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region));
 +
 +						std::stringstream addr;
 +						addr << region->getMappedAddress();
 +
 +						// Send the add notification to the plugin
 +						LLPluginMessage message("base", "shm_added");
 +						message.setValue("name", name);
 +						message.setValueS32("size", (S32)size);
 +						message.setValuePointer("address", region->getMappedAddress());
 +						sendMessageToPlugin(message);
 +
 +						// and send the response to the parent
 +						message.setMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add_response");
 +						message.setValue("name", name);
 +						sendMessageToParent(message);
 +					}
 +					else
 +					{
 +						LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL;
 +						delete region;
 +					}
 +				}
 +
 +			}
 +			else if (message_name == "shm_remove")
 +			{
 +				std::string name = parsed.getValue("name");
 +				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
 +				if (iter != mSharedMemoryRegions.end())
 +				{
 +					// forward the remove request to the plugin -- its response will trigger us to detach the segment.
 +					LLPluginMessage message("base", "shm_remove");
 +					message.setValue("name", name);
 +					sendMessageToPlugin(message);
 +				}
 +				else
 +				{
 +					LL_WARNS("Plugin") << "shm_remove for unknown memory segment!" << LL_ENDL;
 +				}
 +			}
 +			else if (message_name == "sleep_time")
 +			{
 +				mSleepTime = llmax(parsed.getValueReal("time"), 1.0 / 100.0); // clamp to maximum of 100Hz
 +			}
 +			else if (message_name == "crash")
 +			{
 +				// Crash the plugin
 +				LL_ERRS("Plugin") << "Plugin crash requested." << LL_ENDL;
 +			}
 +			else if (message_name == "hang")
 +			{
 +				// Hang the plugin
 +				LL_WARNS("Plugin") << "Plugin hang requested." << LL_ENDL;
 +				while (1)
 +				{
 +					// wheeeeeeeee......
 +				}
 +			}
 +			else
 +			{
 +				LL_WARNS("Plugin") << "Unknown internal message from parent: " << message_name << LL_ENDL;
 +			}
 +		}
 +	}
 +
 +	if (passMessage && mInstance != NULL)
 +	{
 +		LLTimer elapsed;
 +
 +		mInstance->sendMessage(message);
 +
 +		mCPUElapsed += elapsed.getElapsedTimeF64();
 +	}
 +}
 +
 +/* virtual */
 +void LLPluginProcessChild::receivePluginMessage(const std::string &message)
 +{
 +	LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL;
 +
 +	if (mBlockingRequest)
 +	{
 +		// 
 +		LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL;
 +	}
 +
 +	// Incoming message from the plugin instance
 +	bool passMessage = true;
 +
 +	// FIXME: how should we handle queueing here?
 +
 +	// Intercept certain base messages (responses to ones sent by this class)
 +	{
 +		// Decode this message
 +		LLPluginMessage parsed;
 +		parsed.parse(message);
 +
 +		if (parsed.hasValue("blocking_request"))
 +		{
 +			mBlockingRequest = true;
 +		}
 +
 +		std::string message_class = parsed.getClass();
 +		if (message_class == "base")
 +		{
 +			std::string message_name = parsed.getName();
 +			if (message_name == "init_response")
 +			{
 +				// The plugin has finished initializing.
 +				setState(STATE_RUNNING);
 +
 +				// Don't pass this message up to the parent
 +				passMessage = false;
 +
 +				LLPluginMessage new_message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin_response");
 +				LLSD versions = parsed.getValueLLSD("versions");
 +				new_message.setValueLLSD("versions", versions);
 +
 +				if (parsed.hasValue("plugin_version"))
 +				{
 +					std::string plugin_version = parsed.getValue("plugin_version");
 +					new_message.setValueLLSD("plugin_version", plugin_version);
 +				}
 +
 +				// Let the parent know it's loaded and initialized.
 +				sendMessageToParent(new_message);
 +			}
 +			else if (message_name == "goodbye")
 +			{
 +				setState(STATE_UNLOADED);
 +			}
 +			else if (message_name == "shm_remove_response")
 +			{
 +				// Don't pass this message up to the parent
 +				passMessage = false;
 +
 +				std::string name = parsed.getValue("name");
 +				sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name);
 +				if (iter != mSharedMemoryRegions.end())
 +				{
 +					// detach the shared memory region
 +					iter->second->detach();
 +
 +					// and remove it from our map
 +					mSharedMemoryRegions.erase(iter);
 +
 +					// Finally, send the response to the parent.
 +					LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove_response");
 +					message.setValue("name", name);
 +					sendMessageToParent(message);
 +				}
 +				else
 +				{
 +					LL_WARNS("Plugin") << "shm_remove_response for unknown memory segment!" << LL_ENDL;
 +				}
 +			}
 +		}
 +	}
 +
 +	if (passMessage)
 +	{
 +		LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL;
 +		writeMessageRaw(message);
 +	}
 +
 +	while (mBlockingRequest)
 +	{
 +		// The plugin wants to block and wait for a response to this message.
 +		sleep(mSleepTime);	// this will pump the message pipe and process messages
 +
 +		if (mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL))
 +		{
 +			// Response has been received, or we've hit an error state.  Stop waiting.
 +			mBlockingRequest = false;
 +			mBlockingResponseReceived = false;
 +		}
 +	}
 +}
 +
 +
 +void LLPluginProcessChild::setState(EState state)
 +{
 +	LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
 +	mState = state;
 +};
 +
 +void LLPluginProcessChild::deliverQueuedMessages()
 +{
 +	if (!mBlockingRequest)
 +	{
 +		while (!mMessageQueue.empty())
 +		{
 +			receiveMessageRaw(mMessageQueue.front());
 +			mMessageQueue.pop();
 +		}
 +	}
 +}
 diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index 965b755887..a2f7626e81 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -96,12 +96,6 @@ private:  	std::string mCookiePath;  	std::string mPickedFile;  	dullahan* mCEFLib; - -	U8 *mPopupBuffer; -	U32 mPopupW; -	U32 mPopupH; -	U32 mPopupX; -	U32 mPopupY;  };  //////////////////////////////////////////////////////////////////////////////// @@ -131,18 +125,13 @@ MediaPluginBase(host_send_func, host_user_data)  	mPickedFile = "";  	mCEFLib = new dullahan(); -	mPopupBuffer = NULL; -	mPopupW = 0; -	mPopupH = 0; -	mPopupX = 0; -	mPopupY = 0;  }  ////////////////////////////////////////////////////////////////////////////////  //  MediaPluginCEF::~MediaPluginCEF()  { -	delete[] mPopupBuffer; +	mCEFLib->shutdown();  }  //////////////////////////////////////////////////////////////////////////////// @@ -217,10 +206,11 @@ void MediaPluginCEF::onLoadStartCallback()  //  void MediaPluginCEF::onRequestExitCallback()  { -	mCEFLib->shutdown(); -  	LLPluginMessage message("base", "goodbye");  	sendMessage(message); + +	mDeleteMe = true; +	//mCEFLib->shutdown();  }  //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 9f05ee61bd..900075488f 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -604,7 +604,9 @@ void LLViewerMedia::updateMedia(void *dummy_arg)  	LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread"));  	// HACK: we always try to keep a spare running webkit plugin around to improve launch times. -	createSpareBrowserMediaSource(); +	// 2017-04-19 Removed CP - this doesn't appear to buy us much and consumes a lot of resources so +	// removing it for now. +	//createSpareBrowserMediaSource();  	sAnyMediaShowing = false;  	sAnyMediaPlaying = false; | 
