diff --git a/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs index fcb6991969..1d63d2611e 100644 --- a/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs +++ b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs @@ -115,6 +115,8 @@ namespace OpenSim.ApplicationPlugins.LoadRegions Environment.Exit(1); } + List createdScenes = new List(); + for (int i = 0; i < regionsToLoad.Length; i++) { IScene scene; @@ -123,17 +125,22 @@ namespace OpenSim.ApplicationPlugins.LoadRegions ")"); bool changed = m_openSim.PopulateRegionEstateInfo(regionsToLoad[i]); + m_openSim.CreateRegion(regionsToLoad[i], true, out scene); + createdScenes.Add(scene); + if (changed) - regionsToLoad[i].EstateSettings.Save(); - - if (scene != null) + regionsToLoad[i].EstateSettings.Save(); + } + + foreach (IScene scene in createdScenes) + { + scene.Start(); + + m_newRegionCreatedHandler = OnNewRegionCreated; + if (m_newRegionCreatedHandler != null) { - m_newRegionCreatedHandler = OnNewRegionCreated; - if (m_newRegionCreatedHandler != null) - { - m_newRegionCreatedHandler(scene); - } + m_newRegionCreatedHandler(scene); } } } diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index d19b8b64b0..355f7b3078 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -700,6 +700,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController IScene newScene; m_application.CreateRegion(region, out newScene); + newScene.Start(); // If an access specification was provided, use it. // Otherwise accept the default. diff --git a/OpenSim/Capabilities/CapsHandlers.cs b/OpenSim/Capabilities/CapsHandlers.cs index 1709f46c9c..458272d10c 100644 --- a/OpenSim/Capabilities/CapsHandlers.cs +++ b/OpenSim/Capabilities/CapsHandlers.cs @@ -158,7 +158,7 @@ namespace OpenSim.Framework.Capabilities /// capabilities and their handler details. /// /// If true, then exclude the seed cap. - public Hashtable GetCapsDetails(bool excludeSeed) + public Hashtable GetCapsDetails(bool excludeSeed, List requestedCaps) { Hashtable caps = new Hashtable(); string protocol = "http://"; @@ -175,6 +175,9 @@ namespace OpenSim.Framework.Capabilities if (excludeSeed && "SEED" == capsName) continue; + if (requestedCaps != null && !requestedCaps.Contains(capsName)) + continue; + caps[capsName] = baseUrl + m_capsHandlers[capsName].Path; } } @@ -182,4 +185,4 @@ namespace OpenSim.Framework.Capabilities return caps; } } -} \ No newline at end of file +} diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 87ec99ef60..8164f4198c 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -136,5 +136,10 @@ namespace OpenSim.Framework ISceneObject DeserializeObject(string representation); bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); + + /// + /// Start the scene and associated scripts within it. + /// + void Start(); } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index bde46731a1..a3602e93f2 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1840,7 +1840,7 @@ namespace OpenSim.Framework case FireAndForgetMethod.SmartThreadPool: if (m_ThreadPool == null) InitThreadPool(15); - m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); + m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); break; case FireAndForgetMethod.Thread: Thread thread = new Thread(delegate(object o) { realCallback(o); }); @@ -1910,15 +1910,15 @@ namespace OpenSim.Framework return sb.ToString(); } - private static object SmartThreadPoolCallback(object o) - { - object[] array = (object[])o; - WaitCallback callback = (WaitCallback)array[0]; - object obj = array[1]; - - callback(obj); - return null; - } +// private static object SmartThreadPoolCallback(object o) +// { +// object[] array = (object[])o; +// WaitCallback callback = (WaitCallback)array[0]; +// object obj = array[1]; +// +// callback(obj); +// return null; +// } #endregion FireAndForget Threading Pattern diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index d86eefe005..f9e0cf1b49 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -425,9 +425,6 @@ namespace OpenSim mscene = scene; - scene.Start(); - scene.StartScripts(); - return clientServers; } @@ -751,6 +748,7 @@ namespace OpenSim ShutdownClientServer(whichRegion); IScene scene; CreateRegion(whichRegion, true, out scene); + scene.Start(); } # region Setup methods diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index 87524043c9..a46c24a637 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs @@ -273,11 +273,22 @@ namespace OpenSim.Region.ClientStack.Linden return string.Empty; } - Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true); + OSDArray capsRequested = (OSDArray)OSDParser.DeserializeLLSDXml(request); + List validCaps = new List(); + + foreach (OSD c in capsRequested) + validCaps.Add(c.AsString()); + + Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps); // Add the external too foreach (KeyValuePair kvp in m_HostCapsObj.ExternalCapsHandlers) + { + if (!validCaps.Contains(kvp.Key)) + continue; + caps[kvp.Key] = kvp.Value; + } string result = LLSDHelpers.SerialiseLLSDReply(caps); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs index 7d9f581817..a0e0078c7f 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs @@ -75,6 +75,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests [SetUp] public void SetUp() { + base.SetUp(); + UUID userId = TestHelpers.ParseTail(0x3); J2KDecoderModule j2kdm = new J2KDecoderModule(); diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index bcb7f42f42..1417a19605 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs @@ -47,10 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer /// private List m_Scenelist = new List(); -// private Dictionary m_AgentRegions = -// new Dictionary(); - private IMessageTransferModule m_TransferModule = null; + private IMessageTransferModule m_TransferModule; private bool m_Enabled = true; #region Region Module interface @@ -81,9 +79,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer // scene.RegisterModuleInterface(this); scene.EventManager.OnNewClient += OnNewClient; -// scene.EventManager.OnClientClosed += ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; -// scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene; } public void RegionLoaded(Scene scene) @@ -96,11 +92,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only"); m_Enabled = false; - m_Scenelist.Clear(); - scene.EventManager.OnNewClient -= OnNewClient; -// scene.EventManager.OnClientClosed -= ClientLoggedOut; +// m_Scenelist.Clear(); +// scene.EventManager.OnNewClient -= OnNewClient; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; -// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; } } } @@ -108,9 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer public void RemoveRegion(Scene scene) { scene.EventManager.OnNewClient -= OnNewClient; -// scene.EventManager.OnClientClosed -= ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; -// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; m_Scenelist.Remove(scene); } @@ -139,11 +131,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer // Inventory giving is conducted via instant message client.OnInstantMessage += OnInstantMessage; } - -// protected void OnSetRootAgentScene(UUID id, Scene scene) -// { -// m_AgentRegions[id] = scene; -// } private Scene FindClientScene(UUID agentId) { @@ -188,9 +175,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer { UUID folderID = new UUID(im.binaryBucket, 1); - m_log.DebugFormat("[INVENTORY TRANSFER]: Inserting original folder {0} "+ - "into agent {1}'s inventory", - folderID, new UUID(im.toAgentID)); + m_log.DebugFormat( + "[INVENTORY TRANSFER]: Inserting original folder {0} into agent {1}'s inventory", + folderID, new UUID(im.toAgentID)); InventoryFolderBase folderCopy = scene.GiveInventoryFolder(receipientID, client.AgentId, folderID, UUID.Zero); @@ -213,7 +200,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer user.ControllingClient.SendBulkUpdateInventory(folderCopy); // HACK!! - im.imSessionID = folderID.Guid; + // Insert the ID of the copied folder into the IM so that we know which item to move to trash if it + // is rejected. + // XXX: This is probably a misuse of the session ID slot. + im.imSessionID = copyID.Guid; } else { @@ -243,7 +233,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer user.ControllingClient.SendBulkUpdateInventory(itemCopy); // HACK!! - im.imSessionID = itemID.Guid; + // Insert the ID of the copied item into the IM so that we know which item to move to trash if it + // is rejected. + // XXX: This is probably a misuse of the session ID slot. + im.imSessionID = copyID.Guid; } // Send the IM to the recipient. The item is already @@ -403,7 +396,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer { folder = new InventoryFolderBase(inventoryID, client.AgentId); folder = invService.GetFolder(folder); - + if (folder != null & trashFolder != null) { previousParentFolderID = folder.ParentID; @@ -454,70 +447,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer } } -// public bool NeedSceneCacheClear(UUID agentID, Scene scene) -// { -// if (!m_AgentRegions.ContainsKey(agentID)) -// { -// // Since we can get here two ways, we need to scan -// // the scenes here. This is somewhat more expensive -// // but helps avoid a nasty bug -// // -// -// foreach (Scene s in m_Scenelist) -// { -// ScenePresence presence; -// -// if (s.TryGetScenePresence(agentID, out presence)) -// { -// // If the agent is in this scene, then we -// // are being called twice in a single -// // teleport. This is wasteful of cycles -// // but harmless due to this 2nd level check -// // -// // If the agent is found in another scene -// // then the list wasn't current -// // -// // If the agent is totally unknown, then what -// // are we even doing here?? -// // -// if (s == scene) -// { -// //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName); -// return true; -// } -// else -// { -// //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName); -// return false; -// } -// } -// } -// //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName); -// return true; -// } -// -// // The agent is left in current Scene, so we must be -// // going to another instance -// // -// if (m_AgentRegions[agentID] == scene) -// { -// //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName); -// m_AgentRegions.Remove(agentID); -// return true; -// } -// -// // Another region has claimed the agent -// // -// //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName); -// return false; -// } -// -// public void ClientLoggedOut(UUID agentID, Scene scene) -// { -// if (m_AgentRegions.ContainsKey(agentID)) -// m_AgentRegions.Remove(agentID); -// } - /// /// /// diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs new file mode 100644 index 0000000000..162a0c3210 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs @@ -0,0 +1,449 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net.Config; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenMetaverse.Assets; +using OpenSim.Framework; +using OpenSim.Region.CoreModules.Avatar.Inventory.Transfer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Tests.Common; +using OpenSim.Tests.Common.Mock; + +namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests +{ + [TestFixture] + public class InventoryTransferModuleTests : OpenSimTestCase + { + protected TestScene m_scene; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + + IConfigSource config = new IniConfigSource(); + config.AddConfig("Messaging"); + config.Configs["Messaging"].Set("InventoryTransferModule", "InventoryTransferModule"); + + m_scene = new SceneHelpers().SetupScene(); + SceneHelpers.SetupSceneModules(m_scene, config, new InventoryTransferModule()); + } + + [Test] + public void TestAcceptGivenItem() + { +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID itemId = TestHelpers.ParseTail(0x100); + UUID assetId = TestHelpers.ParseTail(0x200); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + // Create the object to test give + InventoryItemBase originalItem + = UserInventoryHelpers.CreateInventoryItem( + m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object); + + byte[] giveImBinaryBucket = new byte[17]; + byte[] itemIdBytes = itemId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + GridInstantMessage acceptIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryAccepted, + false, + "inventory accepted msg", + initialSessionId, + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(acceptIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryItemBase originalItemAfterGive + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterGive, Is.Not.Null); + Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID)); + + // Test for item successfully making it into the receiver's inventory + InventoryItemBase receivedItem + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Objects/givenObj"); + + Assert.That(receivedItem, Is.Not.Null); + Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.DeleteItems(receiverSp.UUID, new List() { receivedItem.ID }); + + InventoryItemBase originalItemAfterDelete + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterDelete, Is.Not.Null); + + // TODO: Test scenario where giver deletes their item first. + } + + /// + /// Test user rejection of a given item. + /// + /// + /// A rejected item still ends up in the user's trash folder. + /// + [Test] + public void TestRejectGivenItem() + { +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID itemId = TestHelpers.ParseTail(0x100); + UUID assetId = TestHelpers.ParseTail(0x200); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + // Create the object to test give + InventoryItemBase originalItem + = UserInventoryHelpers.CreateInventoryItem( + m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object); + + GridInstantMessage receivedIm = null; + receiverClient.OnReceivedInstantMessage += im => receivedIm = im; + + byte[] giveImBinaryBucket = new byte[17]; + byte[] itemIdBytes = itemId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + // Session ID is now the created item ID (!) + GridInstantMessage rejectIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryDeclined, + false, + "inventory declined msg", + new UUID(receivedIm.imSessionID), + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(rejectIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryItemBase originalItemAfterGive + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterGive, Is.Not.Null); + Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID)); + + // Test for item successfully making it into the receiver's inventory + InventoryItemBase receivedItem + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Trash/givenObj"); + + InventoryFolderBase trashFolder + = m_scene.InventoryService.GetFolderForType(receiverSp.UUID, AssetType.TrashFolder); + + Assert.That(receivedItem, Is.Not.Null); + Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID)); + Assert.That(receivedItem.Folder, Is.EqualTo(trashFolder.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.PurgeFolder(trashFolder); + + InventoryItemBase originalItemAfterDelete + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterDelete, Is.Not.Null); + } + + [Test] + public void TestAcceptGivenFolder() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID folderId = TestHelpers.ParseTail(0x100); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + InventoryFolderBase originalFolder + = UserInventoryHelpers.CreateInventoryFolder( + m_scene.InventoryService, giverSp.UUID, folderId, "f1", true); + + byte[] giveImBinaryBucket = new byte[17]; + giveImBinaryBucket[0] = (byte)AssetType.Folder; + byte[] itemIdBytes = folderId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + GridInstantMessage acceptIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryAccepted, + false, + "inventory accepted msg", + initialSessionId, + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(acceptIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryFolderBase originalFolderAfterGive + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterGive, Is.Not.Null); + Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID)); + + // Test for item successfully making it into the receiver's inventory + InventoryFolderBase receivedFolder + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "f1"); + + Assert.That(receivedFolder, Is.Not.Null); + Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.DeleteFolders(receiverSp.UUID, new List() { receivedFolder.ID }); + + InventoryFolderBase originalFolderAfterDelete + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterDelete, Is.Not.Null); + + // TODO: Test scenario where giver deletes their item first. + } + + /// + /// Test user rejection of a given item. + /// + /// + /// A rejected item still ends up in the user's trash folder. + /// + [Test] + public void TestRejectGivenFolder() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID folderId = TestHelpers.ParseTail(0x100); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + // Create the folder to test give + InventoryFolderBase originalFolder + = UserInventoryHelpers.CreateInventoryFolder( + m_scene.InventoryService, giverSp.UUID, folderId, "f1", true); + + GridInstantMessage receivedIm = null; + receiverClient.OnReceivedInstantMessage += im => receivedIm = im; + + byte[] giveImBinaryBucket = new byte[17]; + giveImBinaryBucket[0] = (byte)AssetType.Folder; + byte[] itemIdBytes = folderId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + // Session ID is now the created item ID (!) + GridInstantMessage rejectIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryDeclined, + false, + "inventory declined msg", + new UUID(receivedIm.imSessionID), + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(rejectIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryFolderBase originalFolderAfterGive + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterGive, Is.Not.Null); + Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID)); + + // Test for folder successfully making it into the receiver's inventory + InventoryFolderBase receivedFolder + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "Trash/f1"); + + InventoryFolderBase trashFolder + = m_scene.InventoryService.GetFolderForType(receiverSp.UUID, AssetType.TrashFolder); + + Assert.That(receivedFolder, Is.Not.Null); + Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID)); + Assert.That(receivedFolder.ParentID, Is.EqualTo(trashFolder.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.PurgeFolder(trashFolder); + + InventoryFolderBase originalFolderAfterDelete + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterDelete, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs index 8329af040b..6ae9448af4 100644 --- a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs @@ -240,7 +240,7 @@ namespace OpenSim.Region.CoreModules.Framework { caps.AppendFormat("** User {0}:\n", kvp.Key); - for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false).GetEnumerator(); kvp2.MoveNext(); ) + for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); ) { Uri uri = new Uri(kvp2.Value.ToString()); caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index e0009bb705..e6d6cbf743 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -646,11 +646,12 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess } else { - if (remoteClient == null || so.OwnerID != remoteClient.AgentId) + if (remoteClient == null || so.RootPart.OwnerID != remoteClient.AgentId) { // Taking copy of another person's item. Take to // Objects folder. folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); + so.FromFolderID = UUID.Zero; } else { @@ -666,10 +667,16 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) { - if (so.FromFolderID != UUID.Zero && userID == remoteClient.AgentId) + if (so.FromFolderID != UUID.Zero && so.RootPart.OwnerID == remoteClient.AgentId) { InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID); folder = m_Scene.InventoryService.GetFolder(f); + + if(folder.Type == 14 || folder.Type == 16) + { + // folder.Type = 6; + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); + } } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs index c0c2ca7497..c32820e94d 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs @@ -142,10 +142,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid scene.RegisterModuleInterface(this); - if (m_LocalCache.ContainsKey(scene.RegionInfo.RegionID)) - m_log.ErrorFormat("[LOCAL GRID SERVICE CONNECTOR]: simulator seems to have more than one region with the same UUID. Please correct this!"); - else - m_LocalCache.Add(scene.RegionInfo.RegionID, new RegionCache(scene)); + lock (m_LocalCache) + { + if (m_LocalCache.ContainsKey(scene.RegionInfo.RegionID)) + m_log.ErrorFormat("[LOCAL GRID SERVICE CONNECTOR]: simulator seems to have more than one region with the same UUID. Please correct this!"); + else + m_LocalCache.Add(scene.RegionInfo.RegionID, new RegionCache(scene)); + } } public void RemoveRegion(Scene scene) @@ -153,8 +156,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid if (!m_Enabled) return; - m_LocalCache[scene.RegionInfo.RegionID].Clear(); - m_LocalCache.Remove(scene.RegionInfo.RegionID); + lock (m_LocalCache) + { + m_LocalCache[scene.RegionInfo.RegionID].Clear(); + m_LocalCache.Remove(scene.RegionInfo.RegionID); + } } public void RegionLoaded(Scene scene) @@ -191,12 +197,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid // First see if it's a neighbour, even if it isn't on this sim. // Neighbour data is cached in memory, so this is fast - foreach (RegionCache rcache in m_LocalCache.Values) + + lock (m_LocalCache) { - region = rcache.GetRegionByPosition(x, y); - if (region != null) + foreach (RegionCache rcache in m_LocalCache.Values) { - return region; + region = rcache.GetRegionByPosition(x, y); + if (region != null) + { + return region; + } } } @@ -245,12 +255,15 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid { System.Text.StringBuilder caps = new System.Text.StringBuilder(); - foreach (KeyValuePair kvp in m_LocalCache) + lock (m_LocalCache) { - caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key); - List regions = kvp.Value.GetNeighbours(); - foreach (GridRegion r in regions) - caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize); + foreach (KeyValuePair kvp in m_LocalCache) + { + caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key); + List regions = kvp.Value.GetNeighbours(); + foreach (GridRegion r in regions) + caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize); + } } MainConsole.Instance.Output(caps.ToString()); diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index ff7e5edba1..8fe2d72bdc 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -389,10 +389,12 @@ namespace OpenSim.Region.Framework.Scenes if (value) { if (!m_active) - Start(); + Start(false); } else { + // This appears assymetric with Start() above but is not - setting m_active = false stops the loops + // XXX: Possibly this should be in an explicit Stop() method for symmetry. m_active = false; } } @@ -1331,10 +1333,18 @@ namespace OpenSim.Region.Framework.Scenes } } + public override void Start() + { + Start(true); + } + /// /// Start the scene /// - public void Start() + /// + /// Start the scripts within the scene. + /// + public void Start(bool startScripts) { m_active = true; @@ -1353,6 +1363,8 @@ namespace OpenSim.Region.Framework.Scenes m_heartbeatThread = Watchdog.StartThread( Heartbeat, string.Format("Heartbeat ({0})", RegionInfo.RegionName), ThreadPriority.Normal, false, false); + + StartScripts(); } /// @@ -3699,7 +3711,7 @@ namespace OpenSim.Region.Framework.Scenes // On login test land permisions if (vialogin) { - if (land != null && !TestLandRestrictions(agent, land, out reason)) + if (land != null && !TestLandRestrictions(agent.AgentID, out reason, ref agent.startpos.X, ref agent.startpos.Y)) { m_authenticateHandler.RemoveCircuit(agent.circuitcode); return false; @@ -3880,20 +3892,37 @@ namespace OpenSim.Region.Framework.Scenes return true; } - private bool TestLandRestrictions(AgentCircuitData agent, ILandObject land, out string reason) + public bool TestLandRestrictions(UUID agentID, out string reason, ref float posX, ref float posY) { - bool banned = land.IsBannedFromLand(agent.AgentID); - bool restricted = land.IsRestrictedFromLand(agent.AgentID); + if (posX < 0) + posX = 0; + else if (posX >= 256) + posX = 255.999f; + if (posY < 0) + posY = 0; + else if (posY >= 256) + posY = 255.999f; + + reason = String.Empty; + if (Permissions.IsGod(agentID)) + return true; + + ILandObject land = LandChannel.GetLandObject(posX, posY); + if (land == null) + return false; + + bool banned = land.IsBannedFromLand(agentID); + bool restricted = land.IsRestrictedFromLand(agentID); if (banned || restricted) { - ILandObject nearestParcel = GetNearestAllowedParcel(agent.AgentID, agent.startpos.X, agent.startpos.Y); + ILandObject nearestParcel = GetNearestAllowedParcel(agentID, posX, posY); if (nearestParcel != null) { //Move agent to nearest allowed Vector3 newPosition = GetParcelCenterAtGround(nearestParcel); - agent.startpos.X = newPosition.X; - agent.startpos.Y = newPosition.Y; + posX = newPosition.X; + posY = newPosition.Y; } else { @@ -5478,6 +5507,8 @@ namespace OpenSim.Region.Framework.Scenes /// public bool QueryAccess(UUID agentID, Vector3 position, out string reason) { + reason = "You are banned from the region"; + if (EntityTransferModule.IsInTransit(agentID)) { reason = "Agent is still in transit from this region"; @@ -5489,6 +5520,12 @@ namespace OpenSim.Region.Framework.Scenes return false; } + if (Permissions.IsGod(agentID)) + { + reason = String.Empty; + return true; + } + // FIXME: Root agent count is currently known to be inaccurate. This forces a recount before we check. // However, the long term fix is to make sure root agent count is always accurate. m_sceneGraph.RecalculateStats(); @@ -5509,6 +5546,41 @@ namespace OpenSim.Region.Framework.Scenes } } + ScenePresence presence = GetScenePresence(agentID); + IClientAPI client = null; + AgentCircuitData aCircuit = null; + + if (presence != null) + { + client = presence.ControllingClient; + if (client != null) + aCircuit = client.RequestClientInfo(); + } + + // We may be called before there is a presence or a client. + // Fake AgentCircuitData to keep IAuthorizationModule smiling + if (client == null) + { + aCircuit = new AgentCircuitData(); + aCircuit.AgentID = agentID; + aCircuit.firstname = String.Empty; + aCircuit.lastname = String.Empty; + } + + try + { + if (!AuthorizeUser(aCircuit, out reason)) + { + // m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID); + return false; + } + } + catch (Exception e) + { + m_log.DebugFormat("[SCENE]: Exception authorizing agent: {0} "+ e.StackTrace, e.Message); + return false; + } + if (position == Vector3.Zero) // Teleport { if (!RegionInfo.EstateSettings.AllowDirectTeleport) @@ -5542,6 +5614,27 @@ namespace OpenSim.Region.Framework.Scenes } } } + + float posX = 128.0f; + float posY = 128.0f; + + if (!TestLandRestrictions(agentID, out reason, ref posX, ref posY)) + { + // m_log.DebugFormat("[SCENE]: Denying {0} because they are banned on all parcels", agentID); + return false; + } + } + else // Walking + { + ILandObject land = LandChannel.GetLandObject(position.X, position.Y); + if (land == null) + return false; + + bool banned = land.IsBannedFromLand(agentID); + bool restricted = land.IsRestrictedFromLand(agentID); + + if (banned || restricted) + return false; } reason = String.Empty; diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index d3e968e531..d2097ea7ad 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -561,6 +561,10 @@ namespace OpenSim.Region.Framework.Scenes get { return false; } } + public virtual void Start() + { + } + public void Restart() { // This has to be here to fire the event diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs index fdf2cb98f4..231f0f8197 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs @@ -75,11 +75,11 @@ private sealed class BulletBodyUnman : BulletBody private sealed class BulletShapeUnman : BulletShape { public IntPtr ptr; - public BulletShapeUnman(IntPtr xx, BSPhysicsShapeType typ) + public BulletShapeUnman(IntPtr xx, BSPhysicsShapeType typ) : base() { ptr = xx; - type = typ; + shapeType = typ; } public override bool HasPhysicalShape { @@ -91,7 +91,7 @@ private sealed class BulletShapeUnman : BulletShape } public override BulletShape Clone() { - return new BulletShapeUnman(ptr, type); + return new BulletShapeUnman(ptr, shapeType); } public override bool ReferenceSame(BulletShape other) { @@ -255,7 +255,7 @@ public override BulletShape CreateHullShape(BulletWorld world, int hullCount, fl { BulletWorldUnman worldu = world as BulletWorldUnman; return new BulletShapeUnman( - BSAPICPP.CreateHullShape2(worldu.ptr, hullCount, hulls), + BSAPICPP.CreateHullShape2(worldu.ptr, hullCount, hulls), BSPhysicsShapeType.SHAPE_HULL); } @@ -375,7 +375,7 @@ public override BulletShape DuplicateCollisionShape(BulletWorld world, BulletSha { BulletWorldUnman worldu = world as BulletWorldUnman; BulletShapeUnman srcShapeu = srcShape as BulletShapeUnman; - return new BulletShapeUnman(BSAPICPP.DuplicateCollisionShape2(worldu.ptr, srcShapeu.ptr, id), srcShape.type); + return new BulletShapeUnman(BSAPICPP.DuplicateCollisionShape2(worldu.ptr, srcShapeu.ptr, id), srcShape.shapeType); } public override bool DeleteCollisionShape(BulletWorld world, BulletShape shape) @@ -1503,7 +1503,7 @@ public static extern void DestroyObject2(IntPtr sim, IntPtr obj); public static extern IntPtr CreateGroundPlaneShape2(uint id, float height, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateTerrainShape2(uint id, Vector3 size, float minHeight, float maxHeight, +public static extern IntPtr CreateTerrainShape2(uint id, Vector3 size, float minHeight, float maxHeight, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float scaleFactor, float collisionMargin); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs index b37265a46f..59780ae8ee 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs @@ -81,11 +81,11 @@ private sealed class BulletBodyXNA : BulletBody private sealed class BulletShapeXNA : BulletShape { public CollisionShape shape; - public BulletShapeXNA(CollisionShape xx, BSPhysicsShapeType typ) + public BulletShapeXNA(CollisionShape xx, BSPhysicsShapeType typ) : base() { shape = xx; - type = typ; + shapeType = typ; } public override bool HasPhysicalShape { @@ -97,7 +97,7 @@ private sealed class BulletShapeXNA : BulletShape } public override BulletShape Clone() { - return new BulletShapeXNA(shape, type); + return new BulletShapeXNA(shape, shapeType); } public override bool ReferenceSame(BulletShape other) { @@ -137,8 +137,8 @@ private sealed class BulletConstraintXNA : BulletConstraint internal int LastEntityProperty = 0; internal EntityProperties[] UpdatedObjects; - internal Dictionary specialCollisionObjects; - + internal Dictionary specialCollisionObjects; + private static int m_collisionsThisFrame; private BSScene PhysicsScene { get; set; } @@ -151,7 +151,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } /// - /// + /// /// /// /// @@ -174,7 +174,7 @@ private sealed class BulletConstraintXNA : BulletConstraint DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; TypedConstraint constraint = (pConstraint as BulletConstraintXNA).constrain; world.AddConstraint(constraint, pDisableCollisionsBetweenLinkedObjects); - + return true; } @@ -300,7 +300,7 @@ private sealed class BulletConstraintXNA : BulletConstraint public override bool GetForceUpdateAllAabbs(BulletWorld pWorld) { DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; return world.GetForceUpdateAllAabbs(); - + } public override void SetForceUpdateAllAabbs(BulletWorld pWorld, bool pForce) { @@ -404,7 +404,7 @@ private sealed class BulletConstraintXNA : BulletConstraint IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion); mat._origin = vposition; collisionObject.SetWorldTransform(mat); - + } public override Vector3 GetPosition(BulletBody pCollisionObject) @@ -457,7 +457,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; collisionObject.Activate(pforceactivation); - + } public override Quaternion GetOrientation(BulletBody pCollisionObject) @@ -486,7 +486,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; return collisionObject.GetCcdSweptSphereRadius(); - + } public override IntPtr GetUserPointer(BulletBody pCollisionObject) @@ -559,8 +559,8 @@ private sealed class BulletConstraintXNA : BulletConstraint } - public override BulletConstraint Create6DofConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, - Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + public override BulletConstraint Create6DofConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) { @@ -604,7 +604,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } /// - /// + /// /// /// /// @@ -824,7 +824,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { RigidBody body = (pBody as BulletBodyXNA).rigidBody; float angularDamping = body.GetAngularDamping(); - body.SetDamping(lin_damping, angularDamping); + body.SetDamping(lin_damping, angularDamping); } public override float GetLinearDamping(BulletBody pBody) @@ -907,7 +907,7 @@ private sealed class BulletConstraintXNA : BulletConstraint RigidBody bo = co as RigidBody; if (bo == null) { - + if (world.IsInWorld(co)) { world.RemoveCollisionObject(co); @@ -915,7 +915,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } else { - + if (world.IsInWorld(bo)) { world.RemoveRigidBody(bo); @@ -947,7 +947,7 @@ private sealed class BulletConstraintXNA : BulletConstraint // TODO: Turn this from a reference copy to a Value Copy. BulletShapeXNA shape2 = new BulletShapeXNA(shape1, BSShapeTypeFromBroadPhaseNativeType(shape1.GetShapeType())); - + return shape2; } @@ -957,7 +957,7 @@ private sealed class BulletConstraintXNA : BulletConstraint return false; } //(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation); - + public override BulletBody CreateBodyFromShape(BulletWorld pWorld, BulletShape pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation) { CollisionWorld world = (pWorld as BulletWorldXNA).world; @@ -993,11 +993,11 @@ private sealed class BulletConstraintXNA : BulletConstraint m_startWorldTransform = IndexedMatrix.Identity; */ body.SetUserPointer(pLocalID); - + return new BulletBodyXNA(pLocalID, body); } - + public override BulletBody CreateBodyWithDefaultMotionState( BulletShape pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation) { @@ -1025,7 +1025,7 @@ private sealed class BulletConstraintXNA : BulletConstraint public override Vector3 GetAnisotripicFriction(BulletConstraint pconstrain) { - /* TODO */ + /* TODO */ return Vector3.Zero; } public override Vector3 SetAnisotripicFriction(BulletConstraint pconstrain, Vector3 frict) { /* TODO */ return Vector3.Zero; } @@ -1035,7 +1035,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; return collisionObject.IsStaticObject(); - + } public override bool IsKinematicObject(BulletBody pCollisionObject) { @@ -1098,10 +1098,10 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletWorldXNA(1, PhysicsScene, BSAPIXNA.Initialize2(worldExtent, configparms, maxCollisions, ref collisionArray, maxUpdates, ref updateArray, null)); } - private static DiscreteDynamicsWorld Initialize2(Vector3 worldExtent, + private static DiscreteDynamicsWorld Initialize2(Vector3 worldExtent, ConfigurationParameters[] o, int mMaxCollisionsPerFrame, ref CollisionDesc[] collisionArray, - int mMaxUpdatesPerFrame, ref EntityProperties[] updateArray, + int mMaxUpdatesPerFrame, ref EntityProperties[] updateArray, object mDebugLogCallbackHandle) { CollisionWorld.WorldData.ParamData p = new CollisionWorld.WorldData.ParamData(); @@ -1138,9 +1138,9 @@ private sealed class BulletConstraintXNA : BulletConstraint p.avatarCapsuleDepth = BSParam.AvatarCapsuleDepth; p.avatarCapsuleHeight = BSParam.AvatarCapsuleHeight; p.avatarContactProcessingThreshold = BSParam.AvatarContactProcessingThreshold; - + p.vehicleAngularDamping = BSParam.VehicleAngularDamping; - + p.maxPersistantManifoldPoolSize = o[0].maxPersistantManifoldPoolSize; p.maxCollisionAlgorithmPoolSize = o[0].maxCollisionAlgorithmPoolSize; p.shouldDisableContactPoolDynamicAllocation = o[0].shouldDisableContactPoolDynamicAllocation; @@ -1160,7 +1160,7 @@ private sealed class BulletConstraintXNA : BulletConstraint p.linkConstraintSolverIterations = BSParam.LinkConstraintSolverIterations; p.physicsLoggingFrames = o[0].physicsLoggingFrames; DefaultCollisionConstructionInfo ccci = new DefaultCollisionConstructionInfo(); - + DefaultCollisionConfiguration cci = new DefaultCollisionConfiguration(); CollisionDispatcher m_dispatcher = new CollisionDispatcher(cci); @@ -1263,7 +1263,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } } return ret; - + } public override float GetAngularMotionDisc(BulletShape pShape) @@ -1353,10 +1353,10 @@ private sealed class BulletConstraintXNA : BulletConstraint CollisionShape shape = (pShape as BulletShapeXNA).shape; gObj.SetCollisionShape(shape); gObj.SetUserPointer(pLocalID); - + if (specialCollisionObjects.ContainsKey(pLocalID)) specialCollisionObjects[pLocalID] = gObj; - else + else specialCollisionObjects.Add(pLocalID, gObj); // TODO: Add to Special CollisionObjects! @@ -1447,8 +1447,8 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletShapeXNA(ret, BSShapeTypeFromBroadPhaseNativeType(ret.GetShapeType())); } - public override BulletShape GetChildShapeFromCompoundShapeIndex(BulletShape cShape, int indx) { - + public override BulletShape GetChildShapeFromCompoundShapeIndex(BulletShape cShape, int indx) { + if (cShape == null) return null; CompoundShape compoundShape = (cShape as BulletShapeXNA).shape as CompoundShape; @@ -1456,7 +1456,7 @@ private sealed class BulletConstraintXNA : BulletConstraint BulletShape retShape = new BulletShapeXNA(shape, BSShapeTypeFromBroadPhaseNativeType(shape.GetShapeType())); - return retShape; + return retShape; } public BSPhysicsShapeType BSShapeTypeFromBroadPhaseNativeType(BroadphaseNativeTypes pin) @@ -1598,8 +1598,8 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletShapeXNA(m_planeshape, BSPhysicsShapeType.SHAPE_GROUNDPLANE); } - public override BulletConstraint Create6DofSpringConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, - Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + public override BulletConstraint Create6DofSpringConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) { @@ -1745,7 +1745,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; CompoundShape compoundshape = new CompoundShape(false); - + compoundshape.SetMargin(world.WorldSettings.Params.collisionMargin); int ii = 1; @@ -1761,7 +1761,7 @@ private sealed class BulletConstraintXNA : BulletConstraint int ender = ((ii + 4) + (vertexCount*3)); for (int iii = ii + 4; iii < ender; iii+=3) { - + virts.Add(new IndexedVector3(pConvHulls[iii], pConvHulls[iii + 1], pConvHulls[iii +2])); } ConvexHullShape convexShape = new ConvexHullShape(virts, vertexCount); @@ -1769,7 +1769,7 @@ private sealed class BulletConstraintXNA : BulletConstraint compoundshape.AddChildShape(ref childTrans, convexShape); ii += (vertexCount*3 + 4); } - + return new BulletShapeXNA(compoundshape, BSPhysicsShapeType.SHAPE_HULL); } @@ -1791,13 +1791,13 @@ private sealed class BulletConstraintXNA : BulletConstraint public override BulletShape CreateMeshShape(BulletWorld pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats) { //DumpRaw(indices,verticesAsFloats,pIndicesCount,pVerticesCount); - + for (int iter = 0; iter < pVerticesCount; iter++) { if (verticesAsFloats[iter] > 0 && verticesAsFloats[iter] < 0.0001) verticesAsFloats[iter] = 0; if (verticesAsFloats[iter] < 0 && verticesAsFloats[iter] > -0.0001) verticesAsFloats[iter] = 0; } - + ObjectArray indicesarr = new ObjectArray(indices); ObjectArray vertices = new ObjectArray(verticesAsFloats); DumpRaw(indicesarr,vertices,pIndicesCount,pVerticesCount); @@ -1811,7 +1811,7 @@ private sealed class BulletConstraintXNA : BulletConstraint mesh.m_vertexStride = 3; mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT; mesh.m_triangleIndexStride = 3; - + TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray(); tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER); BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(tribuilder, true,true); @@ -1822,7 +1822,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } public static void DumpRaw(ObjectArrayindices, ObjectArray vertices, int pIndicesCount,int pVerticesCount ) { - + String fileName = "objTest3.raw"; String completePath = System.IO.Path.Combine(Util.configDir(), fileName); StreamWriter sw = new StreamWriter(completePath); @@ -1848,7 +1848,7 @@ private sealed class BulletConstraintXNA : BulletConstraint string s = vertices[indices[i * 3]].ToString("0.0000"); s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000"); s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000"); - + sw.Write(s + "\n"); } @@ -1870,7 +1870,7 @@ private sealed class BulletConstraintXNA : BulletConstraint mesh.m_vertexStride = 3; mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT; mesh.m_triangleIndexStride = 3; - + TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray(); tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER); @@ -1901,7 +1901,7 @@ private sealed class BulletConstraintXNA : BulletConstraint sw.Close(); } - public override BulletShape CreateTerrainShape(uint id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, + public override BulletShape CreateTerrainShape(uint id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, float scaleFactor, float collisionMargin) { const int upAxis = 2; @@ -1943,14 +1943,14 @@ private sealed class BulletConstraintXNA : BulletConstraint /* TODO */ updatedEntityCount = 0; collidersCount = 0; - + int ret = PhysicsStep2(world,timeStep,maxSubSteps,fixedTimeStep,out updatedEntityCount,out world.physicsScene.m_updateArray, out collidersCount, out world.physicsScene.m_collisionArray); return ret; } - private int PhysicsStep2(BulletWorld pWorld, float timeStep, int m_maxSubSteps, float m_fixedTimeStep, + private int PhysicsStep2(BulletWorld pWorld, float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out EntityProperties[] updatedEntities, out int collidersCount, out CollisionDesc[] colliders) { @@ -1959,24 +1959,24 @@ private sealed class BulletConstraintXNA : BulletConstraint return epic; } - private int PhysicsStepint(BulletWorld pWorld,float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, + private int PhysicsStepint(BulletWorld pWorld,float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out EntityProperties[] updatedEntities, out int collidersCount, out CollisionDesc[] colliders, int maxCollisions, int maxUpdates) { int numSimSteps = 0; Array.Clear(UpdatedObjects, 0, UpdatedObjects.Length); Array.Clear(UpdatedCollisions, 0, UpdatedCollisions.Length); LastEntityProperty=0; - + LastCollisionDesc=0; - + updatedEntityCount = 0; collidersCount = 0; - + if (pWorld is BulletWorldXNA) { @@ -2033,7 +2033,7 @@ private sealed class BulletConstraintXNA : BulletConstraint collidersCount = LastCollisionDesc; colliders = UpdatedCollisions; - + } else @@ -2041,15 +2041,15 @@ private sealed class BulletConstraintXNA : BulletConstraint //if (updatedEntities is null) //updatedEntities = new List(); //updatedEntityCount = 0; - - + + //collidersCount = 0; - + updatedEntities = new EntityProperties[0]; - + colliders = new CollisionDesc[0]; - + } return numSimSteps; } @@ -2057,7 +2057,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { IOverlappingPairCache cache = obj.GetOverlappingPairCache(); ObjectArray pairs = cache.GetOverlappingPairArray(); - + DiscreteDynamicsWorld world = (PhysicsScene.World as BulletWorldXNA).world; PersistentManifoldArray manifoldArray = new PersistentManifoldArray(); BroadphasePair collisionPair; @@ -2069,7 +2069,7 @@ private sealed class BulletConstraintXNA : BulletConstraint ManifoldPoint pt; int numPairs = pairs.Count; - + for (int i = 0; i < numPairs; i++) { manifoldArray.Clear(); @@ -2078,7 +2078,7 @@ private sealed class BulletConstraintXNA : BulletConstraint collisionPair = world.GetPairCache().FindPair(pairs[i].m_pProxy0, pairs[i].m_pProxy1); if (collisionPair == null) continue; - + collisionPair.m_algorithm.GetAllContactManifolds(manifoldArray); for (int j = 0; j < manifoldArray.Count; j++) { @@ -2101,7 +2101,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } private static void RecordCollision(BSAPIXNA world, CollisionObject objA, CollisionObject objB, IndexedVector3 contact, IndexedVector3 norm, float penetration) { - + IndexedVector3 contactNormal = norm; if ((objA.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0 && (objB.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0) @@ -2171,11 +2171,11 @@ private sealed class BulletConstraintXNA : BulletConstraint if (NotMe is BulletBodyXNA && NotMe.HasPhysicalBody) { CollisionObject AvoidBody = (NotMe as BulletBodyXNA).body; - + IndexedVector3 rOrigin = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z); IndexedVector3 rEnd = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z - pRayHeight); using ( - ClosestNotMeRayResultCallback rayCallback = + ClosestNotMeRayResultCallback rayCallback = new ClosestNotMeRayResultCallback(rOrigin, rEnd, AvoidBody) ) { @@ -2191,9 +2191,9 @@ private sealed class BulletConstraintXNA : BulletConstraint return false; } } - - + + public class SimMotionState : DefaultMotionState { @@ -2286,12 +2286,12 @@ private sealed class BulletConstraintXNA : BulletConstraint m_lastProperties = m_properties; if (m_world.LastEntityProperty < m_world.UpdatedObjects.Length) m_world.UpdatedObjects[m_world.LastEntityProperty++]=(m_properties); - + //(*m_updatesThisFrame)[m_properties.ID] = &m_properties; } - - - + + + } public override void SetRigidBody(RigidBody body) @@ -2314,7 +2314,7 @@ private sealed class BulletConstraintXNA : BulletConstraint (((v1.Z - nEpsilon) < v2.Z) && (v2.Z < (v1.Z + nEpsilon))) && (((v1.W - nEpsilon) < v2.W) && (v2.W < (v1.W + nEpsilon))); } - + } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs index bd5ee0b17f..4e067b5f53 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs @@ -87,8 +87,8 @@ public class BSActorAvatarMove : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } @@ -115,7 +115,7 @@ public class BSActorAvatarMove : BSActor if (m_velocityMotor == null) { // Infinite decay and timescale values so motor only changes current to target values. - m_velocityMotor = new BSVMotor("BSCharacter.Velocity", + m_velocityMotor = new BSVMotor("BSCharacter.Velocity", 0.2f, // time scale BSMotor.Infinite, // decay time scale BSMotor.InfiniteVector, // friction timescale diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs index 92ace66789..3630ca8e67 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs @@ -87,8 +87,8 @@ public class BSActorHover : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs index 09ee32b07f..7801d8e47f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs @@ -63,7 +63,7 @@ public class BSActorLockAxis : BSActor // BSActor.Refresh() public override void Refresh() { - m_physicsScene.DetailLog("{0},BSActorLockAxis,refresh,lockedAxis={1},enabled={2},pActive={3}", + m_physicsScene.DetailLog("{0},BSActorLockAxis,refresh,lockedAxis={1},enabled={2},pActive={3}", m_controllingPrim.LocalID, m_controllingPrim.LockedAxis, Enabled, m_controllingPrim.IsPhysicallyActive); // If all the axis are free, we don't need to exist if (m_controllingPrim.LockedAxis == m_controllingPrim.LockedAxisFree) @@ -85,8 +85,8 @@ public class BSActorLockAxis : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { if (LockAxisConstraint != null) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs index 56aacc560b..1b598fdfba 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs @@ -88,8 +88,8 @@ public class BSActorMoveToTarget : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the moveToTarget since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs index 3ad138d25d..c0f40fdd0a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs @@ -89,8 +89,8 @@ public class BSActorSetForce : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs index 7a791ec770..b3806e17ad 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs @@ -89,8 +89,8 @@ public class BSActorSetTorque : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs index 12a88177a3..fff63e4eb2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs @@ -106,9 +106,9 @@ public class BSActorCollection { ForEachActor(a => a.Refresh()); } - public void RemoveBodyDependencies() + public void RemoveDependencies() { - ForEachActor(a => a.RemoveBodyDependencies()); + ForEachActor(a => a.RemoveDependencies()); } } @@ -117,7 +117,7 @@ public class BSActorCollection /// Each physical object can have 'actors' who are pushing the object around. /// This can be used for hover, locking axis, making vehicles, etc. /// Each physical object can have multiple actors acting on it. -/// +/// /// An actor usually registers itself with physics scene events (pre-step action) /// and modifies the parameters on the host physical object. /// @@ -154,7 +154,7 @@ public abstract class BSActor public abstract void Refresh(); // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. - public abstract void RemoveBodyDependencies(); + public abstract void RemoveDependencies(); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs index bfeec24419..3378c932d3 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs @@ -298,7 +298,7 @@ public abstract class BSAPITemplate { // Returns the name of the underlying Bullet engine public abstract string BulletEngineName { get; } -public abstract string BulletEngineVersion { get; protected set;} +public abstract string BulletEngineVersion { get; protected set;} // Initialization and simulation public abstract BulletWorld Initialize(Vector3 maxPosition, ConfigurationParameters parms, @@ -373,7 +373,7 @@ public abstract void DestroyObject(BulletWorld sim, BulletBody obj); // ===================================================================================== public abstract BulletShape CreateGroundPlaneShape(UInt32 id, float height, float collisionMargin); -public abstract BulletShape CreateTerrainShape(UInt32 id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, +public abstract BulletShape CreateTerrainShape(UInt32 id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, float scaleFactor, float collisionMargin); // ===================================================================================== @@ -388,7 +388,7 @@ public abstract BulletConstraint Create6DofConstraintToPoint(BulletWorld world, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); public abstract BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1, - Vector3 frameInBloc, Quaternion frameInBrot, + Vector3 frameInBloc, Quaternion frameInBrot, bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies); public abstract BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index a0d58d3fa3..542f732365 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -95,18 +95,18 @@ public sealed class BSCharacter : BSPhysObject // the avatar seeking to reach the motor's target speed. // This motor runs as a prestep action for the avatar so it will keep the avatar // standing as well as moving. Destruction of the avatar will destroy the pre-step action. - m_moveActor = new BSActorAvatarMove(PhysicsScene, this, AvatarMoveActorName); + m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName); PhysicalActors.Add(AvatarMoveActorName, m_moveActor); DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", LocalID, _size, Scale, Density, _avatarVolume, RawMass); // do actual creation in taint time - PhysicsScene.TaintedObject("BSCharacter.create", delegate() + PhysScene.TaintedObject("BSCharacter.create", delegate() { DetailLog("{0},BSCharacter.create,taint", LocalID); // New body and shape into PhysBody and PhysShape - PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this); + PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this); SetPhysicalProperties(); }); @@ -119,18 +119,18 @@ public sealed class BSCharacter : BSPhysObject base.Destroy(); DetailLog("{0},BSCharacter.Destroy", LocalID); - PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() + PhysScene.TaintedObject("BSCharacter.destroy", delegate() { - PhysicsScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); + PhysScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); PhysBody.Clear(); - PhysicsScene.Shapes.DereferenceShape(PhysShape, null /* bodyCallback */); - PhysShape.Clear(); + PhysShape.Dereference(PhysScene); + PhysShape = new BSShapeNull(); }); } private void SetPhysicalProperties() { - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); ZeroMotion(true); ForcePosition = _position; @@ -145,35 +145,35 @@ public sealed class BSCharacter : BSPhysObject // Needs to be reset especially when an avatar is recreated after crossing a region boundry. Flying = _flying; - PhysicsScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); - PhysicsScene.PE.SetMargin(PhysShape, PhysicsScene.Params.collisionMargin); - PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); - PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + PhysScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); + PhysScene.PE.SetMargin(PhysShape.physShapeInfo, PhysScene.Params.collisionMargin); + PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); if (BSParam.CcdMotionThreshold > 0f) { - PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); - PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); } UpdatePhysicalMassProperties(RawMass, false); // Make so capsule does not fall over - PhysicsScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); + PhysScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); // The avatar mover sets some parameters. PhysicalActors.Refresh(); - PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); + PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); - PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); + PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); // Do this after the object has been added to the world PhysBody.collisionType = CollisionType.Avatar; - PhysBody.ApplyCollisionMask(PhysicsScene); + PhysBody.ApplyCollisionMask(PhysScene); } @@ -203,14 +203,14 @@ public sealed class BSCharacter : BSPhysObject DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", LocalID, _size, Scale, Density, _avatarVolume, RawMass); - PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() + PhysScene.TaintedObject("BSCharacter.setSize", delegate() { - if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) + if (PhysBody.HasPhysicalBody && PhysShape.physShapeInfo.HasPhysicalShape) { - PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); + PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); UpdatePhysicalMassProperties(RawMass, true); // Make sure this change appears as a property update event - PhysicsScene.PE.PushUpdate(PhysBody); + PhysScene.PE.PushUpdate(PhysBody); } }); @@ -221,11 +221,6 @@ public sealed class BSCharacter : BSPhysObject { set { BaseShape = value; } } - // I want the physics engine to make an avatar capsule - public override BSPhysicsShapeType PreferredPhysicalShape - { - get {return BSPhysicsShapeType.SHAPE_CAPSULE; } - } public override bool Grabbed { set { _grabbed = value; } @@ -252,24 +247,24 @@ public sealed class BSCharacter : BSPhysObject _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties directly into the physics engine - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.ClearAllForces(PhysBody); + PhysScene.PE.ClearAllForces(PhysBody); }); } public override void ZeroAngularMotion(bool inTaintTime) { _rotationalVelocity = OMV.Vector3.Zero; - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); - PhysicsScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); + PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); + PhysScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); // The next also get rid of applied linear force but the linear velocity is untouched. - PhysicsScene.PE.ClearForces(PhysBody); + PhysScene.PE.ClearForces(PhysBody); } }); } @@ -291,7 +286,7 @@ public sealed class BSCharacter : BSPhysObject set { _position = value; - PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() + PhysScene.TaintedObject("BSCharacter.setPosition", delegate() { DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); PositionSanityCheck(); @@ -301,14 +296,14 @@ public sealed class BSCharacter : BSPhysObject } public override OMV.Vector3 ForcePosition { get { - _position = PhysicsScene.PE.GetPosition(PhysBody); + _position = PhysScene.PE.GetPosition(PhysBody); return _position; } set { _position = value; if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); } } } @@ -322,18 +317,18 @@ public sealed class BSCharacter : BSPhysObject bool ret = false; // TODO: check for out of bounds - if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) + if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) { // The character is out of the known/simulated area. // Force the avatar position to be within known. ScenePresence will use the position // plus the velocity to decide if the avatar is moving out of the region. - RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); + RawPosition = PhysScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); return true; } // If below the ground, move the avatar up - float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); + float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); if (Position.Z < terrainHeight) { DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, _position, terrainHeight); @@ -342,7 +337,7 @@ public sealed class BSCharacter : BSPhysObject } if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) { - float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); + float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(_position); if (Position.Z < waterHeight) { _position.Z = waterHeight; @@ -363,7 +358,7 @@ public sealed class BSCharacter : BSPhysObject { // The new position value must be pushed into the physics engine but we can't // just assign to "Position" because of potential call loops. - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() { DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); ForcePosition = _position; @@ -376,13 +371,13 @@ public sealed class BSCharacter : BSPhysObject public override float Mass { get { return _mass; } } // used when we only want this prim's mass and not the linkset thing - public override float RawMass { + public override float RawMass { get {return _mass; } } public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) { - OMV.Vector3 localInertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); - PhysicsScene.PE.SetMassProps(PhysBody, physMass, localInertia); + OMV.Vector3 localInertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); + PhysScene.PE.SetMassProps(PhysBody, physMass, localInertia); } public override OMV.Vector3 Force { @@ -390,11 +385,11 @@ public sealed class BSCharacter : BSPhysObject set { RawForce = value; // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); - PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() + PhysScene.TaintedObject("BSCharacter.SetForce", delegate() { DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, RawForce); if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetObjectForce(PhysBody, RawForce); + PhysScene.PE.SetObjectForce(PhysBody, RawForce); }); } } @@ -437,7 +432,7 @@ public sealed class BSCharacter : BSPhysObject set { RawVelocity = value; // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, RawVelocity); - PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() + PhysScene.TaintedObject("BSCharacter.setVelocity", delegate() { if (m_moveActor != null) m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, true /* inTaintTime */); @@ -450,11 +445,11 @@ public sealed class BSCharacter : BSPhysObject public override OMV.Vector3 ForceVelocity { get { return RawVelocity; } set { - PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity"); + PhysScene.AssertInTaintTime("BSCharacter.ForceVelocity"); RawVelocity = value; - PhysicsScene.PE.SetLinearVelocity(PhysBody, RawVelocity); - PhysicsScene.PE.Activate(PhysBody, true); + PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); + PhysScene.PE.Activate(PhysBody, true); } } public override OMV.Vector3 Torque { @@ -484,7 +479,7 @@ public sealed class BSCharacter : BSPhysObject if (_orientation != value) { _orientation = value; - PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() + PhysScene.TaintedObject("BSCharacter.setOrientation", delegate() { ForceOrientation = _orientation; }); @@ -496,7 +491,7 @@ public sealed class BSCharacter : BSPhysObject { get { - _orientation = PhysicsScene.PE.GetOrientation(PhysBody); + _orientation = PhysScene.PE.GetOrientation(PhysBody); return _orientation; } set @@ -505,7 +500,7 @@ public sealed class BSCharacter : BSPhysObject if (PhysBody.HasPhysicalBody) { // _position = PhysicsScene.PE.GetPosition(BSBody); - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); } } } @@ -554,14 +549,14 @@ public sealed class BSCharacter : BSPhysObject public override bool FloatOnWater { set { _floatOnWater = value; - PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() + PhysScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() { if (PhysBody.HasPhysicalBody) { if (_floatOnWater) - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); else - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); } }); } @@ -582,7 +577,7 @@ public sealed class BSCharacter : BSPhysObject public override float Buoyancy { get { return _buoyancy; } set { _buoyancy = value; - PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() + PhysScene.TaintedObject("BSCharacter.setBuoyancy", delegate() { DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); ForceBuoyancy = _buoyancy; @@ -591,8 +586,8 @@ public sealed class BSCharacter : BSPhysObject } public override float ForceBuoyancy { get { return _buoyancy; } - set { - PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); + set { + PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); _buoyancy = value; DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); @@ -600,7 +595,7 @@ public sealed class BSCharacter : BSPhysObject float grav = BSParam.Gravity * (1f - _buoyancy); Gravity = new OMV.Vector3(0f, 0f, grav); if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetGravity(PhysBody, Gravity); + PhysScene.PE.SetGravity(PhysBody, Gravity); } } @@ -618,7 +613,7 @@ public sealed class BSCharacter : BSPhysObject public override void AddForce(OMV.Vector3 force, bool pushforce) { // Since this force is being applied in only one step, make this a force per second. - OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep; + OMV.Vector3 addForce = force / PhysScene.LastTimeStep; AddForce(addForce, pushforce, false); } private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { @@ -627,13 +622,13 @@ public sealed class BSCharacter : BSPhysObject OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() { // Bullet adds this central force to the total force for this tick // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); + PhysScene.PE.ApplyCentralForce(PhysBody, addForce); } }); } @@ -652,7 +647,7 @@ public sealed class BSCharacter : BSPhysObject private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size) { OMV.Vector3 newScale; - + // Bullet's capsule total height is the "passed height + radius * 2"; // The base capsule is 1 diameter and 2 height (passed radius=0.5, passed height = 1) // The number we pass in for 'scaling' is the multiplier to get that base diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs index 7714a0352c..ed89f630e1 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs @@ -45,7 +45,7 @@ public sealed class BSConstraintHinge : BSConstraint m_body1 = obj1; m_body2 = obj2; m_constraint = PhysicsScene.PE.CreateHingeConstraint(world, obj1, obj2, - pivotInA, pivotInB, axisInA, axisInB, + pivotInA, pivotInB, axisInA, axisInB, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); m_enabled = true; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 612c68b913..c5bee6d705 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -559,9 +559,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin break; } - // Update any physical parameters based on this type. - Refresh(); - m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, m_linearMotorDecayTimescale, m_linearFrictionTimescale, 1f); @@ -589,6 +586,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin { RegisterForSceneEvents(); } + + // Update any physical parameters based on this type. + Refresh(); } #endregion // Vehicle parameter setting @@ -596,6 +596,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin public override void Refresh() { // If asking for a refresh, reset the physical parameters before the next simulation step. + // Called whether active or not since the active state may be updated before the next step. m_physicsScene.PostTaintObject("BSDynamics.Refresh", ControllingPrim.LocalID, delegate() { SetPhysicalParameters(); @@ -625,7 +626,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Vehicles report collision events so we know when it's on the ground m_physicsScene.PE.AddToCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); - ControllingPrim.Inertia = m_physicsScene.PE.CalculateLocalInertia(ControllingPrim.PhysShape, m_vehicleMass); + ControllingPrim.Inertia = m_physicsScene.PE.CalculateLocalInertia(ControllingPrim.PhysShape.physShapeInfo, m_vehicleMass); m_physicsScene.PE.SetMassProps(ControllingPrim.PhysBody, m_vehicleMass, ControllingPrim.Inertia); m_physicsScene.PE.UpdateInertiaTensor(ControllingPrim.PhysBody); @@ -649,7 +650,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } // BSActor.RemoveBodyDependencies - public override void RemoveBodyDependencies() + public override void RemoveDependencies() { Refresh(); } @@ -789,7 +790,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin if ((m_knownHas & m_knownChangedTerrainHeight) == 0 || pos != lastRememberedHeightPos) { lastRememberedHeightPos = pos; - m_knownTerrainHeight = ControllingPrim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); + m_knownTerrainHeight = ControllingPrim.PhysScene.TerrainManager.GetTerrainHeightAtXYZ(pos); m_knownHas |= m_knownChangedTerrainHeight; } return m_knownTerrainHeight; @@ -801,7 +802,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { if ((m_knownHas & m_knownChangedWaterLevel) == 0) { - m_knownWaterLevel = ControllingPrim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos); + m_knownWaterLevel = ControllingPrim.PhysScene.TerrainManager.GetWaterLevelAtXYZ(pos); m_knownHas |= m_knownChangedWaterLevel; } return (float)m_knownWaterLevel; @@ -1019,7 +1020,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 origVelW = VehicleVelocity; // DEBUG DEBUG VehicleVelocity /= VehicleVelocity.Length(); VehicleVelocity *= BSParam.VehicleMaxLinearVelocity; - VDetailLog("{0}, MoveLinear,clampMax,origVelW={1},lenSq={2},maxVelSq={3},,newVelW={4}", + VDetailLog("{0}, MoveLinear,clampMax,origVelW={1},lenSq={2},maxVelSq={3},,newVelW={4}", ControllingPrim.LocalID, origVelW, newVelocityLengthSq, BSParam.VehicleMaxLinearVelocitySquared, VehicleVelocity); } else if (newVelocityLengthSq < 0.001f) @@ -1094,7 +1095,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin if (VehiclePosition.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = VehiclePosition.Z; } - + if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) { if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f) @@ -1188,7 +1189,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // used with conjunction with banking: the strength of the banking will decay when the // vehicle no longer experiences collisions. The decay timescale is the same as // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering - // when they are in mid jump. + // when they are in mid jump. // TODO: this code is wrong. Also, what should it do for boats (height from water)? // This is just using the ground and a general collision check. Should really be using // a downward raycast to find what is below. @@ -1254,7 +1255,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin VehicleAddForce(appliedGravity); - VDetailLog("{0}, MoveLinear,applyGravity,vehGrav={1},collid={2},fudge={3},mass={4},appliedForce={3}", + VDetailLog("{0}, MoveLinear,applyGravity,vehGrav={1},collid={2},fudge={3},mass={4},appliedForce={3}", ControllingPrim.LocalID, m_VehicleGravity, ControllingPrim.IsColliding, BSParam.VehicleGroundGravityFudge, m_vehicleMass, appliedGravity); } @@ -1330,7 +1331,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : // This flag prevents linear deflection parallel to world z-axis. This is useful // for preventing ground vehicles with large linear deflection, like bumper cars, - // from climbing their linear deflection into the sky. + // from climbing their linear deflection into the sky. // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement // TODO: This is here because this is where ODE put it but documentation says it // is a linear effect. Where should this check go? @@ -1463,7 +1464,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin VehicleRotationalVelocity += (vertContributionV * VehicleOrientation); VDetailLog("{0}, MoveAngular,verticalAttraction,,origRotVW={1},vertError={2},unscaledV={3},eff={4},ts={5},vertContribV={6}", - Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, + Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, vertContributionV); */ } @@ -1530,13 +1531,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude // of the yaw effect will be proportional to the // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's - // velocity along its preferred axis of motion. + // velocity along its preferred axis of motion. // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any // positive rotation (by the right-hand rule) about the roll-axis will effect a // (negative) torque around the yaw-axis, making it turn to the right--that is the // vehicle will lean into the turn, which is how real airplanes and motorcycle's work. // Negating the banking coefficient will make it so that the vehicle leans to the - // outside of the turn (not very "physical" but might allow interesting vehicles so why not?). + // outside of the turn (not very "physical" but might allow interesting vehicles so why not?). // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making // banking vehicles do what you want rather than what the laws of physics allow. // For example, consider a real motorcycle...it must be moving forward in order for @@ -1548,11 +1549,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the // banking effect depends only on the vehicle's rotation about its roll-axis compared // to "dynamic" where the banking is also proportional to its velocity along its - // roll-axis. Finding the best value of the "mixture" will probably require trial and error. + // roll-axis. Finding the best value of the "mixture" will probably require trial and error. // The time it takes for the banking behavior to defeat a preexisting angular velocity about the // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to // bank quickly then give it a banking timescale of about a second or less, otherwise you can - // make a sluggish vehicle by giving it a timescale of several seconds. + // make a sluggish vehicle by giving it a timescale of several seconds. public void ComputeAngularBanking() { if (enableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff) @@ -1581,7 +1582,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin //VehicleRotationalVelocity += bankingContributionV * VehicleOrientation; VehicleRotationalVelocity += bankingContributionV; - + VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", ControllingPrim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV); @@ -1637,8 +1638,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Invoke the detailed logger and output something if it's enabled. private void VDetailLog(string msg, params Object[] args) { - if (ControllingPrim.PhysicsScene.VehicleLoggingEnabled) - ControllingPrim.PhysicsScene.DetailLog(msg, args); + if (ControllingPrim.PhysScene.VehicleLoggingEnabled) + ControllingPrim.PhysScene.DetailLog(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 4ece1eb496..76c2187eed 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -80,7 +80,7 @@ public abstract class BSLinkset public BSPrimLinkable LinksetRoot { get; protected set; } - public BSScene PhysicsScene { get; private set; } + protected BSScene m_physicsScene { get; private set; } static int m_nextLinksetID = 1; public int LinksetID { get; private set; } @@ -93,13 +93,6 @@ public abstract class BSLinkset // to the physical representation is done via the tainting mechenism. protected object m_linksetActivityLock = new Object(); - // Some linksets have a preferred physical shape. - // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected. - public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPrimLinkable requestor) - { - return BSPhysicsShapeType.SHAPE_UNKNOWN; - } - // We keep the prim's mass in the linkset structure since it could be dependent on other prims public float LinksetMass { get; protected set; } @@ -122,7 +115,7 @@ public abstract class BSLinkset // We create LOTS of linksets. if (m_nextLinksetID <= 0) m_nextLinksetID = 1; - PhysicsScene = scene; + m_physicsScene = scene; LinksetRoot = parent; m_children = new HashSet(); LinksetMass = parent.RawMass; @@ -165,7 +158,7 @@ public abstract class BSLinkset } // The child is down to a linkset of just itself - return BSLinkset.Factory(PhysicsScene, child); + return BSLinkset.Factory(m_physicsScene, child); } // Return 'true' if the passed object is the root object of this linkset @@ -221,7 +214,7 @@ public abstract class BSLinkset // I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. protected abstract void AddChildToLinkset(BSPrimLinkable child); - + // I am the root of a linkset and one of my children is being removed. // Safe to call even if the child is not really in my linkset. protected abstract void RemoveChildFromLinkset(BSPrimLinkable child); @@ -263,7 +256,7 @@ public abstract class BSLinkset // This is called when the root body is changing. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public abstract bool RemoveBodyDependencies(BSPrimLinkable child); + public abstract bool RemoveDependencies(BSPrimLinkable child); // ================================================================ protected virtual float ComputeLinksetMass() @@ -323,8 +316,8 @@ public abstract class BSLinkset // Invoke the detailed logger and output something if it's enabled. protected void DetailLog(string msg, params Object[] args) { - if (PhysicsScene.PhysicsLogging.Enabled) - PhysicsScene.DetailLog(msg, args); + if (m_physicsScene.PhysicsLogging.Enabled) + m_physicsScene.DetailLog(msg, args); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index e05562a2c9..1f16cc8db9 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -35,6 +35,7 @@ using OMV = OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { + /* // When a child is linked, the relationship position of the child to the parent // is remembered so the child's world position can be recomputed when it is // removed from the linkset. @@ -88,6 +89,7 @@ sealed class BSLinksetCompoundInfo : BSLinksetInfo return buff.ToString(); } }; + */ public sealed class BSLinksetCompound : BSLinkset { @@ -98,19 +100,6 @@ public sealed class BSLinksetCompound : BSLinkset { } - // For compound implimented linksets, if there are children, use compound shape for the root. - public override BSPhysicsShapeType PreferredPhysicalShape(BSPrimLinkable requestor) - { - // Returning 'unknown' means we don't have a preference. - BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; - if (IsRoot(requestor) && HasAnyChildren) - { - ret = BSPhysicsShapeType.SHAPE_COMPOUND; - } - // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); - return ret; - } - // When physical properties are changed the linkset needs to recalculate // its internal properties. public override void Refresh(BSPrimLinkable requestor) @@ -124,14 +113,14 @@ public sealed class BSLinksetCompound : BSLinkset // Schedule a refresh to happen after all the other taint processing. private void ScheduleRebuild(BSPrimLinkable requestor) { - DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", + DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); // When rebuilding, it is possible to set properties that would normally require a rebuild. // If already rebuilding, don't request another rebuild. // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. if (!Rebuilding && HasAnyChildren) { - PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() + m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() { if (HasAnyChildren) RecomputeLinksetCompound(); @@ -153,26 +142,11 @@ public sealed class BSLinksetCompound : BSLinkset // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. ScheduleRebuild(LinksetRoot); } - else - { - // The origional prims are removed from the world as the shape of the root compound - // shape takes over. - PhysicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); - PhysicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); - // We don't want collisions from the old linkset children. - PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); - - child.PhysBody.collisionType = CollisionType.LinksetChild; - - ret = true; - } return ret; } - // The object is going static (non-physical). Do any setup necessary for a static linkset. + // The object is going static (non-physical). We do not do anything for static linksets. // Return 'true' if any properties updated on the passed object. - // This doesn't normally happen -- OpenSim removes the objects from the physical - // world if it is a static linkset. // Called at taint-time! public override bool MakeStatic(BSPrimLinkable child) { @@ -180,19 +154,9 @@ public sealed class BSLinksetCompound : BSLinkset DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); if (IsRoot(child)) { + // Schedule a rebuild to verify that the root shape is set to the real shape. ScheduleRebuild(LinksetRoot); } - else - { - // The non-physical children can come back to life. - PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); - - child.PhysBody.collisionType = CollisionType.LinksetChild; - - // Don't force activation so setting of DISABLE_SIMULATION can stay if used. - PhysicsScene.PE.Activate(child.PhysBody, false); - ret = true; - } return ret; } @@ -200,13 +164,20 @@ public sealed class BSLinksetCompound : BSLinkset // Called at taint-time. public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated) { + if (!LinksetRoot.IsPhysicallyActive) + { + // No reason to do this physical stuff for static linksets. + DetailLog("{0},BSLinksetCompound.UpdateProperties,notPhysical", LinksetRoot.LocalID); + return; + } + // The user moving a child around requires the rebuilding of the linkset compound shape // One problem is this happens when a border is crossed -- the simulator implementation // stores the position into the group which causes the move of the object // but it also means all the child positions get updated. // What would cause an unnecessary rebuild so we make sure the linkset is in a // region before bothering to do a rebuild. - if (!IsRoot(updated) && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) + if (!IsRoot(updated) && m_physicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) { // If a child of the linkset is updating only the position or rotation, that can be done // without rebuilding the linkset. @@ -218,22 +189,22 @@ public sealed class BSLinksetCompound : BSLinkset // and that is caused by us updating the object. if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) { - // Find the physical instance of the child - if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape)) + // Find the physical instance of the child + if (LinksetRoot.PhysShape.HasPhysicalShape && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo)) { // It is possible that the linkset is still under construction and the child is not yet // inserted into the compound shape. A rebuild of the linkset in a pre-step action will // build the whole thing with the new position or rotation. // The index must be checked because Bullet references the child array but does no validity // checking of the child index passed. - int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape); + int numLinksetChildren = m_physicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo); if (updated.LinksetChildIndex < numLinksetChildren) { - BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, updated.LinksetChildIndex); + BulletShape linksetChildShape = m_physicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex); if (linksetChildShape.HasPhysicalShape) { // Found the child shape within the compound shape - PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, updated.LinksetChildIndex, + m_physicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex, updated.RawPosition - LinksetRoot.RawPosition, updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation), true /* shouldRecalculateLocalAabb */); @@ -275,75 +246,22 @@ public sealed class BSLinksetCompound : BSLinkset } // Routine called when rebuilding the body of some member of the linkset. - // Since we don't keep in world relationships, do nothing unless it's a child changing. + // If one of the bodies is being changed, the linkset needs rebuilding. + // For instance, a linkset is built and then a mesh asset is read in and the mesh is recreated. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public override bool RemoveBodyDependencies(BSPrimLinkable child) + public override bool RemoveDependencies(BSPrimLinkable child) { bool ret = false; DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child)); - if (!IsRoot(child)) - { - // Because it is a convenient time, recompute child world position and rotation based on - // its position in the linkset. - RecomputeChildWorldPosition(child, true /* inTaintTime */); - child.LinksetInfo = null; - } - - // Cannot schedule a refresh/rebuild here because this routine is called when - // the linkset is being rebuilt. - // InternalRefresh(LinksetRoot); + ScheduleRebuild(child); return ret; } - // When the linkset is built, the child shape is added to the compound shape relative to the - // root shape. The linkset then moves around but this does not move the actual child - // prim. The child prim's location must be recomputed based on the location of the root shape. - private void RecomputeChildWorldPosition(BSPrimLinkable child, bool inTaintTime) - { - // For the moment (20130201), disable this computation (converting the child physical addr back to - // a region address) until we have a good handle on center-of-mass offsets and what the physics - // engine moving a child actually means. - // The simulator keeps track of where children should be as the linkset moves. Setting - // the pos/rot here does not effect that knowledge as there is no good way for the - // physics engine to send the simulator an update for a child. - - /* - BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo; - if (lci != null) - { - if (inTaintTime) - { - OMV.Vector3 oldPos = child.RawPosition; - child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetFromRoot; - child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot; - DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}", - child.LocalID, oldPos, lci, child.RawPosition); - } - else - { - // TaintedObject is not used here so the raw position is set now and not at taint-time. - child.Position = LinksetRoot.RawPosition + lci.OffsetFromRoot; - child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot; - } - } - else - { - // This happens when children have been added to the linkset but the linkset - // has not been constructed yet. So like, at taint time, adding children to a linkset - // and then changing properties of the children (makePhysical, for instance) - // but the post-print action of actually rebuilding the linkset has not yet happened. - // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}", - // LogHeader, child.LocalID); - DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID); - } - */ - } - // ================================================================ // Add a new child to the linkset. @@ -376,7 +294,6 @@ public sealed class BSLinksetCompound : BSLinkset child.LocalID, child.PhysBody.AddrString); // Cause the child's body to be rebuilt and thus restored to normal operation - RecomputeChildWorldPosition(child, false); child.LinksetInfo = null; child.ForceBodyShapeRebuild(false); @@ -399,108 +316,105 @@ public sealed class BSLinksetCompound : BSLinkset // Constraint linksets are rebuilt every time. // Note that this works for rebuilding just the root after a linkset is taken apart. // Called at taint time!! - private bool disableCOM = true; // DEBUG DEBUG: disable until we get this debugged + private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape + private bool disableCOM = true; // For basic linkset debugging, turn off the center-of-mass setting private void RecomputeLinksetCompound() { try { - // Suppress rebuilding while rebuilding. (We know rebuilding is on only one thread.) Rebuilding = true; - // Cause the root shape to be rebuilt as a compound object with just the root in it - LinksetRoot.ForceBodyShapeRebuild(true /* inTaintTime */); + // No matter what is being done, force the root prim's PhysBody and PhysShape to get set + // to what they should be as if the root was not in a linkset. + // Not that bad since we only get into this routine if there are children in the linkset and + // something has been updated/changed. + LinksetRoot.ForceBodyShapeRebuild(true); + + // There is no reason to build all this physical stuff for a non-physical linkset. + if (!LinksetRoot.IsPhysicallyActive) + { + // Clean up any old linkset shape and make sure the root shape is set to the root object. + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysical", LinksetRoot.LocalID); + + return; // Note the 'finally' clause at the botton which will get executed. + } + + // Get a new compound shape to build the linkset shape in. + BSShape linksetShape = BSShapeCompound.GetReference(m_physicsScene); // The center of mass for the linkset is the geometric center of the group. // Compute a displacement for each component so it is relative to the center-of-mass. // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass - OMV.Vector3 centerOfMassW = LinksetRoot.RawPosition; - if (!disableCOM) // DEBUG DEBUG - { - // Compute a center-of-mass in world coordinates. - centerOfMassW = ComputeLinksetCenterOfMass(); - } + OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass(); OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); // 'centerDisplacement' is the value to subtract from children to give physical offset position OMV.Vector3 centerDisplacement = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation; - LinksetRoot.SetEffectiveCenterOfMassW(centerDisplacement); + if (UseBulletSimRootOffsetHack || disableCOM) + { + centerDisplacement = OMV.Vector3.Zero; + LinksetRoot.ClearDisplacement(); + } + else + { + LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacement); + } + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}", + LinksetRoot.LocalID, LinksetRoot.RawPosition, centerOfMassW, centerDisplacement); - // This causes the physical position of the root prim to be offset to accomodate for the displacements - LinksetRoot.ForcePosition = LinksetRoot.RawPosition; - - // Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM - PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0 /* childIndex */, - -centerDisplacement, - OMV.Quaternion.Identity, // LinksetRoot.RawOrientation, - false /* shouldRecalculateLocalAabb (is done later after linkset built) */); - - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}", - LinksetRoot.LocalID, centerOfMassW, LinksetRoot.RawPosition, centerDisplacement); - - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", - LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); - - // Add a shape for each of the other children in the linkset + // Add the shapes of all the components of the linkset int memberIndex = 1; ForEachMember(delegate(BSPrimLinkable cPrim) { - if (IsRoot(cPrim)) + // Root shape is always index zero. + cPrim.LinksetChildIndex = IsRoot(cPrim) ? 0 : memberIndex; + + // Get a reference to the shape of the child and add that shape to the linkset compound shape + BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim); + OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; + OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; + m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}", + LinksetRoot.LocalID, memberIndex, childShape, offsetPos, offsetRot); + + // Since we are borrowing the shape of the child, disable the origional child body + if (!IsRoot(cPrim)) { - cPrim.LinksetChildIndex = 0; + m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION); + // We don't want collisions from the old linkset children. + m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + cPrim.PhysBody.collisionType = CollisionType.LinksetChild; } - else - { - cPrim.LinksetChildIndex = memberIndex; - if (cPrim.PhysShape.isNativeShape) - { - // A native shape is turned into a hull collision shape because native - // shapes are not shared so we have to hullify it so it will be tracked - // and freed at the correct time. This also solves the scaling problem - // (native shapes scale but hull/meshes are assumed to not be). - // TODO: decide of the native shape can just be used in the compound shape. - // Use call to CreateGeomNonSpecial(). - BulletShape saveShape = cPrim.PhysShape; - cPrim.PhysShape.Clear(); // Don't let the create free the child's shape - PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); - BulletShape newShape = cPrim.PhysShape; - cPrim.PhysShape = saveShape; + memberIndex++; - OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; - OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; - PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", - LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); - } - else - { - // For the shared shapes (meshes and hulls), just use the shape in the child. - // The reference count added here will be decremented when the compound shape - // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced). - if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) - { - PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", - LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); - } - OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; - OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; - PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNonNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", - LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); - - } - memberIndex++; - } return false; // 'false' says to move onto the next child in the list }); + // Replace the root shape with the built compound shape. + // Object removed and added to world to get collision cache rebuilt for new shape. + LinksetRoot.PhysShape.Dereference(m_physicsScene); + LinksetRoot.PhysShape = linksetShape; + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody); + m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}", + LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape); + // With all of the linkset packed into the root prim, it has the mass of everyone. LinksetMass = ComputeLinksetMass(); LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); - // Enable the physical position updator to return the position and rotation of the root shape - PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); + if (UseBulletSimRootOffsetHack) + { + // Enable the physical position updator to return the position and rotation of the root shape. + // This enables a feature in the C++ code to return the world coordinates of the first shape in the + // compound shape. This eleviates the need to offset the returned physical position by the + // center-of-mass offset. + m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); + } } finally { @@ -508,7 +422,7 @@ public sealed class BSLinksetCompound : BSLinkset } // See that the Aabb surrounds the new shape - PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape); + m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo); } } } \ No newline at end of file diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs index 6d252caab2..a06a44d33f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -51,7 +51,7 @@ public sealed class BSLinksetConstraints : BSLinkset if (HasAnyChildren && IsRoot(requestor)) { // Queue to happen after all the other taint processing - PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() + m_physicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() { if (HasAnyChildren && IsRoot(requestor)) RecomputeLinksetConstraints(); @@ -93,11 +93,11 @@ public sealed class BSLinksetConstraints : BSLinkset // up to rebuild the constraints before the next simulation step. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public override bool RemoveBodyDependencies(BSPrimLinkable child) + public override bool RemoveDependencies(BSPrimLinkable child) { bool ret = false; - DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", + DetailLog("{0},BSLinksetConstraint.RemoveDependencies,removeChildrenForRoot,rID={1},rBody={2}", child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString); lock (m_linksetActivityLock) @@ -142,7 +142,7 @@ public sealed class BSLinksetConstraints : BSLinkset rootx.LocalID, rootx.PhysBody.AddrString, childx.LocalID, childx.PhysBody.AddrString); - PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate() + m_physicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate() { PhysicallyUnlinkAChildFromRoot(rootx, childx); }); @@ -187,7 +187,7 @@ public sealed class BSLinksetConstraints : BSLinkset // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 BSConstraint6Dof constrain = new BSConstraint6Dof( - PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true ); + m_physicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true ); // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true ); /* NOTE: below is an attempt to build constraint with full frame computation, etc. @@ -216,7 +216,7 @@ public sealed class BSLinksetConstraints : BSLinkset // ================================================================================== */ - PhysicsScene.Constraints.AddConstraint(constrain); + m_physicsScene.Constraints.AddConstraint(constrain); // zero linear and angular limits makes the objects unable to move in relation to each other constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); @@ -248,10 +248,10 @@ public sealed class BSLinksetConstraints : BSLinkset childPrim.LocalID, childPrim.PhysBody.AddrString); // Find the constraint for this link and get rid of it from the overall collection and from my list - if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) + if (m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) { // Make the child refresh its location - PhysicsScene.PE.PushUpdate(childPrim.PhysBody); + m_physicsScene.PE.PushUpdate(childPrim.PhysBody); ret = true; } @@ -265,7 +265,7 @@ public sealed class BSLinksetConstraints : BSLinkset { DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); - return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); + return m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); } // Call each of the constraints that make up this linkset and recompute the @@ -289,7 +289,7 @@ public sealed class BSLinksetConstraints : BSLinkset child.UpdatePhysicalMassProperties(linksetMass, true); BSConstraint constrain; - if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain)) + if (!m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain)) { // If constraint doesn't exist yet, create it. constrain = BuildConstraint(LinksetRoot, child); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs index 9501e2d4fe..0128d8dbdd 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs @@ -102,7 +102,7 @@ public class BSVMotor : BSMotor return ErrorIsZero(LastError); } public virtual bool ErrorIsZero(Vector3 err) - { + { return (err == Vector3.Zero || err.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)); } @@ -115,7 +115,7 @@ public class BSVMotor : BSMotor CurrentValue = TargetValue = Vector3.Zero; ErrorZeroThreshold = 0.001f; } - public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) + public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) : this(useName) { TimeScale = timeScale; @@ -237,7 +237,7 @@ public class BSVMotor : BSMotor MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName); MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}", BSScene.DetailLogZero, UseName, - TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency, + TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency, CurrentValue, TargetValue); LastError = BSMotor.InfiniteVector; @@ -248,7 +248,7 @@ public class BSVMotor : BSMotor BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep); } MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName); - + } @@ -279,7 +279,7 @@ public class BSFMotor : BSMotor return ErrorIsZero(LastError); } public virtual bool ErrorIsZero(float err) - { + { return (err >= -ErrorZeroThreshold && err <= ErrorZeroThreshold); } @@ -410,7 +410,7 @@ public class BSPIDVMotor : BSVMotor // The factors are vectors for the three dimensions. This is the proportional of each // that is applied. This could be multiplied through the actual factors but it // is sometimes easier to manipulate the factors and their mix separately. - // to + // to public Vector3 FactorMix; // Arbritrary factor range. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 980d40587b..2ac68e3e43 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -37,7 +37,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public static class BSParam { - private static string LogHeader = "[BULLETSIM PARAMETERS]"; + private static string LogHeader = "[BULLETSIM PARAMETERS]"; // Tuning notes: // From: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6575 @@ -51,7 +51,7 @@ public static class BSParam // This is separate/independent from the collision margin. The collision margin increases the object a bit // to improve collision detection performance and accuracy. // =================== - // From: + // From: // Level of Detail values kept as float because that's what the Meshmerizer wants public static float MeshLOD { get; private set; } @@ -87,6 +87,7 @@ public static class BSParam public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects public static bool ShouldRemoveZeroWidthTriangles { get; private set; } public static bool ShouldUseBulletHACD { get; set; } + public static bool ShouldUseSingleConvexHullForPrims { get; set; } public static float TerrainImplementation { get; private set; } public static int TerrainMeshMagnification { get; private set; } @@ -342,6 +343,10 @@ public static class BSParam false, (s) => { return ShouldUseBulletHACD; }, (s,v) => { ShouldUseBulletHACD = v; } ), + new ParameterDefn("ShouldUseSingleConvexHullForPrims", "If true, use a single convex hull shape for physical prims", + true, + (s) => { return ShouldUseSingleConvexHullForPrims; }, + (s,v) => { ShouldUseSingleConvexHullForPrims = v; } ), new ParameterDefn("CrossingFailuresBeforeOutOfBounds", "How forgiving we are about getting into adjactent regions", 5, @@ -636,7 +641,7 @@ public static class BSParam new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", false, (s) => { return ShouldDisableContactPoolDynamicAllocation; }, - (s,v) => { ShouldDisableContactPoolDynamicAllocation = v; + (s,v) => { ShouldDisableContactPoolDynamicAllocation = v; s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = NumericBool(v); } ), new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", false, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 309d004274..e796804df4 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -38,7 +38,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin * Class to wrap all objects. * The rest of BulletSim doesn't need to keep checking for avatars or prims * unless the difference is significant. - * + * * Variables in the physicsl objects are in three forms: * VariableName: used by the simulator and performs taint operations, etc * RawVariableName: direct reference to the BulletSim storage for the variable value @@ -52,7 +52,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v - * BS.ApplyCentralForce BS.ApplyTorque + * BS.ApplyCentralForce BS.ApplyTorque */ // Flags used to denote which properties updates when making UpdateProperties calls to linksets, etc. @@ -72,14 +72,14 @@ public abstract class BSPhysObject : PhysicsActor } protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName) { - PhysicsScene = parentScene; + PhysScene = parentScene; LocalID = localID; PhysObjectName = name; Name = name; // PhysicsActor also has the name of the object. Someday consolidate. TypeName = typeName; // The collection of things that push me around - PhysicalActors = new BSActorCollection(PhysicsScene); + PhysicalActors = new BSActorCollection(PhysScene); // Initialize variables kept in base. GravModifier = 1.0f; @@ -88,7 +88,7 @@ public abstract class BSPhysObject : PhysicsActor // We don't have any physical representation yet. PhysBody = new BulletBody(localID); - PhysShape = new BulletShape(); + PhysShape = new BSShapeNull(); PrimAssetState = PrimAssetCondition.Unknown; @@ -97,6 +97,9 @@ public abstract class BSPhysObject : PhysicsActor CollisionCollection = new CollisionEventUpdate(); CollisionsLastReported = CollisionCollection; + CollisionsLastTick = new CollisionEventUpdate(); + CollisionsLastTickStep = -1; + SubscribedEventsMs = 0; CollidingStep = 0; CollidingGroundStep = 0; @@ -112,13 +115,13 @@ public abstract class BSPhysObject : PhysicsActor public virtual void Destroy() { PhysicalActors.Enable(false); - PhysicsScene.TaintedObject("BSPhysObject.Destroy", delegate() + PhysScene.TaintedObject("BSPhysObject.Destroy", delegate() { PhysicalActors.Dispose(); }); } - public BSScene PhysicsScene { get; protected set; } + public BSScene PhysScene { get; protected set; } // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor public string PhysObjectName { get; protected set; } public string TypeName { get; protected set; } @@ -138,7 +141,7 @@ public abstract class BSPhysObject : PhysicsActor // Reference to the physical body (btCollisionObject) of this object public BulletBody PhysBody; // Reference to the physical shape (btCollisionShape) of this object - public BulletShape PhysShape; + public BSShape PhysShape; // The physical representation of the prim might require an asset fetch. // The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'. @@ -151,13 +154,6 @@ public abstract class BSPhysObject : PhysicsActor // The objects base shape information. Null if not a prim type shape. public PrimitiveBaseShape BaseShape { get; protected set; } - // Some types of objects have preferred physical representations. - // Returns SHAPE_UNKNOWN if there is no preference. - public virtual BSPhysicsShapeType PreferredPhysicalShape - { - get { return BSPhysicsShapeType.SHAPE_UNKNOWN; } - } - // When the physical properties are updated, an EntityProperty holds the update values. // Keep the current and last EntityProperties to enable computation of differences // between the current update and the previous values. @@ -266,7 +262,8 @@ public abstract class BSPhysObject : PhysicsActor // The user can optionally set the center of mass. The user's setting will override any // computed center-of-mass (like in linksets). - public OMV.Vector3? UserSetCenterOfMass { get; set; } + // Note this is a displacement from the root's coordinates. Zero means use the root prim as center-of-mass. + public OMV.Vector3? UserSetCenterOfMassDisplacement { get; set; } public OMV.Vector3 LockedAxis { get; set; } // zero means locked. one means free. public readonly OMV.Vector3 LockedAxisFree = new OMV.Vector3(1f, 1f, 1f); // All axis are free @@ -277,7 +274,7 @@ public abstract class BSPhysObject : PhysicsActor public void ActivateIfPhysical(bool forceIt) { if (IsPhysical && PhysBody.HasPhysicalBody) - PhysicsScene.PE.Activate(PhysBody, forceIt); + PhysScene.PE.Activate(PhysBody, forceIt); } // 'actors' act on the physical object to change or constrain its motion. These can range from @@ -340,29 +337,29 @@ public abstract class BSPhysObject : PhysicsActor protected long CollisionAccumulation { get; set; } public override bool IsColliding { - get { return (CollidingStep == PhysicsScene.SimulationStep); } + get { return (CollidingStep == PhysScene.SimulationStep); } set { if (value) - CollidingStep = PhysicsScene.SimulationStep; + CollidingStep = PhysScene.SimulationStep; else CollidingStep = 0; } } public override bool CollidingGround { - get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } + get { return (CollidingGroundStep == PhysScene.SimulationStep); } set { if (value) - CollidingGroundStep = PhysicsScene.SimulationStep; + CollidingGroundStep = PhysScene.SimulationStep; else CollidingGroundStep = 0; } } public override bool CollidingObj { - get { return (CollidingObjectStep == PhysicsScene.SimulationStep); } - set { + get { return (CollidingObjectStep == PhysScene.SimulationStep); } + set { if (value) - CollidingObjectStep = PhysicsScene.SimulationStep; + CollidingObjectStep = PhysScene.SimulationStep; else CollidingObjectStep = 0; } @@ -387,14 +384,14 @@ public abstract class BSPhysObject : PhysicsActor bool ret = false; // The following lines make IsColliding(), CollidingGround() and CollidingObj work - CollidingStep = PhysicsScene.SimulationStep; - if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID) + CollidingStep = PhysScene.SimulationStep; + if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID) { - CollidingGroundStep = PhysicsScene.SimulationStep; + CollidingGroundStep = PhysScene.SimulationStep; } else { - CollidingObjectStep = PhysicsScene.SimulationStep; + CollidingObjectStep = PhysScene.SimulationStep; } CollisionAccumulation++; @@ -404,10 +401,10 @@ public abstract class BSPhysObject : PhysicsActor // Make a collection of the collisions that happened the last simulation tick. // This is different than the collection created for sending up to the simulator as it is cleared every tick. - if (CollisionsLastTickStep != PhysicsScene.SimulationStep) + if (CollisionsLastTickStep != PhysScene.SimulationStep) { CollisionsLastTick = new CollisionEventUpdate(); - CollisionsLastTickStep = PhysicsScene.SimulationStep; + CollisionsLastTickStep = PhysScene.SimulationStep; } CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); @@ -434,9 +431,9 @@ public abstract class BSPhysObject : PhysicsActor bool force = (CollisionCollection.Count == 0 && CollisionsLastReported.Count != 0); // throttle the collisions to the number of milliseconds specified in the subscription - if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime)) + if (force || (PhysScene.SimulationNowTime >= NextCollisionOkTime)) { - NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs; + NextCollisionOkTime = PhysScene.SimulationNowTime + SubscribedEventsMs; // We are called if we previously had collisions. If there are no collisions // this time, send up one last empty event so OpenSim can sense collision end. @@ -454,7 +451,7 @@ public abstract class BSPhysObject : PhysicsActor // The CollisionCollection instance is passed around in the simulator. // Make sure we don't have a handle to that one and that a new one is used for next time. - // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here, + // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here, // a race condition is created for the other users of this instance. CollisionCollection = new CollisionEventUpdate(); } @@ -471,10 +468,10 @@ public abstract class BSPhysObject : PhysicsActor // make sure first collision happens NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs); - PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() + PhysScene.TaintedObject(TypeName+".SubscribeEvents", delegate() { if (PhysBody.HasPhysicalBody) - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); } else @@ -486,11 +483,11 @@ public abstract class BSPhysObject : PhysicsActor public override void UnSubscribeEvents() { // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); SubscribedEventsMs = 0; - PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() + PhysScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() { // Make sure there is a body there because sometimes destruction happens in an un-ideal order. if (PhysBody.HasPhysicalBody) - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); } // Return 'true' if the simulator wants collision events @@ -504,7 +501,7 @@ public abstract class BSPhysObject : PhysicsActor { // Scale the collision count by the time since the last collision. // The "+1" prevents dividing by zero. - long timeAgo = PhysicsScene.SimulationStep - CollidingStep + 1; + long timeAgo = PhysScene.SimulationStep - CollidingStep + 1; CollisionScore = CollisionAccumulation / timeAgo; } public override float CollisionScore { get; set; } @@ -531,8 +528,8 @@ public abstract class BSPhysObject : PhysicsActor // High performance detailed logging routine used by the physical objects. protected void DetailLog(string msg, params Object[] args) { - if (PhysicsScene.PhysicsLogging.Enabled) - PhysicsScene.DetailLog(msg, args); + if (PhysScene.PhysicsLogging.Enabled) + PhysScene.DetailLog(msg, args); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 4bc266bea7..d3f34759d1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -101,21 +101,21 @@ public class BSPrim : BSPhysObject _isVolumeDetect = false; // We keep a handle to the vehicle actor so we can set vehicle parameters later. - VehicleActor = new BSDynamics(PhysicsScene, this, VehicleActorName); + VehicleActor = new BSDynamics(PhysScene, this, VehicleActorName); PhysicalActors.Add(VehicleActorName, VehicleActor); _mass = CalculateMass(); // DetailLog("{0},BSPrim.constructor,call", LocalID); // do the actual object creation at taint time - PhysicsScene.TaintedObject("BSPrim.create", delegate() + PhysScene.TaintedObject("BSPrim.create", delegate() { // Make sure the object is being created with some sanity. ExtremeSanityCheck(true /* inTaintTime */); CreateGeomAndObject(true); - CurrentCollisionFlags = PhysicsScene.PE.GetCollisionFlags(PhysBody); + CurrentCollisionFlags = PhysScene.PE.GetCollisionFlags(PhysBody); }); } @@ -128,14 +128,14 @@ public class BSPrim : BSPhysObject // Undo any vehicle properties this.VehicleType = (int)Vehicle.TYPE_NONE; - PhysicsScene.TaintedObject("BSPrim.Destroy", delegate() + PhysScene.TaintedObject("BSPrim.Destroy", delegate() { DetailLog("{0},BSPrim.Destroy,taint,", LocalID); // If there are physical body and shape, release my use of same. - PhysicsScene.Shapes.DereferenceBody(PhysBody, null); + PhysScene.Shapes.DereferenceBody(PhysBody, null); PhysBody.Clear(); - PhysicsScene.Shapes.DereferenceShape(PhysShape, null); - PhysShape.Clear(); + PhysShape.Dereference(PhysScene); + PhysShape = new BSShapeNull(); }); } @@ -161,25 +161,13 @@ public class BSPrim : BSPhysObject ForceBodyShapeRebuild(false); } } - // 'unknown' says to choose the best type - public override BSPhysicsShapeType PreferredPhysicalShape - { get { return BSPhysicsShapeType.SHAPE_UNKNOWN; } } - public override bool ForceBodyShapeRebuild(bool inTaintTime) { - if (inTaintTime) + PhysScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() { _mass = CalculateMass(); // changing the shape changes the mass CreateGeomAndObject(true); - } - else - { - PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", delegate() - { - _mass = CalculateMass(); // changing the shape changes the mass - CreateGeomAndObject(true); - }); - } + }); return true; } public override bool Grabbed { @@ -192,7 +180,7 @@ public class BSPrim : BSPhysObject if (value != _isSelected) { _isSelected = value; - PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() + PhysScene.TaintedObject("BSPrim.setSelected", delegate() { DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); SetObjectDynamic(false); @@ -238,23 +226,23 @@ public class BSPrim : BSPhysObject _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties in the physics engine - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.ClearAllForces(PhysBody); + PhysScene.PE.ClearAllForces(PhysBody); }); } public override void ZeroAngularMotion(bool inTaintTime) { _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties in the physics engine - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() { // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); - PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); + PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); + PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); } }); } @@ -272,11 +260,11 @@ public class BSPrim : BSPhysObject EnableActor(LockedAxis != LockedAxisFree, LockedAxisActorName, delegate() { - return new BSActorLockAxis(PhysicsScene, this, LockedAxisActorName); + return new BSActorLockAxis(PhysScene, this, LockedAxisActorName); }); // Update parameters so the new actor's Refresh() action is called at the right time. - PhysicsScene.TaintedObject("BSPrim.LockAngularMotion", delegate() + PhysScene.TaintedObject("BSPrim.LockAngularMotion", delegate() { UpdatePhysicalParameters(); }); @@ -306,7 +294,7 @@ public class BSPrim : BSPhysObject _position = value; PositionSanityCheck(false); - PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() + PhysScene.TaintedObject("BSPrim.setPosition", delegate() { DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); ForcePosition = _position; @@ -316,14 +304,14 @@ public class BSPrim : BSPhysObject public override OMV.Vector3 ForcePosition { get { - _position = PhysicsScene.PE.GetPosition(PhysBody); + _position = PhysScene.PE.GetPosition(PhysBody); return _position; } set { _position = value; if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); ActivateIfPhysical(false); } } @@ -340,7 +328,7 @@ public class BSPrim : BSPhysObject if (!IsPhysicallyActive) return ret; - if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) + if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) { // The physical object is out of the known/simulated area. // Upper levels of code will handle the transition to other areas so, for @@ -348,7 +336,7 @@ public class BSPrim : BSPhysObject return ret; } - float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); + float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); OMV.Vector3 upForce = OMV.Vector3.Zero; float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z)); if ((RawPosition.Z + approxSize / 2f) < terrainHeight) @@ -369,7 +357,7 @@ public class BSPrim : BSPhysObject if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) { - float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); + float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(_position); // TODO: a floating motor so object will bob in the water if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) { @@ -377,7 +365,7 @@ public class BSPrim : BSPhysObject upForce.Z = (waterHeight - RawPosition.Z) * 1f; // Apply upforce and overcome gravity. - OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity; + OMV.Vector3 correctionForce = upForce - PhysScene.DefaultGravity; DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce); AddForce(correctionForce, false, inTaintTime); ret = true; @@ -432,7 +420,7 @@ public class BSPrim : BSPhysObject get { return _mass; } } // used when we only want this prim's mass and not the linkset thing - public override float RawMass { + public override float RawMass { get { return _mass; } } // Set the physical mass to the passed mass. @@ -443,10 +431,10 @@ public class BSPrim : BSPhysObject { if (IsStatic) { - PhysicsScene.PE.SetGravity(PhysBody, PhysicsScene.DefaultGravity); + PhysScene.PE.SetGravity(PhysBody, PhysScene.DefaultGravity); Inertia = OMV.Vector3.Zero; - PhysicsScene.PE.SetMassProps(PhysBody, 0f, Inertia); - PhysicsScene.PE.UpdateInertiaTensor(PhysBody); + PhysScene.PE.SetMassProps(PhysBody, 0f, Inertia); + PhysScene.PE.UpdateInertiaTensor(PhysBody); } else { @@ -455,16 +443,16 @@ public class BSPrim : BSPhysObject // Changing interesting properties doesn't change proxy and collision cache // information. The Bullet solution is to re-add the object to the world // after parameters are changed. - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); } // The computation of mass props requires gravity to be set on the object. Gravity = ComputeGravity(Buoyancy); - PhysicsScene.PE.SetGravity(PhysBody, Gravity); + PhysScene.PE.SetGravity(PhysBody, Gravity); - Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); - PhysicsScene.PE.SetMassProps(PhysBody, physMass, Inertia); - PhysicsScene.PE.UpdateInertiaTensor(PhysBody); + Inertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); + PhysScene.PE.SetMassProps(PhysBody, physMass, Inertia); + PhysScene.PE.UpdateInertiaTensor(PhysBody); DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", LocalID, physMass, Inertia, Gravity, inWorld); @@ -480,7 +468,7 @@ public class BSPrim : BSPhysObject // Return what gravity should be set to this very moment public OMV.Vector3 ComputeGravity(float buoyancy) { - OMV.Vector3 ret = PhysicsScene.DefaultGravity; + OMV.Vector3 ret = PhysScene.DefaultGravity; if (!IsStatic) { @@ -509,7 +497,7 @@ public class BSPrim : BSPhysObject RawForce = value; EnableActor(RawForce != OMV.Vector3.Zero, SetForceActorName, delegate() { - return new BSActorSetForce(PhysicsScene, this, SetForceActorName); + return new BSActorSetForce(PhysScene, this, SetForceActorName); }); } } @@ -521,9 +509,9 @@ public class BSPrim : BSPhysObject set { Vehicle type = (Vehicle)value; - PhysicsScene.TaintedObject("setVehicleType", delegate() + PhysScene.TaintedObject("setVehicleType", delegate() { - // Vehicle code changes the parameters for this vehicle type. + ZeroMotion(true /* inTaintTime */); VehicleActor.ProcessTypeChange(type); ActivateIfPhysical(false); }); @@ -531,7 +519,7 @@ public class BSPrim : BSPhysObject } public override void VehicleFloatParam(int param, float value) { - PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() + PhysScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() { VehicleActor.ProcessFloatVehicleParam((Vehicle)param, value); ActivateIfPhysical(false); @@ -539,7 +527,7 @@ public class BSPrim : BSPhysObject } public override void VehicleVectorParam(int param, OMV.Vector3 value) { - PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() + PhysScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() { VehicleActor.ProcessVectorVehicleParam((Vehicle)param, value); ActivateIfPhysical(false); @@ -547,7 +535,7 @@ public class BSPrim : BSPhysObject } public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { - PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() + PhysScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() { VehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation); ActivateIfPhysical(false); @@ -555,7 +543,7 @@ public class BSPrim : BSPhysObject } public override void VehicleFlags(int param, bool remove) { - PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() + PhysScene.TaintedObject("BSPrim.VehicleFlags", delegate() { VehicleActor.ProcessVehicleFlags(param, remove); }); @@ -567,7 +555,7 @@ public class BSPrim : BSPhysObject if (_isVolumeDetect != newValue) { _isVolumeDetect = newValue; - PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() + PhysScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() { // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); SetObjectDynamic(true); @@ -578,7 +566,7 @@ public class BSPrim : BSPhysObject public override void SetMaterial(int material) { base.SetMaterial(material); - PhysicsScene.TaintedObject("BSPrim.SetMaterial", delegate() + PhysScene.TaintedObject("BSPrim.SetMaterial", delegate() { UpdatePhysicalParameters(); }); @@ -591,7 +579,7 @@ public class BSPrim : BSPhysObject if (base.Friction != value) { base.Friction = value; - PhysicsScene.TaintedObject("BSPrim.setFriction", delegate() + PhysScene.TaintedObject("BSPrim.setFriction", delegate() { UpdatePhysicalParameters(); }); @@ -606,7 +594,7 @@ public class BSPrim : BSPhysObject if (base.Restitution != value) { base.Restitution = value; - PhysicsScene.TaintedObject("BSPrim.setRestitution", delegate() + PhysScene.TaintedObject("BSPrim.setRestitution", delegate() { UpdatePhysicalParameters(); }); @@ -623,7 +611,7 @@ public class BSPrim : BSPhysObject if (base.Density != value) { base.Density = value; - PhysicsScene.TaintedObject("BSPrim.setDensity", delegate() + PhysScene.TaintedObject("BSPrim.setDensity", delegate() { UpdatePhysicalParameters(); }); @@ -638,7 +626,7 @@ public class BSPrim : BSPhysObject if (base.GravModifier != value) { base.GravModifier = value; - PhysicsScene.TaintedObject("BSPrim.setGravityModifier", delegate() + PhysScene.TaintedObject("BSPrim.setGravityModifier", delegate() { UpdatePhysicalParameters(); }); @@ -649,7 +637,7 @@ public class BSPrim : BSPhysObject get { return RawVelocity; } set { RawVelocity = value; - PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() + PhysScene.TaintedObject("BSPrim.setVelocity", delegate() { // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, RawVelocity); ForceVelocity = RawVelocity; @@ -659,13 +647,13 @@ public class BSPrim : BSPhysObject public override OMV.Vector3 ForceVelocity { get { return RawVelocity; } set { - PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity"); + PhysScene.AssertInTaintTime("BSPrim.ForceVelocity"); RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); if (PhysBody.HasPhysicalBody) { DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, RawVelocity); - PhysicsScene.PE.SetLinearVelocity(PhysBody, RawVelocity); + PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); ActivateIfPhysical(false); } } @@ -676,7 +664,7 @@ public class BSPrim : BSPhysObject RawTorque = value; EnableActor(RawTorque != OMV.Vector3.Zero, SetTorqueActorName, delegate() { - return new BSActorSetTorque(PhysicsScene, this, SetTorqueActorName); + return new BSActorSetTorque(PhysScene, this, SetTorqueActorName); }); DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, RawTorque); } @@ -699,7 +687,7 @@ public class BSPrim : BSPhysObject return; _orientation = value; - PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() + PhysScene.TaintedObject("BSPrim.setOrientation", delegate() { ForceOrientation = _orientation; }); @@ -710,14 +698,14 @@ public class BSPrim : BSPhysObject { get { - _orientation = PhysicsScene.PE.GetOrientation(PhysBody); + _orientation = PhysScene.PE.GetOrientation(PhysBody); return _orientation; } set { _orientation = value; if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); } } public override int PhysicsActorType { @@ -730,7 +718,7 @@ public class BSPrim : BSPhysObject if (_isPhysical != value) { _isPhysical = value; - PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() + PhysScene.TaintedObject("BSPrim.setIsPhysical", delegate() { DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); SetObjectDynamic(true); @@ -779,13 +767,13 @@ public class BSPrim : BSPhysObject if (!PhysBody.HasPhysicalBody) { // This would only happen if updates are called for during initialization when the body is not set up yet. - DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); + // DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); return; } // Mangling all the physical properties requires the object not be in the physical world. // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); // Set up the object physicalness (does gravity and collisions move this object) MakeDynamic(IsStatic); @@ -802,10 +790,11 @@ public class BSPrim : BSPhysObject AddObjectToPhysicalWorld(); // Rebuild its shape - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); + PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", - LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); + LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), + CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); } // "Making dynamic" means changing to and from static. @@ -818,28 +807,28 @@ public class BSPrim : BSPhysObject if (makeStatic) { // Become a Bullet 'static' object type - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); // Stop all movement ZeroMotion(true); // Set various physical properties so other object interact properly - PhysicsScene.PE.SetFriction(PhysBody, Friction); - PhysicsScene.PE.SetRestitution(PhysBody, Restitution); - PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + PhysScene.PE.SetFriction(PhysBody, Friction); + PhysScene.PE.SetRestitution(PhysBody, Restitution); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); // Mass is zero which disables a bunch of physics stuff in Bullet UpdatePhysicalMassProperties(0f, false); // Set collision detection parameters if (BSParam.CcdMotionThreshold > 0f) { - PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); - PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); } // The activation state is 'disabled' so Bullet will not try to act on it. // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); // Start it out sleeping and physical actions could wake it up. - PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); // This collides like a static object PhysBody.collisionType = CollisionType.Static; @@ -847,11 +836,11 @@ public class BSPrim : BSPhysObject else { // Not a Bullet static object - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); // Set various physical properties so other object interact properly - PhysicsScene.PE.SetFriction(PhysBody, Friction); - PhysicsScene.PE.SetRestitution(PhysBody, Restitution); + PhysScene.PE.SetFriction(PhysBody, Friction); + PhysScene.PE.SetRestitution(PhysBody, Restitution); // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution); // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 @@ -869,22 +858,22 @@ public class BSPrim : BSPhysObject // Set collision detection parameters if (BSParam.CcdMotionThreshold > 0f) { - PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); - PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); } // Various values for simulation limits - PhysicsScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); - PhysicsScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); - PhysicsScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); - PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + PhysScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); + PhysScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); + PhysScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); // This collides like an object. PhysBody.collisionType = CollisionType.Dynamic; // Force activation of the object so Bullet will act on it. // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. - PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); } } @@ -894,7 +883,7 @@ public class BSPrim : BSPhysObject // the functions after this one set up the state of a possibly newly created collision body. private void MakeSolid(bool makeSolid) { - CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(PhysBody); + CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysScene.PE.GetBodyType(PhysBody); if (makeSolid) { // Verify the previous code created the correct shape for this type of thing. @@ -902,7 +891,7 @@ public class BSPrim : BSPhysObject { m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); } - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); } else { @@ -910,7 +899,7 @@ public class BSPrim : BSPhysObject { m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); } - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); // Change collision info from a static object to a ghosty collision object PhysBody.collisionType = CollisionType.VolumeDetect; @@ -922,11 +911,11 @@ public class BSPrim : BSPhysObject { if (wantsCollisionEvents) { - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); } else { - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); } } @@ -937,7 +926,7 @@ public class BSPrim : BSPhysObject { if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); } else { @@ -972,12 +961,12 @@ public class BSPrim : BSPhysObject public override bool FloatOnWater { set { _floatOnWater = value; - PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() + PhysScene.TaintedObject("BSPrim.setFloatOnWater", delegate() { if (_floatOnWater) - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); else - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); }); } } @@ -989,7 +978,7 @@ public class BSPrim : BSPhysObject _rotationalVelocity = value; Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); - PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() + PhysScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() { ForceRotationalVelocity = _rotationalVelocity; }); @@ -1004,7 +993,7 @@ public class BSPrim : BSPhysObject if (PhysBody.HasPhysicalBody) { DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); - PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); + PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); ActivateIfPhysical(false); } @@ -1020,7 +1009,7 @@ public class BSPrim : BSPhysObject get { return _buoyancy; } set { _buoyancy = value; - PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() + PhysScene.TaintedObject("BSPrim.setBuoyancy", delegate() { ForceBuoyancy = _buoyancy; }); @@ -1043,7 +1032,7 @@ public class BSPrim : BSPhysObject base.MoveToTargetActive = value; EnableActor(MoveToTargetActive, MoveToTargetActorName, delegate() { - return new BSActorMoveToTarget(PhysicsScene, this, MoveToTargetActorName); + return new BSActorMoveToTarget(PhysScene, this, MoveToTargetActorName); }); } } @@ -1055,7 +1044,7 @@ public class BSPrim : BSPhysObject base.HoverActive = value; EnableActor(HoverActive, HoverActorName, delegate() { - return new BSActorHover(PhysicsScene, this, HoverActorName); + return new BSActorHover(PhysScene, this, HoverActorName); }); } } @@ -1065,7 +1054,7 @@ public class BSPrim : BSPhysObject OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); // Since this force is being applied in only one step, make this a force per second. - addForce /= PhysicsScene.LastTimeStep; + addForce /= PhysScene.LastTimeStep; AddForce(addForce, pushforce, false /* inTaintTime */); } @@ -1080,13 +1069,13 @@ public class BSPrim : BSPhysObject // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); OMV.Vector3 addForce = force; - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() { // Bullet adds this central force to the total force for this tick DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); + PhysScene.PE.ApplyCentralForce(PhysBody, addForce); ActivateIfPhysical(false); } }); @@ -1108,13 +1097,13 @@ public class BSPrim : BSPhysObject OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude); // DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse); - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate() { // Bullet adds this impulse immediately to the velocity DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); + PhysScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); ActivateIfPhysical(false); } }); @@ -1133,12 +1122,12 @@ public class BSPrim : BSPhysObject if (force.IsFinite()) { OMV.Vector3 angForce = force; - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() { if (PhysBody.HasPhysicalBody) { DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); - PhysicsScene.PE.ApplyTorque(PhysBody, angForce); + PhysScene.PE.ApplyTorque(PhysBody, angForce); ActivateIfPhysical(false); } }); @@ -1157,11 +1146,11 @@ public class BSPrim : BSPhysObject public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) { OMV.Vector3 applyImpulse = impulse; - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() { if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); + PhysScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); ActivateIfPhysical(false); } }); @@ -1463,12 +1452,13 @@ public class BSPrim : BSPhysObject // Create the correct physical representation for this type of object. // Updates base.PhysBody and base.PhysShape with the new information. // Ignore 'forceRebuild'. 'GetBodyAndShape' makes the right choices and changes of necessary. - PhysicsScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysicsScene.World, this, null, delegate(BulletBody dBody) + PhysScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysScene.World, this, delegate(BulletBody pBody, BulletShape pShape) { // Called if the current prim body is about to be destroyed. // Remove all the physical dependencies on the old body. // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) - RemoveBodyDependencies(); + // Note: this virtual function is overloaded by BSPrimLinkable to remove linkset constraints. + RemoveDependencies(); }); // Make sure the properties are set on the new object @@ -1477,9 +1467,9 @@ public class BSPrim : BSPhysObject } // Called at taint-time - protected virtual void RemoveBodyDependencies() + protected virtual void RemoveDependencies() { - PhysicalActors.RemoveBodyDependencies(); + PhysicalActors.RemoveDependencies(); } // The physics engine says that properties have updated. Update same and inform diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs index f1c3b5c1bb..f5ee671862 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs @@ -78,14 +78,16 @@ public class BSPrimDisplaced : BSPrim // Set this sets and computes the displacement from the passed prim to the center-of-mass. // A user set value for center-of-mass overrides whatever might be passed in here. // The displacement is in local coordinates (relative to root prim in linkset oriented coordinates). - public virtual void SetEffectiveCenterOfMassW(Vector3 centerOfMassDisplacement) + public virtual void SetEffectiveCenterOfMassDisplacement(Vector3 centerOfMassDisplacement) { Vector3 comDisp; - if (UserSetCenterOfMass.HasValue) - comDisp = (OMV.Vector3)UserSetCenterOfMass; + if (UserSetCenterOfMassDisplacement.HasValue) + comDisp = (OMV.Vector3)UserSetCenterOfMassDisplacement; else comDisp = centerOfMassDisplacement; + DetailLog("{0},BSPrimDisplaced.SetEffectiveCenterOfMassDisplacement,userSet={1},comDisp={2}", + LocalID, UserSetCenterOfMassDisplacement.HasValue, comDisp); if (comDisp == Vector3.Zero) { // If there is no diplacement. Things get reset. @@ -107,9 +109,15 @@ public class BSPrimDisplaced : BSPrim set { if (PositionDisplacement != OMV.Vector3.Zero) - base.ForcePosition = value - (PositionDisplacement * RawOrientation); + { + OMV.Vector3 displacedPos = value - (PositionDisplacement * RawOrientation); + DetailLog("{0},BSPrimDisplaced.ForcePosition,val={1},disp={2},newPos={3}", LocalID, value, PositionDisplacement, displacedPos); + base.ForcePosition = displacedPos; + } else + { base.ForcePosition = value; + } } } @@ -118,6 +126,7 @@ public class BSPrimDisplaced : BSPrim get { return base.ForceOrientation; } set { + // TODO: base.ForceOrientation = value; } } @@ -143,7 +152,10 @@ public class BSPrimDisplaced : BSPrim { // Correct for any rotation around the center-of-mass // TODO!!! - entprop.Position = entprop.Position + (PositionDisplacement * entprop.Rotation); + + OMV.Vector3 displacedPos = entprop.Position + (PositionDisplacement * entprop.Rotation); + DetailLog("{0},BSPrimDisplaced.ForcePosition,physPos={1},disp={2},newPos={3}", LocalID, entprop.Position, PositionDisplacement, displacedPos); + entprop.Position = displacedPos; // entprop.Rotation = something; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs index 28242d4ba0..235da782e1 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs @@ -47,9 +47,9 @@ public class BSPrimLinkable : BSPrimDisplaced OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) : base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical) { - Linkset = BSLinkset.Factory(PhysicsScene, this); + Linkset = BSLinkset.Factory(PhysScene, this); - PhysicsScene.TaintedObject("BSPrimLinksetCompound.Refresh", delegate() + PhysScene.TaintedObject("BSPrimLinksetCompound.Refresh", delegate() { Linkset.Refresh(this); }); @@ -61,9 +61,6 @@ public class BSPrimLinkable : BSPrimDisplaced base.Destroy(); } - public override BSPhysicsShapeType PreferredPhysicalShape - { get { return Linkset.PreferredPhysicalShape(this); } } - public override void link(Manager.PhysicsActor obj) { BSPrimLinkable parent = obj as BSPrimLinkable; @@ -102,7 +99,7 @@ public class BSPrimLinkable : BSPrimDisplaced set { base.Position = value; - PhysicsScene.TaintedObject("BSPrimLinkset.setPosition", delegate() + PhysScene.TaintedObject("BSPrimLinkset.setPosition", delegate() { Linkset.UpdateProperties(UpdatedProperties.Position, this); }); @@ -116,7 +113,7 @@ public class BSPrimLinkable : BSPrimDisplaced set { base.Orientation = value; - PhysicsScene.TaintedObject("BSPrimLinkset.setOrientation", delegate() + PhysScene.TaintedObject("BSPrimLinkset.setOrientation", delegate() { Linkset.UpdateProperties(UpdatedProperties.Orientation, this); }); @@ -149,10 +146,10 @@ public class BSPrimLinkable : BSPrimDisplaced } // Body is being taken apart. Remove physical dependencies and schedule a rebuild. - protected override void RemoveBodyDependencies() + protected override void RemoveDependencies() { - Linkset.RemoveBodyDependencies(this); - base.RemoveBodyDependencies(); + Linkset.RemoveDependencies(this); + base.RemoveDependencies(); } public override void UpdateProperties(EntityProperties entprop) @@ -185,6 +182,10 @@ public class BSPrimLinkable : BSPrimDisplaced { return false; } + + // TODO: handle collisions of other objects with with children of linkset. + // This is a problem for LinksetCompound since the children are packed into the root. + return base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 8e05b587aa..a4a87943a3 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -316,7 +316,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters break; case "bulletxna": ret = new BSAPIXNA(engineName, this); + // Disable some features that are not implemented in BulletXNA + m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader); BSParam.ShouldUseBulletHACD = false; + BSParam.ShouldUseSingleConvexHullForPrims = false; break; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index bc264602ef..64aaa1503d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -38,38 +38,15 @@ public sealed class BSShapeCollection : IDisposable { private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; - private BSScene PhysicsScene { get; set; } + private BSScene m_physicsScene { get; set; } private Object m_collectionActivityLock = new Object(); - // Description of a Mesh - private struct MeshDesc - { - public BulletShape shape; - public int referenceCount; - public DateTime lastReferenced; - public UInt64 shapeKey; - } - - // Description of a hull. - // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations. - private struct HullDesc - { - public BulletShape shape; - public int referenceCount; - public DateTime lastReferenced; - public UInt64 shapeKey; - } - - // The sharable set of meshes and hulls. Indexed by their shape hash. - private Dictionary Meshes = new Dictionary(); - private Dictionary Hulls = new Dictionary(); - private bool DDetail = false; public BSShapeCollection(BSScene physScene) { - PhysicsScene = physScene; + m_physicsScene = physScene; // Set the next to 'true' for very detailed shape update detailed logging (detailed details?) // While detailed debugging is still active, this is better than commenting out all the // DetailLog statements. When debugging slows down, this and the protected logging @@ -86,22 +63,18 @@ public sealed class BSShapeCollection : IDisposable // Mostly used for changing bodies out from under Linksets. // Useful for other cases where parameters need saving. // Passing 'null' says no callback. - public delegate void ShapeDestructionCallback(BulletShape shape); - public delegate void BodyDestructionCallback(BulletBody body); + public delegate void PhysicalDestructionCallback(BulletBody pBody, BulletShape pShape); // Called to update/change the body and shape for an object. - // First checks the shape and updates that if necessary then makes - // sure the body is of the right type. + // The object has some shape and body on it. Here we decide if that is the correct shape + // for the current state of the object (static/dynamic/...). + // If bodyCallback is not null, it is called if either the body or the shape are changed + // so dependencies (like constraints) can be removed before the physical object is dereferenced. // Return 'true' if either the body or the shape changed. - // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before - // the current shape or body is destroyed. This allows the caller to remove any - // higher level dependencies on the shape or body. Mostly used for LinkSets to - // remove the physical constraints before the body is destroyed. - // Called at taint-time!! - public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, - ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) + // Called at taint-time. + public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback) { - PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); + m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); bool ret = false; @@ -111,12 +84,12 @@ public sealed class BSShapeCollection : IDisposable // Do we have the correct geometry for this type of object? // Updates prim.BSShape with information/pointers to shape. // Returns 'true' of BSShape is changed to a new shape. - bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback); + bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback); // If we had to select a new shape geometry for the object, // rebuild the body around it. // Updates prim.BSBody with information/pointers to requested body // Returns 'true' if BSBody was changed. - bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, bodyCallback); + bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback); ret = newGeom || newBody; } DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}", @@ -127,271 +100,20 @@ public sealed class BSShapeCollection : IDisposable public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim) { - return GetBodyAndShape(forceRebuild, sim, prim, null, null); + return GetBodyAndShape(forceRebuild, sim, prim, null); } - // Track another user of a body. - // We presume the caller has allocated the body. - // Bodies only have one user so the body is just put into the world if not already there. - private void ReferenceBody(BulletBody body) + // If the existing prim's shape is to be replaced, remove the tie to the existing shape + // before replacing it. + private void DereferenceExistingShape(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { - lock (m_collectionActivityLock) + if (prim.PhysShape.HasPhysicalShape) { - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); - if (!PhysicsScene.PE.IsInWorld(PhysicsScene.World, body)) - { - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, body); - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); - } - } - } - - // Release the usage of a body. - // Called when releasing use of a BSBody. BSShape is handled separately. - // Called in taint time. - public void DereferenceBody(BulletBody body, BodyDestructionCallback bodyCallback ) - { - if (!body.HasPhysicalBody) - return; - - PhysicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody"); - - lock (m_collectionActivityLock) - { - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); - // If the caller needs to know the old body is going away, pass the event up. - if (bodyCallback != null) bodyCallback(body); - - // Removing an object not in the world is a NOOP - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, body); - - // Zero any reference to the shape so it is not freed when the body is deleted. - PhysicsScene.PE.SetCollisionShape(PhysicsScene.World, body, null); - PhysicsScene.PE.DestroyObject(PhysicsScene.World, body); - } - } - - // Track the datastructures and use count for a shape. - // When creating a hull, this is called first to reference the mesh - // and then again to reference the hull. - // Meshes and hulls for the same shape have the same hash key. - // NOTE that native shapes are not added to the mesh list or removed. - // Returns 'true' if this is the initial reference to the shape. Otherwise reused. - public bool ReferenceShape(BulletShape shape) - { - bool ret = false; - switch (shape.type) - { - case BSPhysicsShapeType.SHAPE_MESH: - MeshDesc meshDesc; - if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) - { - // There is an existing instance of this mesh. - meshDesc.referenceCount++; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); - } - else - { - // This is a new reference to a mesh - meshDesc.shape = shape.Clone(); - meshDesc.shapeKey = shape.shapeKey; - // We keep a reference to the underlying IMesh data so a hull can be built - meshDesc.referenceCount = 1; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); - ret = true; - } - meshDesc.lastReferenced = System.DateTime.Now; - Meshes[shape.shapeKey] = meshDesc; - break; - case BSPhysicsShapeType.SHAPE_HULL: - HullDesc hullDesc; - if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) - { - // There is an existing instance of this hull. - hullDesc.referenceCount++; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); - } - else - { - // This is a new reference to a hull - hullDesc.shape = shape.Clone(); - hullDesc.shapeKey = shape.shapeKey; - hullDesc.referenceCount = 1; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); - ret = true; - - } - hullDesc.lastReferenced = System.DateTime.Now; - Hulls[shape.shapeKey] = hullDesc; - break; - case BSPhysicsShapeType.SHAPE_UNKNOWN: - break; - default: - // Native shapes are not tracked and they don't go into any list - break; - } - return ret; - } - - // Release the usage of a shape. - public void DereferenceShape(BulletShape shape, ShapeDestructionCallback shapeCallback) - { - if (!shape.HasPhysicalShape) - return; - - PhysicsScene.AssertInTaintTime("BSShapeCollection.DereferenceShape"); - - if (shape.HasPhysicalShape) - { - if (shape.isNativeShape) - { - // Native shapes are not tracked and are released immediately - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1}", - BSScene.DetailLogZero, shape.AddrString); - if (shapeCallback != null) shapeCallback(shape); - PhysicsScene.PE.DeleteCollisionShape(PhysicsScene.World, shape); - } - else - { - switch (shape.type) - { - case BSPhysicsShapeType.SHAPE_HULL: - DereferenceHull(shape, shapeCallback); - break; - case BSPhysicsShapeType.SHAPE_MESH: - DereferenceMesh(shape, shapeCallback); - break; - case BSPhysicsShapeType.SHAPE_COMPOUND: - DereferenceCompound(shape, shapeCallback); - break; - case BSPhysicsShapeType.SHAPE_UNKNOWN: - break; - default: - break; - } - } - } - } - - // Count down the reference count for a mesh shape - // Called at taint-time. - private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback) - { - MeshDesc meshDesc; - if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) - { - meshDesc.referenceCount--; - // TODO: release the Bullet storage - if (shapeCallback != null) shapeCallback(shape); - meshDesc.lastReferenced = System.DateTime.Now; - Meshes[shape.shapeKey] = meshDesc; - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}", - BSScene.DetailLogZero, shape, meshDesc.referenceCount); - - } - } - - // Count down the reference count for a hull shape - // Called at taint-time. - private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback) - { - HullDesc hullDesc; - if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) - { - hullDesc.referenceCount--; - // TODO: release the Bullet storage (aging old entries?) - - // Tell upper layers that, if they have dependencies on this shape, this link is going away - if (shapeCallback != null) shapeCallback(shape); - - hullDesc.lastReferenced = System.DateTime.Now; - Hulls[shape.shapeKey] = hullDesc; - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}", - BSScene.DetailLogZero, shape, hullDesc.referenceCount); - } - } - - // Remove a reference to a compound shape. - // Taking a compound shape apart is a little tricky because if you just delete the - // physical shape, it will free all the underlying children. We can't do that because - // they could be shared. So, this removes each of the children from the compound and - // dereferences them separately before destroying the compound collision object itself. - // Called at taint-time. - private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback) - { - if (!PhysicsScene.PE.IsCompound(shape)) - { - // Failed the sanity check!! - PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", - LogHeader, shape.type, shape.AddrString); - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", - BSScene.DetailLogZero, shape.type, shape.AddrString); - return; - } - - int numChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(shape); - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren); - - for (int ii = numChildren - 1; ii >= 0; ii--) - { - BulletShape childShape = PhysicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(shape, ii); - DereferenceAnonCollisionShape(childShape); - } - PhysicsScene.PE.DeleteCollisionShape(PhysicsScene.World, shape); - } - - // Sometimes we have a pointer to a collision shape but don't know what type it is. - // Figure out type and call the correct dereference routine. - // Called at taint-time. - private void DereferenceAnonCollisionShape(BulletShape shapeInfo) - { - MeshDesc meshDesc; - HullDesc hullDesc; - - if (TryGetMeshByPtr(shapeInfo, out meshDesc)) - { - shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH; - shapeInfo.shapeKey = meshDesc.shapeKey; - } - else - { - if (TryGetHullByPtr(shapeInfo, out hullDesc)) - { - shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL; - shapeInfo.shapeKey = hullDesc.shapeKey; - } - else - { - if (PhysicsScene.PE.IsCompound(shapeInfo)) - { - shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND; - } - else - { - if (PhysicsScene.PE.IsNativeShape(shapeInfo)) - { - shapeInfo.isNativeShape = true; - shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) - } - } - } - } - - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); - - if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN) - { - DereferenceShape(shapeInfo, null); - } - else - { - PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}", - LogHeader, PhysicsScene.RegionName, shapeInfo.AddrString); + if (shapeCallback != null) + shapeCallback(prim.PhysBody, prim.PhysShape.physShapeInfo); + prim.PhysShape.Dereference(m_physicsScene); } + prim.PhysShape = new BSShapeNull(); } // Create the geometry information in Bullet for later use. @@ -402,60 +124,41 @@ public sealed class BSShapeCollection : IDisposable // Info in prim.BSShape is updated to the new shape. // Returns 'true' if the geometry was rebuilt. // Called at taint-time! - private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) - { - bool ret = false; - bool haveShape = false; - - if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) - { - // an avatar capsule is close to a native shape (it is not shared) - GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE, shapeCallback); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); - ret = true; - haveShape = true; - } - - // Compound shapes are handled special as they are rebuilt from scratch. - // This isn't too great a hardship since most of the child shapes will have already been created. - if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) - { - ret = GetReferenceToCompoundShape(prim, shapeCallback); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); - haveShape = true; - } - - if (!haveShape) - { - ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback); - } - - return ret; - } - - // Create a mesh, hull or native shape. - // Return 'true' if the prim's shape was changed. - public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) + private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { bool ret = false; bool haveShape = false; bool nativeShapePossible = true; PrimitiveBaseShape pbs = prim.BaseShape; + // Kludge to create the capsule for the avatar. + // TDOD: Remove/redo this when BSShapeAvatar is working!! + BSCharacter theChar = prim as BSCharacter; + if (theChar != null) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE); + ret = true; + haveShape = true; + } + // If the prim attributes are simple, this could be a simple Bullet native shape + // Native shapes work whether to object is static or physical. if (!haveShape && nativeShapePossible && pbs != null - && !pbs.SculptEntry - && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) || PrimHasNoCuts(pbs)) ) + && PrimHasNoCuts(pbs) + && ( !pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) ) + ) { // Get the scale of any existing shape so we can see if the new shape is same native type and same size. OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; if (prim.PhysShape.HasPhysicalShape) - scaleOfExistingShape = PhysicsScene.PE.GetLocalScaling(prim.PhysShape); + scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo); if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", - prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type); + prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType); // It doesn't look like Bullet scales native spheres so make sure the scales are all equal if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) @@ -463,26 +166,30 @@ public sealed class BSShapeCollection : IDisposable { haveShape = true; if (forceRebuild - || prim.Scale != scaleOfExistingShape - || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE - ) + || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE + ) { - ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, - FixedShapeKey.KEY_SPHERE, shapeCallback); + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE); + ret = true; } if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}", prim.LocalID, forceRebuild, ret, prim.PhysShape); } + // If we didn't make a sphere, maybe a box will work. if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) { haveShape = true; if (forceRebuild || prim.Scale != scaleOfExistingShape - || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX + || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX ) { - ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX, - FixedShapeKey.KEY_BOX, shapeCallback); + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + ret = true; } if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}", prim.LocalID, forceRebuild, ret, prim.PhysShape); @@ -499,7 +206,7 @@ public sealed class BSShapeCollection : IDisposable } // return 'true' if this shape description does not include any cutting or twisting. - private bool PrimHasNoCuts(PrimitiveBaseShape pbs) + public static bool PrimHasNoCuts(PrimitiveBaseShape pbs) { return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 && pbs.ProfileHollow == 0 @@ -511,7 +218,7 @@ public sealed class BSShapeCollection : IDisposable } // return 'true' if the prim's shape was changed. - public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) + private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { bool ret = false; @@ -519,538 +226,110 @@ public sealed class BSShapeCollection : IDisposable // made. Native shapes work in either case. if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { - // Update prim.BSShape to reference a hull of this shape. - ret = GetReferenceToHull(prim, shapeCallback); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", - prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); + // Use a simple, single mesh convex hull shape if the object is simple enough + BSShape potentialHull = null; + + PrimitiveBaseShape pbs = prim.BaseShape; + if (BSParam.ShouldUseSingleConvexHullForPrims + && pbs != null + && !pbs.SculptEntry + && PrimHasNoCuts(pbs) + ) + { + potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim); + } + else + { + potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + } + + // If the current shape is not what is on the prim at the moment, time to change. + if (!prim.PhysShape.HasPhysicalShape + || potentialHull.ShapeType != prim.PhysShape.ShapeType + || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = potentialHull; + ret = true; + } + else + { + // The current shape on the prim is the correct one. We don't need the potential reference. + potentialHull.Dereference(m_physicsScene); + } + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape); } else { - ret = GetReferenceToMesh(prim, shapeCallback); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", - prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); + // Update prim.BSShape to reference a mesh of this shape. + BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + // If the current shape is not what is on the prim at the moment, time to change. + if (!prim.PhysShape.HasPhysicalShape + || potentialMesh.ShapeType != prim.PhysShape.ShapeType + || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = potentialMesh; + ret = true; + } + else + { + // We don't need this reference to the mesh that is already being using. + potentialMesh.Dereference(m_physicsScene); + } + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape); } return ret; } - // Creates a native shape and assignes it to prim.BSShape. - // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). - private bool GetReferenceToNativeShape(BSPhysObject prim, - BSPhysicsShapeType shapeType, FixedShapeKey shapeKey, - ShapeDestructionCallback shapeCallback) + // Track another user of a body. + // We presume the caller has allocated the body. + // Bodies only have one user so the body is just put into the world if not already there. + private void ReferenceBody(BulletBody body) { - // release any previous shape - DereferenceShape(prim.PhysShape, shapeCallback); - - BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); - - // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. - if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", - prim.LocalID, newShape, prim.Scale); - - // native shapes are scaled by Bullet - prim.PhysShape = newShape; - return true; - } - - private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType, - FixedShapeKey shapeKey) - { - BulletShape newShape; - // Need to make sure the passed shape information is for the native type. - ShapeData nativeShapeData = new ShapeData(); - nativeShapeData.Type = shapeType; - nativeShapeData.ID = prim.LocalID; - nativeShapeData.Scale = prim.Scale; - nativeShapeData.Size = prim.Scale; // unneeded, I think. - nativeShapeData.MeshKey = (ulong)shapeKey; - nativeShapeData.HullKey = (ulong)shapeKey; - - if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) + lock (m_collectionActivityLock) { - - newShape = PhysicsScene.PE.BuildCapsuleShape(PhysicsScene.World, 1f, 1f, prim.Scale); - if (DDetail) DetailLog("{0},BSShapeCollection.BuildPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); - } - else - { - // Native shapes are scaled in Bullet so set the scaling to the size - newShape = PhysicsScene.PE.BuildNativeShape(PhysicsScene.World, nativeShapeData); - - } - if (!newShape.HasPhysicalShape) - { - PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", - LogHeader, prim.LocalID, shapeType); - } - newShape.shapeKey = (System.UInt64)shapeKey; - newShape.isNativeShape = true; - - return newShape; - } - - // Builds a mesh shape in the physical world and updates prim.BSShape. - // Dereferences previous shape in BSShape and adds a reference for this new shape. - // Returns 'true' of a mesh was actually built. Otherwise . - // Called at taint-time! - private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback) - { - BulletShape newShape = new BulletShape(); - - float lod; - System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - - // if this new shape is the same as last time, don't recreate the mesh - if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH) - return false; - - if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2},size={3},lod={4}", - prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"), prim.Size, lod); - - // Since we're recreating new, get rid of the reference to the previous shape - DereferenceShape(prim.PhysShape, shapeCallback); - - newShape = CreatePhysicalMesh(prim, newMeshKey, prim.BaseShape, prim.Size, lod); - // Take evasive action if the mesh was not constructed. - newShape = VerifyMeshCreated(PhysicsScene, newShape, prim); - - ReferenceShape(newShape); - - prim.PhysShape = newShape; - - return true; // 'true' means a new shape has been added to this prim - } - - private BulletShape CreatePhysicalMesh(BSPhysObject prim, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) - { - BulletShape newShape = new BulletShape(); - - MeshDesc meshDesc; - if (Meshes.TryGetValue(newMeshKey, out meshDesc)) - { - // If the mesh has already been built just use it. - newShape = meshDesc.shape.Clone(); - } - else - { - IMesh meshData = PhysicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, - false, // say it is not physical so a bounding box is not built - false // do not cache the mesh and do not use previously built versions - ); - - if (meshData != null) + if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); + if (!m_physicsScene.PE.IsInWorld(m_physicsScene.World, body)) { - - int[] indices = meshData.getIndexListAsInt(); - int realIndicesIndex = indices.Length; - float[] verticesAsFloats = meshData.getVertexListAsFloat(); - - if (BSParam.ShouldRemoveZeroWidthTriangles) - { - // Remove degenerate triangles. These are triangles with two of the vertices - // are the same. This is complicated by the problem that vertices are not - // made unique in sculpties so we have to compare the values in the vertex. - realIndicesIndex = 0; - for (int tri = 0; tri < indices.Length; tri += 3) - { - // Compute displacements into vertex array for each vertex of the triangle - int v1 = indices[tri + 0] * 3; - int v2 = indices[tri + 1] * 3; - int v3 = indices[tri + 2] * 3; - // Check to see if any two of the vertices are the same - if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0] - && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1] - && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2]) - || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0] - && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1] - && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2]) - || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0] - && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1] - && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) ) - ) - { - // None of the vertices of the triangles are the same. This is a good triangle; - indices[realIndicesIndex + 0] = indices[tri + 0]; - indices[realIndicesIndex + 1] = indices[tri + 1]; - indices[realIndicesIndex + 2] = indices[tri + 2]; - realIndicesIndex += 3; - } - } - } - DetailLog("{0},BSShapeCollection.CreatePhysicalMesh,origTri={1},realTri={2},numVerts={3}", - BSScene.DetailLogZero, indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); - - if (realIndicesIndex != 0) - { - newShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, - realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats); - } - else - { - PhysicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim {1} at {2} in {3}", - LogHeader, prim.PhysObjectName, prim.RawPosition, PhysicsScene.Name); - } + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, body); + if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); } } - newShape.shapeKey = newMeshKey; - - return newShape; } - // See that hull shape exists in the physical world and update prim.BSShape. - // We could be creating the hull because scale changed or whatever. - // Return 'true' if a new hull was built. Otherwise, returning a shared hull instance. - private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) + // Release the usage of a body. + // Called when releasing use of a BSBody. BSShape is handled separately. + // Called in taint time. + public void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback ) { - BulletShape newShape; + if (!body.HasPhysicalBody) + return; - float lod; - System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + m_physicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody"); - // if the hull hasn't changed, don't rebuild it - if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL) - return false; - - if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", - prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X")); - - // Remove usage of the previous shape. - DereferenceShape(prim.PhysShape, shapeCallback); - - newShape = CreatePhysicalHull(prim, newHullKey, prim.BaseShape, prim.Size, lod); - // It might not have been created if we're waiting for an asset. - newShape = VerifyMeshCreated(PhysicsScene, newShape, prim); - - ReferenceShape(newShape); - - prim.PhysShape = newShape; - return true; // 'true' means a new shape has been added to this prim - } - - List m_hulls; - private BulletShape CreatePhysicalHull(BSPhysObject prim, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) - { - - BulletShape newShape = new BulletShape(); - IntPtr hullPtr = IntPtr.Zero; - - HullDesc hullDesc; - if (Hulls.TryGetValue(newHullKey, out hullDesc)) + lock (m_collectionActivityLock) { - // If the hull shape already has been created, just use the one shared instance. - newShape = hullDesc.shape.Clone(); + if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); + // If the caller needs to know the old body is going away, pass the event up. + if (bodyCallback != null) + bodyCallback(body, null); + + // Removing an object not in the world is a NOOP + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, body); + + // Zero any reference to the shape so it is not freed when the body is deleted. + m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, body, null); + + m_physicsScene.PE.DestroyObject(m_physicsScene.World, body); } - else - { - if (BSParam.ShouldUseBulletHACD) - { - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID); - MeshDesc meshDesc; - if (!Meshes.TryGetValue(newHullKey, out meshDesc)) - { - // That's odd because the mesh should have been created before the hull - // but, since it doesn't exist, create it. - newShape = CreatePhysicalMesh(prim, newHullKey, prim.BaseShape, prim.Size, lod); - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,noMeshBuiltNew,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); - - if (newShape.HasPhysicalShape) - { - ReferenceShape(newShape); - Meshes.TryGetValue(newHullKey, out meshDesc); - } - } - if (meshDesc.shape.HasPhysicalShape) - { - HACDParams parms; - parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; - parms.minClusters = BSParam.BHullMinClusters; - parms.compacityWeight = BSParam.BHullCompacityWeight; - parms.volumeWeight = BSParam.BHullVolumeWeight; - parms.concavity = BSParam.BHullConcavity; - parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); - parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); - parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); - parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); - - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); - newShape = PhysicsScene.PE.BuildHullShapeFromMesh(PhysicsScene.World, meshDesc.shape, parms); - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); - } - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); - } - if (!newShape.HasPhysicalShape) - { - // Build a new hull in the physical world. - // Pass true for physicalness as this prevents the creation of bounding box which is not needed - IMesh meshData = PhysicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); - if (meshData != null) - { - int[] indices = meshData.getIndexListAsInt(); - List vertices = meshData.getVertexList(); - - //format conversion from IMesh format to DecompDesc format - List convIndices = new List(); - List convVertices = new List(); - for (int ii = 0; ii < indices.GetLength(0); ii++) - { - convIndices.Add(indices[ii]); - } - foreach (OMV.Vector3 vv in vertices) - { - convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); - } - - uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; - if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) - { - // Simple primitive shapes we know are convex so they are better implemented with - // fewer hulls. - // Check for simple shape (prim without cuts) and reduce split parameter if so. - if (PrimHasNoCuts(pbs)) - { - maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; - } - } - - // setup and do convex hull conversion - m_hulls = new List(); - DecompDesc dcomp = new DecompDesc(); - dcomp.mIndices = convIndices; - dcomp.mVertices = convVertices; - dcomp.mDepth = maxDepthSplit; - dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; - dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; - dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; - dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; - ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); - // create the hull into the _hulls variable - convexBuilder.process(dcomp); - - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", - BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); - - // Convert the vertices and indices for passing to unmanaged. - // The hull information is passed as a large floating point array. - // The format is: - // convHulls[0] = number of hulls - // convHulls[1] = number of vertices in first hull - // convHulls[2] = hull centroid X coordinate - // convHulls[3] = hull centroid Y coordinate - // convHulls[4] = hull centroid Z coordinate - // convHulls[5] = first hull vertex X - // convHulls[6] = first hull vertex Y - // convHulls[7] = first hull vertex Z - // convHulls[8] = second hull vertex X - // ... - // convHulls[n] = number of vertices in second hull - // convHulls[n+1] = second hull centroid X coordinate - // ... - // - // TODO: is is very inefficient. Someday change the convex hull generator to return - // data structures that do not need to be converted in order to pass to Bullet. - // And maybe put the values directly into pinned memory rather than marshaling. - int hullCount = m_hulls.Count; - int totalVertices = 1; // include one for the count of the hulls - foreach (ConvexResult cr in m_hulls) - { - totalVertices += 4; // add four for the vertex count and centroid - totalVertices += cr.HullIndices.Count * 3; // we pass just triangles - } - float[] convHulls = new float[totalVertices]; - - convHulls[0] = (float)hullCount; - int jj = 1; - foreach (ConvexResult cr in m_hulls) - { - // copy vertices for index access - float3[] verts = new float3[cr.HullVertices.Count]; - int kk = 0; - foreach (float3 ff in cr.HullVertices) - { - verts[kk++] = ff; - } - - // add to the array one hull's worth of data - convHulls[jj++] = cr.HullIndices.Count; - convHulls[jj++] = 0f; // centroid x,y,z - convHulls[jj++] = 0f; - convHulls[jj++] = 0f; - foreach (int ind in cr.HullIndices) - { - convHulls[jj++] = verts[ind].x; - convHulls[jj++] = verts[ind].y; - convHulls[jj++] = verts[ind].z; - } - } - // create the hull data structure in Bullet - newShape = PhysicsScene.PE.CreateHullShape(PhysicsScene.World, hullCount, convHulls); - } - } - newShape.shapeKey = newHullKey; - } - - return newShape; - } - - // Callback from convex hull creater with a newly created hull. - // Just add it to our collection of hulls for this shape. - private void HullReturn(ConvexResult result) - { - m_hulls.Add(result); - return; - } - - // Compound shapes are always built from scratch. - // This shouldn't be to bad since most of the parts will be meshes that had been built previously. - private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback) - { - // Remove reference to the old shape - // Don't need to do this as the shape is freed when the new root shape is created below. - // DereferenceShape(prim.PhysShape, true, shapeCallback); - - BulletShape cShape = PhysicsScene.PE.CreateCompoundShape(PhysicsScene.World, false); - - // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. - CreateGeomMeshOrHull(prim, shapeCallback); - PhysicsScene.PE.AddChildShapeToCompoundShape(cShape, prim.PhysShape, OMV.Vector3.Zero, OMV.Quaternion.Identity); - if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}", - prim.LocalID, cShape, prim.PhysShape); - - prim.PhysShape = cShape; - - return true; - } - - // Create a hash of all the shape parameters to be used as a key - // for this particular shape. - public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) - { - // level of detail based on size and type of the object - float lod = BSParam.MeshLOD; - - // prims with curvy internal cuts need higher lod - if (pbs.HollowShape == HollowShape.Circle) - lod = BSParam.MeshCircularLOD; - - if (pbs.SculptEntry) - lod = BSParam.SculptLOD; - - // Mega prims usually get more detail because one can interact with shape approximations at this size. - float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); - if (maxAxis > BSParam.MeshMegaPrimThreshold) - lod = BSParam.MeshMegaPrimLOD; - - retLod = lod; - return pbs.GetMeshKey(size, lod); - } - // For those who don't want the LOD - public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs) - { - float lod; - return ComputeShapeKey(size, pbs, out lod); - } - - // The creation of a mesh or hull can fail if an underlying asset is not available. - // There are two cases: 1) the asset is not in the cache and it needs to be fetched; - // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). - // The first case causes the asset to be fetched. The second case requires - // us to not loop forever. - // Called after creating a physical mesh or hull. If the physical shape was created, - // just return. - public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim) - { - // If the shape was successfully created, nothing more to do - if (newShape.HasPhysicalShape) - return newShape; - - // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been - // fetched but we end up here again, the meshing of the asset must have failed. - // Prevent trying to keep fetching the mesh by declaring failure. - if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) - { - prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", - LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); - } - else - { - // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset - if (prim.BaseShape.SculptEntry - && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed - && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting - && prim.BaseShape.SculptTexture != OMV.UUID.Zero - ) - { - physicsScene.DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID); - // Multiple requestors will know we're waiting for this asset - prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; - - BSPhysObject xprim = prim; - Util.FireAndForget(delegate - { - RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; - if (assetProvider != null) - { - BSPhysObject yprim = xprim; // probably not necessary, but, just in case. - assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) - { - bool assetFound = false; - string mismatchIDs = String.Empty; // DEBUG DEBUG - if (asset != null && yprim.BaseShape.SculptEntry) - { - if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) - { - yprim.BaseShape.SculptData = asset.Data; - // This will cause the prim to see that the filler shape is not the right - // one and try again to build the object. - // No race condition with the normal shape setting since the rebuild is at taint time. - yprim.ForceBodyShapeRebuild(false /* inTaintTime */); - assetFound = true; - } - else - { - mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; - } - } - if (assetFound) - yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; - else - yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - physicsScene.DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", - yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); - - }); - } - else - { - xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", - LogHeader, physicsScene.Name); - } - }); - } - else - { - if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) - { - physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", - LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); - } - } - } - - // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. - BulletShape fillinShape = physicsScene.Shapes.BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); - physicsScene.DetailLog("{0},BSShapeCollection.VerifyMeshCreated,boxTempShape", prim.LocalID); - - return fillinShape; } // Create a body object in Bullet. // Updates prim.BSBody with the information about the new body if one is created. // Returns 'true' if an object was actually created. // Called at taint-time. - private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BodyDestructionCallback bodyCallback) + private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, PhysicalDestructionCallback bodyCallback) { bool ret = false; @@ -1061,7 +340,7 @@ public sealed class BSShapeCollection : IDisposable // If not a solid object, body is a GhostObject. Otherwise a RigidBody. if (!mustRebuild) { - CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(prim.PhysBody); + CollisionObjectTypes bodyType = (CollisionObjectTypes)m_physicsScene.PE.GetBodyType(prim.PhysBody); if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) { @@ -1079,12 +358,12 @@ public sealed class BSShapeCollection : IDisposable BulletBody aBody; if (prim.IsSolid) { - aBody = PhysicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,body={1}", prim.LocalID, aBody); + aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); + if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,rigid,body={1}", prim.LocalID, aBody); } else { - aBody = PhysicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation); + aBody = m_physicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody); } @@ -1098,46 +377,10 @@ public sealed class BSShapeCollection : IDisposable return ret; } - private bool TryGetMeshByPtr(BulletShape shape, out MeshDesc outDesc) - { - bool ret = false; - MeshDesc foundDesc = new MeshDesc(); - foreach (MeshDesc md in Meshes.Values) - { - if (md.shape.ReferenceSame(shape)) - { - foundDesc = md; - ret = true; - break; - } - - } - outDesc = foundDesc; - return ret; - } - - private bool TryGetHullByPtr(BulletShape shape, out HullDesc outDesc) - { - bool ret = false; - HullDesc foundDesc = new HullDesc(); - foreach (HullDesc hd in Hulls.Values) - { - if (hd.shape.ReferenceSame(shape)) - { - foundDesc = hd; - ret = true; - break; - } - - } - outDesc = foundDesc; - return ret; - } - private void DetailLog(string msg, params Object[] args) { - if (PhysicsScene.PhysicsLogging.Enabled) - PhysicsScene.DetailLog(msg, args); + if (m_physicsScene.PhysicsLogging.Enabled) + m_physicsScene.DetailLog(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index dd5ae1ae60..3e4ee5a5b8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -31,6 +31,7 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; using OMV = OpenMetaverse; @@ -38,74 +39,39 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public abstract class BSShape { + private static string LogHeader = "[BULLETSIM SHAPE]"; + public int referenceCount { get; set; } public DateTime lastReferenced { get; set; } public BulletShape physShapeInfo { get; set; } public BSShape() { - referenceCount = 0; + referenceCount = 1; lastReferenced = DateTime.Now; physShapeInfo = new BulletShape(); } public BSShape(BulletShape pShape) { - referenceCount = 0; + referenceCount = 1; lastReferenced = DateTime.Now; physShapeInfo = pShape; } - // Get a reference to a physical shape. Create if it doesn't exist - public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) - { - BSShape ret = null; - - if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) - { - // an avatar capsule is close to a native shape (it is not shared) - ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE, - FixedShapeKey.KEY_CAPSULE); - physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); - } - - // Compound shapes are handled special as they are rebuilt from scratch. - // This isn't too great a hardship since most of the child shapes will have already been created. - if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) - { - // Getting a reference to a compound shape gets you the compound shape with the root prim shape added - ret = BSShapeCompound.GetReference(prim); - physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); - } - - // Avatars have their own unique shape - if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_AVATAR) - { - // Getting a reference to a compound shape gets you the compound shape with the root prim shape added - ret = BSShapeAvatar.GetReference(prim); - physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,avatarShape,shape={1}", prim.LocalID, ret); - } - - if (ret == null) - ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim); - - return ret; - } - private static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) - { - BSShapeMesh.GetReference(physicsScene, forceRebuild, prim); - BSShapeHull.GetReference(physicsScene, forceRebuild, prim); - return null; - } + // Get another reference to this shape. + public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim); // Called when this shape is being used again. - public virtual void IncrementReference() + // Used internally. External callers should call instance.GetReference() to properly copy/reference + // the shape. + protected virtual void IncrementReference() { referenceCount++; lastReferenced = DateTime.Now; } // Called when this shape is being used again. - public virtual void DecrementReference() + protected virtual void DecrementReference() { referenceCount--; lastReferenced = DateTime.Now; @@ -114,22 +80,178 @@ public abstract class BSShape // Release the use of a physical shape. public abstract void Dereference(BSScene physicsScene); + // Return 'true' if there is an allocated physics physical shape under this class instance. + public virtual bool HasPhysicalShape + { + get + { + if (physShapeInfo != null) + return physShapeInfo.HasPhysicalShape; + return false; + } + } + public virtual BSPhysicsShapeType ShapeType + { + get + { + BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + if (physShapeInfo != null && physShapeInfo.HasPhysicalShape) + ret = physShapeInfo.shapeType; + return ret; + } + } + // Returns a string for debugging that uniquily identifies the memory used by this instance public virtual string AddrString { - get { return "unknown"; } + get + { + if (physShapeInfo != null) + return physShapeInfo.AddrString; + return "unknown"; + } } public override string ToString() { StringBuilder buff = new StringBuilder(); - buff.Append(""); return buff.ToString(); } + + #region Common shape routines + // Create a hash of all the shape parameters to be used as a key for this particular shape. + public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) + { + // level of detail based on size and type of the object + float lod = BSParam.MeshLOD; + if (pbs.SculptEntry) + lod = BSParam.SculptLOD; + + // Mega prims usually get more detail because one can interact with shape approximations at this size. + float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); + if (maxAxis > BSParam.MeshMegaPrimThreshold) + lod = BSParam.MeshMegaPrimLOD; + + retLod = lod; + return pbs.GetMeshKey(size, lod); + } + + // The creation of a mesh or hull can fail if an underlying asset is not available. + // There are two cases: 1) the asset is not in the cache and it needs to be fetched; + // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). + // The first case causes the asset to be fetched. The second case requires + // us to not loop forever. + // Called after creating a physical mesh or hull. If the physical shape was created, + // just return. + public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim) + { + // If the shape was successfully created, nothing more to do + if (newShape.HasPhysicalShape) + return newShape; + + // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been + // fetched but we end up here again, the meshing of the asset must have failed. + // Prevent trying to keep fetching the mesh by declaring failure. + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", + LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,objNam={1},tex={2}", + prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); + } + else + { + // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset + if (prim.BaseShape.SculptEntry + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting + && prim.BaseShape.SculptTexture != OMV.UUID.Zero + ) + { + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}", + prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); + // Multiple requestors will know we're waiting for this asset + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; + + BSPhysObject xprim = prim; + Util.FireAndForget(delegate + { + // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID); + RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; + if (assetProvider != null) + { + BSPhysObject yprim = xprim; // probably not necessary, but, just in case. + assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) + { + // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID); + bool assetFound = false; + string mismatchIDs = String.Empty; // DEBUG DEBUG + if (asset != null && yprim.BaseShape.SculptEntry) + { + if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) + { + yprim.BaseShape.SculptData = asset.Data; + // This will cause the prim to see that the filler shape is not the right + // one and try again to build the object. + // No race condition with the normal shape setting since the rebuild is at taint time. + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; + yprim.ForceBodyShapeRebuild(false /* inTaintTime */); + assetFound = true; + } + else + { + mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; + } + } + if (!assetFound) + { + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + } + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}", + yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); + }); + } + else + { + xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", + LogHeader, physicsScene.Name); + } + }); + } + else + { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) + { + physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", + LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,objNam={1},tex={2}", + prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); + } + } + } + + // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. + BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID); + + return fillShape.physShapeInfo; + } + + #endregion // Common shape routines } // ============================================================================================================ @@ -139,6 +261,7 @@ public class BSShapeNull : BSShape { } public static BSShape GetReference() { return new BSShapeNull(); } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return new BSShapeNull(); } public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ } } @@ -150,24 +273,34 @@ public class BSShapeNative : BSShape { } - public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, - BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) + public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, + BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) { // Native shapes are not shared and are always built anew. return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey)); } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + // Native shapes are not shared so we return a new shape. + return new BSShapeNative(CreatePhysicalNativeShape(pPhysicsScene, pPrim, + physShapeInfo.shapeType, (FixedShapeKey)physShapeInfo.shapeKey) ); + } + // Make this reference to the physical shape go away since native shapes are not shared. public override void Dereference(BSScene physicsScene) { // Native shapes are not tracked and are released immediately - if (physShapeInfo.HasPhysicalShape) + lock (physShapeInfo) { - physicsScene.DetailLog("{0},BSShapeNative.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); - physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); + if (physShapeInfo.HasPhysicalShape) + { + physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); + physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); + } + physShapeInfo.Clear(); + // Garbage collection will free up this instance. } - physShapeInfo.Clear(); - // Garbage collection will free up this instance. } private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim, @@ -197,7 +330,7 @@ public class BSShapeNative : BSShape physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", LogHeader, prim.LocalID, shapeType); } - newShape.type = shapeType; + newShape.shapeType = shapeType; newShape.isNativeShape = true; newShape.shapeKey = (UInt64)shapeKey; return newShape; @@ -209,7 +342,7 @@ public class BSShapeNative : BSShape public class BSShapeMesh : BSShape { private static string LogHeader = "[BULLETSIM SHAPE MESH]"; - private static Dictionary Meshes = new Dictionary(); + public static Dictionary Meshes = new Dictionary(); public BSShapeMesh(BulletShape pShape) : base(pShape) { @@ -217,12 +350,9 @@ public class BSShapeMesh : BSShape public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { float lod; - System.UInt64 newMeshKey = BSShapeCollection.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - physicsScene.DetailLog("{0},BSShapeMesh,create,oldKey={1},newKey={2},size={3},lod={4}", - prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"), prim.Size, lod); - - BSShapeMesh retMesh; + BSShapeMesh retMesh = null; lock (Meshes) { if (Meshes.TryGetValue(newMeshKey, out retMesh)) @@ -232,39 +362,78 @@ public class BSShapeMesh : BSShape } else { + retMesh = new BSShapeMesh(new BulletShape()); // An instance of this mesh has not been created. Build and remember same. - BulletShape newShape = CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); - // Take evasive action if the mesh was not constructed. - newShape = BSShapeCollection.VerifyMeshCreated(physicsScene, newShape, prim); + BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); - retMesh = new BSShapeMesh(newShape); + // Check to see if mesh was created (might require an asset). + newShape = VerifyMeshCreated(physicsScene, newShape, prim); + if (!newShape.isNativeShape) + { + // If a mesh was what was created, remember the built shape for later sharing. + Meshes.Add(newMeshKey, retMesh); + } - Meshes.Add(newMeshKey, retMesh); + retMesh.physShapeInfo = newShape; } } + physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod); return retMesh; } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + // Another reference to this shape is just counted. + IncrementReference(); + return this; + } public override void Dereference(BSScene physicsScene) { lock (Meshes) { this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeMesh.Dereference,shape={1}", BSScene.DetailLogZero, this); // TODO: schedule aging and destruction of unused meshes. } } + // Loop through all the known meshes and return the description based on the physical address. + public static bool TryGetMeshByPtr(BulletShape pShape, out BSShapeMesh outMesh) + { + bool ret = false; + BSShapeMesh foundDesc = null; + lock (Meshes) + { + foreach (BSShapeMesh sm in Meshes.Values) + { + if (sm.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sm; + ret = true; + break; + } - private static BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, + } + } + outMesh = foundDesc; + return ret; + } + private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { - BulletShape newShape = null; + BulletShape newShape = new BulletShape(); - IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, + IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, false, // say it is not physical so a bounding box is not built false // do not cache the mesh and do not use previously built versions ); if (meshData != null) { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + // Release the fetched asset data once it has been used. + pbs.SculptData = new byte[0]; + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; + } int[] indices = meshData.getIndexListAsInt(); int realIndicesIndex = indices.Length; @@ -302,8 +471,8 @@ public class BSShapeMesh : BSShape } } } - physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalMesh,origTri={1},realTri={2},numVerts={3}", - BSScene.DetailLogZero, indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); + physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}", + BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); if (realIndicesIndex != 0) { @@ -326,17 +495,239 @@ public class BSShapeMesh : BSShape public class BSShapeHull : BSShape { private static string LogHeader = "[BULLETSIM SHAPE HULL]"; - private static Dictionary Hulls = new Dictionary(); + public static Dictionary Hulls = new Dictionary(); public BSShapeHull(BulletShape pShape) : base(pShape) { } public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { - return new BSShapeNull(); + float lod; + System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + BSShapeHull retHull = null; + lock (Hulls) + { + if (Hulls.TryGetValue(newHullKey, out retHull)) + { + // The mesh has already been created. Return a new reference to same. + retHull.IncrementReference(); + } + else + { + retHull = new BSShapeHull(new BulletShape()); + // An instance of this mesh has not been created. Build and remember same. + BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod); + + // Check to see if hull was created (might require an asset). + newShape = VerifyMeshCreated(physicsScene, newShape, prim); + if (!newShape.isNativeShape) + { + // If a mesh was what was created, remember the built shape for later sharing. + Hulls.Add(newHullKey, retHull); + } + retHull.physShapeInfo = newShape; + } + } + physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod); + return retHull; + } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + // Another reference to this shape is just counted. + IncrementReference(); + return this; } public override void Dereference(BSScene physicsScene) { + lock (Hulls) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this); + // TODO: schedule aging and destruction of unused meshes. + } + } + List m_hulls; + private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, + PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + { + BulletShape newShape = new BulletShape(); + IntPtr hullPtr = IntPtr.Zero; + + if (BSParam.ShouldUseBulletHACD) + { + // Build the hull shape from an existing mesh shape. + // The mesh should have already been created in Bullet. + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID); + BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); + + if (meshShape.physShapeInfo.HasPhysicalShape) + { + HACDParams parms; + parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; + parms.minClusters = BSParam.BHullMinClusters; + parms.compacityWeight = BSParam.BHullCompacityWeight; + parms.volumeWeight = BSParam.BHullVolumeWeight; + parms.concavity = BSParam.BHullConcavity; + parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); + parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); + parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); + parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); + + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); + newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); + + // Now done with the mesh shape. + meshShape.Dereference(physicsScene); + } + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); + } + if (!newShape.HasPhysicalShape) + { + // Build a new hull in the physical world using the C# HACD algorigthm. + // Pass true for physicalness as this prevents the creation of bounding box which is not needed + IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); + if (meshData != null) + { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + // Release the fetched asset data once it has been used. + pbs.SculptData = new byte[0]; + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; + } + + int[] indices = meshData.getIndexListAsInt(); + List vertices = meshData.getVertexList(); + + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) + { + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); + } + + uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; + if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) + { + // Simple primitive shapes we know are convex so they are better implemented with + // fewer hulls. + // Check for simple shape (prim without cuts) and reduce split parameter if so. + if (BSShapeCollection.PrimHasNoCuts(pbs)) + { + maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; + } + } + + // setup and do convex hull conversion + m_hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + dcomp.mDepth = maxDepthSplit; + dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; + dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; + dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; + dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", + BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); + + // Convert the vertices and indices for passing to unmanaged. + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = m_hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in m_hulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in m_hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + // create the hull data structure in Bullet + newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); + } + newShape.shapeKey = newHullKey; + } + return newShape; + } + // Callback from convex hull creater with a newly created hull. + // Just add it to our collection of hulls for this shape. + private void HullReturn(ConvexResult result) + { + m_hulls.Add(result); + return; + } + // Loop through all the known hulls and return the description based on the physical address. + public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeHull outHull) + { + bool ret = false; + BSShapeHull foundDesc = null; + lock (Hulls) + { + foreach (BSShapeHull sh in Hulls.Values) + { + if (sh.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sh; + ret = true; + break; + } + + } + } + outHull = foundDesc; + return ret; } } @@ -344,14 +735,169 @@ public class BSShapeHull : BSShape public class BSShapeCompound : BSShape { private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; - public BSShapeCompound() : base() + public BSShapeCompound(BulletShape pShape) : base(pShape) { } - public static BSShape GetReference(BSPhysObject prim) - { - return new BSShapeNull(); + public static BSShape GetReference(BSScene physicsScene) + { + // Base compound shapes are not shared so this returns a raw shape. + // A built compound shape can be reused in linksets. + return new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene)); + } + public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) + { + // Calling this reference means we want another handle to an existing compound shape + // (usually linksets) so return this copy. + IncrementReference(); + return this; + } + // Dereferencing a compound shape releases the hold on all the child shapes. + public override void Dereference(BSScene physicsScene) + { + lock (physShapeInfo) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this); + if (referenceCount <= 0) + { + if (!physicsScene.PE.IsCompound(physShapeInfo)) + { + // Failed the sanity check!! + physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", + LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", + BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); + return; + } + + int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", + BSScene.DetailLogZero, physShapeInfo, numChildren); + + // Loop through all the children dereferencing each. + for (int ii = numChildren - 1; ii >= 0; ii--) + { + BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); + DereferenceAnonCollisionShape(physicsScene, childShape); + } + physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); + } + } + } + private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene) + { + BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false); + return cShape; + } + // Sometimes we have a pointer to a collision shape but don't know what type it is. + // Figure out type and call the correct dereference routine. + // Called at taint-time. + private void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape) + { + BSShapeMesh meshDesc; + if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc)) + { + meshDesc.Dereference(physicsScene); + } + else + { + BSShapeHull hullDesc; + if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc)) + { + hullDesc.Dereference(physicsScene); + } + else + { + if (physicsScene.PE.IsCompound(pShape)) + { + BSShapeCompound recursiveCompound = new BSShapeCompound(pShape); + recursiveCompound.Dereference(physicsScene); + } + else + { + if (physicsScene.PE.IsNativeShape(pShape)) + { + BSShapeNative nativeShape = new BSShapeNative(pShape); + nativeShape.Dereference(physicsScene); + } + } + } + } + } +} + +// ============================================================================================================ +public class BSShapeConvexHull : BSShape +{ + private static string LogHeader = "[BULLETSIM SHAPE CONVEX HULL]"; + public static Dictionary ConvexHulls = new Dictionary(); + + public BSShapeConvexHull(BulletShape pShape) : base(pShape) + { + } + public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) + { + float lod; + System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + physicsScene.DetailLog("{0},BSShapeMesh,getReference,newKey={1},size={2},lod={3}", + prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); + + BSShapeConvexHull retConvexHull = null; + lock (ConvexHulls) + { + if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull)) + { + // The mesh has already been created. Return a new reference to same. + retConvexHull.IncrementReference(); + } + else + { + retConvexHull = new BSShapeConvexHull(new BulletShape()); + BulletShape convexShape = null; + + // Get a handle to a mesh to build the hull from + BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim); + if (baseMesh.physShapeInfo.isNativeShape) + { + // We get here if the mesh was not creatable. Could be waiting for an asset from the disk. + // In the short term, we return the native shape and a later ForceBodyShapeRebuild should + // get back to this code with a buildable mesh. + // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed? + convexShape = baseMesh.physShapeInfo; + } + else + { + convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo); + convexShape.shapeKey = newMeshKey; + ConvexHulls.Add(convexShape.shapeKey, retConvexHull); + } + + // Done with the base mesh + baseMesh.Dereference(physicsScene); + + retConvexHull.physShapeInfo = convexShape; + } + } + return retConvexHull; + } + public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) + { + // Calling this reference means we want another handle to an existing shape + // (usually linksets) so return this copy. + IncrementReference(); + return this; + } + // Dereferencing a compound shape releases the hold on all the child shapes. + public override void Dereference(BSScene physicsScene) + { + lock (ConvexHulls) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this); + // TODO: schedule aging and destruction of unused meshes. + } } - public override void Dereference(BSScene physicsScene) { } } // ============================================================================================================ @@ -361,8 +907,12 @@ public class BSShapeAvatar : BSShape public BSShapeAvatar() : base() { } - public static BSShape GetReference(BSPhysObject prim) - { + public static BSShape GetReference(BSPhysObject prim) + { + return new BSShapeNull(); + } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { return new BSShapeNull(); } public override void Dereference(BSScene physicsScene) { } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs index e4fecc3aaf..c7deb4ec9d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs @@ -68,7 +68,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys // This minCoords and maxCoords passed in give the size of the terrain (min and max Z // are the high and low points of the heightmap). - public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, + public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, Vector3 minCoords, Vector3 maxCoords) : base(physicsScene, regionBase, id) { @@ -92,7 +92,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys private void BuildHeightmapTerrain() { // Create the terrain shape from the mapInfo - m_mapInfo.terrainShape = PhysicsScene.PE.CreateTerrainShape( m_mapInfo.ID, + m_mapInfo.terrainShape = m_physicsScene.PE.CreateTerrainShape( m_mapInfo.ID, new Vector3(m_mapInfo.sizeX, m_mapInfo.sizeY, 0), m_mapInfo.minZ, m_mapInfo.maxZ, m_mapInfo.heightMap, 1f, BSParam.TerrainCollisionMargin); @@ -103,26 +103,26 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f); centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f); - m_mapInfo.terrainBody = PhysicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, + m_mapInfo.terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, m_mapInfo.ID, centerPos, Quaternion.Identity); // Set current terrain attributes - PhysicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction); - PhysicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction); - PhysicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution); - PhysicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT); + m_physicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction); + m_physicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction); + m_physicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution); + m_physicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT); // Return the new terrain to the world of physical objects - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_mapInfo.terrainBody); // redo its bounding box now that it is in the world - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_mapInfo.terrainBody); m_mapInfo.terrainBody.collisionType = CollisionType.Terrain; - m_mapInfo.terrainBody.ApplyCollisionMask(PhysicsScene); + m_mapInfo.terrainBody.ApplyCollisionMask(m_physicsScene); // Make it so the terrain will not move or be considered for movement. - PhysicsScene.PE.ForceActivationState(m_mapInfo.terrainBody, ActivationState.DISABLE_SIMULATION); + m_physicsScene.PE.ForceActivationState(m_mapInfo.terrainBody, ActivationState.DISABLE_SIMULATION); return; } @@ -134,9 +134,9 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys { if (m_mapInfo.terrainBody.HasPhysicalBody) { - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_mapInfo.terrainBody); // Frees both the body and the shape. - PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_mapInfo.terrainBody); } } m_mapInfo = null; @@ -155,7 +155,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys catch { // Sometimes they give us wonky values of X and Y. Give a warning and return something. - PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", + m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", LogHeader, m_mapInfo.terrainRegionBase, pos); ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; } @@ -165,7 +165,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys // The passed position is relative to the base of the region. public override float GetWaterLevelAtXYZ(Vector3 pos) { - return PhysicsScene.SimpleWaterLevel; + return m_physicsScene.SimpleWaterLevel; } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 5240ad8220..c4807c41b0 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -50,14 +50,14 @@ public abstract class BSTerrainPhys : IDisposable Mesh = 1 } - public BSScene PhysicsScene { get; private set; } + protected BSScene m_physicsScene { get; private set; } // Base of the region in world coordinates. Coordinates inside the region are relative to this. public Vector3 TerrainBase { get; private set; } public uint ID { get; private set; } public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id) { - PhysicsScene = physicsScene; + m_physicsScene = physicsScene; TerrainBase = regionBase; ID = id; } @@ -86,7 +86,7 @@ public sealed class BSTerrainManager : IDisposable public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); // The scene that I am part of - private BSScene PhysicsScene { get; set; } + private BSScene m_physicsScene { get; set; } // The ground plane created to keep thing from falling to infinity. private BulletBody m_groundPlane; @@ -113,7 +113,7 @@ public sealed class BSTerrainManager : IDisposable public BSTerrainManager(BSScene physicsScene) { - PhysicsScene = physicsScene; + m_physicsScene = physicsScene; m_terrains = new Dictionary(); // Assume one region of default size @@ -132,21 +132,21 @@ public sealed class BSTerrainManager : IDisposable // safe to call Bullet in real time. We hope no one is moving prims around yet. public void CreateInitialGroundPlaneAndTerrain() { - DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName); + DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName); // The ground plane is here to catch things that are trying to drop to negative infinity - BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); - m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, + BulletShape groundPlaneShape = m_physicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); + m_groundPlane = m_physicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity); - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_groundPlane); - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_groundPlane); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_groundPlane); + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_groundPlane); // Ground plane does not move - PhysicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION); + m_physicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION); // Everything collides with the ground plane. m_groundPlane.collisionType = CollisionType.Groundplane; - m_groundPlane.ApplyCollisionMask(PhysicsScene); + m_groundPlane.ApplyCollisionMask(m_physicsScene); - BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); + BSTerrainPhys initialTerrain = new BSTerrainHeightmap(m_physicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); lock (m_terrains) { // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. @@ -157,12 +157,12 @@ public sealed class BSTerrainManager : IDisposable // Release all the terrain structures we might have allocated public void ReleaseGroundPlaneAndTerrain() { - DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName); + DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName); if (m_groundPlane.HasPhysicalBody) { - if (PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_groundPlane)) + if (m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_groundPlane)) { - PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_groundPlane); + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_groundPlane); } m_groundPlane.Clear(); } @@ -188,7 +188,7 @@ public sealed class BSTerrainManager : IDisposable float[] localHeightMap = heightMap; // If there are multiple requests for changes to the same terrain between ticks, // only do that last one. - PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate() + m_physicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate() { if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) { @@ -219,7 +219,7 @@ public sealed class BSTerrainManager : IDisposable private void AddMegaRegionChildTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) { // Since we are called by another region's thread, the action must be rescheduled onto our processing thread. - PhysicsScene.PostTaintObject("TerrainManager.AddMegaRegionChild" + minCoords.ToString(), id, delegate() + m_physicsScene.PostTaintObject("TerrainManager.AddMegaRegionChild" + minCoords.ToString(), id, delegate() { UpdateTerrain(id, heightMap, minCoords, maxCoords); }); @@ -318,26 +318,26 @@ public sealed class BSTerrainManager : IDisposable // TODO: redo terrain implementation selection to allow other base types than heightMap. private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) { - PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", - LogHeader, PhysicsScene.RegionName, terrainRegionBase, + m_physicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", + LogHeader, m_physicsScene.RegionName, terrainRegionBase, (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation); BSTerrainPhys newTerrainPhys = null; switch ((int)BSParam.TerrainImplementation) { case (int)BSTerrainPhys.TerrainImplementation.Heightmap: - newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id, + newTerrainPhys = new BSTerrainHeightmap(m_physicsScene, terrainRegionBase, id, heightMap, minCoords, maxCoords); break; case (int)BSTerrainPhys.TerrainImplementation.Mesh: - newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id, + newTerrainPhys = new BSTerrainMesh(m_physicsScene, terrainRegionBase, id, heightMap, minCoords, maxCoords); break; default: - PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", - LogHeader, - (int)BSParam.TerrainImplementation, + m_physicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", + LogHeader, + (int)BSParam.TerrainImplementation, BSParam.TerrainImplementation, - PhysicsScene.RegionName, terrainRegionBase); + m_physicsScene.RegionName, terrainRegionBase); break; } return newTerrainPhys; @@ -429,8 +429,8 @@ public sealed class BSTerrainManager : IDisposable } else { - PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", - LogHeader, PhysicsScene.RegionName, tX, tY); + m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", + LogHeader, m_physicsScene.RegionName, tX, tY); DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}", BSScene.DetailLogZero, pos, terrainBaseXYZ); } @@ -451,8 +451,8 @@ public sealed class BSTerrainManager : IDisposable } else { - PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}", - LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret); + m_physicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}", + LogHeader, m_physicsScene.RegionName, pos, terrainBaseXYZ, ret); } return ret; } @@ -564,7 +564,7 @@ public sealed class BSTerrainManager : IDisposable private void DetailLog(string msg, params Object[] args) { - PhysicsScene.PhysicsLogging.Write(msg, args); + m_physicsScene.PhysicsLogging.Write(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index 2ce1513d54..e4ca098b2d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -51,7 +51,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys BulletShape m_terrainShape; BulletBody m_terrainBody; - public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) : base(physicsScene, regionBase, id) { } @@ -62,7 +62,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys } // Create terrain mesh from a heightmap. - public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, Vector3 minCoords, Vector3 maxCoords) : base(physicsScene, regionBase, id) { @@ -80,7 +80,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys if (BSParam.TerrainMeshMagnification == 1) { // If a magnification of one, use the old routine that is tried and true. - meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, + meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(m_physicsScene, initialMap, m_sizeX, m_sizeY, // input size Vector3.Zero, // base for mesh out indicesCount, out indices, out verticesCount, out vertices); @@ -88,7 +88,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys else { // Other magnifications use the newer routine - meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(PhysicsScene, + meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(m_physicsScene, initialMap, m_sizeX, m_sizeY, // input size BSParam.TerrainMeshMagnification, physicsScene.TerrainManager.DefaultRegionSize, @@ -98,21 +98,21 @@ public sealed class BSTerrainMesh : BSTerrainPhys if (!meshCreationSuccess) { // DISASTER!! - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID); - PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID); + m_physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); // Something is very messed up and a crash is in our future. return; } - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length); - m_terrainShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, indicesCount, indices, verticesCount, vertices); + m_terrainShape = m_physicsScene.PE.CreateMeshShape(m_physicsScene.World, indicesCount, indices, verticesCount, vertices); if (!m_terrainShape.HasPhysicalShape) { // DISASTER!! - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID); - PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID); + m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); // Something is very messed up and a crash is in our future. return; } @@ -120,52 +120,52 @@ public sealed class BSTerrainMesh : BSTerrainPhys Vector3 pos = regionBase; Quaternion rot = Quaternion.Identity; - m_terrainBody = PhysicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot); + m_terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot); if (!m_terrainBody.HasPhysicalBody) { // DISASTER!! - PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); + m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); // Something is very messed up and a crash is in our future. return; } physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin); // Set current terrain attributes - PhysicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction); - PhysicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction); - PhysicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution); - PhysicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold); - PhysicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT); + m_physicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction); + m_physicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction); + m_physicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution); + m_physicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold); + m_physicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT); // Static objects are not very massive. - PhysicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero); + m_physicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero); // Put the new terrain to the world of physical objects - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_terrainBody); // Redo its bounding box now that it is in the world - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_terrainBody); m_terrainBody.collisionType = CollisionType.Terrain; - m_terrainBody.ApplyCollisionMask(PhysicsScene); + m_terrainBody.ApplyCollisionMask(m_physicsScene); if (BSParam.UseSingleSidedMeshes) { - PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id); - PhysicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); + m_physicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id); + m_physicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); } // Make it so the terrain will not move or be considered for movement. - PhysicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION); + m_physicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION); } public override void Dispose() { if (m_terrainBody.HasPhysicalBody) { - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_terrainBody); // Frees both the body and the shape. - PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_terrainBody); m_terrainBody.Clear(); m_terrainShape.Clear(); } @@ -185,7 +185,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys catch { // Sometimes they give us wonky values of X and Y. Give a warning and return something. - PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", + m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", LogHeader, TerrainBase, pos); ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; } @@ -195,7 +195,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys // The passed position is relative to the base of the region. public override float GetWaterLevelAtXYZ(Vector3 pos) { - return PhysicsScene.SimpleWaterLevel; + return m_physicsScene.SimpleWaterLevel; } // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs index 8012d91e2f..d5060e3c6c 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs @@ -104,11 +104,11 @@ public class BulletShape { public BulletShape() { - type = BSPhysicsShapeType.SHAPE_UNKNOWN; + shapeType = BSPhysicsShapeType.SHAPE_UNKNOWN; shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE; isNativeShape = false; } - public BSPhysicsShapeType type; + public BSPhysicsShapeType shapeType; public System.UInt64 shapeKey; public bool isNativeShape; @@ -133,7 +133,7 @@ public class BulletShape buff.Append("= 1) + m_log.DebugFormat( + "[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}", + ScriptName, ItemID, PrimName, ObjectID, timeout, m_InSelfDelete, DateTime.Now.Ticks); IScriptWorkItem workItem; diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 0d9babbca8..5804aa87f2 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -483,7 +483,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine /// /// Basis on which to sort output. Can be null if no sort needs to take place private void HandleScriptsAction( - string[] cmdparams, Action action, Func keySelector) + string[] cmdparams, Action action, System.Func keySelector) { if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene)) return; @@ -1517,7 +1517,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine startInfo.MaxWorkerThreads = maxThreads; startInfo.MinWorkerThreads = minThreads; startInfo.ThreadPriority = threadPriority;; - startInfo.StackSize = stackSize; + startInfo.MaxStackSize = stackSize; startInfo.StartSuspended = true; m_ThreadPool = new SmartThreadPool(startInfo); diff --git a/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs b/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs index 8dd7677d7f..9d9dee182d 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs @@ -52,16 +52,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine return wr.Cancel(); } - public void Abort() + public bool Abort() { - wr.Abort(); + return wr.Cancel(true); } public bool Wait(int t) { // We use the integer version of WaitAll because the current version of SmartThreadPool has a bug with the // TimeSpan version. The number of milliseconds in TimeSpan is an int64 so when STP casts it down to an - // int (32-bit) we can end up with bad values. This occurs on Windows though curious not on Mono 2.10.8 + // int (32-bit) we can end up with bad values. This occurs on Windows though curiously not on Mono 2.10.8 // (or very likely other versions of Mono at least up until 3.0.3). return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false); } diff --git a/OpenSim/Server/Base/ServicesServerBase.cs b/OpenSim/Server/Base/ServicesServerBase.cs index 7c8e6b798f..b13c87d000 100644 --- a/OpenSim/Server/Base/ServicesServerBase.cs +++ b/OpenSim/Server/Base/ServicesServerBase.cs @@ -171,11 +171,6 @@ namespace OpenSim.Server.Base m_console = MainConsole.Instance; - // Configure the appenders for log4net - // - OpenSimAppender consoleAppender = null; - FileAppender fileAppender = null; - if (logConfig != null) { FileInfo cfg = new FileInfo(logConfig); diff --git a/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs b/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs index a1794c972a..b3b75affc2 100644 --- a/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs +++ b/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs @@ -217,13 +217,38 @@ namespace OpenSim.Tests.Common /// public static InventoryFolderBase CreateInventoryFolder( IInventoryService inventoryService, UUID userId, string path, bool useExistingFolders) + { + return CreateInventoryFolder(inventoryService, userId, UUID.Random(), path, useExistingFolders); + } + + /// + /// Create inventory folders starting from the user's root folder. + /// + /// + /// + /// + /// + /// The folders to create. Multiple folders can be specified on a path delimited by the PATH_DELIMITER + /// + /// + /// If true, then folders in the path which already the same name are + /// used. This applies to the terminal folder as well. + /// If false, then all folders in the path are created, even if there is already a folder at a particular + /// level with the same name. + /// + /// + /// The folder created. If the path contains multiple folders then the last one created is returned. + /// Will return null if the root folder could not be found. + /// + public static InventoryFolderBase CreateInventoryFolder( + IInventoryService inventoryService, UUID userId, UUID folderId, string path, bool useExistingFolders) { InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId); if (null == rootFolder) return null; - return CreateInventoryFolder(inventoryService, rootFolder, path, useExistingFolders); + return CreateInventoryFolder(inventoryService, folderId, rootFolder, path, useExistingFolders); } /// @@ -235,6 +260,7 @@ namespace OpenSim.Tests.Common /// TODO: May need to make it an option to create duplicate folders. /// /// + /// ID of the folder to create /// /// /// The folder to create. @@ -249,7 +275,7 @@ namespace OpenSim.Tests.Common /// The folder created. If the path contains multiple folders then the last one created is returned. /// public static InventoryFolderBase CreateInventoryFolder( - IInventoryService inventoryService, InventoryFolderBase parentFolder, string path, bool useExistingFolders) + IInventoryService inventoryService, UUID folderId, InventoryFolderBase parentFolder, string path, bool useExistingFolders) { string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); @@ -262,9 +288,16 @@ namespace OpenSim.Tests.Common { // Console.WriteLine("Creating folder {0} at {1}", components[0], parentFolder.Name); + UUID folderIdForCreate; + + if (components.Length > 1) + folderIdForCreate = UUID.Random(); + else + folderIdForCreate = folderId; + folder = new InventoryFolderBase( - UUID.Random(), components[0], parentFolder.Owner, (short)AssetType.Unknown, parentFolder.ID, 0); + folderIdForCreate, components[0], parentFolder.Owner, (short)AssetType.Unknown, parentFolder.ID, 0); inventoryService.AddFolder(folder); } @@ -274,7 +307,7 @@ namespace OpenSim.Tests.Common // } if (components.Length > 1) - return CreateInventoryFolder(inventoryService, folder, components[1], useExistingFolders); + return CreateInventoryFolder(inventoryService, folderId, folder, components[1], useExistingFolders); else return folder; } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d26e3f732c..41402a4be6 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -61,6 +61,7 @@ namespace OpenSim.Tests.Common.Mock // Test client specific events - for use by tests to implement some IClientAPI behaviour. public event Action OnReceivedMoveAgentIntoRegion; public event Action OnTestClientInformClientOfNeighbour; + public event Action OnReceivedInstantMessage; // disable warning: public events, part of the public API #pragma warning disable 67 @@ -484,6 +485,18 @@ namespace OpenSim.Tests.Common.Mock OnCompleteMovementToRegion(this, true); } + /// + /// Emulate sending an IM from the viewer to the simulator. + /// + /// + public void HandleImprovedInstantMessage(GridInstantMessage im) + { + ImprovedInstantMessage handlerInstantMessage = OnInstantMessage; + + if (handlerInstantMessage != null) + handlerInstantMessage(this, im); + } + public virtual void ActivateGesture(UUID assetId, UUID gestureId) { } @@ -538,7 +551,8 @@ namespace OpenSim.Tests.Common.Mock public void SendInstantMessage(GridInstantMessage im) { - + if (OnReceivedInstantMessage != null) + OnReceivedInstantMessage(im); } public void SendGenericMessage(string method, UUID invoice, List message) diff --git a/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs b/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs index ccbdf81115..30b1f38156 100644 --- a/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs +++ b/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs @@ -53,6 +53,9 @@ namespace OpenSim.Tests.Common.Mock public XInventoryFolder[] GetFolders(string[] fields, string[] vals) { +// Console.WriteLine( +// "Requesting folders, fields {0}, vals {1}", string.Join(",", fields), string.Join(",", vals)); + List origFolders = Get(fields, vals, m_allFolders.Values.ToList()); @@ -104,7 +107,30 @@ namespace OpenSim.Tests.Common.Mock } public bool MoveItem(string id, string newParent) { throw new NotImplementedException(); } - public bool MoveFolder(string id, string newParent) { throw new NotImplementedException(); } + + public bool MoveFolder(string id, string newParent) + { + // Don't use GetFolders() here - it takes a clone! + XInventoryFolder folder = m_allFolders[new UUID(id)]; + + if (folder == null) + return false; + + folder.parentFolderID = new UUID(newParent); + + XInventoryFolder[] newParentFolders + = GetFolders(new string[] { "folderID" }, new string[] { folder.parentFolderID.ToString() }); + +// Console.WriteLine( +// "Moved folder {0} {1}, to {2} {3}", +// folder.folderName, folder.folderID, newParentFolders[0].folderName, folder.parentFolderID); + + // TODO: Really need to implement folder version incrementing, though this should be common code anyway, + // not reimplemented in each db plugin. + + return true; + } + public XInventoryItem[] GetActiveGestures(UUID principalID) { throw new NotImplementedException(); } public int GetAssetPermissions(UUID principalID, UUID assetID) { throw new NotImplementedException(); } } diff --git a/ThirdParty/SmartThreadPool/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/AssemblyInfo.cs deleted file mode 100644 index e2465b032b..0000000000 --- a/ThirdParty/SmartThreadPool/AssemblyInfo.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// -[assembly: AssemblyTitle("")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -[assembly: CLSCompliant(true)] - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly: AssemblyVersion("0.7.6.*")] - -// -// In order to sign your assembly you must specify a key to use. Refer to the -// Microsoft .NET Framework documentation for more information on assembly signing. -// -// Use the attributes below to control which key is used for signing. -// -// Notes: -// (*) If no key is specified, the assembly is not signed. -// (*) KeyName refers to a key that has been installed in the Crypto Service -// Provider (CSP) on your machine. KeyFile refers to a file which contains -// a key. -// (*) If the KeyFile and the KeyName values are both specified, the -// following processing occurs: -// (1) If the KeyName can be found in the CSP, that key is used. -// (2) If the KeyName does not exist and the KeyFile does exist, the key -// in the KeyFile is installed into the CSP and used. -// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. -// When specifying the KeyFile, the location of the KeyFile should be -// relative to the project output directory which is -// %Project Directory%\obj\. For example, if your KeyFile is -// located in the project directory, you would specify the AssemblyKeyFile -// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] -// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework -// documentation for more information on this. -// -[assembly: AssemblyDelaySign(false)] -[assembly: AssemblyKeyFile("")] -[assembly: AssemblyKeyName("")] diff --git a/ThirdParty/SmartThreadPool/CallerThreadContext.cs b/ThirdParty/SmartThreadPool/CallerThreadContext.cs index 6ea53f65d0..e63add594e 100644 --- a/ThirdParty/SmartThreadPool/CallerThreadContext.cs +++ b/ThirdParty/SmartThreadPool/CallerThreadContext.cs @@ -1,3 +1,6 @@ + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + using System; using System.Diagnostics; using System.Threading; @@ -6,218 +9,130 @@ using System.Web; using System.Runtime.Remoting.Messaging; -namespace Amib.Threading -{ - #region CallerThreadContext class - - /// - /// This class stores the caller call context in order to restore - /// it when the work item is executed in the thread pool environment. - /// - internal class CallerThreadContext - { - #region Prepare reflection information - - // Cached type information. - private static MethodInfo getLogicalCallContextMethodInfo = - typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); - - private static MethodInfo setLogicalCallContextMethodInfo = - typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); - - private static string HttpContextSlotName = GetHttpContextSlotName(); - - private static string GetHttpContextSlotName() - { - FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); - - if( fi != null ) - return (string)fi.GetValue(null); - else // Use the default "HttpContext" slot name - return "HttpContext"; - } - - #endregion - - #region Private fields - - private HttpContext _httpContext = null; - private LogicalCallContext _callContext = null; - - #endregion - - /// - /// Constructor - /// - private CallerThreadContext() - { - } - - public bool CapturedCallContext - { - get - { - return (null != _callContext); - } - } - - public bool CapturedHttpContext - { - get - { - return (null != _httpContext); - } - } - - /// - /// Captures the current thread context - /// - /// - public static CallerThreadContext Capture( - bool captureCallContext, - bool captureHttpContext) - { - Debug.Assert(captureCallContext || captureHttpContext); - - CallerThreadContext callerThreadContext = new CallerThreadContext(); - - // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() - // Capture Call Context - if(captureCallContext && (getLogicalCallContextMethodInfo != null)) - { - callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); - if (callerThreadContext._callContext != null) - { - callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); - } - } - - // Capture httpContext - if (captureHttpContext && (null != HttpContext.Current)) - { - callerThreadContext._httpContext = HttpContext.Current; - } - - return callerThreadContext; - } - - /// - /// Applies the thread context stored earlier - /// - /// - public static void Apply(CallerThreadContext callerThreadContext) - { - if (null == callerThreadContext) - { - throw new ArgumentNullException("callerThreadContext"); - } - - // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() - // Restore call context - if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) - { - setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); - } - - // Restore HttpContext - if (callerThreadContext._httpContext != null) - { - CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); - } - } - } - - #endregion - -} - - -/* -// Ami Bar -// amibar@gmail.com - -using System; -using System.Threading; -using System.Globalization; -using System.Security.Principal; -using System.Reflection; -using System.Runtime.Remoting.Contexts; - namespace Amib.Threading.Internal { - #region CallerThreadContext class +#region CallerThreadContext class - /// - /// This class stores the caller thread context in order to restore - /// it when the work item is executed in the context of the thread - /// from the pool. - /// Note that we can't store the thread's CompressedStack, because - /// it throws a security exception - /// - public class CallerThreadContext - { - private CultureInfo _culture = null; - private CultureInfo _cultureUI = null; - private IPrincipal _principal; - private System.Runtime.Remoting.Contexts.Context _context; + /// + /// This class stores the caller call context in order to restore + /// it when the work item is executed in the thread pool environment. + /// + internal class CallerThreadContext + { +#region Prepare reflection information - private static FieldInfo _fieldInfo = GetFieldInfo(); + // Cached type information. + private static readonly MethodInfo getLogicalCallContextMethodInfo = + typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); - private static FieldInfo GetFieldInfo() - { - Type threadType = typeof(Thread); - return threadType.GetField( - "m_Context", - BindingFlags.Instance | BindingFlags.NonPublic); - } + private static readonly MethodInfo setLogicalCallContextMethodInfo = + typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); - /// - /// Constructor - /// - private CallerThreadContext() - { - } + private static string HttpContextSlotName = GetHttpContextSlotName(); - /// - /// Captures the current thread context - /// - /// - public static CallerThreadContext Capture() - { - CallerThreadContext callerThreadContext = new CallerThreadContext(); + private static string GetHttpContextSlotName() + { + FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); - Thread thread = Thread.CurrentThread; - callerThreadContext._culture = thread.CurrentCulture; - callerThreadContext._cultureUI = thread.CurrentUICulture; - callerThreadContext._principal = Thread.CurrentPrincipal; - callerThreadContext._context = Thread.CurrentContext; - return callerThreadContext; - } - - /// - /// Applies the thread context stored earlier - /// - /// - public static void Apply(CallerThreadContext callerThreadContext) - { - Thread thread = Thread.CurrentThread; - thread.CurrentCulture = callerThreadContext._culture; - thread.CurrentUICulture = callerThreadContext._cultureUI; - Thread.CurrentPrincipal = callerThreadContext._principal; - - // Uncomment the following block to enable the Thread.CurrentThread -/* - if (null != _fieldInfo) + if (fi != null) { - _fieldInfo.SetValue( - Thread.CurrentThread, - callerThreadContext._context); + return (string) fi.GetValue(null); } -* / - } - } + + return "HttpContext"; + } + + #endregion + +#region Private fields + + private HttpContext _httpContext; + private LogicalCallContext _callContext; + + #endregion + + /// + /// Constructor + /// + private CallerThreadContext() + { + } + + public bool CapturedCallContext + { + get + { + return (null != _callContext); + } + } + + public bool CapturedHttpContext + { + get + { + return (null != _httpContext); + } + } + + /// + /// Captures the current thread context + /// + /// + public static CallerThreadContext Capture( + bool captureCallContext, + bool captureHttpContext) + { + Debug.Assert(captureCallContext || captureHttpContext); + + CallerThreadContext callerThreadContext = new CallerThreadContext(); + + // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() + // Capture Call Context + if(captureCallContext && (getLogicalCallContextMethodInfo != null)) + { + callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); + if (callerThreadContext._callContext != null) + { + callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); + } + } + + // Capture httpContext + if (captureHttpContext && (null != HttpContext.Current)) + { + callerThreadContext._httpContext = HttpContext.Current; + } + + return callerThreadContext; + } + + /// + /// Applies the thread context stored earlier + /// + /// + public static void Apply(CallerThreadContext callerThreadContext) + { + if (null == callerThreadContext) + { + throw new ArgumentNullException("callerThreadContext"); + } + + // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() + // Restore call context + if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) + { + setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); + } + + // Restore HttpContext + if (callerThreadContext._httpContext != null) + { + HttpContext.Current = callerThreadContext._httpContext; + //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); + } + } + } #endregion } -*/ - +#endif diff --git a/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs new file mode 100644 index 0000000000..5752957878 --- /dev/null +++ b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs @@ -0,0 +1,14 @@ +namespace Amib.Threading.Internal +{ + internal class CanceledWorkItemsGroup + { + public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + public CanceledWorkItemsGroup() + { + IsCanceled = false; + } + + public bool IsCanceled { get; set; } + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandle.cs b/ThirdParty/SmartThreadPool/EventWaitHandle.cs new file mode 100644 index 0000000000..25be07a530 --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandle.cs @@ -0,0 +1,104 @@ +#if (_WINDOWS_CE) + +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandle class + /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation. + /// So I wrote this class to implement these two methods with some of their overloads. + /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny. + /// Note that this class doesn't even inherit from WaitHandle! + /// + public class STPEventWaitHandle + { + #region Public Constants + + public const int WaitTimeout = Timeout.Infinite; + + #endregion + + #region Private External Constants + + private const Int32 WAIT_FAILED = -1; + private const Int32 WAIT_TIMEOUT = 0x102; + private const UInt32 INFINITE = 0xFFFFFFFF; + + #endregion + + #region WaitAll and WaitAny + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + + private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles) + { + IntPtr[] nativeHandles = new IntPtr[waitHandles.Length]; + for (int i = 0; i < waitHandles.Length; i++) + { + nativeHandles[i] = waitHandles[i].Handle; + } + return nativeHandles; + } + + public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout); + + if (result == WAIT_TIMEOUT || result == WAIT_FAILED) + { + return false; + } + + return true; + } + + + public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout); + + if (result >= 0 && result < waitHandles.Length) + { + return result; + } + + return -1; + } + + public static int WaitAny(WaitHandle[] waitHandles) + { + return WaitAny(waitHandles, Timeout.Infinite, false); + } + + public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) + { + int millisecondsTimeout = (int)timeout.TotalMilliseconds; + + return WaitAny(waitHandles, millisecondsTimeout, false); + } + + #endregion + + #region External methods + + [DllImport("coredll.dll", SetLastError = true)] + public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds); + + #endregion + } +} +#endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs new file mode 100644 index 0000000000..3c9c849205 --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs @@ -0,0 +1,82 @@ +using System.Threading; + +#if (_WINDOWS_CE) +using System; +using System.Runtime.InteropServices; +#endif + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandleFactory class. + /// This is a static class that creates AutoResetEvent and ManualResetEvent objects. + /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property + /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API. + /// Consequently this class creates the needed XxxResetEvent and replaces the handle if + /// it's a WindowsCE OS. + /// + public static class EventWaitHandleFactory + { + /// + /// Create a new AutoResetEvent object + /// + /// Return a new AutoResetEvent object + public static AutoResetEvent CreateAutoResetEvent() + { + AutoResetEvent waitHandle = new AutoResetEvent(false); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, false, false); +#endif + + return waitHandle; + } + + /// + /// Create a new ManualResetEvent object + /// + /// Return a new ManualResetEvent object + public static ManualResetEvent CreateManualResetEvent(bool initialState) + { + ManualResetEvent waitHandle = new ManualResetEvent(initialState); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, true, initialState); +#endif + + return waitHandle; + } + +#if (_WINDOWS_CE) + + /// + /// Replace the event handle + /// + /// The WaitHandle object which its handle needs to be replaced. + /// Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false) + /// The initial state of the event + private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState) + { + // Store the old handle + IntPtr oldHandle = waitHandle.Handle; + + // Create a new event + IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null); + + // Replace the old event with the new event + waitHandle.Handle = newHandle; + + // Close the old event + CloseHandle (oldHandle); + } + + [DllImport("coredll.dll", SetLastError = true)] + public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); + + //Handle + [DllImport("coredll.dll", SetLastError = true)] + public static extern bool CloseHandle(IntPtr hObject); +#endif + + } +} diff --git a/ThirdParty/SmartThreadPool/Exceptions.cs b/ThirdParty/SmartThreadPool/Exceptions.cs index c454709051..6c6a88bab1 100644 --- a/ThirdParty/SmartThreadPool/Exceptions.cs +++ b/ThirdParty/SmartThreadPool/Exceptions.cs @@ -1,8 +1,7 @@ -// Ami Bar -// amibar@gmail.com - using System; +#if !(_WINDOWS_CE) using System.Runtime.Serialization; +#endif namespace Amib.Threading { @@ -11,22 +10,73 @@ namespace Amib.Threading /// /// Represents an exception in case IWorkItemResult.GetResult has been canceled /// - [Serializable] - public sealed class WorkItemCancelException : ApplicationException + public sealed partial class WorkItemCancelException : Exception { - public WorkItemCancelException() : base() + public WorkItemCancelException() { } - public WorkItemCancelException(string message) : base(message) + public WorkItemCancelException(string message) + : base(message) { } - public WorkItemCancelException(string message, Exception e) : base(message, e) + public WorkItemCancelException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemTimeoutException : Exception + { + public WorkItemTimeoutException() { } - public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc) + public WorkItemTimeoutException(string message) + : base(message) + { + } + + public WorkItemTimeoutException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemResultException : Exception + { + public WorkItemResultException() + { + } + + public WorkItemResultException(string message) + : base(message) + { + } + + public WorkItemResultException(string message, Exception e) + : base(message, e) + { + } + } + + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// Represents an exception in case IWorkItemResult.GetResult has been canceled + /// + [Serializable] + public sealed partial class WorkItemCancelException + { + public WorkItemCancelException(SerializationInfo si, StreamingContext sc) + : base(si, sc) { } } @@ -35,21 +85,10 @@ namespace Amib.Threading /// Represents an exception in case IWorkItemResult.GetResult has been timed out /// [Serializable] - public sealed class WorkItemTimeoutException : ApplicationException + public sealed partial class WorkItemTimeoutException { - public WorkItemTimeoutException() : base() - { - } - - public WorkItemTimeoutException(string message) : base(message) - { - } - - public WorkItemTimeoutException(string message, Exception e) : base(message, e) - { - } - - public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc) + public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) + : base(si, sc) { } } @@ -58,24 +97,15 @@ namespace Amib.Threading /// Represents an exception in case IWorkItemResult.GetResult has been timed out /// [Serializable] - public sealed class WorkItemResultException : ApplicationException + public sealed partial class WorkItemResultException { - public WorkItemResultException() : base() - { - } - - public WorkItemResultException(string message) : base(message) - { - } - - public WorkItemResultException(string message, Exception e) : base(message, e) - { - } - - public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc) + public WorkItemResultException(SerializationInfo si, StreamingContext sc) + : base(si, sc) { } } +#endif + #endregion } diff --git a/ThirdParty/SmartThreadPool/Interfaces.cs b/ThirdParty/SmartThreadPool/Interfaces.cs index f1c1fcf3ba..513422ff9f 100644 --- a/ThirdParty/SmartThreadPool/Interfaces.cs +++ b/ThirdParty/SmartThreadPool/Interfaces.cs @@ -1,271 +1,628 @@ -// Ami Bar -// amibar@gmail.com - using System; using System.Threading; namespace Amib.Threading { - #region Delegates + #region Delegates - /// - /// A delegate that represents the method to run as the work item - /// - /// A state object for the method to run - public delegate object WorkItemCallback(object state); + /// + /// A delegate that represents the method to run as the work item + /// + /// A state object for the method to run + public delegate object WorkItemCallback(object state); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); /// /// A delegate to call after the WorkItemCallback completed /// /// The work item result object - public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call when a WorkItemsGroup becomes idle + /// + /// A reference to the WorkItemsGroup that became idle + public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); /// - /// A delegate to call when a WorkItemsGroup becomes idle + /// A delegate to call after a thread is created, but before + /// it's first use. /// - /// A reference to the WorkItemsGroup that became idle - public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); - - #endregion - - #region WorkItem Priority - - public enum WorkItemPriority - { - Lowest, - BelowNormal, - Normal, - AboveNormal, - Highest, - } - - #endregion - - #region IHasWorkItemPriority interface - - public interface IHasWorkItemPriority - { - WorkItemPriority WorkItemPriority { get; } - } - - #endregion - - #region IWorkItemsGroup interface + public delegate void ThreadInitializationHandler(); /// - /// IWorkItemsGroup interface + /// A delegate to call when a thread is about to exit, after + /// it is no longer belong to the pool. /// - public interface IWorkItemsGroup - { + public delegate void ThreadTerminationHandler(); + + #endregion + + #region WorkItem Priority + + /// + /// Defines the availeable priorities of a work item. + /// The higher the priority a work item has, the sooner + /// it will be executed. + /// + public enum WorkItemPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } + + #endregion + + #region IWorkItemsGroup interface + + /// + /// IWorkItemsGroup interface + /// Created by SmartThreadPool.CreateWorkItemsGroup() + /// + public interface IWorkItemsGroup + { + /// + /// Get/Set the name of the WorkItemsGroup + /// + string Name { get; set; } + /// - /// Get/Set the name of the WorkItemsGroup + /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool /// - string Name { get; set; } + int Concurrency { get; set; } - IWorkItemResult QueueWorkItem(WorkItemCallback callback); - IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); + /// + /// Get the number of work items waiting in the queue. + /// + int WaitingCallbacks { get; } - IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); - IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + object[] GetStates(); - void WaitForIdle(); - bool WaitForIdle(TimeSpan timeout); + /// + /// Get the WorkItemsGroup start information + /// + WIGStartInfo WIGStartInfo { get; } + + /// + /// Starts to execute work items + /// + void Start(); + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + void Cancel(); + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + void Cancel(bool abortExecution); + + /// + /// Wait for all work item to complete. + /// + void WaitForIdle(); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(TimeSpan timeout); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete in milliseconds + /// Returns true if work items completed within the timeout, otherwise false. bool WaitForIdle(int millisecondsTimeout); - int WaitingCallbacks { get; } + /// + /// IsIdle is true when there are no work items running or queued. + /// + bool IsIdle { get; } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// event WorkItemsGroupIdleHandler OnIdle; - void Cancel(); - void Start(); + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); + + #endregion + + #region QueueWorkItem(Action<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority); + + #endregion + + #region QueueWorkItem(Func<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + #endregion } - #endregion + #endregion - #region CallToPostExecute enumerator + #region CallToPostExecute enumerator - [Flags] - public enum CallToPostExecute - { - Never = 0x00, - WhenWorkItemCanceled = 0x01, - WhenWorkItemNotCanceled = 0x02, - Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, - } + [Flags] + public enum CallToPostExecute + { + /// + /// Never call to the PostExecute call back + /// + Never = 0x00, - #endregion + /// + /// Call to the PostExecute only when the work item is cancelled + /// + WhenWorkItemCanceled = 0x01, - #region IWorkItemResult interface + /// + /// Call to the PostExecute only when the work item is not cancelled + /// + WhenWorkItemNotCanceled = 0x02, + + /// + /// Always call to the PostExecute + /// + Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, + } + + #endregion + + #region IWorkItemResult interface /// - /// IWorkItemResult interface + /// The common interface of IWorkItemResult and IWorkItemResult<T> /// - public interface IWorkItemResult + public interface IWaitableResult { /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. + /// This method intent is for internal use. /// - /// The result of the work item - object GetResult(); + /// + IWorkItemResult GetWorkItemResult(); /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. + /// This method intent is for internal use. /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - int millisecondsTimeout, - bool exitContext); + /// + IWorkItemResult GetWorkItemResultT(); + } - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - TimeSpan timeout, - bool exitContext); + /// + /// IWorkItemResult interface. + /// Created when a WorkItemCallback work item is queued. + /// + public interface IWorkItemResult : IWorkItemResult + { + } - void Abort(); + /// + /// IWorkItemResult<TResult> interface. + /// Created when a Func<TResult> work item is queued. + /// + public interface IWorkItemResult : IWaitableResult + { + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// The result of the work item + TResult GetResult(); - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle); + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext); - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle); + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext); - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - object GetResult(out Exception e); + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle); - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - int millisecondsTimeout, - bool exitContext, - out Exception e); + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle); - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - TimeSpan timeout, - bool exitContext, - out Exception e); + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// Filled with the exception if one was thrown + /// The result of the work item + TResult GetResult(out Exception e); - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + out Exception e); - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// Filled with the exception if one was thrown - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// Filled with the exception if one was thrown + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + out Exception e); - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - bool IsCompleted { get; } + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); - /// - /// Gets an indication whether the asynchronous operation has been canceled. - /// - bool IsCanceled { get; } + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// + /// Filled with the exception if one was thrown + /// + /// + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); - /// - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - /// - object State { get; } + /// + /// Gets an indication whether the asynchronous operation has completed. + /// + bool IsCompleted { get; } - /// - /// Cancel the work item if it didn't start running yet. - /// - /// Returns true on success or false if the work item is in progress or already completed + /// + /// Gets an indication whether the asynchronous operation has been canceled. + /// + bool IsCanceled { get; } + + /// + /// Gets the user-defined object that contains context data + /// for the work item method. + /// + object State { get; } + + /// + /// Same as Cancel(false). + /// bool Cancel(); /// - /// Get the work item's priority + /// Cancel the work item execution. + /// If the work item is in the queue then it won't execute + /// If the work item is completed, it will remain completed + /// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled + /// property to check if the work item has been cancelled. If the abortExecution is set to true then + /// the Smart Thread Pool will send an AbortException to the running thread to stop the execution + /// of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException. + /// If the work item is already cancelled it will remain cancelled /// - WorkItemPriority WorkItemPriority { get; } + /// When true send an AbortException to the executing thread. + /// Returns true if the work item was not completed, otherwise false. + bool Cancel(bool abortExecution); - /// - /// Return the result, same as GetResult() - /// - object Result { get; } + /// + /// Get the work item's priority + /// + WorkItemPriority WorkItemPriority { get; } - /// - /// Returns the exception if occured otherwise returns null. - /// - object Exception { get; } - } + /// + /// Return the result, same as GetResult() + /// + TResult Result { get; } + + /// + /// Returns the exception if occured otherwise returns null. + /// + object Exception { get; } + } + + #endregion + + #region .NET 3.5 + + // All these delegate are built-in .NET 3.5 + // Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity. + + public delegate void Action(); + public delegate void Action(T1 arg1, T2 arg2); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + public delegate TResult Func(); + public delegate TResult Func(T arg1); + public delegate TResult Func(T1 arg1, T2 arg2); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); #endregion } diff --git a/ThirdParty/SmartThreadPool/InternalInterfaces.cs b/ThirdParty/SmartThreadPool/InternalInterfaces.cs new file mode 100644 index 0000000000..0072e10019 --- /dev/null +++ b/ThirdParty/SmartThreadPool/InternalInterfaces.cs @@ -0,0 +1,27 @@ + +namespace Amib.Threading.Internal +{ + /// + /// An internal delegate to call when the WorkItem starts or completes + /// + internal delegate void WorkItemStateCallback(WorkItem workItem); + + internal interface IInternalWorkItemResult + { + event WorkItemStateCallback OnWorkItemStarted; + event WorkItemStateCallback OnWorkItemCompleted; + } + + internal interface IInternalWaitableResult + { + /// + /// This method is intent for internal use. + /// + IWorkItemResult GetWorkItemResult(); + } + + public interface IHasWorkItemPriority + { + WorkItemPriority WorkItemPriority { get; } + } +} diff --git a/ThirdParty/SmartThreadPool/PriorityQueue.cs b/ThirdParty/SmartThreadPool/PriorityQueue.cs index 63d5e84b37..409c879a1f 100644 --- a/ThirdParty/SmartThreadPool/PriorityQueue.cs +++ b/ThirdParty/SmartThreadPool/PriorityQueue.cs @@ -1,240 +1,239 @@ -// Ami Bar -// amibar@gmail.com - using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; namespace Amib.Threading.Internal { - #region PriorityQueue class + #region PriorityQueue class - /// - /// PriorityQueue class - /// This class is not thread safe because we use external lock - /// - public sealed class PriorityQueue : IEnumerable - { - #region Private members + /// + /// PriorityQueue class + /// This class is not thread safe because we use external lock + /// + public sealed class PriorityQueue : IEnumerable + { + #region Private members - /// - /// The number of queues, there is one for each type of priority - /// - private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; + /// + /// The number of queues, there is one for each type of priority + /// + private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; - /// - /// Work items queues. There is one for each type of priority - /// - private Queue [] _queues = new Queue[_queuesCount]; + /// + /// Work items queues. There is one for each type of priority + /// + private readonly LinkedList[] _queues = new LinkedList[_queuesCount]; - /// - /// The total number of work items within the queues - /// - private int _workItemsCount = 0; + /// + /// The total number of work items within the queues + /// + private int _workItemsCount; - /// - /// Use with IEnumerable interface - /// - private int _version = 0; + /// + /// Use with IEnumerable interface + /// + private int _version; - #endregion + #endregion - #region Contructor + #region Contructor - public PriorityQueue() - { - for(int i = 0; i < _queues.Length; ++i) - { - _queues[i] = new Queue(); - } - } + public PriorityQueue() + { + for(int i = 0; i < _queues.Length; ++i) + { + _queues[i] = new LinkedList(); + } + } - #endregion + #endregion - #region Methods + #region Methods - /// - /// Enqueue a work item. - /// - /// A work item - public void Enqueue(IHasWorkItemPriority workItem) - { - Debug.Assert(null != workItem); + /// + /// Enqueue a work item. + /// + /// A work item + public void Enqueue(IHasWorkItemPriority workItem) + { + Debug.Assert(null != workItem); - int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; - Debug.Assert(queueIndex >= 0); - Debug.Assert(queueIndex < _queuesCount); + int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; + Debug.Assert(queueIndex >= 0); + Debug.Assert(queueIndex < _queuesCount); - _queues[queueIndex].Enqueue(workItem); - ++_workItemsCount; - ++_version; - } + _queues[queueIndex].AddLast(workItem); + ++_workItemsCount; + ++_version; + } - /// - /// Dequeque a work item. - /// - /// Returns the next work item - public IHasWorkItemPriority Dequeue() - { - IHasWorkItemPriority workItem = null; + /// + /// Dequeque a work item. + /// + /// Returns the next work item + public IHasWorkItemPriority Dequeue() + { + IHasWorkItemPriority workItem = null; - if(_workItemsCount > 0) - { - int queueIndex = GetNextNonEmptyQueue(-1); - Debug.Assert(queueIndex >= 0); - workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority; - Debug.Assert(null != workItem); - --_workItemsCount; - ++_version; - } + if(_workItemsCount > 0) + { + int queueIndex = GetNextNonEmptyQueue(-1); + Debug.Assert(queueIndex >= 0); + workItem = _queues[queueIndex].First.Value; + _queues[queueIndex].RemoveFirst(); + Debug.Assert(null != workItem); + --_workItemsCount; + ++_version; + } - return workItem; - } + return workItem; + } - /// - /// Find the next non empty queue starting at queue queueIndex+1 - /// - /// The index-1 to start from - /// - /// The index of the next non empty queue or -1 if all the queues are empty - /// - private int GetNextNonEmptyQueue(int queueIndex) - { - for(int i = queueIndex+1; i < _queuesCount; ++i) - { - if(_queues[i].Count > 0) - { - return i; - } - } - return -1; - } + /// + /// Find the next non empty queue starting at queue queueIndex+1 + /// + /// The index-1 to start from + /// + /// The index of the next non empty queue or -1 if all the queues are empty + /// + private int GetNextNonEmptyQueue(int queueIndex) + { + for(int i = queueIndex+1; i < _queuesCount; ++i) + { + if(_queues[i].Count > 0) + { + return i; + } + } + return -1; + } - /// - /// The number of work items - /// - public int Count - { - get - { - return _workItemsCount; - } - } + /// + /// The number of work items + /// + public int Count + { + get + { + return _workItemsCount; + } + } - /// - /// Clear all the work items - /// - public void Clear() - { - if (_workItemsCount > 0) - { - foreach(Queue queue in _queues) - { - queue.Clear(); - } - _workItemsCount = 0; - ++_version; - } - } + /// + /// Clear all the work items + /// + public void Clear() + { + if (_workItemsCount > 0) + { + foreach(LinkedList queue in _queues) + { + queue.Clear(); + } + _workItemsCount = 0; + ++_version; + } + } - #endregion + #endregion - #region IEnumerable Members + #region IEnumerable Members - /// - /// Returns an enumerator to iterate over the work items - /// - /// Returns an enumerator - public IEnumerator GetEnumerator() - { - return new PriorityQueueEnumerator(this); - } + /// + /// Returns an enumerator to iterate over the work items + /// + /// Returns an enumerator + public IEnumerator GetEnumerator() + { + return new PriorityQueueEnumerator(this); + } - #endregion + #endregion - #region PriorityQueueEnumerator + #region PriorityQueueEnumerator - /// - /// The class the implements the enumerator - /// - private class PriorityQueueEnumerator : IEnumerator - { - private PriorityQueue _priorityQueue; - private int _version; - private int _queueIndex; - private IEnumerator _enumerator; + /// + /// The class the implements the enumerator + /// + private class PriorityQueueEnumerator : IEnumerator + { + private readonly PriorityQueue _priorityQueue; + private int _version; + private int _queueIndex; + private IEnumerator _enumerator; - public PriorityQueueEnumerator(PriorityQueue priorityQueue) - { - _priorityQueue = priorityQueue; - _version = _priorityQueue._version; - _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); - if (_queueIndex >= 0) - { - _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); - } - else - { - _enumerator = null; - } - } + public PriorityQueueEnumerator(PriorityQueue priorityQueue) + { + _priorityQueue = priorityQueue; + _version = _priorityQueue._version; + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); + if (_queueIndex >= 0) + { + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + } + else + { + _enumerator = null; + } + } - #region IEnumerator Members + #region IEnumerator Members - public void Reset() - { - _version = _priorityQueue._version; - _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); - if (_queueIndex >= 0) - { - _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); - } - else - { - _enumerator = null; - } - } + public void Reset() + { + _version = _priorityQueue._version; + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); + if (_queueIndex >= 0) + { + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + } + else + { + _enumerator = null; + } + } - public object Current - { - get - { - Debug.Assert(null != _enumerator); - return _enumerator.Current; - } - } + public object Current + { + get + { + Debug.Assert(null != _enumerator); + return _enumerator.Current; + } + } - public bool MoveNext() - { - if (null == _enumerator) - { - return false; - } + public bool MoveNext() + { + if (null == _enumerator) + { + return false; + } - if(_version != _priorityQueue._version) - { - throw new InvalidOperationException("The collection has been modified"); + if(_version != _priorityQueue._version) + { + throw new InvalidOperationException("The collection has been modified"); - } - if (!_enumerator.MoveNext()) - { - _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex); - if(-1 == _queueIndex) - { - return false; - } - _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); - _enumerator.MoveNext(); - return true; - } - return true; - } + } + if (!_enumerator.MoveNext()) + { + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex); + if(-1 == _queueIndex) + { + return false; + } + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + _enumerator.MoveNext(); + return true; + } + return true; + } - #endregion - } + #endregion + } - #endregion - } + #endregion + } - #endregion + #endregion } diff --git a/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4728c1fc0b --- /dev/null +++ b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Amib.Threading")] +[assembly: AssemblyDescription("Smart Thread Pool")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Amib.Threading")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")] +[assembly: AssemblyVersion("2.2.3.0")] + +#if (_PUBLISH) +[assembly: InternalsVisibleTo("STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad")] +#else +[assembly: InternalsVisibleTo("STPTests")] +#endif + + diff --git a/ThirdParty/SmartThreadPool/SLExt.cs b/ThirdParty/SmartThreadPool/SLExt.cs new file mode 100644 index 0000000000..23a60bcb90 --- /dev/null +++ b/ThirdParty/SmartThreadPool/SLExt.cs @@ -0,0 +1,16 @@ +#if _SILVERLIGHT + +using System.Threading; + +namespace Amib.Threading +{ + public enum ThreadPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } +} +#endif diff --git a/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs new file mode 100644 index 0000000000..9b17f694a5 --- /dev/null +++ b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs @@ -0,0 +1,62 @@ +#if !(_WINDOWS_CE) + +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ +#if _WINDOWS || WINDOWS_PHONE + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout); + } + } +#else + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + } +#endif + +} + +#endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs index 077cf17ed9..0663d1dfb8 100644 --- a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs +++ b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs @@ -1,227 +1,20 @@ using System; using System.Diagnostics; +using System.Threading; + +namespace Amib.Threading +{ + public interface ISTPPerformanceCountersReader + { + long InUseThreads { get; } + long ActiveThreads { get; } + long WorkItemsQueued { get; } + long WorkItemsProcessed { get; } + } +} namespace Amib.Threading.Internal { - internal enum STPPerformanceCounterType - { - // Fields - ActiveThreads = 0, - InUseThreads = 1, - OverheadThreads = 2, - OverheadThreadsPercent = 3, - OverheadThreadsPercentBase = 4, - - WorkItems = 5, - WorkItemsInQueue = 6, - WorkItemsProcessed = 7, - - WorkItemsQueuedPerSecond = 8, - WorkItemsProcessedPerSecond = 9, - - AvgWorkItemWaitTime = 10, - AvgWorkItemWaitTimeBase = 11, - - AvgWorkItemProcessTime = 12, - AvgWorkItemProcessTimeBase = 13, - - WorkItemsGroups = 14, - - LastCounter = 14, - } - - - /// - /// Summary description for STPPerformanceCounter. - /// - internal class STPPerformanceCounter - { - // Fields - private PerformanceCounterType _pcType; - protected string _counterHelp; - protected string _counterName; - - // Methods - public STPPerformanceCounter( - string counterName, - string counterHelp, - PerformanceCounterType pcType) - { - this._counterName = counterName; - this._counterHelp = counterHelp; - this._pcType = pcType; - } - - public void AddCounterToCollection(CounterCreationDataCollection counterData) - { - CounterCreationData counterCreationData = new CounterCreationData( - _counterName, - _counterHelp, - _pcType); - - counterData.Add(counterCreationData); - } - - // Properties - public string Name - { - get - { - return _counterName; - } - } - } - - internal class STPPerformanceCounters - { - // Fields - internal STPPerformanceCounter[] _stpPerformanceCounters; - private static STPPerformanceCounters _instance; - internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; - internal const string _stpCategoryName = "SmartThreadPool"; - - // Methods - static STPPerformanceCounters() - { - _instance = new STPPerformanceCounters(); - } - - private STPPerformanceCounters() - { - STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] - { - new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), - new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), - - new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), - - new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), - new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), - - new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), - new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), - - new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), - new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), - - new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), - }; - - _stpPerformanceCounters = stpPerformanceCounters; - SetupCategory(); - } - - private void SetupCategory() - { - if (!PerformanceCounterCategory.Exists(_stpCategoryName)) - { - CounterCreationDataCollection counters = new CounterCreationDataCollection(); - - for (int i = 0; i < _stpPerformanceCounters.Length; i++) - { - _stpPerformanceCounters[i].AddCounterToCollection(counters); - } - - - // *********** Remark for .NET 2.0 *********** - // If you are here, it means you got the warning that this overload - // of the method is deprecated in .NET 2.0. To use the correct - // method overload, uncomment the third argument of - // the method. - #pragma warning disable 0618 - PerformanceCounterCategory.Create( - _stpCategoryName, - _stpCategoryHelp, - //PerformanceCounterCategoryType.MultiInstance, - counters); - #pragma warning restore 0618 - } - } - - // Properties - public static STPPerformanceCounters Instance - { - get - { - return _instance; - } - } - } - - internal class STPInstancePerformanceCounter : IDisposable - { - // Fields - private PerformanceCounter _pcs; - - // Methods - protected STPInstancePerformanceCounter() - { - } - - public STPInstancePerformanceCounter( - string instance, - STPPerformanceCounterType spcType) - { - STPPerformanceCounters counters = STPPerformanceCounters.Instance; - _pcs = new PerformanceCounter( - STPPerformanceCounters._stpCategoryName, - counters._stpPerformanceCounters[(int) spcType].Name, - instance, - false); - _pcs.RawValue = _pcs.RawValue; - } - - ~STPInstancePerformanceCounter() - { - Close(); - } - - public void Close() - { - if (_pcs != null) - { - _pcs.RemoveInstance(); - _pcs.Close(); - _pcs = null; - } - } - - public void Dispose() - { - Close(); - GC.SuppressFinalize(this); - } - - public virtual void Increment() - { - _pcs.Increment(); - } - - public virtual void IncrementBy(long val) - { - _pcs.IncrementBy(val); - } - - public virtual void Set(long val) - { - _pcs.RawValue = val; - } - } - - internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter - { - // Methods - public STPInstanceNullPerformanceCounter() {} - public override void Increment() {} - public override void IncrementBy(long value) {} - public override void Set(long val) {} - } - internal interface ISTPInstancePerformanceCounters : IDisposable { void Close(); @@ -230,125 +23,426 @@ namespace Amib.Threading.Internal void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime); void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime); } +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + internal enum STPPerformanceCounterType + { + // Fields + ActiveThreads = 0, + InUseThreads = 1, + OverheadThreads = 2, + OverheadThreadsPercent = 3, + OverheadThreadsPercentBase = 4, - internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable - { - // Fields - private STPInstancePerformanceCounter[] _pcs; - private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter; + WorkItems = 5, + WorkItemsInQueue = 6, + WorkItemsProcessed = 7, - // Methods - static STPInstancePerformanceCounters() - { - _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter(); - } + WorkItemsQueuedPerSecond = 8, + WorkItemsProcessedPerSecond = 9, + + AvgWorkItemWaitTime = 10, + AvgWorkItemWaitTimeBase = 11, + + AvgWorkItemProcessTime = 12, + AvgWorkItemProcessTimeBase = 13, + + WorkItemsGroups = 14, + + LastCounter = 14, + } - public STPInstancePerformanceCounters(string instance) + + /// + /// Summary description for STPPerformanceCounter. + /// + internal class STPPerformanceCounter + { + // Fields + private readonly PerformanceCounterType _pcType; + protected string _counterHelp; + protected string _counterName; + + // Methods + public STPPerformanceCounter( + string counterName, + string counterHelp, + PerformanceCounterType pcType) + { + _counterName = counterName; + _counterHelp = counterHelp; + _pcType = pcType; + } + + public void AddCounterToCollection(CounterCreationDataCollection counterData) + { + CounterCreationData counterCreationData = new CounterCreationData( + _counterName, + _counterHelp, + _pcType); + + counterData.Add(counterCreationData); + } + + // Properties + public string Name + { + get + { + return _counterName; + } + } + } + + internal class STPPerformanceCounters + { + // Fields + internal STPPerformanceCounter[] _stpPerformanceCounters; + private static readonly STPPerformanceCounters _instance; + internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; + internal const string _stpCategoryName = "SmartThreadPool"; + + // Methods + static STPPerformanceCounters() + { + _instance = new STPPerformanceCounters(); + } + + private STPPerformanceCounters() + { + STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] + { + new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), + new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), + + new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), + + new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), + new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), + + new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), + }; + + _stpPerformanceCounters = stpPerformanceCounters; + SetupCategory(); + } + + private void SetupCategory() + { + if (!PerformanceCounterCategory.Exists(_stpCategoryName)) + { + CounterCreationDataCollection counters = new CounterCreationDataCollection(); + + for (int i = 0; i < _stpPerformanceCounters.Length; i++) + { + _stpPerformanceCounters[i].AddCounterToCollection(counters); + } + + PerformanceCounterCategory.Create( + _stpCategoryName, + _stpCategoryHelp, + PerformanceCounterCategoryType.MultiInstance, + counters); + + } + } + + // Properties + public static STPPerformanceCounters Instance + { + get + { + return _instance; + } + } + } + + internal class STPInstancePerformanceCounter : IDisposable + { + // Fields + private bool _isDisposed; + private PerformanceCounter _pcs; + + // Methods + protected STPInstancePerformanceCounter() + { + _isDisposed = false; + } + + public STPInstancePerformanceCounter( + string instance, + STPPerformanceCounterType spcType) : this() + { + STPPerformanceCounters counters = STPPerformanceCounters.Instance; + _pcs = new PerformanceCounter( + STPPerformanceCounters._stpCategoryName, + counters._stpPerformanceCounters[(int) spcType].Name, + instance, + false); + _pcs.RawValue = _pcs.RawValue; + } + + + public void Close() + { + if (_pcs != null) + { + _pcs.RemoveInstance(); + _pcs.Close(); + _pcs = null; + } + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool disposing) { - _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter]; - // STPPerformanceCounters counters = STPPerformanceCounters.Instance; - for (int i = 0; i < _pcs.Length; i++) + if (!_isDisposed) { - if (instance != null) + if (disposing) { - _pcs[i] = new STPInstancePerformanceCounter( - instance, - (STPPerformanceCounterType) i); - } - else - { - _pcs[i] = _stpInstanceNullPerformanceCounter; + Close(); } } + _isDisposed = true; } + public virtual void Increment() + { + _pcs.Increment(); + } + + public virtual void IncrementBy(long val) + { + _pcs.IncrementBy(val); + } - public void Close() - { - if (null != _pcs) - { - for (int i = 0; i < _pcs.Length; i++) - { + public virtual void Set(long val) + { + _pcs.RawValue = val; + } + } + + internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter + { + // Methods + public override void Increment() {} + public override void IncrementBy(long value) {} + public override void Set(long val) {} + } + + + + internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters + { + private bool _isDisposed; + // Fields + private STPInstancePerformanceCounter[] _pcs; + private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter; + + // Methods + static STPInstancePerformanceCounters() + { + _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter(); + } + + public STPInstancePerformanceCounters(string instance) + { + _isDisposed = false; + _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter]; + + // Call the STPPerformanceCounters.Instance so the static constructor will + // intialize the STPPerformanceCounters singleton. + STPPerformanceCounters.Instance.GetHashCode(); + + for (int i = 0; i < _pcs.Length; i++) + { + if (instance != null) + { + _pcs[i] = new STPInstancePerformanceCounter( + instance, + (STPPerformanceCounterType) i); + } + else + { + _pcs[i] = _stpInstanceNullPerformanceCounter; + } + } + } + + + public void Close() + { + if (null != _pcs) + { + for (int i = 0; i < _pcs.Length; i++) + { if (null != _pcs[i]) { - _pcs[i].Close(); + _pcs[i].Dispose(); } + } + _pcs = null; + } + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + Close(); } - _pcs = null; } - } - - ~STPInstancePerformanceCounters() - { - Close(); - } - - public void Dispose() - { - Close(); - GC.SuppressFinalize(this); + _isDisposed = true; } - private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType) + private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType) + { + return _pcs[(int) spcType]; + } + + public void SampleThreads(long activeThreads, long inUseThreads) + { + GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads); + GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads); + GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads); + + GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads); + GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads); + } + + public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) + { + GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed); + GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued); + GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed); + + GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued); + GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed); + } + + public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) + { + GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds); + GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment(); + } + + public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) + { + GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds); + GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment(); + } + } +#endif + + internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader + { + private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(); + + public static NullSTPInstancePerformanceCounters Instance + { + get { return _instance; } + } + + public void Close() {} + public void Dispose() {} + + public void SampleThreads(long activeThreads, long inUseThreads) {} + public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {} + public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {} + public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {} + public long InUseThreads { - return _pcs[(int) spcType]; + get { return 0; } + } + + public long ActiveThreads + { + get { return 0; } + } + + public long WorkItemsQueued + { + get { return 0; } + } + + public long WorkItemsProcessed + { + get { return 0; } + } + } + + internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader + { + public void Close() { } + public void Dispose() { } + + private long _activeThreads; + private long _inUseThreads; + private long _workItemsQueued; + private long _workItemsProcessed; + + public long InUseThreads + { + get { return _inUseThreads; } + } + + public long ActiveThreads + { + get { return _activeThreads; } + } + + public long WorkItemsQueued + { + get { return _workItemsQueued; } + } + + public long WorkItemsProcessed + { + get { return _workItemsProcessed; } } public void SampleThreads(long activeThreads, long inUseThreads) { - GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads); - GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads); - GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads); - - GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads); - GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads); + _activeThreads = activeThreads; + _inUseThreads = inUseThreads; } public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) { - GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed); - GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued); - GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed); - - GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued); - GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed); + _workItemsQueued = workItemsQueued; + _workItemsProcessed = workItemsProcessed; } public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) { - GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds); - GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment(); + // Not supported } public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) { - GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds); - GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment(); + // Not supported } } - - internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable - { - static NullSTPInstancePerformanceCounters() - { - } - - private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null); - - public static NullSTPInstancePerformanceCounters Instance - { - get { return _instance; } - } - - public NullSTPInstancePerformanceCounters(string instance) {} - public void Close() {} - public void Dispose() {} - - public void SampleThreads(long activeThreads, long inUseThreads) {} - public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {} - public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {} - public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {} - } - } diff --git a/ThirdParty/SmartThreadPool/STPStartInfo.cs b/ThirdParty/SmartThreadPool/STPStartInfo.cs index fa9ceb4bb5..96fa094b3d 100644 --- a/ThirdParty/SmartThreadPool/STPStartInfo.cs +++ b/ThirdParty/SmartThreadPool/STPStartInfo.cs @@ -1,113 +1,212 @@ -// Ami Bar -// amibar@gmail.com - +using System; using System.Threading; namespace Amib.Threading { - /// - /// Summary description for STPStartInfo. - /// + /// + /// Summary description for STPStartInfo. + /// public class STPStartInfo : WIGStartInfo { - /// - /// Idle timeout in milliseconds. - /// If a thread is idle for _idleTimeout milliseconds then - /// it may quit. - /// - private int _idleTimeout; + private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout; + private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; + private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; +#if !(WINDOWS_PHONE) + private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority; +#endif + private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; + private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground; + private bool _enableLocalPerformanceCounters; + private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName; + private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize; - /// - /// The lower limit of threads in the pool. - /// - private int _minWorkerThreads; - - /// - /// The upper limit of threads in the pool. - /// - private int _maxWorkerThreads; - - /// - /// The priority of the threads in the pool - /// - private ThreadPriority _threadPriority; - - /// - /// The thread pool name. Threads will get names depending on this. - /// - private string _threadPoolName; - - /// - /// If this field is not null then the performance counters are enabled - /// and use the string as the name of the instance. - /// - private string _pcInstanceName; - - private int _stackSize; - - public STPStartInfo() : base() + public STPStartInfo() { + _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; +#if !(WINDOWS_PHONE) + _threadPriority = SmartThreadPool.DefaultThreadPriority; +#endif + _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; _idleTimeout = SmartThreadPool.DefaultIdleTimeout; _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; - _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; - _threadPriority = SmartThreadPool.DefaultThreadPriority; - _threadPoolName = SmartThreadPool.DefaultThreadPoolName; - _pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; - _stackSize = SmartThreadPool.DefaultStackSize; } - public STPStartInfo(STPStartInfo stpStartInfo) : base(stpStartInfo) + public STPStartInfo(STPStartInfo stpStartInfo) + : base(stpStartInfo) { - _idleTimeout = stpStartInfo._idleTimeout; - _minWorkerThreads = stpStartInfo._minWorkerThreads; - _maxWorkerThreads = stpStartInfo._maxWorkerThreads; - _threadPriority = stpStartInfo._threadPriority; + _idleTimeout = stpStartInfo.IdleTimeout; + _minWorkerThreads = stpStartInfo.MinWorkerThreads; + _maxWorkerThreads = stpStartInfo.MaxWorkerThreads; +#if !(WINDOWS_PHONE) + _threadPriority = stpStartInfo.ThreadPriority; +#endif + _performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName; + _enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters; _threadPoolName = stpStartInfo._threadPoolName; - _pcInstanceName = stpStartInfo._pcInstanceName; - _stackSize = stpStartInfo._stackSize; + _areThreadsBackground = stpStartInfo.AreThreadsBackground; +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + _apartmentState = stpStartInfo._apartmentState; +#endif } + + /// + /// Get/Set the idle timeout in milliseconds. + /// If a thread is idle (starved) longer than IdleTimeout then it may quit. + /// + public virtual int IdleTimeout + { + get { return _idleTimeout; } + set + { + ThrowIfReadOnly(); + _idleTimeout = value; + } + } - public int IdleTimeout - { - get { return _idleTimeout; } - set { _idleTimeout = value; } - } - public int MinWorkerThreads - { - get { return _minWorkerThreads; } - set { _minWorkerThreads = value; } - } + /// + /// Get/Set the lower limit of threads in the pool. + /// + public virtual int MinWorkerThreads + { + get { return _minWorkerThreads; } + set + { + ThrowIfReadOnly(); + _minWorkerThreads = value; + } + } - public int MaxWorkerThreads - { - get { return _maxWorkerThreads; } - set { _maxWorkerThreads = value; } - } - public ThreadPriority ThreadPriority - { - get { return _threadPriority; } - set { _threadPriority = value; } - } + /// + /// Get/Set the upper limit of threads in the pool. + /// + public virtual int MaxWorkerThreads + { + get { return _maxWorkerThreads; } + set + { + ThrowIfReadOnly(); + _maxWorkerThreads = value; + } + } - public virtual string ThreadPoolName - { +#if !(WINDOWS_PHONE) + /// + /// Get/Set the scheduling priority of the threads in the pool. + /// The Os handles the scheduling. + /// + public virtual ThreadPriority ThreadPriority + { + get { return _threadPriority; } + set + { + ThrowIfReadOnly(); + _threadPriority = value; + } + } +#endif + /// + /// Get/Set the thread pool name. Threads will get names depending on this. + /// + public virtual string ThreadPoolName { get { return _threadPoolName; } - set { _threadPoolName = value; } + set + { + ThrowIfReadOnly (); + _threadPoolName = value; + } } + /// + /// Get/Set the performance counter instance name of this SmartThreadPool + /// The default is null which indicate not to use performance counters at all. + /// + public virtual string PerformanceCounterInstanceName + { + get { return _performanceCounterInstanceName; } + set + { + ThrowIfReadOnly(); + _performanceCounterInstanceName = value; + } + } - public string PerformanceCounterInstanceName + /// + /// Enable/Disable the local performance counter. + /// This enables the user to get some performance information about the SmartThreadPool + /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.) + /// The default is false. + /// + public virtual bool EnableLocalPerformanceCounters + { + get { return _enableLocalPerformanceCounters; } + set + { + ThrowIfReadOnly(); + _enableLocalPerformanceCounters = value; + } + } + + /// + /// Get/Set backgroundness of thread in thread pool. + /// + public virtual bool AreThreadsBackground + { + get { return _areThreadsBackground; } + set + { + ThrowIfReadOnly (); + _areThreadsBackground = value; + } + } + + /// + /// Get a readonly version of this STPStartInfo. + /// + /// Returns a readonly reference to this STPStartInfo + public new STPStartInfo AsReadOnly() { - get { return _pcInstanceName; } - set { _pcInstanceName = value; } + return new STPStartInfo(this) { _readOnly = true }; } - public int StackSize +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState; + + /// + /// Get/Set the apartment state of threads in the thread pool + /// + public ApartmentState ApartmentState { - get { return _stackSize; } - set { _stackSize = value; } + get { return _apartmentState; } + set + { + ThrowIfReadOnly(); + _apartmentState = value; + } + } + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + /// + /// Get/Set the max stack size of threads in the thread pool + /// + public int? MaxStackSize + { + get { return _maxStackSize; } + set + { + ThrowIfReadOnly(); + if (value.HasValue && value.Value < 0) + { + throw new ArgumentOutOfRangeException("value", "Value must be greater than 0."); + } + _maxStackSize = value; + } } +#endif + +#endif } } diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs new file mode 100644 index 0000000000..d9502bbc4c --- /dev/null +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs @@ -0,0 +1,60 @@ + +using System; +using Amib.Threading.Internal; + +namespace Amib.Threading +{ + public partial class SmartThreadPool + { + #region ThreadEntry class + + internal class ThreadEntry + { + /// + /// The thread creation time + /// The value is stored as UTC value. + /// + private readonly DateTime _creationTime; + + /// + /// The last time this thread has been running + /// It is updated by IAmAlive() method + /// The value is stored as UTC value. + /// + private DateTime _lastAliveTime; + + /// + /// A reference from each thread in the thread pool to its SmartThreadPool + /// object container. + /// With this variable a thread can know whatever it belongs to a + /// SmartThreadPool. + /// + private readonly SmartThreadPool _associatedSmartThreadPool; + + /// + /// A reference to the current work item a thread from the thread pool + /// is executing. + /// + public WorkItem CurrentWorkItem { get; set; } + + public ThreadEntry(SmartThreadPool stp) + { + _associatedSmartThreadPool = stp; + _creationTime = DateTime.UtcNow; + _lastAliveTime = DateTime.MinValue; + } + + public SmartThreadPool AssociatedSmartThreadPool + { + get { return _associatedSmartThreadPool; } + } + + public void IAmAlive() + { + _lastAliveTime = DateTime.UtcNow; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs index 19a000717e..a4f4ce5695 100644 --- a/ThirdParty/SmartThreadPool/SmartThreadPool.cs +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs @@ -1,61 +1,106 @@ -// Ami Bar -// amibar@gmail.com -// -// Smart thread pool in C#. +#region Release History + +// Smart Thread Pool // 7 Aug 2004 - Initial release +// // 14 Sep 2004 - Bug fixes +// // 15 Oct 2004 - Added new features -// - Work items return result. -// - Support waiting synchronization for multiple work items. -// - Work items can be cancelled. -// - Passage of the caller thread’s context to the thread in the pool. -// - Minimal usage of WIN32 handles. -// - Minor bug fixes. +// - Work items return result. +// - Support waiting synchronization for multiple work items. +// - Work items can be cancelled. +// - Passage of the caller thread’s context to the thread in the pool. +// - Minimal usage of WIN32 handles. +// - Minor bug fixes. +// // 26 Dec 2004 - Changes: -// - Removed static constructors. +// - Removed static constructors. // - Added finalizers. -// - Changed Exceptions so they are serializable. -// - Fixed the bug in one of the SmartThreadPool constructors. -// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. +// - Changed Exceptions so they are serializable. +// - Fixed the bug in one of the SmartThreadPool constructors. +// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. // The SmartThreadPool.WaitAny() is still limited by the .NET Framework. -// - Added PostExecute with options on which cases to call it. +// - Added PostExecute with options on which cases to call it. // - Added option to dispose of the state objects. // - Added a WaitForIdle() method that waits until the work items queue is empty. // - Added an STPStartInfo class for the initialization of the thread pool. // - Changed exception handling so if a work item throws an exception it // is rethrown at GetResult(), rather then firing an UnhandledException event. // Note that PostExecute exception are always ignored. +// // 25 Mar 2005 - Changes: -// - Fixed lost of work items bug +// - Fixed lost of work items bug +// // 3 Jul 2005: Changes. // - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. +// // 16 Aug 2005: Changes. -// - Fixed bug where the InUseThreads becomes negative when canceling work items. +// - Fixed bug where the InUseThreads becomes negative when canceling work items. // // 31 Jan 2006 - Changes: -// - Added work items priority -// - Removed support of chained delegates in callbacks and post executes (nobody really use this) -// - Added work items groups -// - Added work items groups idle event -// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array -// it returns true rather then throwing an exception. -// - Added option to start the STP and the WIG as suspended -// - Exception behavior changed, the real exception is returned by an -// inner exception -// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) -// - Added performance counters -// - Added priority to the threads in the pool +// - Added work items priority +// - Removed support of chained delegates in callbacks and post executes (nobody really use this) +// - Added work items groups +// - Added work items groups idle event +// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array +// it returns true rather then throwing an exception. +// - Added option to start the STP and the WIG as suspended +// - Exception behavior changed, the real exception is returned by an +// inner exception +// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) +// - Added performance counters +// - Added priority to the threads in the pool // // 13 Feb 2006 - Changes: -// - Added a call to the dispose of the Performance Counter so -// their won't be a Performance Counter leak. -// - Added exception catch in case the Performance Counters cannot -// be created. +// - Added a call to the dispose of the Performance Counter so +// their won't be a Performance Counter leak. +// - Added exception catch in case the Performance Counters cannot +// be created. +// +// 17 May 2008 - Changes: +// - Changed the dispose behavior and removed the Finalizers. +// - Enabled the change of the MaxThreads and MinThreads at run time. +// - Enabled the change of the Concurrency of a IWorkItemsGroup at run +// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency +// refers to the MaxThreads. +// - Improved the cancel behavior. +// - Added events for thread creation and termination. +// - Fixed the HttpContext context capture. +// - Changed internal collections so they use generic collections +// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup +// - Added support for WinCE +// - Added support for Action and Func +// +// 07 April 2009 - Changes: +// - Added support for Silverlight and Mono +// - Added Join, Choice, and Pipe to SmartThreadPool. +// - Added local performance counters (for Mono, Silverlight, and WindowsCE) +// - Changed duration measures from DateTime.Now to Stopwatch. +// - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList. +// +// 21 December 2009 - Changes: +// - Added work item timeout (passive) +// +// 20 August 2012 - Changes: +// - Added set name to threads +// - Fixed the WorkItemsQueue.Dequeue. +// Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... } +// - Fixed SmartThreadPool.Pipe +// - Added IsBackground option to threads +// - Added ApartmentState to threads +// - Fixed thread creation when queuing many work items at the same time. +// +// 24 August 2012 - Changes: +// - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan +// - Added option to set MaxStackSize of threads + +#endregion using System; using System.Security; using System.Threading; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -63,1358 +108,1360 @@ using Amib.Threading.Internal; namespace Amib.Threading { - #region SmartThreadPool class - /// - /// Smart thread pool class. - /// - public class SmartThreadPool : IWorkItemsGroup, IDisposable - { - #region Default Constants + #region SmartThreadPool class + /// + /// Smart thread pool class. + /// + public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable + { + #region Public Default Constants + /// + /// Default minimum number of threads the thread pool contains. (0) + /// + public const int DefaultMinWorkerThreads = 0; + + /// + /// Default maximum number of threads the thread pool contains. (25) + /// + public const int DefaultMaxWorkerThreads = 25; + + /// + /// Default idle timeout in milliseconds. (One minute) + /// + public const int DefaultIdleTimeout = 60*1000; // One minute + + /// + /// Indicate to copy the security context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerCallContext = false; + + /// + /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerHttpContext = false; + + /// + /// Indicate to dispose of the state objects if they support the IDispose interface. (false) + /// + public const bool DefaultDisposeOfStateObjects = false; + + /// + /// The default option to run the post execute (CallToPostExecute.Always) + /// + public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; + + /// + /// The default post execute method to run. (None) + /// When null it means not to call it. + /// + public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback; + + /// + /// The default work item priority (WorkItemPriority.Normal) + /// + public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; + + /// + /// The default is to work on work items as soon as they arrive + /// and not to wait for the start. (false) + /// + public const bool DefaultStartSuspended = false; + + /// + /// The default name to use for the performance counters instance. (null) + /// + public static readonly string DefaultPerformanceCounterInstanceName; + +#if !(WINDOWS_PHONE) + + /// + /// The default thread priority (ThreadPriority.Normal) + /// + public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; +#endif /// - /// Default minimum number of threads the thread pool contains. (0) - /// - public const int DefaultMinWorkerThreads = 0; - - /// - /// Default maximum number of threads the thread pool contains. (25) - /// - public const int DefaultMaxWorkerThreads = 25; - - /// - /// Default idle timeout in milliseconds. (One minute) - /// - public const int DefaultIdleTimeout = 60*1000; // One minute - - /// - /// Indicate to copy the security context of the caller and then use it in the call. (false) - /// - public const bool DefaultUseCallerCallContext = false; - - /// - /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) - /// - public const bool DefaultUseCallerHttpContext = false; - - /// - /// Indicate to dispose of the state objects if they support the IDispose interface. (false) - /// - public const bool DefaultDisposeOfStateObjects = false; - - /// - /// The default option to run the post execute - /// - public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; - - /// - /// The default post execute method to run. - /// When null it means not to call it. - /// - public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null; - - /// - /// The default work item priority - /// - public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; - - /// - /// The default is to work on work items as soon as they arrive - /// and not to wait for the start. - /// - public const bool DefaultStartSuspended = false; - - /// - /// The default is not to use the performance counters - /// - public static readonly string DefaultPerformanceCounterInstanceName = null; - - public static readonly int DefaultStackSize = 0; - - /// - /// The default thread priority - /// - public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; - - /// - /// The default thread pool name + /// The default thread pool name. (SmartThreadPool) /// public const string DefaultThreadPoolName = "SmartThreadPool"; - #endregion + /// + /// The default Max Stack Size. (SmartThreadPool) + /// + public static readonly int? DefaultMaxStackSize = null; + + /// + /// The default fill state with params. (false) + /// It is relevant only to QueueWorkItem of Action<...>/Func<...> + /// + public const bool DefaultFillStateWithArgs = false; + + /// + /// The default thread backgroundness. (true) + /// + public const bool DefaultAreThreadsBackground = true; + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// The default apartment state of a thread in the thread pool. + /// The default is ApartmentState.Unknown which means the STP will not + /// set the apartment of the thread. It will use the .NET default. + /// + public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; +#endif + + #endregion #region Member Variables - /// - /// Contains the name of this instance of SmartThreadPool. - /// Can be changed by the user. - /// - private string _name = DefaultThreadPoolName; + /// + /// Dictionary of all the threads in the thread pool. + /// + private readonly SynchronizedDictionary _workerThreads = new SynchronizedDictionary(); + + /// + /// Queue of work items. + /// + private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); + + /// + /// Count the work items handled. + /// Used by the performance counter. + /// + private int _workItemsProcessed; + + /// + /// Number of threads that currently work (not idle). + /// + private int _inUseWorkerThreads; /// - /// Hashtable of all the threads in the thread pool. + /// Stores a copy of the original STPStartInfo. + /// It is used to change the MinThread and MaxThreads /// - private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable()); + private STPStartInfo _stpStartInfo; + + /// + /// Total number of work items that are stored in the work items queue + /// plus the work items that the threads in the pool are working on. + /// + private int _currentWorkItemsCount; + + /// + /// Signaled when the thread pool is idle, i.e. no thread is busy + /// and the work items queue is empty + /// + //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + + /// + /// An event to signal all the threads to quit immediately. + /// + //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); + private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false); /// - /// Queue of work items. + /// A flag to indicate if the Smart Thread Pool is now suspended. /// - private WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); + private bool _isSuspended; + + /// + /// A flag to indicate the threads to quit. + /// + private bool _shutdown; + + /// + /// Counts the threads created in the pool. + /// It is used to name the threads. + /// + private int _threadCounter; + + /// + /// Indicate that the SmartThreadPool has been disposed + /// + private bool _isDisposed; + + /// + /// Holds all the WorkItemsGroup instaces that have at least one + /// work item int the SmartThreadPool + /// This variable is used in case of Shutdown + /// + private readonly SynchronizedDictionary _workItemsGroups = new SynchronizedDictionary(); /// - /// Count the work items handled. - /// Used by the performance counter. + /// A common object for all the work items int the STP + /// so we can mark them to cancel in O(1) /// - private long _workItemsProcessed = 0; + private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup(); /// - /// Number of threads that currently work (not idle). + /// Windows STP performance counters /// - private int _inUseWorkerThreads = 0; + private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance; /// - /// Start information to use. - /// It is simpler than providing many constructors. + /// Local STP performance counters /// - private STPStartInfo _stpStartInfo = new STPStartInfo(); + private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance; - /// - /// Total number of work items that are stored in the work items queue - /// plus the work items that the threads in the pool are working on. - /// - private int _currentWorkItemsCount = 0; - /// - /// Signaled when the thread pool is idle, i.e. no thread is busy - /// and the work items queue is empty - /// - private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); - - /// - /// An event to signal all the threads to quit immediately. - /// - private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); - - /// - /// A flag to indicate the threads to quit. - /// - private bool _shutdown = false; - - /// - /// Counts the threads created in the pool. - /// It is used to name the threads. - /// - private int _threadCounter = 0; - - /// - /// Indicate that the SmartThreadPool has been disposed - /// - private bool _isDisposed = false; - - /// - /// Event to send that the thread pool is idle - /// - private event EventHandler _stpIdle; - - /// - /// On idle event - /// - //private event WorkItemsGroupIdleHandler _onIdle; - - /// - /// Holds all the WorkItemsGroup instaces that have at least one - /// work item int the SmartThreadPool - /// This variable is used in case of Shutdown - /// - private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable()); - - /// - /// A reference from each thread in the thread pool to its SmartThreadPool - /// object container. - /// With this variable a thread can know whatever it belongs to a - /// SmartThreadPool. - /// +#if (WINDOWS_PHONE) + private static readonly Dictionary _threadEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot(); +#else [ThreadStatic] - private static SmartThreadPool _smartThreadPool; + private static ThreadEntry _threadEntry; + +#endif + + /// + /// An event to call after a thread is created, but before + /// it's first use. + /// + private event ThreadInitializationHandler _onThreadInitialization; + + /// + /// An event to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + private event ThreadTerminationHandler _onThreadTermination; + + #endregion + + #region Per thread properties /// /// A reference to the current work item a thread from the thread pool /// is executing. /// - [ThreadStatic] - private static WorkItem _currentWorkItem; - - /// - /// STP performance counters - /// - private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance; - + internal static ThreadEntry CurrentThreadEntry + { +#if (WINDOWS_PHONE) + get + { + lock(_threadEntries) + { + ThreadEntry threadEntry; + if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry)) + { + return threadEntry; + } + } + return null; + } + set + { + lock(_threadEntries) + { + _threadEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + //Thread.CurrentThread.ManagedThreadId + return Thread.GetData(_threadEntrySlot) as ThreadEntry; + } + set + { + Thread.SetData(_threadEntrySlot, value); + } +#else + get + { + return _threadEntry; + } + set + { + _threadEntry = value; + } +#endif + } #endregion #region Construction and Finalization /// - /// Constructor - /// - public SmartThreadPool() - { + /// Constructor + /// + public SmartThreadPool() + { + _stpStartInfo = new STPStartInfo(); Initialize(); - } + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + public SmartThreadPool(int idleTimeout) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + /// Lower limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads, + int minWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + MinWorkerThreads = minWorkerThreads, + }; + Initialize(); + } /// /// Constructor /// - /// Idle timeout in milliseconds - public SmartThreadPool(int idleTimeout) - { - _stpStartInfo.IdleTimeout = idleTimeout; - Initialize(); - } + /// A SmartThreadPool configuration that overrides the default behavior + public SmartThreadPool(STPStartInfo stpStartInfo) + { + _stpStartInfo = new STPStartInfo(stpStartInfo); + Initialize(); + } - /// - /// Constructor - /// - /// Idle timeout in milliseconds - /// Upper limit of threads in the pool - public SmartThreadPool( - int idleTimeout, - int maxWorkerThreads) - { - _stpStartInfo.IdleTimeout = idleTimeout; - _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; - Initialize(); - } - - /// - /// Constructor - /// - /// Idle timeout in milliseconds - /// Upper limit of threads in the pool - /// Lower limit of threads in the pool - public SmartThreadPool( - int idleTimeout, - int maxWorkerThreads, - int minWorkerThreads) - { - _stpStartInfo.IdleTimeout = idleTimeout; - _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; - _stpStartInfo.MinWorkerThreads = minWorkerThreads; - Initialize(); - } - - /// - /// Constructor - /// - public SmartThreadPool(STPStartInfo stpStartInfo) - { - _stpStartInfo = new STPStartInfo(stpStartInfo); - Initialize(); - } - - private void Initialize() - { + private void Initialize() + { Name = _stpStartInfo.ThreadPoolName; - ValidateSTPStartInfo(); + ValidateSTPStartInfo(); + // _stpStartInfoRW stores a read/write copy of the STPStartInfo. + // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten + + _isSuspended = _stpStartInfo.StartSuspended; + +#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE) + if (null != _stpStartInfo.PerformanceCounterInstanceName) + { + throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); + } +#else if (null != _stpStartInfo.PerformanceCounterInstanceName) { try { - _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); + _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); } - catch(Exception e) + catch (Exception e) { - Debug.WriteLine("Unable to create Performance Counters: " + e.ToString()); - _pcs = NullSTPInstancePerformanceCounters.Instance; + Debug.WriteLine("Unable to create Performance Counters: " + e); + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; } } +#endif - StartOptimalNumberOfThreads(); - } - - private void StartOptimalNumberOfThreads() - { - int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); - threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); - StartThreads(threadsCount); - } - - private void ValidateSTPStartInfo() - { - if (_stpStartInfo.MinWorkerThreads < 0) + if (_stpStartInfo.EnableLocalPerformanceCounters) { - throw new ArgumentOutOfRangeException( - "MinWorkerThreads", "MinWorkerThreads cannot be negative"); + _localPCs = new LocalSTPInstancePerformanceCounters(); } + // If the STP is not started suspended then start the threads. + if (!_isSuspended) + { + StartOptimalNumberOfThreads(); + } + } + + private void StartOptimalNumberOfThreads() + { + int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); + threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); + threadsCount -= _workerThreads.Count; + if (threadsCount > 0) + { + StartThreads(threadsCount); + } + } + + private void ValidateSTPStartInfo() + { + if (_stpStartInfo.MinWorkerThreads < 0) + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads", "MinWorkerThreads cannot be negative"); + } + if (_stpStartInfo.MaxWorkerThreads <= 0) - { - throw new ArgumentOutOfRangeException( - "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); - } + { + throw new ArgumentOutOfRangeException( + "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); + } if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) - { - throw new ArgumentOutOfRangeException( - "MinWorkerThreads, maxWorkerThreads", - "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); - } - } + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads, maxWorkerThreads", + "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); + } + } - private void ValidateCallback(Delegate callback) - { - if(callback.GetInvocationList().Length > 1) - { - throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); - } - } + private static void ValidateCallback(Delegate callback) + { + if(callback.GetInvocationList().Length > 1) + { + throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); + } + } - #endregion + #endregion - #region Thread Processing + #region Thread Processing - /// - /// Waits on the queue for a work item, shutdown, or timeout. - /// - /// - /// Returns the WaitingCallback or null in case of timeout or shutdown. - /// - private WorkItem Dequeue() - { - WorkItem workItem = + /// + /// Waits on the queue for a work item, shutdown, or timeout. + /// + /// + /// Returns the WaitingCallback or null in case of timeout or shutdown. + /// + private WorkItem Dequeue() + { + WorkItem workItem = _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); - return workItem; - } + return workItem; + } - /// - /// Put a new work item in the queue - /// - /// A work item to queue - private void Enqueue(WorkItem workItem) - { - Enqueue(workItem, true); - } + /// + /// Put a new work item in the queue + /// + /// A work item to queue + internal override void Enqueue(WorkItem workItem) + { + // Make sure the workItem is not null + Debug.Assert(null != workItem); - /// - /// Put a new work item in the queue - /// - /// A work item to queue - internal void Enqueue(WorkItem workItem, bool incrementWorkItems) - { - // Make sure the workItem is not null - Debug.Assert(null != workItem); + IncrementWorkItemsCount(); - if (incrementWorkItems) - { - IncrementWorkItemsCount(); - } + workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; + _workItemsQueue.EnqueueWorkItem(workItem); + workItem.WorkItemIsQueued(); - _workItemsQueue.EnqueueWorkItem(workItem); - workItem.WorkItemIsQueued(); + // If all the threads are busy then try to create a new one + if (_currentWorkItemsCount > _workerThreads.Count) + { + StartThreads(1); + } + } - // If all the threads are busy then try to create a new one - if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count) - { - StartThreads(1); - } - } + private void IncrementWorkItemsCount() + { + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - private void IncrementWorkItemsCount() - { - _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - - int count = Interlocked.Increment(ref _currentWorkItemsCount); - //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); - if (count == 1) - { - //Trace.WriteLine("STP is NOT idle"); + int count = Interlocked.Increment(ref _currentWorkItemsCount); + //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); + if (count == 1) + { + IsIdle = false; _isIdleWaitHandle.Reset(); - } - } - - private void DecrementWorkItemsCount() - { - ++_workItemsProcessed; - - // The counter counts even if the work item was cancelled - _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + } + } + private void DecrementWorkItemsCount() + { int count = Interlocked.Decrement(ref _currentWorkItemsCount); //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); - if (count == 0) + if (count == 0) { - //Trace.WriteLine("STP is idle"); + IsIdle = true; _isIdleWaitHandle.Set(); } - } - internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) - { - _workItemsGroups[workItemsGroup] = workItemsGroup; - } + Interlocked.Increment(ref _workItemsProcessed); - internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) - { - if (_workItemsGroups.Contains(workItemsGroup)) + if (!_shutdown) { - _workItemsGroups.Remove(workItemsGroup); - } - } - - /// - /// Inform that the current thread is about to quit or quiting. - /// The same thread may call this method more than once. - /// - private void InformCompleted() - { - // There is no need to lock the two methods together - // since only the current thread removes itself - // and the _workerThreads is a synchronized hashtable - if (_workerThreads.Contains(Thread.CurrentThread)) - { - _workerThreads.Remove(Thread.CurrentThread); - _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - } - } - - /// - /// Starts new threads - /// - /// The number of threads to start - private void StartThreads(int threadsCount) - { - if (_stpStartInfo.StartSuspended) - { - return; + // The counter counts even if the work item was cancelled + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); } - lock(_workerThreads.SyncRoot) - { - // Don't start threads on shut down - if (_shutdown) - { - return; - } + } - for(int i = 0; i < threadsCount; ++i) - { - // Don't create more threads then the upper limit + internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + _workItemsGroups[workItemsGroup] = workItemsGroup; + } + + internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + if (_workItemsGroups.Contains(workItemsGroup)) + { + _workItemsGroups.Remove(workItemsGroup); + } + } + + /// + /// Inform that the current thread is about to quit or quiting. + /// The same thread may call this method more than once. + /// + private void InformCompleted() + { + // There is no need to lock the two methods together + // since only the current thread removes itself + // and the _workerThreads is a synchronized dictionary + if (_workerThreads.Contains(Thread.CurrentThread)) + { + _workerThreads.Remove(Thread.CurrentThread); + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + + /// + /// Starts new threads + /// + /// The number of threads to start + private void StartThreads(int threadsCount) + { + if (_isSuspended) + { + return; + } + + lock(_workerThreads.SyncRoot) + { + // Don't start threads on shut down + if (_shutdown) + { + return; + } + + for(int i = 0; i < threadsCount; ++i) + { + // Don't create more threads then the upper limit if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) - { - return; - } + { + return; + } // Create a new thread - Thread workerThread; - if (_stpStartInfo.StackSize > 0) - workerThread = new Thread(ProcessQueuedItems, _stpStartInfo.StackSize); - else - workerThread = new Thread(ProcessQueuedItems); - // Configure the new thread and start it - workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; - workerThread.IsBackground = true; - workerThread.Priority = _stpStartInfo.ThreadPriority; - workerThread.Start(); - ++_threadCounter; +#if (_SILVERLIGHT) || (WINDOWS_PHONE) + Thread workerThread = new Thread(ProcessQueuedItems); +#else + Thread workerThread = + _stpStartInfo.MaxStackSize.HasValue + ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) + : new Thread(ProcessQueuedItems); +#endif + // Configure the new thread and start it + workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; + workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; - // Add the new thread to the hashtable and update its creation - // time. - _workerThreads[workerThread] = DateTime.Now; - _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - } - } - } - - /// - /// A worker thread method that processes work items from the work items queue. - /// - private void ProcessQueuedItems() - { - // Initialize the _smartThreadPool variable - _smartThreadPool = this; - - try - { - bool bInUseWorkerThreadsWasIncremented = false; - - // Process until shutdown. - while(!_shutdown) - { - // Update the last time this thread was seen alive. - // It's good for debugging. - _workerThreads[Thread.CurrentThread] = DateTime.Now; - - // Wait for a work item, shutdown, or timeout - WorkItem workItem = Dequeue(); - - // Update the last time this thread was seen alive. - // It's good for debugging. - _workerThreads[Thread.CurrentThread] = DateTime.Now; - - // On timeout or shut down. - if (null == workItem) +#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE) + if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) { - // Double lock for quit. - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + workerThread.SetApartmentState(_stpStartInfo.ApartmentState); + } +#endif + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + workerThread.Priority = _stpStartInfo.ThreadPriority; +#endif + workerThread.Start(); + ++_threadCounter; + + // Add it to the dictionary and update its creation time. + _workerThreads[workerThread] = new ThreadEntry(this); + + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + } + + /// + /// A worker thread method that processes work items from the work items queue. + /// + private void ProcessQueuedItems() + { + // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks + // of the dictionary. + CurrentThreadEntry = _workerThreads[Thread.CurrentThread]; + + FireOnThreadInitialization(); + + try + { + bool bInUseWorkerThreadsWasIncremented = false; + + // Process until shutdown. + while(!_shutdown) + { + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); + + // The following block handles the when the MaxWorkerThreads has been + // incremented by the user at run-time. + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + { + lock (_workerThreads.SyncRoot) { - lock(_workerThreads.SyncRoot) + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) { - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) - { - // Inform that the thread is quiting and then quit. - // This method must be called within this lock or else - // more threads will quit and the thread pool will go - // below the lower limit. - InformCompleted(); - break; - } + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; } } } - // If we didn't quit then skip to the next iteration. - if (null == workItem) - { - continue; - } + // Wait for a work item, shutdown, or timeout + WorkItem workItem = Dequeue(); - try - { - // Initialize the value to false - bInUseWorkerThreadsWasIncremented = false; + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); - // Change the state of the work item to 'in progress' if possible. - // We do it here so if the work item has been canceled we won't - // increment the _inUseWorkerThreads. - // The cancel mechanism doesn't delete items from the queue, - // it marks the work item as canceled, and when the work item - // is dequeued, we just skip it. - // If the post execute of work item is set to always or to - // call when the work item is canceled then the StartingWorkItem() - // will return true, so the post execute can run. - if (!workItem.StartingWorkItem()) - { - continue; - } + // On timeout or shut down. + if (null == workItem) + { + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + lock(_workerThreads.SyncRoot) + { + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; + } + } + } + } - // Execute the callback. Make sure to accurately - // record how many callbacks are currently executing. - int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); - _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + // If we didn't quit then skip to the next iteration. + if (null == workItem) + { + continue; + } - // Mark that the _inUseWorkerThreads incremented, so in the finally{} - // statement we will decrement it correctly. - bInUseWorkerThreadsWasIncremented = true; + try + { + // Initialize the value to false + bInUseWorkerThreadsWasIncremented = false; - // Set the _currentWorkItem to the current work item - _currentWorkItem = workItem; + // Set the Current Work Item of the thread. + // Store the Current Work Item before the workItem.StartingWorkItem() is called, + // so WorkItem.Cancel can work when the work item is between InQueue and InProgress + // states. + // If the work item has been cancelled BEFORE the workItem.StartingWorkItem() + // (work item is in InQueue state) then workItem.StartingWorkItem() will return false. + // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then + // (work item is in InProgress state) then the thread will be aborted + CurrentThreadEntry.CurrentWorkItem = workItem; - lock(workItem) - { - workItem.currentThread = Thread.CurrentThread; - } + // Change the state of the work item to 'in progress' if possible. + // We do it here so if the work item has been canceled we won't + // increment the _inUseWorkerThreads. + // The cancel mechanism doesn't delete items from the queue, + // it marks the work item as canceled, and when the work item + // is dequeued, we just skip it. + // If the post execute of work item is set to always or to + // call when the work item is canceled then the StartingWorkItem() + // will return true, so the post execute can run. + if (!workItem.StartingWorkItem()) + { + continue; + } - ExecuteWorkItem(workItem); + // Execute the callback. Make sure to accurately + // record how many callbacks are currently executing. + int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - lock(workItem) - { - workItem.currentThread = null; - } + // Mark that the _inUseWorkerThreads incremented, so in the finally{} + // statement we will decrement it correctly. + bInUseWorkerThreadsWasIncremented = true; - } - catch(ThreadAbortException ex) - { - lock(workItem) - { - workItem.currentThread = null; - } + workItem.FireWorkItemStarted(); + + ExecuteWorkItem(workItem); + } + catch(Exception ex) + { ex.GetHashCode(); - Thread.ResetAbort(); - } - catch(Exception ex) - { - ex.GetHashCode(); - // Do nothing - } - finally - { - lock(workItem) - { - workItem.currentThread = null; - } + // Do nothing + } + finally + { + workItem.DisposeOfState(); - if (null != workItem) - { - workItem.DisposeOfState(); - } + // Set the CurrentWorkItem to null, since we + // no longer run user's code. + CurrentThreadEntry.CurrentWorkItem = null; - // Set the _currentWorkItem to null, since we - // no longer run user's code. - _currentWorkItem = null; + // Decrement the _inUseWorkerThreads only if we had + // incremented it. Note the cancelled work items don't + // increment _inUseWorkerThreads. + if (bInUseWorkerThreadsWasIncremented) + { + int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + } - // Decrement the _inUseWorkerThreads only if we had - // incremented it. Note the cancelled work items don't - // increment _inUseWorkerThreads. - if (bInUseWorkerThreadsWasIncremented) - { - int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); - _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - } + // Notify that the work item has been completed. + // WorkItemsGroup may enqueue their next work item. + workItem.FireWorkItemCompleted(); - // Notify that the work item has been completed. - // WorkItemsGroup may enqueue their next work item. - workItem.FireWorkItemCompleted(); - - // Decrement the number of work items here so the idle - // ManualResetEvent won't fluctuate. - DecrementWorkItemsCount(); - } - } - } - catch(ThreadAbortException tae) - { + // Decrement the number of work items here so the idle + // ManualResetEvent won't fluctuate. + DecrementWorkItemsCount(); + } + } + } + catch(ThreadAbortException tae) + { tae.GetHashCode(); // Handle the abort exception gracfully. - Thread.ResetAbort(); +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + Thread.ResetAbort(); +#endif } - catch(Exception e) + catch(Exception e) + { + Debug.Assert(null != e); + } + finally + { + InformCompleted(); + FireOnThreadTermination(); + } + } + + private void ExecuteWorkItem(WorkItem workItem) + { + _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + try + { + workItem.Execute(); + } + finally + { + _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + } + } + + + #endregion + + #region Public Methods + + private void ValidateWaitForIdle() + { + if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this) + { + throw new NotSupportedException( + "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) + { + if (null == CurrentThreadEntry) { - Debug.Assert(null != e); + return; } - finally - { - InformCompleted(); - } - } - private void ExecuteWorkItem(WorkItem workItem) - { - _pcs.SampleWorkItemsWaitTime(workItem.WaitingTime); - try - { - workItem.Execute(); - } - catch - { - throw; - } - finally - { - _pcs.SampleWorkItemsProcessTime(workItem.ProcessTime); - } - } - - - #endregion - - #region Public Methods - - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Wait for the thread pool to be idle - /// - public void WaitForIdle() - { - WaitForIdle(Timeout.Infinite); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(TimeSpan timeout) - { - return WaitForIdle((int)timeout.TotalMilliseconds); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(int millisecondsTimeout) - { - ValidateWaitForIdle(); - return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); - } - - private void ValidateWaitForIdle() - { - if(_smartThreadPool == this) - { - throw new NotSupportedException( - "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); - } - } - - internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) - { - ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem); - if ((null != workItemsGroup) && - (null != SmartThreadPool._currentWorkItem) && - SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup)) - { - throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) - { - if ((null != workItemsGroup) && + WorkItem workItem = CurrentThreadEntry.CurrentWorkItem; + ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem); + if ((null != workItemsGroup) && (null != workItem) && - workItem.WasQueuedBy(workItemsGroup)) - { - throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); - } - } + CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) + { + if ((null != workItemsGroup) && + (null != workItem) && + workItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + /// + /// Force the SmartThreadPool to shutdown + /// + public void Shutdown() + { + Shutdown(true, 0); + } /// - /// Force the SmartThreadPool to shutdown + /// Force the SmartThreadPool to shutdown with timeout /// - public void Shutdown() - { - Shutdown(true, 0); - } - public void Shutdown(bool forceAbort, TimeSpan timeout) - { - Shutdown(forceAbort, (int)timeout.TotalMilliseconds); - } + { + Shutdown(forceAbort, (int)timeout.TotalMilliseconds); + } + + /// + /// Empties the queue of work items and abort the threads in the pool. + /// + public void Shutdown(bool forceAbort, int millisecondsTimeout) + { + ValidateNotDisposed(); + + ISTPInstancePerformanceCounters pcs = _windowsPCs; + + if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs) + { + // Set the _pcs to "null" to stop updating the performance + // counters + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + + pcs.Dispose(); + } + + Thread [] threads; + lock(_workerThreads.SyncRoot) + { + // Shutdown the work items queue + _workItemsQueue.Dispose(); + + // Signal the threads to exit + _shutdown = true; + _shuttingDownEvent.Set(); + + // Make a copy of the threads' references in the pool + threads = new Thread [_workerThreads.Count]; + _workerThreads.Keys.CopyTo(threads, 0); + } + + int millisecondsLeft = millisecondsTimeout; + Stopwatch stopwatch = Stopwatch.StartNew(); + //DateTime start = DateTime.UtcNow; + bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); + bool timeout = false; + + // Each iteration we update the time left for the timeout. + foreach(Thread thread in threads) + { + // Join don't work with negative numbers + if (!waitInfinitely && (millisecondsLeft < 0)) + { + timeout = true; + break; + } + + // Wait for the thread to terminate + bool success = thread.Join(millisecondsLeft); + if(!success) + { + timeout = true; + break; + } + + if(!waitInfinitely) + { + // Update the time left to wait + //TimeSpan ts = DateTime.UtcNow - start; + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; + } + } + + if (timeout && forceAbort) + { + // Abort the threads in the pool + foreach(Thread thread in threads) + { + + if ((thread != null) +#if !(_WINDOWS_CE) + && thread.IsAlive +#endif + ) + { + try + { + thread.Abort(); // Shutdown + } + catch(SecurityException e) + { + e.GetHashCode(); + } + catch(ThreadStateException ex) + { + ex.GetHashCode(); + // In case the thread has been terminated + // after the check if it is alive. + } + } + } + } + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults) + { + return WaitAll(waitableResults, Timeout.Infinite, true); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } + + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults) + { + return WaitAny(waitableResults, Timeout.Infinite, true); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } /// - /// Empties the queue of work items and abort the threads in the pool. + /// Creates a new WorkItemsGroup. /// - public void Shutdown(bool forceAbort, int millisecondsTimeout) + /// The number of work items that can be run concurrently + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); + return workItemsGroup; + } + + /// + /// Creates a new WorkItemsGroup. + /// + /// The number of work items that can be run concurrently + /// A WorkItemsGroup configuration that overrides the default behavior + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); + return workItemsGroup; + } + + #region Fire Thread's Events + + private void FireOnThreadInitialization() { - ValidateNotDisposed(); - - ISTPInstancePerformanceCounters pcs = _pcs; - - if (NullSTPInstancePerformanceCounters.Instance != _pcs) + if (null != _onThreadInitialization) { - _pcs.Dispose(); - // Set the _pcs to "null" to stop updating the performance - // counters - _pcs = NullSTPInstancePerformanceCounters.Instance; - } - - Thread [] threads = null; - lock(_workerThreads.SyncRoot) - { - // Shutdown the work items queue - _workItemsQueue.Dispose(); - - // Signal the threads to exit - _shutdown = true; - _shuttingDownEvent.Set(); - - // Make a copy of the threads' references in the pool - threads = new Thread [_workerThreads.Count]; - _workerThreads.Keys.CopyTo(threads, 0); - } - - int millisecondsLeft = millisecondsTimeout; - DateTime start = DateTime.Now; - bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); - bool timeout = false; - - // Each iteration we update the time left for the timeout. - foreach(Thread thread in threads) - { - // Join don't work with negative numbers - if (!waitInfinitely && (millisecondsLeft < 0)) + foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList()) { - timeout = true; - break; - } - - // Wait for the thread to terminate - bool success = thread.Join(millisecondsLeft); - if(!success) - { - timeout = true; - break; - } - - if(!waitInfinitely) - { - // Update the time left to wait - TimeSpan ts = DateTime.Now - start; - millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds; - } - } - - if (timeout && forceAbort) - { - // Abort the threads in the pool - foreach(Thread thread in threads) - { - if ((thread != null) && thread.IsAlive) + try { - try - { - thread.Abort("Shutdown"); - } - catch(SecurityException e) - { - e.GetHashCode(); - } - catch(ThreadStateException ex) - { - ex.GetHashCode(); - // In case the thread has been terminated - // after the check if it is alive. - } + tih(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; } } } - - // Dispose of the performance counters - pcs.Dispose(); } - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults) + private void FireOnThreadTermination() { - return WaitAll(workItemResults, Timeout.Infinite, true); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext) - { - return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext) - { - return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); - } - - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults) - { - return WaitAny(workItemResults, Timeout.Infinite, true); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext) - { - return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext) - { - return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) - { - IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); - return workItemsGroup; - } - - public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) - { - IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); - return workItemsGroup; - } - - public event WorkItemsGroupIdleHandler OnIdle - { - add + if (null != _onThreadTermination) { - throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); - //_onIdle += value; - } - remove - { - throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); - //_onIdle -= value; - } - } - - public void Cancel() - { - ICollection workItemsGroups = _workItemsGroups.Values; - foreach(WorkItemsGroup workItemsGroup in workItemsGroups) - { - workItemsGroup.Cancel(); - } - } - - public void Start() - { - lock (this) - { - if (!this._stpStartInfo.StartSuspended) + foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList()) { - return; + try + { + tth(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; + } } - _stpStartInfo.StartSuspended = false; } - - ICollection workItemsGroups = _workItemsGroups.Values; - foreach(WorkItemsGroup workItemsGroup in workItemsGroups) - { - workItemsGroup.OnSTPIsStarting(); - } - - StartOptimalNumberOfThreads(); } #endregion - #region Properties + /// + /// This event is fired when a thread is created. + /// Use it to initialize a thread before the work items use it. + /// + public event ThreadInitializationHandler OnThreadInitialization + { + add { _onThreadInitialization += value; } + remove { _onThreadInitialization -= value; } + } /// - /// Get/Set the name of the SmartThreadPool instance + /// This event is fired when a thread is terminating. + /// Use it for cleanup. /// - public string Name - { - get - { - return _name; - } + public event ThreadTerminationHandler OnThreadTermination + { + add { _onThreadTermination += value; } + remove { _onThreadTermination -= value; } + } + + internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + workItem.WasQueuedBy(wig) && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + + + + #endregion + + #region Properties + + /// + /// Get/Set the lower limit of threads in the pool. + /// + public int MinThreads + { + get + { + ValidateNotDisposed(); + return _stpStartInfo.MinWorkerThreads; + } set { - _name = value; + Debug.Assert(value >= 0); + Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads); + if (_stpStartInfo.MaxWorkerThreads < value) + { + _stpStartInfo.MaxWorkerThreads = value; + } + _stpStartInfo.MinWorkerThreads = value; + StartOptimalNumberOfThreads(); } - } + } - /// - /// Get the lower limit of threads in the pool. - /// - public int MinThreads - { - get - { - ValidateNotDisposed(); - return _stpStartInfo.MinWorkerThreads; - } - } - - /// - /// Get the upper limit of threads in the pool. - /// - public int MaxThreads - { - get - { - ValidateNotDisposed(); + /// + /// Get/Set the upper limit of threads in the pool. + /// + public int MaxThreads + { + get + { + ValidateNotDisposed(); return _stpStartInfo.MaxWorkerThreads; + } + + set + { + Debug.Assert(value > 0); + Debug.Assert(value >= _stpStartInfo.MinWorkerThreads); + if (_stpStartInfo.MinWorkerThreads > value) + { + _stpStartInfo.MinWorkerThreads = value; + } + _stpStartInfo.MaxWorkerThreads = value; + StartOptimalNumberOfThreads(); } - } - /// - /// Get the number of threads in the thread pool. - /// Should be between the lower and the upper limits. - /// - public int ActiveThreads - { - get - { - ValidateNotDisposed(); - return _workerThreads.Count; - } - } + } + /// + /// Get the number of threads in the thread pool. + /// Should be between the lower and the upper limits. + /// + public int ActiveThreads + { + get + { + ValidateNotDisposed(); + return _workerThreads.Count; + } + } + + /// + /// Get the number of busy (not idle) threads in the thread pool. + /// + public int InUseThreads + { + get + { + ValidateNotDisposed(); + return _inUseWorkerThreads; + } + } /// - /// Get the number of busy (not idle) threads in the thread pool. + /// Returns true if the current running work item has been cancelled. + /// Must be used within the work item's callback method. + /// The work item should sample this value in order to know if it + /// needs to quit before its completion. /// - public int InUseThreads - { - get - { - ValidateNotDisposed(); - return _inUseWorkerThreads; - } - } - - /// - /// Get the number of work items in the queue. - /// - public int WaitingCallbacks - { - get - { - ValidateNotDisposed(); - return _workItemsQueue.Count; - } - } - - - public event EventHandler Idle + public static bool IsWorkItemCanceled { - add + get { - _stpIdle += value; + return CurrentThreadEntry.CurrentWorkItem.IsCanceled; } + } + + /// + /// Checks if the work item has been cancelled, and if yes then abort the thread. + /// Can be used with Cancel and timeout + /// + public static void AbortOnWorkItemCancel() + { + if (IsWorkItemCanceled) + { + Thread.CurrentThread.Abort(); + } + } - remove + /// + /// Thread Pool start information (readonly) + /// + public STPStartInfo STPStartInfo + { + get { - _stpIdle -= value; + return _stpStartInfo.AsReadOnly(); } } + public bool IsShuttingdown + { + get { return _shutdown; } + } + + /// + /// Return the local calculated performance counters + /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true. + /// + public ISTPPerformanceCountersReader PerformanceCountersReader + { + get { return (ISTPPerformanceCountersReader)_localPCs; } + } + #endregion #region IDisposable Members -// ~SmartThreadPool() -// { -// Dispose(); -// } - public void Dispose() { if (!_isDisposed) @@ -1430,8 +1477,14 @@ namespace Amib.Threading _shuttingDownEvent = null; } _workerThreads.Clear(); + + if (null != _isIdleWaitHandle) + { + _isIdleWaitHandle.Close(); + _isIdleWaitHandle = null; + } + _isDisposed = true; - GC.SuppressFinalize(this); } } @@ -1443,6 +1496,237 @@ namespace Amib.Threading } } #endregion - } - #endregion + + #region WorkItemsGroupBase Overrides + + /// + /// Get/Set the maximum number of work items that execute cocurrency on the thread pool + /// + public override int Concurrency + { + get { return MaxThreads; } + set { MaxThreads = value; } + } + + /// + /// Get the number of work items in the queue. + /// + public override int WaitingCallbacks + { + get + { + ValidateNotDisposed(); + return _workItemsQueue.Count; + } + } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + public override object[] GetStates() + { + object[] states = _workItemsQueue.GetStates(); + return states; + } + + /// + /// WorkItemsGroup start information (readonly) + /// + public override WIGStartInfo WIGStartInfo + { + get { return _stpStartInfo.AsReadOnly(); } + } + + /// + /// Start the thread pool if it was started suspended. + /// If it is already running, this method is ignored. + /// + public override void Start() + { + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.OnSTPIsStarting(); + } + + StartOptimalNumberOfThreads(); + } + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + public override void Cancel(bool abortExecution) + { + _canceledSmartThreadPool.IsCanceled = true; + _canceledSmartThreadPool = new CanceledWorkItemsGroup(); + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.Cancel(abortExecution); + } + + if (abortExecution) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + threadEntry.AssociatedSmartThreadPool == this && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + ValidateWaitForIdle(); + return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); + } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + public override event WorkItemsGroupIdleHandler OnIdle + { + add + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle += value; + } + remove + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle -= value; + } + } + + internal override void PreQueueWorkItem() + { + ValidateNotDisposed(); + } + + #endregion + + #region Join, Choice, Pipe, etc. + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + foreach (Action action in actions) + { + workItemsGroup.QueueWorkItem(action); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(params Action[] actions) + { + Join((IEnumerable)actions); + } + + private class ChoiceIndex + { + public int _index = -1; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + + ManualResetEvent anActionCompleted = new ManualResetEvent(false); + + ChoiceIndex choiceIndex = new ChoiceIndex(); + + int i = 0; + foreach (Action action in actions) + { + Action act = action; + int value = i; + workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); }); + ++i; + } + workItemsGroup.Start(); + anActionCompleted.WaitOne(); + + return choiceIndex._index; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(params Action[] actions) + { + return Choice((IEnumerable)actions); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// A state context that passes + /// Actions to execute in the order they should run + public void Pipe(T pipeState, IEnumerable> actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo); + foreach (Action action in actions) + { + Action act = action; + workItemsGroup.QueueWorkItem(() => act(pipeState)); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// + /// Actions to execute in the order they should run + public void Pipe(T pipeState, params Action[] actions) + { + Pipe(pipeState, (IEnumerable>)actions); + } + #endregion + } + #endregion } diff --git a/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs new file mode 100644 index 0000000000..0cce19ffac --- /dev/null +++ b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; + +namespace Amib.Threading.Internal +{ + internal class SynchronizedDictionary + { + private readonly Dictionary _dictionary; + private readonly object _lock; + + public SynchronizedDictionary() + { + _lock = new object(); + _dictionary = new Dictionary(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool Contains(TKey key) + { + lock (_lock) + { + return _dictionary.ContainsKey(key); + } + } + + public void Remove(TKey key) + { + lock (_lock) + { + _dictionary.Remove(key); + } + } + + public object SyncRoot + { + get { return _lock; } + } + + public TValue this[TKey key] + { + get + { + lock (_lock) + { + return _dictionary[key]; + } + } + set + { + lock (_lock) + { + _dictionary[key] = value; + } + } + } + + public Dictionary.KeyCollection Keys + { + get + { + lock (_lock) + { + return _dictionary.Keys; + } + } + } + + public Dictionary.ValueCollection Values + { + get + { + lock (_lock) + { + return _dictionary.Values; + } + } + } + public void Clear() + { + lock (_lock) + { + _dictionary.Clear(); + } + } + } +} diff --git a/ThirdParty/SmartThreadPool/WIGStartInfo.cs b/ThirdParty/SmartThreadPool/WIGStartInfo.cs index 150317fc01..8af195b2af 100644 --- a/ThirdParty/SmartThreadPool/WIGStartInfo.cs +++ b/ThirdParty/SmartThreadPool/WIGStartInfo.cs @@ -1,99 +1,171 @@ -// Ami Bar -// amibar@gmail.com +using System; namespace Amib.Threading { - /// - /// Summary description for WIGStartInfo. - /// - public class WIGStartInfo - { - /// - /// Use the caller's security context - /// + /// + /// Summary description for WIGStartInfo. + /// + public class WIGStartInfo + { private bool _useCallerCallContext; - - /// - /// Use the caller's HTTP context - /// private bool _useCallerHttpContext; - - /// - /// Dispose of the state object of a work item - /// private bool _disposeOfStateObjects; - - /// - /// The option to run the post execute - /// private CallToPostExecute _callToPostExecute; - - /// - /// A post execute callback to call when none is provided in - /// the QueueWorkItem method. - /// private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; - - /// - /// Indicate the WorkItemsGroup to suspend the handling of the work items - /// until the Start() method is called. - /// private bool _startSuspended; + private WorkItemPriority _workItemPriority; + private bool _fillStateWithArgs; - public WIGStartInfo() + protected bool _readOnly; + + public WIGStartInfo() { - _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; - _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; - _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; - _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; - _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; + _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs; + _workItemPriority = SmartThreadPool.DefaultWorkItemPriority; _startSuspended = SmartThreadPool.DefaultStartSuspended; + _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; + _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; + _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; + _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; + _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; } - public WIGStartInfo(WIGStartInfo wigStartInfo) + public WIGStartInfo(WIGStartInfo wigStartInfo) { - _useCallerCallContext = wigStartInfo._useCallerCallContext; - _useCallerHttpContext = wigStartInfo._useCallerHttpContext; - _disposeOfStateObjects = wigStartInfo._disposeOfStateObjects; - _callToPostExecute = wigStartInfo._callToPostExecute; - _postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback; - _startSuspended = wigStartInfo._startSuspended; + _useCallerCallContext = wigStartInfo.UseCallerCallContext; + _useCallerHttpContext = wigStartInfo.UseCallerHttpContext; + _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + _callToPostExecute = wigStartInfo.CallToPostExecute; + _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + _workItemPriority = wigStartInfo.WorkItemPriority; + _startSuspended = wigStartInfo.StartSuspended; + _fillStateWithArgs = wigStartInfo.FillStateWithArgs; } - public bool UseCallerCallContext + protected void ThrowIfReadOnly() { - get { return _useCallerCallContext; } - set { _useCallerCallContext = value; } + if (_readOnly) + { + throw new NotSupportedException("This is a readonly instance and set is not supported"); + } } - public bool UseCallerHttpContext - { - get { return _useCallerHttpContext; } - set { _useCallerHttpContext = value; } - } + /// + /// Get/Set if to use the caller's security context + /// + public virtual bool UseCallerCallContext + { + get { return _useCallerCallContext; } + set + { + ThrowIfReadOnly(); + _useCallerCallContext = value; + } + } - public bool DisposeOfStateObjects - { - get { return _disposeOfStateObjects; } - set { _disposeOfStateObjects = value; } - } - public CallToPostExecute CallToPostExecute - { - get { return _callToPostExecute; } - set { _callToPostExecute = value; } - } + /// + /// Get/Set if to use the caller's HTTP context + /// + public virtual bool UseCallerHttpContext + { + get { return _useCallerHttpContext; } + set + { + ThrowIfReadOnly(); + _useCallerHttpContext = value; + } + } - public PostExecuteWorkItemCallback PostExecuteWorkItemCallback - { - get { return _postExecuteWorkItemCallback; } - set { _postExecuteWorkItemCallback = value; } - } - public bool StartSuspended - { - get { return _startSuspended; } - set { _startSuspended = value; } - } + /// + /// Get/Set if to dispose of the state object of a work item + /// + public virtual bool DisposeOfStateObjects + { + get { return _disposeOfStateObjects; } + set + { + ThrowIfReadOnly(); + _disposeOfStateObjects = value; + } + } + + + /// + /// Get/Set the run the post execute options + /// + public virtual CallToPostExecute CallToPostExecute + { + get { return _callToPostExecute; } + set + { + ThrowIfReadOnly(); + _callToPostExecute = value; + } + } + + + /// + /// Get/Set the default post execute callback + /// + public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback + { + get { return _postExecuteWorkItemCallback; } + set + { + ThrowIfReadOnly(); + _postExecuteWorkItemCallback = value; + } + } + + + /// + /// Get/Set if the work items execution should be suspended until the Start() + /// method is called. + /// + public virtual bool StartSuspended + { + get { return _startSuspended; } + set + { + ThrowIfReadOnly(); + _startSuspended = value; + } + } + + + /// + /// Get/Set the default priority that a work item gets when it is enqueued + /// + public virtual WorkItemPriority WorkItemPriority + { + get { return _workItemPriority; } + set { _workItemPriority = value; } + } + + /// + /// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the + /// arguments as an object array into the state of the work item. + /// The arguments can be access later by IWorkItemResult.State. + /// + public virtual bool FillStateWithArgs + { + get { return _fillStateWithArgs; } + set + { + ThrowIfReadOnly(); + _fillStateWithArgs = value; + } + } + + /// + /// Get a readonly version of this WIGStartInfo + /// + /// Returns a readonly reference to this WIGStartInfoRO + public WIGStartInfo AsReadOnly() + { + return new WIGStartInfo(this) { _readOnly = true }; + } } } diff --git a/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs new file mode 100644 index 0000000000..435a14bf2e --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public partial class WorkItem + { + #region WorkItemResult class + + private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult + { + /// + /// A back reference to the work item + /// + private readonly WorkItem _workItem; + + public WorkItemResult(WorkItem workItem) + { + _workItem = workItem; + } + + internal WorkItem GetWorkItem() + { + return _workItem; + } + + #region IWorkItemResult Members + + public bool IsCompleted + { + get + { + return _workItem.IsCompleted; + } + } + + public bool IsCanceled + { + get + { + return _workItem.IsCanceled; + } + } + + public object GetResult() + { + return _workItem.GetResult(Timeout.Infinite, true, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null); + } + + public object GetResult(TimeSpan timeout, bool exitContext) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + public object GetResult(out Exception e) + { + return _workItem.GetResult(Timeout.Infinite, true, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); + } + + public bool Cancel() + { + return Cancel(false); + } + + public bool Cancel(bool abortExecution) + { + return _workItem.Cancel(abortExecution); + } + + public object State + { + get + { + return _workItem._state; + } + } + + public WorkItemPriority WorkItemPriority + { + get + { + return _workItem._workItemInfo.WorkItemPriority; + } + } + + /// + /// Return the result, same as GetResult() + /// + public object Result + { + get { return GetResult(); } + } + + /// + /// Returns the exception if occured otherwise returns null. + /// This value is valid only after the work item completed, + /// before that it is always null. + /// + public object Exception + { + get { return _workItem._exception; } + } + + #endregion + + #region IInternalWorkItemResult Members + + public event WorkItemStateCallback OnWorkItemStarted + { + add + { + _workItem.OnWorkItemStarted += value; + } + remove + { + _workItem.OnWorkItemStarted -= value; + } + } + + + public event WorkItemStateCallback OnWorkItemCompleted + { + add + { + _workItem.OnWorkItemCompleted += value; + } + remove + { + _workItem.OnWorkItemCompleted -= value; + } + } + + #endregion + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return this; + } + + public IWorkItemResult GetWorkItemResultT() + { + return new WorkItemResultTWrapper(this); + } + + #endregion + } + + #endregion + + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItem.cs b/ThirdParty/SmartThreadPool/WorkItem.cs index d0c0524d10..185f10ce58 100644 --- a/ThirdParty/SmartThreadPool/WorkItem.cs +++ b/ThirdParty/SmartThreadPool/WorkItem.cs @@ -1,58 +1,13 @@ -// Ami Bar -// amibar@gmail.com - using System; using System.Threading; using System.Diagnostics; namespace Amib.Threading.Internal { - #region WorkItem Delegate - - /// - /// An internal delegate to call when the WorkItem starts or completes - /// - internal delegate void WorkItemStateCallback(WorkItem workItem); - - #endregion - - #region IInternalWorkItemResult interface - - public class CanceledWorkItemsGroup - { - public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup(); - - private bool _isCanceled = false; - public bool IsCanceled - { - get { return _isCanceled; } - set { _isCanceled = value; } - } - } - - internal interface IInternalWorkItemResult - { - event WorkItemStateCallback OnWorkItemStarted; - event WorkItemStateCallback OnWorkItemCompleted; - } - - #endregion - - #region IWorkItem interface - - public interface IWorkItem - { - - } - - #endregion - - #region WorkItem class - /// /// Holds a callback delegate and the state for that delegate. /// - public class WorkItem : IHasWorkItemPriority, IWorkItem + public partial class WorkItem : IHasWorkItemPriority { #region WorkItemState enum @@ -61,33 +16,57 @@ namespace Amib.Threading.Internal /// private enum WorkItemState { - InQueue, - InProgress, - Completed, - Canceled, + InQueue = 0, // Nexts: InProgress, Canceled + InProgress = 1, // Nexts: Completed, Canceled + Completed = 2, // Stays Completed + Canceled = 3, // Stays Canceled + } + + private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState) + { + bool valid = false; + + switch (currentState) + { + case WorkItemState.InQueue: + valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState); + break; + case WorkItemState.InProgress: + valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState); + break; + case WorkItemState.Completed: + case WorkItemState.Canceled: + // Cannot be changed + break; + default: + // Unknown state + Debug.Assert(false); + break; + } + + return valid; } #endregion - #region Member Variables - - public Thread currentThread; + #region Fields /// /// Callback delegate for the callback. /// - private WorkItemCallback _callback; + private readonly WorkItemCallback _callback; /// /// State with which to call the callback delegate. /// private object _state; +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) /// /// Stores the caller's context /// - private CallerThreadContext _callerContext; - + private readonly CallerThreadContext _callerContext; +#endif /// /// Holds the result of the mehtod /// @@ -117,12 +96,12 @@ namespace Amib.Threading.Internal /// /// Represents the result state of the work item /// - private WorkItemResult _workItemResult; + private readonly WorkItemResult _workItemResult; /// /// Work item info /// - private WorkItemInfo _workItemInfo; + private readonly WorkItemInfo _workItemInfo; /// /// Called when the WorkItem starts @@ -141,30 +120,41 @@ namespace Amib.Threading.Internal private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; /// - /// The work item group this work item belong to. - /// + /// A reference to an object that indicates whatever the + /// SmartThreadPool has been canceled /// - private IWorkItemsGroup _workItemsGroup; + private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; + + /// + /// The work item group this work item belong to. + /// + private readonly IWorkItemsGroup _workItemsGroup; + + /// + /// The thread that executes this workitem. + /// This field is available for the period when the work item is executed, before and after it is null. + /// + private Thread _executingThread; + + /// + /// The absulote time when the work item will be timeout + /// + private long _expirationTime; #region Performance Counter fields - /// - /// The time when the work items is queued. - /// Used with the performance counter. - /// - private DateTime _queuedTime; + + /// - /// The time when the work items starts its execution. - /// Used with the performance counter. + /// Stores how long the work item waited on the stp queue /// - private DateTime _beginProcessTime; + private Stopwatch _waitingOnQueueStopwatch; /// - /// The time when the work items ends its execution. - /// Used with the performance counter. + /// Stores how much time it took the work item to execute after it went out of the queue /// - private DateTime _endProcessTime; + private Stopwatch _processingStopwatch; #endregion @@ -174,17 +164,25 @@ namespace Amib.Threading.Internal public TimeSpan WaitingTime { - get + get { - return (_beginProcessTime - _queuedTime); + return _waitingOnQueueStopwatch.Elapsed; } } public TimeSpan ProcessTime { - get + get { - return (_endProcessTime - _beginProcessTime); + return _processingStopwatch.Elapsed; + } + } + + internal WorkItemInfo WorkItemInfo + { + get + { + return _workItemInfo; } } @@ -195,6 +193,8 @@ namespace Amib.Threading.Internal /// /// Initialize the callback holding object. /// + /// The workItemGroup of the workitem + /// The WorkItemInfo of te workitem /// Callback delegate for the callback. /// State with which to call the callback delegate. /// @@ -203,16 +203,18 @@ namespace Amib.Threading.Internal public WorkItem( IWorkItemsGroup workItemsGroup, WorkItemInfo workItemInfo, - WorkItemCallback callback, + WorkItemCallback callback, object state) { _workItemsGroup = workItemsGroup; _workItemInfo = workItemInfo; +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) { _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); } +#endif _callback = callback; _state = state; @@ -222,9 +224,18 @@ namespace Amib.Threading.Internal internal void Initialize() { + // The _workItemState is changed directly instead of using the SetWorkItemState + // method since we don't want to go throught IsValidStateTransition. _workItemState = WorkItemState.InQueue; + _workItemCompleted = null; _workItemCompletedRefCount = 0; + _waitingOnQueueStopwatch = new Stopwatch(); + _processingStopwatch = new Stopwatch(); + _expirationTime = + _workItemInfo.Timeout > 0 ? + DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond : + long.MaxValue; } internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) @@ -237,17 +248,16 @@ namespace Amib.Threading.Internal #region Methods - public CanceledWorkItemsGroup CanceledWorkItemsGroup + internal CanceledWorkItemsGroup CanceledWorkItemsGroup { - get - { - return _canceledWorkItemsGroup; - } + get { return _canceledWorkItemsGroup; } + set { _canceledWorkItemsGroup = value; } + } - set - { - _canceledWorkItemsGroup = value; - } + internal CanceledWorkItemsGroup CanceledSmartThreadPool + { + get { return _canceledSmartThreadPool; } + set { _canceledSmartThreadPool = value; } } /// @@ -259,9 +269,10 @@ namespace Amib.Threading.Internal /// public bool StartingWorkItem() { - _beginProcessTime = DateTime.Now; + _waitingOnQueueStopwatch.Stop(); + _processingStopwatch.Start(); - lock(this) + lock (this) { if (IsCanceled) { @@ -277,6 +288,9 @@ namespace Amib.Threading.Internal Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); + // No need for a lock yet, only after the state has changed to InProgress + _executingThread = Thread.CurrentThread; + SetWorkItemState(WorkItemState.InProgress); } @@ -291,7 +305,7 @@ namespace Amib.Threading.Internal CallToPostExecute currentCallToPostExecute = 0; // Execute the work item if we are in the correct state - switch(GetWorkItemState()) + switch (GetWorkItemState()) { case WorkItemState.InProgress: currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled; @@ -311,7 +325,7 @@ namespace Amib.Threading.Internal PostExecute(); } - _endProcessTime = DateTime.Now; + _processingStopwatch.Stop(); } internal void FireWorkItemCompleted() @@ -323,8 +337,21 @@ namespace Amib.Threading.Internal _workItemCompletedEvent(this); } } - catch // Ignore exceptions - {} + catch // Suppress exceptions + { } + } + + internal void FireWorkItemStarted() + { + try + { + if (null != _workItemStartedEvent) + { + _workItemStartedEvent(this); + } + } + catch // Suppress exceptions + { } } /// @@ -332,32 +359,70 @@ namespace Amib.Threading.Internal /// private void ExecuteWorkItem() { + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) CallerThreadContext ctc = null; if (null != _callerContext) { ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); CallerThreadContext.Apply(_callerContext); } +#endif Exception exception = null; object result = null; try { - result = _callback(_state); + try + { + result = _callback(_state); + } + catch (Exception e) + { + // Save the exception so we can rethrow it later + exception = e; + } + + // Remove the value of the execution thread, so it will be impossible to cancel the work item, + // since it is already completed. + // Cancelling a work item that already completed may cause the abortion of the next work item!!! + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + + if (null == executionThread) + { + // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException + Thread.Sleep(60 * 1000); + + // If after 1 minute this thread was not aborted then let it continue working. + } } - catch (Exception e) + // We must treat the ThreadAbortException or else it will be stored in the exception variable + catch (ThreadAbortException tae) { - // Save the exception so we can rethrow it later - exception = e; + tae.GetHashCode(); + // Check if the work item was cancelled + // If we got a ThreadAbortException and the STP is not shutting down, it means the + // work items was cancelled. + if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown) + { +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + Thread.ResetAbort(); +#endif + } } - + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) if (null != _callerContext) { CallerThreadContext.Apply(ctc); } +#endif - SetResult(result, exception); + if (!SmartThreadPool.IsWorkItemCanceled) + { + SetResult(result, exception); + } } /// @@ -369,9 +434,9 @@ namespace Amib.Threading.Internal { try { - _workItemInfo.PostExecuteWorkItemCallback(this._workItemResult); + _workItemInfo.PostExecuteWorkItemCallback(_workItemResult); } - catch (Exception e) + catch (Exception e) { Debug.Assert(null != e); } @@ -382,6 +447,8 @@ namespace Amib.Threading.Internal /// Set the result of the work item to return /// /// The result of the work item + /// The exception that was throw while the workitem executed, null + /// if there was no exception. internal void SetResult(object result, Exception exception) { _result = result; @@ -401,48 +468,48 @@ namespace Amib.Threading.Internal /// /// Wait for all work items to complete /// - /// Array of work item result objects + /// Array of work item result objects /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. /// /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. /// /// A cancel wait handle to interrupt the wait if needed /// - /// true when every work item in workItemResults has completed; otherwise false. + /// true when every work item in waitableResults has completed; otherwise false. /// internal static bool WaitAll( - IWorkItemResult [] workItemResults, + IWaitableResult[] waitableResults, int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) { - if (0 == workItemResults.Length) + if (0 == waitableResults.Length) { return true; } bool success; - WaitHandle [] waitHandles = new WaitHandle[workItemResults.Length];; - GetWaitHandles(workItemResults, waitHandles); + WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) { - success = WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); } else { success = true; int millisecondsLeft = millisecondsTimeout; - DateTime start = DateTime.Now; + Stopwatch stopwatch = Stopwatch.StartNew(); - WaitHandle [] whs; + WaitHandle[] whs; if (null != cancelWaitHandle) { - whs = new WaitHandle [] { null, cancelWaitHandle }; + whs = new WaitHandle[] { null, cancelWaitHandle }; } else { - whs = new WaitHandle [] { null }; + whs = new WaitHandle[] { null }; } bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); @@ -450,7 +517,7 @@ namespace Amib.Threading.Internal // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle // won't affect it. // Each iteration we update the time left for the timeout. - for(int i = 0; i < workItemResults.Length; ++i) + for (int i = 0; i < waitableResults.Length; ++i) { // WaitAny don't work with negative numbers if (!waitInfinitely && (millisecondsLeft < 0)) @@ -460,23 +527,22 @@ namespace Amib.Threading.Internal } whs[0] = waitHandles[i]; - int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext); - if((result > 0) || (WaitHandle.WaitTimeout == result)) + int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); + if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) { success = false; break; } - if(!waitInfinitely) + if (!waitInfinitely) { // Update the time left to wait - TimeSpan ts = DateTime.Now - start; - millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds; + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; } } } // Release the wait handles - ReleaseWaitHandles(workItemResults); + ReleaseWaitHandles(waitableResults); return success; } @@ -484,7 +550,7 @@ namespace Amib.Threading.Internal /// /// Waits for any of the work items in the specified array to complete, cancel, or timeout /// - /// Array of work item result objects + /// Array of work item result objects /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. /// /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. @@ -493,38 +559,38 @@ namespace Amib.Threading.Internal /// /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. /// - internal static int WaitAny( - IWorkItemResult [] workItemResults, + internal static int WaitAny( + IWaitableResult[] waitableResults, int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) { - WaitHandle [] waitHandles = null; + WaitHandle[] waitHandles; if (null != cancelWaitHandle) { - waitHandles = new WaitHandle[workItemResults.Length+1]; - GetWaitHandles(workItemResults, waitHandles); - waitHandles[workItemResults.Length] = cancelWaitHandle; + waitHandles = new WaitHandle[waitableResults.Length + 1]; + GetWaitHandles(waitableResults, waitHandles); + waitHandles[waitableResults.Length] = cancelWaitHandle; } else { - waitHandles = new WaitHandle[workItemResults.Length]; - GetWaitHandles(workItemResults, waitHandles); + waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); } - int result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); // Treat cancel as timeout if (null != cancelWaitHandle) { - if (result == workItemResults.Length) + if (result == waitableResults.Length) { - result = WaitHandle.WaitTimeout; + result = STPEventWaitHandle.WaitTimeout; } } - ReleaseWaitHandles(workItemResults); + ReleaseWaitHandles(waitableResults); return result; } @@ -532,16 +598,16 @@ namespace Amib.Threading.Internal /// /// Fill an array of wait handles with the work items wait handles. /// - /// An array of work item results + /// An array of work item results /// An array of wait handles to fill private static void GetWaitHandles( - IWorkItemResult [] workItemResults, - WaitHandle [] waitHandles) + IWaitableResult[] waitableResults, + WaitHandle[] waitHandles) { - for(int i = 0; i < workItemResults.Length; ++i) + for (int i = 0; i < waitableResults.Length; ++i) { - WorkItemResult wir = workItemResults[i] as WorkItemResult; - Debug.Assert(null != wir, "All workItemResults must be WorkItemResult objects"); + WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult; + Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects"); waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); } @@ -550,40 +616,64 @@ namespace Amib.Threading.Internal /// /// Release the work items' wait handles /// - /// An array of work item results - private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults) + /// An array of work item results + private static void ReleaseWaitHandles(IWaitableResult[] waitableResults) { - for(int i = 0; i < workItemResults.Length; ++i) + for (int i = 0; i < waitableResults.Length; ++i) { - WorkItemResult wir = workItemResults[i] as WorkItemResult; + WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult(); wir.GetWorkItem().ReleaseWaitHandle(); } } - #endregion - + #region Private Members private WorkItemState GetWorkItemState() { - if (_canceledWorkItemsGroup.IsCanceled) + lock (this) { - return WorkItemState.Canceled; - } - return _workItemState; + if (WorkItemState.Completed == _workItemState) + { + return _workItemState; + } + long nowTicks = DateTime.UtcNow.Ticks; + + if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime) + { + _workItemState = WorkItemState.Canceled; + } + + if (WorkItemState.InProgress == _workItemState) + { + return _workItemState; + } + + if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled) + { + return WorkItemState.Canceled; + } + + return _workItemState; + } } + + /// /// Sets the work item's state /// /// The state to set the work item to private void SetWorkItemState(WorkItemState workItemState) { - lock(this) + lock (this) { - _workItemState = workItemState; + if (IsValidStatesTransition(_workItemState, workItemState)) + { + _workItemState = workItemState; + } } } @@ -594,7 +684,7 @@ namespace Amib.Threading.Internal private void SignalComplete(bool canceled) { SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed); - lock(this) + lock (this) { // If someone is waiting then signal. if (null != _workItemCompleted) @@ -606,40 +696,93 @@ namespace Amib.Threading.Internal internal void WorkItemIsQueued() { - _queuedTime = DateTime.Now; + _waitingOnQueueStopwatch.Start(); } #endregion - + #region Members exposed by WorkItemResult /// /// Cancel the work item if it didn't start running yet. /// /// Returns true on success or false if the work item is in progress or already completed - private bool Cancel() + private bool Cancel(bool abortExecution) { - lock(this) +#if (_WINDOWS_CE) + if(abortExecution) { - switch(GetWorkItemState()) + throw new ArgumentOutOfRangeException("abortExecution", "WindowsCE doesn't support this feature"); + } +#endif + bool success = false; + bool signalComplete = false; + + lock (this) + { + switch (GetWorkItemState()) { case WorkItemState.Canceled: //Debug.WriteLine("Work item already canceled"); - return true; + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + // No need to signalComplete, because we already cancelled this work item + // so it already signaled its completion. + //signalComplete = true; + } + } + success = true; + break; case WorkItemState.Completed: - case WorkItemState.InProgress: //Debug.WriteLine("Work item cannot be canceled"); - return false; + break; + case WorkItemState.InProgress: + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + success = true; + signalComplete = true; + } + } + else + { + // ************************** + // Stock SmartThreadPool 2.2.3 sets these to true and relies on the thread to check the + // WorkItem cancellation status. However, OpenSimulator uses a different mechanism to notify + // scripts of co-operative termination and the abort code also relies on this method + // returning false in order to implement a small wait. + // + // Therefore, as was the case previously with STP, we will not signal successful cancellation + // here. It's possible that OpenSimulator code could be changed in the future to remove + // the need for this change. + // ************************** + success = false; + signalComplete = false; + } + break; case WorkItemState.InQueue: // Signal to the wait for completion that the work // item has been completed (canceled). There is no // reason to wait for it to get out of the queue - SignalComplete(true); + signalComplete = true; //Debug.WriteLine("Work item canceled"); - return true; + success = true; + break; + } + + if (signalComplete) + { + SignalComplete(true); } } - return false; + return success; } /// @@ -653,7 +796,7 @@ namespace Amib.Threading.Internal bool exitContext, WaitHandle cancelWaitHandle) { - Exception e = null; + Exception e; object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); if (null != e) { @@ -694,7 +837,7 @@ namespace Amib.Threading.Internal { WaitHandle wh = GetWaitHandle(); - bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext); + bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); ReleaseWaitHandle(); @@ -706,10 +849,10 @@ namespace Amib.Threading.Internal else { WaitHandle wh = GetWaitHandle(); - int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); + int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); ReleaseWaitHandle(); - switch(result) + switch (result) { case 0: // The work item signaled @@ -717,7 +860,7 @@ namespace Amib.Threading.Internal // work item (not the get result) break; case 1: - case WaitHandle.WaitTimeout: + case STPEventWaitHandle.WaitTimeout: throw new WorkItemTimeoutException("Work item timeout"); default: Debug.Assert(false); @@ -745,11 +888,11 @@ namespace Amib.Threading.Internal /// private WaitHandle GetWaitHandle() { - lock(this) + lock (this) { if (null == _workItemCompleted) { - _workItemCompleted = new ManualResetEvent(IsCompleted); + _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted); } ++_workItemCompletedRefCount; } @@ -758,7 +901,7 @@ namespace Amib.Threading.Internal private void ReleaseWaitHandle() { - lock(this) + lock (this) { if (null != _workItemCompleted) { @@ -779,10 +922,10 @@ namespace Amib.Threading.Internal { get { - lock(this) + lock (this) { WorkItemState workItemState = GetWorkItemState(); - return ((workItemState == WorkItemState.Completed) || + return ((workItemState == WorkItemState.Completed) || (workItemState == WorkItemState.Canceled)); } } @@ -795,7 +938,7 @@ namespace Amib.Threading.Internal { get { - lock(this) + lock (this) { return (GetWorkItemState() == WorkItemState.Canceled); } @@ -843,172 +986,6 @@ namespace Amib.Threading.Internal } } - - #region WorkItemResult class - - private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult - { - /// - /// A back reference to the work item - /// - private WorkItem _workItem; - - public WorkItemResult(WorkItem workItem) - { - _workItem = workItem; - } - - internal WorkItem GetWorkItem() - { - return _workItem; - } - - #region IWorkItemResult Members - - public bool IsCompleted - { - get - { - return _workItem.IsCompleted; - } - } - - public void Abort() - { - _workItem.Abort(); - } - - public bool IsCanceled - { - get - { - return _workItem.IsCanceled; - } - } - - public object GetResult() - { - return _workItem.GetResult(Timeout.Infinite, true, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null); - } - - public object GetResult(TimeSpan timeout, bool exitContext) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - public object GetResult(out Exception e) - { - return _workItem.GetResult(Timeout.Infinite, true, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); - } - - public bool Cancel() - { - return _workItem.Cancel(); - } - - public object State - { - get - { - return _workItem._state; - } - } - - public WorkItemPriority WorkItemPriority - { - get - { - return _workItem._workItemInfo.WorkItemPriority; - } - } - - /// - /// Return the result, same as GetResult() - /// - public object Result - { - get { return GetResult(); } - } - - /// - /// Returns the exception if occured otherwise returns null. - /// This value is valid only after the work item completed, - /// before that it is always null. - /// - public object Exception - { - get { return _workItem._exception; } - } - - #endregion - - #region IInternalWorkItemResult Members - - public event WorkItemStateCallback OnWorkItemStarted - { - add - { - _workItem.OnWorkItemStarted += value; - } - remove - { - _workItem.OnWorkItemStarted -= value; - } - } - - - public event WorkItemStateCallback OnWorkItemCompleted - { - add - { - _workItem.OnWorkItemCompleted += value; - } - remove - { - _workItem.OnWorkItemCompleted -= value; - } - } - - #endregion - } - - #endregion - public void DisposeOfState() { if (_workItemInfo.DisposeOfStateObjects) @@ -1021,15 +998,5 @@ namespace Amib.Threading.Internal } } } - - public void Abort() - { - lock (this) - { - if(currentThread != null) - currentThread.Abort(); - } - } } - #endregion } diff --git a/ThirdParty/SmartThreadPool/WorkItemFactory.cs b/ThirdParty/SmartThreadPool/WorkItemFactory.cs index dfcb54f7e1..16ccd81a54 100644 --- a/ThirdParty/SmartThreadPool/WorkItemFactory.cs +++ b/ThirdParty/SmartThreadPool/WorkItemFactory.cs @@ -1,148 +1,152 @@ -// Ami Bar -// amibar@gmail.com - using System; namespace Amib.Threading.Internal { - #region WorkItemFactory class + #region WorkItemFactory class - public class WorkItemFactory - { - /// - /// Create a new work item - /// + public class WorkItemFactory + { + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem /// Work item group start information - /// A callback to execute - /// Returns a work item + /// A callback to execute + /// The priority of the work item + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + WorkItemPriority workItemPriority) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// Work item info + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemInfo workItemInfo, + WorkItemCallback callback) + { + return CreateWorkItem( + workItemsGroup, + wigStartInfo, + workItemInfo, + callback, + null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item public static WorkItem CreateWorkItem( IWorkItemsGroup workItemsGroup, WIGStartInfo wigStartInfo, - WorkItemCallback callback) - { - return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// The priority of the work item - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - WorkItemPriority workItemPriority) - { - return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// Work item info - /// A callback to execute - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemInfo workItemInfo, - WorkItemCallback callback) - { - return CreateWorkItem( - workItemsGroup, - wigStartInfo, - workItemInfo, - callback, - null); - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state) - { - ValidateCallback(callback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - WorkItemPriority workItemPriority) - { - ValidateCallback(callback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = workItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemInfo workItemInfo, - WorkItemCallback callback, + WorkItemInfo workItemInfo, + WorkItemCallback callback, object state) { ValidateCallback(callback); @@ -151,183 +155,189 @@ namespace Amib.Threading.Internal WorkItem workItem = new WorkItem( workItemsGroup, new WorkItemInfo(workItemInfo), - callback, + callback, state); return workItem; } - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); - return workItem; - } + return workItem; + } - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = workItemPriority; + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); - return workItem; - } + return workItem; + } - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = callToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = callToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); - return workItem; - } + return workItem; + } - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = callToPostExecute; - workItemInfo.WorkItemPriority = workItemPriority; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = callToPostExecute; + workItemInfo.WorkItemPriority = workItemPriority; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } - private static void ValidateCallback(Delegate callback) - { - if(callback.GetInvocationList().Length > 1) - { - throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); - } - } - } + private static void ValidateCallback(Delegate callback) + { + if (callback != null && callback.GetInvocationList().Length > 1) + { + throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); + } + } + } - #endregion + #endregion } diff --git a/ThirdParty/SmartThreadPool/WorkItemInfo.cs b/ThirdParty/SmartThreadPool/WorkItemInfo.cs index c259339cc2..0d7fc85c65 100644 --- a/ThirdParty/SmartThreadPool/WorkItemInfo.cs +++ b/ThirdParty/SmartThreadPool/WorkItemInfo.cs @@ -1,102 +1,69 @@ -// Ami Bar -// amibar@gmail.com - namespace Amib.Threading { - #region WorkItemInfo class + #region WorkItemInfo class - /// - /// Summary description for WorkItemInfo. - /// - public class WorkItemInfo - { - /// - /// Use the caller's security context - /// - private bool _useCallerCallContext; + /// + /// Summary description for WorkItemInfo. + /// + public class WorkItemInfo + { + public WorkItemInfo() + { + UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; + UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; + DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; + CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute; + PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; + WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority; + } - /// - /// Use the caller's security context - /// - private bool _useCallerHttpContext; + public WorkItemInfo(WorkItemInfo workItemInfo) + { + UseCallerCallContext = workItemInfo.UseCallerCallContext; + UseCallerHttpContext = workItemInfo.UseCallerHttpContext; + DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects; + CallToPostExecute = workItemInfo.CallToPostExecute; + PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback; + WorkItemPriority = workItemInfo.WorkItemPriority; + Timeout = workItemInfo.Timeout; + } - /// - /// Dispose of the state object of a work item - /// - private bool _disposeOfStateObjects; + /// + /// Get/Set if to use the caller's security context + /// + public bool UseCallerCallContext { get; set; } - /// - /// The option to run the post execute - /// - private CallToPostExecute _callToPostExecute; + /// + /// Get/Set if to use the caller's HTTP context + /// + public bool UseCallerHttpContext { get; set; } - /// - /// A post execute callback to call when none is provided in - /// the QueueWorkItem method. - /// - private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; - - /// - /// The priority of the work item - /// - private WorkItemPriority _workItemPriority; + /// + /// Get/Set if to dispose of the state object of a work item + /// + public bool DisposeOfStateObjects { get; set; } - public WorkItemInfo() - { - _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; - _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; - _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; - _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; - _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; - _workItemPriority = SmartThreadPool.DefaultWorkItemPriority; - } + /// + /// Get/Set the run the post execute options + /// + public CallToPostExecute CallToPostExecute { get; set; } - public WorkItemInfo(WorkItemInfo workItemInfo) - { - _useCallerCallContext = workItemInfo._useCallerCallContext; - _useCallerHttpContext = workItemInfo._useCallerHttpContext; - _disposeOfStateObjects = workItemInfo._disposeOfStateObjects; - _callToPostExecute = workItemInfo._callToPostExecute; - _postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback; - _workItemPriority = workItemInfo._workItemPriority; - } + /// + /// Get/Set the post execute callback + /// + public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; } - public bool UseCallerCallContext - { - get { return _useCallerCallContext; } - set { _useCallerCallContext = value; } - } + /// + /// Get/Set the work item's priority + /// + public WorkItemPriority WorkItemPriority { get; set; } - public bool UseCallerHttpContext - { - get { return _useCallerHttpContext; } - set { _useCallerHttpContext = value; } - } + /// + /// Get/Set the work item's timout in milliseconds. + /// This is a passive timout. When the timout expires the work item won't be actively aborted! + /// + public long Timeout { get; set; } + } - public bool DisposeOfStateObjects - { - get { return _disposeOfStateObjects; } - set { _disposeOfStateObjects = value; } - } - - public CallToPostExecute CallToPostExecute - { - get { return _callToPostExecute; } - set { _callToPostExecute = value; } - } - - public PostExecuteWorkItemCallback PostExecuteWorkItemCallback - { - get { return _postExecuteWorkItemCallback; } - set { _postExecuteWorkItemCallback = value; } - } - - public WorkItemPriority WorkItemPriority - { - get { return _workItemPriority; } - set { _workItemPriority = value; } - } - } - - #endregion + #endregion } diff --git a/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs new file mode 100644 index 0000000000..d1eff95184 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs @@ -0,0 +1,128 @@ +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + #region WorkItemResultTWrapper class + + internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult + { + private readonly IWorkItemResult _workItemResult; + + public WorkItemResultTWrapper(IWorkItemResult workItemResult) + { + _workItemResult = workItemResult; + } + + #region IWorkItemResult Members + + public TResult GetResult() + { + return (TResult)_workItemResult.GetResult(); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(out Exception e) + { + return (TResult)_workItemResult.GetResult(out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); + } + + public bool IsCompleted + { + get { return _workItemResult.IsCompleted; } + } + + public bool IsCanceled + { + get { return _workItemResult.IsCanceled; } + } + + public object State + { + get { return _workItemResult.State; } + } + + public bool Cancel() + { + return _workItemResult.Cancel(); + } + + public bool Cancel(bool abortExecution) + { + return _workItemResult.Cancel(abortExecution); + } + + public WorkItemPriority WorkItemPriority + { + get { return _workItemResult.WorkItemPriority; } + } + + public TResult Result + { + get { return (TResult)_workItemResult.Result; } + } + + public object Exception + { + get { return (TResult)_workItemResult.Exception; } + } + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return _workItemResult.GetWorkItemResult(); + } + + public IWorkItemResult GetWorkItemResultT() + { + return (IWorkItemResult)this; + } + + #endregion + + #endregion + } + + #endregion + +} diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs index 01ac8ddaa6..d9d34ac8b2 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs @@ -1,6 +1,3 @@ -// Ami Bar -// amibar@gmail.com - using System; using System.Threading; using System.Runtime.CompilerServices; @@ -8,505 +5,357 @@ using System.Diagnostics; namespace Amib.Threading.Internal { - #region WorkItemsGroup class - /// - /// Summary description for WorkItemsGroup. - /// - public class WorkItemsGroup : IWorkItemsGroup - { - #region Private members + #region WorkItemsGroup class - private object _lock = new object(); - /// - /// Contains the name of this instance of SmartThreadPool. - /// Can be changed by the user. - /// - private string _name = "WorkItemsGroup"; + /// + /// Summary description for WorkItemsGroup. + /// + public class WorkItemsGroup : WorkItemsGroupBase + { + #region Private members + + private readonly object _lock = new object(); + + /// + /// A reference to the SmartThreadPool instance that created this + /// WorkItemsGroup. + /// + private readonly SmartThreadPool _stp; + + /// + /// The OnIdle event + /// + private event WorkItemsGroupIdleHandler _onIdle; /// - /// A reference to the SmartThreadPool instance that created this - /// WorkItemsGroup. + /// A flag to indicate if the Work Items Group is now suspended. /// - private SmartThreadPool _stp; + private bool _isSuspended; - /// - /// The OnIdle event - /// - private event WorkItemsGroupIdleHandler _onIdle; + /// + /// Defines how many work items of this WorkItemsGroup can run at once. + /// + private int _concurrency; - /// - /// Defines how many work items of this WorkItemsGroup can run at once. - /// - private int _concurrency; + /// + /// Priority queue to hold work items before they are passed + /// to the SmartThreadPool. + /// + private readonly PriorityQueue _workItemsQueue; - /// - /// Priority queue to hold work items before they are passed - /// to the SmartThreadPool. - /// - private PriorityQueue _workItemsQueue; + /// + /// Indicate how many work items are waiting in the SmartThreadPool + /// queue. + /// This value is used to apply the concurrency. + /// + private int _workItemsInStpQueue; - /// - /// Indicate how many work items are waiting in the SmartThreadPool - /// queue. - /// This value is used to apply the concurrency. - /// - private int _workItemsInStpQueue; + /// + /// Indicate how many work items are currently running in the SmartThreadPool. + /// This value is used with the Cancel, to calculate if we can send new + /// work items to the STP. + /// + private int _workItemsExecutingInStp = 0; - /// - /// Indicate how many work items are currently running in the SmartThreadPool. - /// This value is used with the Cancel, to calculate if we can send new - /// work items to the STP. - /// - private int _workItemsExecutingInStp = 0; + /// + /// WorkItemsGroup start information + /// + private readonly WIGStartInfo _workItemsGroupStartInfo; - /// - /// WorkItemsGroup start information - /// - private WIGStartInfo _workItemsGroupStartInfo; + /// + /// Signaled when all of the WorkItemsGroup's work item completed. + /// + //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); - /// - /// Signaled when all of the WorkItemsGroup's work item completed. - /// - private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + /// + /// A common object for all the work items that this work items group + /// generate so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - /// - /// A common object for all the work items that this work items group - /// generate so we can mark them to cancel in O(1) - /// - private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + #endregion - #endregion + #region Construction - #region Construction + public WorkItemsGroup( + SmartThreadPool stp, + int concurrency, + WIGStartInfo wigStartInfo) + { + if (concurrency <= 0) + { + throw new ArgumentOutOfRangeException( + "concurrency", +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + concurrency, +#endif + "concurrency must be greater than zero"); + } + _stp = stp; + _concurrency = concurrency; + _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly(); + _workItemsQueue = new PriorityQueue(); + Name = "WorkItemsGroup"; - public WorkItemsGroup( - SmartThreadPool stp, - int concurrency, - WIGStartInfo wigStartInfo) + // The _workItemsInStpQueue gets the number of currently executing work items, + // because once a work item is executing, it cannot be cancelled. + _workItemsInStpQueue = _workItemsExecutingInStp; + + _isSuspended = _workItemsGroupStartInfo.StartSuspended; + } + + #endregion + + #region WorkItemsGroupBase Overrides + + public override int Concurrency { - if (concurrency <= 0) - { - throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero"); - } - _stp = stp; - _concurrency = concurrency; - _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo); - _workItemsQueue = new PriorityQueue(); - - // The _workItemsInStpQueue gets the number of currently executing work items, - // because once a work item is executing, it cannot be cancelled. - _workItemsInStpQueue = _workItemsExecutingInStp; - } - - #endregion - - #region IWorkItemsGroup implementation - - /// - /// Get/Set the name of the SmartThreadPool instance - /// - public string Name - { - get - { - return _name; - } - + get { return _concurrency; } set { - _name = value; - } - } + Debug.Assert(value > 0); - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Wait for the thread pool to be idle - /// - public void WaitForIdle() - { - WaitForIdle(Timeout.Infinite); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(TimeSpan timeout) - { - return WaitForIdle((int)timeout.TotalMilliseconds); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(int millisecondsTimeout) - { - _stp.ValidateWorkItemsGroupWaitForIdle(this); - return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); - } - - public int WaitingCallbacks - { - get - { - return _workItemsQueue.Count; - } - } - - public event WorkItemsGroupIdleHandler OnIdle - { - add - { - _onIdle += value; - } - remove - { - _onIdle -= value; - } - } - - public void Cancel() - { - lock(_lock) - { - _canceledWorkItemsGroup.IsCanceled = true; - _workItemsQueue.Clear(); - _workItemsInStpQueue = 0; - _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - } - } - - public void Start() - { - lock (this) - { - if (!_workItemsGroupStartInfo.StartSuspended) + int diff = value - _concurrency; + _concurrency = value; + if (diff > 0) { - return; - } - _workItemsGroupStartInfo.StartSuspended = false; - } - - for(int i = 0; i < _concurrency; ++i) - { - EnqueueToSTPNextWorkItem(null, false); - } - } - - #endregion - - #region Private methods - - private void RegisterToWorkItemCompletion(IWorkItemResult wir) - { - IInternalWorkItemResult iwir = wir as IInternalWorkItemResult; - iwir.OnWorkItemStarted += new WorkItemStateCallback(OnWorkItemStartedCallback); - iwir.OnWorkItemCompleted += new WorkItemStateCallback(OnWorkItemCompletedCallback); - } - - public void OnSTPIsStarting() - { - lock (this) - { - if (_workItemsGroupStartInfo.StartSuspended) - { - return; + EnqueueToSTPNextNWorkItem(diff); } } - - for(int i = 0; i < _concurrency; ++i) + } + + public override int WaitingCallbacks + { + get { return _workItemsQueue.Count; } + } + + public override object[] GetStates() + { + lock (_lock) { - EnqueueToSTPNextWorkItem(null, false); + object[] states = new object[_workItemsQueue.Count]; + int i = 0; + foreach (WorkItem workItem in _workItemsQueue) + { + states[i] = workItem.GetWorkItemResult().State; + ++i; + } + return states; } } - private object FireOnIdle(object state) + /// + /// WorkItemsGroup start information + /// + public override WIGStartInfo WIGStartInfo { - FireOnIdleImpl(_onIdle); - return null; + get { return _workItemsGroupStartInfo; } } - [MethodImpl(MethodImplOptions.NoInlining)] - private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle) + /// + /// Start the Work Items Group if it was started suspended + /// + public override void Start() + { + // If the Work Items Group already started then quit + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); + } + + public override void Cancel(bool abortExecution) + { + lock (_lock) + { + _canceledWorkItemsGroup.IsCanceled = true; + _workItemsQueue.Clear(); + _workItemsInStpQueue = 0; + _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + } + + if (abortExecution) + { + _stp.CancelAbortWorkItemsGroup(this); + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) { - if(null == onIdle) + SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this); + return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); + } + + public override event WorkItemsGroupIdleHandler OnIdle + { + add { _onIdle += value; } + remove { _onIdle -= value; } + } + + #endregion + + #region Private methods + + private void RegisterToWorkItemCompletion(IWorkItemResult wir) + { + IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir; + iwir.OnWorkItemStarted += OnWorkItemStartedCallback; + iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback; + } + + public void OnSTPIsStarting() + { + if (_isSuspended) { return; } + + EnqueueToSTPNextNWorkItem(_concurrency); + } - Delegate[] delegates = onIdle.GetInvocationList(); - foreach(WorkItemsGroupIdleHandler eh in delegates) + public void EnqueueToSTPNextNWorkItem(int count) + { + for (int i = 0; i < count; ++i) { - try - { - eh(this); - } - // Ignore exceptions - catch{} + EnqueueToSTPNextWorkItem(null, false); } } - private void OnWorkItemStartedCallback(WorkItem workItem) + private object FireOnIdle(object state) + { + FireOnIdleImpl(_onIdle); + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle) + { + if(null == onIdle) + { + return; + } + + Delegate[] delegates = onIdle.GetInvocationList(); + foreach(WorkItemsGroupIdleHandler eh in delegates) + { + try + { + eh(this); + } + catch { } // Suppress exceptions + } + } + + private void OnWorkItemStartedCallback(WorkItem workItem) + { + lock(_lock) + { + ++_workItemsExecutingInStp; + } + } + + private void OnWorkItemCompletedCallback(WorkItem workItem) + { + EnqueueToSTPNextWorkItem(null, true); + } + + internal override void Enqueue(WorkItem workItem) { - lock(_lock) - { - ++_workItemsExecutingInStp; - } + EnqueueToSTPNextWorkItem(workItem); } - private void OnWorkItemCompletedCallback(WorkItem workItem) - { - EnqueueToSTPNextWorkItem(null, true); - } + private void EnqueueToSTPNextWorkItem(WorkItem workItem) + { + EnqueueToSTPNextWorkItem(workItem, false); + } - private void EnqueueToSTPNextWorkItem(WorkItem workItem) - { - EnqueueToSTPNextWorkItem(workItem, false); - } + private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue) + { + lock(_lock) + { + // Got here from OnWorkItemCompletedCallback() + if (decrementWorkItemsInStpQueue) + { + --_workItemsInStpQueue; - private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue) - { - lock(_lock) - { - // Got here from OnWorkItemCompletedCallback() - if (decrementWorkItemsInStpQueue) - { - --_workItemsInStpQueue; + if(_workItemsInStpQueue < 0) + { + _workItemsInStpQueue = 0; + } - if(_workItemsInStpQueue < 0) - { - _workItemsInStpQueue = 0; - } + --_workItemsExecutingInStp; - --_workItemsExecutingInStp; + if(_workItemsExecutingInStp < 0) + { + _workItemsExecutingInStp = 0; + } + } - if(_workItemsExecutingInStp < 0) - { - _workItemsExecutingInStp = 0; - } - } + // If the work item is not null then enqueue it + if (null != workItem) + { + workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup; - // If the work item is not null then enqueue it - if (null != workItem) - { - workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup; + RegisterToWorkItemCompletion(workItem.GetWorkItemResult()); + _workItemsQueue.Enqueue(workItem); + //_stp.IncrementWorkItemsCount(); - RegisterToWorkItemCompletion(workItem.GetWorkItemResult()); - _workItemsQueue.Enqueue(workItem); - //_stp.IncrementWorkItemsCount(); - - if ((1 == _workItemsQueue.Count) && - (0 == _workItemsInStpQueue)) - { - _stp.RegisterWorkItemsGroup(this); - Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle"); + if ((1 == _workItemsQueue.Count) && + (0 == _workItemsInStpQueue)) + { + _stp.RegisterWorkItemsGroup(this); + IsIdle = false; _isIdleWaitHandle.Reset(); - } - } + } + } - // If the work items queue of the group is empty than quit - if (0 == _workItemsQueue.Count) - { - if (0 == _workItemsInStpQueue) - { - _stp.UnregisterWorkItemsGroup(this); - Trace.WriteLine("WorkItemsGroup " + Name + " is idle"); + // If the work items queue of the group is empty than quit + if (0 == _workItemsQueue.Count) + { + if (0 == _workItemsInStpQueue) + { + _stp.UnregisterWorkItemsGroup(this); + IsIdle = true; _isIdleWaitHandle.Set(); - _stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle)); - } - return; - } + if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0) + { + _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle)); + } + } + return; + } - if (!_workItemsGroupStartInfo.StartSuspended) - { - if (_workItemsInStpQueue < _concurrency) - { - WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; - _stp.Enqueue(nextWorkItem, true); - ++_workItemsInStpQueue; - } - } - } - } + if (!_isSuspended) + { + if (_workItemsInStpQueue < _concurrency) + { + WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; + try + { + _stp.Enqueue(nextWorkItem); + } + catch (ObjectDisposedException e) + { + e.GetHashCode(); + // The STP has been shutdown + } - #endregion + ++_workItemsInStpQueue; + } + } + } + } + + #endregion } - #endregion + #endregion } diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs new file mode 100644 index 0000000000..27fae5e813 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs @@ -0,0 +1,471 @@ +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public abstract class WorkItemsGroupBase : IWorkItemsGroup + { + #region Private Fields + + /// + /// Contains the name of this instance of SmartThreadPool. + /// Can be changed by the user. + /// + private string _name = "WorkItemsGroupBase"; + + public WorkItemsGroupBase() + { + IsIdle = true; + } + + #endregion + + #region IWorkItemsGroup Members + + #region Public Methods + + /// + /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + #endregion + + #region Abstract Methods + + public abstract int Concurrency { get; set; } + public abstract int WaitingCallbacks { get; } + public abstract object[] GetStates(); + public abstract WIGStartInfo WIGStartInfo { get; } + public abstract void Start(); + public abstract void Cancel(bool abortExecution); + public abstract bool WaitForIdle(int millisecondsTimeout); + public abstract event WorkItemsGroupIdleHandler OnIdle; + + internal abstract void Enqueue(WorkItem workItem); + internal virtual void PreQueueWorkItem() { } + + #endregion + + #region Common Base Methods + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + public virtual void Cancel() + { + Cancel(false); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public void WaitForIdle() + { + WaitForIdle(Timeout.Infinite); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public bool WaitForIdle(TimeSpan timeout) + { + return WaitForIdle((int)timeout.TotalMilliseconds); + } + + /// + /// IsIdle is true when there are no work items running or queued. + /// + public bool IsIdle { get; protected set; } + + #endregion + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + #endregion + + #region QueueWorkItem(Action<...>) + + public IWorkItemResult QueueWorkItem(Action action) + { + return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + delegate + { + action.Invoke (); + return null; + }, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T arg) + { + return QueueWorkItem (action, arg, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2) + { + return QueueWorkItem (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3) + { + return QueueWorkItem (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority); + ; + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + return QueueWorkItem (action, arg1, arg2, arg3, arg4, + SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem ( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3, arg4); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + #endregion + + #region QueueWorkItem(Func<...>) + + public IWorkItemResult QueueWorkItem(Func func) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(); + }); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T arg) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3, arg4); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs index af5af07b80..e0bc9168ec 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs @@ -1,109 +1,151 @@ -// Ami Bar -// amibar@gmail.com - using System; +using System.Collections.Generic; using System.Threading; namespace Amib.Threading.Internal { - #region WorkItemsQueue class + #region WorkItemsQueue class - /// - /// WorkItemsQueue class. - /// - public class WorkItemsQueue : IDisposable - { - #region Member variables + /// + /// WorkItemsQueue class. + /// + public class WorkItemsQueue : IDisposable + { + #region Member variables - /// - /// Waiters queue (implemented as stack). - /// - private WaiterEntry _headWaiterEntry = new WaiterEntry(); + /// + /// Waiters queue (implemented as stack). + /// + private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); - /// - /// Waiters count - /// - private int _waitersCount = 0; + /// + /// Waiters count + /// + private int _waitersCount = 0; - /// - /// Work items queue - /// - private PriorityQueue _workItems = new PriorityQueue(); + /// + /// Work items queue + /// + private readonly PriorityQueue _workItems = new PriorityQueue(); /// /// Indicate that work items are allowed to be queued /// private bool _isWorkItemsQueueActive = true; + +#if (WINDOWS_PHONE) + private static readonly Dictionary _waiterEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); +#else + + [ThreadStatic] + private static WaiterEntry _waiterEntry; +#endif + + /// /// Each thread in the thread pool keeps its own waiter entry. /// - [ThreadStatic] - private static WaiterEntry _waiterEntry; + private static WaiterEntry CurrentWaiterEntry + { +#if (WINDOWS_PHONE) + get + { + lock (_waiterEntries) + { + WaiterEntry waiterEntry; + if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry)) + { + return waiterEntry; + } + } + return null; + } + set + { + lock (_waiterEntries) + { + _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + return Thread.GetData(_waiterEntrySlot) as WaiterEntry; + } + set + { + Thread.SetData(_waiterEntrySlot, value); + } +#else + get + { + return _waiterEntry; + } + set + { + _waiterEntry = value; + } +#endif + } /// - /// A flag that indicates if the WorkItemsQueue has been disposed. - /// + /// A flag that indicates if the WorkItemsQueue has been disposed. + /// private bool _isDisposed = false; - #endregion + #endregion - #region Public properties + #region Public properties /// /// Returns the current number of work items in the queue /// - public int Count - { - get - { - lock(this) - { - ValidateNotDisposed(); - return _workItems.Count; - } - } - } + public int Count + { + get + { + return _workItems.Count; + } + } - /// - /// Returns the current number of waiters - /// - public int WaitersCount - { - get - { - lock(this) - { - ValidateNotDisposed(); - return _waitersCount; - } - } - } + /// + /// Returns the current number of waiters + /// + public int WaitersCount + { + get + { + return _waitersCount; + } + } #endregion #region Public methods - /// - /// Enqueue a work item to the queue. - /// - public bool EnqueueWorkItem(WorkItem workItem) - { - // A work item cannot be null, since null is used in the - // WaitForWorkItem() method to indicate timeout or cancel - if (null == workItem) - { - throw new ArgumentNullException("workItem" , "workItem cannot be null"); - } + /// + /// Enqueue a work item to the queue. + /// + public bool EnqueueWorkItem(WorkItem workItem) + { + // A work item cannot be null, since null is used in the + // WaitForWorkItem() method to indicate timeout or cancel + if (null == workItem) + { + throw new ArgumentNullException("workItem" , "workItem cannot be null"); + } - bool enqueue = true; + bool enqueue = true; - // First check if there is a waiter waiting for work item. During - // the check, timed out waiters are ignored. If there is no - // waiter then the work item is queued. - lock(this) - { + // First check if there is a waiter waiting for work item. During + // the check, timed out waiters are ignored. If there is no + // waiter then the work item is queued. + lock(this) + { ValidateNotDisposed(); if (!_isWorkItemsQueueActive) @@ -111,56 +153,55 @@ namespace Amib.Threading.Internal return false; } - while(_waitersCount > 0) - { - // Dequeue a waiter. - WaiterEntry waiterEntry = PopWaiter(); + while(_waitersCount > 0) + { + // Dequeue a waiter. + WaiterEntry waiterEntry = PopWaiter(); - // Signal the waiter. On success break the loop - if (waiterEntry.Signal(workItem)) - { - enqueue = false; - break; - } - } + // Signal the waiter. On success break the loop + if (waiterEntry.Signal(workItem)) + { + enqueue = false; + break; + } + } - if (enqueue) - { - // Enqueue the work item - _workItems.Enqueue(workItem); - } - } + if (enqueue) + { + // Enqueue the work item + _workItems.Enqueue(workItem); + } + } return true; - } + } - /// - /// Waits for a work item or exits on timeout or cancel - /// - /// Timeout in milliseconds - /// Cancel wait handle - /// Returns true if the resource was granted - public WorkItem DequeueWorkItem( - int millisecondsTimeout, - WaitHandle cancelEvent) - { - /// This method cause the caller to wait for a work item. - /// If there is at least one waiting work item then the - /// method returns immidiately with true. - /// - /// If there are no waiting work items then the caller - /// is queued between other waiters for a work item to arrive. - /// - /// If a work item didn't come within millisecondsTimeout or - /// the user canceled the wait by signaling the cancelEvent - /// then the method returns false to indicate that the caller - /// didn't get a work item. + /// + /// Waits for a work item or exits on timeout or cancel + /// + /// Timeout in milliseconds + /// Cancel wait handle + /// Returns true if the resource was granted + public WorkItem DequeueWorkItem( + int millisecondsTimeout, + WaitHandle cancelEvent) + { + // This method cause the caller to wait for a work item. + // If there is at least one waiting work item then the + // method returns immidiately with it. + // + // If there are no waiting work items then the caller + // is queued between other waiters for a work item to arrive. + // + // If a work item didn't come within millisecondsTimeout or + // the user canceled the wait by signaling the cancelEvent + // then the method returns null to indicate that the caller + // didn't get a work item. - WaiterEntry waiterEntry = null; - WorkItem workItem = null; - - lock(this) - { + WaiterEntry waiterEntry; + WorkItem workItem = null; + lock (this) + { ValidateNotDisposed(); // If there are waiting work items then take one and return. @@ -169,80 +210,79 @@ namespace Amib.Threading.Internal workItem = _workItems.Dequeue() as WorkItem; return workItem; } - // No waiting work items ... - else - { - // Get the wait entry for the waiters queue - waiterEntry = GetThreadWaiterEntry(); - // Put the waiter with the other waiters - PushWaiter(waiterEntry); - } - } + // No waiting work items ... - // Prepare array of wait handle for the WaitHandle.WaitAny() - WaitHandle [] waitHandles = new WaitHandle [] { - waiterEntry.WaitHandle, - cancelEvent }; + // Get the waiter entry for the waiters queue + waiterEntry = GetThreadWaiterEntry(); - // Wait for an available resource, cancel event, or timeout. + // Put the waiter with the other waiters + PushWaiter(waiterEntry); + } - // During the wait we are supposes to exit the synchronization - // domain. (Placing true as the third argument of the WaitAny()) - // It just doesn't work, I don't know why, so I have lock(this) - // statments insted of one. + // Prepare array of wait handle for the WaitHandle.WaitAny() + WaitHandle [] waitHandles = new WaitHandle[] { + waiterEntry.WaitHandle, + cancelEvent }; - int index = WaitHandle.WaitAny( - waitHandles, - millisecondsTimeout, - true); + // Wait for an available resource, cancel event, or timeout. - lock(this) - { - // success is true if it got a work item. - bool success = (0 == index); + // During the wait we are supposes to exit the synchronization + // domain. (Placing true as the third argument of the WaitAny()) + // It just doesn't work, I don't know why, so I have two lock(this) + // statments instead of one. - // The timeout variable is used only for readability. - // (We treat cancel as timeout) - bool timeout = !success; + int index = STPEventWaitHandle.WaitAny( + waitHandles, + millisecondsTimeout, + true); - // On timeout update the waiterEntry that it is timed out - if (timeout) - { - // The Timeout() fails if the waiter has already been signaled - timeout = waiterEntry.Timeout(); + lock(this) + { + // success is true if it got a work item. + bool success = (0 == index); - // On timeout remove the waiter from the queue. - // Note that the complexity is O(1). - if(timeout) - { - RemoveWaiter(waiterEntry, false); - } + // The timeout variable is used only for readability. + // (We treat cancel as timeout) + bool timeout = !success; - // Again readability - success = !timeout; - } + // On timeout update the waiterEntry that it is timed out + if (timeout) + { + // The Timeout() fails if the waiter has already been signaled + timeout = waiterEntry.Timeout(); - // On success return the work item - if (success) - { - workItem = waiterEntry.WorkItem; + // On timeout remove the waiter from the queue. + // Note that the complexity is O(1). + if(timeout) + { + RemoveWaiter(waiterEntry, false); + } - if (null == workItem) - { - workItem = _workItems.Dequeue() as WorkItem; - } - } - } - // On failure return null. - return workItem; - } + // Again readability + success = !timeout; + } + + // On success return the work item + if (success) + { + workItem = waiterEntry.WorkItem; + + if (null == workItem) + { + workItem = _workItems.Dequeue() as WorkItem; + } + } + } + // On failure return null. + return workItem; + } /// /// Cleanup the work items queue, hence no more work /// items are allowed to be queue /// - protected virtual void Cleanup() + private void Cleanup() { lock(this) { @@ -271,301 +311,312 @@ namespace Amib.Threading.Internal // Tell the waiters that they were timed out. // It won't signal them to exit, but to ignore their // next work item. - while(_waitersCount > 0) + while(_waitersCount > 0) + { + WaiterEntry waiterEntry = PopWaiter(); + waiterEntry.Timeout(); + } + } + } + + public object[] GetStates() + { + lock (this) + { + object[] states = new object[_workItems.Count]; + int i = 0; + foreach (WorkItem workItem in _workItems) { - WaiterEntry waiterEntry = PopWaiter(); - waiterEntry.Timeout(); + states[i] = workItem.GetWorkItemResult().State; + ++i; } + return states; } } - #endregion + #endregion - #region Private methods + #region Private methods - /// - /// Returns the WaiterEntry of the current thread - /// - /// - /// In order to avoid creation and destuction of WaiterEntry - /// objects each thread has its own WaiterEntry object. - private WaiterEntry GetThreadWaiterEntry() - { - if (null == _waiterEntry) - { - _waiterEntry = new WaiterEntry(); - } - _waiterEntry.Reset(); - return _waiterEntry; - } + /// + /// Returns the WaiterEntry of the current thread + /// + /// + /// In order to avoid creation and destuction of WaiterEntry + /// objects each thread has its own WaiterEntry object. + private static WaiterEntry GetThreadWaiterEntry() + { + if (null == CurrentWaiterEntry) + { + CurrentWaiterEntry = new WaiterEntry(); + } + CurrentWaiterEntry.Reset(); + return CurrentWaiterEntry; + } - #region Waiters stack methods + #region Waiters stack methods - /// - /// Push a new waiter into the waiter's stack - /// - /// A waiter to put in the stack - public void PushWaiter(WaiterEntry newWaiterEntry) - { - // Remove the waiter if it is already in the stack and - // update waiter's count as needed - RemoveWaiter(newWaiterEntry, false); + /// + /// Push a new waiter into the waiter's stack + /// + /// A waiter to put in the stack + public void PushWaiter(WaiterEntry newWaiterEntry) + { + // Remove the waiter if it is already in the stack and + // update waiter's count as needed + RemoveWaiter(newWaiterEntry, false); - // If the stack is empty then newWaiterEntry is the new head of the stack - if (null == _headWaiterEntry._nextWaiterEntry) - { - _headWaiterEntry._nextWaiterEntry = newWaiterEntry; - newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + // If the stack is empty then newWaiterEntry is the new head of the stack + if (null == _headWaiterEntry._nextWaiterEntry) + { + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; - } - // If the stack is not empty then put newWaiterEntry as the new head - // of the stack. - else - { - // Save the old first waiter entry - WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + } + // If the stack is not empty then put newWaiterEntry as the new head + // of the stack. + else + { + // Save the old first waiter entry + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; // Update the links - _headWaiterEntry._nextWaiterEntry = newWaiterEntry; - newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; - newWaiterEntry._prevWaiterEntry = _headWaiterEntry; - oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; - } + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; + } - // Increment the number of waiters - ++_waitersCount; - } + // Increment the number of waiters + ++_waitersCount; + } - /// - /// Pop a waiter from the waiter's stack - /// - /// Returns the first waiter in the stack - private WaiterEntry PopWaiter() - { - // Store the current stack head - WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + /// + /// Pop a waiter from the waiter's stack + /// + /// Returns the first waiter in the stack + private WaiterEntry PopWaiter() + { + // Store the current stack head + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; - // Store the new stack head - WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; + // Store the new stack head + WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; - // Update the old stack head list links and decrement the number - // waiters. - RemoveWaiter(oldFirstWaiterEntry, true); + // Update the old stack head list links and decrement the number + // waiters. + RemoveWaiter(oldFirstWaiterEntry, true); - // Update the new stack head - _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; - if (null != newHeadWaiterEntry) - { - newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; - } + // Update the new stack head + _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; + if (null != newHeadWaiterEntry) + { + newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; + } - // Return the old stack head - return oldFirstWaiterEntry; - } + // Return the old stack head + return oldFirstWaiterEntry; + } - /// - /// Remove a waiter from the stack - /// - /// A waiter entry to remove - /// If true the waiter count is always decremented - private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) - { - // Store the prev entry in the list - WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; + /// + /// Remove a waiter from the stack + /// + /// A waiter entry to remove + /// If true the waiter count is always decremented + private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) + { + // Store the prev entry in the list + WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; - // Store the next entry in the list - WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; + // Store the next entry in the list + WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; - // A flag to indicate if we need to decrement the waiters count. - // If we got here from PopWaiter then we must decrement. - // If we got here from PushWaiter then we decrement only if - // the waiter was already in the stack. - bool decrementCounter = popDecrement; + // A flag to indicate if we need to decrement the waiters count. + // If we got here from PopWaiter then we must decrement. + // If we got here from PushWaiter then we decrement only if + // the waiter was already in the stack. + bool decrementCounter = popDecrement; - // Null the waiter's entry links - waiterEntry._prevWaiterEntry = null; - waiterEntry._nextWaiterEntry = null; + // Null the waiter's entry links + waiterEntry._prevWaiterEntry = null; + waiterEntry._nextWaiterEntry = null; - // If the waiter entry had a prev link then update it. - // It also means that the waiter is already in the list and we - // need to decrement the waiters count. - if (null != prevWaiterEntry) - { - prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; - decrementCounter = true; - } + // If the waiter entry had a prev link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != prevWaiterEntry) + { + prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; + decrementCounter = true; + } - // If the waiter entry had a next link then update it. - // It also means that the waiter is already in the list and we - // need to decrement the waiters count. - if (null != nextWaiterEntry) - { - nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; - decrementCounter = true; - } + // If the waiter entry had a next link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != nextWaiterEntry) + { + nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; + decrementCounter = true; + } - // Decrement the waiters count if needed - if (decrementCounter) - { - --_waitersCount; - } - } + // Decrement the waiters count if needed + if (decrementCounter) + { + --_waitersCount; + } + } - #endregion + #endregion - #endregion + #endregion - #region WaiterEntry class + #region WaiterEntry class - // A waiter entry in the _waiters queue. - public class WaiterEntry : IDisposable - { - #region Member variables + // A waiter entry in the _waiters queue. + public sealed class WaiterEntry : IDisposable + { + #region Member variables - /// - /// Event to signal the waiter that it got the work item. - /// - private AutoResetEvent _waitHandle = new AutoResetEvent(false); + /// + /// Event to signal the waiter that it got the work item. + /// + //private AutoResetEvent _waitHandle = new AutoResetEvent(false); + private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); - /// - /// Flag to know if this waiter already quited from the queue - /// because of a timeout. - /// - private bool _isTimedout = false; + /// + /// Flag to know if this waiter already quited from the queue + /// because of a timeout. + /// + private bool _isTimedout = false; - /// - /// Flag to know if the waiter was signaled and got a work item. - /// - private bool _isSignaled = false; + /// + /// Flag to know if the waiter was signaled and got a work item. + /// + private bool _isSignaled = false; /// /// A work item that passed directly to the waiter withou going /// through the queue /// - private WorkItem _workItem = null; + private WorkItem _workItem = null; private bool _isDisposed = false; - // Linked list members - internal WaiterEntry _nextWaiterEntry = null; - internal WaiterEntry _prevWaiterEntry = null; + // Linked list members + internal WaiterEntry _nextWaiterEntry = null; + internal WaiterEntry _prevWaiterEntry = null; - #endregion + #endregion - #region Construction + #region Construction - public WaiterEntry() - { - Reset(); - } - - #endregion + public WaiterEntry() + { + Reset(); + } + + #endregion - #region Public methods + #region Public methods - public WaitHandle WaitHandle - { - get { return _waitHandle; } - } + public WaitHandle WaitHandle + { + get { return _waitHandle; } + } - public WorkItem WorkItem - { - get - { - lock(this) - { - return _workItem; - } - } - } + public WorkItem WorkItem + { + get + { + return _workItem; + } + } - /// - /// Signal the waiter that it got a work item. - /// - /// Return true on success - /// The method fails if Timeout() preceded its call - public bool Signal(WorkItem workItem) - { - lock(this) - { - if (!_isTimedout) - { - _workItem = workItem; - _isSignaled = true; - _waitHandle.Set(); - return true; - } - } - return false; - } + /// + /// Signal the waiter that it got a work item. + /// + /// Return true on success + /// The method fails if Timeout() preceded its call + public bool Signal(WorkItem workItem) + { + lock(this) + { + if (!_isTimedout) + { + _workItem = workItem; + _isSignaled = true; + _waitHandle.Set(); + return true; + } + } + return false; + } - /// - /// Mark the wait entry that it has been timed out - /// - /// Return true on success - /// The method fails if Signal() preceded its call - public bool Timeout() - { - lock(this) - { - // Time out can happen only if the waiter wasn't marked as - // signaled - if (!_isSignaled) - { - // We don't remove the waiter from the queue, the DequeueWorkItem + /// + /// Mark the wait entry that it has been timed out + /// + /// Return true on success + /// The method fails if Signal() preceded its call + public bool Timeout() + { + lock(this) + { + // Time out can happen only if the waiter wasn't marked as + // signaled + if (!_isSignaled) + { + // We don't remove the waiter from the queue, the DequeueWorkItem // method skips _waiters that were timed out. - _isTimedout = true; - return true; + _isTimedout = true; + return true; + } + } + return false; + } + + /// + /// Reset the wait entry so it can be used again + /// + public void Reset() + { + _workItem = null; + _isTimedout = false; + _isSignaled = false; + _waitHandle.Reset(); + } + + /// + /// Free resources + /// + public void Close() + { + if (null != _waitHandle) + { + _waitHandle.Close(); + _waitHandle = null; + } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + lock (this) + { + if (!_isDisposed) + { + Close(); } - } - return false; - } - - /// - /// Reset the wait entry so it can be used again - /// - public void Reset() - { - _workItem = null; - _isTimedout = false; - _isSignaled = false; - _waitHandle.Reset(); - } - - /// - /// Free resources - /// - public void Close() - { - if (null != _waitHandle) - { - _waitHandle.Close(); - _waitHandle = null; - } - } - - #endregion - - #region IDisposable Members - - public void Dispose() - { - if (!_isDisposed) - { - Close(); _isDisposed = true; } } - ~WaiterEntry() - { - Dispose(); - } + #endregion + } - #endregion - } - - #endregion + #endregion #region IDisposable Members @@ -574,14 +625,8 @@ namespace Amib.Threading.Internal if (!_isDisposed) { Cleanup(); - _isDisposed = true; - GC.SuppressFinalize(this); } - } - - ~WorkItemsQueue() - { - Cleanup(); + _isDisposed = true; } private void ValidateNotDisposed() @@ -595,6 +640,6 @@ namespace Amib.Threading.Internal #endregion } - #endregion + #endregion } diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index 581c31d450..fee2a87b4b 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -21,18 +21,21 @@ ; * [[@]/][:] ; * [Startup] + ; Place to create a PID file + ; If no path if specified then a PID file is not created. + ; PIDFile = "/tmp/my.pid" - ; Plugin Registry Location - ; Set path to directory for plugin registry. Information - ; about the registered repositories and installed plugins - ; will be stored here - ; The Robust.exe process must have R/W access to the location - RegistryLocation = "." + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information + ; about the registered repositories and installed plugins + ; will be stored here + ; The Robust.exe process must have R/W access to the location + RegistryLocation = "." - ; Modular configurations - ; Set path to directory for modular ini files... - ; The Robust.exe process must have R/W access to the location - ConfigDirectory = "/home/opensim/etc/Configs" + ; Modular configurations + ; Set path to directory for modular ini files... + ; The Robust.exe process must have R/W access to the location + ConfigDirectory = "." [ServiceList] diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index b98132ecea..2d5aa8c87c 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -13,19 +13,21 @@ ; * [[@]/][:] ; * [Startup] + ; Place to create a PID file + ; If no path if specified then a PID file is not created. + ; PIDFile = "/tmp/my.pid" - ; Plugin Registry Location - ; Set path to directory for plugin registry. Information - ; about the registered repositories and installed plugins - ; will be stored here - ; The Robust.exe process must have R/W access to the location - RegistryLocation = "." + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information + ; about the registered repositories and installed plugins + ; will be stored here + ; The Robust.exe process must have R/W access to the location + RegistryLocation = "." - - ; Modular configurations - ; Set path to directory for modular ini files... - ; The Robust.exe process must have R/W access to the location - ConfigDirectory = "/home/opensim/etc/Configs" + ; Modular configurations + ; Set path to directory for modular ini files... + ; The Robust.exe process must have R/W access to the location + ConfigDirectory = "." [ServiceList] AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector" diff --git a/prebuild.xml b/prebuild.xml index 29d7c90da3..9fbe08b613 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -3055,6 +3055,7 @@ +