* Re-enables map item requests.
* Puts remote requests in a single worker thread * Worker thread only starts when there are agents to serve * When there are no agents to serve, it shuts down * A good example of how to deal with threads in non-shared modules so they don't end up consuming threads per regions0.6.0-stable
parent
2947ef9c00
commit
2c5497fa3a
|
@ -33,6 +33,7 @@ using System.Drawing.Imaging;
|
|||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using OpenMetaverse.StructuredData;
|
||||
|
@ -60,6 +61,8 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
|
||||
private static readonly string m_mapLayerPath = "0001/";
|
||||
|
||||
private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>();
|
||||
|
||||
//private IConfig m_config;
|
||||
private Scene m_scene;
|
||||
private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>();
|
||||
|
@ -67,7 +70,11 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
private byte[] myMapImageJPEG;
|
||||
private bool m_Enabled = false;
|
||||
private Dictionary<UUID, MapRequestState> m_openRequests = new Dictionary<UUID, MapRequestState>();
|
||||
|
||||
private Dictionary<string, int> m_blacklistedurls = new Dictionary<string, int>();
|
||||
private Dictionary<ulong, int> m_blacklistedregions = new Dictionary<ulong, int>();
|
||||
private Dictionary<ulong, string> m_cachedRegionMapItemsAddress = new Dictionary<ulong, string>();
|
||||
private Thread mapItemReqThread;
|
||||
private volatile bool threadrunning = false;
|
||||
|
||||
//private int CacheRegionsDistance = 256;
|
||||
|
||||
|
@ -93,12 +100,15 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
|
||||
m_scene.AddHTTPHandler(regionimage, OnHTTPGetMapImage);
|
||||
m_scene.AddLLSDHandler("/MAP/MapItems/" + scene.RegionInfo.RegionHandle.ToString(),HandleRemoteMapItemRequest);
|
||||
//QuadTree.Subdivide();
|
||||
//QuadTree.Subdivide();
|
||||
|
||||
|
||||
scene.EventManager.OnRegisterCaps += OnRegisterCaps;
|
||||
scene.EventManager.OnNewClient += OnNewClient;
|
||||
scene.EventManager.OnClientClosed += ClientLoggedOut;
|
||||
scene.EventManager.OnMakeChildAgent += MakeChildAgent;
|
||||
scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
|
||||
|
||||
|
||||
}
|
||||
public void PostInitialise()
|
||||
{
|
||||
|
@ -220,33 +230,80 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
}
|
||||
#region EventHandlers
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registered for event
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
private void OnNewClient(IClientAPI client)
|
||||
{
|
||||
// All friends establishment protocol goes over instant message
|
||||
// There's no way to send a message from the sim
|
||||
// to a user to 'add a friend' without causing dialog box spam
|
||||
//
|
||||
// The base set of friends are added when the user signs on in their XMLRPC response
|
||||
// Generated by LoginService. The friends are retreived from the database by the UserManager
|
||||
|
||||
// Subscribe to instant messages
|
||||
|
||||
//client.OnInstantMessage += OnInstantMessage;
|
||||
//client.OnApproveFriendRequest += OnApprovedFriendRequest;
|
||||
//client.OnDenyFriendRequest += OnDenyFriendRequest;
|
||||
//client.OnTerminateFriendship += OnTerminateFriendship;
|
||||
|
||||
//doFriendListUpdateOnline(client.AgentId);
|
||||
client.OnRequestMapBlocks += RequestMapBlocks;
|
||||
client.OnMapItemRequest += HandleMapItemRequest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client logged out, check to see if there are any more root agents in the simulator
|
||||
/// If not, stop the mapItemRequest Thread
|
||||
/// Event handler
|
||||
/// </summary>
|
||||
/// <param name="AgentId">AgentID that logged out</param>
|
||||
private void ClientLoggedOut(UUID AgentId)
|
||||
{
|
||||
|
||||
List<ScenePresence> presences = m_scene.GetAvatars();
|
||||
int rootcount = 0;
|
||||
for (int i=0;i<presences.Count;i++)
|
||||
{
|
||||
if (presences[i] != null)
|
||||
{
|
||||
if (!presences[i].IsChildAgent)
|
||||
rootcount++;
|
||||
}
|
||||
}
|
||||
if (rootcount <= 1)
|
||||
StopThread();
|
||||
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Starts the MapItemRequest Thread
|
||||
/// Note that this only gets started when there are actually agents in the region
|
||||
/// Additionally, it gets stopped when there are none.
|
||||
/// </summary>
|
||||
/// <param name="o"></param>
|
||||
private void StartThread(object o)
|
||||
{
|
||||
if (threadrunning) return;
|
||||
|
||||
m_log.Warn("[WorldMap]: Starting remote MapItem request thread");
|
||||
threadrunning = true;
|
||||
mapItemReqThread = new Thread(new ThreadStart(process));
|
||||
mapItemReqThread.IsBackground = true;
|
||||
mapItemReqThread.Name = "MapItemRequestThread";
|
||||
mapItemReqThread.Priority = ThreadPriority.BelowNormal;
|
||||
mapItemReqThread.SetApartmentState(ApartmentState.MTA);
|
||||
mapItemReqThread.Start();
|
||||
ThreadTracker.Add(mapItemReqThread);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues a 'stop thread' MapRequestState. Causes the MapItemRequest thread to end
|
||||
/// </summary>
|
||||
private void StopThread()
|
||||
{
|
||||
MapRequestState st = new MapRequestState();
|
||||
st.agentID=UUID.Zero;
|
||||
st.EstateID=0;
|
||||
st.flags=0;
|
||||
st.godlike=false;
|
||||
st.itemtype=0;
|
||||
st.regionhandle=0;
|
||||
|
||||
requests.Enqueue(st);
|
||||
}
|
||||
|
||||
|
||||
public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags,
|
||||
uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
|
||||
{
|
||||
|
@ -257,6 +314,7 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
{
|
||||
if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
|
||||
{
|
||||
// Local Map Item Request
|
||||
List<ScenePresence> avatars = m_scene.GetAvatars();
|
||||
int tc = System.Environment.TickCount;
|
||||
List<mapItemReply> mapitems = new List<mapItemReply>();
|
||||
|
@ -295,29 +353,62 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
}
|
||||
else
|
||||
{
|
||||
//RegionInfo mreg = m_scene.SceneGridService.RequestNeighbouringRegionInfo(regionhandle);
|
||||
//if (mreg != null)
|
||||
//{
|
||||
// string httpserver = "http://" + mreg.ExternalEndPoint.Address.ToString() + ":" + mreg.HttpPort + "/MAP/MapItems/" + regionhandle.ToString();
|
||||
// Remote Map Item Request
|
||||
|
||||
// RequestMapItems(httpserver,remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public delegate LLSDMap RequestMapItemsDelegate(string httpserver, UUID id, uint flags,
|
||||
uint EstateID, bool godlike, uint itemtype, ulong regionhandle);
|
||||
|
||||
private void RequestMapItemsCompleted(IAsyncResult iar)
|
||||
// ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes.
|
||||
// Note that we only start up a remote mapItem Request thread if there's users who could
|
||||
// be making requests
|
||||
if (!threadrunning)
|
||||
{
|
||||
m_log.Warn("[WorldMap]: Starting new remote request thread manually. This means that AvatarEnteringParcel never fired! This needs to be fixed! Don't Mantis this, as the developers can see it in this message");
|
||||
StartThread(new object());
|
||||
}
|
||||
|
||||
RequestMapItemsDelegate icon = (RequestMapItemsDelegate)iar.AsyncState;
|
||||
LLSDMap response = icon.EndInvoke(iar);
|
||||
RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processing thread main() loop for doing remote mapitem requests
|
||||
/// </summary>
|
||||
public void process()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
MapRequestState st = requests.Dequeue();
|
||||
|
||||
// end gracefully
|
||||
if (st.agentID == UUID.Zero)
|
||||
{
|
||||
ThreadTracker.Remove(mapItemReqThread);
|
||||
break;
|
||||
}
|
||||
LLSDMap response = RequestMapItemsAsync("", st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle);
|
||||
RequestMapItemsCompleted(response);
|
||||
}
|
||||
threadrunning = false;
|
||||
m_log.Warn("[WorldMap]: Remote request thread exiting");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the map item request into the processing thread
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
public void EnqueueMapItemRequest(MapRequestState state)
|
||||
{
|
||||
requests.Enqueue(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the mapitem response to the IClientAPI
|
||||
/// </summary>
|
||||
/// <param name="response">The LLSDMap Response for the mapitem</param>
|
||||
private void RequestMapItemsCompleted(LLSDMap response)
|
||||
{
|
||||
|
||||
UUID requestID = response["requestID"].AsUUID();
|
||||
|
||||
|
@ -364,18 +455,98 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the MapItem request for remote processing
|
||||
/// </summary>
|
||||
/// <param name="httpserver">blank string, we discover this in the process</param>
|
||||
/// <param name="id">Agent ID that we are making this request on behalf</param>
|
||||
/// <param name="flags">passed in from packet</param>
|
||||
/// <param name="EstateID">passed in from packet</param>
|
||||
/// <param name="godlike">passed in from packet</param>
|
||||
/// <param name="itemtype">passed in from packet</param>
|
||||
/// <param name="regionhandle">Region we're looking up</param>
|
||||
public void RequestMapItems(string httpserver, UUID id, uint flags,
|
||||
uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
|
||||
{
|
||||
//m_log.Info("[INTER]: " + debugRegionName + ": SceneCommunicationService: Sending InterRegion Notification that region is up " + region.RegionName);
|
||||
RequestMapItemsDelegate d = RequestMapItemsAsync;
|
||||
d.BeginInvoke(httpserver, id,flags,EstateID,godlike,itemtype,regionhandle,RequestMapItemsCompleted, d);
|
||||
//bool val = m_commsProvider.InterRegion.RegionUp(new SerializableRegionInfo(region));
|
||||
MapRequestState st = new MapRequestState();
|
||||
st.agentID = id;
|
||||
st.flags = flags;
|
||||
st.EstateID = EstateID;
|
||||
st.godlike = godlike;
|
||||
st.itemtype = itemtype;
|
||||
st.regionhandle = regionhandle;
|
||||
EnqueueMapItemRequest(st);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the actual remote mapitem request
|
||||
/// This should be called from an asynchronous thread
|
||||
/// Request failures get blacklisted until region restart so we don't
|
||||
/// continue to spend resources trying to contact regions that are down.
|
||||
/// </summary>
|
||||
/// <param name="httpserver">blank string, we discover this in the process</param>
|
||||
/// <param name="id">Agent ID that we are making this request on behalf</param>
|
||||
/// <param name="flags">passed in from packet</param>
|
||||
/// <param name="EstateID">passed in from packet</param>
|
||||
/// <param name="godlike">passed in from packet</param>
|
||||
/// <param name="itemtype">passed in from packet</param>
|
||||
/// <param name="regionhandle">Region we're looking up</param>
|
||||
/// <returns></returns>
|
||||
private LLSDMap RequestMapItemsAsync(string httpserver, UUID id, uint flags,
|
||||
uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
|
||||
{
|
||||
bool blacklisted = false;
|
||||
lock (m_blacklistedregions)
|
||||
{
|
||||
if (m_blacklistedregions.ContainsKey(regionhandle))
|
||||
blacklisted = true;
|
||||
|
||||
}
|
||||
|
||||
if (blacklisted)
|
||||
return new LLSDMap();
|
||||
|
||||
UUID requestID = UUID.Random();
|
||||
lock (m_cachedRegionMapItemsAddress)
|
||||
{
|
||||
if (m_cachedRegionMapItemsAddress.ContainsKey(regionhandle))
|
||||
httpserver = m_cachedRegionMapItemsAddress[regionhandle];
|
||||
}
|
||||
if (httpserver.Length == 0)
|
||||
{
|
||||
RegionInfo mreg = m_scene.SceneGridService.RequestNeighbouringRegionInfo(regionhandle);
|
||||
if (mreg != null)
|
||||
{
|
||||
httpserver = "http://" + mreg.ExternalEndPoint.Address.ToString() + ":" + mreg.HttpPort + "/MAP/MapItems/" + regionhandle.ToString();
|
||||
lock (m_cachedRegionMapItemsAddress)
|
||||
{
|
||||
if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle))
|
||||
m_cachedRegionMapItemsAddress.Add(regionhandle, httpserver);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (m_blacklistedregions)
|
||||
{
|
||||
if (!m_blacklistedregions.ContainsKey(regionhandle))
|
||||
m_blacklistedregions.Add(regionhandle, System.Environment.TickCount);
|
||||
}
|
||||
m_log.WarnFormat("[WorldMap]: Blacklisted region {0}", regionhandle.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
blacklisted = false;
|
||||
lock (m_blacklistedurls)
|
||||
{
|
||||
if (m_blacklistedurls.ContainsKey(httpserver))
|
||||
blacklisted = true;
|
||||
}
|
||||
|
||||
// Can't find the http server
|
||||
if (httpserver.Length == 0 || blacklisted)
|
||||
return new LLSDMap();
|
||||
|
||||
MapRequestState mrs = new MapRequestState();
|
||||
mrs.agentID = id;
|
||||
|
@ -413,30 +584,43 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
{
|
||||
m_log.InfoFormat("[WorldMap] Bad send on GetMapItems {0}", ex.Message);
|
||||
responseMap["connect"] = LLSD.FromBoolean(false);
|
||||
lock (m_blacklistedurls)
|
||||
{
|
||||
if (!m_blacklistedurls.ContainsKey(httpserver))
|
||||
m_blacklistedurls.Add(httpserver, System.Environment.TickCount);
|
||||
}
|
||||
|
||||
m_log.WarnFormat("[WorldMap]: Blacklisted {0}", httpserver);
|
||||
|
||||
return responseMap;
|
||||
}
|
||||
|
||||
//m_log.Info("[OGP] waiting for a reply after rez avatar send");
|
||||
string response_mapItems_reply = null;
|
||||
{ // get the response
|
||||
try
|
||||
{
|
||||
WebResponse webResponse = mapitemsrequest.GetResponse();
|
||||
if (webResponse == null)
|
||||
if (webResponse != null)
|
||||
{
|
||||
//m_log.Info("[OGP:] Null reply on rez_avatar post");
|
||||
}
|
||||
|
||||
StreamReader sr = new StreamReader(webResponse.GetResponseStream());
|
||||
response_mapItems_reply = sr.ReadToEnd().Trim();
|
||||
//m_log.InfoFormat("[OGP]: rez_avatar reply was {0} ", response_mapItems_reply);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return new LLSDMap();
|
||||
}
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
//m_log.InfoFormat("[OGP]: exception on read after send of rez avatar {0}", ex.Message);
|
||||
|
||||
responseMap["connect"] = LLSD.FromBoolean(false);
|
||||
lock (m_blacklistedurls)
|
||||
{
|
||||
if (!m_blacklistedurls.ContainsKey(httpserver))
|
||||
m_blacklistedurls.Add(httpserver, System.Environment.TickCount);
|
||||
}
|
||||
|
||||
m_log.WarnFormat("[WorldMap]: Blacklisted {0}", httpserver);
|
||||
|
||||
return responseMap;
|
||||
}
|
||||
|
@ -655,7 +839,34 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
}
|
||||
return responsemap;
|
||||
}
|
||||
|
||||
private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID)
|
||||
{
|
||||
// You may ask, why this is in a threadpool to start with..
|
||||
// The reason is so we don't cause the thread to freeze waiting
|
||||
// for the 1 second it costs to start a thread manually.
|
||||
|
||||
if (!threadrunning)
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartThread));
|
||||
}
|
||||
|
||||
private void MakeChildAgent(ScenePresence avatar)
|
||||
{
|
||||
List<ScenePresence> presences = m_scene.GetAvatars();
|
||||
int rootcount = 0;
|
||||
for (int i = 0; i < presences.Count; i++)
|
||||
{
|
||||
if (presences[i] != null)
|
||||
{
|
||||
if (!presences[i].IsChildAgent)
|
||||
rootcount++;
|
||||
}
|
||||
}
|
||||
if (rootcount <= 1)
|
||||
StopThread();
|
||||
}
|
||||
}
|
||||
|
||||
public struct MapRequestState
|
||||
{
|
||||
public UUID agentID;
|
||||
|
@ -666,5 +877,4 @@ namespace OpenSim.Region.Environment.Modules.World.WorldMap
|
|||
public ulong regionhandle;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue