Merge branch 'master' of ssh://opensimulator.org/var/git/opensim

user_profiles
Diva Canto 2013-05-06 09:18:17 -07:00
commit a81ddf3d70
82 changed files with 8008 additions and 5800 deletions

View File

@ -115,6 +115,8 @@ namespace OpenSim.ApplicationPlugins.LoadRegions
Environment.Exit(1); Environment.Exit(1);
} }
List<IScene> createdScenes = new List<IScene>();
for (int i = 0; i < regionsToLoad.Length; i++) for (int i = 0; i < regionsToLoad.Length; i++)
{ {
IScene scene; IScene scene;
@ -123,12 +125,18 @@ namespace OpenSim.ApplicationPlugins.LoadRegions
")"); ")");
bool changed = m_openSim.PopulateRegionEstateInfo(regionsToLoad[i]); bool changed = m_openSim.PopulateRegionEstateInfo(regionsToLoad[i]);
m_openSim.CreateRegion(regionsToLoad[i], true, out scene); m_openSim.CreateRegion(regionsToLoad[i], true, out scene);
createdScenes.Add(scene);
if (changed) if (changed)
regionsToLoad[i].EstateSettings.Save(); regionsToLoad[i].EstateSettings.Save();
}
if (scene != null) foreach (IScene scene in createdScenes)
{ {
scene.Start();
m_newRegionCreatedHandler = OnNewRegionCreated; m_newRegionCreatedHandler = OnNewRegionCreated;
if (m_newRegionCreatedHandler != null) if (m_newRegionCreatedHandler != null)
{ {
@ -136,7 +144,6 @@ namespace OpenSim.ApplicationPlugins.LoadRegions
} }
} }
} }
}
public void Dispose() public void Dispose()
{ {

View File

@ -700,6 +700,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
IScene newScene; IScene newScene;
m_application.CreateRegion(region, out newScene); m_application.CreateRegion(region, out newScene);
newScene.Start();
// If an access specification was provided, use it. // If an access specification was provided, use it.
// Otherwise accept the default. // Otherwise accept the default.

View File

@ -158,7 +158,7 @@ namespace OpenSim.Framework.Capabilities
/// capabilities and their handler details. /// capabilities and their handler details.
/// </summary> /// </summary>
/// <param name="excludeSeed">If true, then exclude the seed cap.</param> /// <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(); Hashtable caps = new Hashtable();
string protocol = "http://"; string protocol = "http://";
@ -175,6 +175,9 @@ namespace OpenSim.Framework.Capabilities
if (excludeSeed && "SEED" == capsName) if (excludeSeed && "SEED" == capsName)
continue; continue;
if (requestedCaps != null && !requestedCaps.Contains(capsName))
continue;
caps[capsName] = baseUrl + m_capsHandlers[capsName].Path; caps[capsName] = baseUrl + m_capsHandlers[capsName].Path;
} }
} }

View File

@ -136,5 +136,10 @@ namespace OpenSim.Framework
ISceneObject DeserializeObject(string representation); ISceneObject DeserializeObject(string representation);
bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); bool CheckClient(UUID agentID, System.Net.IPEndPoint ep);
/// <summary>
/// Start the scene and associated scripts within it.
/// </summary>
void Start();
} }
} }

View File

@ -1840,7 +1840,7 @@ namespace OpenSim.Framework
case FireAndForgetMethod.SmartThreadPool: case FireAndForgetMethod.SmartThreadPool:
if (m_ThreadPool == null) if (m_ThreadPool == null)
InitThreadPool(15); InitThreadPool(15);
m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj);
break; break;
case FireAndForgetMethod.Thread: case FireAndForgetMethod.Thread:
Thread thread = new Thread(delegate(object o) { realCallback(o); }); Thread thread = new Thread(delegate(object o) { realCallback(o); });
@ -1910,15 +1910,15 @@ namespace OpenSim.Framework
return sb.ToString(); return sb.ToString();
} }
private static object SmartThreadPoolCallback(object o) // private static object SmartThreadPoolCallback(object o)
{ // {
object[] array = (object[])o; // object[] array = (object[])o;
WaitCallback callback = (WaitCallback)array[0]; // WaitCallback callback = (WaitCallback)array[0];
object obj = array[1]; // object obj = array[1];
//
callback(obj); // callback(obj);
return null; // return null;
} // }
#endregion FireAndForget Threading Pattern #endregion FireAndForget Threading Pattern

View File

@ -425,9 +425,6 @@ namespace OpenSim
mscene = scene; mscene = scene;
scene.Start();
scene.StartScripts();
return clientServers; return clientServers;
} }
@ -751,6 +748,7 @@ namespace OpenSim
ShutdownClientServer(whichRegion); ShutdownClientServer(whichRegion);
IScene scene; IScene scene;
CreateRegion(whichRegion, true, out scene); CreateRegion(whichRegion, true, out scene);
scene.Start();
} }
# region Setup methods # region Setup methods

View File

@ -273,11 +273,22 @@ namespace OpenSim.Region.ClientStack.Linden
return string.Empty; 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 // Add the external too
foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers) foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers)
{
if (!validCaps.Contains(kvp.Key))
continue;
caps[kvp.Key] = kvp.Value; caps[kvp.Key] = kvp.Value;
}
string result = LLSDHelpers.SerialiseLLSDReply(caps); string result = LLSDHelpers.SerialiseLLSDReply(caps);

View File

@ -75,6 +75,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
base.SetUp();
UUID userId = TestHelpers.ParseTail(0x3); UUID userId = TestHelpers.ParseTail(0x3);
J2KDecoderModule j2kdm = new J2KDecoderModule(); J2KDecoderModule j2kdm = new J2KDecoderModule();

View File

@ -47,10 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
/// <summary> /// <summary>
private List<Scene> m_Scenelist = new List<Scene>(); 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; private bool m_Enabled = true;
#region Region Module interface #region Region Module interface
@ -81,9 +79,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
// scene.RegisterModuleInterface<IInventoryTransferModule>(this); // scene.RegisterModuleInterface<IInventoryTransferModule>(this);
scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnNewClient += OnNewClient;
// scene.EventManager.OnClientClosed += ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
// scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene;
} }
public void RegionLoaded(Scene scene) 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_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only");
m_Enabled = false; m_Enabled = false;
m_Scenelist.Clear(); // m_Scenelist.Clear();
scene.EventManager.OnNewClient -= OnNewClient; // scene.EventManager.OnNewClient -= OnNewClient;
// scene.EventManager.OnClientClosed -= ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
} }
} }
} }
@ -108,9 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
public void RemoveRegion(Scene scene) public void RemoveRegion(Scene scene)
{ {
scene.EventManager.OnNewClient -= OnNewClient; scene.EventManager.OnNewClient -= OnNewClient;
// scene.EventManager.OnClientClosed -= ClientLoggedOut;
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
m_Scenelist.Remove(scene); m_Scenelist.Remove(scene);
} }
@ -140,11 +132,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
client.OnInstantMessage += OnInstantMessage; client.OnInstantMessage += OnInstantMessage;
} }
// protected void OnSetRootAgentScene(UUID id, Scene scene)
// {
// m_AgentRegions[id] = scene;
// }
private Scene FindClientScene(UUID agentId) private Scene FindClientScene(UUID agentId)
{ {
lock (m_Scenelist) lock (m_Scenelist)
@ -188,8 +175,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
{ {
UUID folderID = new UUID(im.binaryBucket, 1); UUID folderID = new UUID(im.binaryBucket, 1);
m_log.DebugFormat("[INVENTORY TRANSFER]: Inserting original folder {0} "+ m_log.DebugFormat(
"into agent {1}'s inventory", "[INVENTORY TRANSFER]: Inserting original folder {0} into agent {1}'s inventory",
folderID, new UUID(im.toAgentID)); folderID, new UUID(im.toAgentID));
InventoryFolderBase folderCopy InventoryFolderBase folderCopy
@ -213,7 +200,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
user.ControllingClient.SendBulkUpdateInventory(folderCopy); user.ControllingClient.SendBulkUpdateInventory(folderCopy);
// HACK!! // 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 else
{ {
@ -243,7 +233,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
user.ControllingClient.SendBulkUpdateInventory(itemCopy); user.ControllingClient.SendBulkUpdateInventory(itemCopy);
// HACK!! // 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 // 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>
/// ///
/// </summary> /// </summary>

View File

@ -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);
}
}
}

View File

@ -240,7 +240,7 @@ namespace OpenSim.Region.CoreModules.Framework
{ {
caps.AppendFormat("** User {0}:\n", kvp.Key); 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()); Uri uri = new Uri(kvp2.Value.ToString());
caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery);

View File

@ -646,11 +646,12 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
} }
else 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 // Taking copy of another person's item. Take to
// Objects folder. // Objects folder.
folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object);
so.FromFolderID = UUID.Zero;
} }
else else
{ {
@ -666,10 +667,16 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
// //
if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) 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); InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID);
folder = m_Scene.InventoryService.GetFolder(f); folder = m_Scene.InventoryService.GetFolder(f);
if(folder.Type == 14 || folder.Type == 16)
{
// folder.Type = 6;
folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object);
}
} }
} }

View File

@ -142,20 +142,26 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
scene.RegisterModuleInterface<IGridService>(this); scene.RegisterModuleInterface<IGridService>(this);
lock (m_LocalCache)
{
if (m_LocalCache.ContainsKey(scene.RegionInfo.RegionID)) 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!"); m_log.ErrorFormat("[LOCAL GRID SERVICE CONNECTOR]: simulator seems to have more than one region with the same UUID. Please correct this!");
else else
m_LocalCache.Add(scene.RegionInfo.RegionID, new RegionCache(scene)); m_LocalCache.Add(scene.RegionInfo.RegionID, new RegionCache(scene));
} }
}
public void RemoveRegion(Scene scene) public void RemoveRegion(Scene scene)
{ {
if (!m_Enabled) if (!m_Enabled)
return; return;
lock (m_LocalCache)
{
m_LocalCache[scene.RegionInfo.RegionID].Clear(); m_LocalCache[scene.RegionInfo.RegionID].Clear();
m_LocalCache.Remove(scene.RegionInfo.RegionID); m_LocalCache.Remove(scene.RegionInfo.RegionID);
} }
}
public void RegionLoaded(Scene scene) 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. // 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 // Neighbour data is cached in memory, so this is fast
lock (m_LocalCache)
{
foreach (RegionCache rcache in m_LocalCache.Values) foreach (RegionCache rcache in m_LocalCache.Values)
{ {
region = rcache.GetRegionByPosition(x, y); region = rcache.GetRegionByPosition(x, y);
@ -199,6 +208,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
return region; return region;
} }
} }
}
// Then try on this sim (may be a lookup in DB if this is using MySql). // Then try on this sim (may be a lookup in DB if this is using MySql).
return m_GridService.GetRegionByPosition(scopeID, x, y); 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(); System.Text.StringBuilder caps = new System.Text.StringBuilder();
lock (m_LocalCache)
{
foreach (KeyValuePair<UUID, RegionCache> kvp in m_LocalCache) foreach (KeyValuePair<UUID, RegionCache> kvp in m_LocalCache)
{ {
caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key); 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) foreach (GridRegion r in regions)
caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize); caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize);
} }
}
MainConsole.Instance.Output(caps.ToString()); MainConsole.Instance.Output(caps.ToString());
} }

View File

@ -389,10 +389,12 @@ namespace OpenSim.Region.Framework.Scenes
if (value) if (value)
{ {
if (!m_active) if (!m_active)
Start(); Start(false);
} }
else 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; m_active = false;
} }
} }
@ -1331,10 +1333,18 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
public override void Start()
{
Start(true);
}
/// <summary> /// <summary>
/// Start the scene /// Start the scene
/// </summary> /// </summary>
public void Start() /// <param name='startScripts'>
/// Start the scripts within the scene.
/// </param>
public void Start(bool startScripts)
{ {
m_active = true; m_active = true;
@ -1353,6 +1363,8 @@ namespace OpenSim.Region.Framework.Scenes
m_heartbeatThread m_heartbeatThread
= Watchdog.StartThread( = Watchdog.StartThread(
Heartbeat, string.Format("Heartbeat ({0})", RegionInfo.RegionName), ThreadPriority.Normal, false, false); Heartbeat, string.Format("Heartbeat ({0})", RegionInfo.RegionName), ThreadPriority.Normal, false, false);
StartScripts();
} }
/// <summary> /// <summary>
@ -3699,7 +3711,7 @@ namespace OpenSim.Region.Framework.Scenes
// On login test land permisions // On login test land permisions
if (vialogin) 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); m_authenticateHandler.RemoveCircuit(agent.circuitcode);
return false; return false;
@ -3880,20 +3892,37 @@ namespace OpenSim.Region.Framework.Scenes
return true; 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); if (posX < 0)
bool restricted = land.IsRestrictedFromLand(agent.AgentID); 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) if (banned || restricted)
{ {
ILandObject nearestParcel = GetNearestAllowedParcel(agent.AgentID, agent.startpos.X, agent.startpos.Y); ILandObject nearestParcel = GetNearestAllowedParcel(agentID, posX, posY);
if (nearestParcel != null) if (nearestParcel != null)
{ {
//Move agent to nearest allowed //Move agent to nearest allowed
Vector3 newPosition = GetParcelCenterAtGround(nearestParcel); Vector3 newPosition = GetParcelCenterAtGround(nearestParcel);
agent.startpos.X = newPosition.X; posX = newPosition.X;
agent.startpos.Y = newPosition.Y; posY = newPosition.Y;
} }
else else
{ {
@ -5478,6 +5507,8 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns> /// <returns></returns>
public bool QueryAccess(UUID agentID, Vector3 position, out string reason) public bool QueryAccess(UUID agentID, Vector3 position, out string reason)
{ {
reason = "You are banned from the region";
if (EntityTransferModule.IsInTransit(agentID)) if (EntityTransferModule.IsInTransit(agentID))
{ {
reason = "Agent is still in transit from this region"; reason = "Agent is still in transit from this region";
@ -5489,6 +5520,12 @@ namespace OpenSim.Region.Framework.Scenes
return false; 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. // 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. // However, the long term fix is to make sure root agent count is always accurate.
m_sceneGraph.RecalculateStats(); 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 (position == Vector3.Zero) // Teleport
{ {
if (!RegionInfo.EstateSettings.AllowDirectTeleport) 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; reason = String.Empty;

View File

@ -561,6 +561,10 @@ namespace OpenSim.Region.Framework.Scenes
get { return false; } get { return false; }
} }
public virtual void Start()
{
}
public void Restart() public void Restart()
{ {
// This has to be here to fire the event // This has to be here to fire the event

View File

@ -79,7 +79,7 @@ private sealed class BulletShapeUnman : BulletShape
: base() : base()
{ {
ptr = xx; ptr = xx;
type = typ; shapeType = typ;
} }
public override bool HasPhysicalShape public override bool HasPhysicalShape
{ {
@ -91,7 +91,7 @@ private sealed class BulletShapeUnman : BulletShape
} }
public override BulletShape Clone() public override BulletShape Clone()
{ {
return new BulletShapeUnman(ptr, type); return new BulletShapeUnman(ptr, shapeType);
} }
public override bool ReferenceSame(BulletShape other) public override bool ReferenceSame(BulletShape other)
{ {
@ -375,7 +375,7 @@ public override BulletShape DuplicateCollisionShape(BulletWorld world, BulletSha
{ {
BulletWorldUnman worldu = world as BulletWorldUnman; BulletWorldUnman worldu = world as BulletWorldUnman;
BulletShapeUnman srcShapeu = srcShape as BulletShapeUnman; 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) public override bool DeleteCollisionShape(BulletWorld world, BulletShape shape)

View File

@ -85,7 +85,7 @@ private sealed class BulletShapeXNA : BulletShape
: base() : base()
{ {
shape = xx; shape = xx;
type = typ; shapeType = typ;
} }
public override bool HasPhysicalShape public override bool HasPhysicalShape
{ {
@ -97,7 +97,7 @@ private sealed class BulletShapeXNA : BulletShape
} }
public override BulletShape Clone() public override BulletShape Clone()
{ {
return new BulletShapeXNA(shape, type); return new BulletShapeXNA(shape, shapeType);
} }
public override bool ReferenceSame(BulletShape other) public override bool ReferenceSame(BulletShape other)
{ {

View File

@ -87,8 +87,8 @@ public class BSActorAvatarMove : BSActor
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // 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. // Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time. // Called at taint-time.
// BSActor.RemoveBodyDependencies() // BSActor.RemoveDependencies()
public override void RemoveBodyDependencies() public override void RemoveDependencies()
{ {
// Nothing to do for the hoverer since it is all software at pre-step action time. // Nothing to do for the hoverer since it is all software at pre-step action time.
} }

View File

@ -87,8 +87,8 @@ public class BSActorHover : BSActor
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // 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. // Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time. // Called at taint-time.
// BSActor.RemoveBodyDependencies() // BSActor.RemoveDependencies()
public override void RemoveBodyDependencies() public override void RemoveDependencies()
{ {
// Nothing to do for the hoverer since it is all software at pre-step action time. // Nothing to do for the hoverer since it is all software at pre-step action time.
} }

View File

@ -85,8 +85,8 @@ public class BSActorLockAxis : BSActor
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // 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. // Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time. // Called at taint-time.
// BSActor.RemoveBodyDependencies() // BSActor.RemoveDependencies()
public override void RemoveBodyDependencies() public override void RemoveDependencies()
{ {
if (LockAxisConstraint != null) if (LockAxisConstraint != null)
{ {

View File

@ -88,8 +88,8 @@ public class BSActorMoveToTarget : BSActor
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // 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. // Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time. // Called at taint-time.
// BSActor.RemoveBodyDependencies() // BSActor.RemoveDependencies()
public override void RemoveBodyDependencies() public override void RemoveDependencies()
{ {
// Nothing to do for the moveToTarget since it is all software at pre-step action time. // Nothing to do for the moveToTarget since it is all software at pre-step action time.
} }

View File

@ -89,8 +89,8 @@ public class BSActorSetForce : BSActor
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // 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. // Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time. // Called at taint-time.
// BSActor.RemoveBodyDependencies() // BSActor.RemoveDependencies()
public override void RemoveBodyDependencies() public override void RemoveDependencies()
{ {
// Nothing to do for the hoverer since it is all software at pre-step action time. // Nothing to do for the hoverer since it is all software at pre-step action time.
} }

View File

@ -89,8 +89,8 @@ public class BSActorSetTorque : BSActor
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // 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. // Register a prestep action to restore physical requirements before the next simulation step.
// Called at taint-time. // Called at taint-time.
// BSActor.RemoveBodyDependencies() // BSActor.RemoveDependencies()
public override void RemoveBodyDependencies() public override void RemoveDependencies()
{ {
// Nothing to do for the hoverer since it is all software at pre-step action time. // Nothing to do for the hoverer since it is all software at pre-step action time.
} }

View File

@ -106,9 +106,9 @@ public class BSActorCollection
{ {
ForEachActor(a => a.Refresh()); 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(); public abstract void Refresh();
// The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // 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. // Register a prestep action to restore physical requirements before the next simulation step.
public abstract void RemoveBodyDependencies(); public abstract void RemoveDependencies();
} }
} }

View File

@ -95,18 +95,18 @@ public sealed class BSCharacter : BSPhysObject
// the avatar seeking to reach the motor's target speed. // 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 // 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. // 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); PhysicalActors.Add(AvatarMoveActorName, m_moveActor);
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, _size, Scale, Density, _avatarVolume, RawMass); LocalID, _size, Scale, Density, _avatarVolume, RawMass);
// do actual creation in taint time // do actual creation in taint time
PhysicsScene.TaintedObject("BSCharacter.create", delegate() PhysScene.TaintedObject("BSCharacter.create", delegate()
{ {
DetailLog("{0},BSCharacter.create,taint", LocalID); DetailLog("{0},BSCharacter.create,taint", LocalID);
// New body and shape into PhysBody and PhysShape // New body and shape into PhysBody and PhysShape
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this); PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this);
SetPhysicalProperties(); SetPhysicalProperties();
}); });
@ -119,18 +119,18 @@ public sealed class BSCharacter : BSPhysObject
base.Destroy(); base.Destroy();
DetailLog("{0},BSCharacter.Destroy", LocalID); 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(); PhysBody.Clear();
PhysicsScene.Shapes.DereferenceShape(PhysShape, null /* bodyCallback */); PhysShape.Dereference(PhysScene);
PhysShape.Clear(); PhysShape = new BSShapeNull();
}); });
} }
private void SetPhysicalProperties() private void SetPhysicalProperties()
{ {
PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody);
ZeroMotion(true); ZeroMotion(true);
ForcePosition = _position; 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. // Needs to be reset especially when an avatar is recreated after crossing a region boundry.
Flying = _flying; Flying = _flying;
PhysicsScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); PhysScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution);
PhysicsScene.PE.SetMargin(PhysShape, PhysicsScene.Params.collisionMargin); PhysScene.PE.SetMargin(PhysShape.physShapeInfo, PhysScene.Params.collisionMargin);
PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale);
PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold);
if (BSParam.CcdMotionThreshold > 0f) if (BSParam.CcdMotionThreshold > 0f)
{ {
PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold);
PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius);
} }
UpdatePhysicalMassProperties(RawMass, false); UpdatePhysicalMassProperties(RawMass, false);
// Make so capsule does not fall over // 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. // The avatar mover sets some parameters.
PhysicalActors.Refresh(); 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.ACTIVE_TAG);
PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); PhysScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION);
PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody);
// Do this after the object has been added to the world // Do this after the object has been added to the world
PhysBody.collisionType = CollisionType.Avatar; 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}", DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, _size, Scale, Density, _avatarVolume, RawMass); 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); UpdatePhysicalMassProperties(RawMass, true);
// Make sure this change appears as a property update event // 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; } 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 { public override bool Grabbed {
set { _grabbed = value; } set { _grabbed = value; }
@ -252,24 +247,24 @@ public sealed class BSCharacter : BSPhysObject
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties directly into the physics engine // Zero some other properties directly into the physics engine
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.ClearAllForces(PhysBody); PhysScene.PE.ClearAllForces(PhysBody);
}); });
} }
public override void ZeroAngularMotion(bool inTaintTime) public override void ZeroAngularMotion(bool inTaintTime)
{ {
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero);
PhysicsScene.PE.SetAngularVelocity(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. // 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 { set {
_position = value; _position = value;
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() PhysScene.TaintedObject("BSCharacter.setPosition", delegate()
{ {
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
PositionSanityCheck(); PositionSanityCheck();
@ -301,14 +296,14 @@ public sealed class BSCharacter : BSPhysObject
} }
public override OMV.Vector3 ForcePosition { public override OMV.Vector3 ForcePosition {
get { get {
_position = PhysicsScene.PE.GetPosition(PhysBody); _position = PhysScene.PE.GetPosition(PhysBody);
return _position; return _position;
} }
set { set {
_position = value; _position = value;
if (PhysBody.HasPhysicalBody) 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; bool ret = false;
// TODO: check for out of bounds // 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. // The character is out of the known/simulated area.
// Force the avatar position to be within known. ScenePresence will use the position // 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. // 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); DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition);
return true; return true;
} }
// If below the ground, move the avatar up // If below the ground, move the avatar up
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
if (Position.Z < terrainHeight) if (Position.Z < terrainHeight)
{ {
DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, _position, 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) if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{ {
float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(_position);
if (Position.Z < waterHeight) if (Position.Z < waterHeight)
{ {
_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 // The new position value must be pushed into the physics engine but we can't
// just assign to "Position" because of potential call loops. // 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); DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
ForcePosition = _position; ForcePosition = _position;
@ -381,8 +376,8 @@ public sealed class BSCharacter : BSPhysObject
} }
public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
{ {
OMV.Vector3 localInertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); OMV.Vector3 localInertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass);
PhysicsScene.PE.SetMassProps(PhysBody, physMass, localInertia); PhysScene.PE.SetMassProps(PhysBody, physMass, localInertia);
} }
public override OMV.Vector3 Force { public override OMV.Vector3 Force {
@ -390,11 +385,11 @@ public sealed class BSCharacter : BSPhysObject
set { set {
RawForce = value; RawForce = value;
// m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); // 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); DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, RawForce);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.SetObjectForce(PhysBody, RawForce); PhysScene.PE.SetObjectForce(PhysBody, RawForce);
}); });
} }
} }
@ -437,7 +432,7 @@ public sealed class BSCharacter : BSPhysObject
set { set {
RawVelocity = value; RawVelocity = value;
// m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, RawVelocity); // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, RawVelocity);
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() PhysScene.TaintedObject("BSCharacter.setVelocity", delegate()
{ {
if (m_moveActor != null) if (m_moveActor != null)
m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, true /* inTaintTime */); m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, true /* inTaintTime */);
@ -450,11 +445,11 @@ public sealed class BSCharacter : BSPhysObject
public override OMV.Vector3 ForceVelocity { public override OMV.Vector3 ForceVelocity {
get { return RawVelocity; } get { return RawVelocity; }
set { set {
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity"); PhysScene.AssertInTaintTime("BSCharacter.ForceVelocity");
RawVelocity = value; RawVelocity = value;
PhysicsScene.PE.SetLinearVelocity(PhysBody, RawVelocity); PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity);
PhysicsScene.PE.Activate(PhysBody, true); PhysScene.PE.Activate(PhysBody, true);
} }
} }
public override OMV.Vector3 Torque { public override OMV.Vector3 Torque {
@ -484,7 +479,7 @@ public sealed class BSCharacter : BSPhysObject
if (_orientation != value) if (_orientation != value)
{ {
_orientation = value; _orientation = value;
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() PhysScene.TaintedObject("BSCharacter.setOrientation", delegate()
{ {
ForceOrientation = _orientation; ForceOrientation = _orientation;
}); });
@ -496,7 +491,7 @@ public sealed class BSCharacter : BSPhysObject
{ {
get get
{ {
_orientation = PhysicsScene.PE.GetOrientation(PhysBody); _orientation = PhysScene.PE.GetOrientation(PhysBody);
return _orientation; return _orientation;
} }
set set
@ -505,7 +500,7 @@ public sealed class BSCharacter : BSPhysObject
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
// _position = PhysicsScene.PE.GetPosition(BSBody); // _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 { public override bool FloatOnWater {
set { set {
_floatOnWater = value; _floatOnWater = value;
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() PhysScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
if (_floatOnWater) if (_floatOnWater)
CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
else 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 { public override float Buoyancy {
get { return _buoyancy; } get { return _buoyancy; }
set { _buoyancy = value; set { _buoyancy = value;
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() PhysScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
{ {
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
ForceBuoyancy = _buoyancy; ForceBuoyancy = _buoyancy;
@ -592,7 +587,7 @@ public sealed class BSCharacter : BSPhysObject
public override float ForceBuoyancy { public override float ForceBuoyancy {
get { return _buoyancy; } get { return _buoyancy; }
set { set {
PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
_buoyancy = value; _buoyancy = value;
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
@ -600,7 +595,7 @@ public sealed class BSCharacter : BSPhysObject
float grav = BSParam.Gravity * (1f - _buoyancy); float grav = BSParam.Gravity * (1f - _buoyancy);
Gravity = new OMV.Vector3(0f, 0f, grav); Gravity = new OMV.Vector3(0f, 0f, grav);
if (PhysBody.HasPhysicalBody) 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) 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. // 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); AddForce(addForce, pushforce, false);
} }
private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { 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); OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude);
// DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); // 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 // Bullet adds this central force to the total force for this tick
// DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); PhysScene.PE.ApplyCentralForce(PhysBody, addForce);
} }
}); });
} }

View File

@ -559,9 +559,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
break; break;
} }
// Update any physical parameters based on this type.
Refresh();
m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
m_linearMotorDecayTimescale, m_linearFrictionTimescale, m_linearMotorDecayTimescale, m_linearFrictionTimescale,
1f); 1f);
@ -589,6 +586,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
RegisterForSceneEvents(); RegisterForSceneEvents();
} }
// Update any physical parameters based on this type.
Refresh();
} }
#endregion // Vehicle parameter setting #endregion // Vehicle parameter setting
@ -596,6 +596,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
public override void Refresh() public override void Refresh()
{ {
// If asking for a refresh, reset the physical parameters before the next simulation step. // 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() m_physicsScene.PostTaintObject("BSDynamics.Refresh", ControllingPrim.LocalID, delegate()
{ {
SetPhysicalParameters(); SetPhysicalParameters();
@ -625,7 +626,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Vehicles report collision events so we know when it's on the ground // Vehicles report collision events so we know when it's on the ground
m_physicsScene.PE.AddToCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); 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.SetMassProps(ControllingPrim.PhysBody, m_vehicleMass, ControllingPrim.Inertia);
m_physicsScene.PE.UpdateInertiaTensor(ControllingPrim.PhysBody); m_physicsScene.PE.UpdateInertiaTensor(ControllingPrim.PhysBody);
@ -649,7 +650,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
} }
// BSActor.RemoveBodyDependencies // BSActor.RemoveBodyDependencies
public override void RemoveBodyDependencies() public override void RemoveDependencies()
{ {
Refresh(); Refresh();
} }
@ -789,7 +790,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
if ((m_knownHas & m_knownChangedTerrainHeight) == 0 || pos != lastRememberedHeightPos) if ((m_knownHas & m_knownChangedTerrainHeight) == 0 || pos != lastRememberedHeightPos)
{ {
lastRememberedHeightPos = pos; lastRememberedHeightPos = pos;
m_knownTerrainHeight = ControllingPrim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); m_knownTerrainHeight = ControllingPrim.PhysScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
m_knownHas |= m_knownChangedTerrainHeight; m_knownHas |= m_knownChangedTerrainHeight;
} }
return m_knownTerrainHeight; return m_knownTerrainHeight;
@ -801,7 +802,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
if ((m_knownHas & m_knownChangedWaterLevel) == 0) if ((m_knownHas & m_knownChangedWaterLevel) == 0)
{ {
m_knownWaterLevel = ControllingPrim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos); m_knownWaterLevel = ControllingPrim.PhysScene.TerrainManager.GetWaterLevelAtXYZ(pos);
m_knownHas |= m_knownChangedWaterLevel; m_knownHas |= m_knownChangedWaterLevel;
} }
return (float)m_knownWaterLevel; return (float)m_knownWaterLevel;
@ -1637,8 +1638,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Invoke the detailed logger and output something if it's enabled. // Invoke the detailed logger and output something if it's enabled.
private void VDetailLog(string msg, params Object[] args) private void VDetailLog(string msg, params Object[] args)
{ {
if (ControllingPrim.PhysicsScene.VehicleLoggingEnabled) if (ControllingPrim.PhysScene.VehicleLoggingEnabled)
ControllingPrim.PhysicsScene.DetailLog(msg, args); ControllingPrim.PhysScene.DetailLog(msg, args);
} }
} }
} }

View File

@ -80,7 +80,7 @@ public abstract class BSLinkset
public BSPrimLinkable LinksetRoot { get; protected set; } public BSPrimLinkable LinksetRoot { get; protected set; }
public BSScene PhysicsScene { get; private set; } protected BSScene m_physicsScene { get; private set; }
static int m_nextLinksetID = 1; static int m_nextLinksetID = 1;
public int LinksetID { get; private set; } public int LinksetID { get; private set; }
@ -93,13 +93,6 @@ public abstract class BSLinkset
// to the physical representation is done via the tainting mechenism. // to the physical representation is done via the tainting mechenism.
protected object m_linksetActivityLock = new Object(); 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 // We keep the prim's mass in the linkset structure since it could be dependent on other prims
public float LinksetMass { get; protected set; } public float LinksetMass { get; protected set; }
@ -122,7 +115,7 @@ public abstract class BSLinkset
// We create LOTS of linksets. // We create LOTS of linksets.
if (m_nextLinksetID <= 0) if (m_nextLinksetID <= 0)
m_nextLinksetID = 1; m_nextLinksetID = 1;
PhysicsScene = scene; m_physicsScene = scene;
LinksetRoot = parent; LinksetRoot = parent;
m_children = new HashSet<BSPrimLinkable>(); m_children = new HashSet<BSPrimLinkable>();
LinksetMass = parent.RawMass; LinksetMass = parent.RawMass;
@ -165,7 +158,7 @@ public abstract class BSLinkset
} }
// The child is down to a linkset of just itself // 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 // 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. // This is called when the root body is changing.
// Returns 'true' of something was actually removed and would need restoring // Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!! // Called at taint-time!!
public abstract bool RemoveBodyDependencies(BSPrimLinkable child); public abstract bool RemoveDependencies(BSPrimLinkable child);
// ================================================================ // ================================================================
protected virtual float ComputeLinksetMass() protected virtual float ComputeLinksetMass()
@ -323,8 +316,8 @@ public abstract class BSLinkset
// Invoke the detailed logger and output something if it's enabled. // Invoke the detailed logger and output something if it's enabled.
protected void DetailLog(string msg, params Object[] args) protected void DetailLog(string msg, params Object[] args)
{ {
if (PhysicsScene.PhysicsLogging.Enabled) if (m_physicsScene.PhysicsLogging.Enabled)
PhysicsScene.DetailLog(msg, args); m_physicsScene.DetailLog(msg, args);
} }
} }

View File

@ -35,6 +35,7 @@ using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
/*
// When a child is linked, the relationship position of the child to the parent // 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 // is remembered so the child's world position can be recomputed when it is
// removed from the linkset. // removed from the linkset.
@ -88,6 +89,7 @@ sealed class BSLinksetCompoundInfo : BSLinksetInfo
return buff.ToString(); return buff.ToString();
} }
}; };
*/
public sealed class BSLinksetCompound : BSLinkset 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 // When physical properties are changed the linkset needs to recalculate
// its internal properties. // its internal properties.
public override void Refresh(BSPrimLinkable requestor) 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 a linkset with just a root prim (simple non-linked prim) don't bother rebuilding.
if (!Rebuilding && HasAnyChildren) if (!Rebuilding && HasAnyChildren)
{ {
PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
{ {
if (HasAnyChildren) if (HasAnyChildren)
RecomputeLinksetCompound(); 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. // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly.
ScheduleRebuild(LinksetRoot); 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; 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. // 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! // Called at taint-time!
public override bool MakeStatic(BSPrimLinkable child) 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)); DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
if (IsRoot(child)) if (IsRoot(child))
{ {
// Schedule a rebuild to verify that the root shape is set to the real shape.
ScheduleRebuild(LinksetRoot); 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; return ret;
} }
@ -200,13 +164,20 @@ public sealed class BSLinksetCompound : BSLinkset
// Called at taint-time. // Called at taint-time.
public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated) 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 // 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 // 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 // stores the position into the group which causes the move of the object
// but it also means all the child positions get updated. // 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 // What would cause an unnecessary rebuild so we make sure the linkset is in a
// region before bothering to do a rebuild. // 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 // If a child of the linkset is updating only the position or rotation, that can be done
// without rebuilding the linkset. // without rebuilding the linkset.
@ -219,21 +190,21 @@ public sealed class BSLinksetCompound : BSLinkset
if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
{ {
// Find the physical instance of the child // 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 // 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 // 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. // 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 // The index must be checked because Bullet references the child array but does no validity
// checking of the child index passed. // 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) 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) if (linksetChildShape.HasPhysicalShape)
{ {
// Found the child shape within the compound shape // 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.RawPosition - LinksetRoot.RawPosition,
updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation), updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation),
true /* shouldRecalculateLocalAabb */); true /* shouldRecalculateLocalAabb */);
@ -275,75 +246,22 @@ public sealed class BSLinksetCompound : BSLinkset
} }
// Routine called when rebuilding the body of some member of the linkset. // 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 // Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!! // Called at taint-time!!
public override bool RemoveBodyDependencies(BSPrimLinkable child) public override bool RemoveDependencies(BSPrimLinkable child)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child)); child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child));
if (!IsRoot(child)) ScheduleRebuild(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);
return ret; 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. // Add a new child to the linkset.
@ -376,7 +294,6 @@ public sealed class BSLinksetCompound : BSLinkset
child.LocalID, child.PhysBody.AddrString); child.LocalID, child.PhysBody.AddrString);
// Cause the child's body to be rebuilt and thus restored to normal operation // Cause the child's body to be rebuilt and thus restored to normal operation
RecomputeChildWorldPosition(child, false);
child.LinksetInfo = null; child.LinksetInfo = null;
child.ForceBodyShapeRebuild(false); child.ForceBodyShapeRebuild(false);
@ -399,108 +316,105 @@ public sealed class BSLinksetCompound : BSLinkset
// Constraint linksets are rebuilt every time. // Constraint linksets are rebuilt every time.
// Note that this works for rebuilding just the root after a linkset is taken apart. // Note that this works for rebuilding just the root after a linkset is taken apart.
// Called at taint time!! // 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() private void RecomputeLinksetCompound()
{ {
try try
{ {
// Suppress rebuilding while rebuilding. (We know rebuilding is on only one thread.)
Rebuilding = true; Rebuilding = true;
// Cause the root shape to be rebuilt as a compound object with just the root in it // No matter what is being done, force the root prim's PhysBody and PhysShape to get set
LinksetRoot.ForceBodyShapeRebuild(true /* inTaintTime */); // 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. // 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. // 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 // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass
OMV.Vector3 centerOfMassW = LinksetRoot.RawPosition; OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass();
if (!disableCOM) // DEBUG DEBUG
{
// Compute a center-of-mass in world coordinates.
centerOfMassW = ComputeLinksetCenterOfMass();
}
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
// 'centerDisplacement' is the value to subtract from children to give physical offset position // 'centerDisplacement' is the value to subtract from children to give physical offset position
OMV.Vector3 centerDisplacement = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation; 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 // Add the shapes of all the components of the linkset
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
int memberIndex = 1; int memberIndex = 1;
ForEachMember(delegate(BSPrimLinkable cPrim) ForEachMember(delegate(BSPrimLinkable cPrim)
{ {
if (IsRoot(cPrim)) // Root shape is always index zero.
{ cPrim.LinksetChildIndex = IsRoot(cPrim) ? 0 : memberIndex;
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;
// 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.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement;
OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation;
PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}",
LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); LinksetRoot.LocalID, memberIndex, childShape, 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);
// 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++; memberIndex++;
}
return false; // 'false' says to move onto the next child in the list 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. // With all of the linkset packed into the root prim, it has the mass of everyone.
LinksetMass = ComputeLinksetMass(); LinksetMass = ComputeLinksetMass();
LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
// Enable the physical position updator to return the position and rotation of the root shape if (UseBulletSimRootOffsetHack)
PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); {
// 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 finally
{ {
@ -508,7 +422,7 @@ public sealed class BSLinksetCompound : BSLinkset
} }
// See that the Aabb surrounds the new shape // See that the Aabb surrounds the new shape
PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape); m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo);
} }
} }
} }

View File

@ -51,7 +51,7 @@ public sealed class BSLinksetConstraints : BSLinkset
if (HasAnyChildren && IsRoot(requestor)) if (HasAnyChildren && IsRoot(requestor))
{ {
// Queue to happen after all the other taint processing // 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)) if (HasAnyChildren && IsRoot(requestor))
RecomputeLinksetConstraints(); RecomputeLinksetConstraints();
@ -93,11 +93,11 @@ public sealed class BSLinksetConstraints : BSLinkset
// up to rebuild the constraints before the next simulation step. // up to rebuild the constraints before the next simulation step.
// Returns 'true' of something was actually removed and would need restoring // Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!! // Called at taint-time!!
public override bool RemoveBodyDependencies(BSPrimLinkable child) public override bool RemoveDependencies(BSPrimLinkable child)
{ {
bool ret = false; 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); child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString);
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
@ -142,7 +142,7 @@ public sealed class BSLinksetConstraints : BSLinkset
rootx.LocalID, rootx.PhysBody.AddrString, rootx.LocalID, rootx.PhysBody.AddrString,
childx.LocalID, childx.PhysBody.AddrString); childx.LocalID, childx.PhysBody.AddrString);
PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate() m_physicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
{ {
PhysicallyUnlinkAChildFromRoot(rootx, childx); PhysicallyUnlinkAChildFromRoot(rootx, childx);
}); });
@ -187,7 +187,7 @@ public sealed class BSLinksetConstraints : BSLinkset
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
BSConstraint6Dof constrain = new BSConstraint6Dof( 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 ); // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
/* NOTE: below is an attempt to build constraint with full frame computation, etc. /* 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 // zero linear and angular limits makes the objects unable to move in relation to each other
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
@ -248,10 +248,10 @@ public sealed class BSLinksetConstraints : BSLinkset
childPrim.LocalID, childPrim.PhysBody.AddrString); childPrim.LocalID, childPrim.PhysBody.AddrString);
// Find the constraint for this link and get rid of it from the overall collection and from my list // 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 // Make the child refresh its location
PhysicsScene.PE.PushUpdate(childPrim.PhysBody); m_physicsScene.PE.PushUpdate(childPrim.PhysBody);
ret = true; ret = true;
} }
@ -265,7 +265,7 @@ public sealed class BSLinksetConstraints : BSLinkset
{ {
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); 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 // 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); child.UpdatePhysicalMassProperties(linksetMass, true);
BSConstraint constrain; 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. // If constraint doesn't exist yet, create it.
constrain = BuildConstraint(LinksetRoot, child); constrain = BuildConstraint(LinksetRoot, child);

View File

@ -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 ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
public static bool ShouldRemoveZeroWidthTriangles { get; private set; } public static bool ShouldRemoveZeroWidthTriangles { get; private set; }
public static bool ShouldUseBulletHACD { get; set; } public static bool ShouldUseBulletHACD { get; set; }
public static bool ShouldUseSingleConvexHullForPrims { get; set; }
public static float TerrainImplementation { get; private set; } public static float TerrainImplementation { get; private set; }
public static int TerrainMeshMagnification { get; private set; } public static int TerrainMeshMagnification { get; private set; }
@ -342,6 +343,10 @@ public static class BSParam
false, false,
(s) => { return ShouldUseBulletHACD; }, (s) => { return ShouldUseBulletHACD; },
(s,v) => { ShouldUseBulletHACD = v; } ), (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", new ParameterDefn<int>("CrossingFailuresBeforeOutOfBounds", "How forgiving we are about getting into adjactent regions",
5, 5,

View File

@ -72,14 +72,14 @@ public abstract class BSPhysObject : PhysicsActor
} }
protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName) protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
{ {
PhysicsScene = parentScene; PhysScene = parentScene;
LocalID = localID; LocalID = localID;
PhysObjectName = name; PhysObjectName = name;
Name = name; // PhysicsActor also has the name of the object. Someday consolidate. Name = name; // PhysicsActor also has the name of the object. Someday consolidate.
TypeName = typeName; TypeName = typeName;
// The collection of things that push me around // The collection of things that push me around
PhysicalActors = new BSActorCollection(PhysicsScene); PhysicalActors = new BSActorCollection(PhysScene);
// Initialize variables kept in base. // Initialize variables kept in base.
GravModifier = 1.0f; GravModifier = 1.0f;
@ -88,7 +88,7 @@ public abstract class BSPhysObject : PhysicsActor
// We don't have any physical representation yet. // We don't have any physical representation yet.
PhysBody = new BulletBody(localID); PhysBody = new BulletBody(localID);
PhysShape = new BulletShape(); PhysShape = new BSShapeNull();
PrimAssetState = PrimAssetCondition.Unknown; PrimAssetState = PrimAssetCondition.Unknown;
@ -97,6 +97,9 @@ public abstract class BSPhysObject : PhysicsActor
CollisionCollection = new CollisionEventUpdate(); CollisionCollection = new CollisionEventUpdate();
CollisionsLastReported = CollisionCollection; CollisionsLastReported = CollisionCollection;
CollisionsLastTick = new CollisionEventUpdate();
CollisionsLastTickStep = -1;
SubscribedEventsMs = 0; SubscribedEventsMs = 0;
CollidingStep = 0; CollidingStep = 0;
CollidingGroundStep = 0; CollidingGroundStep = 0;
@ -112,13 +115,13 @@ public abstract class BSPhysObject : PhysicsActor
public virtual void Destroy() public virtual void Destroy()
{ {
PhysicalActors.Enable(false); PhysicalActors.Enable(false);
PhysicsScene.TaintedObject("BSPhysObject.Destroy", delegate() PhysScene.TaintedObject("BSPhysObject.Destroy", delegate()
{ {
PhysicalActors.Dispose(); 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 override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
public string PhysObjectName { get; protected set; } public string PhysObjectName { get; protected set; }
public string TypeName { 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 // Reference to the physical body (btCollisionObject) of this object
public BulletBody PhysBody; public BulletBody PhysBody;
// Reference to the physical shape (btCollisionShape) of this object // 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 physical representation of the prim might require an asset fetch.
// The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'. // 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. // The objects base shape information. Null if not a prim type shape.
public PrimitiveBaseShape BaseShape { get; protected set; } 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. // When the physical properties are updated, an EntityProperty holds the update values.
// Keep the current and last EntityProperties to enable computation of differences // Keep the current and last EntityProperties to enable computation of differences
// between the current update and the previous values. // 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 // The user can optionally set the center of mass. The user's setting will override any
// computed center-of-mass (like in linksets). // 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 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 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) public void ActivateIfPhysical(bool forceIt)
{ {
if (IsPhysical && PhysBody.HasPhysicalBody) 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 // '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; } protected long CollisionAccumulation { get; set; }
public override bool IsColliding { public override bool IsColliding {
get { return (CollidingStep == PhysicsScene.SimulationStep); } get { return (CollidingStep == PhysScene.SimulationStep); }
set { set {
if (value) if (value)
CollidingStep = PhysicsScene.SimulationStep; CollidingStep = PhysScene.SimulationStep;
else else
CollidingStep = 0; CollidingStep = 0;
} }
} }
public override bool CollidingGround { public override bool CollidingGround {
get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } get { return (CollidingGroundStep == PhysScene.SimulationStep); }
set set
{ {
if (value) if (value)
CollidingGroundStep = PhysicsScene.SimulationStep; CollidingGroundStep = PhysScene.SimulationStep;
else else
CollidingGroundStep = 0; CollidingGroundStep = 0;
} }
} }
public override bool CollidingObj { public override bool CollidingObj {
get { return (CollidingObjectStep == PhysicsScene.SimulationStep); } get { return (CollidingObjectStep == PhysScene.SimulationStep); }
set { set {
if (value) if (value)
CollidingObjectStep = PhysicsScene.SimulationStep; CollidingObjectStep = PhysScene.SimulationStep;
else else
CollidingObjectStep = 0; CollidingObjectStep = 0;
} }
@ -387,14 +384,14 @@ public abstract class BSPhysObject : PhysicsActor
bool ret = false; bool ret = false;
// The following lines make IsColliding(), CollidingGround() and CollidingObj work // The following lines make IsColliding(), CollidingGround() and CollidingObj work
CollidingStep = PhysicsScene.SimulationStep; CollidingStep = PhysScene.SimulationStep;
if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID) if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID)
{ {
CollidingGroundStep = PhysicsScene.SimulationStep; CollidingGroundStep = PhysScene.SimulationStep;
} }
else else
{ {
CollidingObjectStep = PhysicsScene.SimulationStep; CollidingObjectStep = PhysScene.SimulationStep;
} }
CollisionAccumulation++; CollisionAccumulation++;
@ -404,10 +401,10 @@ public abstract class BSPhysObject : PhysicsActor
// Make a collection of the collisions that happened the last simulation tick. // 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. // 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(); CollisionsLastTick = new CollisionEventUpdate();
CollisionsLastTickStep = PhysicsScene.SimulationStep; CollisionsLastTickStep = PhysScene.SimulationStep;
} }
CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); 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); bool force = (CollisionCollection.Count == 0 && CollisionsLastReported.Count != 0);
// throttle the collisions to the number of milliseconds specified in the subscription // 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 // 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. // 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 // make sure first collision happens
NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs); NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() PhysScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
}); });
} }
else else
@ -486,11 +483,11 @@ public abstract class BSPhysObject : PhysicsActor
public override void UnSubscribeEvents() { public override void UnSubscribeEvents() {
// DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
SubscribedEventsMs = 0; 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. // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
if (PhysBody.HasPhysicalBody) 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 // 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. // Scale the collision count by the time since the last collision.
// The "+1" prevents dividing by zero. // The "+1" prevents dividing by zero.
long timeAgo = PhysicsScene.SimulationStep - CollidingStep + 1; long timeAgo = PhysScene.SimulationStep - CollidingStep + 1;
CollisionScore = CollisionAccumulation / timeAgo; CollisionScore = CollisionAccumulation / timeAgo;
} }
public override float CollisionScore { get; set; } 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. // High performance detailed logging routine used by the physical objects.
protected void DetailLog(string msg, params Object[] args) protected void DetailLog(string msg, params Object[] args)
{ {
if (PhysicsScene.PhysicsLogging.Enabled) if (PhysScene.PhysicsLogging.Enabled)
PhysicsScene.DetailLog(msg, args); PhysScene.DetailLog(msg, args);
} }
} }

View File

@ -101,21 +101,21 @@ public class BSPrim : BSPhysObject
_isVolumeDetect = false; _isVolumeDetect = false;
// We keep a handle to the vehicle actor so we can set vehicle parameters later. // 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); PhysicalActors.Add(VehicleActorName, VehicleActor);
_mass = CalculateMass(); _mass = CalculateMass();
// DetailLog("{0},BSPrim.constructor,call", LocalID); // DetailLog("{0},BSPrim.constructor,call", LocalID);
// do the actual object creation at taint time // 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. // Make sure the object is being created with some sanity.
ExtremeSanityCheck(true /* inTaintTime */); ExtremeSanityCheck(true /* inTaintTime */);
CreateGeomAndObject(true); CreateGeomAndObject(true);
CurrentCollisionFlags = PhysicsScene.PE.GetCollisionFlags(PhysBody); CurrentCollisionFlags = PhysScene.PE.GetCollisionFlags(PhysBody);
}); });
} }
@ -128,14 +128,14 @@ public class BSPrim : BSPhysObject
// Undo any vehicle properties // Undo any vehicle properties
this.VehicleType = (int)Vehicle.TYPE_NONE; this.VehicleType = (int)Vehicle.TYPE_NONE;
PhysicsScene.TaintedObject("BSPrim.Destroy", delegate() PhysScene.TaintedObject("BSPrim.Destroy", delegate()
{ {
DetailLog("{0},BSPrim.Destroy,taint,", LocalID); DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
// If there are physical body and shape, release my use of same. // If there are physical body and shape, release my use of same.
PhysicsScene.Shapes.DereferenceBody(PhysBody, null); PhysScene.Shapes.DereferenceBody(PhysBody, null);
PhysBody.Clear(); PhysBody.Clear();
PhysicsScene.Shapes.DereferenceShape(PhysShape, null); PhysShape.Dereference(PhysScene);
PhysShape.Clear(); PhysShape = new BSShapeNull();
}); });
} }
@ -161,25 +161,13 @@ public class BSPrim : BSPhysObject
ForceBodyShapeRebuild(false); ForceBodyShapeRebuild(false);
} }
} }
// 'unknown' says to choose the best type
public override BSPhysicsShapeType PreferredPhysicalShape
{ get { return BSPhysicsShapeType.SHAPE_UNKNOWN; } }
public override bool ForceBodyShapeRebuild(bool inTaintTime) public override bool ForceBodyShapeRebuild(bool inTaintTime)
{ {
if (inTaintTime) PhysScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate()
{
_mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true);
}
else
{
PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", delegate()
{ {
_mass = CalculateMass(); // changing the shape changes the mass _mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true); CreateGeomAndObject(true);
}); });
}
return true; return true;
} }
public override bool Grabbed { public override bool Grabbed {
@ -192,7 +180,7 @@ public class BSPrim : BSPhysObject
if (value != _isSelected) if (value != _isSelected)
{ {
_isSelected = value; _isSelected = value;
PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() PhysScene.TaintedObject("BSPrim.setSelected", delegate()
{ {
DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
SetObjectDynamic(false); SetObjectDynamic(false);
@ -238,23 +226,23 @@ public class BSPrim : BSPhysObject
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties in the physics engine // Zero some other properties in the physics engine
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() PhysScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.ClearAllForces(PhysBody); PhysScene.PE.ClearAllForces(PhysBody);
}); });
} }
public override void ZeroAngularMotion(bool inTaintTime) public override void ZeroAngularMotion(bool inTaintTime)
{ {
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties in the physics engine // 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); // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity);
PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity);
} }
}); });
} }
@ -272,11 +260,11 @@ public class BSPrim : BSPhysObject
EnableActor(LockedAxis != LockedAxisFree, LockedAxisActorName, delegate() 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. // 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(); UpdatePhysicalParameters();
}); });
@ -306,7 +294,7 @@ public class BSPrim : BSPhysObject
_position = value; _position = value;
PositionSanityCheck(false); PositionSanityCheck(false);
PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() PhysScene.TaintedObject("BSPrim.setPosition", delegate()
{ {
DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
ForcePosition = _position; ForcePosition = _position;
@ -316,14 +304,14 @@ public class BSPrim : BSPhysObject
public override OMV.Vector3 ForcePosition { public override OMV.Vector3 ForcePosition {
get { get {
_position = PhysicsScene.PE.GetPosition(PhysBody); _position = PhysScene.PE.GetPosition(PhysBody);
return _position; return _position;
} }
set { set {
_position = value; _position = value;
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); PhysScene.PE.SetTranslation(PhysBody, _position, _orientation);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
} }
@ -340,7 +328,7 @@ public class BSPrim : BSPhysObject
if (!IsPhysicallyActive) if (!IsPhysicallyActive)
return ret; return ret;
if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition))
{ {
// The physical object is out of the known/simulated area. // The physical object is out of the known/simulated area.
// Upper levels of code will handle the transition to other areas so, for // Upper levels of code will handle the transition to other areas so, for
@ -348,7 +336,7 @@ public class BSPrim : BSPhysObject
return ret; return ret;
} }
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
OMV.Vector3 upForce = OMV.Vector3.Zero; OMV.Vector3 upForce = OMV.Vector3.Zero;
float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z)); float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z));
if ((RawPosition.Z + approxSize / 2f) < terrainHeight) if ((RawPosition.Z + approxSize / 2f) < terrainHeight)
@ -369,7 +357,7 @@ public class BSPrim : BSPhysObject
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 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 // TODO: a floating motor so object will bob in the water
if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
{ {
@ -377,7 +365,7 @@ public class BSPrim : BSPhysObject
upForce.Z = (waterHeight - RawPosition.Z) * 1f; upForce.Z = (waterHeight - RawPosition.Z) * 1f;
// Apply upforce and overcome gravity. // 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); DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
AddForce(correctionForce, false, inTaintTime); AddForce(correctionForce, false, inTaintTime);
ret = true; ret = true;
@ -443,10 +431,10 @@ public class BSPrim : BSPhysObject
{ {
if (IsStatic) if (IsStatic)
{ {
PhysicsScene.PE.SetGravity(PhysBody, PhysicsScene.DefaultGravity); PhysScene.PE.SetGravity(PhysBody, PhysScene.DefaultGravity);
Inertia = OMV.Vector3.Zero; Inertia = OMV.Vector3.Zero;
PhysicsScene.PE.SetMassProps(PhysBody, 0f, Inertia); PhysScene.PE.SetMassProps(PhysBody, 0f, Inertia);
PhysicsScene.PE.UpdateInertiaTensor(PhysBody); PhysScene.PE.UpdateInertiaTensor(PhysBody);
} }
else else
{ {
@ -455,16 +443,16 @@ public class BSPrim : BSPhysObject
// Changing interesting properties doesn't change proxy and collision cache // Changing interesting properties doesn't change proxy and collision cache
// information. The Bullet solution is to re-add the object to the world // information. The Bullet solution is to re-add the object to the world
// after parameters are changed. // 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. // The computation of mass props requires gravity to be set on the object.
Gravity = ComputeGravity(Buoyancy); Gravity = ComputeGravity(Buoyancy);
PhysicsScene.PE.SetGravity(PhysBody, Gravity); PhysScene.PE.SetGravity(PhysBody, Gravity);
Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); Inertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass);
PhysicsScene.PE.SetMassProps(PhysBody, physMass, Inertia); PhysScene.PE.SetMassProps(PhysBody, physMass, Inertia);
PhysicsScene.PE.UpdateInertiaTensor(PhysBody); PhysScene.PE.UpdateInertiaTensor(PhysBody);
DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}",
LocalID, physMass, Inertia, Gravity, inWorld); LocalID, physMass, Inertia, Gravity, inWorld);
@ -480,7 +468,7 @@ public class BSPrim : BSPhysObject
// Return what gravity should be set to this very moment // Return what gravity should be set to this very moment
public OMV.Vector3 ComputeGravity(float buoyancy) public OMV.Vector3 ComputeGravity(float buoyancy)
{ {
OMV.Vector3 ret = PhysicsScene.DefaultGravity; OMV.Vector3 ret = PhysScene.DefaultGravity;
if (!IsStatic) if (!IsStatic)
{ {
@ -509,7 +497,7 @@ public class BSPrim : BSPhysObject
RawForce = value; RawForce = value;
EnableActor(RawForce != OMV.Vector3.Zero, SetForceActorName, delegate() 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 { set {
Vehicle type = (Vehicle)value; 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); VehicleActor.ProcessTypeChange(type);
ActivateIfPhysical(false); ActivateIfPhysical(false);
}); });
@ -531,7 +519,7 @@ public class BSPrim : BSPhysObject
} }
public override void VehicleFloatParam(int param, float value) public override void VehicleFloatParam(int param, float value)
{ {
PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() PhysScene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
{ {
VehicleActor.ProcessFloatVehicleParam((Vehicle)param, value); VehicleActor.ProcessFloatVehicleParam((Vehicle)param, value);
ActivateIfPhysical(false); ActivateIfPhysical(false);
@ -539,7 +527,7 @@ public class BSPrim : BSPhysObject
} }
public override void VehicleVectorParam(int param, OMV.Vector3 value) public override void VehicleVectorParam(int param, OMV.Vector3 value)
{ {
PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() PhysScene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
{ {
VehicleActor.ProcessVectorVehicleParam((Vehicle)param, value); VehicleActor.ProcessVectorVehicleParam((Vehicle)param, value);
ActivateIfPhysical(false); ActivateIfPhysical(false);
@ -547,7 +535,7 @@ public class BSPrim : BSPhysObject
} }
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
{ {
PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() PhysScene.TaintedObject("BSPrim.VehicleRotationParam", delegate()
{ {
VehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation); VehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation);
ActivateIfPhysical(false); ActivateIfPhysical(false);
@ -555,7 +543,7 @@ public class BSPrim : BSPhysObject
} }
public override void VehicleFlags(int param, bool remove) public override void VehicleFlags(int param, bool remove)
{ {
PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() PhysScene.TaintedObject("BSPrim.VehicleFlags", delegate()
{ {
VehicleActor.ProcessVehicleFlags(param, remove); VehicleActor.ProcessVehicleFlags(param, remove);
}); });
@ -567,7 +555,7 @@ public class BSPrim : BSPhysObject
if (_isVolumeDetect != newValue) if (_isVolumeDetect != newValue)
{ {
_isVolumeDetect = newValue; _isVolumeDetect = newValue;
PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() PhysScene.TaintedObject("BSPrim.SetVolumeDetect", delegate()
{ {
// DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect);
SetObjectDynamic(true); SetObjectDynamic(true);
@ -578,7 +566,7 @@ public class BSPrim : BSPhysObject
public override void SetMaterial(int material) public override void SetMaterial(int material)
{ {
base.SetMaterial(material); base.SetMaterial(material);
PhysicsScene.TaintedObject("BSPrim.SetMaterial", delegate() PhysScene.TaintedObject("BSPrim.SetMaterial", delegate()
{ {
UpdatePhysicalParameters(); UpdatePhysicalParameters();
}); });
@ -591,7 +579,7 @@ public class BSPrim : BSPhysObject
if (base.Friction != value) if (base.Friction != value)
{ {
base.Friction = value; base.Friction = value;
PhysicsScene.TaintedObject("BSPrim.setFriction", delegate() PhysScene.TaintedObject("BSPrim.setFriction", delegate()
{ {
UpdatePhysicalParameters(); UpdatePhysicalParameters();
}); });
@ -606,7 +594,7 @@ public class BSPrim : BSPhysObject
if (base.Restitution != value) if (base.Restitution != value)
{ {
base.Restitution = value; base.Restitution = value;
PhysicsScene.TaintedObject("BSPrim.setRestitution", delegate() PhysScene.TaintedObject("BSPrim.setRestitution", delegate()
{ {
UpdatePhysicalParameters(); UpdatePhysicalParameters();
}); });
@ -623,7 +611,7 @@ public class BSPrim : BSPhysObject
if (base.Density != value) if (base.Density != value)
{ {
base.Density = value; base.Density = value;
PhysicsScene.TaintedObject("BSPrim.setDensity", delegate() PhysScene.TaintedObject("BSPrim.setDensity", delegate()
{ {
UpdatePhysicalParameters(); UpdatePhysicalParameters();
}); });
@ -638,7 +626,7 @@ public class BSPrim : BSPhysObject
if (base.GravModifier != value) if (base.GravModifier != value)
{ {
base.GravModifier = value; base.GravModifier = value;
PhysicsScene.TaintedObject("BSPrim.setGravityModifier", delegate() PhysScene.TaintedObject("BSPrim.setGravityModifier", delegate()
{ {
UpdatePhysicalParameters(); UpdatePhysicalParameters();
}); });
@ -649,7 +637,7 @@ public class BSPrim : BSPhysObject
get { return RawVelocity; } get { return RawVelocity; }
set { set {
RawVelocity = value; RawVelocity = value;
PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() PhysScene.TaintedObject("BSPrim.setVelocity", delegate()
{ {
// DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, RawVelocity); // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, RawVelocity);
ForceVelocity = RawVelocity; ForceVelocity = RawVelocity;
@ -659,13 +647,13 @@ public class BSPrim : BSPhysObject
public override OMV.Vector3 ForceVelocity { public override OMV.Vector3 ForceVelocity {
get { return RawVelocity; } get { return RawVelocity; }
set { set {
PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity"); PhysScene.AssertInTaintTime("BSPrim.ForceVelocity");
RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, RawVelocity); DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, RawVelocity);
PhysicsScene.PE.SetLinearVelocity(PhysBody, RawVelocity); PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
} }
@ -676,7 +664,7 @@ public class BSPrim : BSPhysObject
RawTorque = value; RawTorque = value;
EnableActor(RawTorque != OMV.Vector3.Zero, SetTorqueActorName, delegate() 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); DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, RawTorque);
} }
@ -699,7 +687,7 @@ public class BSPrim : BSPhysObject
return; return;
_orientation = value; _orientation = value;
PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() PhysScene.TaintedObject("BSPrim.setOrientation", delegate()
{ {
ForceOrientation = _orientation; ForceOrientation = _orientation;
}); });
@ -710,14 +698,14 @@ public class BSPrim : BSPhysObject
{ {
get get
{ {
_orientation = PhysicsScene.PE.GetOrientation(PhysBody); _orientation = PhysScene.PE.GetOrientation(PhysBody);
return _orientation; return _orientation;
} }
set set
{ {
_orientation = value; _orientation = value;
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); PhysScene.PE.SetTranslation(PhysBody, _position, _orientation);
} }
} }
public override int PhysicsActorType { public override int PhysicsActorType {
@ -730,7 +718,7 @@ public class BSPrim : BSPhysObject
if (_isPhysical != value) if (_isPhysical != value)
{ {
_isPhysical = value; _isPhysical = value;
PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() PhysScene.TaintedObject("BSPrim.setIsPhysical", delegate()
{ {
DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical);
SetObjectDynamic(true); SetObjectDynamic(true);
@ -779,13 +767,13 @@ public class BSPrim : BSPhysObject
if (!PhysBody.HasPhysicalBody) if (!PhysBody.HasPhysicalBody)
{ {
// This would only happen if updates are called for during initialization when the body is not set up yet. // 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; return;
} }
// Mangling all the physical properties requires the object not be in the physical world. // 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). // 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) // Set up the object physicalness (does gravity and collisions move this object)
MakeDynamic(IsStatic); MakeDynamic(IsStatic);
@ -802,10 +790,11 @@ public class BSPrim : BSPhysObject
AddObjectToPhysicalWorld(); AddObjectToPhysicalWorld();
// Rebuild its shape // 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}", 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. // "Making dynamic" means changing to and from static.
@ -818,28 +807,28 @@ public class BSPrim : BSPhysObject
if (makeStatic) if (makeStatic)
{ {
// Become a Bullet 'static' object type // 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 // Stop all movement
ZeroMotion(true); ZeroMotion(true);
// Set various physical properties so other object interact properly // Set various physical properties so other object interact properly
PhysicsScene.PE.SetFriction(PhysBody, Friction); PhysScene.PE.SetFriction(PhysBody, Friction);
PhysicsScene.PE.SetRestitution(PhysBody, Restitution); PhysScene.PE.SetRestitution(PhysBody, Restitution);
PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold);
// Mass is zero which disables a bunch of physics stuff in Bullet // Mass is zero which disables a bunch of physics stuff in Bullet
UpdatePhysicalMassProperties(0f, false); UpdatePhysicalMassProperties(0f, false);
// Set collision detection parameters // Set collision detection parameters
if (BSParam.CcdMotionThreshold > 0f) if (BSParam.CcdMotionThreshold > 0f)
{ {
PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold);
PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius);
} }
// The activation state is 'disabled' so Bullet will not try to act on it. // The activation state is 'disabled' so Bullet will not try to act on it.
// PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION);
// Start it out sleeping and physical actions could wake it up. // 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 // This collides like a static object
PhysBody.collisionType = CollisionType.Static; PhysBody.collisionType = CollisionType.Static;
@ -847,11 +836,11 @@ public class BSPrim : BSPhysObject
else else
{ {
// Not a Bullet static object // 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 // Set various physical properties so other object interact properly
PhysicsScene.PE.SetFriction(PhysBody, Friction); PhysScene.PE.SetFriction(PhysBody, Friction);
PhysicsScene.PE.SetRestitution(PhysBody, Restitution); PhysScene.PE.SetRestitution(PhysBody, Restitution);
// DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution); // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution);
// per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
@ -869,22 +858,22 @@ public class BSPrim : BSPhysObject
// Set collision detection parameters // Set collision detection parameters
if (BSParam.CcdMotionThreshold > 0f) if (BSParam.CcdMotionThreshold > 0f)
{ {
PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold);
PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius);
} }
// Various values for simulation limits // Various values for simulation limits
PhysicsScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); PhysScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping);
PhysicsScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); PhysScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime);
PhysicsScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); PhysScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold);
PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold);
// This collides like an object. // This collides like an object.
PhysBody.collisionType = CollisionType.Dynamic; PhysBody.collisionType = CollisionType.Dynamic;
// Force activation of the object so Bullet will act on it. // Force activation of the object so Bullet will act on it.
// Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. // 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. // the functions after this one set up the state of a possibly newly created collision body.
private void MakeSolid(bool makeSolid) private void MakeSolid(bool makeSolid)
{ {
CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(PhysBody); CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysScene.PE.GetBodyType(PhysBody);
if (makeSolid) if (makeSolid)
{ {
// Verify the previous code created the correct shape for this type of thing. // 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); 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 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); 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 // Change collision info from a static object to a ghosty collision object
PhysBody.collisionType = CollisionType.VolumeDetect; PhysBody.collisionType = CollisionType.VolumeDetect;
@ -922,11 +911,11 @@ public class BSPrim : BSPhysObject
{ {
if (wantsCollisionEvents) if (wantsCollisionEvents)
{ {
CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
} }
else 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) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody);
} }
else else
{ {
@ -972,12 +961,12 @@ public class BSPrim : BSPhysObject
public override bool FloatOnWater { public override bool FloatOnWater {
set { set {
_floatOnWater = value; _floatOnWater = value;
PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() PhysScene.TaintedObject("BSPrim.setFloatOnWater", delegate()
{ {
if (_floatOnWater) if (_floatOnWater)
CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
else 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; _rotationalVelocity = value;
Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity);
// m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() PhysScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
{ {
ForceRotationalVelocity = _rotationalVelocity; ForceRotationalVelocity = _rotationalVelocity;
}); });
@ -1004,7 +993,7 @@ public class BSPrim : BSPhysObject
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity);
// PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
@ -1020,7 +1009,7 @@ public class BSPrim : BSPhysObject
get { return _buoyancy; } get { return _buoyancy; }
set { set {
_buoyancy = value; _buoyancy = value;
PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() PhysScene.TaintedObject("BSPrim.setBuoyancy", delegate()
{ {
ForceBuoyancy = _buoyancy; ForceBuoyancy = _buoyancy;
}); });
@ -1043,7 +1032,7 @@ public class BSPrim : BSPhysObject
base.MoveToTargetActive = value; base.MoveToTargetActive = value;
EnableActor(MoveToTargetActive, MoveToTargetActorName, delegate() 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; base.HoverActive = value;
EnableActor(HoverActive, HoverActorName, delegate() 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); OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude);
// Since this force is being applied in only one step, make this a force per second. // 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 */); AddForce(addForce, pushforce, false /* inTaintTime */);
} }
@ -1080,13 +1069,13 @@ public class BSPrim : BSPhysObject
// DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
OMV.Vector3 addForce = force; 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 // Bullet adds this central force to the total force for this tick
DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); PhysScene.PE.ApplyCentralForce(PhysBody, addForce);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
}); });
@ -1108,13 +1097,13 @@ public class BSPrim : BSPhysObject
OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude); OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude);
// DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse); // 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 // Bullet adds this impulse immediately to the velocity
DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse); DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse);
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); PhysScene.PE.ApplyCentralImpulse(PhysBody, addImpulse);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
}); });
@ -1133,12 +1122,12 @@ public class BSPrim : BSPhysObject
if (force.IsFinite()) if (force.IsFinite())
{ {
OMV.Vector3 angForce = force; OMV.Vector3 angForce = force;
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() PhysScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce);
PhysicsScene.PE.ApplyTorque(PhysBody, angForce); PhysScene.PE.ApplyTorque(PhysBody, angForce);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
}); });
@ -1157,11 +1146,11 @@ public class BSPrim : BSPhysObject
public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
{ {
OMV.Vector3 applyImpulse = impulse; OMV.Vector3 applyImpulse = impulse;
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() PhysScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
{ {
if (PhysBody.HasPhysicalBody) if (PhysBody.HasPhysicalBody)
{ {
PhysicsScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); PhysScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse);
ActivateIfPhysical(false); ActivateIfPhysical(false);
} }
}); });
@ -1463,12 +1452,13 @@ public class BSPrim : BSPhysObject
// Create the correct physical representation for this type of object. // Create the correct physical representation for this type of object.
// Updates base.PhysBody and base.PhysShape with the new information. // Updates base.PhysBody and base.PhysShape with the new information.
// Ignore 'forceRebuild'. 'GetBodyAndShape' makes the right choices and changes of necessary. // 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. // Called if the current prim body is about to be destroyed.
// Remove all the physical dependencies on the old body. // Remove all the physical dependencies on the old body.
// (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) // (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 // Make sure the properties are set on the new object
@ -1477,9 +1467,9 @@ public class BSPrim : BSPhysObject
} }
// Called at taint-time // 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 // The physics engine says that properties have updated. Update same and inform

View File

@ -78,14 +78,16 @@ public class BSPrimDisplaced : BSPrim
// Set this sets and computes the displacement from the passed prim to the center-of-mass. // 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. // 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). // 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; Vector3 comDisp;
if (UserSetCenterOfMass.HasValue) if (UserSetCenterOfMassDisplacement.HasValue)
comDisp = (OMV.Vector3)UserSetCenterOfMass; comDisp = (OMV.Vector3)UserSetCenterOfMassDisplacement;
else else
comDisp = centerOfMassDisplacement; comDisp = centerOfMassDisplacement;
DetailLog("{0},BSPrimDisplaced.SetEffectiveCenterOfMassDisplacement,userSet={1},comDisp={2}",
LocalID, UserSetCenterOfMassDisplacement.HasValue, comDisp);
if (comDisp == Vector3.Zero) if (comDisp == Vector3.Zero)
{ {
// If there is no diplacement. Things get reset. // If there is no diplacement. Things get reset.
@ -107,17 +109,24 @@ public class BSPrimDisplaced : BSPrim
set set
{ {
if (PositionDisplacement != OMV.Vector3.Zero) 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 else
{
base.ForcePosition = value; base.ForcePosition = value;
} }
} }
}
public override Quaternion ForceOrientation public override Quaternion ForceOrientation
{ {
get { return base.ForceOrientation; } get { return base.ForceOrientation; }
set set
{ {
// TODO:
base.ForceOrientation = value; base.ForceOrientation = value;
} }
} }
@ -143,7 +152,10 @@ public class BSPrimDisplaced : BSPrim
{ {
// Correct for any rotation around the center-of-mass // Correct for any rotation around the center-of-mass
// TODO!!! // 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; // entprop.Rotation = something;
} }

View File

@ -47,9 +47,9 @@ public class BSPrimLinkable : BSPrimDisplaced
OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
: base(localID, primName, parent_scene, pos, size, rotation, pbs, 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); Linkset.Refresh(this);
}); });
@ -61,9 +61,6 @@ public class BSPrimLinkable : BSPrimDisplaced
base.Destroy(); base.Destroy();
} }
public override BSPhysicsShapeType PreferredPhysicalShape
{ get { return Linkset.PreferredPhysicalShape(this); } }
public override void link(Manager.PhysicsActor obj) public override void link(Manager.PhysicsActor obj)
{ {
BSPrimLinkable parent = obj as BSPrimLinkable; BSPrimLinkable parent = obj as BSPrimLinkable;
@ -102,7 +99,7 @@ public class BSPrimLinkable : BSPrimDisplaced
set set
{ {
base.Position = value; base.Position = value;
PhysicsScene.TaintedObject("BSPrimLinkset.setPosition", delegate() PhysScene.TaintedObject("BSPrimLinkset.setPosition", delegate()
{ {
Linkset.UpdateProperties(UpdatedProperties.Position, this); Linkset.UpdateProperties(UpdatedProperties.Position, this);
}); });
@ -116,7 +113,7 @@ public class BSPrimLinkable : BSPrimDisplaced
set set
{ {
base.Orientation = value; base.Orientation = value;
PhysicsScene.TaintedObject("BSPrimLinkset.setOrientation", delegate() PhysScene.TaintedObject("BSPrimLinkset.setOrientation", delegate()
{ {
Linkset.UpdateProperties(UpdatedProperties.Orientation, this); 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. // Body is being taken apart. Remove physical dependencies and schedule a rebuild.
protected override void RemoveBodyDependencies() protected override void RemoveDependencies()
{ {
Linkset.RemoveBodyDependencies(this); Linkset.RemoveDependencies(this);
base.RemoveBodyDependencies(); base.RemoveDependencies();
} }
public override void UpdateProperties(EntityProperties entprop) public override void UpdateProperties(EntityProperties entprop)
@ -185,6 +182,10 @@ public class BSPrimLinkable : BSPrimDisplaced
{ {
return false; 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); return base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth);
} }
} }

View File

@ -316,7 +316,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
break; break;
case "bulletxna": case "bulletxna":
ret = new BSAPIXNA(engineName, this); 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.ShouldUseBulletHACD = false;
BSParam.ShouldUseSingleConvexHullForPrims = false;
break; break;
} }

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ using System.Text;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Region.Physics.Manager; using OpenSim.Region.Physics.Manager;
using OpenSim.Region.Physics.ConvexDecompositionDotNet;
using OMV = OpenMetaverse; using OMV = OpenMetaverse;
@ -38,74 +39,39 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public abstract class BSShape public abstract class BSShape
{ {
private static string LogHeader = "[BULLETSIM SHAPE]";
public int referenceCount { get; set; } public int referenceCount { get; set; }
public DateTime lastReferenced { get; set; } public DateTime lastReferenced { get; set; }
public BulletShape physShapeInfo { get; set; } public BulletShape physShapeInfo { get; set; }
public BSShape() public BSShape()
{ {
referenceCount = 0; referenceCount = 1;
lastReferenced = DateTime.Now; lastReferenced = DateTime.Now;
physShapeInfo = new BulletShape(); physShapeInfo = new BulletShape();
} }
public BSShape(BulletShape pShape) public BSShape(BulletShape pShape)
{ {
referenceCount = 0; referenceCount = 1;
lastReferenced = DateTime.Now; lastReferenced = DateTime.Now;
physShapeInfo = pShape; physShapeInfo = pShape;
} }
// Get a reference to a physical shape. Create if it doesn't exist // Get another reference to this shape.
public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim);
{
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;
}
// Called when this shape is being used again. // 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++; referenceCount++;
lastReferenced = DateTime.Now; lastReferenced = DateTime.Now;
} }
// Called when this shape is being used again. // Called when this shape is being used again.
public virtual void DecrementReference() protected virtual void DecrementReference()
{ {
referenceCount--; referenceCount--;
lastReferenced = DateTime.Now; lastReferenced = DateTime.Now;
@ -114,22 +80,178 @@ public abstract class BSShape
// Release the use of a physical shape. // Release the use of a physical shape.
public abstract void Dereference(BSScene physicsScene); 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 // Returns a string for debugging that uniquily identifies the memory used by this instance
public virtual string AddrString public virtual string AddrString
{ {
get { return "unknown"; } get
{
if (physShapeInfo != null)
return physShapeInfo.AddrString;
return "unknown";
}
} }
public override string ToString() public override string ToString()
{ {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.Append("<p="); if (physShapeInfo == null)
buff.Append(AddrString); {
buff.Append(",noPhys");
}
else
{
buff.Append(",phy=");
buff.Append(physShapeInfo.ToString());
}
buff.Append(",c="); buff.Append(",c=");
buff.Append(referenceCount.ToString()); buff.Append(referenceCount.ToString());
buff.Append(">"); buff.Append(">");
return buff.ToString(); 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 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 */ } 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)); 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. // Make this reference to the physical shape go away since native shapes are not shared.
public override void Dereference(BSScene physicsScene) public override void Dereference(BSScene physicsScene)
{ {
// Native shapes are not tracked and are released immediately // Native shapes are not tracked and are released immediately
lock (physShapeInfo)
{
if (physShapeInfo.HasPhysicalShape) 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); physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
} }
physShapeInfo.Clear(); physShapeInfo.Clear();
// Garbage collection will free up this instance. // Garbage collection will free up this instance.
} }
}
private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim, private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim,
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
@ -197,7 +330,7 @@ public class BSShapeNative : BSShape
physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
LogHeader, prim.LocalID, shapeType); LogHeader, prim.LocalID, shapeType);
} }
newShape.type = shapeType; newShape.shapeType = shapeType;
newShape.isNativeShape = true; newShape.isNativeShape = true;
newShape.shapeKey = (UInt64)shapeKey; newShape.shapeKey = (UInt64)shapeKey;
return newShape; return newShape;
@ -209,7 +342,7 @@ public class BSShapeNative : BSShape
public class BSShapeMesh : BSShape public class BSShapeMesh : BSShape
{ {
private static string LogHeader = "[BULLETSIM SHAPE MESH]"; 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) public BSShapeMesh(BulletShape pShape) : base(pShape)
{ {
@ -217,12 +350,9 @@ public class BSShapeMesh : BSShape
public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
{ {
float lod; 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}", BSShapeMesh retMesh = null;
prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"), prim.Size, lod);
BSShapeMesh retMesh;
lock (Meshes) lock (Meshes)
{ {
if (Meshes.TryGetValue(newMeshKey, out retMesh)) if (Meshes.TryGetValue(newMeshKey, out retMesh))
@ -232,31 +362,64 @@ public class BSShapeMesh : BSShape
} }
else else
{ {
retMesh = new BSShapeMesh(new BulletShape());
// An instance of this mesh has not been created. Build and remember same. // An instance of this mesh has not been created. Build and remember same.
BulletShape newShape = CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); BulletShape newShape = retMesh.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);
// Check to see if mesh was created (might require an asset).
newShape = VerifyMeshCreated(physicsScene, newShape, prim);
if (!newShape.isNativeShape)
{
// If a mesh was what was created, remember the built shape for later sharing.
Meshes.Add(newMeshKey, retMesh); Meshes.Add(newMeshKey, retMesh);
} }
retMesh.physShapeInfo = newShape;
} }
}
physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod);
return retMesh; 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) public override void Dereference(BSScene physicsScene)
{ {
lock (Meshes) lock (Meshes)
{ {
this.DecrementReference(); this.DecrementReference();
physicsScene.DetailLog("{0},BSShapeMesh.Dereference,shape={1}", BSScene.DetailLogZero, this);
// TODO: schedule aging and destruction of unused meshes. // 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) PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{ {
BulletShape newShape = null; BulletShape newShape = new BulletShape();
IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod,
false, // say it is not physical so a bounding box is not built false, // say it is not physical so a bounding box is not built
@ -265,6 +428,12 @@ public class BSShapeMesh : BSShape
if (meshData != null) 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[] indices = meshData.getIndexListAsInt();
int realIndicesIndex = indices.Length; int realIndicesIndex = indices.Length;
@ -302,8 +471,8 @@ public class BSShapeMesh : BSShape
} }
} }
} }
physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalMesh,origTri={1},realTri={2},numVerts={3}", physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}",
BSScene.DetailLogZero, indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3);
if (realIndicesIndex != 0) if (realIndicesIndex != 0)
{ {
@ -326,17 +495,239 @@ public class BSShapeMesh : BSShape
public class BSShapeHull : BSShape public class BSShapeHull : BSShape
{ {
private static string LogHeader = "[BULLETSIM SHAPE HULL]"; 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 BSShapeHull(BulletShape pShape) : base(pShape)
{ {
} }
public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) 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) 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 public class BSShapeCompound : BSShape
{ {
private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; 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(); return new BSShapeNull();
} }
public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
{
return new BSShapeNull();
}
public override void Dereference(BSScene physicsScene) { } public override void Dereference(BSScene physicsScene) { }
// From the front: // From the front:

View File

@ -92,7 +92,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
private void BuildHeightmapTerrain() private void BuildHeightmapTerrain()
{ {
// Create the terrain shape from the mapInfo // 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, new Vector3(m_mapInfo.sizeX, m_mapInfo.sizeY, 0), m_mapInfo.minZ, m_mapInfo.maxZ,
m_mapInfo.heightMap, 1f, BSParam.TerrainCollisionMargin); 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.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 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); m_mapInfo.ID, centerPos, Quaternion.Identity);
// Set current terrain attributes // Set current terrain attributes
PhysicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction); m_physicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction);
PhysicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction); m_physicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction);
PhysicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution); m_physicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution);
PhysicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT); m_physicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT);
// Return the new terrain to the world of physical objects // 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 // 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.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. // 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; return;
} }
@ -134,9 +134,9 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
{ {
if (m_mapInfo.terrainBody.HasPhysicalBody) 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. // 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; m_mapInfo = null;
@ -155,7 +155,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
catch catch
{ {
// Sometimes they give us wonky values of X and Y. Give a warning and return something. // 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); LogHeader, m_mapInfo.terrainRegionBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; 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. // The passed position is relative to the base of the region.
public override float GetWaterLevelAtXYZ(Vector3 pos) public override float GetWaterLevelAtXYZ(Vector3 pos)
{ {
return PhysicsScene.SimpleWaterLevel; return m_physicsScene.SimpleWaterLevel;
} }
} }
} }

View File

@ -50,14 +50,14 @@ public abstract class BSTerrainPhys : IDisposable
Mesh = 1 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. // Base of the region in world coordinates. Coordinates inside the region are relative to this.
public Vector3 TerrainBase { get; private set; } public Vector3 TerrainBase { get; private set; }
public uint ID { get; private set; } public uint ID { get; private set; }
public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id) public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
{ {
PhysicsScene = physicsScene; m_physicsScene = physicsScene;
TerrainBase = regionBase; TerrainBase = regionBase;
ID = id; ID = id;
} }
@ -86,7 +86,7 @@ public sealed class BSTerrainManager : IDisposable
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
// The scene that I am part of // 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. // The ground plane created to keep thing from falling to infinity.
private BulletBody m_groundPlane; private BulletBody m_groundPlane;
@ -113,7 +113,7 @@ public sealed class BSTerrainManager : IDisposable
public BSTerrainManager(BSScene physicsScene) public BSTerrainManager(BSScene physicsScene)
{ {
PhysicsScene = physicsScene; m_physicsScene = physicsScene;
m_terrains = new Dictionary<Vector3,BSTerrainPhys>(); m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
// Assume one region of default size // 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. // safe to call Bullet in real time. We hope no one is moving prims around yet.
public void CreateInitialGroundPlaneAndTerrain() 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 // 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); BulletShape groundPlaneShape = m_physicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin);
m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, m_groundPlane = m_physicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape,
BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity); BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity);
PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_groundPlane); m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_groundPlane);
PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_groundPlane); m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_groundPlane);
// Ground plane does not move // 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. // Everything collides with the ground plane.
m_groundPlane.collisionType = CollisionType.Groundplane; 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) lock (m_terrains)
{ {
// Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. // 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 // Release all the terrain structures we might have allocated
public void ReleaseGroundPlaneAndTerrain() 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 (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(); m_groundPlane.Clear();
} }
@ -188,7 +188,7 @@ public sealed class BSTerrainManager : IDisposable
float[] localHeightMap = heightMap; float[] localHeightMap = heightMap;
// If there are multiple requests for changes to the same terrain between ticks, // If there are multiple requests for changes to the same terrain between ticks,
// only do that last one. // 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) 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) 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. // 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); 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. // 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) private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
{ {
PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", m_physicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
LogHeader, PhysicsScene.RegionName, terrainRegionBase, LogHeader, m_physicsScene.RegionName, terrainRegionBase,
(BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation); (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
BSTerrainPhys newTerrainPhys = null; BSTerrainPhys newTerrainPhys = null;
switch ((int)BSParam.TerrainImplementation) switch ((int)BSParam.TerrainImplementation)
{ {
case (int)BSTerrainPhys.TerrainImplementation.Heightmap: case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id, newTerrainPhys = new BSTerrainHeightmap(m_physicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords); heightMap, minCoords, maxCoords);
break; break;
case (int)BSTerrainPhys.TerrainImplementation.Mesh: case (int)BSTerrainPhys.TerrainImplementation.Mesh:
newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id, newTerrainPhys = new BSTerrainMesh(m_physicsScene, terrainRegionBase, id,
heightMap, minCoords, maxCoords); heightMap, minCoords, maxCoords);
break; break;
default: 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, LogHeader,
(int)BSParam.TerrainImplementation, (int)BSParam.TerrainImplementation,
BSParam.TerrainImplementation, BSParam.TerrainImplementation,
PhysicsScene.RegionName, terrainRegionBase); m_physicsScene.RegionName, terrainRegionBase);
break; break;
} }
return newTerrainPhys; return newTerrainPhys;
@ -429,8 +429,8 @@ public sealed class BSTerrainManager : IDisposable
} }
else else
{ {
PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
LogHeader, PhysicsScene.RegionName, tX, tY); LogHeader, m_physicsScene.RegionName, tX, tY);
DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}", DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
BSScene.DetailLogZero, pos, terrainBaseXYZ); BSScene.DetailLogZero, pos, terrainBaseXYZ);
} }
@ -451,8 +451,8 @@ public sealed class BSTerrainManager : IDisposable
} }
else else
{ {
PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}", m_physicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret); LogHeader, m_physicsScene.RegionName, pos, terrainBaseXYZ, ret);
} }
return ret; return ret;
} }
@ -564,7 +564,7 @@ public sealed class BSTerrainManager : IDisposable
private void DetailLog(string msg, params Object[] args) private void DetailLog(string msg, params Object[] args)
{ {
PhysicsScene.PhysicsLogging.Write(msg, args); m_physicsScene.PhysicsLogging.Write(msg, args);
} }
} }
} }

