Merge branch 'master' of ssh://opensimulator.org/var/git/opensim
commit
a81ddf3d70
|
@ -115,6 +115,8 @@ namespace OpenSim.ApplicationPlugins.LoadRegions
|
|||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
List<IScene> createdScenes = new List<IScene>();
|
||||
|
||||
for (int i = 0; i < regionsToLoad.Length; i++)
|
||||
{
|
||||
IScene scene;
|
||||
|
@ -123,12 +125,18 @@ 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)
|
||||
foreach (IScene scene in createdScenes)
|
||||
{
|
||||
scene.Start();
|
||||
|
||||
m_newRegionCreatedHandler = OnNewRegionCreated;
|
||||
if (m_newRegionCreatedHandler != null)
|
||||
{
|
||||
|
@ -136,7 +144,6 @@ namespace OpenSim.ApplicationPlugins.LoadRegions
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -158,7 +158,7 @@ namespace OpenSim.Framework.Capabilities
|
|||
/// capabilities and their handler details.
|
||||
/// </summary>
|
||||
/// <param name="excludeSeed">If true, then exclude the seed cap.</param>
|
||||
public Hashtable GetCapsDetails(bool excludeSeed)
|
||||
public Hashtable GetCapsDetails(bool excludeSeed, List<string> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,5 +136,10 @@ namespace OpenSim.Framework
|
|||
ISceneObject DeserializeObject(string representation);
|
||||
|
||||
bool CheckClient(UUID agentID, System.Net.IPEndPoint ep);
|
||||
|
||||
/// <summary>
|
||||
/// Start the scene and associated scripts within it.
|
||||
/// </summary>
|
||||
void Start();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<string> validCaps = new List<string>();
|
||||
|
||||
foreach (OSD c in capsRequested)
|
||||
validCaps.Add(c.AsString());
|
||||
|
||||
Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps);
|
||||
|
||||
// Add the external too
|
||||
foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers)
|
||||
{
|
||||
if (!validCaps.Contains(kvp.Key))
|
||||
continue;
|
||||
|
||||
caps[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
string result = LLSDHelpers.SerialiseLLSDReply(caps);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -47,10 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
|
||||
/// <summary>
|
||||
private List<Scene> m_Scenelist = new List<Scene>();
|
||||
// private Dictionary<UUID, Scene> m_AgentRegions =
|
||||
// new Dictionary<UUID, Scene>();
|
||||
|
||||
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<IInventoryTransferModule>(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);
|
||||
}
|
||||
|
||||
|
@ -140,11 +132,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
client.OnInstantMessage += OnInstantMessage;
|
||||
}
|
||||
|
||||
// protected void OnSetRootAgentScene(UUID id, Scene scene)
|
||||
// {
|
||||
// m_AgentRegions[id] = scene;
|
||||
// }
|
||||
|
||||
private Scene FindClientScene(UUID agentId)
|
||||
{
|
||||
lock (m_Scenelist)
|
||||
|
@ -188,8 +175,8 @@ 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",
|
||||
m_log.DebugFormat(
|
||||
"[INVENTORY TRANSFER]: Inserting original folder {0} into agent {1}'s inventory",
|
||||
folderID, new UUID(im.toAgentID));
|
||||
|
||||
InventoryFolderBase folderCopy
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
|
|
@ -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<UUID>() { 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.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test user rejection of a given item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A rejected item still ends up in the user's trash folder.
|
||||
/// </remarks>
|
||||
[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<UUID>() { 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.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test user rejection of a given item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A rejected item still ends up in the user's trash folder.
|
||||
/// </remarks>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,20 +142,26 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
|
||||
scene.RegisterModuleInterface<IGridService>(this);
|
||||
|
||||
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)
|
||||
{
|
||||
if (!m_Enabled)
|
||||
return;
|
||||
|
||||
lock (m_LocalCache)
|
||||
{
|
||||
m_LocalCache[scene.RegionInfo.RegionID].Clear();
|
||||
m_LocalCache.Remove(scene.RegionInfo.RegionID);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegionLoaded(Scene scene)
|
||||
{
|
||||
|
@ -191,6 +197,9 @@ 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
|
||||
|
||||
lock (m_LocalCache)
|
||||
{
|
||||
foreach (RegionCache rcache in m_LocalCache.Values)
|
||||
{
|
||||
region = rcache.GetRegionByPosition(x, y);
|
||||
|
@ -199,6 +208,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
return region;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then try on this sim (may be a lookup in DB if this is using MySql).
|
||||
return m_GridService.GetRegionByPosition(scopeID, x, y);
|
||||
|
@ -245,6 +255,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
{
|
||||
System.Text.StringBuilder caps = new System.Text.StringBuilder();
|
||||
|
||||
lock (m_LocalCache)
|
||||
{
|
||||
foreach (KeyValuePair<UUID, RegionCache> kvp in m_LocalCache)
|
||||
{
|
||||
caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key);
|
||||
|
@ -252,6 +264,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the scene
|
||||
/// </summary>
|
||||
public void Start()
|
||||
/// <param name='startScripts'>
|
||||
/// Start the scripts within the scene.
|
||||
/// </param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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
|
|||
/// <returns></returns>
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -79,7 +79,7 @@ private sealed class BulletShapeUnman : BulletShape
|
|||
: 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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
|
|
@ -85,7 +85,7 @@ private sealed class BulletShapeXNA : BulletShape
|
|||
: 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)
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -381,8 +376,8 @@ public sealed class BSCharacter : BSPhysObject
|
|||
}
|
||||
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;
|
||||
|
@ -592,7 +587,7 @@ public sealed class BSCharacter : BSPhysObject
|
|||
public override float ForceBuoyancy {
|
||||
get { return _buoyancy; }
|
||||
set {
|
||||
PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BSPrimLinkable>();
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
@ -131,7 +120,7 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
// 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.
|
||||
|
@ -219,21 +190,21 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
|
||||
{
|
||||
// Find the physical instance of the child
|
||||
if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape))
|
||||
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))
|
||||
{
|
||||
cPrim.LinksetChildIndex = 0;
|
||||
}
|
||||
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;
|
||||
// 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;
|
||||
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);
|
||||
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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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<bool>("ShouldUseSingleConvexHullForPrims", "If true, use a single convex hull shape for physical prims",
|
||||
true,
|
||||
(s) => { return ShouldUseSingleConvexHullForPrims; },
|
||||
(s,v) => { ShouldUseSingleConvexHullForPrims = v; } ),
|
||||
|
||||
new ParameterDefn<int>("CrossingFailuresBeforeOutOfBounds", "How forgiving we are about getting into adjactent regions",
|
||||
5,
|
||||
|
|
|
@ -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); }
|
||||
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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
_mass = CalculateMass(); // changing the shape changes the mass
|
||||
CreateGeomAndObject(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", delegate()
|
||||
PhysScene.TaintedObject(inTaintTime, "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;
|
||||
|
@ -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
|
||||
|
|
|
@ -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,17 +109,24 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Quaternion ForceOrientation
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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("<p=");
|
||||
buff.Append(AddrString);
|
||||
if (physShapeInfo == null)
|
||||
{
|
||||
buff.Append(",noPhys");
|
||||
}
|
||||
else
|
||||
{
|
||||
buff.Append(",phy=");
|
||||
buff.Append(physShapeInfo.ToString());
|
||||
}
|
||||
buff.Append(",c=");
|
||||
buff.Append(referenceCount.ToString());
|
||||
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 */ }
|
||||
}
|
||||
|
||||
|
@ -157,18 +280,28 @@ public class BSShapeNative : BSShape
|
|||
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
|
||||
lock (physShapeInfo)
|
||||
{
|
||||
if (physShapeInfo.HasPhysicalShape)
|
||||
{
|
||||
physicsScene.DetailLog("{0},BSShapeNative.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim,
|
||||
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
|
||||
|
@ -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<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
|
||||
public static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
|
||||
|
||||
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,31 +362,64 @@ 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);
|
||||
|
||||
retMesh = new BSShapeMesh(newShape);
|
||||
BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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,
|
||||
false, // say it is not physical so a bounding box is not built
|
||||
|
@ -265,6 +428,12 @@ public class BSShapeMesh : BSShape
|
|||
|
||||
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<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
|
||||
public static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
|
||||
|
||||
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<ConvexResult> 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<OMV.Vector3> vertices = meshData.getVertexList();
|
||||
|
||||
//format conversion from IMesh format to DecompDesc format
|
||||
List<int> convIndices = new List<int>();
|
||||
List<float3> convVertices = new List<float3>();
|
||||
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<ConvexResult>();
|
||||
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)
|
||||
public static BSShape GetReference(BSScene physicsScene)
|
||||
{
|
||||
return new BSShapeNull();
|
||||
// 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<System.UInt64, BSShapeConvexHull> ConvexHulls = new Dictionary<System.UInt64, BSShapeConvexHull>();
|
||||
|
||||
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) { }
|
||||
}
|
||||
|
||||
// ============================================================================================================
|
||||
|
@ -365,6 +911,10 @@ public class BSShapeAvatar : BSShape
|
|||
{
|
||||
return new BSShapeNull();
|
||||
}
|
||||
public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
|
||||
{
|
||||
return new BSShapeNull();
|
||||
}
|
||||
public override void Dereference(BSScene physicsScene) { }
|
||||
|
||||
// From the front:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Vector3,BSTerrainPhys>();
|
||||
|
||||
// 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}",
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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().
|
||||
|
|
|
@ -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("<p=");
|
||||
buff.Append(AddrString);
|
||||
buff.Append(",s=");
|
||||
buff.Append(type.ToString());
|
||||
buff.Append(shapeType.ToString());
|
||||
buff.Append(",k=");
|
||||
buff.Append(shapeKey.ToString("X"));
|
||||
buff.Append(",n=");
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
PROBLEMS TO LOOK INTO
|
||||
=================================================
|
||||
Nebadon vehicle ride, get up, ride again. Second time vehicle does not act correctly.
|
||||
Have to rez new vehicle and delete the old to fix situation.
|
||||
Hitting RESET on Nebadon's vehicle while riding causes vehicle to get into odd
|
||||
position state where it will not settle onto ground properly, etc
|
||||
Two of Nebadon vehicles in a sim max the CPU. This is new.
|
||||
A sitting, active vehicle bobs up and down a small amount.
|
||||
|
||||
CURRENT PRIORITIES
|
||||
=================================================
|
||||
Use the HACD convex hull routine in Bullet rather than the C# version.
|
||||
|
@ -11,6 +20,8 @@ Deleting a linkset while standing on the root will leave the physical shape of t
|
|||
Linkset child rotations.
|
||||
Nebadon spiral tube has middle sections which are rotated wrong.
|
||||
Select linked spiral tube. Delink and note where the middle section ends up.
|
||||
Refarb compound linkset creation to create a pseudo-root for center-of-mass
|
||||
Let children change their shape to physical indendently and just add shapes to compound
|
||||
Vehicle angular vertical attraction
|
||||
vehicle angular banking
|
||||
Center-of-gravity
|
||||
|
@ -27,14 +38,13 @@ llLookAt
|
|||
Avatars walking up stairs (HALF DONE)
|
||||
Avatar movement
|
||||
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE)
|
||||
walking up stairs is not calibrated correctly (stairs out of Kepler cabin)
|
||||
walking up stairs is not calibrated correctly (stairs out of Kepler cabin) (DONE)
|
||||
avatar capsule rotation completed (NOT DONE - Bullet's capsule shape is not the solution)
|
||||
Vehicle script tuning/debugging
|
||||
Avanti speed script
|
||||
Weapon shooter script
|
||||
Move material definitions (friction, ...) into simulator.
|
||||
Add material densities to the material types.
|
||||
Terrain detail: double terrain mesh detail
|
||||
One sided meshes? Should terrain be built into a closed shape?
|
||||
When meshes get partially wedged into the terrain, they cannot push themselves out.
|
||||
It is possible that Bullet processes collisions whether entering or leaving a mesh.
|
||||
|
@ -42,6 +52,9 @@ One sided meshes? Should terrain be built into a closed shape?
|
|||
|
||||
VEHICLES TODO LIST:
|
||||
=================================================
|
||||
LINEAR_MOTOR_DIRECTION values should be clamped to reasonable numbers.
|
||||
What are the limits in SL?
|
||||
Same for other velocity settings.
|
||||
UBit improvements to remove rubber-banding of avatars sitting on vehicle child prims:
|
||||
https://github.com/UbitUmarov/Ubit-opensim
|
||||
Border crossing with linked vehicle causes crash
|
||||
|
@ -347,4 +360,5 @@ Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE.
|
|||
DONE 20130120: BulletSim properly applies force in vehicle relative coordinates.
|
||||
Nebadon vehicles turning funny in arena (DONE)
|
||||
Lock axis (DONE 20130401)
|
||||
Terrain detail: double terrain mesh detail (DONE)
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
|
|||
public interface IScriptWorkItem
|
||||
{
|
||||
bool Cancel();
|
||||
void Abort();
|
||||
bool Abort();
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the work item to complete.
|
||||
|
|
|
@ -564,9 +564,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
|||
|
||||
public bool Stop(int timeout)
|
||||
{
|
||||
// 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);
|
||||
if (DebugLevel >= 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;
|
||||
|
||||
|
|
|
@ -483,7 +483,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
|||
/// <param name="instance"></param>
|
||||
/// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</param>
|
||||
private void HandleScriptsAction<TKey>(
|
||||
string[] cmdparams, Action<IScriptInstance> action, Func<IScriptInstance, TKey> keySelector)
|
||||
string[] cmdparams, Action<IScriptInstance> action, System.Func<IScriptInstance, TKey> 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -217,13 +217,38 @@ namespace OpenSim.Tests.Common
|
|||
/// </returns>
|
||||
public static InventoryFolderBase CreateInventoryFolder(
|
||||
IInventoryService inventoryService, UUID userId, string path, bool useExistingFolders)
|
||||
{
|
||||
return CreateInventoryFolder(inventoryService, userId, UUID.Random(), path, useExistingFolders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create inventory folders starting from the user's root folder.
|
||||
/// </summary>
|
||||
/// <param name="inventoryService"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="folderId"></param>
|
||||
/// <param name="path">
|
||||
/// The folders to create. Multiple folders can be specified on a path delimited by the PATH_DELIMITER
|
||||
/// </param>
|
||||
/// <param name="useExistingFolders">
|
||||
/// 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.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// 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.
|
||||
/// </returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -235,6 +260,7 @@ namespace OpenSim.Tests.Common
|
|||
/// TODO: May need to make it an option to create duplicate folders.
|
||||
/// </remarks>
|
||||
/// <param name="inventoryService"></param>
|
||||
/// <param name="folderId">ID of the folder to create</param>
|
||||
/// <param name="parentFolder"></param>
|
||||
/// <param name="path">
|
||||
/// 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.
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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<RegionInfo, Vector3, Vector3> OnReceivedMoveAgentIntoRegion;
|
||||
public event Action<ulong, IPEndPoint> OnTestClientInformClientOfNeighbour;
|
||||
public event Action<GridInstantMessage> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emulate sending an IM from the viewer to the simulator.
|
||||
/// </summary>
|
||||
/// <param name='im'></param>
|
||||
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<string> message)
|
||||
|
|
|
@ -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<XInventoryFolder> origFolders
|
||||
= Get<XInventoryFolder>(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(); }
|
||||
}
|
||||
|
|
|
@ -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\<configuration>. 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("")]
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
@ -6,7 +9,7 @@ using System.Web;
|
|||
using System.Runtime.Remoting.Messaging;
|
||||
|
||||
|
||||
namespace Amib.Threading
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region CallerThreadContext class
|
||||
|
||||
|
@ -19,10 +22,10 @@ namespace Amib.Threading
|
|||
#region Prepare reflection information
|
||||
|
||||
// Cached type information.
|
||||
private static MethodInfo getLogicalCallContextMethodInfo =
|
||||
private static readonly MethodInfo getLogicalCallContextMethodInfo =
|
||||
typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
private static MethodInfo setLogicalCallContextMethodInfo =
|
||||
private static readonly MethodInfo setLogicalCallContextMethodInfo =
|
||||
typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
private static string HttpContextSlotName = GetHttpContextSlotName();
|
||||
|
@ -32,8 +35,10 @@ namespace Amib.Threading
|
|||
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";
|
||||
}
|
||||
|
||||
|
@ -41,8 +46,8 @@ namespace Amib.Threading
|
|||
|
||||
#region Private fields
|
||||
|
||||
private HttpContext _httpContext = null;
|
||||
private LogicalCallContext _callContext = null;
|
||||
private HttpContext _httpContext;
|
||||
private LogicalCallContext _callContext;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -122,102 +127,12 @@ namespace Amib.Threading
|
|||
// Restore HttpContext
|
||||
if (callerThreadContext._httpContext != null)
|
||||
{
|
||||
CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
|
||||
HttpContext.Current = callerThreadContext._httpContext;
|
||||
//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
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public class CallerThreadContext
|
||||
{
|
||||
private CultureInfo _culture = null;
|
||||
private CultureInfo _cultureUI = null;
|
||||
private IPrincipal _principal;
|
||||
private System.Runtime.Remoting.Contexts.Context _context;
|
||||
|
||||
private static FieldInfo _fieldInfo = GetFieldInfo();
|
||||
|
||||
private static FieldInfo GetFieldInfo()
|
||||
{
|
||||
Type threadType = typeof(Thread);
|
||||
return threadType.GetField(
|
||||
"m_Context",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
private CallerThreadContext()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the current thread context
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static CallerThreadContext Capture()
|
||||
{
|
||||
CallerThreadContext callerThreadContext = new CallerThreadContext();
|
||||
|
||||
Thread thread = Thread.CurrentThread;
|
||||
callerThreadContext._culture = thread.CurrentCulture;
|
||||
callerThreadContext._cultureUI = thread.CurrentUICulture;
|
||||
callerThreadContext._principal = Thread.CurrentPrincipal;
|
||||
callerThreadContext._context = Thread.CurrentContext;
|
||||
return callerThreadContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the thread context stored earlier
|
||||
/// </summary>
|
||||
/// <param name="callerThreadContext"></param>
|
||||
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)
|
||||
{
|
||||
_fieldInfo.SetValue(
|
||||
Thread.CurrentThread,
|
||||
callerThreadContext._context);
|
||||
}
|
||||
* /
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
#if (_WINDOWS_CE)
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// 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!
|
||||
/// </summary>
|
||||
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
|
|
@ -0,0 +1,82 @@
|
|||
using System.Threading;
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static class EventWaitHandleFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new AutoResetEvent object
|
||||
/// </summary>
|
||||
/// <returns>Return a new AutoResetEvent object</returns>
|
||||
public static AutoResetEvent CreateAutoResetEvent()
|
||||
{
|
||||
AutoResetEvent waitHandle = new AutoResetEvent(false);
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
ReplaceEventHandle(waitHandle, false, false);
|
||||
#endif
|
||||
|
||||
return waitHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new ManualResetEvent object
|
||||
/// </summary>
|
||||
/// <returns>Return a new ManualResetEvent object</returns>
|
||||
public static ManualResetEvent CreateManualResetEvent(bool initialState)
|
||||
{
|
||||
ManualResetEvent waitHandle = new ManualResetEvent(initialState);
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
ReplaceEventHandle(waitHandle, true, initialState);
|
||||
#endif
|
||||
|
||||
return waitHandle;
|
||||
}
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
|
||||
/// <summary>
|
||||
/// Replace the event handle
|
||||
/// </summary>
|
||||
/// <param name="waitHandle">The WaitHandle object which its handle needs to be replaced.</param>
|
||||
/// <param name="manualReset">Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false)</param>
|
||||
/// <param name="initialState">The initial state of the event</param>
|
||||
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
|
||||
|
||||
}
|
||||
}
|
|
@ -1,32 +1,82 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
#if !(_WINDOWS_CE)
|
||||
using System.Runtime.Serialization;
|
||||
#endif
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region Exceptions
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been canceled
|
||||
/// </summary>
|
||||
public sealed partial class WorkItemCancelException : Exception
|
||||
{
|
||||
public WorkItemCancelException()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message, Exception e)
|
||||
: base(message, e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
public sealed partial class WorkItemTimeoutException : Exception
|
||||
{
|
||||
public WorkItemTimeoutException()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemTimeoutException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemTimeoutException(string message, Exception e)
|
||||
: base(message, e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been canceled
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class WorkItemCancelException : ApplicationException
|
||||
public sealed partial class WorkItemCancelException
|
||||
{
|
||||
public WorkItemCancelException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message, Exception e) : base(message, e)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc)
|
||||
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
|
||||
/// </summary>
|
||||
[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
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class WorkItemResultException : ApplicationException
|
||||
public sealed partial class WorkItemResultException
|
||||
{
|
||||
public WorkItemResultException() : base()
|
||||
public WorkItemResultException(SerializationInfo si, StreamingContext sc)
|
||||
: base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public WorkItemResultException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemResultException(string message, Exception e) : base(message, e)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -20,16 +17,39 @@ namespace Amib.Threading
|
|||
/// <param name="wir">The work item result object</param>
|
||||
public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call after the WorkItemCallback completed
|
||||
/// </summary>
|
||||
/// <param name="wir">The work item result object</param>
|
||||
public delegate void PostExecuteWorkItemCallback<TResult>(IWorkItemResult<TResult> wir);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call when a WorkItemsGroup becomes idle
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
|
||||
public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call after a thread is created, but before
|
||||
/// it's first use.
|
||||
/// </summary>
|
||||
public delegate void ThreadInitializationHandler();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call when a thread is about to exit, after
|
||||
/// it is no longer belong to the pool.
|
||||
/// </summary>
|
||||
public delegate void ThreadTerminationHandler();
|
||||
|
||||
#endregion
|
||||
|
||||
#region WorkItem Priority
|
||||
|
||||
/// <summary>
|
||||
/// Defines the availeable priorities of a work item.
|
||||
/// The higher the priority a work item has, the sooner
|
||||
/// it will be executed.
|
||||
/// </summary>
|
||||
public enum WorkItemPriority
|
||||
{
|
||||
Lowest,
|
||||
|
@ -41,19 +61,11 @@ namespace Amib.Threading
|
|||
|
||||
#endregion
|
||||
|
||||
#region IHasWorkItemPriority interface
|
||||
|
||||
public interface IHasWorkItemPriority
|
||||
{
|
||||
WorkItemPriority WorkItemPriority { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorkItemsGroup interface
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemsGroup interface
|
||||
/// Created by SmartThreadPool.CreateWorkItemsGroup()
|
||||
/// </summary>
|
||||
public interface IWorkItemsGroup
|
||||
{
|
||||
|
@ -62,27 +74,293 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
string Name { 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);
|
||||
/// <summary>
|
||||
/// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
|
||||
/// </summary>
|
||||
int Concurrency { get; set; }
|
||||
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
|
||||
/// <summary>
|
||||
/// Get the number of work items waiting in the queue.
|
||||
/// </summary>
|
||||
int WaitingCallbacks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get an array with all the state objects of the currently running items.
|
||||
/// The array represents a snap shot and impact performance.
|
||||
/// </summary>
|
||||
object[] GetStates();
|
||||
|
||||
/// <summary>
|
||||
/// Get the WorkItemsGroup start information
|
||||
/// </summary>
|
||||
WIGStartInfo WIGStartInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts to execute work items
|
||||
/// </summary>
|
||||
void Start();
|
||||
|
||||
/// <summary>
|
||||
/// Cancel all the work items.
|
||||
/// Same as Cancel(false)
|
||||
/// </summary>
|
||||
void Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Cancel all work items using thread abortion
|
||||
/// </summary>
|
||||
/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
|
||||
void Cancel(bool abortExecution);
|
||||
|
||||
/// <summary>
|
||||
/// Wait for all work item to complete.
|
||||
/// </summary>
|
||||
void WaitForIdle();
|
||||
|
||||
/// <summary>
|
||||
/// Wait for all work item to complete, until timeout expired
|
||||
/// </summary>
|
||||
/// <param name="timeout">How long to wait for the work items to complete</param>
|
||||
/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
|
||||
bool WaitForIdle(TimeSpan timeout);
|
||||
|
||||
/// <summary>
|
||||
/// Wait for all work item to complete, until timeout expired
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout">How long to wait for the work items to complete in milliseconds</param>
|
||||
/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
|
||||
bool WaitForIdle(int millisecondsTimeout);
|
||||
|
||||
int WaitingCallbacks { get; }
|
||||
/// <summary>
|
||||
/// IsIdle is true when there are no work items running or queued.
|
||||
/// </summary>
|
||||
bool IsIdle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
event WorkItemsGroupIdleHandler OnIdle;
|
||||
|
||||
void Cancel();
|
||||
void Start();
|
||||
#region QueueWorkItem
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="workItemPriority">The priority of the work item</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item info</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
|
||||
|
||||
#endregion
|
||||
|
||||
#region QueueWorkItem(Action<...>)
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem(Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);
|
||||
|
||||
#endregion
|
||||
|
||||
#region QueueWorkItem(Func<...>)
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<TResult>(Func<TResult> func);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> func, T arg);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 arg1, T2 arg2);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -92,9 +370,24 @@ namespace Amib.Threading
|
|||
[Flags]
|
||||
public enum CallToPostExecute
|
||||
{
|
||||
/// <summary>
|
||||
/// Never call to the PostExecute call back
|
||||
/// </summary>
|
||||
Never = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// Call to the PostExecute only when the work item is cancelled
|
||||
/// </summary>
|
||||
WhenWorkItemCanceled = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Call to the PostExecute only when the work item is not cancelled
|
||||
/// </summary>
|
||||
WhenWorkItemNotCanceled = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Always call to the PostExecute
|
||||
/// </summary>
|
||||
Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
|
||||
}
|
||||
|
||||
|
@ -103,16 +396,43 @@ namespace Amib.Threading
|
|||
#region IWorkItemResult interface
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemResult interface
|
||||
/// The common interface of IWorkItemResult and IWorkItemResult<T>
|
||||
/// </summary>
|
||||
public interface IWorkItemResult
|
||||
public interface IWaitableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// This method intent is for internal use.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IWorkItemResult GetWorkItemResult();
|
||||
|
||||
/// <summary>
|
||||
/// This method intent is for internal use.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IWorkItemResult<TResult> GetWorkItemResultT<TResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemResult interface.
|
||||
/// Created when a WorkItemCallback work item is queued.
|
||||
/// </summary>
|
||||
public interface IWorkItemResult : IWorkItemResult<object>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemResult<TResult> interface.
|
||||
/// Created when a Func<TResult> work item is queued.
|
||||
/// </summary>
|
||||
public interface IWorkItemResult<TResult> : IWaitableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
object GetResult();
|
||||
TResult GetResult();
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
|
@ -120,7 +440,7 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext);
|
||||
|
||||
|
@ -130,12 +450,10 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext);
|
||||
|
||||
void Abort();
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
|
@ -148,7 +466,7 @@ namespace Amib.Threading
|
|||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle);
|
||||
|
@ -160,7 +478,7 @@ namespace Amib.Threading
|
|||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle);
|
||||
|
@ -171,16 +489,18 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
object GetResult(out Exception e);
|
||||
TResult GetResult(out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout"></param>
|
||||
/// <param name="exitContext"></param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
out Exception e);
|
||||
|
@ -189,10 +509,12 @@ namespace Amib.Threading
|
|||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <param name="exitContext"></param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
out Exception e);
|
||||
|
@ -210,7 +532,7 @@ namespace Amib.Threading
|
|||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle,
|
||||
|
@ -221,10 +543,13 @@ namespace Amib.Threading
|
|||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// <param name="cancelWaitHandle"></param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="exitContext"></param>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle,
|
||||
|
@ -241,16 +566,30 @@ namespace Amib.Threading
|
|||
bool IsCanceled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
|
||||
/// Gets the user-defined object that contains context data
|
||||
/// for the work item method.
|
||||
/// </summary>
|
||||
object State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the work item if it didn't start running yet.
|
||||
/// Same as Cancel(false).
|
||||
/// </summary>
|
||||
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
|
||||
bool Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="abortExecution">When true send an AbortException to the executing thread.</param>
|
||||
/// <returns>Returns true if the work item was not completed, otherwise false.</returns>
|
||||
bool Cancel(bool abortExecution);
|
||||
|
||||
/// <summary>
|
||||
/// Get the work item's priority
|
||||
/// </summary>
|
||||
|
@ -259,7 +598,7 @@ namespace Amib.Threading
|
|||
/// <summary>
|
||||
/// Return the result, same as GetResult()
|
||||
/// </summary>
|
||||
object Result { get; }
|
||||
TResult Result { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception if occured otherwise returns null.
|
||||
|
@ -267,5 +606,23 @@ namespace Amib.Threading
|
|||
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, T2>(T1 arg1, T2 arg2);
|
||||
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
|
||||
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
public delegate TResult Func<TResult>();
|
||||
public delegate TResult Func<T, TResult>(T arg1);
|
||||
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
|
||||
public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
|
||||
public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// An internal delegate to call when the WorkItem starts or completes
|
||||
/// </summary>
|
||||
internal delegate void WorkItemStateCallback(WorkItem workItem);
|
||||
|
||||
internal interface IInternalWorkItemResult
|
||||
{
|
||||
event WorkItemStateCallback OnWorkItemStarted;
|
||||
event WorkItemStateCallback OnWorkItemCompleted;
|
||||
}
|
||||
|
||||
internal interface IInternalWaitableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// This method is intent for internal use.
|
||||
/// </summary>
|
||||
IWorkItemResult GetWorkItemResult();
|
||||
}
|
||||
|
||||
public interface IHasWorkItemPriority
|
||||
{
|
||||
WorkItemPriority WorkItemPriority { get; }
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
|
@ -25,17 +23,17 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Work items queues. There is one for each type of priority
|
||||
/// </summary>
|
||||
private Queue [] _queues = new Queue[_queuesCount];
|
||||
private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];
|
||||
|
||||
/// <summary>
|
||||
/// The total number of work items within the queues
|
||||
/// </summary>
|
||||
private int _workItemsCount = 0;
|
||||
private int _workItemsCount;
|
||||
|
||||
/// <summary>
|
||||
/// Use with IEnumerable interface
|
||||
/// </summary>
|
||||
private int _version = 0;
|
||||
private int _version;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -45,7 +43,7 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
for(int i = 0; i < _queues.Length; ++i)
|
||||
{
|
||||
_queues[i] = new Queue();
|
||||
_queues[i] = new LinkedList<IHasWorkItemPriority>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +63,7 @@ namespace Amib.Threading.Internal
|
|||
Debug.Assert(queueIndex >= 0);
|
||||
Debug.Assert(queueIndex < _queuesCount);
|
||||
|
||||
_queues[queueIndex].Enqueue(workItem);
|
||||
_queues[queueIndex].AddLast(workItem);
|
||||
++_workItemsCount;
|
||||
++_version;
|
||||
}
|
||||
|
@ -82,7 +80,8 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
int queueIndex = GetNextNonEmptyQueue(-1);
|
||||
Debug.Assert(queueIndex >= 0);
|
||||
workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority;
|
||||
workItem = _queues[queueIndex].First.Value;
|
||||
_queues[queueIndex].RemoveFirst();
|
||||
Debug.Assert(null != workItem);
|
||||
--_workItemsCount;
|
||||
++_version;
|
||||
|
@ -128,7 +127,7 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
if (_workItemsCount > 0)
|
||||
{
|
||||
foreach(Queue queue in _queues)
|
||||
foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
|
||||
{
|
||||
queue.Clear();
|
||||
}
|
||||
|
@ -159,7 +158,7 @@ namespace Amib.Threading.Internal
|
|||
/// </summary>
|
||||
private class PriorityQueueEnumerator : IEnumerator
|
||||
{
|
||||
private PriorityQueue _priorityQueue;
|
||||
private readonly PriorityQueue _priorityQueue;
|
||||
private int _version;
|
||||
private int _queueIndex;
|
||||
private IEnumerator _enumerator;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#if _SILVERLIGHT
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
public enum ThreadPriority
|
||||
{
|
||||
Lowest,
|
||||
BelowNormal,
|
||||
Normal,
|
||||
AboveNormal,
|
||||
Highest,
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -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
|
|
@ -1,8 +1,30 @@
|
|||
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 interface ISTPInstancePerformanceCounters : IDisposable
|
||||
{
|
||||
void Close();
|
||||
void SampleThreads(long activeThreads, long inUseThreads);
|
||||
void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
|
||||
void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
|
||||
void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
|
||||
}
|
||||
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
internal enum STPPerformanceCounterType
|
||||
{
|
||||
// Fields
|
||||
|
@ -37,7 +59,7 @@ namespace Amib.Threading.Internal
|
|||
internal class STPPerformanceCounter
|
||||
{
|
||||
// Fields
|
||||
private PerformanceCounterType _pcType;
|
||||
private readonly PerformanceCounterType _pcType;
|
||||
protected string _counterHelp;
|
||||
protected string _counterName;
|
||||
|
||||
|
@ -47,9 +69,9 @@ namespace Amib.Threading.Internal
|
|||
string counterHelp,
|
||||
PerformanceCounterType pcType)
|
||||
{
|
||||
this._counterName = counterName;
|
||||
this._counterHelp = counterHelp;
|
||||
this._pcType = pcType;
|
||||
_counterName = counterName;
|
||||
_counterHelp = counterHelp;
|
||||
_pcType = pcType;
|
||||
}
|
||||
|
||||
public void AddCounterToCollection(CounterCreationDataCollection counterData)
|
||||
|
@ -76,7 +98,7 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
// Fields
|
||||
internal STPPerformanceCounter[] _stpPerformanceCounters;
|
||||
private static STPPerformanceCounters _instance;
|
||||
private static readonly STPPerformanceCounters _instance;
|
||||
internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
|
||||
internal const string _stpCategoryName = "SmartThreadPool";
|
||||
|
||||
|
@ -127,19 +149,12 @@ namespace Amib.Threading.Internal
|
|||
_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,
|
||||
PerformanceCounterCategoryType.MultiInstance,
|
||||
counters);
|
||||
#pragma warning restore 0618
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,16 +171,18 @@ namespace Amib.Threading.Internal
|
|||
internal class STPInstancePerformanceCounter : IDisposable
|
||||
{
|
||||
// Fields
|
||||
private bool _isDisposed;
|
||||
private PerformanceCounter _pcs;
|
||||
|
||||
// Methods
|
||||
protected STPInstancePerformanceCounter()
|
||||
{
|
||||
_isDisposed = false;
|
||||
}
|
||||
|
||||
public STPInstancePerformanceCounter(
|
||||
string instance,
|
||||
STPPerformanceCounterType spcType)
|
||||
STPPerformanceCounterType spcType) : this()
|
||||
{
|
||||
STPPerformanceCounters counters = STPPerformanceCounters.Instance;
|
||||
_pcs = new PerformanceCounter(
|
||||
|
@ -176,10 +193,6 @@ namespace Amib.Threading.Internal
|
|||
_pcs.RawValue = _pcs.RawValue;
|
||||
}
|
||||
|
||||
~STPInstancePerformanceCounter()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
|
@ -192,9 +205,20 @@ namespace Amib.Threading.Internal
|
|||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Close();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
public virtual void Increment()
|
||||
|
@ -216,27 +240,19 @@ namespace Amib.Threading.Internal
|
|||
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();
|
||||
void SampleThreads(long activeThreads, long inUseThreads);
|
||||
void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
|
||||
void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
|
||||
void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
|
||||
}
|
||||
|
||||
|
||||
internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
|
||||
internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters
|
||||
{
|
||||
private bool _isDisposed;
|
||||
// Fields
|
||||
private STPInstancePerformanceCounter[] _pcs;
|
||||
private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
|
||||
private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
|
||||
|
||||
// Methods
|
||||
static STPInstancePerformanceCounters()
|
||||
|
@ -246,8 +262,13 @@ namespace Amib.Threading.Internal
|
|||
|
||||
public STPInstancePerformanceCounters(string instance)
|
||||
{
|
||||
_isDisposed = false;
|
||||
_pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
|
||||
// STPPerformanceCounters counters = STPPerformanceCounters.Instance;
|
||||
|
||||
// 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)
|
||||
|
@ -272,22 +293,28 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
if (null != _pcs[i])
|
||||
{
|
||||
_pcs[i].Close();
|
||||
_pcs[i].Dispose();
|
||||
}
|
||||
}
|
||||
_pcs = null;
|
||||
}
|
||||
}
|
||||
|
||||
~STPInstancePerformanceCounters()
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
|
||||
|
@ -327,21 +354,17 @@ namespace Amib.Threading.Internal
|
|||
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
|
||||
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
|
||||
{
|
||||
static NullSTPInstancePerformanceCounters()
|
||||
{
|
||||
}
|
||||
|
||||
private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null);
|
||||
private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();
|
||||
|
||||
public static NullSTPInstancePerformanceCounters Instance
|
||||
{
|
||||
get { return _instance; }
|
||||
}
|
||||
|
||||
public NullSTPInstancePerformanceCounters(string instance) {}
|
||||
public void Close() {}
|
||||
public void Dispose() {}
|
||||
|
||||
|
@ -349,6 +372,77 @@ namespace Amib.Threading.Internal
|
|||
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
|
||||
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
|
||||
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
|
||||
public long InUseThreads
|
||||
{
|
||||
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)
|
||||
{
|
||||
_activeThreads = activeThreads;
|
||||
_inUseThreads = inUseThreads;
|
||||
}
|
||||
|
||||
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
|
||||
{
|
||||
_workItemsQueued = workItemsQueued;
|
||||
_workItemsProcessed = workItemsProcessed;
|
||||
}
|
||||
|
||||
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
|
||||
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
|
@ -10,104 +8,205 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
public class STPStartInfo : WIGStartInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Idle timeout in milliseconds.
|
||||
/// If a thread is idle for _idleTimeout milliseconds then
|
||||
/// it may quit.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The lower limit of threads in the pool.
|
||||
/// </summary>
|
||||
private int _minWorkerThreads;
|
||||
|
||||
/// <summary>
|
||||
/// The upper limit of threads in the pool.
|
||||
/// </summary>
|
||||
private int _maxWorkerThreads;
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the threads in the pool
|
||||
/// </summary>
|
||||
private ThreadPriority _threadPriority;
|
||||
|
||||
/// <summary>
|
||||
/// The thread pool name. Threads will get names depending on this.
|
||||
/// </summary>
|
||||
private string _threadPoolName;
|
||||
|
||||
/// <summary>
|
||||
/// If this field is not null then the performance counters are enabled
|
||||
/// and use the string as the name of the instance.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
public int IdleTimeout
|
||||
/// <summary>
|
||||
/// Get/Set the idle timeout in milliseconds.
|
||||
/// If a thread is idle (starved) longer than IdleTimeout then it may quit.
|
||||
/// </summary>
|
||||
public virtual int IdleTimeout
|
||||
{
|
||||
get { return _idleTimeout; }
|
||||
set { _idleTimeout = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_idleTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MinWorkerThreads
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the lower limit of threads in the pool.
|
||||
/// </summary>
|
||||
public virtual int MinWorkerThreads
|
||||
{
|
||||
get { return _minWorkerThreads; }
|
||||
set { _minWorkerThreads = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_minWorkerThreads = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxWorkerThreads
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the upper limit of threads in the pool.
|
||||
/// </summary>
|
||||
public virtual int MaxWorkerThreads
|
||||
{
|
||||
get { return _maxWorkerThreads; }
|
||||
set { _maxWorkerThreads = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_maxWorkerThreads = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ThreadPriority ThreadPriority
|
||||
#if !(WINDOWS_PHONE)
|
||||
/// <summary>
|
||||
/// Get/Set the scheduling priority of the threads in the pool.
|
||||
/// The Os handles the scheduling.
|
||||
/// </summary>
|
||||
public virtual ThreadPriority ThreadPriority
|
||||
{
|
||||
get { return _threadPriority; }
|
||||
set { _threadPriority = value; }
|
||||
}
|
||||
|
||||
public virtual string ThreadPoolName
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_threadPriority = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Get/Set the thread pool name. Threads will get names depending on this.
|
||||
/// </summary>
|
||||
public virtual string ThreadPoolName {
|
||||
get { return _threadPoolName; }
|
||||
set { _threadPoolName = value; }
|
||||
}
|
||||
|
||||
|
||||
public string PerformanceCounterInstanceName
|
||||
set
|
||||
{
|
||||
get { return _pcInstanceName; }
|
||||
set { _pcInstanceName = value; }
|
||||
ThrowIfReadOnly ();
|
||||
_threadPoolName = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int StackSize
|
||||
/// <summary>
|
||||
/// Get/Set the performance counter instance name of this SmartThreadPool
|
||||
/// The default is null which indicate not to use performance counters at all.
|
||||
/// </summary>
|
||||
public virtual string PerformanceCounterInstanceName
|
||||
{
|
||||
get { return _stackSize; }
|
||||
set { _stackSize = value; }
|
||||
get { return _performanceCounterInstanceName; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_performanceCounterInstanceName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public virtual bool EnableLocalPerformanceCounters
|
||||
{
|
||||
get { return _enableLocalPerformanceCounters; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_enableLocalPerformanceCounters = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set backgroundness of thread in thread pool.
|
||||
/// </summary>
|
||||
public virtual bool AreThreadsBackground
|
||||
{
|
||||
get { return _areThreadsBackground; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly ();
|
||||
_areThreadsBackground = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a readonly version of this STPStartInfo.
|
||||
/// </summary>
|
||||
/// <returns>Returns a readonly reference to this STPStartInfo</returns>
|
||||
public new STPStartInfo AsReadOnly()
|
||||
{
|
||||
return new STPStartInfo(this) { _readOnly = true };
|
||||
}
|
||||
|
||||
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState;
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the apartment state of threads in the thread pool
|
||||
/// </summary>
|
||||
public ApartmentState ApartmentState
|
||||
{
|
||||
get { return _apartmentState; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_apartmentState = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the max stack size of threads in the thread pool
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
using System;
|
||||
using Amib.Threading.Internal;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
public partial class SmartThreadPool
|
||||
{
|
||||
#region ThreadEntry class
|
||||
|
||||
internal class ThreadEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The thread creation time
|
||||
/// The value is stored as UTC value.
|
||||
/// </summary>
|
||||
private readonly DateTime _creationTime;
|
||||
|
||||
/// <summary>
|
||||
/// The last time this thread has been running
|
||||
/// It is updated by IAmAlive() method
|
||||
/// The value is stored as UTC value.
|
||||
/// </summary>
|
||||
private DateTime _lastAliveTime;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
private readonly SmartThreadPool _associatedSmartThreadPool;
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the current work item a thread from the thread pool
|
||||
/// is executing.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,89 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
internal class SynchronizedDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> _dictionary;
|
||||
private readonly object _lock;
|
||||
|
||||
public SynchronizedDictionary()
|
||||
{
|
||||
_lock = new object();
|
||||
_dictionary = new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
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<TKey, TValue>.KeyCollection Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dictionary.Keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<TKey, TValue>.ValueCollection Values
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dictionary.Values;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_dictionary.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
using System;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
|
@ -8,92 +7,165 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
public class WIGStartInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the caller's security context
|
||||
/// </summary>
|
||||
private bool _useCallerCallContext;
|
||||
|
||||
/// <summary>
|
||||
/// Use the caller's HTTP context
|
||||
/// </summary>
|
||||
private bool _useCallerHttpContext;
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the state object of a work item
|
||||
/// </summary>
|
||||
private bool _disposeOfStateObjects;
|
||||
|
||||
/// <summary>
|
||||
/// The option to run the post execute
|
||||
/// </summary>
|
||||
private CallToPostExecute _callToPostExecute;
|
||||
|
||||
/// <summary>
|
||||
/// A post execute callback to call when none is provided in
|
||||
/// the QueueWorkItem method.
|
||||
/// </summary>
|
||||
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate the WorkItemsGroup to suspend the handling of the work items
|
||||
/// until the Start() method is called.
|
||||
/// </summary>
|
||||
private bool _startSuspended;
|
||||
private WorkItemPriority _workItemPriority;
|
||||
private bool _fillStateWithArgs;
|
||||
|
||||
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)
|
||||
{
|
||||
_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()
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new NotSupportedException("This is a readonly instance and set is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's security context
|
||||
/// </summary>
|
||||
public virtual bool UseCallerCallContext
|
||||
{
|
||||
get { return _useCallerCallContext; }
|
||||
set { _useCallerCallContext = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_useCallerCallContext = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseCallerHttpContext
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's HTTP context
|
||||
/// </summary>
|
||||
public virtual bool UseCallerHttpContext
|
||||
{
|
||||
get { return _useCallerHttpContext; }
|
||||
set { _useCallerHttpContext = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_useCallerHttpContext = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DisposeOfStateObjects
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to dispose of the state object of a work item
|
||||
/// </summary>
|
||||
public virtual bool DisposeOfStateObjects
|
||||
{
|
||||
get { return _disposeOfStateObjects; }
|
||||
set { _disposeOfStateObjects = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_disposeOfStateObjects = value;
|
||||
}
|
||||
}
|
||||
|
||||
public CallToPostExecute CallToPostExecute
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the run the post execute options
|
||||
/// </summary>
|
||||
public virtual CallToPostExecute CallToPostExecute
|
||||
{
|
||||
get { return _callToPostExecute; }
|
||||
set { _callToPostExecute = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_callToPostExecute = value;
|
||||
}
|
||||
}
|
||||
|
||||
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the default post execute callback
|
||||
/// </summary>
|
||||
public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback
|
||||
{
|
||||
get { return _postExecuteWorkItemCallback; }
|
||||
set { _postExecuteWorkItemCallback = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_postExecuteWorkItemCallback = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool StartSuspended
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if the work items execution should be suspended until the Start()
|
||||
/// method is called.
|
||||
/// </summary>
|
||||
public virtual bool StartSuspended
|
||||
{
|
||||
get { return _startSuspended; }
|
||||
set { _startSuspended = value; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_startSuspended = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the default priority that a work item gets when it is enqueued
|
||||
/// </summary>
|
||||
public virtual WorkItemPriority WorkItemPriority
|
||||
{
|
||||
get { return _workItemPriority; }
|
||||
set { _workItemPriority = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public virtual bool FillStateWithArgs
|
||||
{
|
||||
get { return _fillStateWithArgs; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_fillStateWithArgs = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a readonly version of this WIGStartInfo
|
||||
/// </summary>
|
||||
/// <returns>Returns a readonly reference to this WIGStartInfoRO</returns>
|
||||
public WIGStartInfo AsReadOnly()
|
||||
{
|
||||
return new WIGStartInfo(this) { _readOnly = true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A back reference to the work item
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the result, same as GetResult()
|
||||
/// </summary>
|
||||
public object Result
|
||||
{
|
||||
get { return GetResult(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception if occured otherwise returns null.
|
||||
/// This value is valid only after the work item completed,
|
||||
/// before that it is always null.
|
||||
/// </summary>
|
||||
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<TResult> GetWorkItemResultT<TResult>()
|
||||
{
|
||||
return new WorkItemResultTWrapper<TResult>(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
|
@ -1,58 +1,13 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region WorkItem Delegate
|
||||
|
||||
/// <summary>
|
||||
/// An internal delegate to call when the WorkItem starts or completes
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Holds a callback delegate and the state for that delegate.
|
||||
/// </summary>
|
||||
public class WorkItem : IHasWorkItemPriority, IWorkItem
|
||||
public partial class WorkItem : IHasWorkItemPriority
|
||||
{
|
||||
#region WorkItemState enum
|
||||
|
||||
|
@ -61,33 +16,57 @@ namespace Amib.Threading.Internal
|
|||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Callback delegate for the callback.
|
||||
/// </summary>
|
||||
private WorkItemCallback _callback;
|
||||
private readonly WorkItemCallback _callback;
|
||||
|
||||
/// <summary>
|
||||
/// State with which to call the callback delegate.
|
||||
/// </summary>
|
||||
private object _state;
|
||||
|
||||
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
/// <summary>
|
||||
/// Stores the caller's context
|
||||
/// </summary>
|
||||
private CallerThreadContext _callerContext;
|
||||
|
||||
private readonly CallerThreadContext _callerContext;
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Holds the result of the mehtod
|
||||
/// </summary>
|
||||
|
@ -117,12 +96,12 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Represents the result state of the work item
|
||||
/// </summary>
|
||||
private WorkItemResult _workItemResult;
|
||||
private readonly WorkItemResult _workItemResult;
|
||||
|
||||
/// <summary>
|
||||
/// Work item info
|
||||
/// </summary>
|
||||
private WorkItemInfo _workItemInfo;
|
||||
private readonly WorkItemInfo _workItemInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the WorkItem starts
|
||||
|
@ -141,30 +120,41 @@ namespace Amib.Threading.Internal
|
|||
private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
|
||||
|
||||
/// <summary>
|
||||
/// The work item group this work item belong to.
|
||||
///
|
||||
/// A reference to an object that indicates whatever the
|
||||
/// SmartThreadPool has been canceled
|
||||
/// </summary>
|
||||
private IWorkItemsGroup _workItemsGroup;
|
||||
private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
|
||||
|
||||
/// <summary>
|
||||
/// The work item group this work item belong to.
|
||||
/// </summary>
|
||||
private readonly IWorkItemsGroup _workItemsGroup;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
private Thread _executingThread;
|
||||
|
||||
/// <summary>
|
||||
/// The absulote time when the work item will be timeout
|
||||
/// </summary>
|
||||
private long _expirationTime;
|
||||
|
||||
#region Performance Counter fields
|
||||
|
||||
/// <summary>
|
||||
/// The time when the work items is queued.
|
||||
/// Used with the performance counter.
|
||||
/// </summary>
|
||||
private DateTime _queuedTime;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private DateTime _beginProcessTime;
|
||||
private Stopwatch _waitingOnQueueStopwatch;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private DateTime _endProcessTime;
|
||||
private Stopwatch _processingStopwatch;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -176,7 +166,7 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
get
|
||||
{
|
||||
return (_beginProcessTime - _queuedTime);
|
||||
return _waitingOnQueueStopwatch.Elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +174,15 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
get
|
||||
{
|
||||
return (_endProcessTime - _beginProcessTime);
|
||||
return _processingStopwatch.Elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
internal WorkItemInfo WorkItemInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workItemInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,6 +193,8 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Initialize the callback holding object.
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The workItemGroup of the workitem</param>
|
||||
/// <param name="workItemInfo">The WorkItemInfo of te workitem</param>
|
||||
/// <param name="callback">Callback delegate for the callback.</param>
|
||||
/// <param name="state">State with which to call the callback delegate.</param>
|
||||
///
|
||||
|
@ -209,10 +209,12 @@ namespace Amib.Threading.Internal
|
|||
_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
|
||||
internal CanceledWorkItemsGroup CanceledSmartThreadPool
|
||||
{
|
||||
_canceledWorkItemsGroup = value;
|
||||
}
|
||||
get { return _canceledSmartThreadPool; }
|
||||
set { _canceledSmartThreadPool = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -259,7 +269,8 @@ namespace Amib.Threading.Internal
|
|||
/// </returns>
|
||||
public bool StartingWorkItem()
|
||||
{
|
||||
_beginProcessTime = DateTime.Now;
|
||||
_waitingOnQueueStopwatch.Stop();
|
||||
_processingStopwatch.Start();
|
||||
|
||||
lock (this)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -311,7 +325,7 @@ namespace Amib.Threading.Internal
|
|||
PostExecute();
|
||||
}
|
||||
|
||||
_endProcessTime = DateTime.Now;
|
||||
_processingStopwatch.Stop();
|
||||
}
|
||||
|
||||
internal void FireWorkItemCompleted()
|
||||
|
@ -323,7 +337,20 @@ 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,16 +359,21 @@ namespace Amib.Threading.Internal
|
|||
/// </summary>
|
||||
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
|
||||
{
|
||||
try
|
||||
{
|
||||
result = _callback(_state);
|
||||
|
@ -352,13 +384,46 @@ namespace Amib.Threading.Internal
|
|||
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.
|
||||
}
|
||||
}
|
||||
// We must treat the ThreadAbortException or else it will be stored in the exception variable
|
||||
catch (ThreadAbortException tae)
|
||||
{
|
||||
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
|
||||
|
||||
if (!SmartThreadPool.IsWorkItemCanceled)
|
||||
{
|
||||
SetResult(result, exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the post execute callback
|
||||
|
@ -369,7 +434,7 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
try
|
||||
{
|
||||
_workItemInfo.PostExecuteWorkItemCallback(this._workItemResult);
|
||||
_workItemInfo.PostExecuteWorkItemCallback(_workItemResult);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -382,6 +447,8 @@ namespace Amib.Threading.Internal
|
|||
/// Set the result of the work item to return
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the work item</param>
|
||||
/// <param name="exception">The exception that was throw while the workitem executed, null
|
||||
/// if there was no exception.</param>
|
||||
internal void SetResult(object result, Exception exception)
|
||||
{
|
||||
_result = result;
|
||||
|
@ -401,39 +468,39 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Wait for all work items to complete
|
||||
/// </summary>
|
||||
/// <param name="workItemResults">Array of work item result objects</param>
|
||||
/// <param name="waitableResults">Array of work item result objects</param>
|
||||
/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
|
||||
/// <param name="exitContext">
|
||||
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
|
||||
/// </param>
|
||||
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
|
||||
/// <returns>
|
||||
/// true when every work item in workItemResults has completed; otherwise false.
|
||||
/// true when every work item in waitableResults has completed; otherwise false.
|
||||
/// </returns>
|
||||
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;
|
||||
if (null != cancelWaitHandle)
|
||||
|
@ -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,8 +527,8 @@ 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;
|
||||
|
@ -470,13 +537,12 @@ namespace Amib.Threading.Internal
|
|||
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
|
|||
/// <summary>
|
||||
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
|
||||
/// </summary>
|
||||
/// <param name="workItemResults">Array of work item result objects</param>
|
||||
/// <param name="waitableResults">Array of work item result objects</param>
|
||||
/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
|
||||
/// <param name="exitContext">
|
||||
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
|
||||
|
@ -494,37 +560,37 @@ 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.
|
||||
/// </returns>
|
||||
internal static int WaitAny(
|
||||
IWorkItemResult [] workItemResults,
|
||||
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
|
|||
/// <summary>
|
||||
/// Fill an array of wait handles with the work items wait handles.
|
||||
/// </summary>
|
||||
/// <param name="workItemResults">An array of work item results</param>
|
||||
/// <param name="waitableResults">An array of work item results</param>
|
||||
/// <param name="waitHandles">An array of wait handles to fill</param>
|
||||
private static void GetWaitHandles(
|
||||
IWorkItemResult [] workItemResults,
|
||||
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,31 +616,52 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Release the work items' wait handles
|
||||
/// </summary>
|
||||
/// <param name="workItemResults">An array of work item results</param>
|
||||
private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults)
|
||||
/// <param name="waitableResults">An array of work item results</param>
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
return _workItemState;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the work item's state
|
||||
/// </summary>
|
||||
|
@ -582,10 +669,13 @@ namespace Amib.Threading.Internal
|
|||
private void SetWorkItemState(WorkItemState workItemState)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (IsValidStatesTransition(_workItemState, workItemState))
|
||||
{
|
||||
_workItemState = workItemState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signals that work item has been completed or canceled
|
||||
|
@ -606,7 +696,7 @@ namespace Amib.Threading.Internal
|
|||
|
||||
internal void WorkItemIsQueued()
|
||||
{
|
||||
_queuedTime = DateTime.Now;
|
||||
_waitingOnQueueStopwatch.Start();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -617,29 +707,82 @@ namespace Amib.Threading.Internal
|
|||
/// Cancel the work item if it didn't start running yet.
|
||||
/// </summary>
|
||||
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
|
||||
private bool Cancel()
|
||||
private bool Cancel(bool abortExecution)
|
||||
{
|
||||
#if (_WINDOWS_CE)
|
||||
if(abortExecution)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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,7 +849,7 @@ 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)
|
||||
|
@ -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);
|
||||
|
@ -749,7 +892,7 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
if (null == _workItemCompleted)
|
||||
{
|
||||
_workItemCompleted = new ManualResetEvent(IsCompleted);
|
||||
_workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted);
|
||||
}
|
||||
++_workItemCompletedRefCount;
|
||||
}
|
||||
|
@ -843,172 +986,6 @@ namespace Amib.Threading.Internal
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#region WorkItemResult class
|
||||
|
||||
private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult
|
||||
{
|
||||
/// <summary>
|
||||
/// A back reference to the work item
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the result, same as GetResult()
|
||||
/// </summary>
|
||||
public object Result
|
||||
{
|
||||
get { return GetResult(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception if occured otherwise returns null.
|
||||
/// This value is valid only after the work item completed,
|
||||
/// before that it is always null.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
|
@ -12,6 +9,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
|
@ -26,6 +24,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="workItemPriority">The priority of the work item</param>
|
||||
|
@ -42,6 +41,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="workItemInfo">Work item info</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
|
@ -63,6 +63,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
|
@ -83,6 +84,7 @@ namespace Amib.Threading.Internal
|
|||
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
|
@ -95,6 +97,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
|
@ -131,6 +134,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="workItemInfo">Work item information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
|
@ -160,6 +164,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
|
@ -185,6 +190,7 @@ namespace Amib.Threading.Internal
|
|||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
|
@ -198,6 +204,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
|
@ -239,6 +246,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
|
@ -266,6 +274,7 @@ namespace Amib.Threading.Internal
|
|||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = callToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
|
@ -279,6 +288,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
|
@ -322,7 +332,7 @@ namespace Amib.Threading.Internal
|
|||
|
||||
private static void ValidateCallback(Delegate callback)
|
||||
{
|
||||
if(callback.GetInvocationList().Length > 1)
|
||||
if (callback != null && callback.GetInvocationList().Length > 1)
|
||||
{
|
||||
throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region WorkItemInfo class
|
||||
|
@ -10,92 +7,62 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
public class WorkItemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the caller's security context
|
||||
/// </summary>
|
||||
private bool _useCallerCallContext;
|
||||
|
||||
/// <summary>
|
||||
/// Use the caller's security context
|
||||
/// </summary>
|
||||
private bool _useCallerHttpContext;
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the state object of a work item
|
||||
/// </summary>
|
||||
private bool _disposeOfStateObjects;
|
||||
|
||||
/// <summary>
|
||||
/// The option to run the post execute
|
||||
/// </summary>
|
||||
private CallToPostExecute _callToPostExecute;
|
||||
|
||||
/// <summary>
|
||||
/// A post execute callback to call when none is provided in
|
||||
/// the QueueWorkItem method.
|
||||
/// </summary>
|
||||
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the work item
|
||||
/// </summary>
|
||||
private WorkItemPriority _workItemPriority;
|
||||
|
||||
public WorkItemInfo()
|
||||
{
|
||||
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
|
||||
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
|
||||
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
|
||||
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
|
||||
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
|
||||
_workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
|
||||
UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
|
||||
UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
|
||||
DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
|
||||
CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
|
||||
PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
|
||||
WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
|
||||
}
|
||||
|
||||
public WorkItemInfo(WorkItemInfo workItemInfo)
|
||||
{
|
||||
_useCallerCallContext = workItemInfo._useCallerCallContext;
|
||||
_useCallerHttpContext = workItemInfo._useCallerHttpContext;
|
||||
_disposeOfStateObjects = workItemInfo._disposeOfStateObjects;
|
||||
_callToPostExecute = workItemInfo._callToPostExecute;
|
||||
_postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback;
|
||||
_workItemPriority = workItemInfo._workItemPriority;
|
||||
UseCallerCallContext = workItemInfo.UseCallerCallContext;
|
||||
UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
|
||||
DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
|
||||
CallToPostExecute = workItemInfo.CallToPostExecute;
|
||||
PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
|
||||
WorkItemPriority = workItemInfo.WorkItemPriority;
|
||||
Timeout = workItemInfo.Timeout;
|
||||
}
|
||||
|
||||
public bool UseCallerCallContext
|
||||
{
|
||||
get { return _useCallerCallContext; }
|
||||
set { _useCallerCallContext = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's security context
|
||||
/// </summary>
|
||||
public bool UseCallerCallContext { get; set; }
|
||||
|
||||
public bool UseCallerHttpContext
|
||||
{
|
||||
get { return _useCallerHttpContext; }
|
||||
set { _useCallerHttpContext = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's HTTP context
|
||||
/// </summary>
|
||||
public bool UseCallerHttpContext { get; set; }
|
||||
|
||||
public bool DisposeOfStateObjects
|
||||
{
|
||||
get { return _disposeOfStateObjects; }
|
||||
set { _disposeOfStateObjects = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Get/Set if to dispose of the state object of a work item
|
||||
/// </summary>
|
||||
public bool DisposeOfStateObjects { get; set; }
|
||||
|
||||
public CallToPostExecute CallToPostExecute
|
||||
{
|
||||
get { return _callToPostExecute; }
|
||||
set { _callToPostExecute = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Get/Set the run the post execute options
|
||||
/// </summary>
|
||||
public CallToPostExecute CallToPostExecute { get; set; }
|
||||
|
||||
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
|
||||
{
|
||||
get { return _postExecuteWorkItemCallback; }
|
||||
set { _postExecuteWorkItemCallback = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Get/Set the post execute callback
|
||||
/// </summary>
|
||||
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }
|
||||
|
||||
public WorkItemPriority WorkItemPriority
|
||||
{
|
||||
get { return _workItemPriority; }
|
||||
set { _workItemPriority = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Get/Set the work item's priority
|
||||
/// </summary>
|
||||
public WorkItemPriority WorkItemPriority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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!
|
||||
/// </summary>
|
||||
public long Timeout { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region WorkItemResultTWrapper class
|
||||
|
||||
internal class WorkItemResultTWrapper<TResult> : IWorkItemResult<TResult>, IInternalWaitableResult
|
||||
{
|
||||
private readonly IWorkItemResult _workItemResult;
|
||||
|
||||
public WorkItemResultTWrapper(IWorkItemResult workItemResult)
|
||||
{
|
||||
_workItemResult = workItemResult;
|
||||
}
|
||||
|
||||
#region IWorkItemResult<TResult> 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<TRes> GetWorkItemResultT<TRes>()
|
||||
{
|
||||
return (IWorkItemResult<TRes>)this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -8,33 +5,34 @@ using System.Diagnostics;
|
|||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
|
||||
#region WorkItemsGroup class
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for WorkItemsGroup.
|
||||
/// </summary>
|
||||
public class WorkItemsGroup : IWorkItemsGroup
|
||||
public class WorkItemsGroup : WorkItemsGroupBase
|
||||
{
|
||||
#region Private members
|
||||
|
||||
private object _lock = new object();
|
||||
/// <summary>
|
||||
/// Contains the name of this instance of SmartThreadPool.
|
||||
/// Can be changed by the user.
|
||||
/// </summary>
|
||||
private string _name = "WorkItemsGroup";
|
||||
private readonly object _lock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the SmartThreadPool instance that created this
|
||||
/// WorkItemsGroup.
|
||||
/// </summary>
|
||||
private SmartThreadPool _stp;
|
||||
private readonly SmartThreadPool _stp;
|
||||
|
||||
/// <summary>
|
||||
/// The OnIdle event
|
||||
/// </summary>
|
||||
private event WorkItemsGroupIdleHandler _onIdle;
|
||||
|
||||
/// <summary>
|
||||
/// A flag to indicate if the Work Items Group is now suspended.
|
||||
/// </summary>
|
||||
private bool _isSuspended;
|
||||
|
||||
/// <summary>
|
||||
/// Defines how many work items of this WorkItemsGroup can run at once.
|
||||
/// </summary>
|
||||
|
@ -44,7 +42,7 @@ namespace Amib.Threading.Internal
|
|||
/// Priority queue to hold work items before they are passed
|
||||
/// to the SmartThreadPool.
|
||||
/// </summary>
|
||||
private PriorityQueue _workItemsQueue;
|
||||
private readonly PriorityQueue _workItemsQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate how many work items are waiting in the SmartThreadPool
|
||||
|
@ -63,12 +61,13 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// WorkItemsGroup start information
|
||||
/// </summary>
|
||||
private WIGStartInfo _workItemsGroupStartInfo;
|
||||
private readonly WIGStartInfo _workItemsGroupStartInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Signaled when all of the WorkItemsGroup's work item completed.
|
||||
/// </summary>
|
||||
private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
|
||||
//private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
|
||||
private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
|
||||
|
||||
/// <summary>
|
||||
/// A common object for all the work items that this work items group
|
||||
|
@ -87,261 +86,90 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
if (concurrency <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero");
|
||||
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);
|
||||
_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly();
|
||||
_workItemsQueue = new PriorityQueue();
|
||||
Name = "WorkItemsGroup";
|
||||
|
||||
// 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 IWorkItemsGroup implementation
|
||||
#region WorkItemsGroupBase Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the name of the SmartThreadPool instance
|
||||
/// </summary>
|
||||
public string Name
|
||||
public override int Concurrency
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
get { return _concurrency; }
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
Debug.Assert(value > 0);
|
||||
|
||||
int diff = value - _concurrency;
|
||||
_concurrency = value;
|
||||
if (diff > 0)
|
||||
{
|
||||
EnqueueToSTPNextNWorkItem(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int WaitingCallbacks
|
||||
{
|
||||
get { return _workItemsQueue.Count; }
|
||||
}
|
||||
|
||||
public override object[] GetStates()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
object[] states = new object[_workItemsQueue.Count];
|
||||
int i = 0;
|
||||
foreach (WorkItem workItem in _workItemsQueue)
|
||||
{
|
||||
states[i] = workItem.GetWorkItemResult().State;
|
||||
++i;
|
||||
}
|
||||
return states;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// WorkItemsGroup start information
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
|
||||
public override WIGStartInfo WIGStartInfo
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback);
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
get { return _workItemsGroupStartInfo; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// Start the Work Items Group if it was started suspended
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="workItemPriority">The priority of the work item</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
|
||||
public override void Start()
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority);
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
// If the Work Items Group already started then quit
|
||||
if (!_isSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isSuspended = false;
|
||||
|
||||
EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item info</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback);
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state);
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority);
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state);
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback);
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the thread pool to be idle
|
||||
/// </summary>
|
||||
public void WaitForIdle()
|
||||
{
|
||||
WaitForIdle(Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the thread pool to be idle
|
||||
/// </summary>
|
||||
public bool WaitForIdle(TimeSpan timeout)
|
||||
{
|
||||
return WaitForIdle((int)timeout.TotalMilliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the thread pool to be idle
|
||||
/// </summary>
|
||||
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()
|
||||
public override void Cancel(bool abortExecution)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
|
@ -350,23 +178,26 @@ namespace Amib.Threading.Internal
|
|||
_workItemsInStpQueue = 0;
|
||||
_canceledWorkItemsGroup = new CanceledWorkItemsGroup();
|
||||
}
|
||||
|
||||
if (abortExecution)
|
||||
{
|
||||
_stp.CancelAbortWorkItemsGroup(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
/// <summary>
|
||||
/// Wait for the thread pool to be idle
|
||||
/// </summary>
|
||||
public override bool WaitForIdle(int millisecondsTimeout)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!_workItemsGroupStartInfo.StartSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_workItemsGroupStartInfo.StartSuspended = false;
|
||||
SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
|
||||
return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
|
||||
}
|
||||
|
||||
for(int i = 0; i < _concurrency; ++i)
|
||||
public override event WorkItemsGroupIdleHandler OnIdle
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(null, false);
|
||||
}
|
||||
add { _onIdle += value; }
|
||||
remove { _onIdle -= value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -375,22 +206,24 @@ namespace Amib.Threading.Internal
|
|||
|
||||
private void RegisterToWorkItemCompletion(IWorkItemResult wir)
|
||||
{
|
||||
IInternalWorkItemResult iwir = wir as IInternalWorkItemResult;
|
||||
iwir.OnWorkItemStarted += new WorkItemStateCallback(OnWorkItemStartedCallback);
|
||||
iwir.OnWorkItemCompleted += new WorkItemStateCallback(OnWorkItemCompletedCallback);
|
||||
IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
|
||||
iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
|
||||
iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
|
||||
}
|
||||
|
||||
public void OnSTPIsStarting()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (_workItemsGroupStartInfo.StartSuspended)
|
||||
if (_isSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnqueueToSTPNextNWorkItem(_concurrency);
|
||||
}
|
||||
|
||||
for(int i = 0; i < _concurrency; ++i)
|
||||
public void EnqueueToSTPNextNWorkItem(int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(null, false);
|
||||
}
|
||||
|
@ -417,8 +250,7 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
eh(this);
|
||||
}
|
||||
// Ignore exceptions
|
||||
catch{}
|
||||
catch { } // Suppress exceptions
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,6 +267,11 @@ namespace Amib.Threading.Internal
|
|||
EnqueueToSTPNextWorkItem(null, true);
|
||||
}
|
||||
|
||||
internal override void Enqueue(WorkItem workItem)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
}
|
||||
|
||||
private void EnqueueToSTPNextWorkItem(WorkItem workItem)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(workItem, false);
|
||||
|
@ -475,7 +312,7 @@ namespace Amib.Threading.Internal
|
|||
(0 == _workItemsInStpQueue))
|
||||
{
|
||||
_stp.RegisterWorkItemsGroup(this);
|
||||
Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle");
|
||||
IsIdle = false;
|
||||
_isIdleWaitHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
@ -486,19 +323,31 @@ namespace Amib.Threading.Internal
|
|||
if (0 == _workItemsInStpQueue)
|
||||
{
|
||||
_stp.UnregisterWorkItemsGroup(this);
|
||||
Trace.WriteLine("WorkItemsGroup " + Name + " is idle");
|
||||
IsIdle = true;
|
||||
_isIdleWaitHandle.Set();
|
||||
_stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle));
|
||||
if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
|
||||
{
|
||||
_stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_workItemsGroupStartInfo.StartSuspended)
|
||||
if (!_isSuspended)
|
||||
{
|
||||
if (_workItemsInStpQueue < _concurrency)
|
||||
{
|
||||
WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
|
||||
_stp.Enqueue(nextWorkItem, true);
|
||||
try
|
||||
{
|
||||
_stp.Enqueue(nextWorkItem);
|
||||
}
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
e.GetHashCode();
|
||||
// The STP has been shutdown
|
||||
}
|
||||
|
||||
++_workItemsInStpQueue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,471 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
public abstract class WorkItemsGroupBase : IWorkItemsGroup
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// Contains the name of this instance of SmartThreadPool.
|
||||
/// Can be changed by the user.
|
||||
/// </summary>
|
||||
private string _name = "WorkItemsGroupBase";
|
||||
|
||||
public WorkItemsGroupBase()
|
||||
{
|
||||
IsIdle = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorkItemsGroup Members
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Cancel all the work items.
|
||||
/// Same as Cancel(false)
|
||||
/// </summary>
|
||||
public virtual void Cancel()
|
||||
{
|
||||
Cancel(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
|
||||
/// </summary>
|
||||
public void WaitForIdle()
|
||||
{
|
||||
WaitForIdle(Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
|
||||
/// </summary>
|
||||
public bool WaitForIdle(TimeSpan timeout)
|
||||
{
|
||||
return WaitForIdle((int)timeout.TotalMilliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IsIdle is true when there are no work items running or queued.
|
||||
/// </summary>
|
||||
public bool IsIdle { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region QueueWorkItem
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback);
|
||||
Enqueue(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="workItemPriority">The priority of the work item</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
|
||||
{
|
||||
PreQueueWorkItem();
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority);
|
||||
Enqueue(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item info</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
|
||||
{
|
||||
PreQueueWorkItem();
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback);
|
||||
Enqueue(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state);
|
||||
Enqueue(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
|
||||
{
|
||||
PreQueueWorkItem();
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority);
|
||||
Enqueue(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
|
||||
{
|
||||
PreQueueWorkItem();
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state);
|
||||
Enqueue(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
public IWorkItemResult QueueWorkItem(
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback)
|
||||
{
|
||||
PreQueueWorkItem();
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback);
|
||||
Enqueue(workItem);
|
||||
return workItem.GetWorkItemResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
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<T>(Action<T> action, T arg)
|
||||
{
|
||||
return QueueWorkItem<T> (action, arg, SmartThreadPool.DefaultWorkItemPriority);
|
||||
}
|
||||
|
||||
public IWorkItemResult QueueWorkItem<T> (Action<T> 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<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
|
||||
{
|
||||
return QueueWorkItem<T1, T2> (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority);
|
||||
}
|
||||
|
||||
public IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> 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<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
|
||||
{
|
||||
return QueueWorkItem<T1, T2, T3> (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority);
|
||||
;
|
||||
}
|
||||
|
||||
public IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> 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<T1, T2, T3, T4>(
|
||||
Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
{
|
||||
return QueueWorkItem<T1, T2, T3, T4> (action, arg1, arg2, arg3, arg4,
|
||||
SmartThreadPool.DefaultWorkItemPriority);
|
||||
}
|
||||
|
||||
public IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (
|
||||
Action<T1, T2, T3, T4> 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<TResult> QueueWorkItem<TResult>(Func<TResult> func)
|
||||
{
|
||||
PreQueueWorkItem();
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(
|
||||
this,
|
||||
WIGStartInfo,
|
||||
state =>
|
||||
{
|
||||
return func.Invoke();
|
||||
});
|
||||
Enqueue(workItem);
|
||||
return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult());
|
||||
}
|
||||
|
||||
public IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> 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<TResult>(workItem.GetWorkItemResult());
|
||||
}
|
||||
|
||||
public IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> 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<TResult>(workItem.GetWorkItemResult());
|
||||
}
|
||||
|
||||
public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>(
|
||||
Func<T1, T2, T3, TResult> 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<TResult>(workItem.GetWorkItemResult());
|
||||
}
|
||||
|
||||
public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult>(
|
||||
Func<T1, T2, T3, T4, TResult> 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<TResult>(workItem.GetWorkItemResult());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
|
@ -18,7 +16,7 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Waiters queue (implemented as stack).
|
||||
/// </summary>
|
||||
private WaiterEntry _headWaiterEntry = new WaiterEntry();
|
||||
private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();
|
||||
|
||||
/// <summary>
|
||||
/// Waiters count
|
||||
|
@ -28,18 +26,70 @@ namespace Amib.Threading.Internal
|
|||
/// <summary>
|
||||
/// Work items queue
|
||||
/// </summary>
|
||||
private PriorityQueue _workItems = new PriorityQueue();
|
||||
private readonly PriorityQueue _workItems = new PriorityQueue();
|
||||
|
||||
/// <summary>
|
||||
/// Indicate that work items are allowed to be queued
|
||||
/// </summary>
|
||||
private bool _isWorkItemsQueueActive = true;
|
||||
|
||||
|
||||
#if (WINDOWS_PHONE)
|
||||
private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>();
|
||||
#elif (_WINDOWS_CE)
|
||||
private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot();
|
||||
#else
|
||||
|
||||
[ThreadStatic]
|
||||
private static WaiterEntry _waiterEntry;
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Each thread in the thread pool keeps its own waiter entry.
|
||||
/// </summary>
|
||||
[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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A flag that indicates if the WorkItemsQueue has been disposed.
|
||||
|
@ -57,13 +107,9 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
get
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
ValidateNotDisposed();
|
||||
return _workItems.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current number of waiters
|
||||
|
@ -72,13 +118,9 @@ namespace Amib.Threading.Internal
|
|||
{
|
||||
get
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
ValidateNotDisposed();
|
||||
return _waitersCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
@ -144,21 +186,20 @@ namespace Amib.Threading.Internal
|
|||
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.
|
||||
// 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;
|
||||
WaiterEntry waiterEntry;
|
||||
WorkItem workItem = null;
|
||||
|
||||
lock (this)
|
||||
{
|
||||
ValidateNotDisposed();
|
||||
|
@ -169,16 +210,15 @@ namespace Amib.Threading.Internal
|
|||
workItem = _workItems.Dequeue() as WorkItem;
|
||||
return workItem;
|
||||
}
|
||||
|
||||
// No waiting work items ...
|
||||
else
|
||||
{
|
||||
// Get the wait entry for the waiters queue
|
||||
|
||||
// Get the waiter entry for the waiters queue
|
||||
waiterEntry = GetThreadWaiterEntry();
|
||||
|
||||
// Put the waiter with the other waiters
|
||||
PushWaiter(waiterEntry);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare array of wait handle for the WaitHandle.WaitAny()
|
||||
WaitHandle [] waitHandles = new WaitHandle[] {
|
||||
|
@ -189,10 +229,10 @@ namespace Amib.Threading.Internal
|
|||
|
||||
// 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.
|
||||
// It just doesn't work, I don't know why, so I have two lock(this)
|
||||
// statments instead of one.
|
||||
|
||||
int index = WaitHandle.WaitAny(
|
||||
int index = STPEventWaitHandle.WaitAny(
|
||||
waitHandles,
|
||||
millisecondsTimeout,
|
||||
true);
|
||||
|
@ -242,7 +282,7 @@ namespace Amib.Threading.Internal
|
|||
/// Cleanup the work items queue, hence no more work
|
||||
/// items are allowed to be queue
|
||||
/// </summary>
|
||||
protected virtual void Cleanup()
|
||||
private void Cleanup()
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
|
@ -279,6 +319,21 @@ namespace Amib.Threading.Internal
|
|||
}
|
||||
}
|
||||
|
||||
public object[] GetStates()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
object[] states = new object[_workItems.Count];
|
||||
int i = 0;
|
||||
foreach (WorkItem workItem in _workItems)
|
||||
{
|
||||
states[i] = workItem.GetWorkItemResult().State;
|
||||
++i;
|
||||
}
|
||||
return states;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
@ -289,14 +344,14 @@ namespace Amib.Threading.Internal
|
|||
/// <returns></returns>
|
||||
/// In order to avoid creation and destuction of WaiterEntry
|
||||
/// objects each thread has its own WaiterEntry object.
|
||||
private WaiterEntry GetThreadWaiterEntry()
|
||||
private static WaiterEntry GetThreadWaiterEntry()
|
||||
{
|
||||
if (null == _waiterEntry)
|
||||
if (null == CurrentWaiterEntry)
|
||||
{
|
||||
_waiterEntry = new WaiterEntry();
|
||||
CurrentWaiterEntry = new WaiterEntry();
|
||||
}
|
||||
_waiterEntry.Reset();
|
||||
return _waiterEntry;
|
||||
CurrentWaiterEntry.Reset();
|
||||
return CurrentWaiterEntry;
|
||||
}
|
||||
|
||||
#region Waiters stack methods
|
||||
|
@ -418,14 +473,15 @@ namespace Amib.Threading.Internal
|
|||
#region WaiterEntry class
|
||||
|
||||
// A waiter entry in the _waiters queue.
|
||||
public class WaiterEntry : IDisposable
|
||||
public sealed class WaiterEntry : IDisposable
|
||||
{
|
||||
#region Member variables
|
||||
|
||||
/// <summary>
|
||||
/// Event to signal the waiter that it got the work item.
|
||||
/// </summary>
|
||||
private AutoResetEvent _waitHandle = new AutoResetEvent(false);
|
||||
//private AutoResetEvent _waitHandle = new AutoResetEvent(false);
|
||||
private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Flag to know if this waiter already quited from the queue
|
||||
|
@ -471,13 +527,10 @@ namespace Amib.Threading.Internal
|
|||
public WorkItem WorkItem
|
||||
{
|
||||
get
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
return _workItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signal the waiter that it got a work item.
|
||||
|
@ -550,18 +603,16 @@ namespace Amib.Threading.Internal
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~WaiterEntry()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -574,14 +625,8 @@ namespace Amib.Threading.Internal
|
|||
if (!_isDisposed)
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
_isDisposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
~WorkItemsQueue()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
private void ValidateNotDisposed()
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
|
||||
; *
|
||||
[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
|
||||
|
@ -32,7 +35,7 @@
|
|||
; 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"
|
||||
ConfigDirectory = "."
|
||||
|
||||
[ServiceList]
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
|
||||
; *
|
||||
[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
|
||||
|
@ -21,11 +24,10 @@
|
|||
; 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"
|
||||
ConfigDirectory = "."
|
||||
|
||||
[ServiceList]
|
||||
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"
|
||||
|
|
|
@ -3055,6 +3055,7 @@
|
|||
<Match path="Avatar/AvatarFactory/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Avatar/Friends/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Avatar/Inventory/Archiver/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Avatar/Inventory/Transfer/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Framework/InventoryAccess/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Scripting/VectorRender/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="World/Archiver/Tests" pattern="*.cs" recurse="true"/>
|
||||
|
|
Loading…
Reference in New Issue