aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2011-07-01 21:25:40 +0100
committerJustin Clark-Casey (justincc)2011-07-01 21:25:40 +0100
commit9f72fbcb7533bd960c38082cbd6956cd01fa6919 (patch)
tree3f2da22ae0a05d76753ab25ae3849ef66b25ccb6 /OpenSim/Region/Framework
parentMerge branch 'master' of ssh://opensimulator.org/var/git/opensim (diff)
downloadopensim-SC_OLD-9f72fbcb7533bd960c38082cbd6956cd01fa6919.zip
opensim-SC_OLD-9f72fbcb7533bd960c38082cbd6956cd01fa6919.tar.gz
opensim-SC_OLD-9f72fbcb7533bd960c38082cbd6956cd01fa6919.tar.bz2
opensim-SC_OLD-9f72fbcb7533bd960c38082cbd6956cd01fa6919.tar.xz
Add an async inventory details sender to respond to FetchInventory packets.
If a user with a very large inventory right-clicks on their "My Inventory" folder, viewer 1 code will send a massive number of Fetchinventory requests. Even though each is handled asynchronously via a pool thread, the sheer frequency of requests overwhelms the pool and freezes inbound packet handling. This change makes the first Fetchinventory thread also handle subsequent requests, freeing up the other threads. Further efficiencies could be made by handling all the items in a particular FetchInventory request together, rather than separately.
Diffstat (limited to 'OpenSim/Region/Framework')
-rw-r--r--OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs156
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.Inventory.cs5
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs25
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs5
4 files changed, 164 insertions, 27 deletions
diff --git a/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs
new file mode 100644
index 0000000..06cd14b
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs
@@ -0,0 +1,156 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Threading;
32using log4net;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Region.Framework.Interfaces;
36
37namespace OpenSim.Region.Framework.Scenes
38{
39 class FetchHolder
40 {
41 public IClientAPI Client { get; private set; }
42 public UUID ItemID { get; private set; }
43
44 public FetchHolder(IClientAPI client, UUID itemID)
45 {
46 Client = client;
47 ItemID = itemID;
48 }
49 }
50
51 /// <summary>
52 /// Send FetchInventoryReply information to clients asynchronously on a single thread rather than asynchronously via
53 /// multiple threads.
54 /// </summary>
55 /// <remarks>
56 /// If the main root inventory is right-clicked on a version 1 viewer for a user with a large inventory, a very
57 /// very large number of FetchInventory requests are sent to the simulator. Each is handled on a separate thread
58 /// by the IClientAPI, but the sheer number of requests overwhelms the number of threads available and ends up
59 /// freezing the inbound packet handling.
60 ///
61 /// This class makes the first FetchInventory packet thread process the queue. If new requests come
62 /// in while it is processing, then the subsequent threads just add the requests and leave it to the original
63 /// thread to process them.
64 ///
65 /// This might slow down outbound packets but these are limited by the IClientAPI outbound queues
66 /// anyway.
67 ///
68 /// It might be possible to ignore FetchInventory requests altogether, particularly as they are redundant wrt to
69 /// FetchInventoryDescendents requests, but this would require more investigation.
70 /// </remarks>
71 public class AsyncInventorySender
72 {
73 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
74
75 protected Scene m_scene;
76
77 /// <summary>
78 /// Queues fetch requests
79 /// </summary>
80 Queue<FetchHolder> m_fetchHolder = new Queue<FetchHolder>();
81
82 /// <summary>
83 /// Signal whether a queue is currently being processed or not.
84 /// </summary>
85 protected volatile bool m_processing;
86
87 public AsyncInventorySender(Scene scene)
88 {
89 m_processing = false;
90 m_scene = scene;
91 }
92
93 /// <summary>
94 /// Handle a fetch inventory request from the client
95 /// </summary>
96 /// <param name="remoteClient"></param>
97 /// <param name="itemID"></param>
98 /// <param name="ownerID"></param>
99 public void HandleFetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID)
100 {
101 lock (m_fetchHolder)
102 {
103// m_log.DebugFormat(
104// "[ASYNC INVENTORY SENDER]: Putting request from {0} for {1} on queue", remoteClient.Name, itemID);
105
106 m_fetchHolder.Enqueue(new FetchHolder(remoteClient, itemID));
107 }
108
109 if (!m_processing)
110 {
111 m_processing = true;
112 ProcessQueue();
113 }
114 }
115
116 /// <summary>
117 /// Process the queue of fetches
118 /// </summary>
119 protected void ProcessQueue()
120 {
121 FetchHolder fh = null;
122
123 while (true)
124 {
125 lock (m_fetchHolder)
126 {
127// m_log.DebugFormat("[ASYNC INVENTORY SENDER]: {0} items left to process", m_fetchHolder.Count);
128
129 if (m_fetchHolder.Count == 0)
130 {
131 m_processing = false;
132 return;
133 }
134 else
135 {
136 fh = m_fetchHolder.Dequeue();
137 }
138 }
139
140 if (fh.Client.IsLoggingOut)
141 continue;
142
143// m_log.DebugFormat(
144// "[ASYNC INVENTORY SENDER]: Handling request from {0} for {1} on queue", fh.Client.Name, fh.ItemID);
145
146 InventoryItemBase item = new InventoryItemBase(fh.ItemID, fh.Client.AgentId);
147 item = m_scene.InventoryService.GetItem(item);
148
149 if (item != null)
150 fh.Client.SendInventoryItemDetails(item.Owner, item);
151
152 // TODO: Possibly log any failure
153 }
154 }
155 }
156} \ No newline at end of file
diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
index 13085e3..30421d4 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
@@ -52,6 +52,11 @@ namespace OpenSim.Region.Framework.Scenes
52 protected AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter; 52 protected AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter;
53 53
54 /// <summary> 54 /// <summary>
55 /// Allows inventory details to be sent to clients asynchronously
56 /// </summary>
57 protected AsyncInventorySender m_asyncInventorySender;
58
59 /// <summary>
55 /// Start all the scripts in the scene which should be started. 60 /// Start all the scripts in the scene which should be started.
56 /// </summary> 61 /// </summary>
57 public void CreateScriptInstances() 62 public void CreateScriptInstances()
diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs
index e2d7208..44472b2 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs
@@ -461,31 +461,6 @@ namespace OpenSim.Region.Framework.Scenes
461 } 461 }
462 ); 462 );
463 } 463 }
464
465
466 /// <summary>
467 /// Handle a fetch inventory request from the client
468 /// </summary>
469 /// <param name="remoteClient"></param>
470 /// <param name="itemID"></param>
471 /// <param name="ownerID"></param>
472 public void HandleFetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID)
473 {
474 if (LibraryService != null && LibraryService.LibraryRootFolder != null && ownerID == LibraryService.LibraryRootFolder.Owner)
475 {
476 //m_log.Debug("request info for library item");
477 return;
478 }
479
480 InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId);
481 item = InventoryService.GetItem(item);
482
483 if (item != null)
484 {
485 remoteClient.SendInventoryItemDetails(ownerID, item);
486 }
487 // else shouldn't we send an alert message?
488 }
489 464
490 /// <summary> 465 /// <summary>
491 /// Tell the client about the various child items and folders contained in the requested folder. 466 /// Tell the client about the various child items and folders contained in the requested folder.
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index ad41e88..eeb881f 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -583,6 +583,8 @@ namespace OpenSim.Region.Framework.Scenes
583 m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this); 583 m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this);
584 m_asyncSceneObjectDeleter.Enabled = true; 584 m_asyncSceneObjectDeleter.Enabled = true;
585 585
586 m_asyncInventorySender = new AsyncInventorySender(this);
587
586 #region Region Settings 588 #region Region Settings
587 589
588 // Load region settings 590 // Load region settings
@@ -2760,14 +2762,13 @@ namespace OpenSim.Region.Framework.Scenes
2760 2762
2761 public virtual void SubscribeToClientInventoryEvents(IClientAPI client) 2763 public virtual void SubscribeToClientInventoryEvents(IClientAPI client)
2762 { 2764 {
2763
2764 client.OnLinkInventoryItem += HandleLinkInventoryItem; 2765 client.OnLinkInventoryItem += HandleLinkInventoryItem;
2765 client.OnCreateNewInventoryFolder += HandleCreateInventoryFolder; 2766 client.OnCreateNewInventoryFolder += HandleCreateInventoryFolder;
2766 client.OnUpdateInventoryFolder += HandleUpdateInventoryFolder; 2767 client.OnUpdateInventoryFolder += HandleUpdateInventoryFolder;
2767 client.OnMoveInventoryFolder += HandleMoveInventoryFolder; // 2; //!! 2768 client.OnMoveInventoryFolder += HandleMoveInventoryFolder; // 2; //!!
2768 client.OnFetchInventoryDescendents += HandleFetchInventoryDescendents; 2769 client.OnFetchInventoryDescendents += HandleFetchInventoryDescendents;
2769 client.OnPurgeInventoryDescendents += HandlePurgeInventoryDescendents; // 2; //!! 2770 client.OnPurgeInventoryDescendents += HandlePurgeInventoryDescendents; // 2; //!!
2770 client.OnFetchInventory += HandleFetchInventory; 2771 client.OnFetchInventory += m_asyncInventorySender.HandleFetchInventory;
2771 client.OnUpdateInventoryItem += UpdateInventoryItemAsset; 2772 client.OnUpdateInventoryItem += UpdateInventoryItemAsset;
2772 client.OnCopyInventoryItem += CopyInventoryItem; 2773 client.OnCopyInventoryItem += CopyInventoryItem;
2773 client.OnMoveInventoryItem += MoveInventoryItem; 2774 client.OnMoveInventoryItem += MoveInventoryItem;