View File

@ -80,7 +80,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
if (BSParam.TerrainMeshMagnification == 1) if (BSParam.TerrainMeshMagnification == 1)
{ {
// If a magnification of one, use the old routine that is tried and true. // 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 initialMap, m_sizeX, m_sizeY, // input size
Vector3.Zero, // base for mesh Vector3.Zero, // base for mesh
out indicesCount, out indices, out verticesCount, out vertices); out indicesCount, out indices, out verticesCount, out vertices);
@ -88,7 +88,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
else else
{ {
// Other magnifications use the newer routine // Other magnifications use the newer routine
meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(PhysicsScene, meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(m_physicsScene,
initialMap, m_sizeX, m_sizeY, // input size initialMap, m_sizeX, m_sizeY, // input size
BSParam.TerrainMeshMagnification, BSParam.TerrainMeshMagnification,
physicsScene.TerrainManager.DefaultRegionSize, physicsScene.TerrainManager.DefaultRegionSize,
@ -98,21 +98,21 @@ public sealed class BSTerrainMesh : BSTerrainPhys
if (!meshCreationSuccess) if (!meshCreationSuccess)
{ {
// DISASTER!! // DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID); m_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.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. // Something is very messed up and a crash is in our future.
return; 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); 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) if (!m_terrainShape.HasPhysicalShape)
{ {
// DISASTER!! // DISASTER!!
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID); m_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.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
// Something is very messed up and a crash is in our future. // Something is very messed up and a crash is in our future.
return; return;
} }
@ -120,52 +120,52 @@ public sealed class BSTerrainMesh : BSTerrainPhys
Vector3 pos = regionBase; Vector3 pos = regionBase;
Quaternion rot = Quaternion.Identity; 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) if (!m_terrainBody.HasPhysicalBody)
{ {
// DISASTER!! // 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. // Something is very messed up and a crash is in our future.
return; return;
} }
physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin); physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin);
// Set current terrain attributes // Set current terrain attributes
PhysicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction); m_physicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction);
PhysicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction); m_physicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction);
PhysicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution); m_physicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution);
PhysicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold); m_physicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold);
PhysicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT); m_physicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT);
// Static objects are not very massive. // 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 // 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 // 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.collisionType = CollisionType.Terrain;
m_terrainBody.ApplyCollisionMask(PhysicsScene); m_terrainBody.ApplyCollisionMask(m_physicsScene);
if (BSParam.UseSingleSidedMeshes) if (BSParam.UseSingleSidedMeshes)
{ {
PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id); m_physicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id);
PhysicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); m_physicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
} }
// Make it so the terrain will not move or be considered for movement. // 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() public override void Dispose()
{ {
if (m_terrainBody.HasPhysicalBody) 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. // 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_terrainBody.Clear();
m_terrainShape.Clear(); m_terrainShape.Clear();
} }
@ -185,7 +185,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
catch catch
{ {
// Sometimes they give us wonky values of X and Y. Give a warning and return something. // 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); LogHeader, TerrainBase, pos);
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; 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. // The passed position is relative to the base of the region.
public override float GetWaterLevelAtXYZ(Vector3 pos) public override float GetWaterLevelAtXYZ(Vector3 pos)
{ {
return PhysicsScene.SimpleWaterLevel; return m_physicsScene.SimpleWaterLevel;
} }
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().

View File

@ -104,11 +104,11 @@ public class BulletShape
{ {
public BulletShape() public BulletShape()
{ {
type = BSPhysicsShapeType.SHAPE_UNKNOWN; shapeType = BSPhysicsShapeType.SHAPE_UNKNOWN;
shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE; shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
isNativeShape = false; isNativeShape = false;
} }
public BSPhysicsShapeType type; public BSPhysicsShapeType shapeType;
public System.UInt64 shapeKey; public System.UInt64 shapeKey;
public bool isNativeShape; public bool isNativeShape;
@ -133,7 +133,7 @@ public class BulletShape
buff.Append("<p="); buff.Append("<p=");
buff.Append(AddrString); buff.Append(AddrString);
buff.Append(",s="); buff.Append(",s=");
buff.Append(type.ToString()); buff.Append(shapeType.ToString());
buff.Append(",k="); buff.Append(",k=");
buff.Append(shapeKey.ToString("X")); buff.Append(shapeKey.ToString("X"));
buff.Append(",n="); buff.Append(",n=");

View File

@ -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 CURRENT PRIORITIES
================================================= =================================================
Use the HACD convex hull routine in Bullet rather than the C# version. 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. Linkset child rotations.
Nebadon spiral tube has middle sections which are rotated wrong. Nebadon spiral tube has middle sections which are rotated wrong.
Select linked spiral tube. Delink and note where the middle section ends up. 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 vertical attraction
vehicle angular banking vehicle angular banking
Center-of-gravity Center-of-gravity
@ -27,14 +38,13 @@ llLookAt
Avatars walking up stairs (HALF DONE) Avatars walking up stairs (HALF DONE)
Avatar movement Avatar movement
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE) 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) avatar capsule rotation completed (NOT DONE - Bullet's capsule shape is not the solution)
Vehicle script tuning/debugging Vehicle script tuning/debugging
Avanti speed script Avanti speed script
Weapon shooter script Weapon shooter script
Move material definitions (friction, ...) into simulator. Move material definitions (friction, ...) into simulator.
Add material densities to the material types. Add material densities to the material types.
Terrain detail: double terrain mesh detail
One sided meshes? Should terrain be built into a closed shape? One sided meshes? Should terrain be built into a closed shape?
When meshes get partially wedged into the terrain, they cannot push themselves out. 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. 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: 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: UBit improvements to remove rubber-banding of avatars sitting on vehicle child prims:
https://github.com/UbitUmarov/Ubit-opensim https://github.com/UbitUmarov/Ubit-opensim
Border crossing with linked vehicle causes crash 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. DONE 20130120: BulletSim properly applies force in vehicle relative coordinates.
Nebadon vehicles turning funny in arena (DONE) Nebadon vehicles turning funny in arena (DONE)
Lock axis (DONE 20130401) Lock axis (DONE 20130401)
Terrain detail: double terrain mesh detail (DONE)

View File

@ -51,7 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
public interface IScriptWorkItem public interface IScriptWorkItem
{ {
bool Cancel(); bool Cancel();
void Abort(); bool Abort();
/// <summary> /// <summary>
/// Wait for the work item to complete. /// Wait for the work item to complete.

View File

@ -564,9 +564,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public bool Stop(int timeout) public bool Stop(int timeout)
{ {
// m_log.DebugFormat( if (DebugLevel >= 1)
// "[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}", m_log.DebugFormat(
// ScriptName, ItemID, PrimName, ObjectID, timeout, m_InSelfDelete, DateTime.Now.Ticks); "[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; IScriptWorkItem workItem;

View File

@ -483,7 +483,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
/// <param name="instance"></param> /// <param name="instance"></param>
/// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</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>( 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)) if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene))
return; return;
@ -1517,7 +1517,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
startInfo.MaxWorkerThreads = maxThreads; startInfo.MaxWorkerThreads = maxThreads;
startInfo.MinWorkerThreads = minThreads; startInfo.MinWorkerThreads = minThreads;
startInfo.ThreadPriority = threadPriority;; startInfo.ThreadPriority = threadPriority;;
startInfo.StackSize = stackSize; startInfo.MaxStackSize = stackSize;
startInfo.StartSuspended = true; startInfo.StartSuspended = true;
m_ThreadPool = new SmartThreadPool(startInfo); m_ThreadPool = new SmartThreadPool(startInfo);

View File

@ -52,16 +52,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine
return wr.Cancel(); return wr.Cancel();
} }
public void Abort() public bool Abort()
{ {
wr.Abort(); return wr.Cancel(true);
} }
public bool Wait(int t) public bool Wait(int t)
{ {
// We use the integer version of WaitAll because the current version of SmartThreadPool has a bug with the // 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 // 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). // (or very likely other versions of Mono at least up until 3.0.3).
return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false); return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false);
} }

View File

@ -171,11 +171,6 @@ namespace OpenSim.Server.Base
m_console = MainConsole.Instance; m_console = MainConsole.Instance;
// Configure the appenders for log4net
//
OpenSimAppender consoleAppender = null;
FileAppender fileAppender = null;
if (logConfig != null) if (logConfig != null)
{ {
FileInfo cfg = new FileInfo(logConfig); FileInfo cfg = new FileInfo(logConfig);

View File

@ -217,13 +217,38 @@ namespace OpenSim.Tests.Common
/// </returns> /// </returns>
public static InventoryFolderBase CreateInventoryFolder( public static InventoryFolderBase CreateInventoryFolder(
IInventoryService inventoryService, UUID userId, string path, bool useExistingFolders) 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); InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
if (null == rootFolder) if (null == rootFolder)
return null; return null;
return CreateInventoryFolder(inventoryService, rootFolder, path, useExistingFolders); return CreateInventoryFolder(inventoryService, folderId, rootFolder, path, useExistingFolders);
} }
/// <summary> /// <summary>
@ -235,6 +260,7 @@ namespace OpenSim.Tests.Common
/// TODO: May need to make it an option to create duplicate folders. /// TODO: May need to make it an option to create duplicate folders.
/// </remarks> /// </remarks>
/// <param name="inventoryService"></param> /// <param name="inventoryService"></param>
/// <param name="folderId">ID of the folder to create</param>
/// <param name="parentFolder"></param> /// <param name="parentFolder"></param>
/// <param name="path"> /// <param name="path">
/// The folder to create. /// The folder to create.
@ -249,7 +275,7 @@ namespace OpenSim.Tests.Common
/// The folder created. If the path contains multiple folders then the last one created is returned. /// The folder created. If the path contains multiple folders then the last one created is returned.
/// </returns> /// </returns>
public static InventoryFolderBase CreateInventoryFolder( 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); 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); // Console.WriteLine("Creating folder {0} at {1}", components[0], parentFolder.Name);
UUID folderIdForCreate;
if (components.Length > 1)
folderIdForCreate = UUID.Random();
else
folderIdForCreate = folderId;
folder folder
= new InventoryFolderBase( = 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); inventoryService.AddFolder(folder);
} }
@ -274,7 +307,7 @@ namespace OpenSim.Tests.Common
// } // }
if (components.Length > 1) if (components.Length > 1)
return CreateInventoryFolder(inventoryService, folder, components[1], useExistingFolders); return CreateInventoryFolder(inventoryService, folderId, folder, components[1], useExistingFolders);
else else
return folder; return folder;
} }

View File

@ -61,6 +61,7 @@ namespace OpenSim.Tests.Common.Mock
// Test client specific events - for use by tests to implement some IClientAPI behaviour. // Test client specific events - for use by tests to implement some IClientAPI behaviour.
public event Action<RegionInfo, Vector3, Vector3> OnReceivedMoveAgentIntoRegion; public event Action<RegionInfo, Vector3, Vector3> OnReceivedMoveAgentIntoRegion;
public event Action<ulong, IPEndPoint> OnTestClientInformClientOfNeighbour; public event Action<ulong, IPEndPoint> OnTestClientInformClientOfNeighbour;
public event Action<GridInstantMessage> OnReceivedInstantMessage;
// disable warning: public events, part of the public API // disable warning: public events, part of the public API
#pragma warning disable 67 #pragma warning disable 67
@ -484,6 +485,18 @@ namespace OpenSim.Tests.Common.Mock
OnCompleteMovementToRegion(this, true); 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) public virtual void ActivateGesture(UUID assetId, UUID gestureId)
{ {
} }
@ -538,7 +551,8 @@ namespace OpenSim.Tests.Common.Mock
public void SendInstantMessage(GridInstantMessage im) public void SendInstantMessage(GridInstantMessage im)
{ {
if (OnReceivedInstantMessage != null)
OnReceivedInstantMessage(im);
} }
public void SendGenericMessage(string method, UUID invoice, List<string> message) public void SendGenericMessage(string method, UUID invoice, List<string> message)

