Merge branch 'master' into bulletsim4
commit
fca4e4ec7c
|
@ -158,7 +158,7 @@ namespace OpenSim.Framework.Capabilities
|
|||
/// capabilities and their handler details.
|
||||
/// </summary>
|
||||
/// <param name="excludeSeed">If true, then exclude the seed cap.</param>
|
||||
public Hashtable GetCapsDetails(bool excludeSeed)
|
||||
public Hashtable GetCapsDetails(bool excludeSeed, List<string> requestedCaps)
|
||||
{
|
||||
Hashtable caps = new Hashtable();
|
||||
string protocol = "http://";
|
||||
|
@ -175,6 +175,9 @@ namespace OpenSim.Framework.Capabilities
|
|||
if (excludeSeed && "SEED" == capsName)
|
||||
continue;
|
||||
|
||||
if (requestedCaps != null && !requestedCaps.Contains(capsName))
|
||||
continue;
|
||||
|
||||
caps[capsName] = baseUrl + m_capsHandlers[capsName].Path;
|
||||
}
|
||||
}
|
||||
|
@ -182,4 +185,4 @@ namespace OpenSim.Framework.Capabilities
|
|||
return caps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1840,7 +1840,7 @@ namespace OpenSim.Framework
|
|||
case FireAndForgetMethod.SmartThreadPool:
|
||||
if (m_ThreadPool == null)
|
||||
InitThreadPool(15);
|
||||
m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj });
|
||||
m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj);
|
||||
break;
|
||||
case FireAndForgetMethod.Thread:
|
||||
Thread thread = new Thread(delegate(object o) { realCallback(o); });
|
||||
|
@ -1910,15 +1910,15 @@ namespace OpenSim.Framework
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static object SmartThreadPoolCallback(object o)
|
||||
{
|
||||
object[] array = (object[])o;
|
||||
WaitCallback callback = (WaitCallback)array[0];
|
||||
object obj = array[1];
|
||||
|
||||
callback(obj);
|
||||
return null;
|
||||
}
|
||||
// private static object SmartThreadPoolCallback(object o)
|
||||
// {
|
||||
// object[] array = (object[])o;
|
||||
// WaitCallback callback = (WaitCallback)array[0];
|
||||
// object obj = array[1];
|
||||
//
|
||||
// callback(obj);
|
||||
// return null;
|
||||
// }
|
||||
|
||||
#endregion FireAndForget Threading Pattern
|
||||
|
||||
|
|
|
@ -273,11 +273,22 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
return string.Empty;
|
||||
}
|
||||
|
||||
Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true);
|
||||
OSDArray capsRequested = (OSDArray)OSDParser.DeserializeLLSDXml(request);
|
||||
List<string> validCaps = new List<string>();
|
||||
|
||||
foreach (OSD c in capsRequested)
|
||||
validCaps.Add(c.AsString());
|
||||
|
||||
Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps);
|
||||
|
||||
// Add the external too
|
||||
foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers)
|
||||
{
|
||||
if (!validCaps.Contains(kvp.Key))
|
||||
continue;
|
||||
|
||||
caps[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
string result = LLSDHelpers.SerialiseLLSDReply(caps);
|
||||
|
||||
|
|
|
@ -75,6 +75,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
base.SetUp();
|
||||
|
||||
UUID userId = TestHelpers.ParseTail(0x3);
|
||||
|
||||
J2KDecoderModule j2kdm = new J2KDecoderModule();
|
||||
|
|
|
@ -47,10 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
|
||||
/// <summary>
|
||||
private List<Scene> m_Scenelist = new List<Scene>();
|
||||
// private Dictionary<UUID, Scene> m_AgentRegions =
|
||||
// new Dictionary<UUID, Scene>();
|
||||
|
||||
private IMessageTransferModule m_TransferModule = null;
|
||||
private IMessageTransferModule m_TransferModule;
|
||||
private bool m_Enabled = true;
|
||||
|
||||
#region Region Module interface
|
||||
|
@ -81,9 +79,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
// scene.RegisterModuleInterface<IInventoryTransferModule>(this);
|
||||
|
||||
scene.EventManager.OnNewClient += OnNewClient;
|
||||
// scene.EventManager.OnClientClosed += ClientLoggedOut;
|
||||
scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
|
||||
// scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene;
|
||||
}
|
||||
|
||||
public void RegionLoaded(Scene scene)
|
||||
|
@ -96,11 +92,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only");
|
||||
m_Enabled = false;
|
||||
|
||||
m_Scenelist.Clear();
|
||||
scene.EventManager.OnNewClient -= OnNewClient;
|
||||
// scene.EventManager.OnClientClosed -= ClientLoggedOut;
|
||||
// m_Scenelist.Clear();
|
||||
// scene.EventManager.OnNewClient -= OnNewClient;
|
||||
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
|
||||
// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,9 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
public void RemoveRegion(Scene scene)
|
||||
{
|
||||
scene.EventManager.OnNewClient -= OnNewClient;
|
||||
// scene.EventManager.OnClientClosed -= ClientLoggedOut;
|
||||
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
|
||||
// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene;
|
||||
m_Scenelist.Remove(scene);
|
||||
}
|
||||
|
||||
|
@ -139,11 +131,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
// Inventory giving is conducted via instant message
|
||||
client.OnInstantMessage += OnInstantMessage;
|
||||
}
|
||||
|
||||
// protected void OnSetRootAgentScene(UUID id, Scene scene)
|
||||
// {
|
||||
// m_AgentRegions[id] = scene;
|
||||
// }
|
||||
|
||||
private Scene FindClientScene(UUID agentId)
|
||||
{
|
||||
|
@ -188,9 +175,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
{
|
||||
UUID folderID = new UUID(im.binaryBucket, 1);
|
||||
|
||||
m_log.DebugFormat("[INVENTORY TRANSFER]: Inserting original folder {0} "+
|
||||
"into agent {1}'s inventory",
|
||||
folderID, new UUID(im.toAgentID));
|
||||
m_log.DebugFormat(
|
||||
"[INVENTORY TRANSFER]: Inserting original folder {0} into agent {1}'s inventory",
|
||||
folderID, new UUID(im.toAgentID));
|
||||
|
||||
InventoryFolderBase folderCopy
|
||||
= scene.GiveInventoryFolder(receipientID, client.AgentId, folderID, UUID.Zero);
|
||||
|
@ -213,7 +200,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
user.ControllingClient.SendBulkUpdateInventory(folderCopy);
|
||||
|
||||
// HACK!!
|
||||
im.imSessionID = folderID.Guid;
|
||||
// Insert the ID of the copied folder into the IM so that we know which item to move to trash if it
|
||||
// is rejected.
|
||||
// XXX: This is probably a misuse of the session ID slot.
|
||||
im.imSessionID = copyID.Guid;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -243,7 +233,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
user.ControllingClient.SendBulkUpdateInventory(itemCopy);
|
||||
|
||||
// HACK!!
|
||||
im.imSessionID = itemID.Guid;
|
||||
// Insert the ID of the copied item into the IM so that we know which item to move to trash if it
|
||||
// is rejected.
|
||||
// XXX: This is probably a misuse of the session ID slot.
|
||||
im.imSessionID = copyID.Guid;
|
||||
}
|
||||
|
||||
// Send the IM to the recipient. The item is already
|
||||
|
@ -403,7 +396,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
{
|
||||
folder = new InventoryFolderBase(inventoryID, client.AgentId);
|
||||
folder = invService.GetFolder(folder);
|
||||
|
||||
|
||||
if (folder != null & trashFolder != null)
|
||||
{
|
||||
previousParentFolderID = folder.ParentID;
|
||||
|
@ -454,70 +447,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
|
|||
}
|
||||
}
|
||||
|
||||
// public bool NeedSceneCacheClear(UUID agentID, Scene scene)
|
||||
// {
|
||||
// if (!m_AgentRegions.ContainsKey(agentID))
|
||||
// {
|
||||
// // Since we can get here two ways, we need to scan
|
||||
// // the scenes here. This is somewhat more expensive
|
||||
// // but helps avoid a nasty bug
|
||||
// //
|
||||
//
|
||||
// foreach (Scene s in m_Scenelist)
|
||||
// {
|
||||
// ScenePresence presence;
|
||||
//
|
||||
// if (s.TryGetScenePresence(agentID, out presence))
|
||||
// {
|
||||
// // If the agent is in this scene, then we
|
||||
// // are being called twice in a single
|
||||
// // teleport. This is wasteful of cycles
|
||||
// // but harmless due to this 2nd level check
|
||||
// //
|
||||
// // If the agent is found in another scene
|
||||
// // then the list wasn't current
|
||||
// //
|
||||
// // If the agent is totally unknown, then what
|
||||
// // are we even doing here??
|
||||
// //
|
||||
// if (s == scene)
|
||||
// {
|
||||
// //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName);
|
||||
// return true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // The agent is left in current Scene, so we must be
|
||||
// // going to another instance
|
||||
// //
|
||||
// if (m_AgentRegions[agentID] == scene)
|
||||
// {
|
||||
// //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName);
|
||||
// m_AgentRegions.Remove(agentID);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // Another region has claimed the agent
|
||||
// //
|
||||
// //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// public void ClientLoggedOut(UUID agentID, Scene scene)
|
||||
// {
|
||||
// if (m_AgentRegions.ContainsKey(agentID))
|
||||
// m_AgentRegions.Remove(agentID);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using log4net.Config;
|
||||
using Nini.Config;
|
||||
using NUnit.Framework;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Assets;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.CoreModules.Avatar.Inventory.Transfer;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class InventoryTransferModuleTests : OpenSimTestCase
|
||||
{
|
||||
protected TestScene m_scene;
|
||||
|
||||
[SetUp]
|
||||
public override void SetUp()
|
||||
{
|
||||
base.SetUp();
|
||||
|
||||
IConfigSource config = new IniConfigSource();
|
||||
config.AddConfig("Messaging");
|
||||
config.Configs["Messaging"].Set("InventoryTransferModule", "InventoryTransferModule");
|
||||
|
||||
m_scene = new SceneHelpers().SetupScene();
|
||||
SceneHelpers.SetupSceneModules(m_scene, config, new InventoryTransferModule());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAcceptGivenItem()
|
||||
{
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
UUID initialSessionId = TestHelpers.ParseTail(0x10);
|
||||
UUID itemId = TestHelpers.ParseTail(0x100);
|
||||
UUID assetId = TestHelpers.ParseTail(0x200);
|
||||
|
||||
UserAccount ua1
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw");
|
||||
UserAccount ua2
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw");
|
||||
|
||||
ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1);
|
||||
TestClient giverClient = (TestClient)giverSp.ControllingClient;
|
||||
|
||||
ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2);
|
||||
TestClient receiverClient = (TestClient)receiverSp.ControllingClient;
|
||||
|
||||
// Create the object to test give
|
||||
InventoryItemBase originalItem
|
||||
= UserInventoryHelpers.CreateInventoryItem(
|
||||
m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object);
|
||||
|
||||
byte[] giveImBinaryBucket = new byte[17];
|
||||
byte[] itemIdBytes = itemId.GetBytes();
|
||||
Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length);
|
||||
|
||||
GridInstantMessage giveIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
giverSp.UUID,
|
||||
giverSp.Name,
|
||||
receiverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryOffered,
|
||||
false,
|
||||
"inventory offered msg",
|
||||
initialSessionId,
|
||||
false,
|
||||
Vector3.Zero,
|
||||
giveImBinaryBucket,
|
||||
true);
|
||||
|
||||
giverClient.HandleImprovedInstantMessage(giveIm);
|
||||
|
||||
// These details might not all be correct.
|
||||
GridInstantMessage acceptIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
receiverSp.UUID,
|
||||
receiverSp.Name,
|
||||
giverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryAccepted,
|
||||
false,
|
||||
"inventory accepted msg",
|
||||
initialSessionId,
|
||||
false,
|
||||
Vector3.Zero,
|
||||
null,
|
||||
true);
|
||||
|
||||
receiverClient.HandleImprovedInstantMessage(acceptIm);
|
||||
|
||||
// Test for item remaining in the giver's inventory (here we assume a copy item)
|
||||
// TODO: Test no-copy items.
|
||||
InventoryItemBase originalItemAfterGive
|
||||
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
|
||||
|
||||
Assert.That(originalItemAfterGive, Is.Not.Null);
|
||||
Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID));
|
||||
|
||||
// Test for item successfully making it into the receiver's inventory
|
||||
InventoryItemBase receivedItem
|
||||
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Objects/givenObj");
|
||||
|
||||
Assert.That(receivedItem, Is.Not.Null);
|
||||
Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID));
|
||||
|
||||
// Test that on a delete, item still exists and is accessible for the giver.
|
||||
m_scene.InventoryService.DeleteItems(receiverSp.UUID, new List<UUID>() { receivedItem.ID });
|
||||
|
||||
InventoryItemBase originalItemAfterDelete
|
||||
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
|
||||
|
||||
Assert.That(originalItemAfterDelete, Is.Not.Null);
|
||||
|
||||
// TODO: Test scenario where giver deletes their item first.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test user rejection of a given item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A rejected item still ends up in the user's trash folder.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void TestRejectGivenItem()
|
||||
{
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
UUID initialSessionId = TestHelpers.ParseTail(0x10);
|
||||
UUID itemId = TestHelpers.ParseTail(0x100);
|
||||
UUID assetId = TestHelpers.ParseTail(0x200);
|
||||
|
||||
UserAccount ua1
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw");
|
||||
UserAccount ua2
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw");
|
||||
|
||||
ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1);
|
||||
TestClient giverClient = (TestClient)giverSp.ControllingClient;
|
||||
|
||||
ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2);
|
||||
TestClient receiverClient = (TestClient)receiverSp.ControllingClient;
|
||||
|
||||
// Create the object to test give
|
||||
InventoryItemBase originalItem
|
||||
= UserInventoryHelpers.CreateInventoryItem(
|
||||
m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object);
|
||||
|
||||
GridInstantMessage receivedIm = null;
|
||||
receiverClient.OnReceivedInstantMessage += im => receivedIm = im;
|
||||
|
||||
byte[] giveImBinaryBucket = new byte[17];
|
||||
byte[] itemIdBytes = itemId.GetBytes();
|
||||
Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length);
|
||||
|
||||
GridInstantMessage giveIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
giverSp.UUID,
|
||||
giverSp.Name,
|
||||
receiverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryOffered,
|
||||
false,
|
||||
"inventory offered msg",
|
||||
initialSessionId,
|
||||
false,
|
||||
Vector3.Zero,
|
||||
giveImBinaryBucket,
|
||||
true);
|
||||
|
||||
giverClient.HandleImprovedInstantMessage(giveIm);
|
||||
|
||||
// These details might not all be correct.
|
||||
// Session ID is now the created item ID (!)
|
||||
GridInstantMessage rejectIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
receiverSp.UUID,
|
||||
receiverSp.Name,
|
||||
giverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryDeclined,
|
||||
false,
|
||||
"inventory declined msg",
|
||||
new UUID(receivedIm.imSessionID),
|
||||
false,
|
||||
Vector3.Zero,
|
||||
null,
|
||||
true);
|
||||
|
||||
receiverClient.HandleImprovedInstantMessage(rejectIm);
|
||||
|
||||
// Test for item remaining in the giver's inventory (here we assume a copy item)
|
||||
// TODO: Test no-copy items.
|
||||
InventoryItemBase originalItemAfterGive
|
||||
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
|
||||
|
||||
Assert.That(originalItemAfterGive, Is.Not.Null);
|
||||
Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID));
|
||||
|
||||
// Test for item successfully making it into the receiver's inventory
|
||||
InventoryItemBase receivedItem
|
||||
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Trash/givenObj");
|
||||
|
||||
InventoryFolderBase trashFolder
|
||||
= m_scene.InventoryService.GetFolderForType(receiverSp.UUID, AssetType.TrashFolder);
|
||||
|
||||
Assert.That(receivedItem, Is.Not.Null);
|
||||
Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID));
|
||||
Assert.That(receivedItem.Folder, Is.EqualTo(trashFolder.ID));
|
||||
|
||||
// Test that on a delete, item still exists and is accessible for the giver.
|
||||
m_scene.InventoryService.PurgeFolder(trashFolder);
|
||||
|
||||
InventoryItemBase originalItemAfterDelete
|
||||
= UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj");
|
||||
|
||||
Assert.That(originalItemAfterDelete, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAcceptGivenFolder()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
UUID initialSessionId = TestHelpers.ParseTail(0x10);
|
||||
UUID folderId = TestHelpers.ParseTail(0x100);
|
||||
|
||||
UserAccount ua1
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw");
|
||||
UserAccount ua2
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw");
|
||||
|
||||
ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1);
|
||||
TestClient giverClient = (TestClient)giverSp.ControllingClient;
|
||||
|
||||
ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2);
|
||||
TestClient receiverClient = (TestClient)receiverSp.ControllingClient;
|
||||
|
||||
InventoryFolderBase originalFolder
|
||||
= UserInventoryHelpers.CreateInventoryFolder(
|
||||
m_scene.InventoryService, giverSp.UUID, folderId, "f1", true);
|
||||
|
||||
byte[] giveImBinaryBucket = new byte[17];
|
||||
giveImBinaryBucket[0] = (byte)AssetType.Folder;
|
||||
byte[] itemIdBytes = folderId.GetBytes();
|
||||
Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length);
|
||||
|
||||
GridInstantMessage giveIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
giverSp.UUID,
|
||||
giverSp.Name,
|
||||
receiverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryOffered,
|
||||
false,
|
||||
"inventory offered msg",
|
||||
initialSessionId,
|
||||
false,
|
||||
Vector3.Zero,
|
||||
giveImBinaryBucket,
|
||||
true);
|
||||
|
||||
giverClient.HandleImprovedInstantMessage(giveIm);
|
||||
|
||||
// These details might not all be correct.
|
||||
GridInstantMessage acceptIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
receiverSp.UUID,
|
||||
receiverSp.Name,
|
||||
giverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryAccepted,
|
||||
false,
|
||||
"inventory accepted msg",
|
||||
initialSessionId,
|
||||
false,
|
||||
Vector3.Zero,
|
||||
null,
|
||||
true);
|
||||
|
||||
receiverClient.HandleImprovedInstantMessage(acceptIm);
|
||||
|
||||
// Test for item remaining in the giver's inventory (here we assume a copy item)
|
||||
// TODO: Test no-copy items.
|
||||
InventoryFolderBase originalFolderAfterGive
|
||||
= UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1");
|
||||
|
||||
Assert.That(originalFolderAfterGive, Is.Not.Null);
|
||||
Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID));
|
||||
|
||||
// Test for item successfully making it into the receiver's inventory
|
||||
InventoryFolderBase receivedFolder
|
||||
= UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "f1");
|
||||
|
||||
Assert.That(receivedFolder, Is.Not.Null);
|
||||
Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID));
|
||||
|
||||
// Test that on a delete, item still exists and is accessible for the giver.
|
||||
m_scene.InventoryService.DeleteFolders(receiverSp.UUID, new List<UUID>() { receivedFolder.ID });
|
||||
|
||||
InventoryFolderBase originalFolderAfterDelete
|
||||
= UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1");
|
||||
|
||||
Assert.That(originalFolderAfterDelete, Is.Not.Null);
|
||||
|
||||
// TODO: Test scenario where giver deletes their item first.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test user rejection of a given item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A rejected item still ends up in the user's trash folder.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void TestRejectGivenFolder()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
UUID initialSessionId = TestHelpers.ParseTail(0x10);
|
||||
UUID folderId = TestHelpers.ParseTail(0x100);
|
||||
|
||||
UserAccount ua1
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw");
|
||||
UserAccount ua2
|
||||
= UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw");
|
||||
|
||||
ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1);
|
||||
TestClient giverClient = (TestClient)giverSp.ControllingClient;
|
||||
|
||||
ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2);
|
||||
TestClient receiverClient = (TestClient)receiverSp.ControllingClient;
|
||||
|
||||
// Create the folder to test give
|
||||
InventoryFolderBase originalFolder
|
||||
= UserInventoryHelpers.CreateInventoryFolder(
|
||||
m_scene.InventoryService, giverSp.UUID, folderId, "f1", true);
|
||||
|
||||
GridInstantMessage receivedIm = null;
|
||||
receiverClient.OnReceivedInstantMessage += im => receivedIm = im;
|
||||
|
||||
byte[] giveImBinaryBucket = new byte[17];
|
||||
giveImBinaryBucket[0] = (byte)AssetType.Folder;
|
||||
byte[] itemIdBytes = folderId.GetBytes();
|
||||
Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length);
|
||||
|
||||
GridInstantMessage giveIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
giverSp.UUID,
|
||||
giverSp.Name,
|
||||
receiverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryOffered,
|
||||
false,
|
||||
"inventory offered msg",
|
||||
initialSessionId,
|
||||
false,
|
||||
Vector3.Zero,
|
||||
giveImBinaryBucket,
|
||||
true);
|
||||
|
||||
giverClient.HandleImprovedInstantMessage(giveIm);
|
||||
|
||||
// These details might not all be correct.
|
||||
// Session ID is now the created item ID (!)
|
||||
GridInstantMessage rejectIm
|
||||
= new GridInstantMessage(
|
||||
m_scene,
|
||||
receiverSp.UUID,
|
||||
receiverSp.Name,
|
||||
giverSp.UUID,
|
||||
(byte)InstantMessageDialog.InventoryDeclined,
|
||||
false,
|
||||
"inventory declined msg",
|
||||
new UUID(receivedIm.imSessionID),
|
||||
false,
|
||||
Vector3.Zero,
|
||||
null,
|
||||
true);
|
||||
|
||||
receiverClient.HandleImprovedInstantMessage(rejectIm);
|
||||
|
||||
// Test for item remaining in the giver's inventory (here we assume a copy item)
|
||||
// TODO: Test no-copy items.
|
||||
InventoryFolderBase originalFolderAfterGive
|
||||
= UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1");
|
||||
|
||||
Assert.That(originalFolderAfterGive, Is.Not.Null);
|
||||
Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID));
|
||||
|
||||
// Test for folder successfully making it into the receiver's inventory
|
||||
InventoryFolderBase receivedFolder
|
||||
= UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "Trash/f1");
|
||||
|
||||
InventoryFolderBase trashFolder
|
||||
= m_scene.InventoryService.GetFolderForType(receiverSp.UUID, AssetType.TrashFolder);
|
||||
|
||||
Assert.That(receivedFolder, Is.Not.Null);
|
||||
Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID));
|
||||
Assert.That(receivedFolder.ParentID, Is.EqualTo(trashFolder.ID));
|
||||
|
||||
// Test that on a delete, item still exists and is accessible for the giver.
|
||||
m_scene.InventoryService.PurgeFolder(trashFolder);
|
||||
|
||||
InventoryFolderBase originalFolderAfterDelete
|
||||
= UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1");
|
||||
|
||||
Assert.That(originalFolderAfterDelete, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -240,7 +240,7 @@ namespace OpenSim.Region.CoreModules.Framework
|
|||
{
|
||||
caps.AppendFormat("** User {0}:\n", kvp.Key);
|
||||
|
||||
for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false).GetEnumerator(); kvp2.MoveNext(); )
|
||||
for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); )
|
||||
{
|
||||
Uri uri = new Uri(kvp2.Value.ToString());
|
||||
caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery);
|
||||
|
|
|
@ -97,6 +97,9 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
CollisionsLastReported = CollisionCollection;
|
||||
CollisionsLastTick = new CollisionEventUpdate();
|
||||
CollisionsLastTickStep = -1;
|
||||
|
||||
SubscribedEventsMs = 0;
|
||||
CollidingStep = 0;
|
||||
CollidingGroundStep = 0;
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
|
|||
public interface IScriptWorkItem
|
||||
{
|
||||
bool Cancel();
|
||||
void Abort();
|
||||
bool Abort();
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the work item to complete.
|
||||
|
|
|
@ -564,9 +564,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
|||
|
||||
public bool Stop(int timeout)
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}",
|
||||
// ScriptName, ItemID, PrimName, ObjectID, timeout, m_InSelfDelete, DateTime.Now.Ticks);
|
||||
if (DebugLevel >= 1)
|
||||
m_log.DebugFormat(
|
||||
"[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}",
|
||||
ScriptName, ItemID, PrimName, ObjectID, timeout, m_InSelfDelete, DateTime.Now.Ticks);
|
||||
|
||||
IScriptWorkItem workItem;
|
||||
|
||||
|
|
|
@ -483,7 +483,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
|||
/// <param name="instance"></param>
|
||||
/// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</param>
|
||||
private void HandleScriptsAction<TKey>(
|
||||
string[] cmdparams, Action<IScriptInstance> action, Func<IScriptInstance, TKey> keySelector)
|
||||
string[] cmdparams, Action<IScriptInstance> action, System.Func<IScriptInstance, TKey> keySelector)
|
||||
{
|
||||
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene))
|
||||
return;
|
||||
|
@ -1517,7 +1517,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
|||
startInfo.MaxWorkerThreads = maxThreads;
|
||||
startInfo.MinWorkerThreads = minThreads;
|
||||
startInfo.ThreadPriority = threadPriority;;
|
||||
startInfo.StackSize = stackSize;
|
||||
startInfo.MaxStackSize = stackSize;
|
||||
startInfo.StartSuspended = true;
|
||||
|
||||
m_ThreadPool = new SmartThreadPool(startInfo);
|
||||
|
|
|
@ -52,16 +52,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
|||
return wr.Cancel();
|
||||
}
|
||||
|
||||
public void Abort()
|
||||
public bool Abort()
|
||||
{
|
||||
wr.Abort();
|
||||
return wr.Cancel(true);
|
||||
}
|
||||
|
||||
public bool Wait(int t)
|
||||
{
|
||||
// We use the integer version of WaitAll because the current version of SmartThreadPool has a bug with the
|
||||
// TimeSpan version. The number of milliseconds in TimeSpan is an int64 so when STP casts it down to an
|
||||
// int (32-bit) we can end up with bad values. This occurs on Windows though curious not on Mono 2.10.8
|
||||
// int (32-bit) we can end up with bad values. This occurs on Windows though curiously not on Mono 2.10.8
|
||||
// (or very likely other versions of Mono at least up until 3.0.3).
|
||||
return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false);
|
||||
}
|
||||
|
|
|
@ -171,11 +171,6 @@ namespace OpenSim.Server.Base
|
|||
|
||||
m_console = MainConsole.Instance;
|
||||
|
||||
// Configure the appenders for log4net
|
||||
//
|
||||
OpenSimAppender consoleAppender = null;
|
||||
FileAppender fileAppender = null;
|
||||
|
||||
if (logConfig != null)
|
||||
{
|
||||
FileInfo cfg = new FileInfo(logConfig);
|
||||
|
|
|
@ -217,13 +217,38 @@ namespace OpenSim.Tests.Common
|
|||
/// </returns>
|
||||
public static InventoryFolderBase CreateInventoryFolder(
|
||||
IInventoryService inventoryService, UUID userId, string path, bool useExistingFolders)
|
||||
{
|
||||
return CreateInventoryFolder(inventoryService, userId, UUID.Random(), path, useExistingFolders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create inventory folders starting from the user's root folder.
|
||||
/// </summary>
|
||||
/// <param name="inventoryService"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="folderId"></param>
|
||||
/// <param name="path">
|
||||
/// The folders to create. Multiple folders can be specified on a path delimited by the PATH_DELIMITER
|
||||
/// </param>
|
||||
/// <param name="useExistingFolders">
|
||||
/// If true, then folders in the path which already the same name are
|
||||
/// used. This applies to the terminal folder as well.
|
||||
/// If false, then all folders in the path are created, even if there is already a folder at a particular
|
||||
/// level with the same name.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The folder created. If the path contains multiple folders then the last one created is returned.
|
||||
/// Will return null if the root folder could not be found.
|
||||
/// </returns>
|
||||
public static InventoryFolderBase CreateInventoryFolder(
|
||||
IInventoryService inventoryService, UUID userId, UUID folderId, string path, bool useExistingFolders)
|
||||
{
|
||||
InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
|
||||
|
||||
if (null == rootFolder)
|
||||
return null;
|
||||
|
||||
return CreateInventoryFolder(inventoryService, rootFolder, path, useExistingFolders);
|
||||
return CreateInventoryFolder(inventoryService, folderId, rootFolder, path, useExistingFolders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -235,6 +260,7 @@ namespace OpenSim.Tests.Common
|
|||
/// TODO: May need to make it an option to create duplicate folders.
|
||||
/// </remarks>
|
||||
/// <param name="inventoryService"></param>
|
||||
/// <param name="folderId">ID of the folder to create</param>
|
||||
/// <param name="parentFolder"></param>
|
||||
/// <param name="path">
|
||||
/// The folder to create.
|
||||
|
@ -249,7 +275,7 @@ namespace OpenSim.Tests.Common
|
|||
/// The folder created. If the path contains multiple folders then the last one created is returned.
|
||||
/// </returns>
|
||||
public static InventoryFolderBase CreateInventoryFolder(
|
||||
IInventoryService inventoryService, InventoryFolderBase parentFolder, string path, bool useExistingFolders)
|
||||
IInventoryService inventoryService, UUID folderId, InventoryFolderBase parentFolder, string path, bool useExistingFolders)
|
||||
{
|
||||
string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None);
|
||||
|
||||
|
@ -262,9 +288,16 @@ namespace OpenSim.Tests.Common
|
|||
{
|
||||
// Console.WriteLine("Creating folder {0} at {1}", components[0], parentFolder.Name);
|
||||
|
||||
UUID folderIdForCreate;
|
||||
|
||||
if (components.Length > 1)
|
||||
folderIdForCreate = UUID.Random();
|
||||
else
|
||||
folderIdForCreate = folderId;
|
||||
|
||||
folder
|
||||
= new InventoryFolderBase(
|
||||
UUID.Random(), components[0], parentFolder.Owner, (short)AssetType.Unknown, parentFolder.ID, 0);
|
||||
folderIdForCreate, components[0], parentFolder.Owner, (short)AssetType.Unknown, parentFolder.ID, 0);
|
||||
|
||||
inventoryService.AddFolder(folder);
|
||||
}
|
||||
|
@ -274,7 +307,7 @@ namespace OpenSim.Tests.Common
|
|||
// }
|
||||
|
||||
if (components.Length > 1)
|
||||
return CreateInventoryFolder(inventoryService, folder, components[1], useExistingFolders);
|
||||
return CreateInventoryFolder(inventoryService, folderId, folder, components[1], useExistingFolders);
|
||||
else
|
||||
return folder;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace OpenSim.Tests.Common.Mock
|
|||
// Test client specific events - for use by tests to implement some IClientAPI behaviour.
|
||||
public event Action<RegionInfo, Vector3, Vector3> OnReceivedMoveAgentIntoRegion;
|
||||
public event Action<ulong, IPEndPoint> OnTestClientInformClientOfNeighbour;
|
||||
public event Action<GridInstantMessage> OnReceivedInstantMessage;
|
||||
|
||||
// disable warning: public events, part of the public API
|
||||
#pragma warning disable 67
|
||||
|
@ -484,6 +485,18 @@ namespace OpenSim.Tests.Common.Mock
|
|||
OnCompleteMovementToRegion(this, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emulate sending an IM from the viewer to the simulator.
|
||||
/// </summary>
|
||||
/// <param name='im'></param>
|
||||
public void HandleImprovedInstantMessage(GridInstantMessage im)
|
||||
{
|
||||
ImprovedInstantMessage handlerInstantMessage = OnInstantMessage;
|
||||
|
||||
if (handlerInstantMessage != null)
|
||||
handlerInstantMessage(this, im);
|
||||
}
|
||||
|
||||
public virtual void ActivateGesture(UUID assetId, UUID gestureId)
|
||||
{
|
||||
}
|
||||
|
@ -538,7 +551,8 @@ namespace OpenSim.Tests.Common.Mock
|
|||
|
||||
public void SendInstantMessage(GridInstantMessage im)
|
||||
{
|
||||
|
||||
if (OnReceivedInstantMessage != null)
|
||||
OnReceivedInstantMessage(im);
|
||||
}
|
||||
|
||||
public void SendGenericMessage(string method, UUID invoice, List<string> message)
|
||||
|
|
|
@ -53,6 +53,9 @@ namespace OpenSim.Tests.Common.Mock
|
|||
|
||||
public XInventoryFolder[] GetFolders(string[] fields, string[] vals)
|
||||
{
|
||||
// Console.WriteLine(
|
||||
// "Requesting folders, fields {0}, vals {1}", string.Join(",", fields), string.Join(",", vals));
|
||||
|
||||
List<XInventoryFolder> origFolders
|
||||
= Get<XInventoryFolder>(fields, vals, m_allFolders.Values.ToList());
|
||||
|
||||
|
@ -104,7 +107,30 @@ namespace OpenSim.Tests.Common.Mock
|
|||
}
|
||||
|
||||
public bool MoveItem(string id, string newParent) { throw new NotImplementedException(); }
|
||||
public bool MoveFolder(string id, string newParent) { throw new NotImplementedException(); }
|
||||
|
||||
public bool MoveFolder(string id, string newParent)
|
||||
{
|
||||
// Don't use GetFolders() here - it takes a clone!
|
||||
XInventoryFolder folder = m_allFolders[new UUID(id)];
|
||||
|
||||
if (folder == null)
|
||||
return false;
|
||||
|
||||
folder.parentFolderID = new UUID(newParent);
|
||||
|
||||
XInventoryFolder[] newParentFolders
|
||||
= GetFolders(new string[] { "folderID" }, new string[] { folder.parentFolderID.ToString() });
|
||||
|
||||
// Console.WriteLine(
|
||||
// "Moved folder {0} {1}, to {2} {3}",
|
||||
// folder.folderName, folder.folderID, newParentFolders[0].folderName, folder.parentFolderID);
|
||||
|
||||
// TODO: Really need to implement folder version incrementing, though this should be common code anyway,
|
||||
// not reimplemented in each db plugin.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public XInventoryItem[] GetActiveGestures(UUID principalID) { throw new NotImplementedException(); }
|
||||
public int GetAssetPermissions(UUID principalID, UUID assetID) { throw new NotImplementedException(); }
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
//
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
//
|
||||
[assembly: AssemblyTitle("")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
//
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("0.7.6.*")]
|
||||
|
||||
//
|
||||
// In order to sign your assembly you must specify a key to use. Refer to the
|
||||
// Microsoft .NET Framework documentation for more information on assembly signing.
|
||||
//
|
||||
// Use the attributes below to control which key is used for signing.
|
||||
//
|
||||
// Notes:
|
||||
// (*) If no key is specified, the assembly is not signed.
|
||||
// (*) KeyName refers to a key that has been installed in the Crypto Service
|
||||
// Provider (CSP) on your machine. KeyFile refers to a file which contains
|
||||
// a key.
|
||||
// (*) If the KeyFile and the KeyName values are both specified, the
|
||||
// following processing occurs:
|
||||
// (1) If the KeyName can be found in the CSP, that key is used.
|
||||
// (2) If the KeyName does not exist and the KeyFile does exist, the key
|
||||
// in the KeyFile is installed into the CSP and used.
|
||||
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
|
||||
// When specifying the KeyFile, the location of the KeyFile should be
|
||||
// relative to the project output directory which is
|
||||
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
|
||||
// located in the project directory, you would specify the AssemblyKeyFile
|
||||
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
|
||||
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
|
||||
// documentation for more information on this.
|
||||
//
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
[assembly: AssemblyKeyName("")]
|
|
@ -1,223 +1,138 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region CallerThreadContext class
|
||||
|
||||
/// <summary>
|
||||
/// This class stores the caller call context in order to restore
|
||||
/// it when the work item is executed in the thread pool environment.
|
||||
/// </summary>
|
||||
internal class CallerThreadContext
|
||||
{
|
||||
#region Prepare reflection information
|
||||
|
||||
// Cached type information.
|
||||
private static MethodInfo getLogicalCallContextMethodInfo =
|
||||
typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
private static MethodInfo setLogicalCallContextMethodInfo =
|
||||
typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
private static string HttpContextSlotName = GetHttpContextSlotName();
|
||||
|
||||
private static string GetHttpContextSlotName()
|
||||
{
|
||||
FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
if( fi != null )
|
||||
return (string)fi.GetValue(null);
|
||||
else // Use the default "HttpContext" slot name
|
||||
return "HttpContext";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
|
||||
private HttpContext _httpContext = null;
|
||||
private LogicalCallContext _callContext = null;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
private CallerThreadContext()
|
||||
{
|
||||
}
|
||||
|
||||
public bool CapturedCallContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return (null != _callContext);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CapturedHttpContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return (null != _httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the current thread context
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static CallerThreadContext Capture(
|
||||
bool captureCallContext,
|
||||
bool captureHttpContext)
|
||||
{
|
||||
Debug.Assert(captureCallContext || captureHttpContext);
|
||||
|
||||
CallerThreadContext callerThreadContext = new CallerThreadContext();
|
||||
|
||||
// TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture()
|
||||
// Capture Call Context
|
||||
if(captureCallContext && (getLogicalCallContextMethodInfo != null))
|
||||
{
|
||||
callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null);
|
||||
if (callerThreadContext._callContext != null)
|
||||
{
|
||||
callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Capture httpContext
|
||||
if (captureHttpContext && (null != HttpContext.Current))
|
||||
{
|
||||
callerThreadContext._httpContext = HttpContext.Current;
|
||||
}
|
||||
|
||||
return callerThreadContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the thread context stored earlier
|
||||
/// </summary>
|
||||
/// <param name="callerThreadContext"></param>
|
||||
public static void Apply(CallerThreadContext callerThreadContext)
|
||||
{
|
||||
if (null == callerThreadContext)
|
||||
{
|
||||
throw new ArgumentNullException("callerThreadContext");
|
||||
}
|
||||
|
||||
// Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run()
|
||||
// Restore call context
|
||||
if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null))
|
||||
{
|
||||
setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
|
||||
}
|
||||
|
||||
// Restore HttpContext
|
||||
if (callerThreadContext._httpContext != null)
|
||||
{
|
||||
CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Globalization;
|
||||
using System.Security.Principal;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Remoting.Contexts;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region CallerThreadContext class
|
||||
|
||||
/// <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
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region CallerThreadContext class
|
||||
|
||||
/// <summary>
|
||||
/// This class stores the caller call context in order to restore
|
||||
/// it when the work item is executed in the thread pool environment.
|
||||
/// </summary>
|
||||
internal class CallerThreadContext
|
||||
{
|
||||
#region Prepare reflection information
|
||||
|
||||
// Cached type information.
|
||||
private static readonly MethodInfo getLogicalCallContextMethodInfo =
|
||||
typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
private static readonly MethodInfo setLogicalCallContextMethodInfo =
|
||||
typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
private static string HttpContextSlotName = GetHttpContextSlotName();
|
||||
|
||||
private static string GetHttpContextSlotName()
|
||||
{
|
||||
FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
if (fi != null)
|
||||
{
|
||||
return (string) fi.GetValue(null);
|
||||
}
|
||||
|
||||
return "HttpContext";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
|
||||
private HttpContext _httpContext;
|
||||
private LogicalCallContext _callContext;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
private CallerThreadContext()
|
||||
{
|
||||
}
|
||||
|
||||
public bool CapturedCallContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return (null != _callContext);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CapturedHttpContext
|
||||
{
|
||||
get
|
||||
{
|
||||
return (null != _httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the current thread context
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static CallerThreadContext Capture(
|
||||
bool captureCallContext,
|
||||
bool captureHttpContext)
|
||||
{
|
||||
Debug.Assert(captureCallContext || captureHttpContext);
|
||||
|
||||
CallerThreadContext callerThreadContext = new CallerThreadContext();
|
||||
|
||||
// TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture()
|
||||
// Capture Call Context
|
||||
if(captureCallContext && (getLogicalCallContextMethodInfo != null))
|
||||
{
|
||||
callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null);
|
||||
if (callerThreadContext._callContext != null)
|
||||
{
|
||||
callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Capture httpContext
|
||||
if (captureHttpContext && (null != HttpContext.Current))
|
||||
{
|
||||
callerThreadContext._httpContext = HttpContext.Current;
|
||||
}
|
||||
|
||||
return callerThreadContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the thread context stored earlier
|
||||
/// </summary>
|
||||
/// <param name="callerThreadContext"></param>
|
||||
public static void Apply(CallerThreadContext callerThreadContext)
|
||||
{
|
||||
if (null == callerThreadContext)
|
||||
{
|
||||
throw new ArgumentNullException("callerThreadContext");
|
||||
}
|
||||
|
||||
// Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run()
|
||||
// Restore call context
|
||||
if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null))
|
||||
{
|
||||
setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
|
||||
}
|
||||
|
||||
// Restore HttpContext
|
||||
if (callerThreadContext._httpContext != null)
|
||||
{
|
||||
HttpContext.Current = callerThreadContext._httpContext;
|
||||
//CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
namespace Amib.Threading.Internal
|
||||
{
|
||||
internal class CanceledWorkItemsGroup
|
||||
{
|
||||
public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup();
|
||||
|
||||
public CanceledWorkItemsGroup()
|
||||
{
|
||||
IsCanceled = false;
|
||||
}
|
||||
|
||||
public bool IsCanceled { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
#if (_WINDOWS_CE)
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// EventWaitHandle class
|
||||
/// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation.
|
||||
/// So I wrote this class to implement these two methods with some of their overloads.
|
||||
/// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny.
|
||||
/// Note that this class doesn't even inherit from WaitHandle!
|
||||
/// </summary>
|
||||
public class STPEventWaitHandle
|
||||
{
|
||||
#region Public Constants
|
||||
|
||||
public const int WaitTimeout = Timeout.Infinite;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private External Constants
|
||||
|
||||
private const Int32 WAIT_FAILED = -1;
|
||||
private const Int32 WAIT_TIMEOUT = 0x102;
|
||||
private const UInt32 INFINITE = 0xFFFFFFFF;
|
||||
|
||||
#endregion
|
||||
|
||||
#region WaitAll and WaitAny
|
||||
|
||||
internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return waitHandle.WaitOne(millisecondsTimeout, exitContext);
|
||||
}
|
||||
|
||||
private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles)
|
||||
{
|
||||
IntPtr[] nativeHandles = new IntPtr[waitHandles.Length];
|
||||
for (int i = 0; i < waitHandles.Length; i++)
|
||||
{
|
||||
nativeHandles[i] = waitHandles[i].Handle;
|
||||
}
|
||||
return nativeHandles;
|
||||
}
|
||||
|
||||
public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;
|
||||
|
||||
IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);
|
||||
|
||||
int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout);
|
||||
|
||||
if (result == WAIT_TIMEOUT || result == WAIT_FAILED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;
|
||||
|
||||
IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);
|
||||
|
||||
int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout);
|
||||
|
||||
if (result >= 0 && result < waitHandles.Length)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int WaitAny(WaitHandle[] waitHandles)
|
||||
{
|
||||
return WaitAny(waitHandles, Timeout.Infinite, false);
|
||||
}
|
||||
|
||||
public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext)
|
||||
{
|
||||
int millisecondsTimeout = (int)timeout.TotalMilliseconds;
|
||||
|
||||
return WaitAny(waitHandles, millisecondsTimeout, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region External methods
|
||||
|
||||
[DllImport("coredll.dll", SetLastError = true)]
|
||||
public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,82 @@
|
|||
using System.Threading;
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// EventWaitHandleFactory class.
|
||||
/// This is a static class that creates AutoResetEvent and ManualResetEvent objects.
|
||||
/// In WindowCE the WaitForMultipleObjects API fails to use the Handle property
|
||||
/// of XxxResetEvent. It can use only handles that were created by the CreateEvent API.
|
||||
/// Consequently this class creates the needed XxxResetEvent and replaces the handle if
|
||||
/// it's a WindowsCE OS.
|
||||
/// </summary>
|
||||
public static class EventWaitHandleFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new AutoResetEvent object
|
||||
/// </summary>
|
||||
/// <returns>Return a new AutoResetEvent object</returns>
|
||||
public static AutoResetEvent CreateAutoResetEvent()
|
||||
{
|
||||
AutoResetEvent waitHandle = new AutoResetEvent(false);
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
ReplaceEventHandle(waitHandle, false, false);
|
||||
#endif
|
||||
|
||||
return waitHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new ManualResetEvent object
|
||||
/// </summary>
|
||||
/// <returns>Return a new ManualResetEvent object</returns>
|
||||
public static ManualResetEvent CreateManualResetEvent(bool initialState)
|
||||
{
|
||||
ManualResetEvent waitHandle = new ManualResetEvent(initialState);
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
ReplaceEventHandle(waitHandle, true, initialState);
|
||||
#endif
|
||||
|
||||
return waitHandle;
|
||||
}
|
||||
|
||||
#if (_WINDOWS_CE)
|
||||
|
||||
/// <summary>
|
||||
/// Replace the event handle
|
||||
/// </summary>
|
||||
/// <param name="waitHandle">The WaitHandle object which its handle needs to be replaced.</param>
|
||||
/// <param name="manualReset">Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false)</param>
|
||||
/// <param name="initialState">The initial state of the event</param>
|
||||
private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState)
|
||||
{
|
||||
// Store the old handle
|
||||
IntPtr oldHandle = waitHandle.Handle;
|
||||
|
||||
// Create a new event
|
||||
IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null);
|
||||
|
||||
// Replace the old event with the new event
|
||||
waitHandle.Handle = newHandle;
|
||||
|
||||
// Close the old event
|
||||
CloseHandle (oldHandle);
|
||||
}
|
||||
|
||||
[DllImport("coredll.dll", SetLastError = true)]
|
||||
public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
|
||||
|
||||
//Handle
|
||||
[DllImport("coredll.dll", SetLastError = true)]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
|
@ -1,81 +1,111 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region Exceptions
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been canceled
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class WorkItemCancelException : ApplicationException
|
||||
{
|
||||
public WorkItemCancelException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message, Exception e) : base(message, e)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class WorkItemTimeoutException : ApplicationException
|
||||
{
|
||||
public WorkItemTimeoutException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemTimeoutException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemTimeoutException(string message, Exception e) : base(message, e)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class WorkItemResultException : ApplicationException
|
||||
{
|
||||
public WorkItemResultException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemResultException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemResultException(string message, Exception e) : base(message, e)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
using System;
|
||||
#if !(_WINDOWS_CE)
|
||||
using System.Runtime.Serialization;
|
||||
#endif
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region Exceptions
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been canceled
|
||||
/// </summary>
|
||||
public sealed partial class WorkItemCancelException : Exception
|
||||
{
|
||||
public WorkItemCancelException()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemCancelException(string message, Exception e)
|
||||
: base(message, e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
public sealed partial class WorkItemTimeoutException : Exception
|
||||
{
|
||||
public WorkItemTimeoutException()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemTimeoutException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemTimeoutException(string message, Exception e)
|
||||
: base(message, e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
public sealed partial class WorkItemResultException : Exception
|
||||
{
|
||||
public WorkItemResultException()
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemResultException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WorkItemResultException(string message, Exception e)
|
||||
: base(message, e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been canceled
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed partial class WorkItemCancelException
|
||||
{
|
||||
public WorkItemCancelException(SerializationInfo si, StreamingContext sc)
|
||||
: base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed partial class WorkItemTimeoutException
|
||||
{
|
||||
public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc)
|
||||
: base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed partial class WorkItemResultException
|
||||
{
|
||||
public WorkItemResultException(SerializationInfo si, StreamingContext sc)
|
||||
: base(si, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -1,271 +1,628 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region Delegates
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that represents the method to run as the work item
|
||||
/// </summary>
|
||||
/// <param name="state">A state object for the method to run</param>
|
||||
public delegate object WorkItemCallback(object state);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call after the WorkItemCallback completed
|
||||
/// </summary>
|
||||
/// <param name="wir">The work item result object</param>
|
||||
public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call when a WorkItemsGroup becomes idle
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
|
||||
public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
|
||||
|
||||
#endregion
|
||||
|
||||
#region WorkItem Priority
|
||||
|
||||
public enum WorkItemPriority
|
||||
{
|
||||
Lowest,
|
||||
BelowNormal,
|
||||
Normal,
|
||||
AboveNormal,
|
||||
Highest,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IHasWorkItemPriority interface
|
||||
|
||||
public interface IHasWorkItemPriority
|
||||
{
|
||||
WorkItemPriority WorkItemPriority { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorkItemsGroup interface
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemsGroup interface
|
||||
/// </summary>
|
||||
public interface IWorkItemsGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// Get/Set the name of the WorkItemsGroup
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback);
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
|
||||
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
|
||||
|
||||
void WaitForIdle();
|
||||
bool WaitForIdle(TimeSpan timeout);
|
||||
bool WaitForIdle(int millisecondsTimeout);
|
||||
|
||||
int WaitingCallbacks { get; }
|
||||
event WorkItemsGroupIdleHandler OnIdle;
|
||||
|
||||
void Cancel();
|
||||
void Start();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CallToPostExecute enumerator
|
||||
|
||||
[Flags]
|
||||
public enum CallToPostExecute
|
||||
{
|
||||
Never = 0x00,
|
||||
WhenWorkItemCanceled = 0x01,
|
||||
WhenWorkItemNotCanceled = 0x02,
|
||||
Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorkItemResult interface
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemResult interface
|
||||
/// </summary>
|
||||
public interface IWorkItemResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
object GetResult();
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext);
|
||||
|
||||
void Abort();
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
|
||||
/// <param name="exitContext">
|
||||
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
|
||||
/// </param>
|
||||
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits.
|
||||
/// </summary>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
object GetResult(out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
object GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
|
||||
/// <param name="exitContext">
|
||||
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
|
||||
/// </param>
|
||||
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
object GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication whether the asynchronous operation has completed.
|
||||
/// </summary>
|
||||
bool IsCompleted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication whether the asynchronous operation has been canceled.
|
||||
/// </summary>
|
||||
bool IsCanceled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
|
||||
/// </summary>
|
||||
object State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the work item if it didn't start running yet.
|
||||
/// </summary>
|
||||
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
|
||||
bool Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Get the work item's priority
|
||||
/// </summary>
|
||||
WorkItemPriority WorkItemPriority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the result, same as GetResult()
|
||||
/// </summary>
|
||||
object Result { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception if occured otherwise returns null.
|
||||
/// </summary>
|
||||
object Exception { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region Delegates
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that represents the method to run as the work item
|
||||
/// </summary>
|
||||
/// <param name="state">A state object for the method to run</param>
|
||||
public delegate object WorkItemCallback(object state);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call after the WorkItemCallback completed
|
||||
/// </summary>
|
||||
/// <param name="wir">The work item result object</param>
|
||||
public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call after the WorkItemCallback completed
|
||||
/// </summary>
|
||||
/// <param name="wir">The work item result object</param>
|
||||
public delegate void PostExecuteWorkItemCallback<TResult>(IWorkItemResult<TResult> wir);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call when a WorkItemsGroup becomes idle
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
|
||||
public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call after a thread is created, but before
|
||||
/// it's first use.
|
||||
/// </summary>
|
||||
public delegate void ThreadInitializationHandler();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to call when a thread is about to exit, after
|
||||
/// it is no longer belong to the pool.
|
||||
/// </summary>
|
||||
public delegate void ThreadTerminationHandler();
|
||||
|
||||
#endregion
|
||||
|
||||
#region WorkItem Priority
|
||||
|
||||
/// <summary>
|
||||
/// Defines the availeable priorities of a work item.
|
||||
/// The higher the priority a work item has, the sooner
|
||||
/// it will be executed.
|
||||
/// </summary>
|
||||
public enum WorkItemPriority
|
||||
{
|
||||
Lowest,
|
||||
BelowNormal,
|
||||
Normal,
|
||||
AboveNormal,
|
||||
Highest,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorkItemsGroup interface
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemsGroup interface
|
||||
/// Created by SmartThreadPool.CreateWorkItemsGroup()
|
||||
/// </summary>
|
||||
public interface IWorkItemsGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// Get/Set the name of the WorkItemsGroup
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
|
||||
/// </summary>
|
||||
int Concurrency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of work items waiting in the queue.
|
||||
/// </summary>
|
||||
int WaitingCallbacks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get an array with all the state objects of the currently running items.
|
||||
/// The array represents a snap shot and impact performance.
|
||||
/// </summary>
|
||||
object[] GetStates();
|
||||
|
||||
/// <summary>
|
||||
/// Get the WorkItemsGroup start information
|
||||
/// </summary>
|
||||
WIGStartInfo WIGStartInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts to execute work items
|
||||
/// </summary>
|
||||
void Start();
|
||||
|
||||
/// <summary>
|
||||
/// Cancel all the work items.
|
||||
/// Same as Cancel(false)
|
||||
/// </summary>
|
||||
void Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Cancel all work items using thread abortion
|
||||
/// </summary>
|
||||
/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
|
||||
void Cancel(bool abortExecution);
|
||||
|
||||
/// <summary>
|
||||
/// Wait for all work item to complete.
|
||||
/// </summary>
|
||||
void WaitForIdle();
|
||||
|
||||
/// <summary>
|
||||
/// Wait for all work item to complete, until timeout expired
|
||||
/// </summary>
|
||||
/// <param name="timeout">How long to wait for the work items to complete</param>
|
||||
/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
|
||||
bool WaitForIdle(TimeSpan timeout);
|
||||
|
||||
/// <summary>
|
||||
/// Wait for all work item to complete, until timeout expired
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout">How long to wait for the work items to complete in milliseconds</param>
|
||||
/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
|
||||
bool WaitForIdle(int millisecondsTimeout);
|
||||
|
||||
/// <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;
|
||||
|
||||
#region QueueWorkItem
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="workItemPriority">The priority of the work item</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item info</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item
|
||||
/// </summary>
|
||||
/// <param name="workItemInfo">Work item information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item result</returns>
|
||||
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
|
||||
|
||||
#endregion
|
||||
|
||||
#region QueueWorkItem(Action<...>)
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem(Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
|
||||
IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);
|
||||
|
||||
#endregion
|
||||
|
||||
#region QueueWorkItem(Func<...>)
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<TResult>(Func<TResult> func);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> func, T arg);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 arg1, T2 arg2);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns a IWorkItemResult<TResult> object.
|
||||
/// its GetResult() returns a TResult object</returns>
|
||||
IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CallToPostExecute enumerator
|
||||
|
||||
[Flags]
|
||||
public enum CallToPostExecute
|
||||
{
|
||||
/// <summary>
|
||||
/// Never call to the PostExecute call back
|
||||
/// </summary>
|
||||
Never = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// Call to the PostExecute only when the work item is cancelled
|
||||
/// </summary>
|
||||
WhenWorkItemCanceled = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Call to the PostExecute only when the work item is not cancelled
|
||||
/// </summary>
|
||||
WhenWorkItemNotCanceled = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Always call to the PostExecute
|
||||
/// </summary>
|
||||
Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorkItemResult interface
|
||||
|
||||
/// <summary>
|
||||
/// The common interface of IWorkItemResult and IWorkItemResult<T>
|
||||
/// </summary>
|
||||
public interface IWaitableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// This method intent is for internal use.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IWorkItemResult GetWorkItemResult();
|
||||
|
||||
/// <summary>
|
||||
/// This method intent is for internal use.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IWorkItemResult<TResult> GetWorkItemResultT<TResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemResult interface.
|
||||
/// Created when a WorkItemCallback work item is queued.
|
||||
/// </summary>
|
||||
public interface IWorkItemResult : IWorkItemResult<object>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IWorkItemResult<TResult> interface.
|
||||
/// Created when a Func<TResult> work item is queued.
|
||||
/// </summary>
|
||||
public interface IWorkItemResult<TResult> : IWaitableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
TResult GetResult();
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
|
||||
/// <param name="exitContext">
|
||||
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
|
||||
/// </param>
|
||||
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits.
|
||||
/// </summary>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
TResult GetResult(out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout"></param>
|
||||
/// <param name="exitContext"></param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout.
|
||||
/// </summary>
|
||||
/// <param name="exitContext"></param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
|
||||
/// <param name="exitContext">
|
||||
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
|
||||
/// </param>
|
||||
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
TResult GetResult(
|
||||
int millisecondsTimeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of the work item.
|
||||
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
|
||||
/// </summary>
|
||||
/// <returns>The result of the work item</returns>
|
||||
/// <param name="cancelWaitHandle"></param>
|
||||
/// <param name="e">Filled with the exception if one was thrown</param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="exitContext"></param>
|
||||
/// On timeout throws WorkItemTimeoutException
|
||||
/// On cancel throws WorkItemCancelException
|
||||
TResult GetResult(
|
||||
TimeSpan timeout,
|
||||
bool exitContext,
|
||||
WaitHandle cancelWaitHandle,
|
||||
out Exception e);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication whether the asynchronous operation has completed.
|
||||
/// </summary>
|
||||
bool IsCompleted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication whether the asynchronous operation has been canceled.
|
||||
/// </summary>
|
||||
bool IsCanceled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user-defined object that contains context data
|
||||
/// for the work item method.
|
||||
/// </summary>
|
||||
object State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Same as Cancel(false).
|
||||
/// </summary>
|
||||
bool Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the work item execution.
|
||||
/// If the work item is in the queue then it won't execute
|
||||
/// If the work item is completed, it will remain completed
|
||||
/// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled
|
||||
/// property to check if the work item has been cancelled. If the abortExecution is set to true then
|
||||
/// the Smart Thread Pool will send an AbortException to the running thread to stop the execution
|
||||
/// of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException.
|
||||
/// If the work item is already cancelled it will remain cancelled
|
||||
/// </summary>
|
||||
/// <param name="abortExecution">When true send an AbortException to the executing thread.</param>
|
||||
/// <returns>Returns true if the work item was not completed, otherwise false.</returns>
|
||||
bool Cancel(bool abortExecution);
|
||||
|
||||
/// <summary>
|
||||
/// Get the work item's priority
|
||||
/// </summary>
|
||||
WorkItemPriority WorkItemPriority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the result, same as GetResult()
|
||||
/// </summary>
|
||||
TResult Result { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception if occured otherwise returns null.
|
||||
/// </summary>
|
||||
object Exception { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region .NET 3.5
|
||||
|
||||
// All these delegate are built-in .NET 3.5
|
||||
// Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity.
|
||||
|
||||
public delegate void Action();
|
||||
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
|
||||
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
|
||||
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
public delegate TResult Func<TResult>();
|
||||
public delegate TResult Func<T, TResult>(T arg1);
|
||||
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
|
||||
public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
|
||||
public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// An internal delegate to call when the WorkItem starts or completes
|
||||
/// </summary>
|
||||
internal delegate void WorkItemStateCallback(WorkItem workItem);
|
||||
|
||||
internal interface IInternalWorkItemResult
|
||||
{
|
||||
event WorkItemStateCallback OnWorkItemStarted;
|
||||
event WorkItemStateCallback OnWorkItemCompleted;
|
||||
}
|
||||
|
||||
internal interface IInternalWaitableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// This method is intent for internal use.
|
||||
/// </summary>
|
||||
IWorkItemResult GetWorkItemResult();
|
||||
}
|
||||
|
||||
public interface IHasWorkItemPriority
|
||||
{
|
||||
WorkItemPriority WorkItemPriority { get; }
|
||||
}
|
||||
}
|
|
@ -1,240 +1,239 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region PriorityQueue class
|
||||
|
||||
/// <summary>
|
||||
/// PriorityQueue class
|
||||
/// This class is not thread safe because we use external lock
|
||||
/// </summary>
|
||||
public sealed class PriorityQueue : IEnumerable
|
||||
{
|
||||
#region Private members
|
||||
|
||||
/// <summary>
|
||||
/// The number of queues, there is one for each type of priority
|
||||
/// </summary>
|
||||
private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1;
|
||||
|
||||
/// <summary>
|
||||
/// Work items queues. There is one for each type of priority
|
||||
/// </summary>
|
||||
private Queue [] _queues = new Queue[_queuesCount];
|
||||
|
||||
/// <summary>
|
||||
/// The total number of work items within the queues
|
||||
/// </summary>
|
||||
private int _workItemsCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Use with IEnumerable interface
|
||||
/// </summary>
|
||||
private int _version = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Contructor
|
||||
|
||||
public PriorityQueue()
|
||||
{
|
||||
for(int i = 0; i < _queues.Length; ++i)
|
||||
{
|
||||
_queues[i] = new Queue();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue a work item.
|
||||
/// </summary>
|
||||
/// <param name="workItem">A work item</param>
|
||||
public void Enqueue(IHasWorkItemPriority workItem)
|
||||
{
|
||||
Debug.Assert(null != workItem);
|
||||
|
||||
int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1;
|
||||
Debug.Assert(queueIndex >= 0);
|
||||
Debug.Assert(queueIndex < _queuesCount);
|
||||
|
||||
_queues[queueIndex].Enqueue(workItem);
|
||||
++_workItemsCount;
|
||||
++_version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeque a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns the next work item</returns>
|
||||
public IHasWorkItemPriority Dequeue()
|
||||
{
|
||||
IHasWorkItemPriority workItem = null;
|
||||
|
||||
if(_workItemsCount > 0)
|
||||
{
|
||||
int queueIndex = GetNextNonEmptyQueue(-1);
|
||||
Debug.Assert(queueIndex >= 0);
|
||||
workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority;
|
||||
Debug.Assert(null != workItem);
|
||||
--_workItemsCount;
|
||||
++_version;
|
||||
}
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the next non empty queue starting at queue queueIndex+1
|
||||
/// </summary>
|
||||
/// <param name="queueIndex">The index-1 to start from</param>
|
||||
/// <returns>
|
||||
/// The index of the next non empty queue or -1 if all the queues are empty
|
||||
/// </returns>
|
||||
private int GetNextNonEmptyQueue(int queueIndex)
|
||||
{
|
||||
for(int i = queueIndex+1; i < _queuesCount; ++i)
|
||||
{
|
||||
if(_queues[i].Count > 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of work items
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workItemsCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all the work items
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
if (_workItemsCount > 0)
|
||||
{
|
||||
foreach(Queue queue in _queues)
|
||||
{
|
||||
queue.Clear();
|
||||
}
|
||||
_workItemsCount = 0;
|
||||
++_version;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator to iterate over the work items
|
||||
/// </summary>
|
||||
/// <returns>Returns an enumerator</returns>
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return new PriorityQueueEnumerator(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PriorityQueueEnumerator
|
||||
|
||||
/// <summary>
|
||||
/// The class the implements the enumerator
|
||||
/// </summary>
|
||||
private class PriorityQueueEnumerator : IEnumerator
|
||||
{
|
||||
private PriorityQueue _priorityQueue;
|
||||
private int _version;
|
||||
private int _queueIndex;
|
||||
private IEnumerator _enumerator;
|
||||
|
||||
public PriorityQueueEnumerator(PriorityQueue priorityQueue)
|
||||
{
|
||||
_priorityQueue = priorityQueue;
|
||||
_version = _priorityQueue._version;
|
||||
_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
|
||||
if (_queueIndex >= 0)
|
||||
{
|
||||
_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
|
||||
}
|
||||
else
|
||||
{
|
||||
_enumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region IEnumerator Members
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_version = _priorityQueue._version;
|
||||
_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
|
||||
if (_queueIndex >= 0)
|
||||
{
|
||||
_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
|
||||
}
|
||||
else
|
||||
{
|
||||
_enumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
public object Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(null != _enumerator);
|
||||
return _enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (null == _enumerator)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_version != _priorityQueue._version)
|
||||
{
|
||||
throw new InvalidOperationException("The collection has been modified");
|
||||
|
||||
}
|
||||
if (!_enumerator.MoveNext())
|
||||
{
|
||||
_queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex);
|
||||
if(-1 == _queueIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
|
||||
_enumerator.MoveNext();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region PriorityQueue class
|
||||
|
||||
/// <summary>
|
||||
/// PriorityQueue class
|
||||
/// This class is not thread safe because we use external lock
|
||||
/// </summary>
|
||||
public sealed class PriorityQueue : IEnumerable
|
||||
{
|
||||
#region Private members
|
||||
|
||||
/// <summary>
|
||||
/// The number of queues, there is one for each type of priority
|
||||
/// </summary>
|
||||
private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1;
|
||||
|
||||
/// <summary>
|
||||
/// Work items queues. There is one for each type of priority
|
||||
/// </summary>
|
||||
private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];
|
||||
|
||||
/// <summary>
|
||||
/// The total number of work items within the queues
|
||||
/// </summary>
|
||||
private int _workItemsCount;
|
||||
|
||||
/// <summary>
|
||||
/// Use with IEnumerable interface
|
||||
/// </summary>
|
||||
private int _version;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Contructor
|
||||
|
||||
public PriorityQueue()
|
||||
{
|
||||
for(int i = 0; i < _queues.Length; ++i)
|
||||
{
|
||||
_queues[i] = new LinkedList<IHasWorkItemPriority>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue a work item.
|
||||
/// </summary>
|
||||
/// <param name="workItem">A work item</param>
|
||||
public void Enqueue(IHasWorkItemPriority workItem)
|
||||
{
|
||||
Debug.Assert(null != workItem);
|
||||
|
||||
int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1;
|
||||
Debug.Assert(queueIndex >= 0);
|
||||
Debug.Assert(queueIndex < _queuesCount);
|
||||
|
||||
_queues[queueIndex].AddLast(workItem);
|
||||
++_workItemsCount;
|
||||
++_version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeque a work item.
|
||||
/// </summary>
|
||||
/// <returns>Returns the next work item</returns>
|
||||
public IHasWorkItemPriority Dequeue()
|
||||
{
|
||||
IHasWorkItemPriority workItem = null;
|
||||
|
||||
if(_workItemsCount > 0)
|
||||
{
|
||||
int queueIndex = GetNextNonEmptyQueue(-1);
|
||||
Debug.Assert(queueIndex >= 0);
|
||||
workItem = _queues[queueIndex].First.Value;
|
||||
_queues[queueIndex].RemoveFirst();
|
||||
Debug.Assert(null != workItem);
|
||||
--_workItemsCount;
|
||||
++_version;
|
||||
}
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the next non empty queue starting at queue queueIndex+1
|
||||
/// </summary>
|
||||
/// <param name="queueIndex">The index-1 to start from</param>
|
||||
/// <returns>
|
||||
/// The index of the next non empty queue or -1 if all the queues are empty
|
||||
/// </returns>
|
||||
private int GetNextNonEmptyQueue(int queueIndex)
|
||||
{
|
||||
for(int i = queueIndex+1; i < _queuesCount; ++i)
|
||||
{
|
||||
if(_queues[i].Count > 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of work items
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workItemsCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all the work items
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
if (_workItemsCount > 0)
|
||||
{
|
||||
foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
|
||||
{
|
||||
queue.Clear();
|
||||
}
|
||||
_workItemsCount = 0;
|
||||
++_version;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator to iterate over the work items
|
||||
/// </summary>
|
||||
/// <returns>Returns an enumerator</returns>
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return new PriorityQueueEnumerator(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PriorityQueueEnumerator
|
||||
|
||||
/// <summary>
|
||||
/// The class the implements the enumerator
|
||||
/// </summary>
|
||||
private class PriorityQueueEnumerator : IEnumerator
|
||||
{
|
||||
private readonly PriorityQueue _priorityQueue;
|
||||
private int _version;
|
||||
private int _queueIndex;
|
||||
private IEnumerator _enumerator;
|
||||
|
||||
public PriorityQueueEnumerator(PriorityQueue priorityQueue)
|
||||
{
|
||||
_priorityQueue = priorityQueue;
|
||||
_version = _priorityQueue._version;
|
||||
_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
|
||||
if (_queueIndex >= 0)
|
||||
{
|
||||
_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
|
||||
}
|
||||
else
|
||||
{
|
||||
_enumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region IEnumerator Members
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_version = _priorityQueue._version;
|
||||
_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
|
||||
if (_queueIndex >= 0)
|
||||
{
|
||||
_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
|
||||
}
|
||||
else
|
||||
{
|
||||
_enumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
public object Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(null != _enumerator);
|
||||
return _enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (null == _enumerator)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_version != _priorityQueue._version)
|
||||
{
|
||||
throw new InvalidOperationException("The collection has been modified");
|
||||
|
||||
}
|
||||
if (!_enumerator.MoveNext())
|
||||
{
|
||||
_queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex);
|
||||
if(-1 == _queueIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
|
||||
_enumerator.MoveNext();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("Amib.Threading")]
|
||||
[assembly: AssemblyDescription("Smart Thread Pool")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Amib.Threading")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")]
|
||||
[assembly: AssemblyVersion("2.2.3.0")]
|
||||
|
||||
#if (_PUBLISH)
|
||||
[assembly: InternalsVisibleTo("STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad")]
|
||||
#else
|
||||
[assembly: InternalsVisibleTo("STPTests")]
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#if _SILVERLIGHT
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
public enum ThreadPriority
|
||||
{
|
||||
Lowest,
|
||||
BelowNormal,
|
||||
Normal,
|
||||
AboveNormal,
|
||||
Highest,
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
#if !(_WINDOWS_CE)
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#if _WINDOWS || WINDOWS_PHONE
|
||||
internal static class STPEventWaitHandle
|
||||
{
|
||||
public const int WaitTimeout = Timeout.Infinite;
|
||||
|
||||
internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return WaitHandle.WaitAll(waitHandles, millisecondsTimeout);
|
||||
}
|
||||
|
||||
internal static int WaitAny(WaitHandle[] waitHandles)
|
||||
{
|
||||
return WaitHandle.WaitAny(waitHandles);
|
||||
}
|
||||
|
||||
internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return WaitHandle.WaitAny(waitHandles, millisecondsTimeout);
|
||||
}
|
||||
|
||||
internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return waitHandle.WaitOne(millisecondsTimeout);
|
||||
}
|
||||
}
|
||||
#else
|
||||
internal static class STPEventWaitHandle
|
||||
{
|
||||
public const int WaitTimeout = Timeout.Infinite;
|
||||
|
||||
internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
|
||||
}
|
||||
|
||||
internal static int WaitAny(WaitHandle[] waitHandles)
|
||||
{
|
||||
return WaitHandle.WaitAny(waitHandles);
|
||||
}
|
||||
|
||||
internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
|
||||
}
|
||||
|
||||
internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return waitHandle.WaitOne(millisecondsTimeout, exitContext);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,354 +1,448 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
internal enum STPPerformanceCounterType
|
||||
{
|
||||
// Fields
|
||||
ActiveThreads = 0,
|
||||
InUseThreads = 1,
|
||||
OverheadThreads = 2,
|
||||
OverheadThreadsPercent = 3,
|
||||
OverheadThreadsPercentBase = 4,
|
||||
|
||||
WorkItems = 5,
|
||||
WorkItemsInQueue = 6,
|
||||
WorkItemsProcessed = 7,
|
||||
|
||||
WorkItemsQueuedPerSecond = 8,
|
||||
WorkItemsProcessedPerSecond = 9,
|
||||
|
||||
AvgWorkItemWaitTime = 10,
|
||||
AvgWorkItemWaitTimeBase = 11,
|
||||
|
||||
AvgWorkItemProcessTime = 12,
|
||||
AvgWorkItemProcessTimeBase = 13,
|
||||
|
||||
WorkItemsGroups = 14,
|
||||
|
||||
LastCounter = 14,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for STPPerformanceCounter.
|
||||
/// </summary>
|
||||
internal class STPPerformanceCounter
|
||||
{
|
||||
// Fields
|
||||
private PerformanceCounterType _pcType;
|
||||
protected string _counterHelp;
|
||||
protected string _counterName;
|
||||
|
||||
// Methods
|
||||
public STPPerformanceCounter(
|
||||
string counterName,
|
||||
string counterHelp,
|
||||
PerformanceCounterType pcType)
|
||||
{
|
||||
this._counterName = counterName;
|
||||
this._counterHelp = counterHelp;
|
||||
this._pcType = pcType;
|
||||
}
|
||||
|
||||
public void AddCounterToCollection(CounterCreationDataCollection counterData)
|
||||
{
|
||||
CounterCreationData counterCreationData = new CounterCreationData(
|
||||
_counterName,
|
||||
_counterHelp,
|
||||
_pcType);
|
||||
|
||||
counterData.Add(counterCreationData);
|
||||
}
|
||||
|
||||
// Properties
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _counterName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class STPPerformanceCounters
|
||||
{
|
||||
// Fields
|
||||
internal STPPerformanceCounter[] _stpPerformanceCounters;
|
||||
private static STPPerformanceCounters _instance;
|
||||
internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
|
||||
internal const string _stpCategoryName = "SmartThreadPool";
|
||||
|
||||
// Methods
|
||||
static STPPerformanceCounters()
|
||||
{
|
||||
_instance = new STPPerformanceCounters();
|
||||
}
|
||||
|
||||
private STPPerformanceCounters()
|
||||
{
|
||||
STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[]
|
||||
{
|
||||
new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction),
|
||||
new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase),
|
||||
|
||||
new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32),
|
||||
|
||||
new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32),
|
||||
new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32),
|
||||
|
||||
new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64),
|
||||
new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase),
|
||||
|
||||
new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64),
|
||||
new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase),
|
||||
|
||||
new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32),
|
||||
};
|
||||
|
||||
_stpPerformanceCounters = stpPerformanceCounters;
|
||||
SetupCategory();
|
||||
}
|
||||
|
||||
private void SetupCategory()
|
||||
{
|
||||
if (!PerformanceCounterCategory.Exists(_stpCategoryName))
|
||||
{
|
||||
CounterCreationDataCollection counters = new CounterCreationDataCollection();
|
||||
|
||||
for (int i = 0; i < _stpPerformanceCounters.Length; i++)
|
||||
{
|
||||
_stpPerformanceCounters[i].AddCounterToCollection(counters);
|
||||
}
|
||||
|
||||
|
||||
// *********** Remark for .NET 2.0 ***********
|
||||
// If you are here, it means you got the warning that this overload
|
||||
// of the method is deprecated in .NET 2.0. To use the correct
|
||||
// method overload, uncomment the third argument of
|
||||
// the method.
|
||||
#pragma warning disable 0618
|
||||
PerformanceCounterCategory.Create(
|
||||
_stpCategoryName,
|
||||
_stpCategoryHelp,
|
||||
//PerformanceCounterCategoryType.MultiInstance,
|
||||
counters);
|
||||
#pragma warning restore 0618
|
||||
}
|
||||
}
|
||||
|
||||
// Properties
|
||||
public static STPPerformanceCounters Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class STPInstancePerformanceCounter : IDisposable
|
||||
{
|
||||
// Fields
|
||||
private PerformanceCounter _pcs;
|
||||
|
||||
// Methods
|
||||
protected STPInstancePerformanceCounter()
|
||||
{
|
||||
}
|
||||
|
||||
public STPInstancePerformanceCounter(
|
||||
string instance,
|
||||
STPPerformanceCounterType spcType)
|
||||
{
|
||||
STPPerformanceCounters counters = STPPerformanceCounters.Instance;
|
||||
_pcs = new PerformanceCounter(
|
||||
STPPerformanceCounters._stpCategoryName,
|
||||
counters._stpPerformanceCounters[(int) spcType].Name,
|
||||
instance,
|
||||
false);
|
||||
_pcs.RawValue = _pcs.RawValue;
|
||||
}
|
||||
|
||||
~STPInstancePerformanceCounter()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_pcs != null)
|
||||
{
|
||||
_pcs.RemoveInstance();
|
||||
_pcs.Close();
|
||||
_pcs = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public virtual void Increment()
|
||||
{
|
||||
_pcs.Increment();
|
||||
}
|
||||
|
||||
public virtual void IncrementBy(long val)
|
||||
{
|
||||
_pcs.IncrementBy(val);
|
||||
}
|
||||
|
||||
public virtual void Set(long val)
|
||||
{
|
||||
_pcs.RawValue = val;
|
||||
}
|
||||
}
|
||||
|
||||
internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
|
||||
{
|
||||
// Methods
|
||||
public STPInstanceNullPerformanceCounter() {}
|
||||
public override void Increment() {}
|
||||
public override void IncrementBy(long value) {}
|
||||
public override void Set(long val) {}
|
||||
}
|
||||
|
||||
internal interface ISTPInstancePerformanceCounters : IDisposable
|
||||
{
|
||||
void Close();
|
||||
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
|
||||
{
|
||||
// Fields
|
||||
private STPInstancePerformanceCounter[] _pcs;
|
||||
private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
|
||||
|
||||
// Methods
|
||||
static STPInstancePerformanceCounters()
|
||||
{
|
||||
_stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter();
|
||||
}
|
||||
|
||||
public STPInstancePerformanceCounters(string instance)
|
||||
{
|
||||
_pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
|
||||
// STPPerformanceCounters counters = STPPerformanceCounters.Instance;
|
||||
for (int i = 0; i < _pcs.Length; i++)
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
_pcs[i] = new STPInstancePerformanceCounter(
|
||||
instance,
|
||||
(STPPerformanceCounterType) i);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pcs[i] = _stpInstanceNullPerformanceCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (null != _pcs)
|
||||
{
|
||||
for (int i = 0; i < _pcs.Length; i++)
|
||||
{
|
||||
if (null != _pcs[i])
|
||||
{
|
||||
_pcs[i].Close();
|
||||
}
|
||||
}
|
||||
_pcs = null;
|
||||
}
|
||||
}
|
||||
|
||||
~STPInstancePerformanceCounters()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
|
||||
{
|
||||
return _pcs[(int) spcType];
|
||||
}
|
||||
|
||||
public void SampleThreads(long activeThreads, long inUseThreads)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads);
|
||||
GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads);
|
||||
GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads);
|
||||
|
||||
GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads);
|
||||
GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads);
|
||||
}
|
||||
|
||||
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed);
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued);
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed);
|
||||
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued);
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed);
|
||||
}
|
||||
|
||||
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds);
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment();
|
||||
}
|
||||
|
||||
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds);
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
|
||||
}
|
||||
}
|
||||
|
||||
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
|
||||
{
|
||||
static NullSTPInstancePerformanceCounters()
|
||||
{
|
||||
}
|
||||
|
||||
private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null);
|
||||
|
||||
public static NullSTPInstancePerformanceCounters Instance
|
||||
{
|
||||
get { return _instance; }
|
||||
}
|
||||
|
||||
public NullSTPInstancePerformanceCounters(string instance) {}
|
||||
public void Close() {}
|
||||
public void Dispose() {}
|
||||
|
||||
public void SampleThreads(long activeThreads, long inUseThreads) {}
|
||||
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
|
||||
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
|
||||
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
public interface ISTPPerformanceCountersReader
|
||||
{
|
||||
long InUseThreads { get; }
|
||||
long ActiveThreads { get; }
|
||||
long WorkItemsQueued { get; }
|
||||
long WorkItemsProcessed { get; }
|
||||
}
|
||||
}
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
internal interface ISTPInstancePerformanceCounters : IDisposable
|
||||
{
|
||||
void Close();
|
||||
void SampleThreads(long activeThreads, long inUseThreads);
|
||||
void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
|
||||
void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
|
||||
void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
|
||||
}
|
||||
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
internal enum STPPerformanceCounterType
|
||||
{
|
||||
// Fields
|
||||
ActiveThreads = 0,
|
||||
InUseThreads = 1,
|
||||
OverheadThreads = 2,
|
||||
OverheadThreadsPercent = 3,
|
||||
OverheadThreadsPercentBase = 4,
|
||||
|
||||
WorkItems = 5,
|
||||
WorkItemsInQueue = 6,
|
||||
WorkItemsProcessed = 7,
|
||||
|
||||
WorkItemsQueuedPerSecond = 8,
|
||||
WorkItemsProcessedPerSecond = 9,
|
||||
|
||||
AvgWorkItemWaitTime = 10,
|
||||
AvgWorkItemWaitTimeBase = 11,
|
||||
|
||||
AvgWorkItemProcessTime = 12,
|
||||
AvgWorkItemProcessTimeBase = 13,
|
||||
|
||||
WorkItemsGroups = 14,
|
||||
|
||||
LastCounter = 14,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for STPPerformanceCounter.
|
||||
/// </summary>
|
||||
internal class STPPerformanceCounter
|
||||
{
|
||||
// Fields
|
||||
private readonly PerformanceCounterType _pcType;
|
||||
protected string _counterHelp;
|
||||
protected string _counterName;
|
||||
|
||||
// Methods
|
||||
public STPPerformanceCounter(
|
||||
string counterName,
|
||||
string counterHelp,
|
||||
PerformanceCounterType pcType)
|
||||
{
|
||||
_counterName = counterName;
|
||||
_counterHelp = counterHelp;
|
||||
_pcType = pcType;
|
||||
}
|
||||
|
||||
public void AddCounterToCollection(CounterCreationDataCollection counterData)
|
||||
{
|
||||
CounterCreationData counterCreationData = new CounterCreationData(
|
||||
_counterName,
|
||||
_counterHelp,
|
||||
_pcType);
|
||||
|
||||
counterData.Add(counterCreationData);
|
||||
}
|
||||
|
||||
// Properties
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _counterName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class STPPerformanceCounters
|
||||
{
|
||||
// Fields
|
||||
internal STPPerformanceCounter[] _stpPerformanceCounters;
|
||||
private static readonly STPPerformanceCounters _instance;
|
||||
internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
|
||||
internal const string _stpCategoryName = "SmartThreadPool";
|
||||
|
||||
// Methods
|
||||
static STPPerformanceCounters()
|
||||
{
|
||||
_instance = new STPPerformanceCounters();
|
||||
}
|
||||
|
||||
private STPPerformanceCounters()
|
||||
{
|
||||
STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[]
|
||||
{
|
||||
new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction),
|
||||
new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase),
|
||||
|
||||
new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32),
|
||||
new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32),
|
||||
|
||||
new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32),
|
||||
new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32),
|
||||
|
||||
new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64),
|
||||
new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase),
|
||||
|
||||
new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64),
|
||||
new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase),
|
||||
|
||||
new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32),
|
||||
};
|
||||
|
||||
_stpPerformanceCounters = stpPerformanceCounters;
|
||||
SetupCategory();
|
||||
}
|
||||
|
||||
private void SetupCategory()
|
||||
{
|
||||
if (!PerformanceCounterCategory.Exists(_stpCategoryName))
|
||||
{
|
||||
CounterCreationDataCollection counters = new CounterCreationDataCollection();
|
||||
|
||||
for (int i = 0; i < _stpPerformanceCounters.Length; i++)
|
||||
{
|
||||
_stpPerformanceCounters[i].AddCounterToCollection(counters);
|
||||
}
|
||||
|
||||
PerformanceCounterCategory.Create(
|
||||
_stpCategoryName,
|
||||
_stpCategoryHelp,
|
||||
PerformanceCounterCategoryType.MultiInstance,
|
||||
counters);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Properties
|
||||
public static STPPerformanceCounters Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class STPInstancePerformanceCounter : IDisposable
|
||||
{
|
||||
// Fields
|
||||
private bool _isDisposed;
|
||||
private PerformanceCounter _pcs;
|
||||
|
||||
// Methods
|
||||
protected STPInstancePerformanceCounter()
|
||||
{
|
||||
_isDisposed = false;
|
||||
}
|
||||
|
||||
public STPInstancePerformanceCounter(
|
||||
string instance,
|
||||
STPPerformanceCounterType spcType) : this()
|
||||
{
|
||||
STPPerformanceCounters counters = STPPerformanceCounters.Instance;
|
||||
_pcs = new PerformanceCounter(
|
||||
STPPerformanceCounters._stpCategoryName,
|
||||
counters._stpPerformanceCounters[(int) spcType].Name,
|
||||
instance,
|
||||
false);
|
||||
_pcs.RawValue = _pcs.RawValue;
|
||||
}
|
||||
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_pcs != null)
|
||||
{
|
||||
_pcs.RemoveInstance();
|
||||
_pcs.Close();
|
||||
_pcs = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
public virtual void Increment()
|
||||
{
|
||||
_pcs.Increment();
|
||||
}
|
||||
|
||||
public virtual void IncrementBy(long val)
|
||||
{
|
||||
_pcs.IncrementBy(val);
|
||||
}
|
||||
|
||||
public virtual void Set(long val)
|
||||
{
|
||||
_pcs.RawValue = val;
|
||||
}
|
||||
}
|
||||
|
||||
internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
|
||||
{
|
||||
// Methods
|
||||
public override void Increment() {}
|
||||
public override void IncrementBy(long value) {}
|
||||
public override void Set(long val) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters
|
||||
{
|
||||
private bool _isDisposed;
|
||||
// Fields
|
||||
private STPInstancePerformanceCounter[] _pcs;
|
||||
private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
|
||||
|
||||
// Methods
|
||||
static STPInstancePerformanceCounters()
|
||||
{
|
||||
_stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter();
|
||||
}
|
||||
|
||||
public STPInstancePerformanceCounters(string instance)
|
||||
{
|
||||
_isDisposed = false;
|
||||
_pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
|
||||
|
||||
// Call the STPPerformanceCounters.Instance so the static constructor will
|
||||
// intialize the STPPerformanceCounters singleton.
|
||||
STPPerformanceCounters.Instance.GetHashCode();
|
||||
|
||||
for (int i = 0; i < _pcs.Length; i++)
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
_pcs[i] = new STPInstancePerformanceCounter(
|
||||
instance,
|
||||
(STPPerformanceCounterType) i);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pcs[i] = _stpInstanceNullPerformanceCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (null != _pcs)
|
||||
{
|
||||
for (int i = 0; i < _pcs.Length; i++)
|
||||
{
|
||||
if (null != _pcs[i])
|
||||
{
|
||||
_pcs[i].Dispose();
|
||||
}
|
||||
}
|
||||
_pcs = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
|
||||
{
|
||||
return _pcs[(int) spcType];
|
||||
}
|
||||
|
||||
public void SampleThreads(long activeThreads, long inUseThreads)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads);
|
||||
GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads);
|
||||
GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads);
|
||||
|
||||
GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads);
|
||||
GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads);
|
||||
}
|
||||
|
||||
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed);
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued);
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed);
|
||||
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued);
|
||||
GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed);
|
||||
}
|
||||
|
||||
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds);
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment();
|
||||
}
|
||||
|
||||
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
|
||||
{
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds);
|
||||
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
|
||||
{
|
||||
private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();
|
||||
|
||||
public static NullSTPInstancePerformanceCounters Instance
|
||||
{
|
||||
get { return _instance; }
|
||||
}
|
||||
|
||||
public void Close() {}
|
||||
public void Dispose() {}
|
||||
|
||||
public void SampleThreads(long activeThreads, long inUseThreads) {}
|
||||
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
|
||||
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
|
||||
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
|
||||
public long InUseThreads
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public long ActiveThreads
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public long WorkItemsQueued
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public long WorkItemsProcessed
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
|
||||
{
|
||||
public void Close() { }
|
||||
public void Dispose() { }
|
||||
|
||||
private long _activeThreads;
|
||||
private long _inUseThreads;
|
||||
private long _workItemsQueued;
|
||||
private long _workItemsProcessed;
|
||||
|
||||
public long InUseThreads
|
||||
{
|
||||
get { return _inUseThreads; }
|
||||
}
|
||||
|
||||
public long ActiveThreads
|
||||
{
|
||||
get { return _activeThreads; }
|
||||
}
|
||||
|
||||
public long WorkItemsQueued
|
||||
{
|
||||
get { return _workItemsQueued; }
|
||||
}
|
||||
|
||||
public long WorkItemsProcessed
|
||||
{
|
||||
get { return _workItemsProcessed; }
|
||||
}
|
||||
|
||||
public void SampleThreads(long activeThreads, long inUseThreads)
|
||||
{
|
||||
_activeThreads = activeThreads;
|
||||
_inUseThreads = inUseThreads;
|
||||
}
|
||||
|
||||
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
|
||||
{
|
||||
_workItemsQueued = workItemsQueued;
|
||||
_workItemsProcessed = workItemsProcessed;
|
||||
}
|
||||
|
||||
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
|
||||
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,113 +1,212 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for STPStartInfo.
|
||||
/// </summary>
|
||||
public class STPStartInfo : WIGStartInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Idle timeout in milliseconds.
|
||||
/// If a thread is idle for _idleTimeout milliseconds then
|
||||
/// it may quit.
|
||||
/// </summary>
|
||||
private int _idleTimeout;
|
||||
|
||||
/// <summary>
|
||||
/// The lower limit of threads in the pool.
|
||||
/// </summary>
|
||||
private int _minWorkerThreads;
|
||||
|
||||
/// <summary>
|
||||
/// The upper limit of threads in the pool.
|
||||
/// </summary>
|
||||
private int _maxWorkerThreads;
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the threads in the pool
|
||||
/// </summary>
|
||||
private ThreadPriority _threadPriority;
|
||||
|
||||
/// <summary>
|
||||
/// The thread pool name. Threads will get names depending on this.
|
||||
/// </summary>
|
||||
private string _threadPoolName;
|
||||
|
||||
/// <summary>
|
||||
/// If this field is not null then the performance counters are enabled
|
||||
/// and use the string as the name of the instance.
|
||||
/// </summary>
|
||||
private string _pcInstanceName;
|
||||
|
||||
private int _stackSize;
|
||||
|
||||
public STPStartInfo() : base()
|
||||
{
|
||||
_idleTimeout = SmartThreadPool.DefaultIdleTimeout;
|
||||
_minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
|
||||
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
|
||||
_threadPriority = SmartThreadPool.DefaultThreadPriority;
|
||||
_threadPoolName = SmartThreadPool.DefaultThreadPoolName;
|
||||
_pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
|
||||
_stackSize = SmartThreadPool.DefaultStackSize;
|
||||
}
|
||||
|
||||
public STPStartInfo(STPStartInfo stpStartInfo) : base(stpStartInfo)
|
||||
{
|
||||
_idleTimeout = stpStartInfo._idleTimeout;
|
||||
_minWorkerThreads = stpStartInfo._minWorkerThreads;
|
||||
_maxWorkerThreads = stpStartInfo._maxWorkerThreads;
|
||||
_threadPriority = stpStartInfo._threadPriority;
|
||||
_threadPoolName = stpStartInfo._threadPoolName;
|
||||
_pcInstanceName = stpStartInfo._pcInstanceName;
|
||||
_stackSize = stpStartInfo._stackSize;
|
||||
}
|
||||
|
||||
public int IdleTimeout
|
||||
{
|
||||
get { return _idleTimeout; }
|
||||
set { _idleTimeout = value; }
|
||||
}
|
||||
|
||||
public int MinWorkerThreads
|
||||
{
|
||||
get { return _minWorkerThreads; }
|
||||
set { _minWorkerThreads = value; }
|
||||
}
|
||||
|
||||
public int MaxWorkerThreads
|
||||
{
|
||||
get { return _maxWorkerThreads; }
|
||||
set { _maxWorkerThreads = value; }
|
||||
}
|
||||
|
||||
public ThreadPriority ThreadPriority
|
||||
{
|
||||
get { return _threadPriority; }
|
||||
set { _threadPriority = value; }
|
||||
}
|
||||
|
||||
public virtual string ThreadPoolName
|
||||
{
|
||||
get { return _threadPoolName; }
|
||||
set { _threadPoolName = value; }
|
||||
}
|
||||
|
||||
|
||||
public string PerformanceCounterInstanceName
|
||||
{
|
||||
get { return _pcInstanceName; }
|
||||
set { _pcInstanceName = value; }
|
||||
}
|
||||
|
||||
public int StackSize
|
||||
{
|
||||
get { return _stackSize; }
|
||||
set { _stackSize = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for STPStartInfo.
|
||||
/// </summary>
|
||||
public class STPStartInfo : WIGStartInfo
|
||||
{
|
||||
private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
|
||||
private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
|
||||
private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
|
||||
#if !(WINDOWS_PHONE)
|
||||
private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
|
||||
#endif
|
||||
private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
|
||||
private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground;
|
||||
private bool _enableLocalPerformanceCounters;
|
||||
private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName;
|
||||
private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize;
|
||||
|
||||
public STPStartInfo()
|
||||
{
|
||||
_performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
|
||||
#if !(WINDOWS_PHONE)
|
||||
_threadPriority = SmartThreadPool.DefaultThreadPriority;
|
||||
#endif
|
||||
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
|
||||
_idleTimeout = SmartThreadPool.DefaultIdleTimeout;
|
||||
_minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
|
||||
}
|
||||
|
||||
public STPStartInfo(STPStartInfo stpStartInfo)
|
||||
: base(stpStartInfo)
|
||||
{
|
||||
_idleTimeout = stpStartInfo.IdleTimeout;
|
||||
_minWorkerThreads = stpStartInfo.MinWorkerThreads;
|
||||
_maxWorkerThreads = stpStartInfo.MaxWorkerThreads;
|
||||
#if !(WINDOWS_PHONE)
|
||||
_threadPriority = stpStartInfo.ThreadPriority;
|
||||
#endif
|
||||
_performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName;
|
||||
_enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters;
|
||||
_threadPoolName = stpStartInfo._threadPoolName;
|
||||
_areThreadsBackground = stpStartInfo.AreThreadsBackground;
|
||||
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
_apartmentState = stpStartInfo._apartmentState;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the idle timeout in milliseconds.
|
||||
/// If a thread is idle (starved) longer than IdleTimeout then it may quit.
|
||||
/// </summary>
|
||||
public virtual int IdleTimeout
|
||||
{
|
||||
get { return _idleTimeout; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_idleTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the lower limit of threads in the pool.
|
||||
/// </summary>
|
||||
public virtual int MinWorkerThreads
|
||||
{
|
||||
get { return _minWorkerThreads; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_minWorkerThreads = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the upper limit of threads in the pool.
|
||||
/// </summary>
|
||||
public virtual int MaxWorkerThreads
|
||||
{
|
||||
get { return _maxWorkerThreads; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_maxWorkerThreads = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if !(WINDOWS_PHONE)
|
||||
/// <summary>
|
||||
/// Get/Set the scheduling priority of the threads in the pool.
|
||||
/// The Os handles the scheduling.
|
||||
/// </summary>
|
||||
public virtual ThreadPriority ThreadPriority
|
||||
{
|
||||
get { return _threadPriority; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_threadPriority = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Get/Set the thread pool name. Threads will get names depending on this.
|
||||
/// </summary>
|
||||
public virtual string ThreadPoolName {
|
||||
get { return _threadPoolName; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly ();
|
||||
_threadPoolName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 _performanceCounterInstanceName; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_performanceCounterInstanceName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable/Disable the local performance counter.
|
||||
/// This enables the user to get some performance information about the SmartThreadPool
|
||||
/// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.)
|
||||
/// The default is false.
|
||||
/// </summary>
|
||||
public virtual bool EnableLocalPerformanceCounters
|
||||
{
|
||||
get { return _enableLocalPerformanceCounters; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_enableLocalPerformanceCounters = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set backgroundness of thread in thread pool.
|
||||
/// </summary>
|
||||
public virtual bool AreThreadsBackground
|
||||
{
|
||||
get { return _areThreadsBackground; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly ();
|
||||
_areThreadsBackground = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a readonly version of this STPStartInfo.
|
||||
/// </summary>
|
||||
/// <returns>Returns a readonly reference to this STPStartInfo</returns>
|
||||
public new STPStartInfo AsReadOnly()
|
||||
{
|
||||
return new STPStartInfo(this) { _readOnly = true };
|
||||
}
|
||||
|
||||
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState;
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the apartment state of threads in the thread pool
|
||||
/// </summary>
|
||||
public ApartmentState ApartmentState
|
||||
{
|
||||
get { return _apartmentState; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_apartmentState = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the max stack size of threads in the thread pool
|
||||
/// </summary>
|
||||
public int? MaxStackSize
|
||||
{
|
||||
get { return _maxStackSize; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
if (value.HasValue && value.Value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value", "Value must be greater than 0.");
|
||||
}
|
||||
_maxStackSize = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
using System;
|
||||
using Amib.Threading.Internal;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
public partial class SmartThreadPool
|
||||
{
|
||||
#region ThreadEntry class
|
||||
|
||||
internal class ThreadEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The thread creation time
|
||||
/// The value is stored as UTC value.
|
||||
/// </summary>
|
||||
private readonly DateTime _creationTime;
|
||||
|
||||
/// <summary>
|
||||
/// The last time this thread has been running
|
||||
/// It is updated by IAmAlive() method
|
||||
/// The value is stored as UTC value.
|
||||
/// </summary>
|
||||
private DateTime _lastAliveTime;
|
||||
|
||||
/// <summary>
|
||||
/// A reference from each thread in the thread pool to its SmartThreadPool
|
||||
/// object container.
|
||||
/// With this variable a thread can know whatever it belongs to a
|
||||
/// SmartThreadPool.
|
||||
/// </summary>
|
||||
private readonly SmartThreadPool _associatedSmartThreadPool;
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the current work item a thread from the thread pool
|
||||
/// is executing.
|
||||
/// </summary>
|
||||
public WorkItem CurrentWorkItem { get; set; }
|
||||
|
||||
public ThreadEntry(SmartThreadPool stp)
|
||||
{
|
||||
_associatedSmartThreadPool = stp;
|
||||
_creationTime = DateTime.UtcNow;
|
||||
_lastAliveTime = DateTime.MinValue;
|
||||
}
|
||||
|
||||
public SmartThreadPool AssociatedSmartThreadPool
|
||||
{
|
||||
get { return _associatedSmartThreadPool; }
|
||||
}
|
||||
|
||||
public void IAmAlive()
|
||||
{
|
||||
_lastAliveTime = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,89 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
internal class SynchronizedDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> _dictionary;
|
||||
private readonly object _lock;
|
||||
|
||||
public SynchronizedDictionary()
|
||||
{
|
||||
_lock = new object();
|
||||
_dictionary = new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _dictionary.Count; }
|
||||
}
|
||||
|
||||
public bool Contains(TKey key)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dictionary.ContainsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(TKey key)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_dictionary.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get { return _lock; }
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dictionary[key];
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_dictionary[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<TKey, TValue>.KeyCollection Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dictionary.Keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<TKey, TValue>.ValueCollection Values
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _dictionary.Values;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_dictionary.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +1,171 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for WIGStartInfo.
|
||||
/// </summary>
|
||||
public class WIGStartInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the caller's security context
|
||||
/// </summary>
|
||||
private bool _useCallerCallContext;
|
||||
|
||||
/// <summary>
|
||||
/// Use the caller's HTTP context
|
||||
/// </summary>
|
||||
private bool _useCallerHttpContext;
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the state object of a work item
|
||||
/// </summary>
|
||||
private bool _disposeOfStateObjects;
|
||||
|
||||
/// <summary>
|
||||
/// The option to run the post execute
|
||||
/// </summary>
|
||||
private CallToPostExecute _callToPostExecute;
|
||||
|
||||
/// <summary>
|
||||
/// A post execute callback to call when none is provided in
|
||||
/// the QueueWorkItem method.
|
||||
/// </summary>
|
||||
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate the WorkItemsGroup to suspend the handling of the work items
|
||||
/// until the Start() method is called.
|
||||
/// </summary>
|
||||
private bool _startSuspended;
|
||||
|
||||
public WIGStartInfo()
|
||||
{
|
||||
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
|
||||
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
|
||||
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
|
||||
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
|
||||
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
|
||||
_startSuspended = SmartThreadPool.DefaultStartSuspended;
|
||||
}
|
||||
|
||||
public WIGStartInfo(WIGStartInfo wigStartInfo)
|
||||
{
|
||||
_useCallerCallContext = wigStartInfo._useCallerCallContext;
|
||||
_useCallerHttpContext = wigStartInfo._useCallerHttpContext;
|
||||
_disposeOfStateObjects = wigStartInfo._disposeOfStateObjects;
|
||||
_callToPostExecute = wigStartInfo._callToPostExecute;
|
||||
_postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback;
|
||||
_startSuspended = wigStartInfo._startSuspended;
|
||||
}
|
||||
|
||||
public bool UseCallerCallContext
|
||||
{
|
||||
get { return _useCallerCallContext; }
|
||||
set { _useCallerCallContext = value; }
|
||||
}
|
||||
|
||||
public bool UseCallerHttpContext
|
||||
{
|
||||
get { return _useCallerHttpContext; }
|
||||
set { _useCallerHttpContext = value; }
|
||||
}
|
||||
|
||||
public bool DisposeOfStateObjects
|
||||
{
|
||||
get { return _disposeOfStateObjects; }
|
||||
set { _disposeOfStateObjects = value; }
|
||||
}
|
||||
|
||||
public CallToPostExecute CallToPostExecute
|
||||
{
|
||||
get { return _callToPostExecute; }
|
||||
set { _callToPostExecute = value; }
|
||||
}
|
||||
|
||||
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
|
||||
{
|
||||
get { return _postExecuteWorkItemCallback; }
|
||||
set { _postExecuteWorkItemCallback = value; }
|
||||
}
|
||||
|
||||
public bool StartSuspended
|
||||
{
|
||||
get { return _startSuspended; }
|
||||
set { _startSuspended = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for WIGStartInfo.
|
||||
/// </summary>
|
||||
public class WIGStartInfo
|
||||
{
|
||||
private bool _useCallerCallContext;
|
||||
private bool _useCallerHttpContext;
|
||||
private bool _disposeOfStateObjects;
|
||||
private CallToPostExecute _callToPostExecute;
|
||||
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
|
||||
private bool _startSuspended;
|
||||
private WorkItemPriority _workItemPriority;
|
||||
private bool _fillStateWithArgs;
|
||||
|
||||
protected bool _readOnly;
|
||||
|
||||
public WIGStartInfo()
|
||||
{
|
||||
_fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
|
||||
_workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
|
||||
_startSuspended = SmartThreadPool.DefaultStartSuspended;
|
||||
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
|
||||
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
|
||||
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
|
||||
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
|
||||
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
|
||||
}
|
||||
|
||||
public WIGStartInfo(WIGStartInfo wigStartInfo)
|
||||
{
|
||||
_useCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
_useCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
_disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
_callToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
_postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
|
||||
_workItemPriority = wigStartInfo.WorkItemPriority;
|
||||
_startSuspended = wigStartInfo.StartSuspended;
|
||||
_fillStateWithArgs = wigStartInfo.FillStateWithArgs;
|
||||
}
|
||||
|
||||
protected void ThrowIfReadOnly()
|
||||
{
|
||||
if (_readOnly)
|
||||
{
|
||||
throw new NotSupportedException("This is a readonly instance and set is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's security context
|
||||
/// </summary>
|
||||
public virtual bool UseCallerCallContext
|
||||
{
|
||||
get { return _useCallerCallContext; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_useCallerCallContext = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's HTTP context
|
||||
/// </summary>
|
||||
public virtual bool UseCallerHttpContext
|
||||
{
|
||||
get { return _useCallerHttpContext; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_useCallerHttpContext = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to dispose of the state object of a work item
|
||||
/// </summary>
|
||||
public virtual bool DisposeOfStateObjects
|
||||
{
|
||||
get { return _disposeOfStateObjects; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_disposeOfStateObjects = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the run the post execute options
|
||||
/// </summary>
|
||||
public virtual CallToPostExecute CallToPostExecute
|
||||
{
|
||||
get { return _callToPostExecute; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_callToPostExecute = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the default post execute callback
|
||||
/// </summary>
|
||||
public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback
|
||||
{
|
||||
get { return _postExecuteWorkItemCallback; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_postExecuteWorkItemCallback = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if the work items execution should be suspended until the Start()
|
||||
/// method is called.
|
||||
/// </summary>
|
||||
public virtual bool StartSuspended
|
||||
{
|
||||
get { return _startSuspended; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_startSuspended = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the default priority that a work item gets when it is enqueued
|
||||
/// </summary>
|
||||
public virtual WorkItemPriority WorkItemPriority
|
||||
{
|
||||
get { return _workItemPriority; }
|
||||
set { _workItemPriority = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the
|
||||
/// arguments as an object array into the state of the work item.
|
||||
/// The arguments can be access later by IWorkItemResult.State.
|
||||
/// </summary>
|
||||
public virtual bool FillStateWithArgs
|
||||
{
|
||||
get { return _fillStateWithArgs; }
|
||||
set
|
||||
{
|
||||
ThrowIfReadOnly();
|
||||
_fillStateWithArgs = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a readonly version of this WIGStartInfo
|
||||
/// </summary>
|
||||
/// <returns>Returns a readonly reference to this WIGStartInfoRO</returns>
|
||||
public WIGStartInfo AsReadOnly()
|
||||
{
|
||||
return new WIGStartInfo(this) { _readOnly = true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
public partial class WorkItem
|
||||
{
|
||||
#region WorkItemResult class
|
||||
|
||||
private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// A back reference to the work item
|
||||
/// </summary>
|
||||
private readonly WorkItem _workItem;
|
||||
|
||||
public WorkItemResult(WorkItem workItem)
|
||||
{
|
||||
_workItem = workItem;
|
||||
}
|
||||
|
||||
internal WorkItem GetWorkItem()
|
||||
{
|
||||
return _workItem;
|
||||
}
|
||||
|
||||
#region IWorkItemResult Members
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workItem.IsCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCanceled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workItem.IsCanceled;
|
||||
}
|
||||
}
|
||||
|
||||
public object GetResult()
|
||||
{
|
||||
return _workItem.GetResult(Timeout.Infinite, true, null);
|
||||
}
|
||||
|
||||
public object GetResult(int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return _workItem.GetResult(millisecondsTimeout, exitContext, null);
|
||||
}
|
||||
|
||||
public object GetResult(TimeSpan timeout, bool exitContext)
|
||||
{
|
||||
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null);
|
||||
}
|
||||
|
||||
public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
|
||||
{
|
||||
return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
|
||||
}
|
||||
|
||||
public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
|
||||
{
|
||||
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
|
||||
}
|
||||
|
||||
public object GetResult(out Exception e)
|
||||
{
|
||||
return _workItem.GetResult(Timeout.Infinite, true, null, out e);
|
||||
}
|
||||
|
||||
public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
|
||||
{
|
||||
return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e);
|
||||
}
|
||||
|
||||
public object GetResult(TimeSpan timeout, bool exitContext, out Exception e)
|
||||
{
|
||||
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e);
|
||||
}
|
||||
|
||||
public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
|
||||
{
|
||||
return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
|
||||
}
|
||||
|
||||
public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
|
||||
{
|
||||
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e);
|
||||
}
|
||||
|
||||
public bool Cancel()
|
||||
{
|
||||
return Cancel(false);
|
||||
}
|
||||
|
||||
public bool Cancel(bool abortExecution)
|
||||
{
|
||||
return _workItem.Cancel(abortExecution);
|
||||
}
|
||||
|
||||
public object State
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workItem._state;
|
||||
}
|
||||
}
|
||||
|
||||
public WorkItemPriority WorkItemPriority
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workItem._workItemInfo.WorkItemPriority;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the result, same as GetResult()
|
||||
/// </summary>
|
||||
public object Result
|
||||
{
|
||||
get { return GetResult(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception if occured otherwise returns null.
|
||||
/// This value is valid only after the work item completed,
|
||||
/// before that it is always null.
|
||||
/// </summary>
|
||||
public object Exception
|
||||
{
|
||||
get { return _workItem._exception; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInternalWorkItemResult Members
|
||||
|
||||
public event WorkItemStateCallback OnWorkItemStarted
|
||||
{
|
||||
add
|
||||
{
|
||||
_workItem.OnWorkItemStarted += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
_workItem.OnWorkItemStarted -= value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public event WorkItemStateCallback OnWorkItemCompleted
|
||||
{
|
||||
add
|
||||
{
|
||||
_workItem.OnWorkItemCompleted += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
_workItem.OnWorkItemCompleted -= value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInternalWorkItemResult Members
|
||||
|
||||
public IWorkItemResult GetWorkItemResult()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWorkItemResult<TResult> GetWorkItemResultT<TResult>()
|
||||
{
|
||||
return new WorkItemResultTWrapper<TResult>(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,333 +1,343 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region WorkItemFactory class
|
||||
|
||||
public class WorkItemFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback)
|
||||
{
|
||||
return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="workItemPriority">The priority of the work item</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="workItemInfo">Work item info</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemInfo workItemInfo,
|
||||
WorkItemCallback callback)
|
||||
{
|
||||
return CreateWorkItem(
|
||||
workItemsGroup,
|
||||
wigStartInfo,
|
||||
workItemInfo,
|
||||
callback,
|
||||
null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start 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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start 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>
|
||||
/// <param name="workItemPriority">The work item priority</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = workItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemInfo workItemInfo,
|
||||
WorkItemCallback callback,
|
||||
object state)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(workItemInfo.PostExecuteWorkItemCallback);
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
new WorkItemInfo(workItemInfo),
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start 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>
|
||||
/// <param name="postExecuteWorkItemCallback">
|
||||
/// A delegate to call after the callback completion
|
||||
/// </param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start 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>
|
||||
/// <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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = workItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start 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>
|
||||
/// <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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
||||
CallToPostExecute callToPostExecute)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = callToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="wigStartInfo">Work item group start 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>
|
||||
/// <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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
||||
CallToPostExecute callToPostExecute,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = callToPostExecute;
|
||||
workItemInfo.WorkItemPriority = workItemPriority;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
private static void ValidateCallback(Delegate callback)
|
||||
{
|
||||
if(callback.GetInvocationList().Length > 1)
|
||||
{
|
||||
throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region WorkItemFactory class
|
||||
|
||||
public class WorkItemFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback)
|
||||
{
|
||||
return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="workItemPriority">The priority of the work item</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="workItemInfo">Work item info</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemInfo workItemInfo,
|
||||
WorkItemCallback callback)
|
||||
{
|
||||
return CreateWorkItem(
|
||||
workItemsGroup,
|
||||
wigStartInfo,
|
||||
workItemInfo,
|
||||
callback,
|
||||
null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// 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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = workItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="workItemInfo">Work item information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// The context object of the work item. Used for passing arguments to the work item.
|
||||
/// </param>
|
||||
/// <returns>Returns a work item</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemInfo workItemInfo,
|
||||
WorkItemCallback callback,
|
||||
object state)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(workItemInfo.PostExecuteWorkItemCallback);
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
new WorkItemInfo(workItemInfo),
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// 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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// 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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = workItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// 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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
||||
CallToPostExecute callToPostExecute)
|
||||
{
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = callToPostExecute;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new work item
|
||||
/// </summary>
|
||||
/// <param name="workItemsGroup">The work items group</param>
|
||||
/// <param name="wigStartInfo">Work item group start information</param>
|
||||
/// <param name="callback">A callback to execute</param>
|
||||
/// <param name="state">
|
||||
/// 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</returns>
|
||||
public static WorkItem CreateWorkItem(
|
||||
IWorkItemsGroup workItemsGroup,
|
||||
WIGStartInfo wigStartInfo,
|
||||
WorkItemCallback callback,
|
||||
object state,
|
||||
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
|
||||
CallToPostExecute callToPostExecute,
|
||||
WorkItemPriority workItemPriority)
|
||||
{
|
||||
|
||||
ValidateCallback(callback);
|
||||
ValidateCallback(postExecuteWorkItemCallback);
|
||||
|
||||
WorkItemInfo workItemInfo = new WorkItemInfo();
|
||||
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
|
||||
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
|
||||
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
|
||||
workItemInfo.CallToPostExecute = callToPostExecute;
|
||||
workItemInfo.WorkItemPriority = workItemPriority;
|
||||
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
|
||||
|
||||
WorkItem workItem = new WorkItem(
|
||||
workItemsGroup,
|
||||
workItemInfo,
|
||||
callback,
|
||||
state);
|
||||
|
||||
return workItem;
|
||||
}
|
||||
|
||||
private static void ValidateCallback(Delegate callback)
|
||||
{
|
||||
if (callback != null && callback.GetInvocationList().Length > 1)
|
||||
{
|
||||
throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -1,102 +1,69 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region WorkItemInfo class
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for WorkItemInfo.
|
||||
/// </summary>
|
||||
public class WorkItemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the caller's security context
|
||||
/// </summary>
|
||||
private bool _useCallerCallContext;
|
||||
|
||||
/// <summary>
|
||||
/// Use the caller's security context
|
||||
/// </summary>
|
||||
private bool _useCallerHttpContext;
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the state object of a work item
|
||||
/// </summary>
|
||||
private bool _disposeOfStateObjects;
|
||||
|
||||
/// <summary>
|
||||
/// The option to run the post execute
|
||||
/// </summary>
|
||||
private CallToPostExecute _callToPostExecute;
|
||||
|
||||
/// <summary>
|
||||
/// A post execute callback to call when none is provided in
|
||||
/// the QueueWorkItem method.
|
||||
/// </summary>
|
||||
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the work item
|
||||
/// </summary>
|
||||
private WorkItemPriority _workItemPriority;
|
||||
|
||||
public WorkItemInfo()
|
||||
{
|
||||
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
|
||||
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
|
||||
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
|
||||
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
|
||||
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
|
||||
_workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
|
||||
}
|
||||
|
||||
public WorkItemInfo(WorkItemInfo workItemInfo)
|
||||
{
|
||||
_useCallerCallContext = workItemInfo._useCallerCallContext;
|
||||
_useCallerHttpContext = workItemInfo._useCallerHttpContext;
|
||||
_disposeOfStateObjects = workItemInfo._disposeOfStateObjects;
|
||||
_callToPostExecute = workItemInfo._callToPostExecute;
|
||||
_postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback;
|
||||
_workItemPriority = workItemInfo._workItemPriority;
|
||||
}
|
||||
|
||||
public bool UseCallerCallContext
|
||||
{
|
||||
get { return _useCallerCallContext; }
|
||||
set { _useCallerCallContext = value; }
|
||||
}
|
||||
|
||||
public bool UseCallerHttpContext
|
||||
{
|
||||
get { return _useCallerHttpContext; }
|
||||
set { _useCallerHttpContext = value; }
|
||||
}
|
||||
|
||||
public bool DisposeOfStateObjects
|
||||
{
|
||||
get { return _disposeOfStateObjects; }
|
||||
set { _disposeOfStateObjects = value; }
|
||||
}
|
||||
|
||||
public CallToPostExecute CallToPostExecute
|
||||
{
|
||||
get { return _callToPostExecute; }
|
||||
set { _callToPostExecute = value; }
|
||||
}
|
||||
|
||||
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
|
||||
{
|
||||
get { return _postExecuteWorkItemCallback; }
|
||||
set { _postExecuteWorkItemCallback = value; }
|
||||
}
|
||||
|
||||
public WorkItemPriority WorkItemPriority
|
||||
{
|
||||
get { return _workItemPriority; }
|
||||
set { _workItemPriority = value; }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
namespace Amib.Threading
|
||||
{
|
||||
#region WorkItemInfo class
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for WorkItemInfo.
|
||||
/// </summary>
|
||||
public class WorkItemInfo
|
||||
{
|
||||
public WorkItemInfo()
|
||||
{
|
||||
UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
|
||||
UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
|
||||
DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
|
||||
CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
|
||||
PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
|
||||
WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
|
||||
}
|
||||
|
||||
public WorkItemInfo(WorkItemInfo workItemInfo)
|
||||
{
|
||||
UseCallerCallContext = workItemInfo.UseCallerCallContext;
|
||||
UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
|
||||
DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
|
||||
CallToPostExecute = workItemInfo.CallToPostExecute;
|
||||
PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
|
||||
WorkItemPriority = workItemInfo.WorkItemPriority;
|
||||
Timeout = workItemInfo.Timeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's security context
|
||||
/// </summary>
|
||||
public bool UseCallerCallContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to use the caller's HTTP context
|
||||
/// </summary>
|
||||
public bool UseCallerHttpContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set if to dispose of the state object of a work item
|
||||
/// </summary>
|
||||
public bool DisposeOfStateObjects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the run the post execute options
|
||||
/// </summary>
|
||||
public CallToPostExecute CallToPostExecute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the post execute callback
|
||||
/// </summary>
|
||||
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the work item's priority
|
||||
/// </summary>
|
||||
public WorkItemPriority WorkItemPriority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the work item's timout in milliseconds.
|
||||
/// This is a passive timout. When the timout expires the work item won't be actively aborted!
|
||||
/// </summary>
|
||||
public long Timeout { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region WorkItemResultTWrapper class
|
||||
|
||||
internal class WorkItemResultTWrapper<TResult> : IWorkItemResult<TResult>, IInternalWaitableResult
|
||||
{
|
||||
private readonly IWorkItemResult _workItemResult;
|
||||
|
||||
public WorkItemResultTWrapper(IWorkItemResult workItemResult)
|
||||
{
|
||||
_workItemResult = workItemResult;
|
||||
}
|
||||
|
||||
#region IWorkItemResult<TResult> Members
|
||||
|
||||
public TResult GetResult()
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult();
|
||||
}
|
||||
|
||||
public TResult GetResult(int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext);
|
||||
}
|
||||
|
||||
public TResult GetResult(TimeSpan timeout, bool exitContext)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(timeout, exitContext);
|
||||
}
|
||||
|
||||
public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
|
||||
}
|
||||
|
||||
public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle);
|
||||
}
|
||||
|
||||
public TResult GetResult(out Exception e)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(out e);
|
||||
}
|
||||
|
||||
public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e);
|
||||
}
|
||||
|
||||
public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(timeout, exitContext, out e);
|
||||
}
|
||||
|
||||
public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
|
||||
}
|
||||
|
||||
public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
|
||||
{
|
||||
return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e);
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get { return _workItemResult.IsCompleted; }
|
||||
}
|
||||
|
||||
public bool IsCanceled
|
||||
{
|
||||
get { return _workItemResult.IsCanceled; }
|
||||
}
|
||||
|
||||
public object State
|
||||
{
|
||||
get { return _workItemResult.State; }
|
||||
}
|
||||
|
||||
public bool Cancel()
|
||||
{
|
||||
return _workItemResult.Cancel();
|
||||
}
|
||||
|
||||
public bool Cancel(bool abortExecution)
|
||||
{
|
||||
return _workItemResult.Cancel(abortExecution);
|
||||
}
|
||||
|
||||
public WorkItemPriority WorkItemPriority
|
||||
{
|
||||
get { return _workItemResult.WorkItemPriority; }
|
||||
}
|
||||
|
||||
public TResult Result
|
||||
{
|
||||
get { return (TResult)_workItemResult.Result; }
|
||||
}
|
||||
|
||||
public object Exception
|
||||
{
|
||||
get { return (TResult)_workItemResult.Exception; }
|
||||
}
|
||||
|
||||
#region IInternalWorkItemResult Members
|
||||
|
||||
public IWorkItemResult GetWorkItemResult()
|
||||
{
|
||||
return _workItemResult.GetWorkItemResult();
|
||||
}
|
||||
|
||||
public IWorkItemResult<TRes> GetWorkItemResultT<TRes>()
|
||||
{
|
||||
return (IWorkItemResult<TRes>)this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
|
@ -1,512 +1,361 @@
|
|||
// Ami Bar
|
||||
// amibar@gmail.com
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
#region WorkItemsGroup class
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for WorkItemsGroup.
|
||||
/// </summary>
|
||||
public class WorkItemsGroup : IWorkItemsGroup
|
||||
{
|
||||
#region Private members
|
||||
|
||||
private object _lock = new object();
|
||||
/// <summary>
|
||||
/// Contains the name of this instance of SmartThreadPool.
|
||||
/// Can be changed by the user.
|
||||
/// </summary>
|
||||
private string _name = "WorkItemsGroup";
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the SmartThreadPool instance that created this
|
||||
/// WorkItemsGroup.
|
||||
/// </summary>
|
||||
private SmartThreadPool _stp;
|
||||
|
||||
/// <summary>
|
||||
/// The OnIdle event
|
||||
/// </summary>
|
||||
private event WorkItemsGroupIdleHandler _onIdle;
|
||||
|
||||
/// <summary>
|
||||
/// Defines how many work items of this WorkItemsGroup can run at once.
|
||||
/// </summary>
|
||||
private int _concurrency;
|
||||
|
||||
/// <summary>
|
||||
/// Priority queue to hold work items before they are passed
|
||||
/// to the SmartThreadPool.
|
||||
/// </summary>
|
||||
private PriorityQueue _workItemsQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate how many work items are waiting in the SmartThreadPool
|
||||
/// queue.
|
||||
/// This value is used to apply the concurrency.
|
||||
/// </summary>
|
||||
private int _workItemsInStpQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate how many work items are currently running in the SmartThreadPool.
|
||||
/// This value is used with the Cancel, to calculate if we can send new
|
||||
/// work items to the STP.
|
||||
/// </summary>
|
||||
private int _workItemsExecutingInStp = 0;
|
||||
|
||||
/// <summary>
|
||||
/// WorkItemsGroup start information
|
||||
/// </summary>
|
||||
private WIGStartInfo _workItemsGroupStartInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Signaled when all of the WorkItemsGroup's work item completed.
|
||||
/// </summary>
|
||||
private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
|
||||
|
||||
/// <summary>
|
||||
/// A common object for all the work items that this work items group
|
||||
/// generate so we can mark them to cancel in O(1)
|
||||
/// </summary>
|
||||
private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public WorkItemsGroup(
|
||||
SmartThreadPool stp,
|
||||
int concurrency,
|
||||
WIGStartInfo wigStartInfo)
|
||||
{
|
||||
if (concurrency <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero");
|
||||
}
|
||||
_stp = stp;
|
||||
_concurrency = concurrency;
|
||||
_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo);
|
||||
_workItemsQueue = new PriorityQueue();
|
||||
|
||||
// The _workItemsInStpQueue gets the number of currently executing work items,
|
||||
// because once a work item is executing, it cannot be cancelled.
|
||||
_workItemsInStpQueue = _workItemsExecutingInStp;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorkItemsGroup implementation
|
||||
|
||||
/// <summary>
|
||||
/// Get/Set the name of the SmartThreadPool instance
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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, _workItemsGroupStartInfo, callback);
|
||||
EnqueueToSTPNextWorkItem(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)
|
||||
{
|
||||
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority);
|
||||
EnqueueToSTPNextWorkItem(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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
_canceledWorkItemsGroup.IsCanceled = true;
|
||||
_workItemsQueue.Clear();
|
||||
_workItemsInStpQueue = 0;
|
||||
_canceledWorkItemsGroup = new CanceledWorkItemsGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!_workItemsGroupStartInfo.StartSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_workItemsGroupStartInfo.StartSuspended = false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < _concurrency; ++i)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
||||
private void RegisterToWorkItemCompletion(IWorkItemResult wir)
|
||||
{
|
||||
IInternalWorkItemResult iwir = wir as IInternalWorkItemResult;
|
||||
iwir.OnWorkItemStarted += new WorkItemStateCallback(OnWorkItemStartedCallback);
|
||||
iwir.OnWorkItemCompleted += new WorkItemStateCallback(OnWorkItemCompletedCallback);
|
||||
}
|
||||
|
||||
public void OnSTPIsStarting()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (_workItemsGroupStartInfo.StartSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < _concurrency; ++i)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
private object FireOnIdle(object state)
|
||||
{
|
||||
FireOnIdleImpl(_onIdle);
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
|
||||
{
|
||||
if(null == onIdle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Delegate[] delegates = onIdle.GetInvocationList();
|
||||
foreach(WorkItemsGroupIdleHandler eh in delegates)
|
||||
{
|
||||
try
|
||||
{
|
||||
eh(this);
|
||||
}
|
||||
// Ignore exceptions
|
||||
catch{}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWorkItemStartedCallback(WorkItem workItem)
|
||||
{
|
||||
lock(_lock)
|
||||
{
|
||||
++_workItemsExecutingInStp;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWorkItemCompletedCallback(WorkItem workItem)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(null, true);
|
||||
}
|
||||
|
||||
private void EnqueueToSTPNextWorkItem(WorkItem workItem)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(workItem, false);
|
||||
}
|
||||
|
||||
private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
|
||||
{
|
||||
lock(_lock)
|
||||
{
|
||||
// Got here from OnWorkItemCompletedCallback()
|
||||
if (decrementWorkItemsInStpQueue)
|
||||
{
|
||||
--_workItemsInStpQueue;
|
||||
|
||||
if(_workItemsInStpQueue < 0)
|
||||
{
|
||||
_workItemsInStpQueue = 0;
|
||||
}
|
||||
|
||||
--_workItemsExecutingInStp;
|
||||
|
||||
if(_workItemsExecutingInStp < 0)
|
||||
{
|
||||
_workItemsExecutingInStp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If the work item is not null then enqueue it
|
||||
if (null != workItem)
|
||||
{
|
||||
workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;
|
||||
|
||||
RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
|
||||
_workItemsQueue.Enqueue(workItem);
|
||||
//_stp.IncrementWorkItemsCount();
|
||||
|
||||
if ((1 == _workItemsQueue.Count) &&
|
||||
(0 == _workItemsInStpQueue))
|
||||
{
|
||||
_stp.RegisterWorkItemsGroup(this);
|
||||
Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle");
|
||||
_isIdleWaitHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// If the work items queue of the group is empty than quit
|
||||
if (0 == _workItemsQueue.Count)
|
||||
{
|
||||
if (0 == _workItemsInStpQueue)
|
||||
{
|
||||
_stp.UnregisterWorkItemsGroup(this);
|
||||
Trace.WriteLine("WorkItemsGroup " + Name + " is idle");
|
||||
_isIdleWaitHandle.Set();
|
||||
_stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_workItemsGroupStartInfo.StartSuspended)
|
||||
{
|
||||
if (_workItemsInStpQueue < _concurrency)
|
||||
{
|
||||
WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
|
||||
_stp.Enqueue(nextWorkItem, true);
|
||||
++_workItemsInStpQueue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Amib.Threading.Internal
|
||||
{
|
||||
|
||||
#region WorkItemsGroup class
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for WorkItemsGroup.
|
||||
/// </summary>
|
||||
public class WorkItemsGroup : WorkItemsGroupBase
|
||||
{
|
||||
#region Private members
|
||||
|
||||
private readonly object _lock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the SmartThreadPool instance that created this
|
||||
/// WorkItemsGroup.
|
||||
/// </summary>
|
||||
private readonly SmartThreadPool _stp;
|
||||
|
||||
/// <summary>
|
||||
/// The OnIdle event
|
||||
/// </summary>
|
||||
private event WorkItemsGroupIdleHandler _onIdle;
|
||||
|
||||
/// <summary>
|
||||
/// A flag to indicate if the Work Items Group is now suspended.
|
||||
/// </summary>
|
||||
private bool _isSuspended;
|
||||
|
||||
/// <summary>
|
||||
/// Defines how many work items of this WorkItemsGroup can run at once.
|
||||
/// </summary>
|
||||
private int _concurrency;
|
||||
|
||||
/// <summary>
|
||||
/// Priority queue to hold work items before they are passed
|
||||
/// to the SmartThreadPool.
|
||||
/// </summary>
|
||||
private readonly PriorityQueue _workItemsQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate how many work items are waiting in the SmartThreadPool
|
||||
/// queue.
|
||||
/// This value is used to apply the concurrency.
|
||||
/// </summary>
|
||||
private int _workItemsInStpQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate how many work items are currently running in the SmartThreadPool.
|
||||
/// This value is used with the Cancel, to calculate if we can send new
|
||||
/// work items to the STP.
|
||||
/// </summary>
|
||||
private int _workItemsExecutingInStp = 0;
|
||||
|
||||
/// <summary>
|
||||
/// WorkItemsGroup start information
|
||||
/// </summary>
|
||||
private readonly WIGStartInfo _workItemsGroupStartInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Signaled when all of the WorkItemsGroup's work item completed.
|
||||
/// </summary>
|
||||
//private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
|
||||
private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
|
||||
|
||||
/// <summary>
|
||||
/// A common object for all the work items that this work items group
|
||||
/// generate so we can mark them to cancel in O(1)
|
||||
/// </summary>
|
||||
private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public WorkItemsGroup(
|
||||
SmartThreadPool stp,
|
||||
int concurrency,
|
||||
WIGStartInfo wigStartInfo)
|
||||
{
|
||||
if (concurrency <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
"concurrency",
|
||||
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
|
||||
concurrency,
|
||||
#endif
|
||||
"concurrency must be greater than zero");
|
||||
}
|
||||
_stp = stp;
|
||||
_concurrency = concurrency;
|
||||
_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly();
|
||||
_workItemsQueue = new PriorityQueue();
|
||||
Name = "WorkItemsGroup";
|
||||
|
||||
// The _workItemsInStpQueue gets the number of currently executing work items,
|
||||
// because once a work item is executing, it cannot be cancelled.
|
||||
_workItemsInStpQueue = _workItemsExecutingInStp;
|
||||
|
||||
_isSuspended = _workItemsGroupStartInfo.StartSuspended;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WorkItemsGroupBase Overrides
|
||||
|
||||
public override int Concurrency
|
||||
{
|
||||
get { return _concurrency; }
|
||||
set
|
||||
{
|
||||
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>
|
||||
/// WorkItemsGroup start information
|
||||
/// </summary>
|
||||
public override WIGStartInfo WIGStartInfo
|
||||
{
|
||||
get { return _workItemsGroupStartInfo; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the Work Items Group if it was started suspended
|
||||
/// </summary>
|
||||
public override void Start()
|
||||
{
|
||||
// If the Work Items Group already started then quit
|
||||
if (!_isSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isSuspended = false;
|
||||
|
||||
EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
|
||||
}
|
||||
|
||||
public override void Cancel(bool abortExecution)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_canceledWorkItemsGroup.IsCanceled = true;
|
||||
_workItemsQueue.Clear();
|
||||
_workItemsInStpQueue = 0;
|
||||
_canceledWorkItemsGroup = new CanceledWorkItemsGroup();
|
||||
}
|
||||
|
||||
if (abortExecution)
|
||||
{
|
||||
_stp.CancelAbortWorkItemsGroup(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the thread pool to be idle
|
||||
/// </summary>
|
||||
public override bool WaitForIdle(int millisecondsTimeout)
|
||||
{
|
||||
SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
|
||||
return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
|
||||
}
|
||||
|
||||
public override event WorkItemsGroupIdleHandler OnIdle
|
||||
{
|
||||
add { _onIdle += value; }
|
||||
remove { _onIdle -= value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
||||
private void RegisterToWorkItemCompletion(IWorkItemResult wir)
|
||||
{
|
||||
IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
|
||||
iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
|
||||
iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
|
||||
}
|
||||
|
||||
public void OnSTPIsStarting()
|
||||
{
|
||||
if (_isSuspended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnqueueToSTPNextNWorkItem(_concurrency);
|
||||
}
|
||||
|
||||
public void EnqueueToSTPNextNWorkItem(int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
private object FireOnIdle(object state)
|
||||
{
|
||||
FireOnIdleImpl(_onIdle);
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
|
||||
{
|
||||
if(null == onIdle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Delegate[] delegates = onIdle.GetInvocationList();
|
||||
foreach(WorkItemsGroupIdleHandler eh in delegates)
|
||||
{
|
||||
try
|
||||
{
|
||||
eh(this);
|
||||
}
|
||||
catch { } // Suppress exceptions
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWorkItemStartedCallback(WorkItem workItem)
|
||||
{
|
||||
lock(_lock)
|
||||
{
|
||||
++_workItemsExecutingInStp;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWorkItemCompletedCallback(WorkItem workItem)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(null, true);
|
||||
}
|
||||
|
||||
internal override void Enqueue(WorkItem workItem)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(workItem);
|
||||
}
|
||||
|
||||
private void EnqueueToSTPNextWorkItem(WorkItem workItem)
|
||||
{
|
||||
EnqueueToSTPNextWorkItem(workItem, false);
|
||||
}
|
||||
|
||||
private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
|
||||
{
|
||||
lock(_lock)
|
||||
{
|
||||
// Got here from OnWorkItemCompletedCallback()
|
||||
if (decrementWorkItemsInStpQueue)
|
||||
{
|
||||
--_workItemsInStpQueue;
|
||||
|
||||
if(_workItemsInStpQueue < 0)
|
||||
{
|
||||
_workItemsInStpQueue = 0;
|
||||
}
|
||||
|
||||
--_workItemsExecutingInStp;
|
||||
|
||||
if(_workItemsExecutingInStp < 0)
|
||||
{
|
||||
_workItemsExecutingInStp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If the work item is not null then enqueue it
|
||||
if (null != workItem)
|
||||
{
|
||||
workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;
|
||||
|
||||
RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
|
||||
_workItemsQueue.Enqueue(workItem);
|
||||
//_stp.IncrementWorkItemsCount();
|
||||
|
||||
if ((1 == _workItemsQueue.Count) &&
|
||||
(0 == _workItemsInStpQueue))
|
||||
{
|
||||
_stp.RegisterWorkItemsGroup(this);
|
||||
IsIdle = false;
|
||||
_isIdleWaitHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// If the work items queue of the group is empty than quit
|
||||
if (0 == _workItemsQueue.Count)
|
||||
{
|
||||
if (0 == _workItemsInStpQueue)
|
||||
{
|
||||
_stp.UnregisterWorkItemsGroup(this);
|
||||
IsIdle = true;
|
||||
_isIdleWaitHandle.Set();
|
||||
if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
|
||||
{
|
||||
_stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isSuspended)
|
||||
{
|
||||
if (_workItemsInStpQueue < _concurrency)
|
||||
{
|
||||
WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
|
||||
try
|
||||
{
|
||||
_stp.Enqueue(nextWorkItem);
|
||||
}
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
e.GetHashCode();
|
||||
// The STP has been shutdown
|
||||
}
|
||||
|
||||
++_workItemsInStpQueue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -21,18 +21,21 @@
|
|||
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
|
||||
; *
|
||||
[Startup]
|
||||
; Place to create a PID file
|
||||
; If no path if specified then a PID file is not created.
|
||||
; PIDFile = "/tmp/my.pid"
|
||||
|
||||
; Plugin Registry Location
|
||||
; Set path to directory for plugin registry. Information
|
||||
; about the registered repositories and installed plugins
|
||||
; will be stored here
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
RegistryLocation = "."
|
||||
; Plugin Registry Location
|
||||
; Set path to directory for plugin registry. Information
|
||||
; about the registered repositories and installed plugins
|
||||
; will be stored here
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
RegistryLocation = "."
|
||||
|
||||
; Modular configurations
|
||||
; Set path to directory for modular ini files...
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
ConfigDirectory = "/home/opensim/etc/Configs"
|
||||
; Modular configurations
|
||||
; Set path to directory for modular ini files...
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
ConfigDirectory = "/home/opensim/etc/Configs"
|
||||
|
||||
[ServiceList]
|
||||
|
||||
|
|
|
@ -13,19 +13,21 @@
|
|||
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
|
||||
; *
|
||||
[Startup]
|
||||
; Place to create a PID file
|
||||
; If no path if specified then a PID file is not created.
|
||||
; PIDFile = "/tmp/my.pid"
|
||||
|
||||
; Plugin Registry Location
|
||||
; Set path to directory for plugin registry. Information
|
||||
; about the registered repositories and installed plugins
|
||||
; will be stored here
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
RegistryLocation = "."
|
||||
; Plugin Registry Location
|
||||
; Set path to directory for plugin registry. Information
|
||||
; about the registered repositories and installed plugins
|
||||
; will be stored here
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
RegistryLocation = "."
|
||||
|
||||
|
||||
; Modular configurations
|
||||
; Set path to directory for modular ini files...
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
ConfigDirectory = "/home/opensim/etc/Configs"
|
||||
; Modular configurations
|
||||
; Set path to directory for modular ini files...
|
||||
; The Robust.exe process must have R/W access to the location
|
||||
ConfigDirectory = "/home/opensim/etc/Configs"
|
||||
|
||||
[ServiceList]
|
||||
AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector"
|
||||
|
|
|
@ -3055,6 +3055,7 @@
|
|||
<Match path="Avatar/AvatarFactory/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Avatar/Friends/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Avatar/Inventory/Archiver/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Avatar/Inventory/Transfer/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Framework/InventoryAccess/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="Scripting/VectorRender/Tests" pattern="*.cs" recurse="true"/>
|
||||
<Match path="World/Archiver/Tests" pattern="*.cs" recurse="true"/>
|
||||
|
|
Loading…
Reference in New Issue