View File

@ -53,6 +53,9 @@ namespace OpenSim.Tests.Common.Mock
public XInventoryFolder[] GetFolders(string[] fields, string[] vals) public XInventoryFolder[] GetFolders(string[] fields, string[] vals)
{ {
// Console.WriteLine(
// "Requesting folders, fields {0}, vals {1}", string.Join(",", fields), string.Join(",", vals));
List<XInventoryFolder> origFolders List<XInventoryFolder> origFolders
= Get<XInventoryFolder>(fields, vals, m_allFolders.Values.ToList()); = 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 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 XInventoryItem[] GetActiveGestures(UUID principalID) { throw new NotImplementedException(); }
public int GetAssetPermissions(UUID principalID, UUID assetID) { throw new NotImplementedException(); } public int GetAssetPermissions(UUID principalID, UUID assetID) { throw new NotImplementedException(); }
} }

View File

@ -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("")]

View File

@ -1,3 +1,6 @@
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@ -6,7 +9,7 @@ using System.Web;
using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Messaging;
namespace Amib.Threading namespace Amib.Threading.Internal
{ {
#region CallerThreadContext class #region CallerThreadContext class
@ -19,10 +22,10 @@ namespace Amib.Threading
#region Prepare reflection information #region Prepare reflection information
// Cached type information. // Cached type information.
private static MethodInfo getLogicalCallContextMethodInfo = private static readonly MethodInfo getLogicalCallContextMethodInfo =
typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); 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); typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
private static string HttpContextSlotName = GetHttpContextSlotName(); private static string HttpContextSlotName = GetHttpContextSlotName();
@ -32,8 +35,10 @@ namespace Amib.Threading
FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
if (fi != null) if (fi != null)
{
return (string) fi.GetValue(null); return (string) fi.GetValue(null);
else // Use the default "HttpContext" slot name }
return "HttpContext"; return "HttpContext";
} }
@ -41,8 +46,8 @@ namespace Amib.Threading
#region Private fields #region Private fields
private HttpContext _httpContext = null; private HttpContext _httpContext;
private LogicalCallContext _callContext = null; private LogicalCallContext _callContext;
#endregion #endregion
@ -122,102 +127,12 @@ namespace Amib.Threading
// Restore HttpContext // Restore HttpContext
if (callerThreadContext._httpContext != null) if (callerThreadContext._httpContext != null)
{ {
CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); HttpContext.Current = callerThreadContext._httpContext;
//CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
} }
} }
} }
#endregion #endregion
} }
#endif
/*
// 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
}
*/

View File

@ -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; }
}
}

View File

@ -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

View File

@ -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
}
}

View File

@ -1,32 +1,82 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
#if !(_WINDOWS_CE)
using System.Runtime.Serialization; using System.Runtime.Serialization;
#endif
namespace Amib.Threading namespace Amib.Threading
{ {
#region Exceptions #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> /// <summary>
/// Represents an exception in case IWorkItemResult.GetResult has been canceled /// Represents an exception in case IWorkItemResult.GetResult has been canceled
/// </summary> /// </summary>
[Serializable] [Serializable]
public sealed class WorkItemCancelException : ApplicationException public sealed partial class WorkItemCancelException
{ {
public WorkItemCancelException() : base() public WorkItemCancelException(SerializationInfo si, StreamingContext sc)
{ : base(si, sc)
}
public WorkItemCancelException(string message) : base(message)
{
}
public WorkItemCancelException(string message, Exception e) : base(message, e)
{
}
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 /// Represents an exception in case IWorkItemResult.GetResult has been timed out
/// </summary> /// </summary>
[Serializable] [Serializable]
public sealed class WorkItemTimeoutException : ApplicationException public sealed partial class WorkItemTimeoutException
{ {
public WorkItemTimeoutException() : base() public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc)
{ : base(si, sc)
}
public WorkItemTimeoutException(string message) : base(message)
{
}
public WorkItemTimeoutException(string message, Exception e) : base(message, e)
{
}
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 /// Represents an exception in case IWorkItemResult.GetResult has been timed out
/// </summary> /// </summary>
[Serializable] [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) #endif
{
}
public WorkItemResultException(string message, Exception e) : base(message, e)
{
}
public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc)
{
}
}
#endregion #endregion
} }

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Threading; using System.Threading;
@ -20,16 +17,39 @@ namespace Amib.Threading
/// <param name="wir">The work item result object</param> /// <param name="wir">The work item result object</param>
public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); 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> /// <summary>
/// A delegate to call when a WorkItemsGroup becomes idle /// A delegate to call when a WorkItemsGroup becomes idle
/// </summary> /// </summary>
/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param> /// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); 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 #endregion
#region WorkItem Priority #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 public enum WorkItemPriority
{ {
Lowest, Lowest,
@ -41,19 +61,11 @@ namespace Amib.Threading
#endregion #endregion
#region IHasWorkItemPriority interface
public interface IHasWorkItemPriority
{
WorkItemPriority WorkItemPriority { get; }
}
#endregion
#region IWorkItemsGroup interface #region IWorkItemsGroup interface
/// <summary> /// <summary>
/// IWorkItemsGroup interface /// IWorkItemsGroup interface
/// Created by SmartThreadPool.CreateWorkItemsGroup()
/// </summary> /// </summary>
public interface IWorkItemsGroup public interface IWorkItemsGroup
{ {
@ -62,27 +74,293 @@ namespace Amib.Threading
/// </summary> /// </summary>
string Name { get; set; } string Name { get; set; }
IWorkItemResult QueueWorkItem(WorkItemCallback callback); /// <summary>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); /// </summary>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); int Concurrency { get; set; }
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);
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); /// <summary>
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); /// 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(); 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); 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); 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; event WorkItemsGroupIdleHandler OnIdle;
void Cancel(); #region QueueWorkItem
void Start();
/// <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&lt;TResult&gt; 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&lt;TResult&gt; 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&lt;TResult&gt; 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&lt;TResult&gt; 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&lt;TResult&gt; 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 #endregion
@ -92,9 +370,24 @@ namespace Amib.Threading
[Flags] [Flags]
public enum CallToPostExecute public enum CallToPostExecute
{ {
/// <summary>
/// Never call to the PostExecute call back
/// </summary>
Never = 0x00, Never = 0x00,
/// <summary>
/// Call to the PostExecute only when the work item is cancelled
/// </summary>
WhenWorkItemCanceled = 0x01, WhenWorkItemCanceled = 0x01,
/// <summary>
/// Call to the PostExecute only when the work item is not cancelled
/// </summary>
WhenWorkItemNotCanceled = 0x02, WhenWorkItemNotCanceled = 0x02,
/// <summary>
/// Always call to the PostExecute
/// </summary>
Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
} }
@ -103,16 +396,43 @@ namespace Amib.Threading
#region IWorkItemResult interface #region IWorkItemResult interface
/// <summary> /// <summary>
/// IWorkItemResult interface /// The common interface of IWorkItemResult and IWorkItemResult&lt;T&gt;
/// </summary> /// </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&lt;TResult&gt; interface.
/// Created when a Func&lt;TResult&gt; work item is queued.
/// </summary>
public interface IWorkItemResult<TResult> : IWaitableResult
{ {
/// <summary> /// <summary>
/// Get the result of the work item. /// Get the result of the work item.
/// If the work item didn't run yet then the caller waits. /// If the work item didn't run yet then the caller waits.
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
object GetResult(); TResult GetResult();
/// <summary> /// <summary>
/// Get the result of the work item. /// Get the result of the work item.
@ -120,7 +440,7 @@ namespace Amib.Threading
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext); bool exitContext);
@ -130,12 +450,10 @@ namespace Amib.Threading
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext); bool exitContext);
void Abort();
/// <summary> /// <summary>
/// Get the result of the work item. /// 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. /// 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> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle); WaitHandle cancelWaitHandle);
@ -160,7 +478,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle); WaitHandle cancelWaitHandle);
@ -171,16 +489,18 @@ namespace Amib.Threading
/// </summary> /// </summary>
/// <param name="e">Filled with the exception if one was thrown</param> /// <param name="e">Filled with the exception if one was thrown</param>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
object GetResult(out Exception e); TResult GetResult(out Exception e);
/// <summary> /// <summary>
/// Get the result of the work item. /// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout. /// If the work item didn't run yet then the caller waits until timeout.
/// </summary> /// </summary>
/// <param name="millisecondsTimeout"></param>
/// <param name="exitContext"></param>
/// <param name="e">Filled with the exception if one was thrown</param> /// <param name="e">Filled with the exception if one was thrown</param>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
out Exception e); out Exception e);
@ -189,10 +509,12 @@ namespace Amib.Threading
/// Get the result of the work item. /// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout. /// If the work item didn't run yet then the caller waits until timeout.
/// </summary> /// </summary>
/// <param name="exitContext"></param>
/// <param name="e">Filled with the exception if one was thrown</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> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext, bool exitContext,
out Exception e); out Exception e);
@ -210,7 +532,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle, 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. /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <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="e">Filled with the exception if one was thrown</param>
/// <param name="timeout"></param>
/// <param name="exitContext"></param>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle, WaitHandle cancelWaitHandle,
@ -241,16 +566,30 @@ namespace Amib.Threading
bool IsCanceled { get; } bool IsCanceled { get; }
/// <summary> /// <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> /// </summary>
object State { get; } object State { get; }
/// <summary> /// <summary>
/// Cancel the work item if it didn't start running yet. /// Same as Cancel(false).
/// </summary> /// </summary>
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
bool Cancel(); 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> /// <summary>
/// Get the work item's priority /// Get the work item's priority
/// </summary> /// </summary>
@ -259,7 +598,7 @@ namespace Amib.Threading
/// <summary> /// <summary>
/// Return the result, same as GetResult() /// Return the result, same as GetResult()
/// </summary> /// </summary>
object Result { get; } TResult Result { get; }
/// <summary> /// <summary>
/// Returns the exception if occured otherwise returns null. /// Returns the exception if occured otherwise returns null.
@ -267,5 +606,23 @@ namespace Amib.Threading
object Exception { get; } 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 #endregion
} }

View File

@ -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; }
}
}

View File

@ -1,8 +1,6 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
@ -25,17 +23,17 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Work items queues. There is one for each type of priority /// Work items queues. There is one for each type of priority
/// </summary> /// </summary>
private Queue [] _queues = new Queue[_queuesCount]; private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];
/// <summary> /// <summary>
/// The total number of work items within the queues /// The total number of work items within the queues
/// </summary> /// </summary>
private int _workItemsCount = 0; private int _workItemsCount;
/// <summary> /// <summary>
/// Use with IEnumerable interface /// Use with IEnumerable interface
/// </summary> /// </summary>
private int _version = 0; private int _version;
#endregion #endregion
@ -45,7 +43,7 @@ namespace Amib.Threading.Internal
{ {
for(int i = 0; i < _queues.Length; ++i) 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 >= 0);
Debug.Assert(queueIndex < _queuesCount); Debug.Assert(queueIndex < _queuesCount);
_queues[queueIndex].Enqueue(workItem); _queues[queueIndex].AddLast(workItem);
++_workItemsCount; ++_workItemsCount;
++_version; ++_version;
} }
@ -82,7 +80,8 @@ namespace Amib.Threading.Internal
{ {
int queueIndex = GetNextNonEmptyQueue(-1); int queueIndex = GetNextNonEmptyQueue(-1);
Debug.Assert(queueIndex >= 0); Debug.Assert(queueIndex >= 0);
workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority; workItem = _queues[queueIndex].First.Value;
_queues[queueIndex].RemoveFirst();
Debug.Assert(null != workItem); Debug.Assert(null != workItem);
--_workItemsCount; --_workItemsCount;
++_version; ++_version;
@ -128,7 +127,7 @@ namespace Amib.Threading.Internal
{ {
if (_workItemsCount > 0) if (_workItemsCount > 0)
{ {
foreach(Queue queue in _queues) foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
{ {
queue.Clear(); queue.Clear();
} }
@ -159,7 +158,7 @@ namespace Amib.Threading.Internal
/// </summary> /// </summary>
private class PriorityQueueEnumerator : IEnumerator private class PriorityQueueEnumerator : IEnumerator
{ {
private PriorityQueue _priorityQueue; private readonly PriorityQueue _priorityQueue;
private int _version; private int _version;
private int _queueIndex; private int _queueIndex;
private IEnumerator _enumerator; private IEnumerator _enumerator;

View File

@ -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

16
ThirdParty/SmartThreadPool/SLExt.cs vendored Normal file
View File

@ -0,0 +1,16 @@
#if _SILVERLIGHT
using System.Threading;
namespace Amib.Threading
{
public enum ThreadPriority
{
Lowest,
BelowNormal,
Normal,
AboveNormal,
Highest,
}
}
#endif

View File

@ -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

View File

@ -1,8 +1,30 @@
using System; using System;
using System.Diagnostics; 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 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 internal enum STPPerformanceCounterType
{ {
// Fields // Fields
@ -37,7 +59,7 @@ namespace Amib.Threading.Internal
internal class STPPerformanceCounter internal class STPPerformanceCounter
{ {
// Fields // Fields
private PerformanceCounterType _pcType; private readonly PerformanceCounterType _pcType;
protected string _counterHelp; protected string _counterHelp;
protected string _counterName; protected string _counterName;
@ -47,9 +69,9 @@ namespace Amib.Threading.Internal
string counterHelp, string counterHelp,
PerformanceCounterType pcType) PerformanceCounterType pcType)
{ {
this._counterName = counterName; _counterName = counterName;
this._counterHelp = counterHelp; _counterHelp = counterHelp;
this._pcType = pcType; _pcType = pcType;
} }
public void AddCounterToCollection(CounterCreationDataCollection counterData) public void AddCounterToCollection(CounterCreationDataCollection counterData)
@ -76,7 +98,7 @@ namespace Amib.Threading.Internal
{ {
// Fields // Fields
internal STPPerformanceCounter[] _stpPerformanceCounters; internal STPPerformanceCounter[] _stpPerformanceCounters;
private static STPPerformanceCounters _instance; private static readonly STPPerformanceCounters _instance;
internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
internal const string _stpCategoryName = "SmartThreadPool"; internal const string _stpCategoryName = "SmartThreadPool";
@ -127,19 +149,12 @@ namespace Amib.Threading.Internal
_stpPerformanceCounters[i].AddCounterToCollection(counters); _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( PerformanceCounterCategory.Create(
_stpCategoryName, _stpCategoryName,
_stpCategoryHelp, _stpCategoryHelp,
//PerformanceCounterCategoryType.MultiInstance, PerformanceCounterCategoryType.MultiInstance,
counters); counters);
#pragma warning restore 0618
} }
} }
@ -156,16 +171,18 @@ namespace Amib.Threading.Internal
internal class STPInstancePerformanceCounter : IDisposable internal class STPInstancePerformanceCounter : IDisposable
{ {
// Fields // Fields
private bool _isDisposed;
private PerformanceCounter _pcs; private PerformanceCounter _pcs;
// Methods // Methods
protected STPInstancePerformanceCounter() protected STPInstancePerformanceCounter()
{ {
_isDisposed = false;
} }
public STPInstancePerformanceCounter( public STPInstancePerformanceCounter(
string instance, string instance,
STPPerformanceCounterType spcType) STPPerformanceCounterType spcType) : this()
{ {
STPPerformanceCounters counters = STPPerformanceCounters.Instance; STPPerformanceCounters counters = STPPerformanceCounters.Instance;
_pcs = new PerformanceCounter( _pcs = new PerformanceCounter(
@ -176,10 +193,6 @@ namespace Amib.Threading.Internal
_pcs.RawValue = _pcs.RawValue; _pcs.RawValue = _pcs.RawValue;
} }
~STPInstancePerformanceCounter()
{
Close();
}
public void Close() public void Close()
{ {
@ -192,9 +205,20 @@ namespace Amib.Threading.Internal
} }
public void Dispose() public void Dispose()
{
Dispose(true);
}
public virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{ {
Close(); Close();
GC.SuppressFinalize(this); }
}
_isDisposed = true;
} }
public virtual void Increment() public virtual void Increment()
@ -216,27 +240,19 @@ namespace Amib.Threading.Internal
internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
{ {
// Methods // Methods
public STPInstanceNullPerformanceCounter() {}
public override void Increment() {} public override void Increment() {}
public override void IncrementBy(long value) {} public override void IncrementBy(long value) {}
public override void Set(long val) {} 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 // Fields
private STPInstancePerformanceCounter[] _pcs; private STPInstancePerformanceCounter[] _pcs;
private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter; private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
// Methods // Methods
static STPInstancePerformanceCounters() static STPInstancePerformanceCounters()
@ -246,8 +262,13 @@ namespace Amib.Threading.Internal
public STPInstancePerformanceCounters(string instance) public STPInstancePerformanceCounters(string instance)
{ {
_isDisposed = false;
_pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter]; _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++) for (int i = 0; i < _pcs.Length; i++)
{ {
if (instance != null) if (instance != null)
@ -272,22 +293,28 @@ namespace Amib.Threading.Internal
{ {
if (null != _pcs[i]) if (null != _pcs[i])
{ {
_pcs[i].Close(); _pcs[i].Dispose();
} }
} }
_pcs = null; _pcs = null;
} }
} }
~STPInstancePerformanceCounters() public void Dispose()
{
Dispose(true);
}
public virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{ {
Close(); Close();
} }
}
public void Dispose() _isDisposed = true;
{
Close();
GC.SuppressFinalize(this);
} }
private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType) private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
@ -327,21 +354,17 @@ namespace Amib.Threading.Internal
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment(); GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
} }
} }
#endif
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
{ {
static NullSTPInstancePerformanceCounters() private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();
{
}
private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null);
public static NullSTPInstancePerformanceCounters Instance public static NullSTPInstancePerformanceCounters Instance
{ {
get { return _instance; } get { return _instance; }
} }
public NullSTPInstancePerformanceCounters(string instance) {}
public void Close() {} public void Close() {}
public void Dispose() {} public void Dispose() {}
@ -349,6 +372,77 @@ namespace Amib.Threading.Internal
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {} public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {} public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {} 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
}
}
} }

View File

@ -1,6 +1,4 @@
// Ami Bar using System;
// amibar@gmail.com
using System.Threading; using System.Threading;
namespace Amib.Threading namespace Amib.Threading
@ -10,104 +8,205 @@ namespace Amib.Threading
/// </summary> /// </summary>
public class STPStartInfo : WIGStartInfo public class STPStartInfo : WIGStartInfo
{ {
/// <summary> private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
/// Idle timeout in milliseconds. private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
/// If a thread is idle for _idleTimeout milliseconds then private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
/// it may quit. #if !(WINDOWS_PHONE)
/// </summary> private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
private int _idleTimeout; #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> public STPStartInfo()
/// 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()
{ {
_performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
#if !(WINDOWS_PHONE)
_threadPriority = SmartThreadPool.DefaultThreadPriority;
#endif
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
_idleTimeout = SmartThreadPool.DefaultIdleTimeout; _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
_minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; _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; _idleTimeout = stpStartInfo.IdleTimeout;
_minWorkerThreads = stpStartInfo._minWorkerThreads; _minWorkerThreads = stpStartInfo.MinWorkerThreads;
_maxWorkerThreads = stpStartInfo._maxWorkerThreads; _maxWorkerThreads = stpStartInfo.MaxWorkerThreads;
_threadPriority = stpStartInfo._threadPriority; #if !(WINDOWS_PHONE)
_threadPriority = stpStartInfo.ThreadPriority;
#endif
_performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName;
_enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters;
_threadPoolName = stpStartInfo._threadPoolName; _threadPoolName = stpStartInfo._threadPoolName;
_pcInstanceName = stpStartInfo._pcInstanceName; _areThreadsBackground = stpStartInfo.AreThreadsBackground;
_stackSize = stpStartInfo._stackSize; #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; } 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; } 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; } 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; } get { return _threadPriority; }
set { _threadPriority = value; } set
}
public virtual string ThreadPoolName
{ {
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; } get { return _threadPoolName; }
set { _threadPoolName = value; } set
}
public string PerformanceCounterInstanceName
{ {
get { return _pcInstanceName; } ThrowIfReadOnly ();
set { _pcInstanceName = value; } _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; } get { return _performanceCounterInstanceName; }
set { _stackSize = value; } 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
}
} }

View File

@ -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

View File

@ -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();
}
}
}
}

View File

@ -1,5 +1,4 @@
// Ami Bar using System;
// amibar@gmail.com
namespace Amib.Threading namespace Amib.Threading
{ {
@ -8,92 +7,165 @@ namespace Amib.Threading
/// </summary> /// </summary>
public class WIGStartInfo public class WIGStartInfo
{ {
/// <summary>
/// Use the caller's security context
/// </summary>
private bool _useCallerCallContext; private bool _useCallerCallContext;
/// <summary>
/// Use the caller's HTTP context
/// </summary>
private bool _useCallerHttpContext; private bool _useCallerHttpContext;
/// <summary>
/// Dispose of the state object of a work item
/// </summary>
private bool _disposeOfStateObjects; private bool _disposeOfStateObjects;
/// <summary>
/// The option to run the post execute
/// </summary>
private CallToPostExecute _callToPostExecute; private CallToPostExecute _callToPostExecute;
/// <summary>
/// A post execute callback to call when none is provided in
/// the QueueWorkItem method.
/// </summary>
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; 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 bool _startSuspended;
private WorkItemPriority _workItemPriority;
private bool _fillStateWithArgs;
protected bool _readOnly;
public WIGStartInfo() public WIGStartInfo()
{ {
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; _workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_startSuspended = SmartThreadPool.DefaultStartSuspended; _startSuspended = SmartThreadPool.DefaultStartSuspended;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
} }
public WIGStartInfo(WIGStartInfo wigStartInfo) public WIGStartInfo(WIGStartInfo wigStartInfo)
{ {
_useCallerCallContext = wigStartInfo._useCallerCallContext; _useCallerCallContext = wigStartInfo.UseCallerCallContext;
_useCallerHttpContext = wigStartInfo._useCallerHttpContext; _useCallerHttpContext = wigStartInfo.UseCallerHttpContext;
_disposeOfStateObjects = wigStartInfo._disposeOfStateObjects; _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
_callToPostExecute = wigStartInfo._callToPostExecute; _callToPostExecute = wigStartInfo.CallToPostExecute;
_postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback; _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
_startSuspended = wigStartInfo._startSuspended; _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; } 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; } 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; } 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; } 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; } 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; } 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&lt;...&gt;/Func&lt;...&gt; 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 };
} }
} }
} }

View File

@ -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
}
}

View File

@ -1,58 +1,13 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Threading; using System.Threading;
using System.Diagnostics; using System.Diagnostics;
namespace Amib.Threading.Internal 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> /// <summary>
/// Holds a callback delegate and the state for that delegate. /// Holds a callback delegate and the state for that delegate.
/// </summary> /// </summary>
public class WorkItem : IHasWorkItemPriority, IWorkItem public partial class WorkItem : IHasWorkItemPriority
{ {
#region WorkItemState enum #region WorkItemState enum
@ -61,33 +16,57 @@ namespace Amib.Threading.Internal
/// </summary> /// </summary>
private enum WorkItemState private enum WorkItemState
{ {
InQueue, InQueue = 0, // Nexts: InProgress, Canceled
InProgress, InProgress = 1, // Nexts: Completed, Canceled
Completed, Completed = 2, // Stays Completed
Canceled, 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 #endregion
#region Member Variables #region Fields
public Thread currentThread;
/// <summary> /// <summary>
/// Callback delegate for the callback. /// Callback delegate for the callback.
/// </summary> /// </summary>
private WorkItemCallback _callback; private readonly WorkItemCallback _callback;
/// <summary> /// <summary>
/// State with which to call the callback delegate. /// State with which to call the callback delegate.
/// </summary> /// </summary>
private object _state; private object _state;
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
/// <summary> /// <summary>
/// Stores the caller's context /// Stores the caller's context
/// </summary> /// </summary>
private CallerThreadContext _callerContext; private readonly CallerThreadContext _callerContext;
#endif
/// <summary> /// <summary>
/// Holds the result of the mehtod /// Holds the result of the mehtod
/// </summary> /// </summary>
@ -117,12 +96,12 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Represents the result state of the work item /// Represents the result state of the work item
/// </summary> /// </summary>
private WorkItemResult _workItemResult; private readonly WorkItemResult _workItemResult;
/// <summary> /// <summary>
/// Work item info /// Work item info
/// </summary> /// </summary>
private WorkItemInfo _workItemInfo; private readonly WorkItemInfo _workItemInfo;
/// <summary> /// <summary>
/// Called when the WorkItem starts /// Called when the WorkItem starts
@ -141,30 +120,41 @@ namespace Amib.Threading.Internal
private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
/// <summary> /// <summary>
/// The work item group this work item belong to. /// A reference to an object that indicates whatever the
/// /// SmartThreadPool has been canceled
/// </summary> /// </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 #region Performance Counter fields
/// <summary>
/// The time when the work items is queued.
/// Used with the performance counter.
/// </summary>
private DateTime _queuedTime;
/// <summary> /// <summary>
/// The time when the work items starts its execution. /// Stores how long the work item waited on the stp queue
/// Used with the performance counter.
/// </summary> /// </summary>
private DateTime _beginProcessTime; private Stopwatch _waitingOnQueueStopwatch;
/// <summary> /// <summary>
/// The time when the work items ends its execution. /// Stores how much time it took the work item to execute after it went out of the queue
/// Used with the performance counter.
/// </summary> /// </summary>
private DateTime _endProcessTime; private Stopwatch _processingStopwatch;
#endregion #endregion
@ -176,7 +166,7 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
return (_beginProcessTime - _queuedTime); return _waitingOnQueueStopwatch.Elapsed;
} }
} }
@ -184,7 +174,15 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
return (_endProcessTime - _beginProcessTime); return _processingStopwatch.Elapsed;
}
}
internal WorkItemInfo WorkItemInfo
{
get
{
return _workItemInfo;
} }
} }
@ -195,6 +193,8 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Initialize the callback holding object. /// Initialize the callback holding object.
/// </summary> /// </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="callback">Callback delegate for the callback.</param>
/// <param name="state">State with which to call the callback delegate.</param> /// <param name="state">State with which to call the callback delegate.</param>
/// ///
@ -209,10 +209,12 @@ namespace Amib.Threading.Internal
_workItemsGroup = workItemsGroup; _workItemsGroup = workItemsGroup;
_workItemInfo = workItemInfo; _workItemInfo = workItemInfo;
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
{ {
_callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
} }
#endif
_callback = callback; _callback = callback;
_state = state; _state = state;
@ -222,9 +224,18 @@ namespace Amib.Threading.Internal
internal void Initialize() 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; _workItemState = WorkItemState.InQueue;
_workItemCompleted = null; _workItemCompleted = null;
_workItemCompletedRefCount = 0; _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) internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup)
@ -237,17 +248,16 @@ namespace Amib.Threading.Internal
#region Methods #region Methods
public CanceledWorkItemsGroup CanceledWorkItemsGroup internal CanceledWorkItemsGroup CanceledWorkItemsGroup
{ {
get get { return _canceledWorkItemsGroup; }
{ set { _canceledWorkItemsGroup = value; }
return _canceledWorkItemsGroup;
} }
set internal CanceledWorkItemsGroup CanceledSmartThreadPool
{ {
_canceledWorkItemsGroup = value; get { return _canceledSmartThreadPool; }
} set { _canceledSmartThreadPool = value; }
} }
/// <summary> /// <summary>
@ -259,7 +269,8 @@ namespace Amib.Threading.Internal
/// </returns> /// </returns>
public bool StartingWorkItem() public bool StartingWorkItem()
{ {
_beginProcessTime = DateTime.Now; _waitingOnQueueStopwatch.Stop();
_processingStopwatch.Start();
lock (this) lock (this)
{ {
@ -277,6 +288,9 @@ namespace Amib.Threading.Internal
Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); 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); SetWorkItemState(WorkItemState.InProgress);
} }
@ -311,7 +325,7 @@ namespace Amib.Threading.Internal
PostExecute(); PostExecute();
} }
_endProcessTime = DateTime.Now; _processingStopwatch.Stop();
} }
internal void FireWorkItemCompleted() internal void FireWorkItemCompleted()
@ -323,7 +337,20 @@ namespace Amib.Threading.Internal
_workItemCompletedEvent(this); _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> /// </summary>
private void ExecuteWorkItem() private void ExecuteWorkItem()
{ {
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
CallerThreadContext ctc = null; CallerThreadContext ctc = null;
if (null != _callerContext) if (null != _callerContext)
{ {
ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
CallerThreadContext.Apply(_callerContext); CallerThreadContext.Apply(_callerContext);
} }
#endif
Exception exception = null; Exception exception = null;
object result = null; object result = null;
try
{
try try
{ {
result = _callback(_state); result = _callback(_state);
@ -352,13 +384,46 @@ namespace Amib.Threading.Internal
exception = e; 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) if (null != _callerContext)
{ {
CallerThreadContext.Apply(ctc); CallerThreadContext.Apply(ctc);
} }
#endif
if (!SmartThreadPool.IsWorkItemCanceled)
{
SetResult(result, exception); SetResult(result, exception);
} }
}
/// <summary> /// <summary>
/// Runs the post execute callback /// Runs the post execute callback
@ -369,7 +434,7 @@ namespace Amib.Threading.Internal
{ {
try try
{ {
_workItemInfo.PostExecuteWorkItemCallback(this._workItemResult); _workItemInfo.PostExecuteWorkItemCallback(_workItemResult);
} }
catch (Exception e) catch (Exception e)
{ {
@ -382,6 +447,8 @@ namespace Amib.Threading.Internal
/// Set the result of the work item to return /// Set the result of the work item to return
/// </summary> /// </summary>
/// <param name="result">The result of the work item</param> /// <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) internal void SetResult(object result, Exception exception)
{ {
_result = result; _result = result;
@ -401,39 +468,39 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Wait for all work items to complete /// Wait for all work items to complete
/// </summary> /// </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="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
/// <param name="exitContext"> /// <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. /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
/// </param> /// </param>
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
/// <returns> /// <returns>
/// true when every work item in workItemResults has completed; otherwise false. /// true when every work item in waitableResults has completed; otherwise false.
/// </returns> /// </returns>
internal static bool WaitAll( internal static bool WaitAll(
IWorkItemResult [] workItemResults, IWaitableResult[] waitableResults,
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle) WaitHandle cancelWaitHandle)
{ {
if (0 == workItemResults.Length) if (0 == waitableResults.Length)
{ {
return true; return true;
} }
bool success; bool success;
WaitHandle [] waitHandles = new WaitHandle[workItemResults.Length];; WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length];
GetWaitHandles(workItemResults, waitHandles); GetWaitHandles(waitableResults, waitHandles);
if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) if ((null == cancelWaitHandle) && (waitHandles.Length <= 64))
{ {
success = WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
} }
else else
{ {
success = true; success = true;
int millisecondsLeft = millisecondsTimeout; int millisecondsLeft = millisecondsTimeout;
DateTime start = DateTime.Now; Stopwatch stopwatch = Stopwatch.StartNew();
WaitHandle[] whs; WaitHandle[] whs;
if (null != cancelWaitHandle) if (null != cancelWaitHandle)
@ -450,7 +517,7 @@ namespace Amib.Threading.Internal
// We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle
// won't affect it. // won't affect it.
// Each iteration we update the time left for the timeout. // 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 // WaitAny don't work with negative numbers
if (!waitInfinitely && (millisecondsLeft < 0)) if (!waitInfinitely && (millisecondsLeft < 0))
@ -460,8 +527,8 @@ namespace Amib.Threading.Internal
} }
whs[0] = waitHandles[i]; whs[0] = waitHandles[i];
int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext); int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
if((result > 0) || (WaitHandle.WaitTimeout == result)) if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result))
{ {
success = false; success = false;
break; break;
@ -470,13 +537,12 @@ namespace Amib.Threading.Internal
if (!waitInfinitely) if (!waitInfinitely)
{ {
// Update the time left to wait // Update the time left to wait
TimeSpan ts = DateTime.Now - start; millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
} }
} }
} }
// Release the wait handles // Release the wait handles
ReleaseWaitHandles(workItemResults); ReleaseWaitHandles(waitableResults);
return success; return success;
} }
@ -484,7 +550,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout /// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary> /// </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="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
/// <param name="exitContext"> /// <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. /// 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. /// 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> /// </returns>
internal static int WaitAny( internal static int WaitAny(
IWorkItemResult [] workItemResults, IWaitableResult[] waitableResults,
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle) WaitHandle cancelWaitHandle)
{ {
WaitHandle [] waitHandles = null; WaitHandle[] waitHandles;
if (null != cancelWaitHandle) if (null != cancelWaitHandle)
{ {
waitHandles = new WaitHandle[workItemResults.Length+1]; waitHandles = new WaitHandle[waitableResults.Length + 1];
GetWaitHandles(workItemResults, waitHandles); GetWaitHandles(waitableResults, waitHandles);
waitHandles[workItemResults.Length] = cancelWaitHandle; waitHandles[waitableResults.Length] = cancelWaitHandle;
} }
else else
{ {
waitHandles = new WaitHandle[workItemResults.Length]; waitHandles = new WaitHandle[waitableResults.Length];
GetWaitHandles(workItemResults, waitHandles); GetWaitHandles(waitableResults, waitHandles);
} }
int result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
// Treat cancel as timeout // Treat cancel as timeout
if (null != cancelWaitHandle) if (null != cancelWaitHandle)
{ {
if (result == workItemResults.Length) if (result == waitableResults.Length)
{ {
result = WaitHandle.WaitTimeout; result = STPEventWaitHandle.WaitTimeout;
} }
} }
ReleaseWaitHandles(workItemResults); ReleaseWaitHandles(waitableResults);
return result; return result;
} }
@ -532,16 +598,16 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Fill an array of wait handles with the work items wait handles. /// Fill an array of wait handles with the work items wait handles.
/// </summary> /// </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> /// <param name="waitHandles">An array of wait handles to fill</param>
private static void GetWaitHandles( private static void GetWaitHandles(
IWorkItemResult [] workItemResults, IWaitableResult[] waitableResults,
WaitHandle[] waitHandles) WaitHandle[] waitHandles)
{ {
for(int i = 0; i < workItemResults.Length; ++i) for (int i = 0; i < waitableResults.Length; ++i)
{ {
WorkItemResult wir = workItemResults[i] as WorkItemResult; WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult;
Debug.Assert(null != wir, "All workItemResults must be WorkItemResult objects"); Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects");
waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); waitHandles[i] = wir.GetWorkItem().GetWaitHandle();
} }
@ -550,31 +616,52 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Release the work items' wait handles /// Release the work items' wait handles
/// </summary> /// </summary>
/// <param name="workItemResults">An array of work item results</param> /// <param name="waitableResults">An array of work item results</param>
private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults) 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(); wir.GetWorkItem().ReleaseWaitHandle();
} }
} }
#endregion #endregion
#region Private Members #region Private Members
private WorkItemState GetWorkItemState() 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.Canceled;
} }
return _workItemState;
return _workItemState;
} }
}
/// <summary> /// <summary>
/// Sets the work item's state /// Sets the work item's state
/// </summary> /// </summary>
@ -582,10 +669,13 @@ namespace Amib.Threading.Internal
private void SetWorkItemState(WorkItemState workItemState) private void SetWorkItemState(WorkItemState workItemState)
{ {
lock (this) lock (this)
{
if (IsValidStatesTransition(_workItemState, workItemState))
{ {
_workItemState = workItemState; _workItemState = workItemState;
} }
} }
}
/// <summary> /// <summary>
/// Signals that work item has been completed or canceled /// Signals that work item has been completed or canceled
@ -606,7 +696,7 @@ namespace Amib.Threading.Internal
internal void WorkItemIsQueued() internal void WorkItemIsQueued()
{ {
_queuedTime = DateTime.Now; _waitingOnQueueStopwatch.Start();
} }
#endregion #endregion
@ -617,29 +707,82 @@ namespace Amib.Threading.Internal
/// Cancel the work item if it didn't start running yet. /// Cancel the work item if it didn't start running yet.
/// </summary> /// </summary>
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns> /// <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) lock (this)
{ {
switch (GetWorkItemState()) switch (GetWorkItemState())
{ {
case WorkItemState.Canceled: case WorkItemState.Canceled:
//Debug.WriteLine("Work item already 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.Completed:
case WorkItemState.InProgress:
//Debug.WriteLine("Work item cannot be canceled"); //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: case WorkItemState.InQueue:
// Signal to the wait for completion that the work // Signal to the wait for completion that the work
// item has been completed (canceled). There is no // item has been completed (canceled). There is no
// reason to wait for it to get out of the queue // reason to wait for it to get out of the queue
SignalComplete(true); signalComplete = true;
//Debug.WriteLine("Work item canceled"); //Debug.WriteLine("Work item canceled");
return true; success = true;
break;
}
if (signalComplete)
{
SignalComplete(true);
} }
} }
return false; return success;
} }
/// <summary> /// <summary>
@ -653,7 +796,7 @@ namespace Amib.Threading.Internal
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle) WaitHandle cancelWaitHandle)
{ {
Exception e = null; Exception e;
object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
if (null != e) if (null != e)
{ {
@ -694,7 +837,7 @@ namespace Amib.Threading.Internal
{ {
WaitHandle wh = GetWaitHandle(); WaitHandle wh = GetWaitHandle();
bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext); bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext);
ReleaseWaitHandle(); ReleaseWaitHandle();
@ -706,7 +849,7 @@ namespace Amib.Threading.Internal
else else
{ {
WaitHandle wh = GetWaitHandle(); WaitHandle wh = GetWaitHandle();
int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
ReleaseWaitHandle(); ReleaseWaitHandle();
switch (result) switch (result)
@ -717,7 +860,7 @@ namespace Amib.Threading.Internal
// work item (not the get result) // work item (not the get result)
break; break;
case 1: case 1:
case WaitHandle.WaitTimeout: case STPEventWaitHandle.WaitTimeout:
throw new WorkItemTimeoutException("Work item timeout"); throw new WorkItemTimeoutException("Work item timeout");
default: default:
Debug.Assert(false); Debug.Assert(false);
@ -749,7 +892,7 @@ namespace Amib.Threading.Internal
{ {
if (null == _workItemCompleted) if (null == _workItemCompleted)
{ {
_workItemCompleted = new ManualResetEvent(IsCompleted); _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted);
} }
++_workItemCompletedRefCount; ++_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() public void DisposeOfState()
{ {
if (_workItemInfo.DisposeOfStateObjects) if (_workItemInfo.DisposeOfStateObjects)
@ -1021,15 +998,5 @@ namespace Amib.Threading.Internal
} }
} }
} }
public void Abort()
{
lock (this)
{
if(currentThread != null)
currentThread.Abort();
} }
} }
}
#endregion
}

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
@ -12,6 +9,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item</returns> /// <returns>Returns a work item</returns>
@ -26,6 +24,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="workItemPriority">The priority of the work item</param> /// <param name="workItemPriority">The priority of the work item</param>
@ -42,6 +41,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="workItemInfo">Work item info</param> /// <param name="workItemInfo">Work item info</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
@ -63,6 +63,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -83,6 +84,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem( WorkItem workItem = new WorkItem(
workItemsGroup, workItemsGroup,
@ -95,6 +97,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -131,6 +134,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="workItemInfo">Work item information</param> /// <param name="workItemInfo">Work item information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
@ -160,6 +164,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -185,6 +190,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem( WorkItem workItem = new WorkItem(
workItemsGroup, workItemsGroup,
@ -198,6 +204,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -239,6 +246,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -266,6 +274,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = callToPostExecute; workItemInfo.CallToPostExecute = callToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem( WorkItem workItem = new WorkItem(
workItemsGroup, workItemsGroup,
@ -279,6 +288,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -322,7 +332,7 @@ namespace Amib.Threading.Internal
private static void ValidateCallback(Delegate callback) 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"); throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
} }

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
namespace Amib.Threading namespace Amib.Threading
{ {
#region WorkItemInfo class #region WorkItemInfo class
@ -10,92 +7,62 @@ namespace Amib.Threading
/// </summary> /// </summary>
public class WorkItemInfo 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() public WorkItemInfo()
{ {
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_workItemPriority = SmartThreadPool.DefaultWorkItemPriority; WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
} }
public WorkItemInfo(WorkItemInfo workItemInfo) public WorkItemInfo(WorkItemInfo workItemInfo)
{ {
_useCallerCallContext = workItemInfo._useCallerCallContext; UseCallerCallContext = workItemInfo.UseCallerCallContext;
_useCallerHttpContext = workItemInfo._useCallerHttpContext; UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
_disposeOfStateObjects = workItemInfo._disposeOfStateObjects; DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
_callToPostExecute = workItemInfo._callToPostExecute; CallToPostExecute = workItemInfo.CallToPostExecute;
_postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback; PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
_workItemPriority = workItemInfo._workItemPriority; WorkItemPriority = workItemInfo.WorkItemPriority;
Timeout = workItemInfo.Timeout;
} }
public bool UseCallerCallContext /// <summary>
{ /// Get/Set if to use the caller's security context
get { return _useCallerCallContext; } /// </summary>
set { _useCallerCallContext = value; } public bool UseCallerCallContext { get; set; }
}
public bool UseCallerHttpContext /// <summary>
{ /// Get/Set if to use the caller's HTTP context
get { return _useCallerHttpContext; } /// </summary>
set { _useCallerHttpContext = value; } public bool UseCallerHttpContext { get; set; }
}
public bool DisposeOfStateObjects /// <summary>
{ /// Get/Set if to dispose of the state object of a work item
get { return _disposeOfStateObjects; } /// </summary>
set { _disposeOfStateObjects = value; } public bool DisposeOfStateObjects { get; set; }
}
public CallToPostExecute CallToPostExecute /// <summary>
{ /// Get/Set the run the post execute options
get { return _callToPostExecute; } /// </summary>
set { _callToPostExecute = value; } public CallToPostExecute CallToPostExecute { get; set; }
}
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback /// <summary>
{ /// Get/Set the post execute callback
get { return _postExecuteWorkItemCallback; } /// </summary>
set { _postExecuteWorkItemCallback = value; } public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }
}
public WorkItemPriority WorkItemPriority /// <summary>
{ /// Get/Set the work item's priority
get { return _workItemPriority; } /// </summary>
set { _workItemPriority = value; } 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 #endregion

View File

@ -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
}

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Threading; using System.Threading;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -8,33 +5,34 @@ using System.Diagnostics;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
{ {
#region WorkItemsGroup class #region WorkItemsGroup class
/// <summary> /// <summary>
/// Summary description for WorkItemsGroup. /// Summary description for WorkItemsGroup.
/// </summary> /// </summary>
public class WorkItemsGroup : IWorkItemsGroup public class WorkItemsGroup : WorkItemsGroupBase
{ {
#region Private members #region Private members
private object _lock = new object(); private readonly object _lock = new object();
/// <summary>
/// Contains the name of this instance of SmartThreadPool.
/// Can be changed by the user.
/// </summary>
private string _name = "WorkItemsGroup";
/// <summary> /// <summary>
/// A reference to the SmartThreadPool instance that created this /// A reference to the SmartThreadPool instance that created this
/// WorkItemsGroup. /// WorkItemsGroup.
/// </summary> /// </summary>
private SmartThreadPool _stp; private readonly SmartThreadPool _stp;
/// <summary> /// <summary>
/// The OnIdle event /// The OnIdle event
/// </summary> /// </summary>
private event WorkItemsGroupIdleHandler _onIdle; private event WorkItemsGroupIdleHandler _onIdle;
/// <summary>
/// A flag to indicate if the Work Items Group is now suspended.
/// </summary>
private bool _isSuspended;
/// <summary> /// <summary>
/// Defines how many work items of this WorkItemsGroup can run at once. /// Defines how many work items of this WorkItemsGroup can run at once.
/// </summary> /// </summary>
@ -44,7 +42,7 @@ namespace Amib.Threading.Internal
/// Priority queue to hold work items before they are passed /// Priority queue to hold work items before they are passed
/// to the SmartThreadPool. /// to the SmartThreadPool.
/// </summary> /// </summary>
private PriorityQueue _workItemsQueue; private readonly PriorityQueue _workItemsQueue;
/// <summary> /// <summary>
/// Indicate how many work items are waiting in the SmartThreadPool /// Indicate how many work items are waiting in the SmartThreadPool
@ -63,12 +61,13 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// WorkItemsGroup start information /// WorkItemsGroup start information
/// </summary> /// </summary>
private WIGStartInfo _workItemsGroupStartInfo; private readonly WIGStartInfo _workItemsGroupStartInfo;
/// <summary> /// <summary>
/// Signaled when all of the WorkItemsGroup's work item completed. /// Signaled when all of the WorkItemsGroup's work item completed.
/// </summary> /// </summary>
private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
/// <summary> /// <summary>
/// A common object for all the work items that this work items group /// A common object for all the work items that this work items group
@ -87,261 +86,90 @@ namespace Amib.Threading.Internal
{ {
if (concurrency <= 0) 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; _stp = stp;
_concurrency = concurrency; _concurrency = concurrency;
_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo); _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly();
_workItemsQueue = new PriorityQueue(); _workItemsQueue = new PriorityQueue();
Name = "WorkItemsGroup";
// The _workItemsInStpQueue gets the number of currently executing work items, // The _workItemsInStpQueue gets the number of currently executing work items,
// because once a work item is executing, it cannot be cancelled. // because once a work item is executing, it cannot be cancelled.
_workItemsInStpQueue = _workItemsExecutingInStp; _workItemsInStpQueue = _workItemsExecutingInStp;
_isSuspended = _workItemsGroupStartInfo.StartSuspended;
} }
#endregion #endregion
#region IWorkItemsGroup implementation #region WorkItemsGroupBase Overrides
/// <summary> public override int Concurrency
/// Get/Set the name of the SmartThreadPool instance
/// </summary>
public string Name
{ {
get get { return _concurrency; }
{
return _name;
}
set 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> /// <summary>
/// Queue a work item /// WorkItemsGroup start information
/// </summary> /// </summary>
/// <param name="callback">A callback to execute</param> public override WIGStartInfo WIGStartInfo
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
{ {
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback); get { return _workItemsGroupStartInfo; }
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
} }
/// <summary> /// <summary>
/// Queue a work item /// Start the Work Items Group if it was started suspended
/// </summary> /// </summary>
/// <param name="callback">A callback to execute</param> public override void Start()
/// <param name="workItemPriority">The priority of the work item</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
{ {
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority); // If the Work Items Group already started then quit
EnqueueToSTPNextWorkItem(workItem); if (!_isSuspended)
return workItem.GetWorkItemResult(); {
return;
}
_isSuspended = false;
EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
} }
/// <summary> public override void Cancel(bool abortExecution)
/// 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()
{ {
lock (_lock) lock (_lock)
{ {
@ -350,23 +178,26 @@ namespace Amib.Threading.Internal
_workItemsInStpQueue = 0; _workItemsInStpQueue = 0;
_canceledWorkItemsGroup = new CanceledWorkItemsGroup(); _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) SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
{ return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
if (!_workItemsGroupStartInfo.StartSuspended)
{
return;
}
_workItemsGroupStartInfo.StartSuspended = false;
} }
for(int i = 0; i < _concurrency; ++i) public override event WorkItemsGroupIdleHandler OnIdle
{ {
EnqueueToSTPNextWorkItem(null, false); add { _onIdle += value; }
} remove { _onIdle -= value; }
} }
#endregion #endregion
@ -375,22 +206,24 @@ namespace Amib.Threading.Internal
private void RegisterToWorkItemCompletion(IWorkItemResult wir) private void RegisterToWorkItemCompletion(IWorkItemResult wir)
{ {
IInternalWorkItemResult iwir = wir as IInternalWorkItemResult; IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
iwir.OnWorkItemStarted += new WorkItemStateCallback(OnWorkItemStartedCallback); iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
iwir.OnWorkItemCompleted += new WorkItemStateCallback(OnWorkItemCompletedCallback); iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
} }
public void OnSTPIsStarting() public void OnSTPIsStarting()
{ {
lock (this) if (_isSuspended)
{
if (_workItemsGroupStartInfo.StartSuspended)
{ {
return; 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); EnqueueToSTPNextWorkItem(null, false);
} }
@ -417,8 +250,7 @@ namespace Amib.Threading.Internal
{ {
eh(this); eh(this);
} }
// Ignore exceptions catch { } // Suppress exceptions
catch{}
} }
} }
@ -435,6 +267,11 @@ namespace Amib.Threading.Internal
EnqueueToSTPNextWorkItem(null, true); EnqueueToSTPNextWorkItem(null, true);
} }
internal override void Enqueue(WorkItem workItem)
{
EnqueueToSTPNextWorkItem(workItem);
}
private void EnqueueToSTPNextWorkItem(WorkItem workItem) private void EnqueueToSTPNextWorkItem(WorkItem workItem)
{ {
EnqueueToSTPNextWorkItem(workItem, false); EnqueueToSTPNextWorkItem(workItem, false);
@ -475,7 +312,7 @@ namespace Amib.Threading.Internal
(0 == _workItemsInStpQueue)) (0 == _workItemsInStpQueue))
{ {
_stp.RegisterWorkItemsGroup(this); _stp.RegisterWorkItemsGroup(this);
Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle"); IsIdle = false;
_isIdleWaitHandle.Reset(); _isIdleWaitHandle.Reset();
} }
} }
@ -486,19 +323,31 @@ namespace Amib.Threading.Internal
if (0 == _workItemsInStpQueue) if (0 == _workItemsInStpQueue)
{ {
_stp.UnregisterWorkItemsGroup(this); _stp.UnregisterWorkItemsGroup(this);
Trace.WriteLine("WorkItemsGroup " + Name + " is idle"); IsIdle = true;
_isIdleWaitHandle.Set(); _isIdleWaitHandle.Set();
_stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle)); if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
{
_stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
}
} }
return; return;
} }
if (!_workItemsGroupStartInfo.StartSuspended) if (!_isSuspended)
{ {
if (_workItemsInStpQueue < _concurrency) if (_workItemsInStpQueue < _concurrency)
{ {
WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; 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; ++_workItemsInStpQueue;
} }
} }

View File

@ -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
}
}

View File

@ -1,7 +1,5 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
@ -18,7 +16,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Waiters queue (implemented as stack). /// Waiters queue (implemented as stack).
/// </summary> /// </summary>
private WaiterEntry _headWaiterEntry = new WaiterEntry(); private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();
/// <summary> /// <summary>
/// Waiters count /// Waiters count
@ -28,18 +26,70 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Work items queue /// Work items queue
/// </summary> /// </summary>
private PriorityQueue _workItems = new PriorityQueue(); private readonly PriorityQueue _workItems = new PriorityQueue();
/// <summary> /// <summary>
/// Indicate that work items are allowed to be queued /// Indicate that work items are allowed to be queued
/// </summary> /// </summary>
private bool _isWorkItemsQueueActive = true; 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> /// <summary>
/// Each thread in the thread pool keeps its own waiter entry. /// Each thread in the thread pool keeps its own waiter entry.
/// </summary> /// </summary>
[ThreadStatic] private static WaiterEntry CurrentWaiterEntry
private static WaiterEntry _waiterEntry; {
#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> /// <summary>
/// A flag that indicates if the WorkItemsQueue has been disposed. /// A flag that indicates if the WorkItemsQueue has been disposed.
@ -57,13 +107,9 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
lock(this)
{
ValidateNotDisposed();
return _workItems.Count; return _workItems.Count;
} }
} }
}
/// <summary> /// <summary>
/// Returns the current number of waiters /// Returns the current number of waiters
@ -72,13 +118,9 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
lock(this)
{
ValidateNotDisposed();
return _waitersCount; return _waitersCount;
} }
} }
}
#endregion #endregion
@ -144,21 +186,20 @@ namespace Amib.Threading.Internal
int millisecondsTimeout, int millisecondsTimeout,
WaitHandle cancelEvent) WaitHandle cancelEvent)
{ {
/// This method cause the caller to wait for 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 // If there is at least one waiting work item then the
/// method returns immidiately with true. // method returns immidiately with it.
/// //
/// If there are no waiting work items then the caller // If there are no waiting work items then the caller
/// is queued between other waiters for a work item to arrive. // is queued between other waiters for a work item to arrive.
/// //
/// If a work item didn't come within millisecondsTimeout or // If a work item didn't come within millisecondsTimeout or
/// the user canceled the wait by signaling the cancelEvent // the user canceled the wait by signaling the cancelEvent
/// then the method returns false to indicate that the caller // then the method returns null to indicate that the caller
/// didn't get a work item. // didn't get a work item.
WaiterEntry waiterEntry = null; WaiterEntry waiterEntry;
WorkItem workItem = null; WorkItem workItem = null;
lock (this) lock (this)
{ {
ValidateNotDisposed(); ValidateNotDisposed();
@ -169,16 +210,15 @@ namespace Amib.Threading.Internal
workItem = _workItems.Dequeue() as WorkItem; workItem = _workItems.Dequeue() as WorkItem;
return workItem; return workItem;
} }
// No waiting work items ... // No waiting work items ...
else
{ // Get the waiter entry for the waiters queue
// Get the wait entry for the waiters queue
waiterEntry = GetThreadWaiterEntry(); waiterEntry = GetThreadWaiterEntry();
// Put the waiter with the other waiters // Put the waiter with the other waiters
PushWaiter(waiterEntry); PushWaiter(waiterEntry);
} }
}
// Prepare array of wait handle for the WaitHandle.WaitAny() // Prepare array of wait handle for the WaitHandle.WaitAny()
WaitHandle [] waitHandles = new WaitHandle[] { WaitHandle [] waitHandles = new WaitHandle[] {
@ -189,10 +229,10 @@ namespace Amib.Threading.Internal
// During the wait we are supposes to exit the synchronization // During the wait we are supposes to exit the synchronization
// domain. (Placing true as the third argument of the WaitAny()) // 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) // It just doesn't work, I don't know why, so I have two lock(this)
// statments insted of one. // statments instead of one.
int index = WaitHandle.WaitAny( int index = STPEventWaitHandle.WaitAny(
waitHandles, waitHandles,
millisecondsTimeout, millisecondsTimeout,
true); true);
@ -242,7 +282,7 @@ namespace Amib.Threading.Internal
/// Cleanup the work items queue, hence no more work /// Cleanup the work items queue, hence no more work
/// items are allowed to be queue /// items are allowed to be queue
/// </summary> /// </summary>
protected virtual void Cleanup() private void Cleanup()
{ {
lock(this) 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 #endregion
#region Private methods #region Private methods
@ -289,14 +344,14 @@ namespace Amib.Threading.Internal
/// <returns></returns> /// <returns></returns>
/// In order to avoid creation and destuction of WaiterEntry /// In order to avoid creation and destuction of WaiterEntry
/// objects each thread has its own WaiterEntry object. /// 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(); CurrentWaiterEntry.Reset();
return _waiterEntry; return CurrentWaiterEntry;
} }
#region Waiters stack methods #region Waiters stack methods
@ -418,14 +473,15 @@ namespace Amib.Threading.Internal
#region WaiterEntry class #region WaiterEntry class
// A waiter entry in the _waiters queue. // A waiter entry in the _waiters queue.
public class WaiterEntry : IDisposable public sealed class WaiterEntry : IDisposable
{ {
#region Member variables #region Member variables
/// <summary> /// <summary>
/// Event to signal the waiter that it got the work item. /// Event to signal the waiter that it got the work item.
/// </summary> /// </summary>
private AutoResetEvent _waitHandle = new AutoResetEvent(false); //private AutoResetEvent _waitHandle = new AutoResetEvent(false);
private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();
/// <summary> /// <summary>
/// Flag to know if this waiter already quited from the queue /// Flag to know if this waiter already quited from the queue
@ -471,13 +527,10 @@ namespace Amib.Threading.Internal
public WorkItem WorkItem public WorkItem WorkItem
{ {
get get
{
lock(this)
{ {
return _workItem; return _workItem;
} }
} }
}
/// <summary> /// <summary>
/// Signal the waiter that it got a work item. /// Signal the waiter that it got a work item.
@ -550,18 +603,16 @@ namespace Amib.Threading.Internal
public void Dispose() public void Dispose()
{ {
lock (this)
{
if (!_isDisposed) if (!_isDisposed)
{ {
Close(); Close();
}
_isDisposed = true; _isDisposed = true;
} }
} }
~WaiterEntry()
{
Dispose();
}
#endregion #endregion
} }
@ -574,14 +625,8 @@ namespace Amib.Threading.Internal
if (!_isDisposed) if (!_isDisposed)
{ {
Cleanup(); Cleanup();
}
_isDisposed = true; _isDisposed = true;
GC.SuppressFinalize(this);
}
}
~WorkItemsQueue()
{
Cleanup();
} }
private void ValidateNotDisposed() private void ValidateNotDisposed()

View File

@ -21,6 +21,9 @@
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>] ; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
; * ; *
[Startup] [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 ; Plugin Registry Location
; Set path to directory for plugin registry. Information ; Set path to directory for plugin registry. Information
@ -32,7 +35,7 @@
; Modular configurations ; Modular configurations
; Set path to directory for modular ini files... ; Set path to directory for modular ini files...
; The Robust.exe process must have R/W access to the location ; The Robust.exe process must have R/W access to the location
ConfigDirectory = "/home/opensim/etc/Configs" ConfigDirectory = "."
[ServiceList] [ServiceList]

View File

@ -13,6 +13,9 @@
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>] ; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
; * ; *
[Startup] [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 ; Plugin Registry Location
; Set path to directory for plugin registry. Information ; Set path to directory for plugin registry. Information
@ -21,11 +24,10 @@
; The Robust.exe process must have R/W access to the location ; The Robust.exe process must have R/W access to the location
RegistryLocation = "." RegistryLocation = "."
; Modular configurations ; Modular configurations
; Set path to directory for modular ini files... ; Set path to directory for modular ini files...
; The Robust.exe process must have R/W access to the location ; The Robust.exe process must have R/W access to the location
ConfigDirectory = "/home/opensim/etc/Configs" ConfigDirectory = "."
[ServiceList] [ServiceList]
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector" AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"

View File

@ -3055,6 +3055,7 @@
<Match path="Avatar/AvatarFactory/Tests" pattern="*.cs" recurse="true"/> <Match path="Avatar/AvatarFactory/Tests" pattern="*.cs" recurse="true"/>
<Match path="Avatar/Friends/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/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="Framework/InventoryAccess/Tests" pattern="*.cs" recurse="true"/>
<Match path="Scripting/VectorRender/Tests" pattern="*.cs" recurse="true"/> <Match path="Scripting/VectorRender/Tests" pattern="*.cs" recurse="true"/>
<Match path="World/Archiver/Tests" pattern="*.cs" recurse="true"/> <Match path="World/Archiver/Tests" pattern="*.cs" recurse="true"/>