From 3ff7391495271fed152aadc7a588ae976e09bafc Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 29 Apr 2013 00:55:34 +0100 Subject: Some more pieces of Avination's ban system - if an avatar isn't allowed on any parcel in the sim, keep them out entirely. --- OpenSim/Region/Framework/Scenes/Scene.cs | 95 +++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 69fe137..829a7e9 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -3693,7 +3693,7 @@ namespace OpenSim.Region.Framework.Scenes //On login test land permisions if (vialogin) { - if (land != null && !TestLandRestrictions(agent, land, out reason)) + if (land != null && !TestLandRestrictions(agent.AgentID, out reason, ref agent.startpos.X, ref agent.startpos.Y)) { return false; } @@ -3868,20 +3868,37 @@ namespace OpenSim.Region.Framework.Scenes return true; } - private bool TestLandRestrictions(AgentCircuitData agent, ILandObject land, out string reason) + public bool TestLandRestrictions(UUID agentID, out string reason, ref float posX, ref float posY) { - bool banned = land.IsBannedFromLand(agent.AgentID); - bool restricted = land.IsRestrictedFromLand(agent.AgentID); + if (posX < 0) + posX = 0; + else if (posX >= 256) + posX = 255.999f; + if (posY < 0) + posY = 0; + else if (posY >= 256) + posY = 255.999f; + + reason = String.Empty; + if (Permissions.IsGod(agentID)) + return true; + + ILandObject land = LandChannel.GetLandObject(posX, posY); + if (land == null) + return false; + + bool banned = land.IsBannedFromLand(agentID); + bool restricted = land.IsRestrictedFromLand(agentID); if (banned || restricted) { - ILandObject nearestParcel = GetNearestAllowedParcel(agent.AgentID, agent.startpos.X, agent.startpos.Y); + ILandObject nearestParcel = GetNearestAllowedParcel(agentID, posX, posY); if (nearestParcel != null) { //Move agent to nearest allowed Vector3 newPosition = GetParcelCenterAtGround(nearestParcel); - agent.startpos.X = newPosition.X; - agent.startpos.Y = newPosition.Y; + posX = newPosition.X; + posY = newPosition.Y; } else { @@ -5466,6 +5483,8 @@ namespace OpenSim.Region.Framework.Scenes /// public bool QueryAccess(UUID agentID, Vector3 position, out string reason) { + reason = "You are banned from the region"; + if (EntityTransferModule.IsInTransit(agentID)) { reason = "Agent is still in transit from this region"; @@ -5477,6 +5496,12 @@ namespace OpenSim.Region.Framework.Scenes return false; } + if (Permissions.IsGod(agentID)) + { + reason = String.Empty; + return true; + } + // FIXME: Root agent count is currently known to be inaccurate. This forces a recount before we check. // However, the long term fix is to make sure root agent count is always accurate. m_sceneGraph.RecalculateStats(); @@ -5497,6 +5522,41 @@ namespace OpenSim.Region.Framework.Scenes } } + ScenePresence presence = GetScenePresence(agentID); + IClientAPI client = null; + AgentCircuitData aCircuit = null; + + if (presence != null) + { + client = presence.ControllingClient; + if (client != null) + aCircuit = client.RequestClientInfo(); + } + + // We may be called before there is a presence or a client. + // Fake AgentCircuitData to keep IAuthorizationModule smiling + if (client == null) + { + aCircuit = new AgentCircuitData(); + aCircuit.AgentID = agentID; + aCircuit.firstname = String.Empty; + aCircuit.lastname = String.Empty; + } + + try + { + if (!AuthorizeUser(aCircuit, out reason)) + { + // m_log.DebugFormat("[SCENE]: Denying access for {0}", agentID); + return false; + } + } + catch (Exception e) + { + m_log.DebugFormat("[SCENE]: Exception authorizing agent: {0} "+ e.StackTrace, e.Message); + return false; + } + if (position == Vector3.Zero) // Teleport { if (!RegionInfo.EstateSettings.AllowDirectTeleport) @@ -5530,6 +5590,27 @@ namespace OpenSim.Region.Framework.Scenes } } } + + float posX = 128.0f; + float posY = 128.0f; + + if (!TestLandRestrictions(agentID, out reason, ref posX, ref posY)) + { + // m_log.DebugFormat("[SCENE]: Denying {0} because they are banned on all parcels", agentID); + return false; + } + } + else // Walking + { + ILandObject land = LandChannel.GetLandObject(position.X, position.Y); + if (land == null) + return false; + + bool banned = land.IsBannedFromLand(agentID); + bool restricted = land.IsRestrictedFromLand(agentID); + + if (banned || restricted) + return false; } reason = String.Empty; -- cgit v1.1 From 890cb6a29373a54dde2d06b13e42d07676710dc2 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sat, 27 Apr 2013 14:00:58 -0700 Subject: BulletSim: complete BSShape classes. --- .../Physics/BulletSPlugin/BSShapeCollection.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 327 ++++++++++++++++++++- .../Region/Physics/BulletSPlugin/BulletSimTODO.txt | 6 +- 3 files changed, 316 insertions(+), 19 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index bc26460..0f9b3c3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -499,7 +499,7 @@ public sealed class BSShapeCollection : IDisposable } // return 'true' if this shape description does not include any cutting or twisting. - private bool PrimHasNoCuts(PrimitiveBaseShape pbs) + public static bool PrimHasNoCuts(PrimitiveBaseShape pbs) { return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 && pbs.ProfileHollow == 0 diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index dd5ae1a..e427dbc 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -31,6 +31,7 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; using OMV = OpenMetaverse; @@ -73,7 +74,7 @@ public abstract class BSShape if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) { // Getting a reference to a compound shape gets you the compound shape with the root prim shape added - ret = BSShapeCompound.GetReference(prim); + ret = BSShapeCompound.GetReference(physicsScene, prim); physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); } @@ -92,6 +93,7 @@ public abstract class BSShape } private static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { + // TODO: work needed here!! BSShapeMesh.GetReference(physicsScene, forceRebuild, prim); BSShapeHull.GetReference(physicsScene, forceRebuild, prim); return null; @@ -209,7 +211,7 @@ public class BSShapeNative : BSShape public class BSShapeMesh : BSShape { private static string LogHeader = "[BULLETSIM SHAPE MESH]"; - private static Dictionary Meshes = new Dictionary(); + public static Dictionary Meshes = new Dictionary(); public BSShapeMesh(BulletShape pShape) : base(pShape) { @@ -219,10 +221,10 @@ public class BSShapeMesh : BSShape float lod; System.UInt64 newMeshKey = BSShapeCollection.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - physicsScene.DetailLog("{0},BSShapeMesh,create,oldKey={1},newKey={2},size={3},lod={4}", + physicsScene.DetailLog("{0},BSShapeMesh,getReference,oldKey={1},newKey={2},size={3},lod={4}", prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"), prim.Size, lod); - BSShapeMesh retMesh; + BSShapeMesh retMesh = new BSShapeMesh(new BulletShape()); lock (Meshes) { if (Meshes.TryGetValue(newMeshKey, out retMesh)) @@ -233,13 +235,17 @@ public class BSShapeMesh : BSShape else { // An instance of this mesh has not been created. Build and remember same. - BulletShape newShape = CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); - // Take evasive action if the mesh was not constructed. - newShape = BSShapeCollection.VerifyMeshCreated(physicsScene, newShape, prim); + BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); - retMesh = new BSShapeMesh(newShape); + // Check to see if mesh was created (might require an asset). + newShape = BSShapeCollection.VerifyMeshCreated(physicsScene, newShape, prim); + if (newShape.type == BSPhysicsShapeType.SHAPE_MESH) + { + // If a mesh was what was created, remember the built shape for later sharing. + Meshes.Add(newMeshKey, retMesh); + } - Meshes.Add(newMeshKey, retMesh); + retMesh.physShapeInfo = newShape; } } return retMesh; @@ -252,8 +258,28 @@ public class BSShapeMesh : BSShape // TODO: schedule aging and destruction of unused meshes. } } + // Loop through all the known meshes and return the description based on the physical address. + public static bool TryGetMeshByPtr(BulletShape pShape, out BSShapeMesh outMesh) + { + bool ret = false; + BSShapeMesh foundDesc = null; + lock (Meshes) + { + foreach (BSShapeMesh sm in Meshes.Values) + { + if (sm.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sm; + ret = true; + break; + } - private static BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, + } + } + outMesh = foundDesc; + return ret; + } + private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { BulletShape newShape = null; @@ -326,32 +352,301 @@ public class BSShapeMesh : BSShape public class BSShapeHull : BSShape { private static string LogHeader = "[BULLETSIM SHAPE HULL]"; - private static Dictionary Hulls = new Dictionary(); + public static Dictionary Hulls = new Dictionary(); public BSShapeHull(BulletShape pShape) : base(pShape) { } public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { - return new BSShapeNull(); + float lod; + System.UInt64 newHullKey = BSShapeCollection.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + physicsScene.DetailLog("{0},BSShapeHull,getReference,oldKey={1},newKey={2},size={3},lod={4}", + prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"), prim.Size, lod); + + BSShapeHull retHull = new BSShapeHull(new BulletShape()); + lock (Hulls) + { + if (Hulls.TryGetValue(newHullKey, out retHull)) + { + // The mesh has already been created. Return a new reference to same. + retHull.IncrementReference(); + } + else + { + // An instance of this mesh has not been created. Build and remember same. + BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod); + + // Check to see if mesh was created (might require an asset). + newShape = BSShapeCollection.VerifyMeshCreated(physicsScene, newShape, prim); + if (newShape.type == BSPhysicsShapeType.SHAPE_MESH) + { + // If a mesh was what was created, remember the built shape for later sharing. + Hulls.Add(newHullKey, retHull); + } + + retHull = new BSShapeHull(newShape); + retHull.physShapeInfo = newShape; + } + } + return retHull; } public override void Dereference(BSScene physicsScene) { } + List m_hulls; + private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, + PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + { + BulletShape newShape = new BulletShape(); + IntPtr hullPtr = IntPtr.Zero; + + if (BSParam.ShouldUseBulletHACD) + { + physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID); + BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); + + if (meshShape.physShapeInfo.HasPhysicalShape) + { + HACDParams parms; + parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; + parms.minClusters = BSParam.BHullMinClusters; + parms.compacityWeight = BSParam.BHullCompacityWeight; + parms.volumeWeight = BSParam.BHullVolumeWeight; + parms.concavity = BSParam.BHullConcavity; + parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); + parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); + parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); + parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); + + physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); + newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); + physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); + } + // Now done with the mesh shape. + meshShape.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); + } + if (!newShape.HasPhysicalShape) + { + // Build a new hull in the physical world. + // Pass true for physicalness as this prevents the creation of bounding box which is not needed + IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); + if (meshData != null) + { + int[] indices = meshData.getIndexListAsInt(); + List vertices = meshData.getVertexList(); + + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) + { + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); + } + + uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; + if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) + { + // Simple primitive shapes we know are convex so they are better implemented with + // fewer hulls. + // Check for simple shape (prim without cuts) and reduce split parameter if so. + if (BSShapeCollection.PrimHasNoCuts(pbs)) + { + maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; + } + } + + // setup and do convex hull conversion + m_hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + dcomp.mDepth = maxDepthSplit; + dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; + dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; + dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; + dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", + BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); + + // Convert the vertices and indices for passing to unmanaged. + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = m_hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in m_hulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in m_hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + // create the hull data structure in Bullet + newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); + } + newShape.shapeKey = newHullKey; + } + return newShape; + } + // Callback from convex hull creater with a newly created hull. + // Just add it to our collection of hulls for this shape. + private void HullReturn(ConvexResult result) + { + m_hulls.Add(result); + return; + } + // Loop through all the known hulls and return the description based on the physical address. + public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeHull outHull) + { + bool ret = false; + BSShapeHull foundDesc = null; + lock (Hulls) + { + foreach (BSShapeHull sh in Hulls.Values) + { + if (sh.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sh; + ret = true; + break; + } + + } + } + outHull = foundDesc; + return ret; + } } + // ============================================================================================================ public class BSShapeCompound : BSShape { private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; - public BSShapeCompound() : base() + public BSShapeCompound(BulletShape pShape) : base(pShape) { } - public static BSShape GetReference(BSPhysObject prim) + public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim) { - return new BSShapeNull(); + // Compound shapes are not shared so a new one is created every time. + return new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene, prim)); + } + // Dereferencing a compound shape releases the hold on all the child shapes. + public override void Dereference(BSScene physicsScene) + { + if (!physicsScene.PE.IsCompound(physShapeInfo)) + { + // Failed the sanity check!! + physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", + LogHeader, physShapeInfo.type, physShapeInfo.AddrString); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", + BSScene.DetailLogZero, physShapeInfo.type, physShapeInfo.AddrString); + return; + } + + int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", + BSScene.DetailLogZero, physShapeInfo, numChildren); + + // Loop through all the children dereferencing each. + for (int ii = numChildren - 1; ii >= 0; ii--) + { + BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); + DereferenceAnonCollisionShape(physicsScene, childShape); + } + physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); + } + private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene, BSPhysObject prim) + { + BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false); + return cShape; + } + // Sometimes we have a pointer to a collision shape but don't know what type it is. + // Figure out type and call the correct dereference routine. + // Called at taint-time. + private void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape) + { + BSShapeMesh meshDesc; + if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc)) + { + meshDesc.Dereference(physicsScene); + } + else + { + BSShapeHull hullDesc; + if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc)) + { + hullDesc.Dereference(physicsScene); + } + else + { + if (physicsScene.PE.IsCompound(pShape)) + { + BSShapeCompound recursiveCompound = new BSShapeCompound(pShape); + recursiveCompound.Dereference(physicsScene); + } + else + { + if (physicsScene.PE.IsNativeShape(pShape)) + { + BSShapeNative nativeShape = new BSShapeNative(pShape); + nativeShape.Dereference(physicsScene); + } + } + } + } } - public override void Dereference(BSScene physicsScene) { } } // ============================================================================================================ diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index 1284ae7..c67081a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt @@ -11,6 +11,8 @@ Deleting a linkset while standing on the root will leave the physical shape of t Linkset child rotations. Nebadon spiral tube has middle sections which are rotated wrong. Select linked spiral tube. Delink and note where the middle section ends up. +Refarb compound linkset creation to create a pseudo-root for center-of-mass + Let children change their shape to physical indendently and just add shapes to compound Vehicle angular vertical attraction vehicle angular banking Center-of-gravity @@ -27,14 +29,13 @@ llLookAt Avatars walking up stairs (HALF DONE) Avatar movement flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE) - walking up stairs is not calibrated correctly (stairs out of Kepler cabin) + walking up stairs is not calibrated correctly (stairs out of Kepler cabin) (DONE) avatar capsule rotation completed (NOT DONE - Bullet's capsule shape is not the solution) Vehicle script tuning/debugging Avanti speed script Weapon shooter script Move material definitions (friction, ...) into simulator. Add material densities to the material types. -Terrain detail: double terrain mesh detail One sided meshes? Should terrain be built into a closed shape? When meshes get partially wedged into the terrain, they cannot push themselves out. It is possible that Bullet processes collisions whether entering or leaving a mesh. @@ -347,4 +348,5 @@ Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE. DONE 20130120: BulletSim properly applies force in vehicle relative coordinates. Nebadon vehicles turning funny in arena (DONE) Lock axis (DONE 20130401) +Terrain detail: double terrain mesh detail (DONE) -- cgit v1.1 From e5582939fd8d78b61c6f1eeda6de45d94f4b4926 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sun, 28 Apr 2013 14:44:21 -0700 Subject: BulletSim: massive refactor of shape classes. Removed shape specific code from BSShapeCollection. Using BSShape* classes to hold references to shape. Simplified shape dependency callbacks. Remove 'PreferredShape' methods and have each class specify shape type. Disable compound shape linkset for a later commit that will simplify linkset implementation. --- OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs | 6 +- OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs | 4 +- .../Physics/BulletSPlugin/BSActorAvatarMove.cs | 4 +- .../Region/Physics/BulletSPlugin/BSActorHover.cs | 4 +- .../Physics/BulletSPlugin/BSActorLockAxis.cs | 4 +- .../Physics/BulletSPlugin/BSActorMoveToTarget.cs | 4 +- .../Physics/BulletSPlugin/BSActorSetForce.cs | 4 +- .../Physics/BulletSPlugin/BSActorSetTorque.cs | 4 +- OpenSim/Region/Physics/BulletSPlugin/BSActors.cs | 6 +- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 19 +- OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 4 +- OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs | 9 +- .../Physics/BulletSPlugin/BSLinksetCompound.cs | 35 +- .../Physics/BulletSPlugin/BSLinksetConstraints.cs | 4 +- .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 11 +- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 34 +- .../Region/Physics/BulletSPlugin/BSPrimLinkable.cs | 9 +- .../Physics/BulletSPlugin/BSShapeCollection.cs | 966 ++------------------- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 203 +++-- .../Region/Physics/BulletSPlugin/BulletSimData.cs | 6 +- 20 files changed, 290 insertions(+), 1050 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs index fdf2cb9..8a22bc7 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs @@ -79,7 +79,7 @@ private sealed class BulletShapeUnman : BulletShape : base() { ptr = xx; - type = typ; + shapeType = typ; } public override bool HasPhysicalShape { @@ -91,7 +91,7 @@ private sealed class BulletShapeUnman : BulletShape } public override BulletShape Clone() { - return new BulletShapeUnman(ptr, type); + return new BulletShapeUnman(ptr, shapeType); } public override bool ReferenceSame(BulletShape other) { @@ -375,7 +375,7 @@ public override BulletShape DuplicateCollisionShape(BulletWorld world, BulletSha { BulletWorldUnman worldu = world as BulletWorldUnman; BulletShapeUnman srcShapeu = srcShape as BulletShapeUnman; - return new BulletShapeUnman(BSAPICPP.DuplicateCollisionShape2(worldu.ptr, srcShapeu.ptr, id), srcShape.type); + return new BulletShapeUnman(BSAPICPP.DuplicateCollisionShape2(worldu.ptr, srcShapeu.ptr, id), srcShape.shapeType); } public override bool DeleteCollisionShape(BulletWorld world, BulletShape shape) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs index b37265a..1ef8b17 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs @@ -85,7 +85,7 @@ private sealed class BulletShapeXNA : BulletShape : base() { shape = xx; - type = typ; + shapeType = typ; } public override bool HasPhysicalShape { @@ -97,7 +97,7 @@ private sealed class BulletShapeXNA : BulletShape } public override BulletShape Clone() { - return new BulletShapeXNA(shape, type); + return new BulletShapeXNA(shape, shapeType); } public override bool ReferenceSame(BulletShape other) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs index bd5ee0b1..ac05979 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs @@ -87,8 +87,8 @@ public class BSActorAvatarMove : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs index 92ace66..3630ca8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorHover.cs @@ -87,8 +87,8 @@ public class BSActorHover : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs index 09ee32b..6059af5 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs @@ -85,8 +85,8 @@ public class BSActorLockAxis : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { if (LockAxisConstraint != null) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs index 56aacc5..1b598fd 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorMoveToTarget.cs @@ -88,8 +88,8 @@ public class BSActorMoveToTarget : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the moveToTarget since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs index 3ad138d..c0f40fd 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetForce.cs @@ -89,8 +89,8 @@ public class BSActorSetForce : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs index 7a791ec..b3806e1 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorSetTorque.cs @@ -89,8 +89,8 @@ public class BSActorSetTorque : BSActor // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. // Called at taint-time. - // BSActor.RemoveBodyDependencies() - public override void RemoveBodyDependencies() + // BSActor.RemoveDependencies() + public override void RemoveDependencies() { // Nothing to do for the hoverer since it is all software at pre-step action time. } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs index 12a8817..5e3f1c4 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs @@ -106,9 +106,9 @@ public class BSActorCollection { ForEachActor(a => a.Refresh()); } - public void RemoveBodyDependencies() + public void RemoveDependencies() { - ForEachActor(a => a.RemoveBodyDependencies()); + ForEachActor(a => a.RemoveDependencies()); } } @@ -154,7 +154,7 @@ public abstract class BSActor public abstract void Refresh(); // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). // Register a prestep action to restore physical requirements before the next simulation step. - public abstract void RemoveBodyDependencies(); + public abstract void RemoveDependencies(); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index a0d58d3..da23a262 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -123,8 +123,8 @@ public sealed class BSCharacter : BSPhysObject { PhysicsScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); PhysBody.Clear(); - PhysicsScene.Shapes.DereferenceShape(PhysShape, null /* bodyCallback */); - PhysShape.Clear(); + PhysShape.Dereference(PhysicsScene); + PhysShape = new BSShapeNull(); }); } @@ -146,8 +146,8 @@ public sealed class BSCharacter : BSPhysObject Flying = _flying; PhysicsScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); - PhysicsScene.PE.SetMargin(PhysShape, PhysicsScene.Params.collisionMargin); - PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); + PhysicsScene.PE.SetMargin(PhysShape.physShapeInfo, PhysicsScene.Params.collisionMargin); + PhysicsScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); if (BSParam.CcdMotionThreshold > 0f) { @@ -205,9 +205,9 @@ public sealed class BSCharacter : BSPhysObject PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() { - if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) + if (PhysBody.HasPhysicalBody && PhysShape.physShapeInfo.HasPhysicalShape) { - PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); + PhysicsScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); UpdatePhysicalMassProperties(RawMass, true); // Make sure this change appears as a property update event PhysicsScene.PE.PushUpdate(PhysBody); @@ -221,11 +221,6 @@ public sealed class BSCharacter : BSPhysObject { set { BaseShape = value; } } - // I want the physics engine to make an avatar capsule - public override BSPhysicsShapeType PreferredPhysicalShape - { - get {return BSPhysicsShapeType.SHAPE_CAPSULE; } - } public override bool Grabbed { set { _grabbed = value; } @@ -381,7 +376,7 @@ public sealed class BSCharacter : BSPhysObject } public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) { - OMV.Vector3 localInertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); + OMV.Vector3 localInertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); PhysicsScene.PE.SetMassProps(PhysBody, physMass, localInertia); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 612c68b..e2e807e 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -625,7 +625,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Vehicles report collision events so we know when it's on the ground m_physicsScene.PE.AddToCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); - ControllingPrim.Inertia = m_physicsScene.PE.CalculateLocalInertia(ControllingPrim.PhysShape, m_vehicleMass); + ControllingPrim.Inertia = m_physicsScene.PE.CalculateLocalInertia(ControllingPrim.PhysShape.physShapeInfo, m_vehicleMass); m_physicsScene.PE.SetMassProps(ControllingPrim.PhysBody, m_vehicleMass, ControllingPrim.Inertia); m_physicsScene.PE.UpdateInertiaTensor(ControllingPrim.PhysBody); @@ -649,7 +649,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } // BSActor.RemoveBodyDependencies - public override void RemoveBodyDependencies() + public override void RemoveDependencies() { Refresh(); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 4ece1eb..df1dd34 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -93,13 +93,6 @@ public abstract class BSLinkset // to the physical representation is done via the tainting mechenism. protected object m_linksetActivityLock = new Object(); - // Some linksets have a preferred physical shape. - // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected. - public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPrimLinkable requestor) - { - return BSPhysicsShapeType.SHAPE_UNKNOWN; - } - // We keep the prim's mass in the linkset structure since it could be dependent on other prims public float LinksetMass { get; protected set; } @@ -263,7 +256,7 @@ public abstract class BSLinkset // This is called when the root body is changing. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public abstract bool RemoveBodyDependencies(BSPrimLinkable child); + public abstract bool RemoveDependencies(BSPrimLinkable child); // ================================================================ protected virtual float ComputeLinksetMass() diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index e05562a..a20bbc3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -98,19 +98,6 @@ public sealed class BSLinksetCompound : BSLinkset { } - // For compound implimented linksets, if there are children, use compound shape for the root. - public override BSPhysicsShapeType PreferredPhysicalShape(BSPrimLinkable requestor) - { - // Returning 'unknown' means we don't have a preference. - BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; - if (IsRoot(requestor) && HasAnyChildren) - { - ret = BSPhysicsShapeType.SHAPE_COMPOUND; - } - // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); - return ret; - } - // When physical properties are changed the linkset needs to recalculate // its internal properties. public override void Refresh(BSPrimLinkable requestor) @@ -218,22 +205,22 @@ public sealed class BSLinksetCompound : BSLinkset // and that is caused by us updating the object. if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) { - // Find the physical instance of the child - if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape)) + // Find the physical instance of the child + if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo)) { // It is possible that the linkset is still under construction and the child is not yet // inserted into the compound shape. A rebuild of the linkset in a pre-step action will // build the whole thing with the new position or rotation. // The index must be checked because Bullet references the child array but does no validity // checking of the child index passed. - int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape); + int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo); if (updated.LinksetChildIndex < numLinksetChildren) { - BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, updated.LinksetChildIndex); + BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex); if (linksetChildShape.HasPhysicalShape) { // Found the child shape within the compound shape - PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, updated.LinksetChildIndex, + PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex, updated.RawPosition - LinksetRoot.RawPosition, updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation), true /* shouldRecalculateLocalAabb */); @@ -278,7 +265,7 @@ public sealed class BSLinksetCompound : BSLinkset // Since we don't keep in world relationships, do nothing unless it's a child changing. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public override bool RemoveBodyDependencies(BSPrimLinkable child) + public override bool RemoveDependencies(BSPrimLinkable child) { bool ret = false; @@ -404,11 +391,12 @@ public sealed class BSLinksetCompound : BSLinkset { try { + /* // Suppress rebuilding while rebuilding. (We know rebuilding is on only one thread.) Rebuilding = true; // Cause the root shape to be rebuilt as a compound object with just the root in it - LinksetRoot.ForceBodyShapeRebuild(true /* inTaintTime */); + LinksetRoot.ForceBodyShapeRebuild(true /* inTaintTime ); // The center of mass for the linkset is the geometric center of the group. // Compute a displacement for each component so it is relative to the center-of-mass. @@ -430,10 +418,10 @@ public sealed class BSLinksetCompound : BSLinkset LinksetRoot.ForcePosition = LinksetRoot.RawPosition; // Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM - PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0 /* childIndex */, + PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, 0 /* childIndex , -centerDisplacement, OMV.Quaternion.Identity, // LinksetRoot.RawOrientation, - false /* shouldRecalculateLocalAabb (is done later after linkset built) */); + false /* shouldRecalculateLocalAabb (is done later after linkset built) ); DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}", LinksetRoot.LocalID, centerOfMassW, LinksetRoot.RawPosition, centerDisplacement); @@ -501,6 +489,7 @@ public sealed class BSLinksetCompound : BSLinkset // Enable the physical position updator to return the position and rotation of the root shape PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); + */ } finally { @@ -508,7 +497,7 @@ public sealed class BSLinksetCompound : BSLinkset } // See that the Aabb surrounds the new shape - PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape); + PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo); } } } \ No newline at end of file diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs index 6d252ca..1811772 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -93,11 +93,11 @@ public sealed class BSLinksetConstraints : BSLinkset // up to rebuild the constraints before the next simulation step. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public override bool RemoveBodyDependencies(BSPrimLinkable child) + public override bool RemoveDependencies(BSPrimLinkable child) { bool ret = false; - DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", + DetailLog("{0},BSLinksetConstraint.RemoveDependencies,removeChildrenForRoot,rID={1},rBody={2}", child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString); lock (m_linksetActivityLock) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 309d004..b6eb619 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -88,7 +88,7 @@ public abstract class BSPhysObject : PhysicsActor // We don't have any physical representation yet. PhysBody = new BulletBody(localID); - PhysShape = new BulletShape(); + PhysShape = new BSShapeNull(); PrimAssetState = PrimAssetCondition.Unknown; @@ -138,7 +138,7 @@ public abstract class BSPhysObject : PhysicsActor // Reference to the physical body (btCollisionObject) of this object public BulletBody PhysBody; // Reference to the physical shape (btCollisionShape) of this object - public BulletShape PhysShape; + public BSShape PhysShape; // The physical representation of the prim might require an asset fetch. // The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'. @@ -151,13 +151,6 @@ public abstract class BSPhysObject : PhysicsActor // The objects base shape information. Null if not a prim type shape. public PrimitiveBaseShape BaseShape { get; protected set; } - // Some types of objects have preferred physical representations. - // Returns SHAPE_UNKNOWN if there is no preference. - public virtual BSPhysicsShapeType PreferredPhysicalShape - { - get { return BSPhysicsShapeType.SHAPE_UNKNOWN; } - } - // When the physical properties are updated, an EntityProperty holds the update values. // Keep the current and last EntityProperties to enable computation of differences // between the current update and the previous values. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 4bc266b..5d12338 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -134,8 +134,8 @@ public class BSPrim : BSPhysObject // If there are physical body and shape, release my use of same. PhysicsScene.Shapes.DereferenceBody(PhysBody, null); PhysBody.Clear(); - PhysicsScene.Shapes.DereferenceShape(PhysShape, null); - PhysShape.Clear(); + PhysShape.Dereference(PhysicsScene); + PhysShape = new BSShapeNull(); }); } @@ -161,25 +161,13 @@ public class BSPrim : BSPhysObject ForceBodyShapeRebuild(false); } } - // 'unknown' says to choose the best type - public override BSPhysicsShapeType PreferredPhysicalShape - { get { return BSPhysicsShapeType.SHAPE_UNKNOWN; } } - public override bool ForceBodyShapeRebuild(bool inTaintTime) { - if (inTaintTime) + PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() { _mass = CalculateMass(); // changing the shape changes the mass CreateGeomAndObject(true); - } - else - { - PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", delegate() - { - _mass = CalculateMass(); // changing the shape changes the mass - CreateGeomAndObject(true); - }); - } + }); return true; } public override bool Grabbed { @@ -462,7 +450,7 @@ public class BSPrim : BSPhysObject Gravity = ComputeGravity(Buoyancy); PhysicsScene.PE.SetGravity(PhysBody, Gravity); - Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); + Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); PhysicsScene.PE.SetMassProps(PhysBody, physMass, Inertia); PhysicsScene.PE.UpdateInertiaTensor(PhysBody); @@ -805,7 +793,8 @@ public class BSPrim : BSPhysObject PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", - LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); + LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), + CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); } // "Making dynamic" means changing to and from static. @@ -1463,12 +1452,13 @@ public class BSPrim : BSPhysObject // Create the correct physical representation for this type of object. // Updates base.PhysBody and base.PhysShape with the new information. // Ignore 'forceRebuild'. 'GetBodyAndShape' makes the right choices and changes of necessary. - PhysicsScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysicsScene.World, this, null, delegate(BulletBody dBody) + PhysicsScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysicsScene.World, this, delegate(BulletBody pBody, BulletShape pShape) { // Called if the current prim body is about to be destroyed. // Remove all the physical dependencies on the old body. // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) - RemoveBodyDependencies(); + // Note: this virtual function is overloaded by BSPrimLinkable to remove linkset constraints. + RemoveDependencies(); }); // Make sure the properties are set on the new object @@ -1477,9 +1467,9 @@ public class BSPrim : BSPhysObject } // Called at taint-time - protected virtual void RemoveBodyDependencies() + protected virtual void RemoveDependencies() { - PhysicalActors.RemoveBodyDependencies(); + PhysicalActors.RemoveDependencies(); } // The physics engine says that properties have updated. Update same and inform diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs index 28242d4..81104ec 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs @@ -61,9 +61,6 @@ public class BSPrimLinkable : BSPrimDisplaced base.Destroy(); } - public override BSPhysicsShapeType PreferredPhysicalShape - { get { return Linkset.PreferredPhysicalShape(this); } } - public override void link(Manager.PhysicsActor obj) { BSPrimLinkable parent = obj as BSPrimLinkable; @@ -149,10 +146,10 @@ public class BSPrimLinkable : BSPrimDisplaced } // Body is being taken apart. Remove physical dependencies and schedule a rebuild. - protected override void RemoveBodyDependencies() + protected override void RemoveDependencies() { - Linkset.RemoveBodyDependencies(this); - base.RemoveBodyDependencies(); + Linkset.RemoveDependencies(this); + base.RemoveDependencies(); } public override void UpdateProperties(EntityProperties entprop) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 0f9b3c3..3c23509 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -38,38 +38,15 @@ public sealed class BSShapeCollection : IDisposable { private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; - private BSScene PhysicsScene { get; set; } + private BSScene m_physicsScene { get; set; } private Object m_collectionActivityLock = new Object(); - // Description of a Mesh - private struct MeshDesc - { - public BulletShape shape; - public int referenceCount; - public DateTime lastReferenced; - public UInt64 shapeKey; - } - - // Description of a hull. - // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations. - private struct HullDesc - { - public BulletShape shape; - public int referenceCount; - public DateTime lastReferenced; - public UInt64 shapeKey; - } - - // The sharable set of meshes and hulls. Indexed by their shape hash. - private Dictionary Meshes = new Dictionary(); - private Dictionary Hulls = new Dictionary(); - private bool DDetail = false; public BSShapeCollection(BSScene physScene) { - PhysicsScene = physScene; + m_physicsScene = physScene; // Set the next to 'true' for very detailed shape update detailed logging (detailed details?) // While detailed debugging is still active, this is better than commenting out all the // DetailLog statements. When debugging slows down, this and the protected logging @@ -86,22 +63,18 @@ public sealed class BSShapeCollection : IDisposable // Mostly used for changing bodies out from under Linksets. // Useful for other cases where parameters need saving. // Passing 'null' says no callback. - public delegate void ShapeDestructionCallback(BulletShape shape); - public delegate void BodyDestructionCallback(BulletBody body); + public delegate void PhysicalDestructionCallback(BulletBody pBody, BulletShape pShape); // Called to update/change the body and shape for an object. - // First checks the shape and updates that if necessary then makes - // sure the body is of the right type. + // The object has some shape and body on it. Here we decide if that is the correct shape + // for the current state of the object (static/dynamic/...). + // If bodyCallback is not null, it is called if either the body or the shape are changed + // so dependencies (like constraints) can be removed before the physical object is dereferenced. // Return 'true' if either the body or the shape changed. - // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before - // the current shape or body is destroyed. This allows the caller to remove any - // higher level dependencies on the shape or body. Mostly used for LinkSets to - // remove the physical constraints before the body is destroyed. - // Called at taint-time!! - public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, - ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) + // Called at taint-time. + public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback) { - PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); + m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); bool ret = false; @@ -111,12 +84,12 @@ public sealed class BSShapeCollection : IDisposable // Do we have the correct geometry for this type of object? // Updates prim.BSShape with information/pointers to shape. // Returns 'true' of BSShape is changed to a new shape. - bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback); + bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback); // If we had to select a new shape geometry for the object, // rebuild the body around it. // Updates prim.BSBody with information/pointers to requested body // Returns 'true' if BSBody was changed. - bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, bodyCallback); + bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback); ret = newGeom || newBody; } DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}", @@ -127,271 +100,20 @@ public sealed class BSShapeCollection : IDisposable public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim) { - return GetBodyAndShape(forceRebuild, sim, prim, null, null); - } - - // Track another user of a body. - // We presume the caller has allocated the body. - // Bodies only have one user so the body is just put into the world if not already there. - private void ReferenceBody(BulletBody body) - { - lock (m_collectionActivityLock) - { - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); - if (!PhysicsScene.PE.IsInWorld(PhysicsScene.World, body)) - { - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, body); - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); - } - } - } - - // Release the usage of a body. - // Called when releasing use of a BSBody. BSShape is handled separately. - // Called in taint time. - public void DereferenceBody(BulletBody body, BodyDestructionCallback bodyCallback ) - { - if (!body.HasPhysicalBody) - return; - - PhysicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody"); - - lock (m_collectionActivityLock) - { - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); - // If the caller needs to know the old body is going away, pass the event up. - if (bodyCallback != null) bodyCallback(body); - - // Removing an object not in the world is a NOOP - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, body); - - // Zero any reference to the shape so it is not freed when the body is deleted. - PhysicsScene.PE.SetCollisionShape(PhysicsScene.World, body, null); - PhysicsScene.PE.DestroyObject(PhysicsScene.World, body); - } - } - - // Track the datastructures and use count for a shape. - // When creating a hull, this is called first to reference the mesh - // and then again to reference the hull. - // Meshes and hulls for the same shape have the same hash key. - // NOTE that native shapes are not added to the mesh list or removed. - // Returns 'true' if this is the initial reference to the shape. Otherwise reused. - public bool ReferenceShape(BulletShape shape) - { - bool ret = false; - switch (shape.type) - { - case BSPhysicsShapeType.SHAPE_MESH: - MeshDesc meshDesc; - if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) - { - // There is an existing instance of this mesh. - meshDesc.referenceCount++; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); - } - else - { - // This is a new reference to a mesh - meshDesc.shape = shape.Clone(); - meshDesc.shapeKey = shape.shapeKey; - // We keep a reference to the underlying IMesh data so a hull can be built - meshDesc.referenceCount = 1; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); - ret = true; - } - meshDesc.lastReferenced = System.DateTime.Now; - Meshes[shape.shapeKey] = meshDesc; - break; - case BSPhysicsShapeType.SHAPE_HULL: - HullDesc hullDesc; - if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) - { - // There is an existing instance of this hull. - hullDesc.referenceCount++; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); - } - else - { - // This is a new reference to a hull - hullDesc.shape = shape.Clone(); - hullDesc.shapeKey = shape.shapeKey; - hullDesc.referenceCount = 1; - if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", - BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); - ret = true; - - } - hullDesc.lastReferenced = System.DateTime.Now; - Hulls[shape.shapeKey] = hullDesc; - break; - case BSPhysicsShapeType.SHAPE_UNKNOWN: - break; - default: - // Native shapes are not tracked and they don't go into any list - break; - } - return ret; - } - - // Release the usage of a shape. - public void DereferenceShape(BulletShape shape, ShapeDestructionCallback shapeCallback) - { - if (!shape.HasPhysicalShape) - return; - - PhysicsScene.AssertInTaintTime("BSShapeCollection.DereferenceShape"); - - if (shape.HasPhysicalShape) - { - if (shape.isNativeShape) - { - // Native shapes are not tracked and are released immediately - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1}", - BSScene.DetailLogZero, shape.AddrString); - if (shapeCallback != null) shapeCallback(shape); - PhysicsScene.PE.DeleteCollisionShape(PhysicsScene.World, shape); - } - else - { - switch (shape.type) - { - case BSPhysicsShapeType.SHAPE_HULL: - DereferenceHull(shape, shapeCallback); - break; - case BSPhysicsShapeType.SHAPE_MESH: - DereferenceMesh(shape, shapeCallback); - break; - case BSPhysicsShapeType.SHAPE_COMPOUND: - DereferenceCompound(shape, shapeCallback); - break; - case BSPhysicsShapeType.SHAPE_UNKNOWN: - break; - default: - break; - } - } - } - } - - // Count down the reference count for a mesh shape - // Called at taint-time. - private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback) - { - MeshDesc meshDesc; - if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) - { - meshDesc.referenceCount--; - // TODO: release the Bullet storage - if (shapeCallback != null) shapeCallback(shape); - meshDesc.lastReferenced = System.DateTime.Now; - Meshes[shape.shapeKey] = meshDesc; - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}", - BSScene.DetailLogZero, shape, meshDesc.referenceCount); - - } + return GetBodyAndShape(forceRebuild, sim, prim, null); } - // Count down the reference count for a hull shape - // Called at taint-time. - private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback) + // If the existing prim's shape is to be replaced, remove the tie to the existing shape + // before replacing it. + private void DereferenceExistingShape(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { - HullDesc hullDesc; - if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) + if (prim.PhysShape.HasPhysicalShape) { - hullDesc.referenceCount--; - // TODO: release the Bullet storage (aging old entries?) - - // Tell upper layers that, if they have dependencies on this shape, this link is going away - if (shapeCallback != null) shapeCallback(shape); - - hullDesc.lastReferenced = System.DateTime.Now; - Hulls[shape.shapeKey] = hullDesc; - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}", - BSScene.DetailLogZero, shape, hullDesc.referenceCount); - } - } - - // Remove a reference to a compound shape. - // Taking a compound shape apart is a little tricky because if you just delete the - // physical shape, it will free all the underlying children. We can't do that because - // they could be shared. So, this removes each of the children from the compound and - // dereferences them separately before destroying the compound collision object itself. - // Called at taint-time. - private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback) - { - if (!PhysicsScene.PE.IsCompound(shape)) - { - // Failed the sanity check!! - PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", - LogHeader, shape.type, shape.AddrString); - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", - BSScene.DetailLogZero, shape.type, shape.AddrString); - return; - } - - int numChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(shape); - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren); - - for (int ii = numChildren - 1; ii >= 0; ii--) - { - BulletShape childShape = PhysicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(shape, ii); - DereferenceAnonCollisionShape(childShape); - } - PhysicsScene.PE.DeleteCollisionShape(PhysicsScene.World, shape); - } - - // Sometimes we have a pointer to a collision shape but don't know what type it is. - // Figure out type and call the correct dereference routine. - // Called at taint-time. - private void DereferenceAnonCollisionShape(BulletShape shapeInfo) - { - MeshDesc meshDesc; - HullDesc hullDesc; - - if (TryGetMeshByPtr(shapeInfo, out meshDesc)) - { - shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH; - shapeInfo.shapeKey = meshDesc.shapeKey; - } - else - { - if (TryGetHullByPtr(shapeInfo, out hullDesc)) - { - shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL; - shapeInfo.shapeKey = hullDesc.shapeKey; - } - else - { - if (PhysicsScene.PE.IsCompound(shapeInfo)) - { - shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND; - } - else - { - if (PhysicsScene.PE.IsNativeShape(shapeInfo)) - { - shapeInfo.isNativeShape = true; - shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) - } - } - } - } - - if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); - - if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN) - { - DereferenceShape(shapeInfo, null); - } - else - { - PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}", - LogHeader, PhysicsScene.RegionName, shapeInfo.AddrString); + if (shapeCallback != null) + shapeCallback(prim.PhysBody, prim.PhysShape.physShapeInfo); + prim.PhysShape.Dereference(m_physicsScene); } + prim.PhysShape = new BSShapeNull(); } // Create the geometry information in Bullet for later use. @@ -402,40 +124,7 @@ public sealed class BSShapeCollection : IDisposable // Info in prim.BSShape is updated to the new shape. // Returns 'true' if the geometry was rebuilt. // Called at taint-time! - private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) - { - bool ret = false; - bool haveShape = false; - - if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) - { - // an avatar capsule is close to a native shape (it is not shared) - GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE, shapeCallback); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); - ret = true; - haveShape = true; - } - - // Compound shapes are handled special as they are rebuilt from scratch. - // This isn't too great a hardship since most of the child shapes will have already been created. - if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) - { - ret = GetReferenceToCompoundShape(prim, shapeCallback); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); - haveShape = true; - } - - if (!haveShape) - { - ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback); - } - - return ret; - } - - // Create a mesh, hull or native shape. - // Return 'true' if the prim's shape was changed. - public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) + private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { bool ret = false; bool haveShape = false; @@ -443,19 +132,21 @@ public sealed class BSShapeCollection : IDisposable PrimitiveBaseShape pbs = prim.BaseShape; // If the prim attributes are simple, this could be a simple Bullet native shape + // Native shapes work whether to object is static or physical. if (!haveShape && nativeShapePossible && pbs != null - && !pbs.SculptEntry - && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) || PrimHasNoCuts(pbs)) ) + && PrimHasNoCuts(pbs) + && ( !pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) ) + ) { // Get the scale of any existing shape so we can see if the new shape is same native type and same size. OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; if (prim.PhysShape.HasPhysicalShape) - scaleOfExistingShape = PhysicsScene.PE.GetLocalScaling(prim.PhysShape); + scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo); if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", - prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type); + prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType); // It doesn't look like Bullet scales native spheres so make sure the scales are all equal if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) @@ -463,26 +154,28 @@ public sealed class BSShapeCollection : IDisposable { haveShape = true; if (forceRebuild - || prim.Scale != scaleOfExistingShape - || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE - ) + || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE + ) { - ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, - FixedShapeKey.KEY_SPHERE, shapeCallback); + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE); } if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}", prim.LocalID, forceRebuild, ret, prim.PhysShape); } + // If we didn't make a sphere, maybe a box will work. if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) { haveShape = true; if (forceRebuild || prim.Scale != scaleOfExistingShape - || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX + || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX ) { - ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX, - FixedShapeKey.KEY_BOX, shapeCallback); + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); } if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}", prim.LocalID, forceRebuild, ret, prim.PhysShape); @@ -511,7 +204,7 @@ public sealed class BSShapeCollection : IDisposable } // return 'true' if the prim's shape was changed. - public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) + private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { bool ret = false; @@ -520,537 +213,70 @@ public sealed class BSShapeCollection : IDisposable if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { // Update prim.BSShape to reference a hull of this shape. - ret = GetReferenceToHull(prim, shapeCallback); + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", - prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); + prim.LocalID, prim.PhysShape, prim.PhysShape.physShapeInfo.shapeKey.ToString("X")); } else { - ret = GetReferenceToMesh(prim, shapeCallback); + // Update prim.BSShape to reference a mesh of this shape. + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", - prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); + prim.LocalID, prim.PhysShape, prim.PhysShape.physShapeInfo.shapeKey.ToString("X")); } return ret; } - // Creates a native shape and assignes it to prim.BSShape. - // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). - private bool GetReferenceToNativeShape(BSPhysObject prim, - BSPhysicsShapeType shapeType, FixedShapeKey shapeKey, - ShapeDestructionCallback shapeCallback) - { - // release any previous shape - DereferenceShape(prim.PhysShape, shapeCallback); - - BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); - - // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. - if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", - prim.LocalID, newShape, prim.Scale); - - // native shapes are scaled by Bullet - prim.PhysShape = newShape; - return true; - } - - private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType, - FixedShapeKey shapeKey) - { - BulletShape newShape; - // Need to make sure the passed shape information is for the native type. - ShapeData nativeShapeData = new ShapeData(); - nativeShapeData.Type = shapeType; - nativeShapeData.ID = prim.LocalID; - nativeShapeData.Scale = prim.Scale; - nativeShapeData.Size = prim.Scale; // unneeded, I think. - nativeShapeData.MeshKey = (ulong)shapeKey; - nativeShapeData.HullKey = (ulong)shapeKey; - - if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) - { - - newShape = PhysicsScene.PE.BuildCapsuleShape(PhysicsScene.World, 1f, 1f, prim.Scale); - if (DDetail) DetailLog("{0},BSShapeCollection.BuildPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); - } - else - { - // Native shapes are scaled in Bullet so set the scaling to the size - newShape = PhysicsScene.PE.BuildNativeShape(PhysicsScene.World, nativeShapeData); - - } - if (!newShape.HasPhysicalShape) - { - PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", - LogHeader, prim.LocalID, shapeType); - } - newShape.shapeKey = (System.UInt64)shapeKey; - newShape.isNativeShape = true; - - return newShape; - } - - // Builds a mesh shape in the physical world and updates prim.BSShape. - // Dereferences previous shape in BSShape and adds a reference for this new shape. - // Returns 'true' of a mesh was actually built. Otherwise . - // Called at taint-time! - private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback) - { - BulletShape newShape = new BulletShape(); - - float lod; - System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - - // if this new shape is the same as last time, don't recreate the mesh - if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH) - return false; - - if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2},size={3},lod={4}", - prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"), prim.Size, lod); - - // Since we're recreating new, get rid of the reference to the previous shape - DereferenceShape(prim.PhysShape, shapeCallback); - - newShape = CreatePhysicalMesh(prim, newMeshKey, prim.BaseShape, prim.Size, lod); - // Take evasive action if the mesh was not constructed. - newShape = VerifyMeshCreated(PhysicsScene, newShape, prim); - - ReferenceShape(newShape); - - prim.PhysShape = newShape; - - return true; // 'true' means a new shape has been added to this prim - } - - private BulletShape CreatePhysicalMesh(BSPhysObject prim, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + // Track another user of a body. + // We presume the caller has allocated the body. + // Bodies only have one user so the body is just put into the world if not already there. + private void ReferenceBody(BulletBody body) { - BulletShape newShape = new BulletShape(); - - MeshDesc meshDesc; - if (Meshes.TryGetValue(newMeshKey, out meshDesc)) - { - // If the mesh has already been built just use it. - newShape = meshDesc.shape.Clone(); - } - else + lock (m_collectionActivityLock) { - IMesh meshData = PhysicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, - false, // say it is not physical so a bounding box is not built - false // do not cache the mesh and do not use previously built versions - ); - - if (meshData != null) + if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); + if (!m_physicsScene.PE.IsInWorld(m_physicsScene.World, body)) { - - int[] indices = meshData.getIndexListAsInt(); - int realIndicesIndex = indices.Length; - float[] verticesAsFloats = meshData.getVertexListAsFloat(); - - if (BSParam.ShouldRemoveZeroWidthTriangles) - { - // Remove degenerate triangles. These are triangles with two of the vertices - // are the same. This is complicated by the problem that vertices are not - // made unique in sculpties so we have to compare the values in the vertex. - realIndicesIndex = 0; - for (int tri = 0; tri < indices.Length; tri += 3) - { - // Compute displacements into vertex array for each vertex of the triangle - int v1 = indices[tri + 0] * 3; - int v2 = indices[tri + 1] * 3; - int v3 = indices[tri + 2] * 3; - // Check to see if any two of the vertices are the same - if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0] - && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1] - && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2]) - || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0] - && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1] - && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2]) - || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0] - && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1] - && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) ) - ) - { - // None of the vertices of the triangles are the same. This is a good triangle; - indices[realIndicesIndex + 0] = indices[tri + 0]; - indices[realIndicesIndex + 1] = indices[tri + 1]; - indices[realIndicesIndex + 2] = indices[tri + 2]; - realIndicesIndex += 3; - } - } - } - DetailLog("{0},BSShapeCollection.CreatePhysicalMesh,origTri={1},realTri={2},numVerts={3}", - BSScene.DetailLogZero, indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); - - if (realIndicesIndex != 0) - { - newShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, - realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats); - } - else - { - PhysicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim {1} at {2} in {3}", - LogHeader, prim.PhysObjectName, prim.RawPosition, PhysicsScene.Name); - } + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, body); + if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); } } - newShape.shapeKey = newMeshKey; - - return newShape; - } - - // See that hull shape exists in the physical world and update prim.BSShape. - // We could be creating the hull because scale changed or whatever. - // Return 'true' if a new hull was built. Otherwise, returning a shared hull instance. - private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) - { - BulletShape newShape; - - float lod; - System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - - // if the hull hasn't changed, don't rebuild it - if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL) - return false; - - if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", - prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X")); - - // Remove usage of the previous shape. - DereferenceShape(prim.PhysShape, shapeCallback); - - newShape = CreatePhysicalHull(prim, newHullKey, prim.BaseShape, prim.Size, lod); - // It might not have been created if we're waiting for an asset. - newShape = VerifyMeshCreated(PhysicsScene, newShape, prim); - - ReferenceShape(newShape); - - prim.PhysShape = newShape; - return true; // 'true' means a new shape has been added to this prim } - List m_hulls; - private BulletShape CreatePhysicalHull(BSPhysObject prim, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + // Release the usage of a body. + // Called when releasing use of a BSBody. BSShape is handled separately. + // Called in taint time. + public void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback ) { + if (!body.HasPhysicalBody) + return; - BulletShape newShape = new BulletShape(); - IntPtr hullPtr = IntPtr.Zero; + m_physicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody"); - HullDesc hullDesc; - if (Hulls.TryGetValue(newHullKey, out hullDesc)) - { - // If the hull shape already has been created, just use the one shared instance. - newShape = hullDesc.shape.Clone(); - } - else + lock (m_collectionActivityLock) { - if (BSParam.ShouldUseBulletHACD) - { - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID); - MeshDesc meshDesc; - if (!Meshes.TryGetValue(newHullKey, out meshDesc)) - { - // That's odd because the mesh should have been created before the hull - // but, since it doesn't exist, create it. - newShape = CreatePhysicalMesh(prim, newHullKey, prim.BaseShape, prim.Size, lod); - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,noMeshBuiltNew,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); - - if (newShape.HasPhysicalShape) - { - ReferenceShape(newShape); - Meshes.TryGetValue(newHullKey, out meshDesc); - } - } - if (meshDesc.shape.HasPhysicalShape) - { - HACDParams parms; - parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; - parms.minClusters = BSParam.BHullMinClusters; - parms.compacityWeight = BSParam.BHullCompacityWeight; - parms.volumeWeight = BSParam.BHullVolumeWeight; - parms.concavity = BSParam.BHullConcavity; - parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); - parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); - parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); - parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); - - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); - newShape = PhysicsScene.PE.BuildHullShapeFromMesh(PhysicsScene.World, meshDesc.shape, parms); - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); - } - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); - } - if (!newShape.HasPhysicalShape) - { - // Build a new hull in the physical world. - // Pass true for physicalness as this prevents the creation of bounding box which is not needed - IMesh meshData = PhysicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); - if (meshData != null) - { - int[] indices = meshData.getIndexListAsInt(); - List vertices = meshData.getVertexList(); - - //format conversion from IMesh format to DecompDesc format - List convIndices = new List(); - List convVertices = new List(); - for (int ii = 0; ii < indices.GetLength(0); ii++) - { - convIndices.Add(indices[ii]); - } - foreach (OMV.Vector3 vv in vertices) - { - convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); - } - - uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; - if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) - { - // Simple primitive shapes we know are convex so they are better implemented with - // fewer hulls. - // Check for simple shape (prim without cuts) and reduce split parameter if so. - if (PrimHasNoCuts(pbs)) - { - maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; - } - } - - // setup and do convex hull conversion - m_hulls = new List(); - DecompDesc dcomp = new DecompDesc(); - dcomp.mIndices = convIndices; - dcomp.mVertices = convVertices; - dcomp.mDepth = maxDepthSplit; - dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; - dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; - dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; - dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; - ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); - // create the hull into the _hulls variable - convexBuilder.process(dcomp); - - DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", - BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); - - // Convert the vertices and indices for passing to unmanaged. - // The hull information is passed as a large floating point array. - // The format is: - // convHulls[0] = number of hulls - // convHulls[1] = number of vertices in first hull - // convHulls[2] = hull centroid X coordinate - // convHulls[3] = hull centroid Y coordinate - // convHulls[4] = hull centroid Z coordinate - // convHulls[5] = first hull vertex X - // convHulls[6] = first hull vertex Y - // convHulls[7] = first hull vertex Z - // convHulls[8] = second hull vertex X - // ... - // convHulls[n] = number of vertices in second hull - // convHulls[n+1] = second hull centroid X coordinate - // ... - // - // TODO: is is very inefficient. Someday change the convex hull generator to return - // data structures that do not need to be converted in order to pass to Bullet. - // And maybe put the values directly into pinned memory rather than marshaling. - int hullCount = m_hulls.Count; - int totalVertices = 1; // include one for the count of the hulls - foreach (ConvexResult cr in m_hulls) - { - totalVertices += 4; // add four for the vertex count and centroid - totalVertices += cr.HullIndices.Count * 3; // we pass just triangles - } - float[] convHulls = new float[totalVertices]; - - convHulls[0] = (float)hullCount; - int jj = 1; - foreach (ConvexResult cr in m_hulls) - { - // copy vertices for index access - float3[] verts = new float3[cr.HullVertices.Count]; - int kk = 0; - foreach (float3 ff in cr.HullVertices) - { - verts[kk++] = ff; - } - - // add to the array one hull's worth of data - convHulls[jj++] = cr.HullIndices.Count; - convHulls[jj++] = 0f; // centroid x,y,z - convHulls[jj++] = 0f; - convHulls[jj++] = 0f; - foreach (int ind in cr.HullIndices) - { - convHulls[jj++] = verts[ind].x; - convHulls[jj++] = verts[ind].y; - convHulls[jj++] = verts[ind].z; - } - } - // create the hull data structure in Bullet - newShape = PhysicsScene.PE.CreateHullShape(PhysicsScene.World, hullCount, convHulls); - } - } - newShape.shapeKey = newHullKey; - } - - return newShape; - } - - // Callback from convex hull creater with a newly created hull. - // Just add it to our collection of hulls for this shape. - private void HullReturn(ConvexResult result) - { - m_hulls.Add(result); - return; - } - - // Compound shapes are always built from scratch. - // This shouldn't be to bad since most of the parts will be meshes that had been built previously. - private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback) - { - // Remove reference to the old shape - // Don't need to do this as the shape is freed when the new root shape is created below. - // DereferenceShape(prim.PhysShape, true, shapeCallback); - - BulletShape cShape = PhysicsScene.PE.CreateCompoundShape(PhysicsScene.World, false); - - // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. - CreateGeomMeshOrHull(prim, shapeCallback); - PhysicsScene.PE.AddChildShapeToCompoundShape(cShape, prim.PhysShape, OMV.Vector3.Zero, OMV.Quaternion.Identity); - if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}", - prim.LocalID, cShape, prim.PhysShape); - - prim.PhysShape = cShape; - - return true; - } - - // Create a hash of all the shape parameters to be used as a key - // for this particular shape. - public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) - { - // level of detail based on size and type of the object - float lod = BSParam.MeshLOD; - - // prims with curvy internal cuts need higher lod - if (pbs.HollowShape == HollowShape.Circle) - lod = BSParam.MeshCircularLOD; - - if (pbs.SculptEntry) - lod = BSParam.SculptLOD; - - // Mega prims usually get more detail because one can interact with shape approximations at this size. - float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); - if (maxAxis > BSParam.MeshMegaPrimThreshold) - lod = BSParam.MeshMegaPrimLOD; - - retLod = lod; - return pbs.GetMeshKey(size, lod); - } - // For those who don't want the LOD - public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs) - { - float lod; - return ComputeShapeKey(size, pbs, out lod); - } - - // The creation of a mesh or hull can fail if an underlying asset is not available. - // There are two cases: 1) the asset is not in the cache and it needs to be fetched; - // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). - // The first case causes the asset to be fetched. The second case requires - // us to not loop forever. - // Called after creating a physical mesh or hull. If the physical shape was created, - // just return. - public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim) - { - // If the shape was successfully created, nothing more to do - if (newShape.HasPhysicalShape) - return newShape; + if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); + // If the caller needs to know the old body is going away, pass the event up. + if (bodyCallback != null) + bodyCallback(body, null); - // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been - // fetched but we end up here again, the meshing of the asset must have failed. - // Prevent trying to keep fetching the mesh by declaring failure. - if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) - { - prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", - LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); - } - else - { - // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset - if (prim.BaseShape.SculptEntry - && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed - && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting - && prim.BaseShape.SculptTexture != OMV.UUID.Zero - ) - { - physicsScene.DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID); - // Multiple requestors will know we're waiting for this asset - prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; + // Removing an object not in the world is a NOOP + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, body); - BSPhysObject xprim = prim; - Util.FireAndForget(delegate - { - RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; - if (assetProvider != null) - { - BSPhysObject yprim = xprim; // probably not necessary, but, just in case. - assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) - { - bool assetFound = false; - string mismatchIDs = String.Empty; // DEBUG DEBUG - if (asset != null && yprim.BaseShape.SculptEntry) - { - if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) - { - yprim.BaseShape.SculptData = asset.Data; - // This will cause the prim to see that the filler shape is not the right - // one and try again to build the object. - // No race condition with the normal shape setting since the rebuild is at taint time. - yprim.ForceBodyShapeRebuild(false /* inTaintTime */); - assetFound = true; - } - else - { - mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; - } - } - if (assetFound) - yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; - else - yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - physicsScene.DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", - yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); + // Zero any reference to the shape so it is not freed when the body is deleted. + m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, body, null); - }); - } - else - { - xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", - LogHeader, physicsScene.Name); - } - }); - } - else - { - if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) - { - physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", - LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); - } - } + m_physicsScene.PE.DestroyObject(m_physicsScene.World, body); } - - // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. - BulletShape fillinShape = physicsScene.Shapes.BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); - physicsScene.DetailLog("{0},BSShapeCollection.VerifyMeshCreated,boxTempShape", prim.LocalID); - - return fillinShape; } // Create a body object in Bullet. // Updates prim.BSBody with the information about the new body if one is created. // Returns 'true' if an object was actually created. // Called at taint-time. - private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BodyDestructionCallback bodyCallback) + private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, PhysicalDestructionCallback bodyCallback) { bool ret = false; @@ -1061,7 +287,7 @@ public sealed class BSShapeCollection : IDisposable // If not a solid object, body is a GhostObject. Otherwise a RigidBody. if (!mustRebuild) { - CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(prim.PhysBody); + CollisionObjectTypes bodyType = (CollisionObjectTypes)m_physicsScene.PE.GetBodyType(prim.PhysBody); if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) { @@ -1079,12 +305,12 @@ public sealed class BSShapeCollection : IDisposable BulletBody aBody; if (prim.IsSolid) { - aBody = PhysicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation); + aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,body={1}", prim.LocalID, aBody); } else { - aBody = PhysicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation); + aBody = m_physicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody); } @@ -1098,46 +324,10 @@ public sealed class BSShapeCollection : IDisposable return ret; } - private bool TryGetMeshByPtr(BulletShape shape, out MeshDesc outDesc) - { - bool ret = false; - MeshDesc foundDesc = new MeshDesc(); - foreach (MeshDesc md in Meshes.Values) - { - if (md.shape.ReferenceSame(shape)) - { - foundDesc = md; - ret = true; - break; - } - - } - outDesc = foundDesc; - return ret; - } - - private bool TryGetHullByPtr(BulletShape shape, out HullDesc outDesc) - { - bool ret = false; - HullDesc foundDesc = new HullDesc(); - foreach (HullDesc hd in Hulls.Values) - { - if (hd.shape.ReferenceSame(shape)) - { - foundDesc = hd; - ret = true; - break; - } - - } - outDesc = foundDesc; - return ret; - } - private void DetailLog(string msg, params Object[] args) { - if (PhysicsScene.PhysicsLogging.Enabled) - PhysicsScene.DetailLog(msg, args); + if (m_physicsScene.PhysicsLogging.Enabled) + m_physicsScene.DetailLog(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index e427dbc..a7b3f02 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -39,6 +39,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public abstract class BSShape { + private static string LogHeader = "[BULLETSIM SHAPE]"; + public int referenceCount { get; set; } public DateTime lastReferenced { get; set; } public BulletShape physShapeInfo { get; set; } @@ -56,49 +58,6 @@ public abstract class BSShape physShapeInfo = pShape; } - // Get a reference to a physical shape. Create if it doesn't exist - public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) - { - BSShape ret = null; - - if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) - { - // an avatar capsule is close to a native shape (it is not shared) - ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE, - FixedShapeKey.KEY_CAPSULE); - physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); - } - - // Compound shapes are handled special as they are rebuilt from scratch. - // This isn't too great a hardship since most of the child shapes will have already been created. - if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) - { - // Getting a reference to a compound shape gets you the compound shape with the root prim shape added - ret = BSShapeCompound.GetReference(physicsScene, prim); - physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); - } - - // Avatars have their own unique shape - if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_AVATAR) - { - // Getting a reference to a compound shape gets you the compound shape with the root prim shape added - ret = BSShapeAvatar.GetReference(prim); - physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,avatarShape,shape={1}", prim.LocalID, ret); - } - - if (ret == null) - ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim); - - return ret; - } - private static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) - { - // TODO: work needed here!! - BSShapeMesh.GetReference(physicsScene, forceRebuild, prim); - BSShapeHull.GetReference(physicsScene, forceRebuild, prim); - return null; - } - // Called when this shape is being used again. public virtual void IncrementReference() { @@ -116,6 +75,27 @@ public abstract class BSShape // Release the use of a physical shape. public abstract void Dereference(BSScene physicsScene); + // Return 'true' if there is an allocated physics physical shape under this class instance. + public virtual bool HasPhysicalShape + { + get + { + if (physShapeInfo != null) + return physShapeInfo.HasPhysicalShape; + return false; + } + } + public virtual BSPhysicsShapeType ShapeType + { + get + { + BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + if (physShapeInfo != null && physShapeInfo.HasPhysicalShape) + ret = physShapeInfo.shapeType; + return ret; + } + } + // Returns a string for debugging that uniquily identifies the memory used by this instance public virtual string AddrString { @@ -132,6 +112,119 @@ public abstract class BSShape buff.Append(">"); return buff.ToString(); } + + // Create a hash of all the shape parameters to be used as a key for this particular shape. + public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) + { + // level of detail based on size and type of the object + float lod = BSParam.MeshLOD; + if (pbs.SculptEntry) + lod = BSParam.SculptLOD; + + // Mega prims usually get more detail because one can interact with shape approximations at this size. + float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); + if (maxAxis > BSParam.MeshMegaPrimThreshold) + lod = BSParam.MeshMegaPrimLOD; + + retLod = lod; + return pbs.GetMeshKey(size, lod); + } + + // The creation of a mesh or hull can fail if an underlying asset is not available. + // There are two cases: 1) the asset is not in the cache and it needs to be fetched; + // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). + // The first case causes the asset to be fetched. The second case requires + // us to not loop forever. + // Called after creating a physical mesh or hull. If the physical shape was created, + // just return. + public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim) + { + // If the shape was successfully created, nothing more to do + if (newShape.HasPhysicalShape) + return newShape; + + // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been + // fetched but we end up here again, the meshing of the asset must have failed. + // Prevent trying to keep fetching the mesh by declaring failure. + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", + LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + } + else + { + // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset + if (prim.BaseShape.SculptEntry + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting + && prim.BaseShape.SculptTexture != OMV.UUID.Zero + ) + { + physicsScene.DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID); + // Multiple requestors will know we're waiting for this asset + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; + + BSPhysObject xprim = prim; + Util.FireAndForget(delegate + { + RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; + if (assetProvider != null) + { + BSPhysObject yprim = xprim; // probably not necessary, but, just in case. + assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) + { + bool assetFound = false; + string mismatchIDs = String.Empty; // DEBUG DEBUG + if (asset != null && yprim.BaseShape.SculptEntry) + { + if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) + { + yprim.BaseShape.SculptData = asset.Data; + // This will cause the prim to see that the filler shape is not the right + // one and try again to build the object. + // No race condition with the normal shape setting since the rebuild is at taint time. + yprim.ForceBodyShapeRebuild(false /* inTaintTime */); + assetFound = true; + } + else + { + mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; + } + } + if (assetFound) + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; + else + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + physicsScene.DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", + yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); + }); + } + else + { + xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", + LogHeader, physicsScene.Name); + } + }); + } + else + { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) + { + physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", + LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + } + } + } + + // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. + BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + physicsScene.DetailLog("{0},BSShapeCollection.VerifyMeshCreated,boxTempShape", prim.LocalID); + + return fillShape.physShapeInfo; + } + } // ============================================================================================================ @@ -199,7 +292,7 @@ public class BSShapeNative : BSShape physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", LogHeader, prim.LocalID, shapeType); } - newShape.type = shapeType; + newShape.shapeType = shapeType; newShape.isNativeShape = true; newShape.shapeKey = (UInt64)shapeKey; return newShape; @@ -219,10 +312,11 @@ public class BSShapeMesh : BSShape public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { float lod; - System.UInt64 newMeshKey = BSShapeCollection.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); physicsScene.DetailLog("{0},BSShapeMesh,getReference,oldKey={1},newKey={2},size={3},lod={4}", - prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"), prim.Size, lod); + prim.LocalID, prim.PhysShape.physShapeInfo.shapeKey.ToString("X"), + newMeshKey.ToString("X"), prim.Size, lod); BSShapeMesh retMesh = new BSShapeMesh(new BulletShape()); lock (Meshes) @@ -238,8 +332,8 @@ public class BSShapeMesh : BSShape BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); // Check to see if mesh was created (might require an asset). - newShape = BSShapeCollection.VerifyMeshCreated(physicsScene, newShape, prim); - if (newShape.type == BSPhysicsShapeType.SHAPE_MESH) + newShape = VerifyMeshCreated(physicsScene, newShape, prim); + if (newShape.shapeType == BSPhysicsShapeType.SHAPE_MESH) { // If a mesh was what was created, remember the built shape for later sharing. Meshes.Add(newMeshKey, retMesh); @@ -360,10 +454,10 @@ public class BSShapeHull : BSShape public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { float lod; - System.UInt64 newHullKey = BSShapeCollection.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); physicsScene.DetailLog("{0},BSShapeHull,getReference,oldKey={1},newKey={2},size={3},lod={4}", - prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"), prim.Size, lod); + prim.LocalID, prim.PhysShape.physShapeInfo.shapeKey.ToString("X"), newHullKey.ToString("X"), prim.Size, lod); BSShapeHull retHull = new BSShapeHull(new BulletShape()); lock (Hulls) @@ -379,8 +473,8 @@ public class BSShapeHull : BSShape BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod); // Check to see if mesh was created (might require an asset). - newShape = BSShapeCollection.VerifyMeshCreated(physicsScene, newShape, prim); - if (newShape.type == BSPhysicsShapeType.SHAPE_MESH) + newShape = VerifyMeshCreated(physicsScene, newShape, prim); + if (newShape.shapeType == BSPhysicsShapeType.SHAPE_MESH) { // If a mesh was what was created, remember the built shape for later sharing. Hulls.Add(newHullKey, retHull); @@ -569,7 +663,6 @@ public class BSShapeHull : BSShape } } - // ============================================================================================================ public class BSShapeCompound : BSShape { @@ -589,9 +682,9 @@ public class BSShapeCompound : BSShape { // Failed the sanity check!! physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", - LogHeader, physShapeInfo.type, physShapeInfo.AddrString); + LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", - BSScene.DetailLogZero, physShapeInfo.type, physShapeInfo.AddrString); + BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); return; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs index 8012d91..906e4f9 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs @@ -104,11 +104,11 @@ public class BulletShape { public BulletShape() { - type = BSPhysicsShapeType.SHAPE_UNKNOWN; + shapeType = BSPhysicsShapeType.SHAPE_UNKNOWN; shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE; isNativeShape = false; } - public BSPhysicsShapeType type; + public BSPhysicsShapeType shapeType; public System.UInt64 shapeKey; public bool isNativeShape; @@ -133,7 +133,7 @@ public class BulletShape buff.Append(") is its center-of-mass - OMV.Vector3 centerOfMassW = LinksetRoot.RawPosition; - if (!disableCOM) // DEBUG DEBUG - { - // Compute a center-of-mass in world coordinates. - centerOfMassW = ComputeLinksetCenterOfMass(); - } + OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass(); OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); @@ -414,20 +416,7 @@ public sealed class BSLinksetCompound : BSLinkset OMV.Vector3 centerDisplacement = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation; LinksetRoot.SetEffectiveCenterOfMassW(centerDisplacement); - // This causes the physical position of the root prim to be offset to accomodate for the displacements - LinksetRoot.ForcePosition = LinksetRoot.RawPosition; - - // Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM - PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, 0 /* childIndex , - -centerDisplacement, - OMV.Quaternion.Identity, // LinksetRoot.RawOrientation, - false /* shouldRecalculateLocalAabb (is done later after linkset built) ); - - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}", - LinksetRoot.LocalID, centerOfMassW, LinksetRoot.RawPosition, centerDisplacement); - - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", - LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); + // TODO: add phantom root shape to be the center-of-mass // Add a shape for each of the other children in the linkset int memberIndex = 1; @@ -440,56 +429,31 @@ public sealed class BSLinksetCompound : BSLinkset else { cPrim.LinksetChildIndex = memberIndex; + } - if (cPrim.PhysShape.isNativeShape) - { - // A native shape is turned into a hull collision shape because native - // shapes are not shared so we have to hullify it so it will be tracked - // and freed at the correct time. This also solves the scaling problem - // (native shapes scale but hull/meshes are assumed to not be). - // TODO: decide of the native shape can just be used in the compound shape. - // Use call to CreateGeomNonSpecial(). - BulletShape saveShape = cPrim.PhysShape; - cPrim.PhysShape.Clear(); // Don't let the create free the child's shape - PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); - BulletShape newShape = cPrim.PhysShape; - cPrim.PhysShape = saveShape; - - OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; - OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; - PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", - LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); - } - else - { - // For the shared shapes (meshes and hulls), just use the shape in the child. - // The reference count added here will be decremented when the compound shape - // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced). - if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) - { - PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", - LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); - } - OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; - OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; - PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNonNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", + BSShape childShape = cPrim.PhysShape; + childShape.IncrementReference(); + OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; + OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; + PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); - } - memberIndex++; - } + memberIndex++; + return false; // 'false' says to move onto the next child in the list }); + // Sneak the built compound shape in as the shape of the root prim. + // Note this doesn't touch the root prim's PhysShape so be sure the manage the difference. + PhysicsScene.PE.SetCollisionShape(PhysicsScene.World, LinksetRoot.PhysBody, LinksetShape.physShapeInfo); + // With all of the linkset packed into the root prim, it has the mass of everyone. LinksetMass = ComputeLinksetMass(); LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); // Enable the physical position updator to return the position and rotation of the root shape PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); - */ } finally { -- cgit v1.1 From ad1787770ed02f71feaa002ab689467e187803bb Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sun, 28 Apr 2013 21:50:47 -0700 Subject: BulletSim: rename variable 'PhysicsScene' to be either 'PhysScene' or 'm_physicsScene' to match coding conventions and reduce confusion. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 112 ++++++------ OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 8 +- OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs | 10 +- .../Physics/BulletSPlugin/BSLinksetCompound.cs | 34 ++-- .../Physics/BulletSPlugin/BSLinksetConstraints.cs | 16 +- .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 52 +++--- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 188 ++++++++++----------- .../Region/Physics/BulletSPlugin/BSPrimLinkable.cs | 8 +- .../Physics/BulletSPlugin/BSTerrainHeightmap.cs | 28 +-- .../Physics/BulletSPlugin/BSTerrainManager.cs | 56 +++--- .../Region/Physics/BulletSPlugin/BSTerrainMesh.cs | 52 +++--- 11 files changed, 282 insertions(+), 282 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index da23a262..e12fc8e 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -95,18 +95,18 @@ public sealed class BSCharacter : BSPhysObject // the avatar seeking to reach the motor's target speed. // This motor runs as a prestep action for the avatar so it will keep the avatar // standing as well as moving. Destruction of the avatar will destroy the pre-step action. - m_moveActor = new BSActorAvatarMove(PhysicsScene, this, AvatarMoveActorName); + m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName); PhysicalActors.Add(AvatarMoveActorName, m_moveActor); DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", LocalID, _size, Scale, Density, _avatarVolume, RawMass); // do actual creation in taint time - PhysicsScene.TaintedObject("BSCharacter.create", delegate() + PhysScene.TaintedObject("BSCharacter.create", delegate() { DetailLog("{0},BSCharacter.create,taint", LocalID); // New body and shape into PhysBody and PhysShape - PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this); + PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this); SetPhysicalProperties(); }); @@ -119,18 +119,18 @@ public sealed class BSCharacter : BSPhysObject base.Destroy(); DetailLog("{0},BSCharacter.Destroy", LocalID); - PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() + PhysScene.TaintedObject("BSCharacter.destroy", delegate() { - PhysicsScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); + PhysScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); PhysBody.Clear(); - PhysShape.Dereference(PhysicsScene); + PhysShape.Dereference(PhysScene); PhysShape = new BSShapeNull(); }); } private void SetPhysicalProperties() { - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); ZeroMotion(true); ForcePosition = _position; @@ -145,35 +145,35 @@ public sealed class BSCharacter : BSPhysObject // Needs to be reset especially when an avatar is recreated after crossing a region boundry. Flying = _flying; - PhysicsScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); - PhysicsScene.PE.SetMargin(PhysShape.physShapeInfo, PhysicsScene.Params.collisionMargin); - PhysicsScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); - PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + PhysScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); + PhysScene.PE.SetMargin(PhysShape.physShapeInfo, PhysScene.Params.collisionMargin); + PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); if (BSParam.CcdMotionThreshold > 0f) { - PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); - PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); } UpdatePhysicalMassProperties(RawMass, false); // Make so capsule does not fall over - PhysicsScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); + PhysScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); // The avatar mover sets some parameters. PhysicalActors.Refresh(); - PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); + PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); - PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); + PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); // Do this after the object has been added to the world PhysBody.collisionType = CollisionType.Avatar; - PhysBody.ApplyCollisionMask(PhysicsScene); + PhysBody.ApplyCollisionMask(PhysScene); } @@ -203,14 +203,14 @@ public sealed class BSCharacter : BSPhysObject DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", LocalID, _size, Scale, Density, _avatarVolume, RawMass); - PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() + PhysScene.TaintedObject("BSCharacter.setSize", delegate() { if (PhysBody.HasPhysicalBody && PhysShape.physShapeInfo.HasPhysicalShape) { - PhysicsScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); + PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); UpdatePhysicalMassProperties(RawMass, true); // Make sure this change appears as a property update event - PhysicsScene.PE.PushUpdate(PhysBody); + PhysScene.PE.PushUpdate(PhysBody); } }); @@ -247,24 +247,24 @@ public sealed class BSCharacter : BSPhysObject _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties directly into the physics engine - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.ClearAllForces(PhysBody); + PhysScene.PE.ClearAllForces(PhysBody); }); } public override void ZeroAngularMotion(bool inTaintTime) { _rotationalVelocity = OMV.Vector3.Zero; - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); - PhysicsScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); + PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); + PhysScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); // The next also get rid of applied linear force but the linear velocity is untouched. - PhysicsScene.PE.ClearForces(PhysBody); + PhysScene.PE.ClearForces(PhysBody); } }); } @@ -286,7 +286,7 @@ public sealed class BSCharacter : BSPhysObject set { _position = value; - PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() + PhysScene.TaintedObject("BSCharacter.setPosition", delegate() { DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); PositionSanityCheck(); @@ -296,14 +296,14 @@ public sealed class BSCharacter : BSPhysObject } public override OMV.Vector3 ForcePosition { get { - _position = PhysicsScene.PE.GetPosition(PhysBody); + _position = PhysScene.PE.GetPosition(PhysBody); return _position; } set { _position = value; if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); } } } @@ -317,18 +317,18 @@ public sealed class BSCharacter : BSPhysObject bool ret = false; // TODO: check for out of bounds - if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) + if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) { // The character is out of the known/simulated area. // Force the avatar position to be within known. ScenePresence will use the position // plus the velocity to decide if the avatar is moving out of the region. - RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); + RawPosition = PhysScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); return true; } // If below the ground, move the avatar up - float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); + float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); if (Position.Z < terrainHeight) { DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, _position, terrainHeight); @@ -337,7 +337,7 @@ public sealed class BSCharacter : BSPhysObject } if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) { - float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); + float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(_position); if (Position.Z < waterHeight) { _position.Z = waterHeight; @@ -358,7 +358,7 @@ public sealed class BSCharacter : BSPhysObject { // The new position value must be pushed into the physics engine but we can't // just assign to "Position" because of potential call loops. - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() { DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); ForcePosition = _position; @@ -376,8 +376,8 @@ public sealed class BSCharacter : BSPhysObject } public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) { - OMV.Vector3 localInertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); - PhysicsScene.PE.SetMassProps(PhysBody, physMass, localInertia); + OMV.Vector3 localInertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); + PhysScene.PE.SetMassProps(PhysBody, physMass, localInertia); } public override OMV.Vector3 Force { @@ -385,11 +385,11 @@ public sealed class BSCharacter : BSPhysObject set { RawForce = value; // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); - PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() + PhysScene.TaintedObject("BSCharacter.SetForce", delegate() { DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, RawForce); if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetObjectForce(PhysBody, RawForce); + PhysScene.PE.SetObjectForce(PhysBody, RawForce); }); } } @@ -432,7 +432,7 @@ public sealed class BSCharacter : BSPhysObject set { RawVelocity = value; // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, RawVelocity); - PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() + PhysScene.TaintedObject("BSCharacter.setVelocity", delegate() { if (m_moveActor != null) m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, true /* inTaintTime */); @@ -445,11 +445,11 @@ public sealed class BSCharacter : BSPhysObject public override OMV.Vector3 ForceVelocity { get { return RawVelocity; } set { - PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity"); + PhysScene.AssertInTaintTime("BSCharacter.ForceVelocity"); RawVelocity = value; - PhysicsScene.PE.SetLinearVelocity(PhysBody, RawVelocity); - PhysicsScene.PE.Activate(PhysBody, true); + PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); + PhysScene.PE.Activate(PhysBody, true); } } public override OMV.Vector3 Torque { @@ -479,7 +479,7 @@ public sealed class BSCharacter : BSPhysObject if (_orientation != value) { _orientation = value; - PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() + PhysScene.TaintedObject("BSCharacter.setOrientation", delegate() { ForceOrientation = _orientation; }); @@ -491,7 +491,7 @@ public sealed class BSCharacter : BSPhysObject { get { - _orientation = PhysicsScene.PE.GetOrientation(PhysBody); + _orientation = PhysScene.PE.GetOrientation(PhysBody); return _orientation; } set @@ -500,7 +500,7 @@ public sealed class BSCharacter : BSPhysObject if (PhysBody.HasPhysicalBody) { // _position = PhysicsScene.PE.GetPosition(BSBody); - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); } } } @@ -549,14 +549,14 @@ public sealed class BSCharacter : BSPhysObject public override bool FloatOnWater { set { _floatOnWater = value; - PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() + PhysScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() { if (PhysBody.HasPhysicalBody) { if (_floatOnWater) - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); else - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); } }); } @@ -577,7 +577,7 @@ public sealed class BSCharacter : BSPhysObject public override float Buoyancy { get { return _buoyancy; } set { _buoyancy = value; - PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() + PhysScene.TaintedObject("BSCharacter.setBuoyancy", delegate() { DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); ForceBuoyancy = _buoyancy; @@ -587,7 +587,7 @@ public sealed class BSCharacter : BSPhysObject public override float ForceBuoyancy { get { return _buoyancy; } set { - PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); + PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); _buoyancy = value; DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); @@ -595,7 +595,7 @@ public sealed class BSCharacter : BSPhysObject float grav = BSParam.Gravity * (1f - _buoyancy); Gravity = new OMV.Vector3(0f, 0f, grav); if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetGravity(PhysBody, Gravity); + PhysScene.PE.SetGravity(PhysBody, Gravity); } } @@ -613,7 +613,7 @@ public sealed class BSCharacter : BSPhysObject public override void AddForce(OMV.Vector3 force, bool pushforce) { // Since this force is being applied in only one step, make this a force per second. - OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep; + OMV.Vector3 addForce = force / PhysScene.LastTimeStep; AddForce(addForce, pushforce, false); } private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { @@ -622,13 +622,13 @@ public sealed class BSCharacter : BSPhysObject OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); - PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() + PhysScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() { // Bullet adds this central force to the total force for this tick // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); + PhysScene.PE.ApplyCentralForce(PhysBody, addForce); } }); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index e2e807e..56d2415 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -789,7 +789,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin if ((m_knownHas & m_knownChangedTerrainHeight) == 0 || pos != lastRememberedHeightPos) { lastRememberedHeightPos = pos; - m_knownTerrainHeight = ControllingPrim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); + m_knownTerrainHeight = ControllingPrim.PhysScene.TerrainManager.GetTerrainHeightAtXYZ(pos); m_knownHas |= m_knownChangedTerrainHeight; } return m_knownTerrainHeight; @@ -801,7 +801,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { if ((m_knownHas & m_knownChangedWaterLevel) == 0) { - m_knownWaterLevel = ControllingPrim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos); + m_knownWaterLevel = ControllingPrim.PhysScene.TerrainManager.GetWaterLevelAtXYZ(pos); m_knownHas |= m_knownChangedWaterLevel; } return (float)m_knownWaterLevel; @@ -1637,8 +1637,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Invoke the detailed logger and output something if it's enabled. private void VDetailLog(string msg, params Object[] args) { - if (ControllingPrim.PhysicsScene.VehicleLoggingEnabled) - ControllingPrim.PhysicsScene.DetailLog(msg, args); + if (ControllingPrim.PhysScene.VehicleLoggingEnabled) + ControllingPrim.PhysScene.DetailLog(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index df1dd34..6d0d0eb 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -80,7 +80,7 @@ public abstract class BSLinkset public BSPrimLinkable LinksetRoot { get; protected set; } - public BSScene PhysicsScene { get; private set; } + protected BSScene m_physicsScene { get; private set; } static int m_nextLinksetID = 1; public int LinksetID { get; private set; } @@ -115,7 +115,7 @@ public abstract class BSLinkset // We create LOTS of linksets. if (m_nextLinksetID <= 0) m_nextLinksetID = 1; - PhysicsScene = scene; + m_physicsScene = scene; LinksetRoot = parent; m_children = new HashSet(); LinksetMass = parent.RawMass; @@ -158,7 +158,7 @@ public abstract class BSLinkset } // The child is down to a linkset of just itself - return BSLinkset.Factory(PhysicsScene, child); + return BSLinkset.Factory(m_physicsScene, child); } // Return 'true' if the passed object is the root object of this linkset @@ -316,8 +316,8 @@ public abstract class BSLinkset // Invoke the detailed logger and output something if it's enabled. protected void DetailLog(string msg, params Object[] args) { - if (PhysicsScene.PhysicsLogging.Enabled) - PhysicsScene.DetailLog(msg, args); + if (m_physicsScene.PhysicsLogging.Enabled) + m_physicsScene.DetailLog(msg, args); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index be01808..01ada3f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -121,7 +121,7 @@ public sealed class BSLinksetCompound : BSLinkset // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. if (!Rebuilding && HasAnyChildren) { - PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() + m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() { if (HasAnyChildren) RecomputeLinksetCompound(); @@ -147,10 +147,10 @@ public sealed class BSLinksetCompound : BSLinkset { // The origional prims are removed from the world as the shape of the root compound // shape takes over. - PhysicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); - PhysicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); + m_physicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + m_physicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); // We don't want collisions from the old linkset children. - PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + m_physicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); child.PhysBody.collisionType = CollisionType.LinksetChild; @@ -175,12 +175,12 @@ public sealed class BSLinksetCompound : BSLinkset else { // The non-physical children can come back to life. - PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + m_physicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); child.PhysBody.collisionType = CollisionType.LinksetChild; // Don't force activation so setting of DISABLE_SIMULATION can stay if used. - PhysicsScene.PE.Activate(child.PhysBody, false); + m_physicsScene.PE.Activate(child.PhysBody, false); ret = true; } return ret; @@ -196,7 +196,7 @@ public sealed class BSLinksetCompound : BSLinkset // but it also means all the child positions get updated. // What would cause an unnecessary rebuild so we make sure the linkset is in a // region before bothering to do a rebuild. - if (!IsRoot(updated) && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) + if (!IsRoot(updated) && m_physicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) { // If a child of the linkset is updating only the position or rotation, that can be done // without rebuilding the linkset. @@ -209,21 +209,21 @@ public sealed class BSLinksetCompound : BSLinkset if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) { // Find the physical instance of the child - if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo)) + if (LinksetRoot.PhysShape.HasPhysicalShape && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo)) { // It is possible that the linkset is still under construction and the child is not yet // inserted into the compound shape. A rebuild of the linkset in a pre-step action will // build the whole thing with the new position or rotation. // The index must be checked because Bullet references the child array but does no validity // checking of the child index passed. - int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo); + int numLinksetChildren = m_physicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo); if (updated.LinksetChildIndex < numLinksetChildren) { - BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex); + BulletShape linksetChildShape = m_physicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex); if (linksetChildShape.HasPhysicalShape) { // Found the child shape within the compound shape - PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex, + m_physicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex, updated.RawPosition - LinksetRoot.RawPosition, updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation), true /* shouldRecalculateLocalAabb */); @@ -401,9 +401,9 @@ public sealed class BSLinksetCompound : BSLinkset // Here we build the compound shape made up of all the children. // Free up any shape we'd previously built. - LinksetShape.Dereference(PhysicsScene); + LinksetShape.Dereference(m_physicsScene); - LinksetShape = BSShapeCompound.GetReference(PhysicsScene, LinksetRoot); + LinksetShape = BSShapeCompound.GetReference(m_physicsScene, LinksetRoot); // The center of mass for the linkset is the geometric center of the group. // Compute a displacement for each component so it is relative to the center-of-mass. @@ -435,7 +435,7 @@ public sealed class BSLinksetCompound : BSLinkset childShape.IncrementReference(); OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; - PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); + m_physicsScene.PE.AddChildShapeToCompoundShape(LinksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); @@ -446,14 +446,14 @@ public sealed class BSLinksetCompound : BSLinkset // Sneak the built compound shape in as the shape of the root prim. // Note this doesn't touch the root prim's PhysShape so be sure the manage the difference. - PhysicsScene.PE.SetCollisionShape(PhysicsScene.World, LinksetRoot.PhysBody, LinksetShape.physShapeInfo); + m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, LinksetShape.physShapeInfo); // With all of the linkset packed into the root prim, it has the mass of everyone. LinksetMass = ComputeLinksetMass(); LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); // Enable the physical position updator to return the position and rotation of the root shape - PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); + m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); } finally { @@ -461,7 +461,7 @@ public sealed class BSLinksetCompound : BSLinkset } // See that the Aabb surrounds the new shape - PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo); + m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo); } } } \ No newline at end of file diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs index 1811772..a06a44d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -51,7 +51,7 @@ public sealed class BSLinksetConstraints : BSLinkset if (HasAnyChildren && IsRoot(requestor)) { // Queue to happen after all the other taint processing - PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() + m_physicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() { if (HasAnyChildren && IsRoot(requestor)) RecomputeLinksetConstraints(); @@ -142,7 +142,7 @@ public sealed class BSLinksetConstraints : BSLinkset rootx.LocalID, rootx.PhysBody.AddrString, childx.LocalID, childx.PhysBody.AddrString); - PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate() + m_physicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate() { PhysicallyUnlinkAChildFromRoot(rootx, childx); }); @@ -187,7 +187,7 @@ public sealed class BSLinksetConstraints : BSLinkset // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 BSConstraint6Dof constrain = new BSConstraint6Dof( - PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true ); + m_physicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true ); // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true ); /* NOTE: below is an attempt to build constraint with full frame computation, etc. @@ -216,7 +216,7 @@ public sealed class BSLinksetConstraints : BSLinkset // ================================================================================== */ - PhysicsScene.Constraints.AddConstraint(constrain); + m_physicsScene.Constraints.AddConstraint(constrain); // zero linear and angular limits makes the objects unable to move in relation to each other constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); @@ -248,10 +248,10 @@ public sealed class BSLinksetConstraints : BSLinkset childPrim.LocalID, childPrim.PhysBody.AddrString); // Find the constraint for this link and get rid of it from the overall collection and from my list - if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) + if (m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) { // Make the child refresh its location - PhysicsScene.PE.PushUpdate(childPrim.PhysBody); + m_physicsScene.PE.PushUpdate(childPrim.PhysBody); ret = true; } @@ -265,7 +265,7 @@ public sealed class BSLinksetConstraints : BSLinkset { DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); - return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); + return m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); } // Call each of the constraints that make up this linkset and recompute the @@ -289,7 +289,7 @@ public sealed class BSLinksetConstraints : BSLinkset child.UpdatePhysicalMassProperties(linksetMass, true); BSConstraint constrain; - if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain)) + if (!m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain)) { // If constraint doesn't exist yet, create it. constrain = BuildConstraint(LinksetRoot, child); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index b6eb619..6a3ada2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -72,14 +72,14 @@ public abstract class BSPhysObject : PhysicsActor } protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName) { - PhysicsScene = parentScene; + PhysScene = parentScene; LocalID = localID; PhysObjectName = name; Name = name; // PhysicsActor also has the name of the object. Someday consolidate. TypeName = typeName; // The collection of things that push me around - PhysicalActors = new BSActorCollection(PhysicsScene); + PhysicalActors = new BSActorCollection(PhysScene); // Initialize variables kept in base. GravModifier = 1.0f; @@ -112,13 +112,13 @@ public abstract class BSPhysObject : PhysicsActor public virtual void Destroy() { PhysicalActors.Enable(false); - PhysicsScene.TaintedObject("BSPhysObject.Destroy", delegate() + PhysScene.TaintedObject("BSPhysObject.Destroy", delegate() { PhysicalActors.Dispose(); }); } - public BSScene PhysicsScene { get; protected set; } + public BSScene PhysScene { get; protected set; } // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor public string PhysObjectName { get; protected set; } public string TypeName { get; protected set; } @@ -270,7 +270,7 @@ public abstract class BSPhysObject : PhysicsActor public void ActivateIfPhysical(bool forceIt) { if (IsPhysical && PhysBody.HasPhysicalBody) - PhysicsScene.PE.Activate(PhysBody, forceIt); + PhysScene.PE.Activate(PhysBody, forceIt); } // 'actors' act on the physical object to change or constrain its motion. These can range from @@ -333,29 +333,29 @@ public abstract class BSPhysObject : PhysicsActor protected long CollisionAccumulation { get; set; } public override bool IsColliding { - get { return (CollidingStep == PhysicsScene.SimulationStep); } + get { return (CollidingStep == PhysScene.SimulationStep); } set { if (value) - CollidingStep = PhysicsScene.SimulationStep; + CollidingStep = PhysScene.SimulationStep; else CollidingStep = 0; } } public override bool CollidingGround { - get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } + get { return (CollidingGroundStep == PhysScene.SimulationStep); } set { if (value) - CollidingGroundStep = PhysicsScene.SimulationStep; + CollidingGroundStep = PhysScene.SimulationStep; else CollidingGroundStep = 0; } } public override bool CollidingObj { - get { return (CollidingObjectStep == PhysicsScene.SimulationStep); } + get { return (CollidingObjectStep == PhysScene.SimulationStep); } set { if (value) - CollidingObjectStep = PhysicsScene.SimulationStep; + CollidingObjectStep = PhysScene.SimulationStep; else CollidingObjectStep = 0; } @@ -380,14 +380,14 @@ public abstract class BSPhysObject : PhysicsActor bool ret = false; // The following lines make IsColliding(), CollidingGround() and CollidingObj work - CollidingStep = PhysicsScene.SimulationStep; - if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID) + CollidingStep = PhysScene.SimulationStep; + if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID) { - CollidingGroundStep = PhysicsScene.SimulationStep; + CollidingGroundStep = PhysScene.SimulationStep; } else { - CollidingObjectStep = PhysicsScene.SimulationStep; + CollidingObjectStep = PhysScene.SimulationStep; } CollisionAccumulation++; @@ -397,10 +397,10 @@ public abstract class BSPhysObject : PhysicsActor // Make a collection of the collisions that happened the last simulation tick. // This is different than the collection created for sending up to the simulator as it is cleared every tick. - if (CollisionsLastTickStep != PhysicsScene.SimulationStep) + if (CollisionsLastTickStep != PhysScene.SimulationStep) { CollisionsLastTick = new CollisionEventUpdate(); - CollisionsLastTickStep = PhysicsScene.SimulationStep; + CollisionsLastTickStep = PhysScene.SimulationStep; } CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); @@ -427,9 +427,9 @@ public abstract class BSPhysObject : PhysicsActor bool force = (CollisionCollection.Count == 0 && CollisionsLastReported.Count != 0); // throttle the collisions to the number of milliseconds specified in the subscription - if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime)) + if (force || (PhysScene.SimulationNowTime >= NextCollisionOkTime)) { - NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs; + NextCollisionOkTime = PhysScene.SimulationNowTime + SubscribedEventsMs; // We are called if we previously had collisions. If there are no collisions // this time, send up one last empty event so OpenSim can sense collision end. @@ -464,10 +464,10 @@ public abstract class BSPhysObject : PhysicsActor // make sure first collision happens NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs); - PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() + PhysScene.TaintedObject(TypeName+".SubscribeEvents", delegate() { if (PhysBody.HasPhysicalBody) - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); } else @@ -479,11 +479,11 @@ public abstract class BSPhysObject : PhysicsActor public override void UnSubscribeEvents() { // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); SubscribedEventsMs = 0; - PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() + PhysScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() { // Make sure there is a body there because sometimes destruction happens in an un-ideal order. if (PhysBody.HasPhysicalBody) - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); } // Return 'true' if the simulator wants collision events @@ -497,7 +497,7 @@ public abstract class BSPhysObject : PhysicsActor { // Scale the collision count by the time since the last collision. // The "+1" prevents dividing by zero. - long timeAgo = PhysicsScene.SimulationStep - CollidingStep + 1; + long timeAgo = PhysScene.SimulationStep - CollidingStep + 1; CollisionScore = CollisionAccumulation / timeAgo; } public override float CollisionScore { get; set; } @@ -524,8 +524,8 @@ public abstract class BSPhysObject : PhysicsActor // High performance detailed logging routine used by the physical objects. protected void DetailLog(string msg, params Object[] args) { - if (PhysicsScene.PhysicsLogging.Enabled) - PhysicsScene.DetailLog(msg, args); + if (PhysScene.PhysicsLogging.Enabled) + PhysScene.DetailLog(msg, args); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 5d12338..0d45579 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -101,21 +101,21 @@ public class BSPrim : BSPhysObject _isVolumeDetect = false; // We keep a handle to the vehicle actor so we can set vehicle parameters later. - VehicleActor = new BSDynamics(PhysicsScene, this, VehicleActorName); + VehicleActor = new BSDynamics(PhysScene, this, VehicleActorName); PhysicalActors.Add(VehicleActorName, VehicleActor); _mass = CalculateMass(); // DetailLog("{0},BSPrim.constructor,call", LocalID); // do the actual object creation at taint time - PhysicsScene.TaintedObject("BSPrim.create", delegate() + PhysScene.TaintedObject("BSPrim.create", delegate() { // Make sure the object is being created with some sanity. ExtremeSanityCheck(true /* inTaintTime */); CreateGeomAndObject(true); - CurrentCollisionFlags = PhysicsScene.PE.GetCollisionFlags(PhysBody); + CurrentCollisionFlags = PhysScene.PE.GetCollisionFlags(PhysBody); }); } @@ -128,13 +128,13 @@ public class BSPrim : BSPhysObject // Undo any vehicle properties this.VehicleType = (int)Vehicle.TYPE_NONE; - PhysicsScene.TaintedObject("BSPrim.Destroy", delegate() + PhysScene.TaintedObject("BSPrim.Destroy", delegate() { DetailLog("{0},BSPrim.Destroy,taint,", LocalID); // If there are physical body and shape, release my use of same. - PhysicsScene.Shapes.DereferenceBody(PhysBody, null); + PhysScene.Shapes.DereferenceBody(PhysBody, null); PhysBody.Clear(); - PhysShape.Dereference(PhysicsScene); + PhysShape.Dereference(PhysScene); PhysShape = new BSShapeNull(); }); } @@ -163,7 +163,7 @@ public class BSPrim : BSPhysObject } public override bool ForceBodyShapeRebuild(bool inTaintTime) { - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() { _mass = CalculateMass(); // changing the shape changes the mass CreateGeomAndObject(true); @@ -180,7 +180,7 @@ public class BSPrim : BSPhysObject if (value != _isSelected) { _isSelected = value; - PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() + PhysScene.TaintedObject("BSPrim.setSelected", delegate() { DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); SetObjectDynamic(false); @@ -226,23 +226,23 @@ public class BSPrim : BSPhysObject _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties in the physics engine - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() { if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.ClearAllForces(PhysBody); + PhysScene.PE.ClearAllForces(PhysBody); }); } public override void ZeroAngularMotion(bool inTaintTime) { _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties in the physics engine - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() { // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); - PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); + PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); + PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); } }); } @@ -260,11 +260,11 @@ public class BSPrim : BSPhysObject EnableActor(LockedAxis != LockedAxisFree, LockedAxisActorName, delegate() { - return new BSActorLockAxis(PhysicsScene, this, LockedAxisActorName); + return new BSActorLockAxis(PhysScene, this, LockedAxisActorName); }); // Update parameters so the new actor's Refresh() action is called at the right time. - PhysicsScene.TaintedObject("BSPrim.LockAngularMotion", delegate() + PhysScene.TaintedObject("BSPrim.LockAngularMotion", delegate() { UpdatePhysicalParameters(); }); @@ -294,7 +294,7 @@ public class BSPrim : BSPhysObject _position = value; PositionSanityCheck(false); - PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() + PhysScene.TaintedObject("BSPrim.setPosition", delegate() { DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); ForcePosition = _position; @@ -304,14 +304,14 @@ public class BSPrim : BSPhysObject public override OMV.Vector3 ForcePosition { get { - _position = PhysicsScene.PE.GetPosition(PhysBody); + _position = PhysScene.PE.GetPosition(PhysBody); return _position; } set { _position = value; if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); ActivateIfPhysical(false); } } @@ -328,7 +328,7 @@ public class BSPrim : BSPhysObject if (!IsPhysicallyActive) return ret; - if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) + if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) { // The physical object is out of the known/simulated area. // Upper levels of code will handle the transition to other areas so, for @@ -336,7 +336,7 @@ public class BSPrim : BSPhysObject return ret; } - float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); + float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); OMV.Vector3 upForce = OMV.Vector3.Zero; float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z)); if ((RawPosition.Z + approxSize / 2f) < terrainHeight) @@ -357,7 +357,7 @@ public class BSPrim : BSPhysObject if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) { - float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); + float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(_position); // TODO: a floating motor so object will bob in the water if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) { @@ -365,7 +365,7 @@ public class BSPrim : BSPhysObject upForce.Z = (waterHeight - RawPosition.Z) * 1f; // Apply upforce and overcome gravity. - OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity; + OMV.Vector3 correctionForce = upForce - PhysScene.DefaultGravity; DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce); AddForce(correctionForce, false, inTaintTime); ret = true; @@ -431,10 +431,10 @@ public class BSPrim : BSPhysObject { if (IsStatic) { - PhysicsScene.PE.SetGravity(PhysBody, PhysicsScene.DefaultGravity); + PhysScene.PE.SetGravity(PhysBody, PhysScene.DefaultGravity); Inertia = OMV.Vector3.Zero; - PhysicsScene.PE.SetMassProps(PhysBody, 0f, Inertia); - PhysicsScene.PE.UpdateInertiaTensor(PhysBody); + PhysScene.PE.SetMassProps(PhysBody, 0f, Inertia); + PhysScene.PE.UpdateInertiaTensor(PhysBody); } else { @@ -443,16 +443,16 @@ public class BSPrim : BSPhysObject // Changing interesting properties doesn't change proxy and collision cache // information. The Bullet solution is to re-add the object to the world // after parameters are changed. - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); } // The computation of mass props requires gravity to be set on the object. Gravity = ComputeGravity(Buoyancy); - PhysicsScene.PE.SetGravity(PhysBody, Gravity); + PhysScene.PE.SetGravity(PhysBody, Gravity); - Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); - PhysicsScene.PE.SetMassProps(PhysBody, physMass, Inertia); - PhysicsScene.PE.UpdateInertiaTensor(PhysBody); + Inertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); + PhysScene.PE.SetMassProps(PhysBody, physMass, Inertia); + PhysScene.PE.UpdateInertiaTensor(PhysBody); DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", LocalID, physMass, Inertia, Gravity, inWorld); @@ -468,7 +468,7 @@ public class BSPrim : BSPhysObject // Return what gravity should be set to this very moment public OMV.Vector3 ComputeGravity(float buoyancy) { - OMV.Vector3 ret = PhysicsScene.DefaultGravity; + OMV.Vector3 ret = PhysScene.DefaultGravity; if (!IsStatic) { @@ -497,7 +497,7 @@ public class BSPrim : BSPhysObject RawForce = value; EnableActor(RawForce != OMV.Vector3.Zero, SetForceActorName, delegate() { - return new BSActorSetForce(PhysicsScene, this, SetForceActorName); + return new BSActorSetForce(PhysScene, this, SetForceActorName); }); } } @@ -509,7 +509,7 @@ public class BSPrim : BSPhysObject set { Vehicle type = (Vehicle)value; - PhysicsScene.TaintedObject("setVehicleType", delegate() + PhysScene.TaintedObject("setVehicleType", delegate() { // Vehicle code changes the parameters for this vehicle type. VehicleActor.ProcessTypeChange(type); @@ -519,7 +519,7 @@ public class BSPrim : BSPhysObject } public override void VehicleFloatParam(int param, float value) { - PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() + PhysScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() { VehicleActor.ProcessFloatVehicleParam((Vehicle)param, value); ActivateIfPhysical(false); @@ -527,7 +527,7 @@ public class BSPrim : BSPhysObject } public override void VehicleVectorParam(int param, OMV.Vector3 value) { - PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() + PhysScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() { VehicleActor.ProcessVectorVehicleParam((Vehicle)param, value); ActivateIfPhysical(false); @@ -535,7 +535,7 @@ public class BSPrim : BSPhysObject } public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { - PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() + PhysScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() { VehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation); ActivateIfPhysical(false); @@ -543,7 +543,7 @@ public class BSPrim : BSPhysObject } public override void VehicleFlags(int param, bool remove) { - PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() + PhysScene.TaintedObject("BSPrim.VehicleFlags", delegate() { VehicleActor.ProcessVehicleFlags(param, remove); }); @@ -555,7 +555,7 @@ public class BSPrim : BSPhysObject if (_isVolumeDetect != newValue) { _isVolumeDetect = newValue; - PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() + PhysScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() { // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); SetObjectDynamic(true); @@ -566,7 +566,7 @@ public class BSPrim : BSPhysObject public override void SetMaterial(int material) { base.SetMaterial(material); - PhysicsScene.TaintedObject("BSPrim.SetMaterial", delegate() + PhysScene.TaintedObject("BSPrim.SetMaterial", delegate() { UpdatePhysicalParameters(); }); @@ -579,7 +579,7 @@ public class BSPrim : BSPhysObject if (base.Friction != value) { base.Friction = value; - PhysicsScene.TaintedObject("BSPrim.setFriction", delegate() + PhysScene.TaintedObject("BSPrim.setFriction", delegate() { UpdatePhysicalParameters(); }); @@ -594,7 +594,7 @@ public class BSPrim : BSPhysObject if (base.Restitution != value) { base.Restitution = value; - PhysicsScene.TaintedObject("BSPrim.setRestitution", delegate() + PhysScene.TaintedObject("BSPrim.setRestitution", delegate() { UpdatePhysicalParameters(); }); @@ -611,7 +611,7 @@ public class BSPrim : BSPhysObject if (base.Density != value) { base.Density = value; - PhysicsScene.TaintedObject("BSPrim.setDensity", delegate() + PhysScene.TaintedObject("BSPrim.setDensity", delegate() { UpdatePhysicalParameters(); }); @@ -626,7 +626,7 @@ public class BSPrim : BSPhysObject if (base.GravModifier != value) { base.GravModifier = value; - PhysicsScene.TaintedObject("BSPrim.setGravityModifier", delegate() + PhysScene.TaintedObject("BSPrim.setGravityModifier", delegate() { UpdatePhysicalParameters(); }); @@ -637,7 +637,7 @@ public class BSPrim : BSPhysObject get { return RawVelocity; } set { RawVelocity = value; - PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() + PhysScene.TaintedObject("BSPrim.setVelocity", delegate() { // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, RawVelocity); ForceVelocity = RawVelocity; @@ -647,13 +647,13 @@ public class BSPrim : BSPhysObject public override OMV.Vector3 ForceVelocity { get { return RawVelocity; } set { - PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity"); + PhysScene.AssertInTaintTime("BSPrim.ForceVelocity"); RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); if (PhysBody.HasPhysicalBody) { DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, RawVelocity); - PhysicsScene.PE.SetLinearVelocity(PhysBody, RawVelocity); + PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); ActivateIfPhysical(false); } } @@ -664,7 +664,7 @@ public class BSPrim : BSPhysObject RawTorque = value; EnableActor(RawTorque != OMV.Vector3.Zero, SetTorqueActorName, delegate() { - return new BSActorSetTorque(PhysicsScene, this, SetTorqueActorName); + return new BSActorSetTorque(PhysScene, this, SetTorqueActorName); }); DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, RawTorque); } @@ -687,7 +687,7 @@ public class BSPrim : BSPhysObject return; _orientation = value; - PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() + PhysScene.TaintedObject("BSPrim.setOrientation", delegate() { ForceOrientation = _orientation; }); @@ -698,14 +698,14 @@ public class BSPrim : BSPhysObject { get { - _orientation = PhysicsScene.PE.GetOrientation(PhysBody); + _orientation = PhysScene.PE.GetOrientation(PhysBody); return _orientation; } set { _orientation = value; if (PhysBody.HasPhysicalBody) - PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); + PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); } } public override int PhysicsActorType { @@ -718,7 +718,7 @@ public class BSPrim : BSPhysObject if (_isPhysical != value) { _isPhysical = value; - PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() + PhysScene.TaintedObject("BSPrim.setIsPhysical", delegate() { DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); SetObjectDynamic(true); @@ -773,7 +773,7 @@ public class BSPrim : BSPhysObject // Mangling all the physical properties requires the object not be in the physical world. // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); // Set up the object physicalness (does gravity and collisions move this object) MakeDynamic(IsStatic); @@ -790,7 +790,7 @@ public class BSPrim : BSPhysObject AddObjectToPhysicalWorld(); // Rebuild its shape - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); + PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), @@ -807,28 +807,28 @@ public class BSPrim : BSPhysObject if (makeStatic) { // Become a Bullet 'static' object type - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); // Stop all movement ZeroMotion(true); // Set various physical properties so other object interact properly - PhysicsScene.PE.SetFriction(PhysBody, Friction); - PhysicsScene.PE.SetRestitution(PhysBody, Restitution); - PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + PhysScene.PE.SetFriction(PhysBody, Friction); + PhysScene.PE.SetRestitution(PhysBody, Restitution); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); // Mass is zero which disables a bunch of physics stuff in Bullet UpdatePhysicalMassProperties(0f, false); // Set collision detection parameters if (BSParam.CcdMotionThreshold > 0f) { - PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); - PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); } // The activation state is 'disabled' so Bullet will not try to act on it. // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); // Start it out sleeping and physical actions could wake it up. - PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); // This collides like a static object PhysBody.collisionType = CollisionType.Static; @@ -836,11 +836,11 @@ public class BSPrim : BSPhysObject else { // Not a Bullet static object - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); // Set various physical properties so other object interact properly - PhysicsScene.PE.SetFriction(PhysBody, Friction); - PhysicsScene.PE.SetRestitution(PhysBody, Restitution); + PhysScene.PE.SetFriction(PhysBody, Friction); + PhysScene.PE.SetRestitution(PhysBody, Restitution); // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution); // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 @@ -858,22 +858,22 @@ public class BSPrim : BSPhysObject // Set collision detection parameters if (BSParam.CcdMotionThreshold > 0f) { - PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); - PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); } // Various values for simulation limits - PhysicsScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); - PhysicsScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); - PhysicsScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); - PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + PhysScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); + PhysScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); + PhysScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); // This collides like an object. PhysBody.collisionType = CollisionType.Dynamic; // Force activation of the object so Bullet will act on it. // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. - PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); } } @@ -883,7 +883,7 @@ public class BSPrim : BSPhysObject // the functions after this one set up the state of a possibly newly created collision body. private void MakeSolid(bool makeSolid) { - CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(PhysBody); + CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysScene.PE.GetBodyType(PhysBody); if (makeSolid) { // Verify the previous code created the correct shape for this type of thing. @@ -891,7 +891,7 @@ public class BSPrim : BSPhysObject { m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); } - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); } else { @@ -899,7 +899,7 @@ public class BSPrim : BSPhysObject { m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); } - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); // Change collision info from a static object to a ghosty collision object PhysBody.collisionType = CollisionType.VolumeDetect; @@ -911,11 +911,11 @@ public class BSPrim : BSPhysObject { if (wantsCollisionEvents) { - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); } else { - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); } } @@ -926,7 +926,7 @@ public class BSPrim : BSPhysObject { if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); + PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); } else { @@ -961,12 +961,12 @@ public class BSPrim : BSPhysObject public override bool FloatOnWater { set { _floatOnWater = value; - PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() + PhysScene.TaintedObject("BSPrim.setFloatOnWater", delegate() { if (_floatOnWater) - CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); else - CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); }); } } @@ -978,7 +978,7 @@ public class BSPrim : BSPhysObject _rotationalVelocity = value; Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); - PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() + PhysScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() { ForceRotationalVelocity = _rotationalVelocity; }); @@ -993,7 +993,7 @@ public class BSPrim : BSPhysObject if (PhysBody.HasPhysicalBody) { DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); - PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); + PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); ActivateIfPhysical(false); } @@ -1009,7 +1009,7 @@ public class BSPrim : BSPhysObject get { return _buoyancy; } set { _buoyancy = value; - PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() + PhysScene.TaintedObject("BSPrim.setBuoyancy", delegate() { ForceBuoyancy = _buoyancy; }); @@ -1032,7 +1032,7 @@ public class BSPrim : BSPhysObject base.MoveToTargetActive = value; EnableActor(MoveToTargetActive, MoveToTargetActorName, delegate() { - return new BSActorMoveToTarget(PhysicsScene, this, MoveToTargetActorName); + return new BSActorMoveToTarget(PhysScene, this, MoveToTargetActorName); }); } } @@ -1044,7 +1044,7 @@ public class BSPrim : BSPhysObject base.HoverActive = value; EnableActor(HoverActive, HoverActorName, delegate() { - return new BSActorHover(PhysicsScene, this, HoverActorName); + return new BSActorHover(PhysScene, this, HoverActorName); }); } } @@ -1054,7 +1054,7 @@ public class BSPrim : BSPhysObject OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); // Since this force is being applied in only one step, make this a force per second. - addForce /= PhysicsScene.LastTimeStep; + addForce /= PhysScene.LastTimeStep; AddForce(addForce, pushforce, false /* inTaintTime */); } @@ -1069,13 +1069,13 @@ public class BSPrim : BSPhysObject // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); OMV.Vector3 addForce = force; - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() { // Bullet adds this central force to the total force for this tick DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); + PhysScene.PE.ApplyCentralForce(PhysBody, addForce); ActivateIfPhysical(false); } }); @@ -1097,13 +1097,13 @@ public class BSPrim : BSPhysObject OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude); // DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse); - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate() { // Bullet adds this impulse immediately to the velocity DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse); if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); + PhysScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); ActivateIfPhysical(false); } }); @@ -1122,12 +1122,12 @@ public class BSPrim : BSPhysObject if (force.IsFinite()) { OMV.Vector3 angForce = force; - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() { if (PhysBody.HasPhysicalBody) { DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); - PhysicsScene.PE.ApplyTorque(PhysBody, angForce); + PhysScene.PE.ApplyTorque(PhysBody, angForce); ActivateIfPhysical(false); } }); @@ -1146,11 +1146,11 @@ public class BSPrim : BSPhysObject public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) { OMV.Vector3 applyImpulse = impulse; - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() + PhysScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() { if (PhysBody.HasPhysicalBody) { - PhysicsScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); + PhysScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); ActivateIfPhysical(false); } }); @@ -1452,7 +1452,7 @@ public class BSPrim : BSPhysObject // Create the correct physical representation for this type of object. // Updates base.PhysBody and base.PhysShape with the new information. // Ignore 'forceRebuild'. 'GetBodyAndShape' makes the right choices and changes of necessary. - PhysicsScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysicsScene.World, this, delegate(BulletBody pBody, BulletShape pShape) + PhysScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysScene.World, this, delegate(BulletBody pBody, BulletShape pShape) { // Called if the current prim body is about to be destroyed. // Remove all the physical dependencies on the old body. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs index 81104ec..5236909 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs @@ -47,9 +47,9 @@ public class BSPrimLinkable : BSPrimDisplaced OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) : base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical) { - Linkset = BSLinkset.Factory(PhysicsScene, this); + Linkset = BSLinkset.Factory(PhysScene, this); - PhysicsScene.TaintedObject("BSPrimLinksetCompound.Refresh", delegate() + PhysScene.TaintedObject("BSPrimLinksetCompound.Refresh", delegate() { Linkset.Refresh(this); }); @@ -99,7 +99,7 @@ public class BSPrimLinkable : BSPrimDisplaced set { base.Position = value; - PhysicsScene.TaintedObject("BSPrimLinkset.setPosition", delegate() + PhysScene.TaintedObject("BSPrimLinkset.setPosition", delegate() { Linkset.UpdateProperties(UpdatedProperties.Position, this); }); @@ -113,7 +113,7 @@ public class BSPrimLinkable : BSPrimDisplaced set { base.Orientation = value; - PhysicsScene.TaintedObject("BSPrimLinkset.setOrientation", delegate() + PhysScene.TaintedObject("BSPrimLinkset.setOrientation", delegate() { Linkset.UpdateProperties(UpdatedProperties.Orientation, this); }); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs index e4fecc3..5a19797 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs @@ -92,7 +92,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys private void BuildHeightmapTerrain() { // Create the terrain shape from the mapInfo - m_mapInfo.terrainShape = PhysicsScene.PE.CreateTerrainShape( m_mapInfo.ID, + m_mapInfo.terrainShape = m_physicsScene.PE.CreateTerrainShape( m_mapInfo.ID, new Vector3(m_mapInfo.sizeX, m_mapInfo.sizeY, 0), m_mapInfo.minZ, m_mapInfo.maxZ, m_mapInfo.heightMap, 1f, BSParam.TerrainCollisionMargin); @@ -103,26 +103,26 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f); centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f); - m_mapInfo.terrainBody = PhysicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, + m_mapInfo.terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, m_mapInfo.ID, centerPos, Quaternion.Identity); // Set current terrain attributes - PhysicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction); - PhysicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction); - PhysicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution); - PhysicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT); + m_physicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction); + m_physicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction); + m_physicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution); + m_physicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT); // Return the new terrain to the world of physical objects - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_mapInfo.terrainBody); // redo its bounding box now that it is in the world - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_mapInfo.terrainBody); m_mapInfo.terrainBody.collisionType = CollisionType.Terrain; - m_mapInfo.terrainBody.ApplyCollisionMask(PhysicsScene); + m_mapInfo.terrainBody.ApplyCollisionMask(m_physicsScene); // Make it so the terrain will not move or be considered for movement. - PhysicsScene.PE.ForceActivationState(m_mapInfo.terrainBody, ActivationState.DISABLE_SIMULATION); + m_physicsScene.PE.ForceActivationState(m_mapInfo.terrainBody, ActivationState.DISABLE_SIMULATION); return; } @@ -134,9 +134,9 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys { if (m_mapInfo.terrainBody.HasPhysicalBody) { - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_mapInfo.terrainBody); // Frees both the body and the shape. - PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_mapInfo.terrainBody); + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_mapInfo.terrainBody); } } m_mapInfo = null; @@ -155,7 +155,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys catch { // Sometimes they give us wonky values of X and Y. Give a warning and return something. - PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", + m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", LogHeader, m_mapInfo.terrainRegionBase, pos); ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; } @@ -165,7 +165,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys // The passed position is relative to the base of the region. public override float GetWaterLevelAtXYZ(Vector3 pos) { - return PhysicsScene.SimpleWaterLevel; + return m_physicsScene.SimpleWaterLevel; } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 5240ad8..0d16eda 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -50,14 +50,14 @@ public abstract class BSTerrainPhys : IDisposable Mesh = 1 } - public BSScene PhysicsScene { get; private set; } + protected BSScene m_physicsScene { get; private set; } // Base of the region in world coordinates. Coordinates inside the region are relative to this. public Vector3 TerrainBase { get; private set; } public uint ID { get; private set; } public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id) { - PhysicsScene = physicsScene; + m_physicsScene = physicsScene; TerrainBase = regionBase; ID = id; } @@ -86,7 +86,7 @@ public sealed class BSTerrainManager : IDisposable public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); // The scene that I am part of - private BSScene PhysicsScene { get; set; } + private BSScene m_physicsScene { get; set; } // The ground plane created to keep thing from falling to infinity. private BulletBody m_groundPlane; @@ -113,7 +113,7 @@ public sealed class BSTerrainManager : IDisposable public BSTerrainManager(BSScene physicsScene) { - PhysicsScene = physicsScene; + m_physicsScene = physicsScene; m_terrains = new Dictionary(); // Assume one region of default size @@ -132,21 +132,21 @@ public sealed class BSTerrainManager : IDisposable // safe to call Bullet in real time. We hope no one is moving prims around yet. public void CreateInitialGroundPlaneAndTerrain() { - DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName); + DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName); // The ground plane is here to catch things that are trying to drop to negative infinity - BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); - m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, + BulletShape groundPlaneShape = m_physicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); + m_groundPlane = m_physicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity); - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_groundPlane); - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_groundPlane); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_groundPlane); + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_groundPlane); // Ground plane does not move - PhysicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION); + m_physicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION); // Everything collides with the ground plane. m_groundPlane.collisionType = CollisionType.Groundplane; - m_groundPlane.ApplyCollisionMask(PhysicsScene); + m_groundPlane.ApplyCollisionMask(m_physicsScene); - BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); + BSTerrainPhys initialTerrain = new BSTerrainHeightmap(m_physicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); lock (m_terrains) { // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. @@ -157,12 +157,12 @@ public sealed class BSTerrainManager : IDisposable // Release all the terrain structures we might have allocated public void ReleaseGroundPlaneAndTerrain() { - DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName); + DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName); if (m_groundPlane.HasPhysicalBody) { - if (PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_groundPlane)) + if (m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_groundPlane)) { - PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_groundPlane); + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_groundPlane); } m_groundPlane.Clear(); } @@ -188,7 +188,7 @@ public sealed class BSTerrainManager : IDisposable float[] localHeightMap = heightMap; // If there are multiple requests for changes to the same terrain between ticks, // only do that last one. - PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate() + m_physicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate() { if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) { @@ -219,7 +219,7 @@ public sealed class BSTerrainManager : IDisposable private void AddMegaRegionChildTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) { // Since we are called by another region's thread, the action must be rescheduled onto our processing thread. - PhysicsScene.PostTaintObject("TerrainManager.AddMegaRegionChild" + minCoords.ToString(), id, delegate() + m_physicsScene.PostTaintObject("TerrainManager.AddMegaRegionChild" + minCoords.ToString(), id, delegate() { UpdateTerrain(id, heightMap, minCoords, maxCoords); }); @@ -318,26 +318,26 @@ public sealed class BSTerrainManager : IDisposable // TODO: redo terrain implementation selection to allow other base types than heightMap. private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) { - PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", - LogHeader, PhysicsScene.RegionName, terrainRegionBase, + m_physicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", + LogHeader, m_physicsScene.RegionName, terrainRegionBase, (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation); BSTerrainPhys newTerrainPhys = null; switch ((int)BSParam.TerrainImplementation) { case (int)BSTerrainPhys.TerrainImplementation.Heightmap: - newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id, + newTerrainPhys = new BSTerrainHeightmap(m_physicsScene, terrainRegionBase, id, heightMap, minCoords, maxCoords); break; case (int)BSTerrainPhys.TerrainImplementation.Mesh: - newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id, + newTerrainPhys = new BSTerrainMesh(m_physicsScene, terrainRegionBase, id, heightMap, minCoords, maxCoords); break; default: - PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", + m_physicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", LogHeader, (int)BSParam.TerrainImplementation, BSParam.TerrainImplementation, - PhysicsScene.RegionName, terrainRegionBase); + m_physicsScene.RegionName, terrainRegionBase); break; } return newTerrainPhys; @@ -429,8 +429,8 @@ public sealed class BSTerrainManager : IDisposable } else { - PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", - LogHeader, PhysicsScene.RegionName, tX, tY); + m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", + LogHeader, m_physicsScene.RegionName, tX, tY); DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}", BSScene.DetailLogZero, pos, terrainBaseXYZ); } @@ -451,8 +451,8 @@ public sealed class BSTerrainManager : IDisposable } else { - PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}", - LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret); + m_physicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}", + LogHeader, m_physicsScene.RegionName, pos, terrainBaseXYZ, ret); } return ret; } @@ -564,7 +564,7 @@ public sealed class BSTerrainManager : IDisposable private void DetailLog(string msg, params Object[] args) { - PhysicsScene.PhysicsLogging.Write(msg, args); + m_physicsScene.PhysicsLogging.Write(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index 2ce1513..ee2a1f2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -80,7 +80,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys if (BSParam.TerrainMeshMagnification == 1) { // If a magnification of one, use the old routine that is tried and true. - meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, + meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(m_physicsScene, initialMap, m_sizeX, m_sizeY, // input size Vector3.Zero, // base for mesh out indicesCount, out indices, out verticesCount, out vertices); @@ -88,7 +88,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys else { // Other magnifications use the newer routine - meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(PhysicsScene, + meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(m_physicsScene, initialMap, m_sizeX, m_sizeY, // input size BSParam.TerrainMeshMagnification, physicsScene.TerrainManager.DefaultRegionSize, @@ -98,21 +98,21 @@ public sealed class BSTerrainMesh : BSTerrainPhys if (!meshCreationSuccess) { // DISASTER!! - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID); - PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID); + m_physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); // Something is very messed up and a crash is in our future. return; } - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length); - m_terrainShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, indicesCount, indices, verticesCount, vertices); + m_terrainShape = m_physicsScene.PE.CreateMeshShape(m_physicsScene.World, indicesCount, indices, verticesCount, vertices); if (!m_terrainShape.HasPhysicalShape) { // DISASTER!! - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID); - PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID); + m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); // Something is very messed up and a crash is in our future. return; } @@ -120,52 +120,52 @@ public sealed class BSTerrainMesh : BSTerrainPhys Vector3 pos = regionBase; Quaternion rot = Quaternion.Identity; - m_terrainBody = PhysicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot); + m_terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot); if (!m_terrainBody.HasPhysicalBody) { // DISASTER!! - PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); + m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); // Something is very messed up and a crash is in our future. return; } physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin); // Set current terrain attributes - PhysicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction); - PhysicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction); - PhysicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution); - PhysicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold); - PhysicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT); + m_physicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction); + m_physicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction); + m_physicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution); + m_physicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold); + m_physicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT); // Static objects are not very massive. - PhysicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero); + m_physicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero); // Put the new terrain to the world of physical objects - PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_terrainBody); // Redo its bounding box now that it is in the world - PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_terrainBody); m_terrainBody.collisionType = CollisionType.Terrain; - m_terrainBody.ApplyCollisionMask(PhysicsScene); + m_terrainBody.ApplyCollisionMask(m_physicsScene); if (BSParam.UseSingleSidedMeshes) { - PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id); - PhysicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); + m_physicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id); + m_physicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); } // Make it so the terrain will not move or be considered for movement. - PhysicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION); + m_physicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION); } public override void Dispose() { if (m_terrainBody.HasPhysicalBody) { - PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_terrainBody); // Frees both the body and the shape. - PhysicsScene.PE.DestroyObject(PhysicsScene.World, m_terrainBody); + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_terrainBody); m_terrainBody.Clear(); m_terrainShape.Clear(); } @@ -185,7 +185,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys catch { // Sometimes they give us wonky values of X and Y. Give a warning and return something. - PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", + m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", LogHeader, TerrainBase, pos); ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; } @@ -195,7 +195,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys // The passed position is relative to the base of the region. public override float GetWaterLevelAtXYZ(Vector3 pos) { - return PhysicsScene.SimpleWaterLevel; + return m_physicsScene.SimpleWaterLevel; } // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). -- cgit v1.1 From 92ee288d666963aae2a058cc964be009a504f084 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 29 Apr 2013 07:54:50 -0700 Subject: BulletSim: remove trailing white space to make git happier. No functional changes. --- OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs | 6 +- OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs | 138 ++++++++++----------- .../Physics/BulletSPlugin/BSActorAvatarMove.cs | 2 +- .../Physics/BulletSPlugin/BSActorLockAxis.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSActors.cs | 2 +- .../Region/Physics/BulletSPlugin/BSApiTemplate.cs | 6 +- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 6 +- .../Physics/BulletSPlugin/BSConstraintHinge.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 22 ++-- OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs | 2 +- .../Physics/BulletSPlugin/BSLinksetCompound.cs | 4 +- OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs | 12 +- OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | 6 +- .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 8 +- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 20 +-- .../Physics/BulletSPlugin/BSTerrainHeightmap.cs | 6 +- .../Physics/BulletSPlugin/BSTerrainManager.cs | 10 +- .../Region/Physics/BulletSPlugin/BSTerrainMesh.cs | 6 +- .../Region/Physics/BulletSPlugin/BulletSimData.cs | 42 +++---- 20 files changed, 152 insertions(+), 152 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs index 8a22bc7..231f0f8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIUnman.cs @@ -75,7 +75,7 @@ private sealed class BulletBodyUnman : BulletBody private sealed class BulletShapeUnman : BulletShape { public IntPtr ptr; - public BulletShapeUnman(IntPtr xx, BSPhysicsShapeType typ) + public BulletShapeUnman(IntPtr xx, BSPhysicsShapeType typ) : base() { ptr = xx; @@ -255,7 +255,7 @@ public override BulletShape CreateHullShape(BulletWorld world, int hullCount, fl { BulletWorldUnman worldu = world as BulletWorldUnman; return new BulletShapeUnman( - BSAPICPP.CreateHullShape2(worldu.ptr, hullCount, hulls), + BSAPICPP.CreateHullShape2(worldu.ptr, hullCount, hulls), BSPhysicsShapeType.SHAPE_HULL); } @@ -1503,7 +1503,7 @@ public static extern void DestroyObject2(IntPtr sim, IntPtr obj); public static extern IntPtr CreateGroundPlaneShape2(uint id, float height, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateTerrainShape2(uint id, Vector3 size, float minHeight, float maxHeight, +public static extern IntPtr CreateTerrainShape2(uint id, Vector3 size, float minHeight, float maxHeight, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float scaleFactor, float collisionMargin); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs index 1ef8b17..59780ae 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSAPIXNA.cs @@ -81,7 +81,7 @@ private sealed class BulletBodyXNA : BulletBody private sealed class BulletShapeXNA : BulletShape { public CollisionShape shape; - public BulletShapeXNA(CollisionShape xx, BSPhysicsShapeType typ) + public BulletShapeXNA(CollisionShape xx, BSPhysicsShapeType typ) : base() { shape = xx; @@ -137,8 +137,8 @@ private sealed class BulletConstraintXNA : BulletConstraint internal int LastEntityProperty = 0; internal EntityProperties[] UpdatedObjects; - internal Dictionary specialCollisionObjects; - + internal Dictionary specialCollisionObjects; + private static int m_collisionsThisFrame; private BSScene PhysicsScene { get; set; } @@ -151,7 +151,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } /// - /// + /// /// /// /// @@ -174,7 +174,7 @@ private sealed class BulletConstraintXNA : BulletConstraint DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; TypedConstraint constraint = (pConstraint as BulletConstraintXNA).constrain; world.AddConstraint(constraint, pDisableCollisionsBetweenLinkedObjects); - + return true; } @@ -300,7 +300,7 @@ private sealed class BulletConstraintXNA : BulletConstraint public override bool GetForceUpdateAllAabbs(BulletWorld pWorld) { DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; return world.GetForceUpdateAllAabbs(); - + } public override void SetForceUpdateAllAabbs(BulletWorld pWorld, bool pForce) { @@ -404,7 +404,7 @@ private sealed class BulletConstraintXNA : BulletConstraint IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion); mat._origin = vposition; collisionObject.SetWorldTransform(mat); - + } public override Vector3 GetPosition(BulletBody pCollisionObject) @@ -457,7 +457,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; collisionObject.Activate(pforceactivation); - + } public override Quaternion GetOrientation(BulletBody pCollisionObject) @@ -486,7 +486,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; return collisionObject.GetCcdSweptSphereRadius(); - + } public override IntPtr GetUserPointer(BulletBody pCollisionObject) @@ -559,8 +559,8 @@ private sealed class BulletConstraintXNA : BulletConstraint } - public override BulletConstraint Create6DofConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, - Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + public override BulletConstraint Create6DofConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) { @@ -604,7 +604,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } /// - /// + /// /// /// /// @@ -824,7 +824,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { RigidBody body = (pBody as BulletBodyXNA).rigidBody; float angularDamping = body.GetAngularDamping(); - body.SetDamping(lin_damping, angularDamping); + body.SetDamping(lin_damping, angularDamping); } public override float GetLinearDamping(BulletBody pBody) @@ -907,7 +907,7 @@ private sealed class BulletConstraintXNA : BulletConstraint RigidBody bo = co as RigidBody; if (bo == null) { - + if (world.IsInWorld(co)) { world.RemoveCollisionObject(co); @@ -915,7 +915,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } else { - + if (world.IsInWorld(bo)) { world.RemoveRigidBody(bo); @@ -947,7 +947,7 @@ private sealed class BulletConstraintXNA : BulletConstraint // TODO: Turn this from a reference copy to a Value Copy. BulletShapeXNA shape2 = new BulletShapeXNA(shape1, BSShapeTypeFromBroadPhaseNativeType(shape1.GetShapeType())); - + return shape2; } @@ -957,7 +957,7 @@ private sealed class BulletConstraintXNA : BulletConstraint return false; } //(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation); - + public override BulletBody CreateBodyFromShape(BulletWorld pWorld, BulletShape pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation) { CollisionWorld world = (pWorld as BulletWorldXNA).world; @@ -993,11 +993,11 @@ private sealed class BulletConstraintXNA : BulletConstraint m_startWorldTransform = IndexedMatrix.Identity; */ body.SetUserPointer(pLocalID); - + return new BulletBodyXNA(pLocalID, body); } - + public override BulletBody CreateBodyWithDefaultMotionState( BulletShape pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation) { @@ -1025,7 +1025,7 @@ private sealed class BulletConstraintXNA : BulletConstraint public override Vector3 GetAnisotripicFriction(BulletConstraint pconstrain) { - /* TODO */ + /* TODO */ return Vector3.Zero; } public override Vector3 SetAnisotripicFriction(BulletConstraint pconstrain, Vector3 frict) { /* TODO */ return Vector3.Zero; } @@ -1035,7 +1035,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; return collisionObject.IsStaticObject(); - + } public override bool IsKinematicObject(BulletBody pCollisionObject) { @@ -1098,10 +1098,10 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletWorldXNA(1, PhysicsScene, BSAPIXNA.Initialize2(worldExtent, configparms, maxCollisions, ref collisionArray, maxUpdates, ref updateArray, null)); } - private static DiscreteDynamicsWorld Initialize2(Vector3 worldExtent, + private static DiscreteDynamicsWorld Initialize2(Vector3 worldExtent, ConfigurationParameters[] o, int mMaxCollisionsPerFrame, ref CollisionDesc[] collisionArray, - int mMaxUpdatesPerFrame, ref EntityProperties[] updateArray, + int mMaxUpdatesPerFrame, ref EntityProperties[] updateArray, object mDebugLogCallbackHandle) { CollisionWorld.WorldData.ParamData p = new CollisionWorld.WorldData.ParamData(); @@ -1138,9 +1138,9 @@ private sealed class BulletConstraintXNA : BulletConstraint p.avatarCapsuleDepth = BSParam.AvatarCapsuleDepth; p.avatarCapsuleHeight = BSParam.AvatarCapsuleHeight; p.avatarContactProcessingThreshold = BSParam.AvatarContactProcessingThreshold; - + p.vehicleAngularDamping = BSParam.VehicleAngularDamping; - + p.maxPersistantManifoldPoolSize = o[0].maxPersistantManifoldPoolSize; p.maxCollisionAlgorithmPoolSize = o[0].maxCollisionAlgorithmPoolSize; p.shouldDisableContactPoolDynamicAllocation = o[0].shouldDisableContactPoolDynamicAllocation; @@ -1160,7 +1160,7 @@ private sealed class BulletConstraintXNA : BulletConstraint p.linkConstraintSolverIterations = BSParam.LinkConstraintSolverIterations; p.physicsLoggingFrames = o[0].physicsLoggingFrames; DefaultCollisionConstructionInfo ccci = new DefaultCollisionConstructionInfo(); - + DefaultCollisionConfiguration cci = new DefaultCollisionConfiguration(); CollisionDispatcher m_dispatcher = new CollisionDispatcher(cci); @@ -1263,7 +1263,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } } return ret; - + } public override float GetAngularMotionDisc(BulletShape pShape) @@ -1353,10 +1353,10 @@ private sealed class BulletConstraintXNA : BulletConstraint CollisionShape shape = (pShape as BulletShapeXNA).shape; gObj.SetCollisionShape(shape); gObj.SetUserPointer(pLocalID); - + if (specialCollisionObjects.ContainsKey(pLocalID)) specialCollisionObjects[pLocalID] = gObj; - else + else specialCollisionObjects.Add(pLocalID, gObj); // TODO: Add to Special CollisionObjects! @@ -1447,8 +1447,8 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletShapeXNA(ret, BSShapeTypeFromBroadPhaseNativeType(ret.GetShapeType())); } - public override BulletShape GetChildShapeFromCompoundShapeIndex(BulletShape cShape, int indx) { - + public override BulletShape GetChildShapeFromCompoundShapeIndex(BulletShape cShape, int indx) { + if (cShape == null) return null; CompoundShape compoundShape = (cShape as BulletShapeXNA).shape as CompoundShape; @@ -1456,7 +1456,7 @@ private sealed class BulletConstraintXNA : BulletConstraint BulletShape retShape = new BulletShapeXNA(shape, BSShapeTypeFromBroadPhaseNativeType(shape.GetShapeType())); - return retShape; + return retShape; } public BSPhysicsShapeType BSShapeTypeFromBroadPhaseNativeType(BroadphaseNativeTypes pin) @@ -1598,8 +1598,8 @@ private sealed class BulletConstraintXNA : BulletConstraint return new BulletShapeXNA(m_planeshape, BSPhysicsShapeType.SHAPE_GROUNDPLANE); } - public override BulletConstraint Create6DofSpringConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, - Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + public override BulletConstraint Create6DofSpringConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) { @@ -1745,7 +1745,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; CompoundShape compoundshape = new CompoundShape(false); - + compoundshape.SetMargin(world.WorldSettings.Params.collisionMargin); int ii = 1; @@ -1761,7 +1761,7 @@ private sealed class BulletConstraintXNA : BulletConstraint int ender = ((ii + 4) + (vertexCount*3)); for (int iii = ii + 4; iii < ender; iii+=3) { - + virts.Add(new IndexedVector3(pConvHulls[iii], pConvHulls[iii + 1], pConvHulls[iii +2])); } ConvexHullShape convexShape = new ConvexHullShape(virts, vertexCount); @@ -1769,7 +1769,7 @@ private sealed class BulletConstraintXNA : BulletConstraint compoundshape.AddChildShape(ref childTrans, convexShape); ii += (vertexCount*3 + 4); } - + return new BulletShapeXNA(compoundshape, BSPhysicsShapeType.SHAPE_HULL); } @@ -1791,13 +1791,13 @@ private sealed class BulletConstraintXNA : BulletConstraint public override BulletShape CreateMeshShape(BulletWorld pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats) { //DumpRaw(indices,verticesAsFloats,pIndicesCount,pVerticesCount); - + for (int iter = 0; iter < pVerticesCount; iter++) { if (verticesAsFloats[iter] > 0 && verticesAsFloats[iter] < 0.0001) verticesAsFloats[iter] = 0; if (verticesAsFloats[iter] < 0 && verticesAsFloats[iter] > -0.0001) verticesAsFloats[iter] = 0; } - + ObjectArray indicesarr = new ObjectArray(indices); ObjectArray vertices = new ObjectArray(verticesAsFloats); DumpRaw(indicesarr,vertices,pIndicesCount,pVerticesCount); @@ -1811,7 +1811,7 @@ private sealed class BulletConstraintXNA : BulletConstraint mesh.m_vertexStride = 3; mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT; mesh.m_triangleIndexStride = 3; - + TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray(); tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER); BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(tribuilder, true,true); @@ -1822,7 +1822,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } public static void DumpRaw(ObjectArrayindices, ObjectArray vertices, int pIndicesCount,int pVerticesCount ) { - + String fileName = "objTest3.raw"; String completePath = System.IO.Path.Combine(Util.configDir(), fileName); StreamWriter sw = new StreamWriter(completePath); @@ -1848,7 +1848,7 @@ private sealed class BulletConstraintXNA : BulletConstraint string s = vertices[indices[i * 3]].ToString("0.0000"); s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000"); s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000"); - + sw.Write(s + "\n"); } @@ -1870,7 +1870,7 @@ private sealed class BulletConstraintXNA : BulletConstraint mesh.m_vertexStride = 3; mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT; mesh.m_triangleIndexStride = 3; - + TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray(); tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER); @@ -1901,7 +1901,7 @@ private sealed class BulletConstraintXNA : BulletConstraint sw.Close(); } - public override BulletShape CreateTerrainShape(uint id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, + public override BulletShape CreateTerrainShape(uint id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, float scaleFactor, float collisionMargin) { const int upAxis = 2; @@ -1943,14 +1943,14 @@ private sealed class BulletConstraintXNA : BulletConstraint /* TODO */ updatedEntityCount = 0; collidersCount = 0; - + int ret = PhysicsStep2(world,timeStep,maxSubSteps,fixedTimeStep,out updatedEntityCount,out world.physicsScene.m_updateArray, out collidersCount, out world.physicsScene.m_collisionArray); return ret; } - private int PhysicsStep2(BulletWorld pWorld, float timeStep, int m_maxSubSteps, float m_fixedTimeStep, + private int PhysicsStep2(BulletWorld pWorld, float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out EntityProperties[] updatedEntities, out int collidersCount, out CollisionDesc[] colliders) { @@ -1959,24 +1959,24 @@ private sealed class BulletConstraintXNA : BulletConstraint return epic; } - private int PhysicsStepint(BulletWorld pWorld,float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, + private int PhysicsStepint(BulletWorld pWorld,float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out EntityProperties[] updatedEntities, out int collidersCount, out CollisionDesc[] colliders, int maxCollisions, int maxUpdates) { int numSimSteps = 0; Array.Clear(UpdatedObjects, 0, UpdatedObjects.Length); Array.Clear(UpdatedCollisions, 0, UpdatedCollisions.Length); LastEntityProperty=0; - + LastCollisionDesc=0; - + updatedEntityCount = 0; collidersCount = 0; - + if (pWorld is BulletWorldXNA) { @@ -2033,7 +2033,7 @@ private sealed class BulletConstraintXNA : BulletConstraint collidersCount = LastCollisionDesc; colliders = UpdatedCollisions; - + } else @@ -2041,15 +2041,15 @@ private sealed class BulletConstraintXNA : BulletConstraint //if (updatedEntities is null) //updatedEntities = new List(); //updatedEntityCount = 0; - - + + //collidersCount = 0; - + updatedEntities = new EntityProperties[0]; - + colliders = new CollisionDesc[0]; - + } return numSimSteps; } @@ -2057,7 +2057,7 @@ private sealed class BulletConstraintXNA : BulletConstraint { IOverlappingPairCache cache = obj.GetOverlappingPairCache(); ObjectArray pairs = cache.GetOverlappingPairArray(); - + DiscreteDynamicsWorld world = (PhysicsScene.World as BulletWorldXNA).world; PersistentManifoldArray manifoldArray = new PersistentManifoldArray(); BroadphasePair collisionPair; @@ -2069,7 +2069,7 @@ private sealed class BulletConstraintXNA : BulletConstraint ManifoldPoint pt; int numPairs = pairs.Count; - + for (int i = 0; i < numPairs; i++) { manifoldArray.Clear(); @@ -2078,7 +2078,7 @@ private sealed class BulletConstraintXNA : BulletConstraint collisionPair = world.GetPairCache().FindPair(pairs[i].m_pProxy0, pairs[i].m_pProxy1); if (collisionPair == null) continue; - + collisionPair.m_algorithm.GetAllContactManifolds(manifoldArray); for (int j = 0; j < manifoldArray.Count; j++) { @@ -2101,7 +2101,7 @@ private sealed class BulletConstraintXNA : BulletConstraint } private static void RecordCollision(BSAPIXNA world, CollisionObject objA, CollisionObject objB, IndexedVector3 contact, IndexedVector3 norm, float penetration) { - + IndexedVector3 contactNormal = norm; if ((objA.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0 && (objB.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0) @@ -2171,11 +2171,11 @@ private sealed class BulletConstraintXNA : BulletConstraint if (NotMe is BulletBodyXNA && NotMe.HasPhysicalBody) { CollisionObject AvoidBody = (NotMe as BulletBodyXNA).body; - + IndexedVector3 rOrigin = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z); IndexedVector3 rEnd = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z - pRayHeight); using ( - ClosestNotMeRayResultCallback rayCallback = + ClosestNotMeRayResultCallback rayCallback = new ClosestNotMeRayResultCallback(rOrigin, rEnd, AvoidBody) ) { @@ -2191,9 +2191,9 @@ private sealed class BulletConstraintXNA : BulletConstraint return false; } } - - + + public class SimMotionState : DefaultMotionState { @@ -2286,12 +2286,12 @@ private sealed class BulletConstraintXNA : BulletConstraint m_lastProperties = m_properties; if (m_world.LastEntityProperty < m_world.UpdatedObjects.Length) m_world.UpdatedObjects[m_world.LastEntityProperty++]=(m_properties); - + //(*m_updatesThisFrame)[m_properties.ID] = &m_properties; } - - - + + + } public override void SetRigidBody(RigidBody body) @@ -2314,7 +2314,7 @@ private sealed class BulletConstraintXNA : BulletConstraint (((v1.Z - nEpsilon) < v2.Z) && (v2.Z < (v1.Z + nEpsilon))) && (((v1.W - nEpsilon) < v2.W) && (v2.W < (v1.W + nEpsilon))); } - + } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs index ac05979..4e067b5 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs @@ -115,7 +115,7 @@ public class BSActorAvatarMove : BSActor if (m_velocityMotor == null) { // Infinite decay and timescale values so motor only changes current to target values. - m_velocityMotor = new BSVMotor("BSCharacter.Velocity", + m_velocityMotor = new BSVMotor("BSCharacter.Velocity", 0.2f, // time scale BSMotor.Infinite, // decay time scale BSMotor.InfiniteVector, // friction timescale diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs index 6059af5..7801d8e 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorLockAxis.cs @@ -63,7 +63,7 @@ public class BSActorLockAxis : BSActor // BSActor.Refresh() public override void Refresh() { - m_physicsScene.DetailLog("{0},BSActorLockAxis,refresh,lockedAxis={1},enabled={2},pActive={3}", + m_physicsScene.DetailLog("{0},BSActorLockAxis,refresh,lockedAxis={1},enabled={2},pActive={3}", m_controllingPrim.LocalID, m_controllingPrim.LockedAxis, Enabled, m_controllingPrim.IsPhysicallyActive); // If all the axis are free, we don't need to exist if (m_controllingPrim.LockedAxis == m_controllingPrim.LockedAxisFree) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs index 5e3f1c4..fff63e4 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs @@ -117,7 +117,7 @@ public class BSActorCollection /// Each physical object can have 'actors' who are pushing the object around. /// This can be used for hover, locking axis, making vehicles, etc. /// Each physical object can have multiple actors acting on it. -/// +/// /// An actor usually registers itself with physics scene events (pre-step action) /// and modifies the parameters on the host physical object. /// diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs index bfeec24..3378c93 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs @@ -298,7 +298,7 @@ public abstract class BSAPITemplate { // Returns the name of the underlying Bullet engine public abstract string BulletEngineName { get; } -public abstract string BulletEngineVersion { get; protected set;} +public abstract string BulletEngineVersion { get; protected set;} // Initialization and simulation public abstract BulletWorld Initialize(Vector3 maxPosition, ConfigurationParameters parms, @@ -373,7 +373,7 @@ public abstract void DestroyObject(BulletWorld sim, BulletBody obj); // ===================================================================================== public abstract BulletShape CreateGroundPlaneShape(UInt32 id, float height, float collisionMargin); -public abstract BulletShape CreateTerrainShape(UInt32 id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, +public abstract BulletShape CreateTerrainShape(UInt32 id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, float scaleFactor, float collisionMargin); // ===================================================================================== @@ -388,7 +388,7 @@ public abstract BulletConstraint Create6DofConstraintToPoint(BulletWorld world, bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); public abstract BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1, - Vector3 frameInBloc, Quaternion frameInBrot, + Vector3 frameInBloc, Quaternion frameInBrot, bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies); public abstract BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index e12fc8e..542f732 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -371,7 +371,7 @@ public sealed class BSCharacter : BSPhysObject public override float Mass { get { return _mass; } } // used when we only want this prim's mass and not the linkset thing - public override float RawMass { + public override float RawMass { get {return _mass; } } public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) @@ -586,7 +586,7 @@ public sealed class BSCharacter : BSPhysObject } public override float ForceBuoyancy { get { return _buoyancy; } - set { + set { PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); _buoyancy = value; @@ -647,7 +647,7 @@ public sealed class BSCharacter : BSPhysObject private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size) { OMV.Vector3 newScale; - + // Bullet's capsule total height is the "passed height + radius * 2"; // The base capsule is 1 diameter and 2 height (passed radius=0.5, passed height = 1) // The number we pass in for 'scaling' is the multiplier to get that base diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs index 7714a03..ed89f63 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintHinge.cs @@ -45,7 +45,7 @@ public sealed class BSConstraintHinge : BSConstraint m_body1 = obj1; m_body2 = obj2; m_constraint = PhysicsScene.PE.CreateHingeConstraint(world, obj1, obj2, - pivotInA, pivotInB, axisInA, axisInB, + pivotInA, pivotInB, axisInA, axisInB, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); m_enabled = true; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 56d2415..f535e50 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -1019,7 +1019,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 origVelW = VehicleVelocity; // DEBUG DEBUG VehicleVelocity /= VehicleVelocity.Length(); VehicleVelocity *= BSParam.VehicleMaxLinearVelocity; - VDetailLog("{0}, MoveLinear,clampMax,origVelW={1},lenSq={2},maxVelSq={3},,newVelW={4}", + VDetailLog("{0}, MoveLinear,clampMax,origVelW={1},lenSq={2},maxVelSq={3},,newVelW={4}", ControllingPrim.LocalID, origVelW, newVelocityLengthSq, BSParam.VehicleMaxLinearVelocitySquared, VehicleVelocity); } else if (newVelocityLengthSq < 0.001f) @@ -1094,7 +1094,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin if (VehiclePosition.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = VehiclePosition.Z; } - + if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) { if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f) @@ -1188,7 +1188,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // used with conjunction with banking: the strength of the banking will decay when the // vehicle no longer experiences collisions. The decay timescale is the same as // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering - // when they are in mid jump. + // when they are in mid jump. // TODO: this code is wrong. Also, what should it do for boats (height from water)? // This is just using the ground and a general collision check. Should really be using // a downward raycast to find what is below. @@ -1254,7 +1254,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin VehicleAddForce(appliedGravity); - VDetailLog("{0}, MoveLinear,applyGravity,vehGrav={1},collid={2},fudge={3},mass={4},appliedForce={3}", + VDetailLog("{0}, MoveLinear,applyGravity,vehGrav={1},collid={2},fudge={3},mass={4},appliedForce={3}", ControllingPrim.LocalID, m_VehicleGravity, ControllingPrim.IsColliding, BSParam.VehicleGroundGravityFudge, m_vehicleMass, appliedGravity); } @@ -1330,7 +1330,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : // This flag prevents linear deflection parallel to world z-axis. This is useful // for preventing ground vehicles with large linear deflection, like bumper cars, - // from climbing their linear deflection into the sky. + // from climbing their linear deflection into the sky. // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement // TODO: This is here because this is where ODE put it but documentation says it // is a linear effect. Where should this check go? @@ -1463,7 +1463,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin VehicleRotationalVelocity += (vertContributionV * VehicleOrientation); VDetailLog("{0}, MoveAngular,verticalAttraction,,origRotVW={1},vertError={2},unscaledV={3},eff={4},ts={5},vertContribV={6}", - Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, + Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, vertContributionV); */ } @@ -1530,13 +1530,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude // of the yaw effect will be proportional to the // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's - // velocity along its preferred axis of motion. + // velocity along its preferred axis of motion. // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any // positive rotation (by the right-hand rule) about the roll-axis will effect a // (negative) torque around the yaw-axis, making it turn to the right--that is the // vehicle will lean into the turn, which is how real airplanes and motorcycle's work. // Negating the banking coefficient will make it so that the vehicle leans to the - // outside of the turn (not very "physical" but might allow interesting vehicles so why not?). + // outside of the turn (not very "physical" but might allow interesting vehicles so why not?). // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making // banking vehicles do what you want rather than what the laws of physics allow. // For example, consider a real motorcycle...it must be moving forward in order for @@ -1548,11 +1548,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the // banking effect depends only on the vehicle's rotation about its roll-axis compared // to "dynamic" where the banking is also proportional to its velocity along its - // roll-axis. Finding the best value of the "mixture" will probably require trial and error. + // roll-axis. Finding the best value of the "mixture" will probably require trial and error. // The time it takes for the banking behavior to defeat a preexisting angular velocity about the // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to // bank quickly then give it a banking timescale of about a second or less, otherwise you can - // make a sluggish vehicle by giving it a timescale of several seconds. + // make a sluggish vehicle by giving it a timescale of several seconds. public void ComputeAngularBanking() { if (enableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff) @@ -1581,7 +1581,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin //VehicleRotationalVelocity += bankingContributionV * VehicleOrientation; VehicleRotationalVelocity += bankingContributionV; - + VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", ControllingPrim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 6d0d0eb..76c2187 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -214,7 +214,7 @@ public abstract class BSLinkset // I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. protected abstract void AddChildToLinkset(BSPrimLinkable child); - + // I am the root of a linkset and one of my children is being removed. // Safe to call even if the child is not really in my linkset. protected abstract void RemoveChildFromLinkset(BSPrimLinkable child); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index 01ada3f..1fd541f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -114,7 +114,7 @@ public sealed class BSLinksetCompound : BSLinkset // Schedule a refresh to happen after all the other taint processing. private void ScheduleRebuild(BSPrimLinkable requestor) { - DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", + DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); // When rebuilding, it is possible to set properties that would normally require a rebuild. // If already rebuilding, don't request another rebuild. @@ -208,7 +208,7 @@ public sealed class BSLinksetCompound : BSLinkset // and that is caused by us updating the object. if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) { - // Find the physical instance of the child + // Find the physical instance of the child if (LinksetRoot.PhysShape.HasPhysicalShape && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo)) { // It is possible that the linkset is still under construction and the child is not yet diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs index 9501e2d..0128d8d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs @@ -102,7 +102,7 @@ public class BSVMotor : BSMotor return ErrorIsZero(LastError); } public virtual bool ErrorIsZero(Vector3 err) - { + { return (err == Vector3.Zero || err.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)); } @@ -115,7 +115,7 @@ public class BSVMotor : BSMotor CurrentValue = TargetValue = Vector3.Zero; ErrorZeroThreshold = 0.001f; } - public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) + public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) : this(useName) { TimeScale = timeScale; @@ -237,7 +237,7 @@ public class BSVMotor : BSMotor MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName); MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}", BSScene.DetailLogZero, UseName, - TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency, + TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency, CurrentValue, TargetValue); LastError = BSMotor.InfiniteVector; @@ -248,7 +248,7 @@ public class BSVMotor : BSMotor BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep); } MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName); - + } @@ -279,7 +279,7 @@ public class BSFMotor : BSMotor return ErrorIsZero(LastError); } public virtual bool ErrorIsZero(float err) - { + { return (err >= -ErrorZeroThreshold && err <= ErrorZeroThreshold); } @@ -410,7 +410,7 @@ public class BSPIDVMotor : BSVMotor // The factors are vectors for the three dimensions. This is the proportional of each // that is applied. This could be multiplied through the actual factors but it // is sometimes easier to manipulate the factors and their mix separately. - // to + // to public Vector3 FactorMix; // Arbritrary factor range. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 980d405..5ebeace 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -37,7 +37,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public static class BSParam { - private static string LogHeader = "[BULLETSIM PARAMETERS]"; + private static string LogHeader = "[BULLETSIM PARAMETERS]"; // Tuning notes: // From: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6575 @@ -51,7 +51,7 @@ public static class BSParam // This is separate/independent from the collision margin. The collision margin increases the object a bit // to improve collision detection performance and accuracy. // =================== - // From: + // From: // Level of Detail values kept as float because that's what the Meshmerizer wants public static float MeshLOD { get; private set; } @@ -636,7 +636,7 @@ public static class BSParam new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", false, (s) => { return ShouldDisableContactPoolDynamicAllocation; }, - (s,v) => { ShouldDisableContactPoolDynamicAllocation = v; + (s,v) => { ShouldDisableContactPoolDynamicAllocation = v; s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = NumericBool(v); } ), new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", false, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 6a3ada2..28d4bd7 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -38,7 +38,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin * Class to wrap all objects. * The rest of BulletSim doesn't need to keep checking for avatars or prims * unless the difference is significant. - * + * * Variables in the physicsl objects are in three forms: * VariableName: used by the simulator and performs taint operations, etc * RawVariableName: direct reference to the BulletSim storage for the variable value @@ -52,7 +52,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v - * BS.ApplyCentralForce BS.ApplyTorque + * BS.ApplyCentralForce BS.ApplyTorque */ // Flags used to denote which properties updates when making UpdateProperties calls to linksets, etc. @@ -353,7 +353,7 @@ public abstract class BSPhysObject : PhysicsActor } public override bool CollidingObj { get { return (CollidingObjectStep == PhysScene.SimulationStep); } - set { + set { if (value) CollidingObjectStep = PhysScene.SimulationStep; else @@ -447,7 +447,7 @@ public abstract class BSPhysObject : PhysicsActor // The CollisionCollection instance is passed around in the simulator. // Make sure we don't have a handle to that one and that a new one is used for next time. - // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here, + // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here, // a race condition is created for the other users of this instance. CollisionCollection = new CollisionEventUpdate(); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 0d45579..7e2af78 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -420,7 +420,7 @@ public class BSPrim : BSPhysObject get { return _mass; } } // used when we only want this prim's mass and not the linkset thing - public override float RawMass { + public override float RawMass { get { return _mass; } } // Set the physical mass to the passed mass. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index bfc7ae7..5c58ad5 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -142,7 +142,7 @@ public abstract class BSShape // If the shape was successfully created, nothing more to do if (newShape.HasPhysicalShape) return newShape; - + // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been // fetched but we end up here again, the meshing of the asset must have failed. // Prevent trying to keep fetching the mesh by declaring failure. @@ -155,7 +155,7 @@ public abstract class BSShape else { // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset - if (prim.BaseShape.SculptEntry + if (prim.BaseShape.SculptEntry && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting && prim.BaseShape.SculptTexture != OMV.UUID.Zero @@ -164,7 +164,7 @@ public abstract class BSShape physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset", prim.LocalID); // Multiple requestors will know we're waiting for this asset prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; - + BSPhysObject xprim = prim; Util.FireAndForget(delegate { @@ -245,8 +245,8 @@ public class BSShapeNative : BSShape { } - public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, - BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) + public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, + BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) { // Native shapes are not shared and are always built anew. return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey)); @@ -379,7 +379,7 @@ public class BSShapeMesh : BSShape { BulletShape newShape = null; - IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, + IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, false, // say it is not physical so a bounding box is not built false // do not cache the mesh and do not use previously built versions ); @@ -671,8 +671,8 @@ public class BSShapeCompound : BSShape public BSShapeCompound(BulletShape pShape) : base(pShape) { } - public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim) - { + public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim) + { // Compound shapes are not shared so a new one is created every time. return new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene, prim)); } @@ -750,8 +750,8 @@ public class BSShapeAvatar : BSShape public BSShapeAvatar() : base() { } - public static BSShape GetReference(BSPhysObject prim) - { + public static BSShape GetReference(BSPhysObject prim) + { return new BSShapeNull(); } public override void Dereference(BSScene physicsScene) { } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs index 5a19797..c7deb4e 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs @@ -68,7 +68,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys // This minCoords and maxCoords passed in give the size of the terrain (min and max Z // are the high and low points of the heightmap). - public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, + public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, Vector3 minCoords, Vector3 maxCoords) : base(physicsScene, regionBase, id) { @@ -92,7 +92,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys private void BuildHeightmapTerrain() { // Create the terrain shape from the mapInfo - m_mapInfo.terrainShape = m_physicsScene.PE.CreateTerrainShape( m_mapInfo.ID, + m_mapInfo.terrainShape = m_physicsScene.PE.CreateTerrainShape( m_mapInfo.ID, new Vector3(m_mapInfo.sizeX, m_mapInfo.sizeY, 0), m_mapInfo.minZ, m_mapInfo.maxZ, m_mapInfo.heightMap, 1f, BSParam.TerrainCollisionMargin); @@ -103,7 +103,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f); centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f); - m_mapInfo.terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, + m_mapInfo.terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, m_mapInfo.ID, centerPos, Quaternion.Identity); // Set current terrain attributes diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 0d16eda..c4807c4 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -135,7 +135,7 @@ public sealed class BSTerrainManager : IDisposable DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName); // The ground plane is here to catch things that are trying to drop to negative infinity BulletShape groundPlaneShape = m_physicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); - m_groundPlane = m_physicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, + m_groundPlane = m_physicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity); m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_groundPlane); @@ -318,8 +318,8 @@ public sealed class BSTerrainManager : IDisposable // TODO: redo terrain implementation selection to allow other base types than heightMap. private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) { - m_physicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", - LogHeader, m_physicsScene.RegionName, terrainRegionBase, + m_physicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", + LogHeader, m_physicsScene.RegionName, terrainRegionBase, (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation); BSTerrainPhys newTerrainPhys = null; switch ((int)BSParam.TerrainImplementation) @@ -334,8 +334,8 @@ public sealed class BSTerrainManager : IDisposable break; default: m_physicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", - LogHeader, - (int)BSParam.TerrainImplementation, + LogHeader, + (int)BSParam.TerrainImplementation, BSParam.TerrainImplementation, m_physicsScene.RegionName, terrainRegionBase); break; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index ee2a1f2..e4ca098 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -51,7 +51,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys BulletShape m_terrainShape; BulletBody m_terrainBody; - public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) : base(physicsScene, regionBase, id) { } @@ -62,7 +62,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys } // Create terrain mesh from a heightmap. - public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, Vector3 minCoords, Vector3 maxCoords) : base(physicsScene, regionBase, id) { @@ -104,7 +104,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys return; } - m_physicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length); m_terrainShape = m_physicsScene.PE.CreateMeshShape(m_physicsScene.World, indicesCount, indices, verticesCount, vertices); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs index 906e4f9..d5060e3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs @@ -224,42 +224,42 @@ public static class BulletSimData // As mentioned above, don't use the CollisionFilterGroups definitions directly in the code // but, instead, use references to this dictionary. Finding and debugging // collision flag problems will be made easier. -public static Dictionary CollisionTypeMasks +public static Dictionary CollisionTypeMasks = new Dictionary() { - { CollisionType.Avatar, - new CollisionTypeFilterGroup(CollisionType.Avatar, - (uint)CollisionFilterGroups.BCharacterGroup, + { CollisionType.Avatar, + new CollisionTypeFilterGroup(CollisionType.Avatar, + (uint)CollisionFilterGroups.BCharacterGroup, (uint)CollisionFilterGroups.BAllGroup) }, - { CollisionType.Groundplane, - new CollisionTypeFilterGroup(CollisionType.Groundplane, - (uint)CollisionFilterGroups.BGroundPlaneGroup, + { CollisionType.Groundplane, + new CollisionTypeFilterGroup(CollisionType.Groundplane, + (uint)CollisionFilterGroups.BGroundPlaneGroup, (uint)CollisionFilterGroups.BAllGroup) }, - { CollisionType.Terrain, - new CollisionTypeFilterGroup(CollisionType.Terrain, - (uint)CollisionFilterGroups.BTerrainGroup, + { CollisionType.Terrain, + new CollisionTypeFilterGroup(CollisionType.Terrain, + (uint)CollisionFilterGroups.BTerrainGroup, (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup)) }, - { CollisionType.Static, - new CollisionTypeFilterGroup(CollisionType.Static, - (uint)CollisionFilterGroups.BStaticGroup, + { CollisionType.Static, + new CollisionTypeFilterGroup(CollisionType.Static, + (uint)CollisionFilterGroups.BStaticGroup, (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup)) }, - { CollisionType.Dynamic, - new CollisionTypeFilterGroup(CollisionType.Dynamic, - (uint)CollisionFilterGroups.BSolidGroup, + { CollisionType.Dynamic, + new CollisionTypeFilterGroup(CollisionType.Dynamic, + (uint)CollisionFilterGroups.BSolidGroup, (uint)(CollisionFilterGroups.BAllGroup)) }, - { CollisionType.VolumeDetect, - new CollisionTypeFilterGroup(CollisionType.VolumeDetect, - (uint)CollisionFilterGroups.BSensorTrigger, + { CollisionType.VolumeDetect, + new CollisionTypeFilterGroup(CollisionType.VolumeDetect, + (uint)CollisionFilterGroups.BSensorTrigger, (uint)(~CollisionFilterGroups.BSensorTrigger)) }, { CollisionType.LinksetChild, - new CollisionTypeFilterGroup(CollisionType.LinksetChild, - (uint)CollisionFilterGroups.BLinksetChildGroup, + new CollisionTypeFilterGroup(CollisionType.LinksetChild, + (uint)CollisionFilterGroups.BLinksetChildGroup, (uint)(CollisionFilterGroups.BNoneGroup)) // (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup)) }, -- cgit v1.1 From 12054aaa9ff66cc785d2018526ff6f3e94f86915 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Mon, 29 Apr 2013 17:14:44 +0100 Subject: Fix bug where an agent that declined an inventory offer and subsequently emptied their trash would make the item invalid in the giver's inventory This was because the original item/folder ID was sent in the session slot of the offer IM rather than the copy. --- .../Avatar/Inventory/Transfer/InventoryTransferModule.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index bcb7f42..e1ada97 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs @@ -213,7 +213,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer user.ControllingClient.SendBulkUpdateInventory(folderCopy); // HACK!! - im.imSessionID = folderID.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; } else { @@ -243,7 +246,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 -- cgit v1.1 From a7cbb9edc98fabc6d2705d69310f4356e84c7596 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Mon, 29 Apr 2013 20:50:49 +0100 Subject: Add regression test for offer, accept and subsequent receiver delete of an item offered via instant message. --- .../Inventory/Transfer/InventoryTransferModule.cs | 83 +--------------------- OpenSim/Tests/Common/Mock/TestClient.cs | 12 ++++ prebuild.xml | 1 + 3 files changed, 16 insertions(+), 80 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index e1ada97..c292700 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs @@ -47,10 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer /// private List m_Scenelist = new List(); -// private Dictionary m_AgentRegions = -// new Dictionary(); - private IMessageTransferModule m_TransferModule = null; + private IMessageTransferModule m_TransferModule; private bool m_Enabled = true; #region Region Module interface @@ -81,9 +79,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer // scene.RegisterModuleInterface(this); scene.EventManager.OnNewClient += OnNewClient; -// scene.EventManager.OnClientClosed += ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; -// scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene; } public void RegionLoaded(Scene scene) @@ -96,11 +92,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only"); m_Enabled = false; - m_Scenelist.Clear(); - scene.EventManager.OnNewClient -= OnNewClient; -// scene.EventManager.OnClientClosed -= ClientLoggedOut; +// m_Scenelist.Clear(); +// scene.EventManager.OnNewClient -= OnNewClient; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; -// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; } } } @@ -108,9 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer public void RemoveRegion(Scene scene) { scene.EventManager.OnNewClient -= OnNewClient; -// scene.EventManager.OnClientClosed -= ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; -// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; m_Scenelist.Remove(scene); } @@ -139,11 +131,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer // Inventory giving is conducted via instant message client.OnInstantMessage += OnInstantMessage; } - -// protected void OnSetRootAgentScene(UUID id, Scene scene) -// { -// m_AgentRegions[id] = scene; -// } private Scene FindClientScene(UUID agentId) { @@ -460,70 +447,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer } } -// public bool NeedSceneCacheClear(UUID agentID, Scene scene) -// { -// if (!m_AgentRegions.ContainsKey(agentID)) -// { -// // Since we can get here two ways, we need to scan -// // the scenes here. This is somewhat more expensive -// // but helps avoid a nasty bug -// // -// -// foreach (Scene s in m_Scenelist) -// { -// ScenePresence presence; -// -// if (s.TryGetScenePresence(agentID, out presence)) -// { -// // If the agent is in this scene, then we -// // are being called twice in a single -// // teleport. This is wasteful of cycles -// // but harmless due to this 2nd level check -// // -// // If the agent is found in another scene -// // then the list wasn't current -// // -// // If the agent is totally unknown, then what -// // are we even doing here?? -// // -// if (s == scene) -// { -// //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName); -// return true; -// } -// else -// { -// //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName); -// return false; -// } -// } -// } -// //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName); -// return true; -// } -// -// // The agent is left in current Scene, so we must be -// // going to another instance -// // -// if (m_AgentRegions[agentID] == scene) -// { -// //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName); -// m_AgentRegions.Remove(agentID); -// return true; -// } -// -// // Another region has claimed the agent -// // -// //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName); -// return false; -// } -// -// public void ClientLoggedOut(UUID agentID, Scene scene) -// { -// if (m_AgentRegions.ContainsKey(agentID)) -// m_AgentRegions.Remove(agentID); -// } - /// /// /// diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d26e3f7..b7a7463 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -484,6 +484,18 @@ namespace OpenSim.Tests.Common.Mock OnCompleteMovementToRegion(this, true); } + /// + /// Emulate sending an IM from the viewer to the simulator. + /// + /// + public void HandleImprovedInstantMessage(GridInstantMessage im) + { + ImprovedInstantMessage handlerInstantMessage = OnInstantMessage; + + if (handlerInstantMessage != null) + handlerInstantMessage(this, im); + } + public virtual void ActivateGesture(UUID assetId, UUID gestureId) { } diff --git a/prebuild.xml b/prebuild.xml index 29d7c90..9fbe08b 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -3055,6 +3055,7 @@ + -- cgit v1.1 From 0beccf23c0c2e7b2420f4f150d5f2566f0d63370 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Mon, 29 Apr 2013 21:11:44 +0100 Subject: Add regression test for inventory item give, reject and subsequent trash folder purge by receiver. This commit also actually adds the InventoryTransferModuleTests file which I previously forgot --- .../Transfer/Tests/InventoryTransferModuleTests.cs | 256 +++++++++++++++++++++ OpenSim/Tests/Common/Mock/TestClient.cs | 4 +- 2 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs new file mode 100644 index 0000000..b07d38c --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs @@ -0,0 +1,256 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net.Config; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenMetaverse.Assets; +using OpenSim.Framework; +using OpenSim.Region.CoreModules.Avatar.Inventory.Transfer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Tests.Common; +using OpenSim.Tests.Common.Mock; + +namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests +{ + [TestFixture] + public class InventoryTransferModuleTests : OpenSimTestCase + { + protected TestScene m_scene; + + [SetUp] + public override void SetUp() + { + base.SetUp(); + + IConfigSource config = new IniConfigSource(); + config.AddConfig("Messaging"); + config.Configs["Messaging"].Set("InventoryTransferModule", "InventoryTransferModule"); + + m_scene = new SceneHelpers().SetupScene(); + SceneHelpers.SetupSceneModules(m_scene, config, new InventoryTransferModule()); + } + + [Test] + public void TestAcceptGivenItem() + { +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID itemId = TestHelpers.ParseTail(0x100); + UUID assetId = TestHelpers.ParseTail(0x200); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + // Create the object to test give + InventoryItemBase originalItem + = UserInventoryHelpers.CreateInventoryItem( + m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object); + + byte[] giveImBinaryBucket = new byte[17]; + byte[] itemIdBytes = itemId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + GridInstantMessage acceptIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryAccepted, + false, + "inventory accepted msg", + initialSessionId, + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(acceptIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryItemBase originalItemAfterGive + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterGive, Is.Not.Null); + Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID)); + + // Test for item successfully making it into the receiver's inventory + InventoryItemBase receivedItem + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Objects/givenObj"); + + Assert.That(receivedItem, Is.Not.Null); + Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.DeleteItems(receiverSp.UUID, new List() { receivedItem.ID }); + + InventoryItemBase originalItemAfterDelete + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterDelete, Is.Not.Null); + + // TODO: Test scenario where giver deletes their item first. + } + + /// + /// Test user rejection of a given item. + /// + /// + /// A rejected item still ends up in the user's trash folder. + /// + [Test] + public void TestRejectGivenItem() + { +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID itemId = TestHelpers.ParseTail(0x100); + UUID assetId = TestHelpers.ParseTail(0x200); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + // Create the object to test give + InventoryItemBase originalItem + = UserInventoryHelpers.CreateInventoryItem( + m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object); + + GridInstantMessage receivedIm = null; + receiverClient.OnReceivedInstantMessage += im => receivedIm = im; + + byte[] giveImBinaryBucket = new byte[17]; + byte[] itemIdBytes = itemId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + // Session ID is now the created item ID (!) + GridInstantMessage rejectIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryDeclined, + false, + "inventory declined msg", + new UUID(receivedIm.imSessionID), + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(rejectIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryItemBase originalItemAfterGive + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterGive, Is.Not.Null); + Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID)); + + // Test for item successfully making it into the receiver's inventory + InventoryItemBase receivedItem + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Trash/givenObj"); + + InventoryFolderBase trashFolder + = m_scene.InventoryService.GetFolderForType(receiverSp.UUID, AssetType.TrashFolder); + + Assert.That(receivedItem, Is.Not.Null); + Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID)); + Assert.That(receivedItem.Folder, Is.EqualTo(trashFolder.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.PurgeFolder(trashFolder); + + InventoryItemBase originalItemAfterDelete + = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); + + Assert.That(originalItemAfterDelete, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index b7a7463..41402a4 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -61,6 +61,7 @@ namespace OpenSim.Tests.Common.Mock // Test client specific events - for use by tests to implement some IClientAPI behaviour. public event Action OnReceivedMoveAgentIntoRegion; public event Action OnTestClientInformClientOfNeighbour; + public event Action OnReceivedInstantMessage; // disable warning: public events, part of the public API #pragma warning disable 67 @@ -550,7 +551,8 @@ namespace OpenSim.Tests.Common.Mock public void SendInstantMessage(GridInstantMessage im) { - + if (OnReceivedInstantMessage != null) + OnReceivedInstantMessage(im); } public void SendGenericMessage(string method, UUID invoice, List message) -- cgit v1.1 From 67789201c3ae40bd4697f3479eb183599baa3658 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Mon, 29 Apr 2013 22:14:13 +0100 Subject: Add TestRejectGivenFolder() and TestAcceptGivenFolder() regression tests --- .../Inventory/Transfer/InventoryTransferModule.cs | 10 +- .../Transfer/Tests/InventoryTransferModuleTests.cs | 193 +++++++++++++++++++++ .../Tests/Common/Helpers/UserInventoryHelpers.cs | 41 ++++- .../Tests/Common/Mock/TestXInventoryDataPlugin.cs | 28 ++- 4 files changed, 262 insertions(+), 10 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index c292700..1417a19 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs @@ -175,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); @@ -200,7 +200,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer user.ControllingClient.SendBulkUpdateInventory(folderCopy); // HACK!! - // Insert the ID of the copied item into the IM so that we know which item to move to trash if it + // 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; @@ -396,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; diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs index b07d38c..162a0c3 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs @@ -252,5 +252,198 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests Assert.That(originalItemAfterDelete, Is.Not.Null); } + + [Test] + public void TestAcceptGivenFolder() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID folderId = TestHelpers.ParseTail(0x100); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + InventoryFolderBase originalFolder + = UserInventoryHelpers.CreateInventoryFolder( + m_scene.InventoryService, giverSp.UUID, folderId, "f1", true); + + byte[] giveImBinaryBucket = new byte[17]; + giveImBinaryBucket[0] = (byte)AssetType.Folder; + byte[] itemIdBytes = folderId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + GridInstantMessage acceptIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryAccepted, + false, + "inventory accepted msg", + initialSessionId, + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(acceptIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryFolderBase originalFolderAfterGive + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterGive, Is.Not.Null); + Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID)); + + // Test for item successfully making it into the receiver's inventory + InventoryFolderBase receivedFolder + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "f1"); + + Assert.That(receivedFolder, Is.Not.Null); + Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.DeleteFolders(receiverSp.UUID, new List() { receivedFolder.ID }); + + InventoryFolderBase originalFolderAfterDelete + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterDelete, Is.Not.Null); + + // TODO: Test scenario where giver deletes their item first. + } + + /// + /// Test user rejection of a given item. + /// + /// + /// A rejected item still ends up in the user's trash folder. + /// + [Test] + public void TestRejectGivenFolder() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID initialSessionId = TestHelpers.ParseTail(0x10); + UUID folderId = TestHelpers.ParseTail(0x100); + + UserAccount ua1 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); + UserAccount ua2 + = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); + + ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); + TestClient giverClient = (TestClient)giverSp.ControllingClient; + + ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); + TestClient receiverClient = (TestClient)receiverSp.ControllingClient; + + // Create the folder to test give + InventoryFolderBase originalFolder + = UserInventoryHelpers.CreateInventoryFolder( + m_scene.InventoryService, giverSp.UUID, folderId, "f1", true); + + GridInstantMessage receivedIm = null; + receiverClient.OnReceivedInstantMessage += im => receivedIm = im; + + byte[] giveImBinaryBucket = new byte[17]; + giveImBinaryBucket[0] = (byte)AssetType.Folder; + byte[] itemIdBytes = folderId.GetBytes(); + Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); + + GridInstantMessage giveIm + = new GridInstantMessage( + m_scene, + giverSp.UUID, + giverSp.Name, + receiverSp.UUID, + (byte)InstantMessageDialog.InventoryOffered, + false, + "inventory offered msg", + initialSessionId, + false, + Vector3.Zero, + giveImBinaryBucket, + true); + + giverClient.HandleImprovedInstantMessage(giveIm); + + // These details might not all be correct. + // Session ID is now the created item ID (!) + GridInstantMessage rejectIm + = new GridInstantMessage( + m_scene, + receiverSp.UUID, + receiverSp.Name, + giverSp.UUID, + (byte)InstantMessageDialog.InventoryDeclined, + false, + "inventory declined msg", + new UUID(receivedIm.imSessionID), + false, + Vector3.Zero, + null, + true); + + receiverClient.HandleImprovedInstantMessage(rejectIm); + + // Test for item remaining in the giver's inventory (here we assume a copy item) + // TODO: Test no-copy items. + InventoryFolderBase originalFolderAfterGive + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterGive, Is.Not.Null); + Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID)); + + // Test for folder successfully making it into the receiver's inventory + InventoryFolderBase receivedFolder + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "Trash/f1"); + + InventoryFolderBase trashFolder + = m_scene.InventoryService.GetFolderForType(receiverSp.UUID, AssetType.TrashFolder); + + Assert.That(receivedFolder, Is.Not.Null); + Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID)); + Assert.That(receivedFolder.ParentID, Is.EqualTo(trashFolder.ID)); + + // Test that on a delete, item still exists and is accessible for the giver. + m_scene.InventoryService.PurgeFolder(trashFolder); + + InventoryFolderBase originalFolderAfterDelete + = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); + + Assert.That(originalFolderAfterDelete, Is.Not.Null); + } } } \ No newline at end of file diff --git a/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs b/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs index a1794c9..b3b75af 100644 --- a/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs +++ b/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs @@ -218,12 +218,37 @@ namespace OpenSim.Tests.Common public static InventoryFolderBase CreateInventoryFolder( IInventoryService inventoryService, UUID userId, string path, bool useExistingFolders) { + return CreateInventoryFolder(inventoryService, userId, UUID.Random(), path, useExistingFolders); + } + + /// + /// Create inventory folders starting from the user's root folder. + /// + /// + /// + /// + /// + /// The folders to create. Multiple folders can be specified on a path delimited by the PATH_DELIMITER + /// + /// + /// If true, then folders in the path which already the same name are + /// used. This applies to the terminal folder as well. + /// If false, then all folders in the path are created, even if there is already a folder at a particular + /// level with the same name. + /// + /// + /// The folder created. If the path contains multiple folders then the last one created is returned. + /// Will return null if the root folder could not be found. + /// + public static InventoryFolderBase CreateInventoryFolder( + IInventoryService inventoryService, UUID userId, UUID folderId, string path, bool useExistingFolders) + { InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId); if (null == rootFolder) return null; - return CreateInventoryFolder(inventoryService, rootFolder, path, useExistingFolders); + return CreateInventoryFolder(inventoryService, folderId, rootFolder, path, useExistingFolders); } /// @@ -235,6 +260,7 @@ namespace OpenSim.Tests.Common /// TODO: May need to make it an option to create duplicate folders. /// /// + /// ID of the folder to create /// /// /// The folder to create. @@ -249,7 +275,7 @@ namespace OpenSim.Tests.Common /// The folder created. If the path contains multiple folders then the last one created is returned. /// public static InventoryFolderBase CreateInventoryFolder( - IInventoryService inventoryService, InventoryFolderBase parentFolder, string path, bool useExistingFolders) + IInventoryService inventoryService, UUID folderId, InventoryFolderBase parentFolder, string path, bool useExistingFolders) { string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); @@ -262,9 +288,16 @@ namespace OpenSim.Tests.Common { // Console.WriteLine("Creating folder {0} at {1}", components[0], parentFolder.Name); + UUID folderIdForCreate; + + if (components.Length > 1) + folderIdForCreate = UUID.Random(); + else + folderIdForCreate = folderId; + folder = new InventoryFolderBase( - UUID.Random(), components[0], parentFolder.Owner, (short)AssetType.Unknown, parentFolder.ID, 0); + folderIdForCreate, components[0], parentFolder.Owner, (short)AssetType.Unknown, parentFolder.ID, 0); inventoryService.AddFolder(folder); } @@ -274,7 +307,7 @@ namespace OpenSim.Tests.Common // } if (components.Length > 1) - return CreateInventoryFolder(inventoryService, folder, components[1], useExistingFolders); + return CreateInventoryFolder(inventoryService, folderId, folder, components[1], useExistingFolders); else return folder; } diff --git a/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs b/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs index ccbdf81..30b1f38 100644 --- a/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs +++ b/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs @@ -53,6 +53,9 @@ namespace OpenSim.Tests.Common.Mock public XInventoryFolder[] GetFolders(string[] fields, string[] vals) { +// Console.WriteLine( +// "Requesting folders, fields {0}, vals {1}", string.Join(",", fields), string.Join(",", vals)); + List origFolders = Get(fields, vals, m_allFolders.Values.ToList()); @@ -104,7 +107,30 @@ namespace OpenSim.Tests.Common.Mock } public bool MoveItem(string id, string newParent) { throw new NotImplementedException(); } - public bool MoveFolder(string id, string newParent) { throw new NotImplementedException(); } + + public bool MoveFolder(string id, string newParent) + { + // Don't use GetFolders() here - it takes a clone! + XInventoryFolder folder = m_allFolders[new UUID(id)]; + + if (folder == null) + return false; + + folder.parentFolderID = new UUID(newParent); + + XInventoryFolder[] newParentFolders + = GetFolders(new string[] { "folderID" }, new string[] { folder.parentFolderID.ToString() }); + +// Console.WriteLine( +// "Moved folder {0} {1}, to {2} {3}", +// folder.folderName, folder.folderID, newParentFolders[0].folderName, folder.parentFolderID); + + // TODO: Really need to implement folder version incrementing, though this should be common code anyway, + // not reimplemented in each db plugin. + + return true; + } + public XInventoryItem[] GetActiveGestures(UUID principalID) { throw new NotImplementedException(); } public int GetAssetPermissions(UUID principalID, UUID assetID) { throw new NotImplementedException(); } } -- cgit v1.1 From 537b243360a69f63210003371d1e6ef9f9c3716c Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Mon, 29 Apr 2013 22:18:11 +0100 Subject: minor: Eliminate warning in LLimageManagerTests by properly calling through to OpenSimTestCase.SetUp() --- OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs index 7d9f581..a0e0078 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs @@ -75,6 +75,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests [SetUp] public void SetUp() { + base.SetUp(); + UUID userId = TestHelpers.ParseTail(0x3); J2KDecoderModule j2kdm = new J2KDecoderModule(); -- cgit v1.1 From 3ce198165c3533d416148a9f4ffa056bf22c602d Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Mon, 29 Apr 2013 22:21:57 +0100 Subject: minor: remove some mono compiler warnings in ServicesServerBase --- OpenSim/Server/Base/ServicesServerBase.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/OpenSim/Server/Base/ServicesServerBase.cs b/OpenSim/Server/Base/ServicesServerBase.cs index 7c8e6b7..b13c87d 100644 --- a/OpenSim/Server/Base/ServicesServerBase.cs +++ b/OpenSim/Server/Base/ServicesServerBase.cs @@ -171,11 +171,6 @@ namespace OpenSim.Server.Base m_console = MainConsole.Instance; - // Configure the appenders for log4net - // - OpenSimAppender consoleAppender = null; - FileAppender fileAppender = null; - if (logConfig != null) { FileInfo cfg = new FileInfo(logConfig); -- cgit v1.1 From 15a3f80e2eb82c5e4726d5cd93cfed32da5d6768 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 29 Apr 2013 17:30:38 -0700 Subject: BulletSim: LinksetCompound work to disable collision for root and child prims so compound shape can do all collisions. Don't try to build a compound linkset for non-physical linksets. Remove and replace root body when compound shape is added so collision cache is rebuilt. --- .../Physics/BulletSPlugin/BSLinksetCompound.cs | 44 +++++++++++++++------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index 1fd541f..e967dfc 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -143,19 +143,18 @@ public sealed class BSLinksetCompound : BSLinkset // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. ScheduleRebuild(LinksetRoot); } - else - { - // The origional prims are removed from the world as the shape of the root compound - // shape takes over. - m_physicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); - m_physicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); - // We don't want collisions from the old linkset children. - m_physicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); - child.PhysBody.collisionType = CollisionType.LinksetChild; + // The origional prims are removed from the world as the shape of the root compound + // shape takes over. + m_physicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + m_physicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); + // We don't want collisions from the old linkset children. + m_physicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + + child.PhysBody.collisionType = CollisionType.LinksetChild; + + ret = true; - ret = true; - } return ret; } @@ -190,6 +189,13 @@ public sealed class BSLinksetCompound : BSLinkset // Called at taint-time. public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated) { + if (!LinksetRoot.IsPhysicallyActive) + { + // No reason to do this physical stuff for static linksets. + DetailLog("{0},BSLinksetCompound.UpdateProperties,notPhysical", LinksetRoot.LocalID); + return; + } + // The user moving a child around requires the rebuilding of the linkset compound shape // One problem is this happens when a border is crossed -- the simulator implementation // stores the position into the group which causes the move of the object @@ -392,6 +398,13 @@ public sealed class BSLinksetCompound : BSLinkset private bool disableCOM = true; // DEBUG DEBUG: disable until we get this debugged private void RecomputeLinksetCompound() { + if (!LinksetRoot.IsPhysicallyActive) + { + // There is no reason to build all this physical stuff for a non-physical linkset + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysical", LinksetRoot.LocalID); + return; + } + try { // This replaces the physical shape of the root prim with a compound shape made up of the root @@ -403,7 +416,8 @@ public sealed class BSLinksetCompound : BSLinkset // Free up any shape we'd previously built. LinksetShape.Dereference(m_physicsScene); - LinksetShape = BSShapeCompound.GetReference(m_physicsScene, LinksetRoot); + // Get a new compound shape to build the linkset shape in. + LinksetShape = BSShapeCompound.GetReference(m_physicsScene); // The center of mass for the linkset is the geometric center of the group. // Compute a displacement for each component so it is relative to the center-of-mass. @@ -431,8 +445,7 @@ public sealed class BSLinksetCompound : BSLinkset cPrim.LinksetChildIndex = memberIndex; } - BSShape childShape = cPrim.PhysShape; - childShape.IncrementReference(); + BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim); OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; m_physicsScene.PE.AddChildShapeToCompoundShape(LinksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); @@ -446,7 +459,10 @@ public sealed class BSLinksetCompound : BSLinkset // Sneak the built compound shape in as the shape of the root prim. // Note this doesn't touch the root prim's PhysShape so be sure the manage the difference. + // Object removed and added to world to get collision cache rebuilt for new shape. + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody); m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, LinksetShape.physShapeInfo); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody); // With all of the linkset packed into the root prim, it has the mass of everyone. LinksetMass = ComputeLinksetMass(); -- cgit v1.1 From d322625f9062d7748a1a896b6fd37c51f7f41435 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 29 Apr 2013 17:30:54 -0700 Subject: BulletSim: Add non-static BSShape.GetReference for getting another reference to an existing shape instance. BSShapeNative rebuilds shape for all references. BSShapeCompound returns another reference copy if the compound shape already exists (for linksets). --- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 125 +++++++++++++++------ .../Region/Physics/BulletSPlugin/BulletSimTODO.txt | 9 ++ 2 files changed, 102 insertions(+), 32 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index 5c58ad5..f0c9fd5 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -47,26 +47,31 @@ public abstract class BSShape public BSShape() { - referenceCount = 0; + referenceCount = 1; lastReferenced = DateTime.Now; physShapeInfo = new BulletShape(); } public BSShape(BulletShape pShape) { - referenceCount = 0; + referenceCount = 1; lastReferenced = DateTime.Now; physShapeInfo = pShape; } + // Get another reference to this shape. + public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim); + // Called when this shape is being used again. - public virtual void IncrementReference() + // Used internally. External callers should call instance.GetReference() to properly copy/reference + // the shape. + protected virtual void IncrementReference() { referenceCount++; lastReferenced = DateTime.Now; } // Called when this shape is being used again. - public virtual void DecrementReference() + protected virtual void DecrementReference() { referenceCount--; lastReferenced = DateTime.Now; @@ -99,12 +104,19 @@ public abstract class BSShape // Returns a string for debugging that uniquily identifies the memory used by this instance public virtual string AddrString { - get { return "unknown"; } + get + { + if (physShapeInfo != null) + return physShapeInfo.AddrString; + return "unknown"; + } } public override string ToString() { StringBuilder buff = new StringBuilder(); + buff.Append(" m_hulls; private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, @@ -520,7 +562,7 @@ public class BSShapeHull : BSShape physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); } // Now done with the mesh shape. - meshShape.DecrementReference(); + meshShape.Dereference(physicsScene); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); } if (!newShape.HasPhysicalShape) @@ -671,37 +713,52 @@ public class BSShapeCompound : BSShape public BSShapeCompound(BulletShape pShape) : base(pShape) { } - public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim) + public static BSShape GetReference(BSScene physicsScene) + { + // Base compound shapes are not shared so this returns a raw shape. + // A built compound shape can be reused in linksets. + return new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene)); + } + public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) { - // Compound shapes are not shared so a new one is created every time. - return new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene, prim)); + // Calling this reference means we want another handle to an existing compound shape + // (usually linksets) so return this copy. + IncrementReference(); + return this; } // Dereferencing a compound shape releases the hold on all the child shapes. public override void Dereference(BSScene physicsScene) { - if (!physicsScene.PE.IsCompound(physShapeInfo)) + lock (physShapeInfo) { - // Failed the sanity check!! - physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", - LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); - physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", - BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); - return; - } + Dereference(physicsScene); + if (referenceCount <= 0) + { + if (!physicsScene.PE.IsCompound(physShapeInfo)) + { + // Failed the sanity check!! + physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", + LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", + BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); + return; + } - int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); - physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", - BSScene.DetailLogZero, physShapeInfo, numChildren); + int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", + BSScene.DetailLogZero, physShapeInfo, numChildren); - // Loop through all the children dereferencing each. - for (int ii = numChildren - 1; ii >= 0; ii--) - { - BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); - DereferenceAnonCollisionShape(physicsScene, childShape); + // Loop through all the children dereferencing each. + for (int ii = numChildren - 1; ii >= 0; ii--) + { + BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); + DereferenceAnonCollisionShape(physicsScene, childShape); + } + physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); + } } - physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); } - private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene, BSPhysObject prim) + private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene) { BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false); return cShape; @@ -754,6 +811,10 @@ public class BSShapeAvatar : BSShape { return new BSShapeNull(); } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + return new BSShapeNull(); + } public override void Dereference(BSScene physicsScene) { } // From the front: diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index c67081a..559a73f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt @@ -1,3 +1,12 @@ +PROBLEMS TO LOOK INTO +================================================= +Nebadon vehicle ride, get up, ride again. Second time vehicle does not act correctly. + Have to rez new vehicle and delete the old to fix situation. +Hitting RESET on Nebadon's vehicle while riding causes vehicle to get into odd + position state where it will not settle onto ground properly, etc +Two of Nebadon vehicles in a sim max the CPU. This is new. +A sitting, active vehicle bobs up and down a small amount. + CURRENT PRIORITIES ================================================= Use the HACD convex hull routine in Bullet rather than the C# version. -- cgit v1.1 From 7cdb07b386ffe88749fcc6cabadd9851f254699d Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Tue, 30 Apr 2013 11:42:11 -0700 Subject: BulletSim: improvements to LinksetCompound and PrimDisplaced. Not all working yet. --- .../Physics/BulletSPlugin/BSLinksetCompound.cs | 37 ++++++++++++++++------ .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 3 +- .../Physics/BulletSPlugin/BSPrimDisplaced.cs | 22 ++++++++++--- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 2 +- .../Region/Physics/BulletSPlugin/BulletSimTODO.txt | 3 ++ 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index e967dfc..e3ce7fb 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -395,7 +395,8 @@ public sealed class BSLinksetCompound : BSLinkset // Constraint linksets are rebuilt every time. // Note that this works for rebuilding just the root after a linkset is taken apart. // Called at taint time!! - private bool disableCOM = true; // DEBUG DEBUG: disable until we get this debugged + private bool UseBulletSimRootOffsetHack = false; + private bool disableCOM = true; // For basic linkset debugging, turn off the center-of-mass setting private void RecomputeLinksetCompound() { if (!LinksetRoot.IsPhysicallyActive) @@ -428,11 +429,19 @@ public sealed class BSLinksetCompound : BSLinkset // 'centerDisplacement' is the value to subtract from children to give physical offset position OMV.Vector3 centerDisplacement = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation; - LinksetRoot.SetEffectiveCenterOfMassW(centerDisplacement); - - // TODO: add phantom root shape to be the center-of-mass + if (UseBulletSimRootOffsetHack || disableCOM) + { + centerDisplacement = OMV.Vector3.Zero; + LinksetRoot.ClearDisplacement(); + } + else + { + LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacement); + } + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}", + LinksetRoot.LocalID, LinksetRoot.RawPosition, centerOfMassW, centerDisplacement); - // Add a shape for each of the other children in the linkset + // Add the shapes of all the components of the linkset int memberIndex = 1; ForEachMember(delegate(BSPrimLinkable cPrim) { @@ -449,8 +458,8 @@ public sealed class BSLinksetCompound : BSLinkset OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; m_physicsScene.PE.AddChildShapeToCompoundShape(LinksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", - LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1}cShape={2},offPos={3},offRot={4}", + LinksetRoot.LocalID, memberIndex, cPrim.PhysShape, offsetPos, offsetRot); memberIndex++; @@ -463,13 +472,21 @@ public sealed class BSLinksetCompound : BSLinkset m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody); m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, LinksetShape.physShapeInfo); m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}", + LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetShape); // With all of the linkset packed into the root prim, it has the mass of everyone. LinksetMass = ComputeLinksetMass(); LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); - // Enable the physical position updator to return the position and rotation of the root shape - m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); + if (UseBulletSimRootOffsetHack) + { + // Enable the physical position updator to return the position and rotation of the root shape. + // This enables a feature in the C++ code to return the world coordinates of the first shape in the + // compound shape. This eleviates the need to offset the returned physical position by the + // center-of-mass offset. + m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); + } } finally { @@ -477,7 +494,7 @@ public sealed class BSLinksetCompound : BSLinkset } // See that the Aabb surrounds the new shape - m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo); + m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetShape.physShapeInfo); } } } \ No newline at end of file diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 28d4bd7..7eba851 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -259,7 +259,8 @@ public abstract class BSPhysObject : PhysicsActor // The user can optionally set the center of mass. The user's setting will override any // computed center-of-mass (like in linksets). - public OMV.Vector3? UserSetCenterOfMass { get; set; } + // Note this is a displacement from the root's coordinates. Zero means use the root prim as center-of-mass. + public OMV.Vector3? UserSetCenterOfMassDisplacement { get; set; } public OMV.Vector3 LockedAxis { get; set; } // zero means locked. one means free. public readonly OMV.Vector3 LockedAxisFree = new OMV.Vector3(1f, 1f, 1f); // All axis are free diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs index f1c3b5c..f5ee671 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs @@ -78,14 +78,16 @@ public class BSPrimDisplaced : BSPrim // Set this sets and computes the displacement from the passed prim to the center-of-mass. // A user set value for center-of-mass overrides whatever might be passed in here. // The displacement is in local coordinates (relative to root prim in linkset oriented coordinates). - public virtual void SetEffectiveCenterOfMassW(Vector3 centerOfMassDisplacement) + public virtual void SetEffectiveCenterOfMassDisplacement(Vector3 centerOfMassDisplacement) { Vector3 comDisp; - if (UserSetCenterOfMass.HasValue) - comDisp = (OMV.Vector3)UserSetCenterOfMass; + if (UserSetCenterOfMassDisplacement.HasValue) + comDisp = (OMV.Vector3)UserSetCenterOfMassDisplacement; else comDisp = centerOfMassDisplacement; + DetailLog("{0},BSPrimDisplaced.SetEffectiveCenterOfMassDisplacement,userSet={1},comDisp={2}", + LocalID, UserSetCenterOfMassDisplacement.HasValue, comDisp); if (comDisp == Vector3.Zero) { // If there is no diplacement. Things get reset. @@ -107,9 +109,15 @@ public class BSPrimDisplaced : BSPrim set { if (PositionDisplacement != OMV.Vector3.Zero) - base.ForcePosition = value - (PositionDisplacement * RawOrientation); + { + OMV.Vector3 displacedPos = value - (PositionDisplacement * RawOrientation); + DetailLog("{0},BSPrimDisplaced.ForcePosition,val={1},disp={2},newPos={3}", LocalID, value, PositionDisplacement, displacedPos); + base.ForcePosition = displacedPos; + } else + { base.ForcePosition = value; + } } } @@ -118,6 +126,7 @@ public class BSPrimDisplaced : BSPrim get { return base.ForceOrientation; } set { + // TODO: base.ForceOrientation = value; } } @@ -143,7 +152,10 @@ public class BSPrimDisplaced : BSPrim { // Correct for any rotation around the center-of-mass // TODO!!! - entprop.Position = entprop.Position + (PositionDisplacement * entprop.Rotation); + + OMV.Vector3 displacedPos = entprop.Position + (PositionDisplacement * entprop.Rotation); + DetailLog("{0},BSPrimDisplaced.ForcePosition,physPos={1},disp={2},newPos={3}", LocalID, entprop.Position, PositionDisplacement, displacedPos); + entprop.Position = displacedPos; // entprop.Rotation = something; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index f0c9fd5..3faa484 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -117,7 +117,7 @@ public abstract class BSShape StringBuilder buff = new StringBuilder(); buff.Append(" /// If true, then exclude the seed cap. - public Hashtable GetCapsDetails(bool excludeSeed) + public Hashtable GetCapsDetails(bool excludeSeed, List requestedCaps) { Hashtable caps = new Hashtable(); string protocol = "http://"; @@ -175,6 +175,9 @@ namespace OpenSim.Framework.Capabilities if (excludeSeed && "SEED" == capsName) continue; + if (requestedCaps != null && !requestedCaps.Contains(capsName)) + continue; + caps[capsName] = baseUrl + m_capsHandlers[capsName].Path; } } @@ -182,4 +185,4 @@ namespace OpenSim.Framework.Capabilities return caps; } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index 8752404..a46c24a 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs @@ -273,11 +273,22 @@ namespace OpenSim.Region.ClientStack.Linden return string.Empty; } - Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true); + OSDArray capsRequested = (OSDArray)OSDParser.DeserializeLLSDXml(request); + List validCaps = new List(); + + foreach (OSD c in capsRequested) + validCaps.Add(c.AsString()); + + Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps); // Add the external too foreach (KeyValuePair kvp in m_HostCapsObj.ExternalCapsHandlers) + { + if (!validCaps.Contains(kvp.Key)) + continue; + caps[kvp.Key] = kvp.Value; + } string result = LLSDHelpers.SerialiseLLSDReply(caps); diff --git a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs index 8329af0..6ae9448 100644 --- a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs @@ -240,7 +240,7 @@ namespace OpenSim.Region.CoreModules.Framework { caps.AppendFormat("** User {0}:\n", kvp.Key); - for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false).GetEnumerator(); kvp2.MoveNext(); ) + for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); ) { Uri uri = new Uri(kvp2.Value.ToString()); caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); -- cgit v1.1 From 206fb306a7820cf593570e35ddfa8e7c5a10e449 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 1 May 2013 19:01:43 +0100 Subject: Update SmartThreadPool to latest version 2.2.3 with a major and minor change. SmartThreadPool code comes from http://www.codeproject.com/Articles/7933/Smart-Thread-Pool This version implements thread abort (via WorkItem.Cancel(true)), threadpool naming, max thread stack, etc. so we no longer need to manually patch those. However, two changes have been made to stock 2.2.3. Major change: WorkItem.Cancel(bool abortExecution) in our version does not succeed if the work item was in progress and thread abort was not specified. This is to match previous behaviour where we handle co-operative termination via another mechanism rather than checking WorkItem.IsCanceled. Minor change: Did not add STP's StopWatch implementation as this is only used WinCE and Silverlight and causes a build clash with System.Diagnostics.StopWatch The reason for updating is to see if this improves http://opensimulator.org/mantis/view.php?id=6557 and http://opensimulator.org/mantis/view.php?id=6586 --- OpenSim/Framework/Util.cs | 20 +- .../ScriptEngine/Interfaces/IScriptInstance.cs | 2 +- .../ScriptEngine/Shared/Instance/ScriptInstance.cs | 9 +- OpenSim/Region/ScriptEngine/XEngine/XEngine.cs | 4 +- OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs | 6 +- ThirdParty/SmartThreadPool/AssemblyInfo.cs | 61 - ThirdParty/SmartThreadPool/CallerThreadContext.cs | 361 +-- .../SmartThreadPool/CanceledWorkItemsGroup.cs | 14 + ThirdParty/SmartThreadPool/EventWaitHandle.cs | 104 + .../SmartThreadPool/EventWaitHandleFactory.cs | 82 + ThirdParty/SmartThreadPool/Exceptions.cs | 192 +- ThirdParty/SmartThreadPool/Interfaces.cs | 899 ++++-- ThirdParty/SmartThreadPool/InternalInterfaces.cs | 27 + ThirdParty/SmartThreadPool/PriorityQueue.cs | 479 ++- .../SmartThreadPool/Properties/AssemblyInfo.cs | 23 + ThirdParty/SmartThreadPool/SLExt.cs | 16 + ThirdParty/SmartThreadPool/STPEventWaitHandle.cs | 62 + .../SmartThreadPool/STPPerformanceCounter.cs | 802 ++--- ThirdParty/SmartThreadPool/STPStartInfo.cs | 325 +- .../SmartThreadPool/SmartThreadPool.ThreadEntry.cs | 60 + ThirdParty/SmartThreadPool/SmartThreadPool.cs | 3180 +++++++++++--------- .../SmartThreadPool/SynchronizedDictionary.cs | 89 + ThirdParty/SmartThreadPool/WIGStartInfo.cs | 270 +- .../SmartThreadPool/WorkItem.WorkItemResult.cs | 190 ++ ThirdParty/SmartThreadPool/WorkItem.cs | 649 ++-- ThirdParty/SmartThreadPool/WorkItemFactory.cs | 676 +++-- ThirdParty/SmartThreadPool/WorkItemInfo.cs | 171 +- .../SmartThreadPool/WorkItemResultTWrapper.cs | 128 + ThirdParty/SmartThreadPool/WorkItemsGroup.cs | 873 +++--- ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs | 471 +++ ThirdParty/SmartThreadPool/WorkItemsQueue.cs | 1245 ++++---- 31 files changed, 6688 insertions(+), 4802 deletions(-) delete mode 100644 ThirdParty/SmartThreadPool/AssemblyInfo.cs create mode 100644 ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs create mode 100644 ThirdParty/SmartThreadPool/EventWaitHandle.cs create mode 100644 ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs create mode 100644 ThirdParty/SmartThreadPool/InternalInterfaces.cs create mode 100644 ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs create mode 100644 ThirdParty/SmartThreadPool/SLExt.cs create mode 100644 ThirdParty/SmartThreadPool/STPEventWaitHandle.cs create mode 100644 ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs create mode 100644 ThirdParty/SmartThreadPool/SynchronizedDictionary.cs create mode 100644 ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs create mode 100644 ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs create mode 100644 ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index bde4673..a3602e9 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1840,7 +1840,7 @@ namespace OpenSim.Framework case FireAndForgetMethod.SmartThreadPool: if (m_ThreadPool == null) InitThreadPool(15); - m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); + m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); break; case FireAndForgetMethod.Thread: Thread thread = new Thread(delegate(object o) { realCallback(o); }); @@ -1910,15 +1910,15 @@ namespace OpenSim.Framework return sb.ToString(); } - private static object SmartThreadPoolCallback(object o) - { - object[] array = (object[])o; - WaitCallback callback = (WaitCallback)array[0]; - object obj = array[1]; - - callback(obj); - return null; - } +// private static object SmartThreadPoolCallback(object o) +// { +// object[] array = (object[])o; +// WaitCallback callback = (WaitCallback)array[0]; +// object obj = array[1]; +// +// callback(obj); +// return null; +// } #endregion FireAndForget Threading Pattern diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs index 35ae44c..b9a217b 100644 --- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs @@ -51,7 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Interfaces public interface IScriptWorkItem { bool Cancel(); - void Abort(); + bool Abort(); /// /// Wait for the work item to complete. diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 1e6db43..f9d3afc 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -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; @@ -627,6 +628,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance } } + Console.WriteLine("Here9"); + lock (EventQueue) { workItem = m_CurrentWorkItem; diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 0d9babb..5804aa8 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -483,7 +483,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine /// /// Basis on which to sort output. Can be null if no sort needs to take place private void HandleScriptsAction( - string[] cmdparams, Action action, Func keySelector) + string[] cmdparams, Action action, System.Func keySelector) { if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene)) return; @@ -1517,7 +1517,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine startInfo.MaxWorkerThreads = maxThreads; startInfo.MinWorkerThreads = minThreads; startInfo.ThreadPriority = threadPriority;; - startInfo.StackSize = stackSize; + startInfo.MaxStackSize = stackSize; startInfo.StartSuspended = true; m_ThreadPool = new SmartThreadPool(startInfo); diff --git a/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs b/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs index 8dd7677..9d9dee1 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs @@ -52,16 +52,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine return wr.Cancel(); } - public void Abort() + public bool Abort() { - wr.Abort(); + return wr.Cancel(true); } public bool Wait(int t) { // We use the integer version of WaitAll because the current version of SmartThreadPool has a bug with the // TimeSpan version. The number of milliseconds in TimeSpan is an int64 so when STP casts it down to an - // int (32-bit) we can end up with bad values. This occurs on Windows though curious not on Mono 2.10.8 + // int (32-bit) we can end up with bad values. This occurs on Windows though curiously not on Mono 2.10.8 // (or very likely other versions of Mono at least up until 3.0.3). return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false); } diff --git a/ThirdParty/SmartThreadPool/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/AssemblyInfo.cs deleted file mode 100644 index e2465b0..0000000 --- a/ThirdParty/SmartThreadPool/AssemblyInfo.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// -[assembly: AssemblyTitle("")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -[assembly: CLSCompliant(true)] - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly: AssemblyVersion("0.7.6.*")] - -// -// In order to sign your assembly you must specify a key to use. Refer to the -// Microsoft .NET Framework documentation for more information on assembly signing. -// -// Use the attributes below to control which key is used for signing. -// -// Notes: -// (*) If no key is specified, the assembly is not signed. -// (*) KeyName refers to a key that has been installed in the Crypto Service -// Provider (CSP) on your machine. KeyFile refers to a file which contains -// a key. -// (*) If the KeyFile and the KeyName values are both specified, the -// following processing occurs: -// (1) If the KeyName can be found in the CSP, that key is used. -// (2) If the KeyName does not exist and the KeyFile does exist, the key -// in the KeyFile is installed into the CSP and used. -// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. -// When specifying the KeyFile, the location of the KeyFile should be -// relative to the project output directory which is -// %Project Directory%\obj\. For example, if your KeyFile is -// located in the project directory, you would specify the AssemblyKeyFile -// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] -// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework -// documentation for more information on this. -// -[assembly: AssemblyDelaySign(false)] -[assembly: AssemblyKeyFile("")] -[assembly: AssemblyKeyName("")] diff --git a/ThirdParty/SmartThreadPool/CallerThreadContext.cs b/ThirdParty/SmartThreadPool/CallerThreadContext.cs index 6ea53f6..2177241 100644 --- a/ThirdParty/SmartThreadPool/CallerThreadContext.cs +++ b/ThirdParty/SmartThreadPool/CallerThreadContext.cs @@ -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 - - /// - /// This class stores the caller call context in order to restore - /// it when the work item is executed in the thread pool environment. - /// - internal class CallerThreadContext - { - #region Prepare reflection information - - // Cached type information. - private static MethodInfo getLogicalCallContextMethodInfo = - typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); - - private static MethodInfo setLogicalCallContextMethodInfo = - typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); - - private static string HttpContextSlotName = GetHttpContextSlotName(); - - private static string GetHttpContextSlotName() - { - FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); - - if( fi != null ) - return (string)fi.GetValue(null); - else // Use the default "HttpContext" slot name - return "HttpContext"; - } - - #endregion - - #region Private fields - - private HttpContext _httpContext = null; - private LogicalCallContext _callContext = null; - - #endregion - - /// - /// Constructor - /// - private CallerThreadContext() - { - } - - public bool CapturedCallContext - { - get - { - return (null != _callContext); - } - } - - public bool CapturedHttpContext - { - get - { - return (null != _httpContext); - } - } - - /// - /// Captures the current thread context - /// - /// - public static CallerThreadContext Capture( - bool captureCallContext, - bool captureHttpContext) - { - Debug.Assert(captureCallContext || captureHttpContext); - - CallerThreadContext callerThreadContext = new CallerThreadContext(); - - // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() - // Capture Call Context - if(captureCallContext && (getLogicalCallContextMethodInfo != null)) - { - callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); - if (callerThreadContext._callContext != null) - { - callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); - } - } - - // Capture httpContext - if (captureHttpContext && (null != HttpContext.Current)) - { - callerThreadContext._httpContext = HttpContext.Current; - } - - return callerThreadContext; - } - - /// - /// Applies the thread context stored earlier - /// - /// - public static void Apply(CallerThreadContext callerThreadContext) - { - if (null == callerThreadContext) - { - throw new ArgumentNullException("callerThreadContext"); - } - - // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() - // Restore call context - if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) - { - setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); - } - - // Restore HttpContext - if (callerThreadContext._httpContext != null) - { - CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); - } - } - } - - #endregion - -} - - -/* -// Ami Bar -// amibar@gmail.com - -using System; -using System.Threading; -using System.Globalization; -using System.Security.Principal; -using System.Reflection; -using System.Runtime.Remoting.Contexts; - -namespace Amib.Threading.Internal -{ - #region CallerThreadContext class - - /// - /// This class stores the caller thread context in order to restore - /// it when the work item is executed in the context of the thread - /// from the pool. - /// Note that we can't store the thread's CompressedStack, because - /// it throws a security exception - /// - public class CallerThreadContext - { - private CultureInfo _culture = null; - private CultureInfo _cultureUI = null; - private IPrincipal _principal; - private System.Runtime.Remoting.Contexts.Context _context; - - private static FieldInfo _fieldInfo = GetFieldInfo(); - - private static FieldInfo GetFieldInfo() - { - Type threadType = typeof(Thread); - return threadType.GetField( - "m_Context", - BindingFlags.Instance | BindingFlags.NonPublic); - } - - /// - /// Constructor - /// - private CallerThreadContext() - { - } - - /// - /// Captures the current thread context - /// - /// - 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; - } - - /// - /// Applies the thread context stored earlier - /// - /// - public static void Apply(CallerThreadContext callerThreadContext) - { - Thread thread = Thread.CurrentThread; - thread.CurrentCulture = callerThreadContext._culture; - thread.CurrentUICulture = callerThreadContext._cultureUI; - Thread.CurrentPrincipal = callerThreadContext._principal; - - // Uncomment the following block to enable the Thread.CurrentThread -/* - if (null != _fieldInfo) - { - _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 + + /// + /// This class stores the caller call context in order to restore + /// it when the work item is executed in the thread pool environment. + /// + internal class CallerThreadContext + { +#region Prepare reflection information + + // Cached type information. + private static 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 + + /// + /// Constructor + /// + private CallerThreadContext() + { + } + + public bool CapturedCallContext + { + get + { + return (null != _callContext); + } + } + + public bool CapturedHttpContext + { + get + { + return (null != _httpContext); + } + } + + /// + /// Captures the current thread context + /// + /// + public static CallerThreadContext Capture( + bool captureCallContext, + bool captureHttpContext) + { + Debug.Assert(captureCallContext || captureHttpContext); + + CallerThreadContext callerThreadContext = new CallerThreadContext(); + + // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() + // Capture Call Context + if(captureCallContext && (getLogicalCallContextMethodInfo != null)) + { + callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); + if (callerThreadContext._callContext != null) + { + callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); + } + } + + // Capture httpContext + if (captureHttpContext && (null != HttpContext.Current)) + { + callerThreadContext._httpContext = HttpContext.Current; + } + + return callerThreadContext; + } + + /// + /// Applies the thread context stored earlier + /// + /// + public static void Apply(CallerThreadContext callerThreadContext) + { + if (null == callerThreadContext) + { + throw new ArgumentNullException("callerThreadContext"); + } + + // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() + // Restore call context + if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) + { + setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); + } + + // Restore HttpContext + if (callerThreadContext._httpContext != null) + { + HttpContext.Current = callerThreadContext._httpContext; + //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); + } + } + } + + #endregion +} +#endif diff --git a/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs new file mode 100644 index 0000000..4a2a3e7 --- /dev/null +++ b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs @@ -0,0 +1,14 @@ +namespace Amib.Threading.Internal +{ + internal class CanceledWorkItemsGroup + { + public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + public CanceledWorkItemsGroup() + { + IsCanceled = false; + } + + public bool IsCanceled { get; set; } + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandle.cs b/ThirdParty/SmartThreadPool/EventWaitHandle.cs new file mode 100644 index 0000000..70a1a29 --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandle.cs @@ -0,0 +1,104 @@ +#if (_WINDOWS_CE) + +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandle class + /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation. + /// So I wrote this class to implement these two methods with some of their overloads. + /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny. + /// Note that this class doesn't even inherit from WaitHandle! + /// + public class STPEventWaitHandle + { + #region Public Constants + + public const int WaitTimeout = Timeout.Infinite; + + #endregion + + #region Private External Constants + + private const Int32 WAIT_FAILED = -1; + private const Int32 WAIT_TIMEOUT = 0x102; + private const UInt32 INFINITE = 0xFFFFFFFF; + + #endregion + + #region WaitAll and WaitAny + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + + private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles) + { + IntPtr[] nativeHandles = new IntPtr[waitHandles.Length]; + for (int i = 0; i < waitHandles.Length; i++) + { + nativeHandles[i] = waitHandles[i].Handle; + } + return nativeHandles; + } + + public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout); + + if (result == WAIT_TIMEOUT || result == WAIT_FAILED) + { + return false; + } + + return true; + } + + + public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout); + + if (result >= 0 && result < waitHandles.Length) + { + return result; + } + + return -1; + } + + public static int WaitAny(WaitHandle[] waitHandles) + { + return WaitAny(waitHandles, Timeout.Infinite, false); + } + + public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) + { + int millisecondsTimeout = (int)timeout.TotalMilliseconds; + + return WaitAny(waitHandles, millisecondsTimeout, false); + } + + #endregion + + #region External methods + + [DllImport("coredll.dll", SetLastError = true)] + public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds); + + #endregion + } +} +#endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs new file mode 100644 index 0000000..2f8c55b --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs @@ -0,0 +1,82 @@ +using System.Threading; + +#if (_WINDOWS_CE) +using System; +using System.Runtime.InteropServices; +#endif + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandleFactory class. + /// This is a static class that creates AutoResetEvent and ManualResetEvent objects. + /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property + /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API. + /// Consequently this class creates the needed XxxResetEvent and replaces the handle if + /// it's a WindowsCE OS. + /// + public static class EventWaitHandleFactory + { + /// + /// Create a new AutoResetEvent object + /// + /// Return a new AutoResetEvent object + public static AutoResetEvent CreateAutoResetEvent() + { + AutoResetEvent waitHandle = new AutoResetEvent(false); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, false, false); +#endif + + return waitHandle; + } + + /// + /// Create a new ManualResetEvent object + /// + /// Return a new ManualResetEvent object + public static ManualResetEvent CreateManualResetEvent(bool initialState) + { + ManualResetEvent waitHandle = new ManualResetEvent(initialState); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, true, initialState); +#endif + + return waitHandle; + } + +#if (_WINDOWS_CE) + + /// + /// Replace the event handle + /// + /// The WaitHandle object which its handle needs to be replaced. + /// Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false) + /// The initial state of the event + private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState) + { + // Store the old handle + IntPtr oldHandle = waitHandle.Handle; + + // Create a new event + IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null); + + // Replace the old event with the new event + waitHandle.Handle = newHandle; + + // Close the old event + CloseHandle (oldHandle); + } + + [DllImport("coredll.dll", SetLastError = true)] + public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); + + //Handle + [DllImport("coredll.dll", SetLastError = true)] + public static extern bool CloseHandle(IntPtr hObject); +#endif + + } +} diff --git a/ThirdParty/SmartThreadPool/Exceptions.cs b/ThirdParty/SmartThreadPool/Exceptions.cs index c454709..8e66ce9 100644 --- a/ThirdParty/SmartThreadPool/Exceptions.cs +++ b/ThirdParty/SmartThreadPool/Exceptions.cs @@ -1,81 +1,111 @@ -// Ami Bar -// amibar@gmail.com - -using System; -using System.Runtime.Serialization; - -namespace Amib.Threading -{ - #region Exceptions - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been canceled - /// - [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) - { - } - } - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been timed out - /// - [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) - { - } - } - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been timed out - /// - [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 + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been canceled + /// + public sealed partial class WorkItemCancelException : Exception + { + public WorkItemCancelException() + { + } + + public WorkItemCancelException(string message) + : base(message) + { + } + + public WorkItemCancelException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemTimeoutException : Exception + { + public WorkItemTimeoutException() + { + } + + public WorkItemTimeoutException(string message) + : base(message) + { + } + + public WorkItemTimeoutException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemResultException : Exception + { + public WorkItemResultException() + { + } + + public WorkItemResultException(string message) + : base(message) + { + } + + public WorkItemResultException(string message, Exception e) + : base(message, e) + { + } + } + + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// Represents an exception in case IWorkItemResult.GetResult has been canceled + /// + [Serializable] + public sealed partial class WorkItemCancelException + { + public WorkItemCancelException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + [Serializable] + public sealed partial class WorkItemTimeoutException + { + public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + [Serializable] + public sealed partial class WorkItemResultException + { + public WorkItemResultException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + +#endif + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/Interfaces.cs b/ThirdParty/SmartThreadPool/Interfaces.cs index f1c1fcf..29c8a3e 100644 --- a/ThirdParty/SmartThreadPool/Interfaces.cs +++ b/ThirdParty/SmartThreadPool/Interfaces.cs @@ -1,271 +1,628 @@ -// Ami Bar -// amibar@gmail.com - -using System; -using System.Threading; - -namespace Amib.Threading -{ - #region Delegates - - /// - /// A delegate that represents the method to run as the work item - /// - /// A state object for the method to run - public delegate object WorkItemCallback(object state); - - /// - /// A delegate to call after the WorkItemCallback completed - /// - /// The work item result object - public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); - - /// - /// A delegate to call when a WorkItemsGroup becomes idle - /// - /// A reference to the WorkItemsGroup that became idle - public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); - - #endregion - - #region WorkItem Priority - - public enum WorkItemPriority - { - Lowest, - BelowNormal, - Normal, - AboveNormal, - Highest, - } - - #endregion - - #region IHasWorkItemPriority interface - - public interface IHasWorkItemPriority - { - WorkItemPriority WorkItemPriority { get; } - } - - #endregion - - #region IWorkItemsGroup interface - - /// - /// IWorkItemsGroup interface - /// - public interface IWorkItemsGroup - { - /// - /// Get/Set the name of the WorkItemsGroup - /// - 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 - - /// - /// IWorkItemResult interface - /// - public interface IWorkItemResult - { - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. - /// - /// The result of the work item - object GetResult(); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - int millisecondsTimeout, - bool exitContext); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - TimeSpan timeout, - bool exitContext); - - void Abort(); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - object GetResult(out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - int millisecondsTimeout, - bool exitContext, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - object GetResult( - TimeSpan timeout, - bool exitContext, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// Filled with the exception if one was thrown - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - object GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - bool IsCompleted { get; } - - /// - /// Gets an indication whether the asynchronous operation has been canceled. - /// - bool IsCanceled { get; } - - /// - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - /// - object State { get; } - - /// - /// Cancel the work item if it didn't start running yet. - /// - /// Returns true on success or false if the work item is in progress or already completed - bool Cancel(); - - /// - /// Get the work item's priority - /// - WorkItemPriority WorkItemPriority { get; } - - /// - /// Return the result, same as GetResult() - /// - object Result { get; } - - /// - /// Returns the exception if occured otherwise returns null. - /// - object Exception { get; } - } - - #endregion -} +using System; +using System.Threading; + +namespace Amib.Threading +{ + #region Delegates + + /// + /// A delegate that represents the method to run as the work item + /// + /// A state object for the method to run + public delegate object WorkItemCallback(object state); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call when a WorkItemsGroup becomes idle + /// + /// A reference to the WorkItemsGroup that became idle + public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); + + /// + /// A delegate to call after a thread is created, but before + /// it's first use. + /// + public delegate void ThreadInitializationHandler(); + + /// + /// A delegate to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + public delegate void ThreadTerminationHandler(); + + #endregion + + #region WorkItem Priority + + /// + /// Defines the availeable priorities of a work item. + /// The higher the priority a work item has, the sooner + /// it will be executed. + /// + public enum WorkItemPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } + + #endregion + + #region IWorkItemsGroup interface + + /// + /// IWorkItemsGroup interface + /// Created by SmartThreadPool.CreateWorkItemsGroup() + /// + public interface IWorkItemsGroup + { + /// + /// Get/Set the name of the WorkItemsGroup + /// + string Name { get; set; } + + /// + /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool + /// + int Concurrency { get; set; } + + /// + /// Get the number of work items waiting in the queue. + /// + int WaitingCallbacks { get; } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + object[] GetStates(); + + /// + /// Get the WorkItemsGroup start information + /// + WIGStartInfo WIGStartInfo { get; } + + /// + /// Starts to execute work items + /// + void Start(); + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + void Cancel(); + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + void Cancel(bool abortExecution); + + /// + /// Wait for all work item to complete. + /// + void WaitForIdle(); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(TimeSpan timeout); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete in milliseconds + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(int millisecondsTimeout); + + /// + /// IsIdle is true when there are no work items running or queued. + /// + bool IsIdle { get; } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + event WorkItemsGroupIdleHandler OnIdle; + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); + + #endregion + + #region QueueWorkItem(Action<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority); + + #endregion + + #region QueueWorkItem(Func<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + #endregion + } + + #endregion + + #region CallToPostExecute enumerator + + [Flags] + public enum CallToPostExecute + { + /// + /// Never call to the PostExecute call back + /// + Never = 0x00, + + /// + /// Call to the PostExecute only when the work item is cancelled + /// + WhenWorkItemCanceled = 0x01, + + /// + /// Call to the PostExecute only when the work item is not cancelled + /// + WhenWorkItemNotCanceled = 0x02, + + /// + /// Always call to the PostExecute + /// + Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, + } + + #endregion + + #region IWorkItemResult interface + + /// + /// The common interface of IWorkItemResult and IWorkItemResult<T> + /// + public interface IWaitableResult + { + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResult(); + + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResultT(); + } + + /// + /// IWorkItemResult interface. + /// Created when a WorkItemCallback work item is queued. + /// + public interface IWorkItemResult : IWorkItemResult + { + } + + /// + /// IWorkItemResult<TResult> interface. + /// Created when a Func<TResult> work item is queued. + /// + public interface IWorkItemResult : IWaitableResult + { + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// The result of the work item + TResult GetResult(); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// Filled with the exception if one was thrown + /// The result of the work item + TResult GetResult(out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// Filled with the exception if one was thrown + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// + /// Filled with the exception if one was thrown + /// + /// + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Gets an indication whether the asynchronous operation has completed. + /// + bool IsCompleted { get; } + + /// + /// Gets an indication whether the asynchronous operation has been canceled. + /// + bool IsCanceled { get; } + + /// + /// Gets the user-defined object that contains context data + /// for the work item method. + /// + object State { get; } + + /// + /// Same as Cancel(false). + /// + bool Cancel(); + + /// + /// 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 + /// + /// When true send an AbortException to the executing thread. + /// Returns true if the work item was not completed, otherwise false. + bool Cancel(bool abortExecution); + + /// + /// Get the work item's priority + /// + WorkItemPriority WorkItemPriority { get; } + + /// + /// Return the result, same as GetResult() + /// + TResult Result { get; } + + /// + /// Returns the exception if occured otherwise returns null. + /// + object Exception { get; } + } + + #endregion + + #region .NET 3.5 + + // All these delegate are built-in .NET 3.5 + // Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity. + + public delegate void Action(); + public delegate void Action(T1 arg1, T2 arg2); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + public delegate TResult Func(); + public delegate TResult Func(T arg1); + public delegate TResult Func(T1 arg1, T2 arg2); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/InternalInterfaces.cs b/ThirdParty/SmartThreadPool/InternalInterfaces.cs new file mode 100644 index 0000000..8be7161 --- /dev/null +++ b/ThirdParty/SmartThreadPool/InternalInterfaces.cs @@ -0,0 +1,27 @@ + +namespace Amib.Threading.Internal +{ + /// + /// An internal delegate to call when the WorkItem starts or completes + /// + internal delegate void WorkItemStateCallback(WorkItem workItem); + + internal interface IInternalWorkItemResult + { + event WorkItemStateCallback OnWorkItemStarted; + event WorkItemStateCallback OnWorkItemCompleted; + } + + internal interface IInternalWaitableResult + { + /// + /// This method is intent for internal use. + /// + IWorkItemResult GetWorkItemResult(); + } + + public interface IHasWorkItemPriority + { + WorkItemPriority WorkItemPriority { get; } + } +} diff --git a/ThirdParty/SmartThreadPool/PriorityQueue.cs b/ThirdParty/SmartThreadPool/PriorityQueue.cs index 63d5e84..6245fd8 100644 --- a/ThirdParty/SmartThreadPool/PriorityQueue.cs +++ b/ThirdParty/SmartThreadPool/PriorityQueue.cs @@ -1,240 +1,239 @@ -// Ami Bar -// amibar@gmail.com - -using System; -using System.Collections; -using System.Diagnostics; - -namespace Amib.Threading.Internal -{ - #region PriorityQueue class - - /// - /// PriorityQueue class - /// This class is not thread safe because we use external lock - /// - public sealed class PriorityQueue : IEnumerable - { - #region Private members - - /// - /// The number of queues, there is one for each type of priority - /// - private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; - - /// - /// Work items queues. There is one for each type of priority - /// - private Queue [] _queues = new Queue[_queuesCount]; - - /// - /// The total number of work items within the queues - /// - private int _workItemsCount = 0; - - /// - /// Use with IEnumerable interface - /// - private int _version = 0; - - #endregion - - #region Contructor - - public PriorityQueue() - { - for(int i = 0; i < _queues.Length; ++i) - { - _queues[i] = new Queue(); - } - } - - #endregion - - #region Methods - - /// - /// Enqueue a work item. - /// - /// A work item - public void Enqueue(IHasWorkItemPriority workItem) - { - Debug.Assert(null != workItem); - - int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; - Debug.Assert(queueIndex >= 0); - Debug.Assert(queueIndex < _queuesCount); - - _queues[queueIndex].Enqueue(workItem); - ++_workItemsCount; - ++_version; - } - - /// - /// Dequeque a work item. - /// - /// Returns the next work item - public IHasWorkItemPriority Dequeue() - { - IHasWorkItemPriority workItem = null; - - if(_workItemsCount > 0) - { - int queueIndex = GetNextNonEmptyQueue(-1); - Debug.Assert(queueIndex >= 0); - workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority; - Debug.Assert(null != workItem); - --_workItemsCount; - ++_version; - } - - return workItem; - } - - /// - /// Find the next non empty queue starting at queue queueIndex+1 - /// - /// The index-1 to start from - /// - /// The index of the next non empty queue or -1 if all the queues are empty - /// - private int GetNextNonEmptyQueue(int queueIndex) - { - for(int i = queueIndex+1; i < _queuesCount; ++i) - { - if(_queues[i].Count > 0) - { - return i; - } - } - return -1; - } - - /// - /// The number of work items - /// - public int Count - { - get - { - return _workItemsCount; - } - } - - /// - /// Clear all the work items - /// - public void Clear() - { - if (_workItemsCount > 0) - { - foreach(Queue queue in _queues) - { - queue.Clear(); - } - _workItemsCount = 0; - ++_version; - } - } - - #endregion - - #region IEnumerable Members - - /// - /// Returns an enumerator to iterate over the work items - /// - /// Returns an enumerator - public IEnumerator GetEnumerator() - { - return new PriorityQueueEnumerator(this); - } - - #endregion - - #region PriorityQueueEnumerator - - /// - /// The class the implements the enumerator - /// - 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 + + /// + /// PriorityQueue class + /// This class is not thread safe because we use external lock + /// + public sealed class PriorityQueue : IEnumerable + { + #region Private members + + /// + /// The number of queues, there is one for each type of priority + /// + private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; + + /// + /// Work items queues. There is one for each type of priority + /// + private readonly LinkedList[] _queues = new LinkedList[_queuesCount]; + + /// + /// The total number of work items within the queues + /// + private int _workItemsCount; + + /// + /// Use with IEnumerable interface + /// + private int _version; + + #endregion + + #region Contructor + + public PriorityQueue() + { + for(int i = 0; i < _queues.Length; ++i) + { + _queues[i] = new LinkedList(); + } + } + + #endregion + + #region Methods + + /// + /// Enqueue a work item. + /// + /// A work item + public void Enqueue(IHasWorkItemPriority workItem) + { + Debug.Assert(null != workItem); + + int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; + Debug.Assert(queueIndex >= 0); + Debug.Assert(queueIndex < _queuesCount); + + _queues[queueIndex].AddLast(workItem); + ++_workItemsCount; + ++_version; + } + + /// + /// Dequeque a work item. + /// + /// Returns the next work item + public IHasWorkItemPriority Dequeue() + { + IHasWorkItemPriority workItem = null; + + if(_workItemsCount > 0) + { + int queueIndex = GetNextNonEmptyQueue(-1); + Debug.Assert(queueIndex >= 0); + workItem = _queues[queueIndex].First.Value; + _queues[queueIndex].RemoveFirst(); + Debug.Assert(null != workItem); + --_workItemsCount; + ++_version; + } + + return workItem; + } + + /// + /// Find the next non empty queue starting at queue queueIndex+1 + /// + /// The index-1 to start from + /// + /// The index of the next non empty queue or -1 if all the queues are empty + /// + private int GetNextNonEmptyQueue(int queueIndex) + { + for(int i = queueIndex+1; i < _queuesCount; ++i) + { + if(_queues[i].Count > 0) + { + return i; + } + } + return -1; + } + + /// + /// The number of work items + /// + public int Count + { + get + { + return _workItemsCount; + } + } + + /// + /// Clear all the work items + /// + public void Clear() + { + if (_workItemsCount > 0) + { + foreach(LinkedList queue in _queues) + { + queue.Clear(); + } + _workItemsCount = 0; + ++_version; + } + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator to iterate over the work items + /// + /// Returns an enumerator + public IEnumerator GetEnumerator() + { + return new PriorityQueueEnumerator(this); + } + + #endregion + + #region PriorityQueueEnumerator + + /// + /// The class the implements the enumerator + /// + private class PriorityQueueEnumerator : IEnumerator + { + private readonly PriorityQueue _priorityQueue; + private int _version; + private int _queueIndex; + private IEnumerator _enumerator; + + public PriorityQueueEnumerator(PriorityQueue priorityQueue) + { + _priorityQueue = priorityQueue; + _version = _priorityQueue._version; + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); + if (_queueIndex >= 0) + { + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + } + else + { + _enumerator = null; + } + } + + #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 +} diff --git a/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1651e78 --- /dev/null +++ b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Amib.Threading")] +[assembly: AssemblyDescription("Smart Thread Pool")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Amib.Threading")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")] +[assembly: AssemblyVersion("2.2.3.0")] + +#if (_PUBLISH) +[assembly: InternalsVisibleTo("STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad")] +#else +[assembly: InternalsVisibleTo("STPTests")] +#endif + + diff --git a/ThirdParty/SmartThreadPool/SLExt.cs b/ThirdParty/SmartThreadPool/SLExt.cs new file mode 100644 index 0000000..fafff19 --- /dev/null +++ b/ThirdParty/SmartThreadPool/SLExt.cs @@ -0,0 +1,16 @@ +#if _SILVERLIGHT + +using System.Threading; + +namespace Amib.Threading +{ + public enum ThreadPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } +} +#endif diff --git a/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs new file mode 100644 index 0000000..3b92645 --- /dev/null +++ b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs @@ -0,0 +1,62 @@ +#if !(_WINDOWS_CE) + +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ +#if _WINDOWS || WINDOWS_PHONE + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout); + } + } +#else + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + } +#endif + +} + +#endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs index 077cf17..2508661 100644 --- a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs +++ b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs @@ -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 description for STPPerformanceCounter. - /// - internal class STPPerformanceCounter - { - // Fields - private PerformanceCounterType _pcType; - protected string _counterHelp; - protected string _counterName; - - // Methods - public STPPerformanceCounter( - string counterName, - string counterHelp, - PerformanceCounterType pcType) - { - this._counterName = counterName; - this._counterHelp = counterHelp; - this._pcType = pcType; - } - - public void AddCounterToCollection(CounterCreationDataCollection counterData) - { - CounterCreationData counterCreationData = new CounterCreationData( - _counterName, - _counterHelp, - _pcType); - - counterData.Add(counterCreationData); - } - - // Properties - public string Name - { - get - { - return _counterName; - } - } - } - - internal class STPPerformanceCounters - { - // Fields - internal STPPerformanceCounter[] _stpPerformanceCounters; - private static STPPerformanceCounters _instance; - internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; - internal const string _stpCategoryName = "SmartThreadPool"; - - // Methods - static STPPerformanceCounters() - { - _instance = new STPPerformanceCounters(); - } - - private STPPerformanceCounters() - { - STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] - { - new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), - new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), - - new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), - - new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), - new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), - - new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), - new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), - - new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), - new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), - - new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), - }; - - _stpPerformanceCounters = stpPerformanceCounters; - SetupCategory(); - } - - private void SetupCategory() - { - if (!PerformanceCounterCategory.Exists(_stpCategoryName)) - { - CounterCreationDataCollection counters = new CounterCreationDataCollection(); - - for (int i = 0; i < _stpPerformanceCounters.Length; i++) - { - _stpPerformanceCounters[i].AddCounterToCollection(counters); - } - - - // *********** Remark for .NET 2.0 *********** - // If you are here, it means you got the warning that this overload - // of the method is deprecated in .NET 2.0. To use the correct - // method overload, uncomment the third argument of - // the method. - #pragma warning disable 0618 - PerformanceCounterCategory.Create( - _stpCategoryName, - _stpCategoryHelp, - //PerformanceCounterCategoryType.MultiInstance, - counters); - #pragma warning restore 0618 - } - } - - // Properties - public static STPPerformanceCounters Instance - { - get - { - return _instance; - } - } - } - - internal class STPInstancePerformanceCounter : IDisposable - { - // Fields - private PerformanceCounter _pcs; - - // Methods - protected STPInstancePerformanceCounter() - { - } - - public STPInstancePerformanceCounter( - string instance, - STPPerformanceCounterType spcType) - { - STPPerformanceCounters counters = STPPerformanceCounters.Instance; - _pcs = new PerformanceCounter( - STPPerformanceCounters._stpCategoryName, - counters._stpPerformanceCounters[(int) spcType].Name, - instance, - false); - _pcs.RawValue = _pcs.RawValue; - } - - ~STPInstancePerformanceCounter() - { - Close(); - } - - public void Close() - { - if (_pcs != null) - { - _pcs.RemoveInstance(); - _pcs.Close(); - _pcs = null; - } - } - - public void Dispose() - { - Close(); - GC.SuppressFinalize(this); - } - - public virtual void Increment() - { - _pcs.Increment(); - } - - public virtual void IncrementBy(long val) - { - _pcs.IncrementBy(val); - } - - public virtual void Set(long val) - { - _pcs.RawValue = val; - } - } - - internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter - { - // Methods - public STPInstanceNullPerformanceCounter() {} - public override void Increment() {} - public override void IncrementBy(long value) {} - public override void Set(long val) {} - } - - internal interface ISTPInstancePerformanceCounters : IDisposable - { - void Close(); - 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 description for STPPerformanceCounter. + /// + internal class STPPerformanceCounter + { + // Fields + private readonly PerformanceCounterType _pcType; + protected string _counterHelp; + protected string _counterName; + + // Methods + public STPPerformanceCounter( + string counterName, + string counterHelp, + PerformanceCounterType pcType) + { + _counterName = counterName; + _counterHelp = counterHelp; + _pcType = pcType; + } + + public void AddCounterToCollection(CounterCreationDataCollection counterData) + { + CounterCreationData counterCreationData = new CounterCreationData( + _counterName, + _counterHelp, + _pcType); + + counterData.Add(counterCreationData); + } + + // Properties + public string Name + { + get + { + return _counterName; + } + } + } + + internal class STPPerformanceCounters + { + // Fields + internal STPPerformanceCounter[] _stpPerformanceCounters; + private static readonly STPPerformanceCounters _instance; + internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; + internal const string _stpCategoryName = "SmartThreadPool"; + + // Methods + static STPPerformanceCounters() + { + _instance = new STPPerformanceCounters(); + } + + private STPPerformanceCounters() + { + STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] + { + new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), + new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), + + new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), + + new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), + new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), + + new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), + }; + + _stpPerformanceCounters = stpPerformanceCounters; + SetupCategory(); + } + + private void SetupCategory() + { + if (!PerformanceCounterCategory.Exists(_stpCategoryName)) + { + CounterCreationDataCollection counters = new CounterCreationDataCollection(); + + for (int i = 0; i < _stpPerformanceCounters.Length; i++) + { + _stpPerformanceCounters[i].AddCounterToCollection(counters); + } + + PerformanceCounterCategory.Create( + _stpCategoryName, + _stpCategoryHelp, + PerformanceCounterCategoryType.MultiInstance, + counters); + + } + } + + // Properties + public static STPPerformanceCounters Instance + { + get + { + return _instance; + } + } + } + + internal class STPInstancePerformanceCounter : IDisposable + { + // Fields + private bool _isDisposed; + private PerformanceCounter _pcs; + + // Methods + protected STPInstancePerformanceCounter() + { + _isDisposed = false; + } + + public STPInstancePerformanceCounter( + string instance, + STPPerformanceCounterType spcType) : this() + { + STPPerformanceCounters counters = STPPerformanceCounters.Instance; + _pcs = new PerformanceCounter( + STPPerformanceCounters._stpCategoryName, + counters._stpPerformanceCounters[(int) spcType].Name, + instance, + false); + _pcs.RawValue = _pcs.RawValue; + } + + + public void Close() + { + if (_pcs != null) + { + _pcs.RemoveInstance(); + _pcs.Close(); + _pcs = null; + } + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool disposing) + { + 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 + } + } +} diff --git a/ThirdParty/SmartThreadPool/STPStartInfo.cs b/ThirdParty/SmartThreadPool/STPStartInfo.cs index fa9ceb4..2ec8dc6 100644 --- a/ThirdParty/SmartThreadPool/STPStartInfo.cs +++ b/ThirdParty/SmartThreadPool/STPStartInfo.cs @@ -1,113 +1,212 @@ -// Ami Bar -// amibar@gmail.com - -using System.Threading; - -namespace Amib.Threading -{ - /// - /// Summary description for STPStartInfo. - /// - public class STPStartInfo : WIGStartInfo - { - /// - /// Idle timeout in milliseconds. - /// If a thread is idle for _idleTimeout milliseconds then - /// it may quit. - /// - private int _idleTimeout; - - /// - /// The lower limit of threads in the pool. - /// - private int _minWorkerThreads; - - /// - /// The upper limit of threads in the pool. - /// - private int _maxWorkerThreads; - - /// - /// The priority of the threads in the pool - /// - private ThreadPriority _threadPriority; - - /// - /// The thread pool name. Threads will get names depending on this. - /// - private string _threadPoolName; - - /// - /// If this field is not null then the performance counters are enabled - /// and use the string as the name of the instance. - /// - private string _pcInstanceName; - - private int _stackSize; - - public STPStartInfo() : base() - { - _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 description for STPStartInfo. + /// + 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 + } + + /// + /// Get/Set the idle timeout in milliseconds. + /// If a thread is idle (starved) longer than IdleTimeout then it may quit. + /// + public virtual int IdleTimeout + { + get { return _idleTimeout; } + set + { + ThrowIfReadOnly(); + _idleTimeout = value; + } + } + + + /// + /// Get/Set the lower limit of threads in the pool. + /// + public virtual int MinWorkerThreads + { + get { return _minWorkerThreads; } + set + { + ThrowIfReadOnly(); + _minWorkerThreads = value; + } + } + + + /// + /// Get/Set the upper limit of threads in the pool. + /// + public virtual int MaxWorkerThreads + { + get { return _maxWorkerThreads; } + set + { + ThrowIfReadOnly(); + _maxWorkerThreads = value; + } + } + +#if !(WINDOWS_PHONE) + /// + /// Get/Set the scheduling priority of the threads in the pool. + /// The Os handles the scheduling. + /// + public virtual ThreadPriority ThreadPriority + { + get { return _threadPriority; } + set + { + ThrowIfReadOnly(); + _threadPriority = value; + } + } +#endif + /// + /// Get/Set the thread pool name. Threads will get names depending on this. + /// + public virtual string ThreadPoolName { + get { return _threadPoolName; } + set + { + ThrowIfReadOnly (); + _threadPoolName = value; + } + } + + /// + /// Get/Set the performance counter instance name of this SmartThreadPool + /// The default is null which indicate not to use performance counters at all. + /// + public virtual string PerformanceCounterInstanceName + { + get { return _performanceCounterInstanceName; } + set + { + ThrowIfReadOnly(); + _performanceCounterInstanceName = value; + } + } + + /// + /// Enable/Disable the local performance counter. + /// This enables the user to get some performance information about the SmartThreadPool + /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.) + /// The default is false. + /// + public virtual bool EnableLocalPerformanceCounters + { + get { return _enableLocalPerformanceCounters; } + set + { + ThrowIfReadOnly(); + _enableLocalPerformanceCounters = value; + } + } + + /// + /// Get/Set backgroundness of thread in thread pool. + /// + public virtual bool AreThreadsBackground + { + get { return _areThreadsBackground; } + set + { + ThrowIfReadOnly (); + _areThreadsBackground = value; + } + } + + /// + /// Get a readonly version of this STPStartInfo. + /// + /// Returns a readonly reference to this STPStartInfo + public new STPStartInfo AsReadOnly() + { + return new STPStartInfo(this) { _readOnly = true }; + } + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState; + + /// + /// Get/Set the apartment state of threads in the thread pool + /// + public ApartmentState ApartmentState + { + get { return _apartmentState; } + set + { + ThrowIfReadOnly(); + _apartmentState = value; + } + } + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + /// + /// Get/Set the max stack size of threads in the thread pool + /// + public int? MaxStackSize + { + get { return _maxStackSize; } + set + { + ThrowIfReadOnly(); + if (value.HasValue && value.Value < 0) + { + throw new ArgumentOutOfRangeException("value", "Value must be greater than 0."); + } + _maxStackSize = value; + } + } +#endif + +#endif + } +} diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs new file mode 100644 index 0000000..ba7d73f --- /dev/null +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs @@ -0,0 +1,60 @@ + +using System; +using Amib.Threading.Internal; + +namespace Amib.Threading +{ + public partial class SmartThreadPool + { + #region ThreadEntry class + + internal class ThreadEntry + { + /// + /// The thread creation time + /// The value is stored as UTC value. + /// + private readonly DateTime _creationTime; + + /// + /// The last time this thread has been running + /// It is updated by IAmAlive() method + /// The value is stored as UTC value. + /// + private DateTime _lastAliveTime; + + /// + /// A reference from each thread in the thread pool to its SmartThreadPool + /// object container. + /// With this variable a thread can know whatever it belongs to a + /// SmartThreadPool. + /// + private readonly SmartThreadPool _associatedSmartThreadPool; + + /// + /// A reference to the current work item a thread from the thread pool + /// is executing. + /// + public WorkItem CurrentWorkItem { get; set; } + + public ThreadEntry(SmartThreadPool stp) + { + _associatedSmartThreadPool = stp; + _creationTime = DateTime.UtcNow; + _lastAliveTime = DateTime.MinValue; + } + + public SmartThreadPool AssociatedSmartThreadPool + { + get { return _associatedSmartThreadPool; } + } + + public void IAmAlive() + { + _lastAliveTime = DateTime.UtcNow; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs index 19a0007..9256777 100644 --- a/ThirdParty/SmartThreadPool/SmartThreadPool.cs +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs @@ -1,1448 +1,1732 @@ -// Ami Bar -// amibar@gmail.com -// -// Smart thread pool in C#. -// 7 Aug 2004 - Initial release -// 14 Sep 2004 - Bug fixes -// 15 Oct 2004 - Added new features -// - Work items return result. -// - Support waiting synchronization for multiple work items. -// - Work items can be cancelled. -// - Passage of the caller thread’s context to the thread in the pool. -// - Minimal usage of WIN32 handles. -// - Minor bug fixes. -// 26 Dec 2004 - Changes: -// - Removed static constructors. -// - Added finalizers. -// - Changed Exceptions so they are serializable. -// - Fixed the bug in one of the SmartThreadPool constructors. -// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. -// The SmartThreadPool.WaitAny() is still limited by the .NET Framework. -// - Added PostExecute with options on which cases to call it. -// - Added option to dispose of the state objects. -// - Added a WaitForIdle() method that waits until the work items queue is empty. -// - Added an STPStartInfo class for the initialization of the thread pool. -// - Changed exception handling so if a work item throws an exception it -// is rethrown at GetResult(), rather then firing an UnhandledException event. -// Note that PostExecute exception are always ignored. -// 25 Mar 2005 - Changes: -// - Fixed lost of work items bug -// 3 Jul 2005: Changes. -// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. -// 16 Aug 2005: Changes. -// - Fixed bug where the InUseThreads becomes negative when canceling work items. -// -// 31 Jan 2006 - Changes: -// - Added work items priority -// - Removed support of chained delegates in callbacks and post executes (nobody really use this) -// - Added work items groups -// - Added work items groups idle event -// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array -// it returns true rather then throwing an exception. -// - Added option to start the STP and the WIG as suspended -// - Exception behavior changed, the real exception is returned by an -// inner exception -// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) -// - Added performance counters -// - Added priority to the threads in the pool -// -// 13 Feb 2006 - Changes: -// - Added a call to the dispose of the Performance Counter so -// their won't be a Performance Counter leak. -// - Added exception catch in case the Performance Counters cannot -// be created. - -using System; -using System.Security; -using System.Threading; -using System.Collections; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -using Amib.Threading.Internal; - -namespace Amib.Threading -{ - #region SmartThreadPool class - /// - /// Smart thread pool class. - /// - public class SmartThreadPool : IWorkItemsGroup, IDisposable - { - #region Default Constants - - /// - /// Default minimum number of threads the thread pool contains. (0) - /// - public const int DefaultMinWorkerThreads = 0; - - /// - /// Default maximum number of threads the thread pool contains. (25) - /// - public const int DefaultMaxWorkerThreads = 25; - - /// - /// Default idle timeout in milliseconds. (One minute) - /// - public const int DefaultIdleTimeout = 60*1000; // One minute - - /// - /// Indicate to copy the security context of the caller and then use it in the call. (false) - /// - public const bool DefaultUseCallerCallContext = false; - - /// - /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) - /// - public const bool DefaultUseCallerHttpContext = false; - - /// - /// Indicate to dispose of the state objects if they support the IDispose interface. (false) - /// - public const bool DefaultDisposeOfStateObjects = false; - - /// - /// The default option to run the post execute - /// - public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; - - /// - /// The default post execute method to run. - /// When null it means not to call it. - /// - public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null; - - /// - /// The default work item priority - /// - public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; - - /// - /// The default is to work on work items as soon as they arrive - /// and not to wait for the start. - /// - public const bool DefaultStartSuspended = false; - - /// - /// The default is not to use the performance counters - /// - public static readonly string DefaultPerformanceCounterInstanceName = null; - - public static readonly int DefaultStackSize = 0; - - /// - /// The default thread priority - /// - public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; - - /// - /// The default thread pool name - /// - public const string DefaultThreadPoolName = "SmartThreadPool"; - - #endregion - - #region Member Variables - - /// - /// Contains the name of this instance of SmartThreadPool. - /// Can be changed by the user. - /// - private string _name = DefaultThreadPoolName; - - /// - /// Hashtable of all the threads in the thread pool. - /// - private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable()); - - /// - /// Queue of work items. - /// - private WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); - - /// - /// Count the work items handled. - /// Used by the performance counter. - /// - private long _workItemsProcessed = 0; - - /// - /// Number of threads that currently work (not idle). - /// - private int _inUseWorkerThreads = 0; - - /// - /// Start information to use. - /// It is simpler than providing many constructors. - /// - private STPStartInfo _stpStartInfo = new STPStartInfo(); - - /// - /// Total number of work items that are stored in the work items queue - /// plus the work items that the threads in the pool are working on. - /// - private int _currentWorkItemsCount = 0; - - /// - /// Signaled when the thread pool is idle, i.e. no thread is busy - /// and the work items queue is empty - /// - private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); - - /// - /// An event to signal all the threads to quit immediately. - /// - private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); - - /// - /// A flag to indicate the threads to quit. - /// - private bool _shutdown = false; - - /// - /// Counts the threads created in the pool. - /// It is used to name the threads. - /// - private int _threadCounter = 0; - - /// - /// Indicate that the SmartThreadPool has been disposed - /// - private bool _isDisposed = false; - - /// - /// Event to send that the thread pool is idle - /// - private event EventHandler _stpIdle; - - /// - /// On idle event - /// - //private event WorkItemsGroupIdleHandler _onIdle; - - /// - /// Holds all the WorkItemsGroup instaces that have at least one - /// work item int the SmartThreadPool - /// This variable is used in case of Shutdown - /// - private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable()); - - /// - /// A reference from each thread in the thread pool to its SmartThreadPool - /// object container. - /// With this variable a thread can know whatever it belongs to a - /// SmartThreadPool. - /// - [ThreadStatic] - private static SmartThreadPool _smartThreadPool; - - /// - /// A reference to the current work item a thread from the thread pool - /// is executing. - /// - [ThreadStatic] - private static WorkItem _currentWorkItem; - - /// - /// STP performance counters - /// - private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance; - - #endregion - - #region Construction and Finalization - - /// - /// Constructor - /// - public SmartThreadPool() - { - Initialize(); - } - - /// - /// Constructor - /// - /// Idle timeout in milliseconds - public SmartThreadPool(int idleTimeout) - { - _stpStartInfo.IdleTimeout = idleTimeout; - Initialize(); - } - - /// - /// Constructor - /// - /// Idle timeout in milliseconds - /// Upper limit of threads in the pool - public SmartThreadPool( - int idleTimeout, - int maxWorkerThreads) - { - _stpStartInfo.IdleTimeout = idleTimeout; - _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; - Initialize(); - } - - /// - /// Constructor - /// - /// Idle timeout in milliseconds - /// Upper limit of threads in the pool - /// Lower limit of threads in the pool - public SmartThreadPool( - int idleTimeout, - int maxWorkerThreads, - int minWorkerThreads) - { - _stpStartInfo.IdleTimeout = idleTimeout; - _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; - _stpStartInfo.MinWorkerThreads = minWorkerThreads; - Initialize(); - } - - /// - /// Constructor - /// - public SmartThreadPool(STPStartInfo stpStartInfo) - { - _stpStartInfo = new STPStartInfo(stpStartInfo); - Initialize(); - } - - private void Initialize() - { - Name = _stpStartInfo.ThreadPoolName; - ValidateSTPStartInfo(); - - if (null != _stpStartInfo.PerformanceCounterInstanceName) - { - try - { - _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); - } - catch(Exception e) - { - Debug.WriteLine("Unable to create Performance Counters: " + e.ToString()); - _pcs = NullSTPInstancePerformanceCounters.Instance; - } - } - - StartOptimalNumberOfThreads(); - } - - private void StartOptimalNumberOfThreads() - { - int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); - threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); - StartThreads(threadsCount); - } - - private void ValidateSTPStartInfo() - { - if (_stpStartInfo.MinWorkerThreads < 0) - { - throw new ArgumentOutOfRangeException( - "MinWorkerThreads", "MinWorkerThreads cannot be negative"); - } - - if (_stpStartInfo.MaxWorkerThreads <= 0) - { - throw new ArgumentOutOfRangeException( - "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); - } - - if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) - { - throw new ArgumentOutOfRangeException( - "MinWorkerThreads, maxWorkerThreads", - "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); - } - } - - private void ValidateCallback(Delegate callback) - { - if(callback.GetInvocationList().Length > 1) - { - throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); - } - } - - #endregion - - #region Thread Processing - - /// - /// Waits on the queue for a work item, shutdown, or timeout. - /// - /// - /// Returns the WaitingCallback or null in case of timeout or shutdown. - /// - private WorkItem Dequeue() - { - WorkItem workItem = - _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); - - return workItem; - } - - /// - /// Put a new work item in the queue - /// - /// A work item to queue - private void Enqueue(WorkItem workItem) - { - Enqueue(workItem, true); - } - - /// - /// Put a new work item in the queue - /// - /// A work item to queue - internal void Enqueue(WorkItem workItem, bool incrementWorkItems) - { - // Make sure the workItem is not null - Debug.Assert(null != workItem); - - if (incrementWorkItems) - { - IncrementWorkItemsCount(); - } - - _workItemsQueue.EnqueueWorkItem(workItem); - workItem.WorkItemIsQueued(); - - // If all the threads are busy then try to create a new one - if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count) - { - StartThreads(1); - } - } - - private void IncrementWorkItemsCount() - { - _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - - int count = Interlocked.Increment(ref _currentWorkItemsCount); - //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); - if (count == 1) - { - //Trace.WriteLine("STP is NOT idle"); - _isIdleWaitHandle.Reset(); - } - } - - private void DecrementWorkItemsCount() - { - ++_workItemsProcessed; - - // The counter counts even if the work item was cancelled - _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - - int count = Interlocked.Decrement(ref _currentWorkItemsCount); - //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); - if (count == 0) - { - //Trace.WriteLine("STP is idle"); - _isIdleWaitHandle.Set(); - } - } - - internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) - { - _workItemsGroups[workItemsGroup] = workItemsGroup; - } - - internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) - { - if (_workItemsGroups.Contains(workItemsGroup)) - { - _workItemsGroups.Remove(workItemsGroup); - } - } - - /// - /// Inform that the current thread is about to quit or quiting. - /// The same thread may call this method more than once. - /// - private void InformCompleted() - { - // There is no need to lock the two methods together - // since only the current thread removes itself - // and the _workerThreads is a synchronized hashtable - if (_workerThreads.Contains(Thread.CurrentThread)) - { - _workerThreads.Remove(Thread.CurrentThread); - _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - } - } - - /// - /// Starts new threads - /// - /// The number of threads to start - private void StartThreads(int threadsCount) - { - if (_stpStartInfo.StartSuspended) - { - return; - } - - lock(_workerThreads.SyncRoot) - { - // Don't start threads on shut down - if (_shutdown) - { - return; - } - - for(int i = 0; i < threadsCount; ++i) - { - // Don't create more threads then the upper limit - if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) - { - return; - } - - // Create a new thread - Thread workerThread; - if (_stpStartInfo.StackSize > 0) - workerThread = new Thread(ProcessQueuedItems, _stpStartInfo.StackSize); - else - workerThread = new Thread(ProcessQueuedItems); - - // Configure the new thread and start it - workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; - workerThread.IsBackground = true; - workerThread.Priority = _stpStartInfo.ThreadPriority; - workerThread.Start(); - ++_threadCounter; - - // Add the new thread to the hashtable and update its creation - // time. - _workerThreads[workerThread] = DateTime.Now; - _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - } - } - } - - /// - /// A worker thread method that processes work items from the work items queue. - /// - private void ProcessQueuedItems() - { - // Initialize the _smartThreadPool variable - _smartThreadPool = this; - - try - { - bool bInUseWorkerThreadsWasIncremented = false; - - // Process until shutdown. - while(!_shutdown) - { - // Update the last time this thread was seen alive. - // It's good for debugging. - _workerThreads[Thread.CurrentThread] = DateTime.Now; - - // Wait for a work item, shutdown, or timeout - WorkItem workItem = Dequeue(); - - // Update the last time this thread was seen alive. - // It's good for debugging. - _workerThreads[Thread.CurrentThread] = DateTime.Now; - - // On timeout or shut down. - if (null == workItem) - { - // Double lock for quit. - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) - { - lock(_workerThreads.SyncRoot) - { - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) - { - // Inform that the thread is quiting and then quit. - // This method must be called within this lock or else - // more threads will quit and the thread pool will go - // below the lower limit. - InformCompleted(); - break; - } - } - } - } - - // If we didn't quit then skip to the next iteration. - if (null == workItem) - { - continue; - } - - try - { - // Initialize the value to false - bInUseWorkerThreadsWasIncremented = false; - - // Change the state of the work item to 'in progress' if possible. - // We do it here so if the work item has been canceled we won't - // increment the _inUseWorkerThreads. - // The cancel mechanism doesn't delete items from the queue, - // it marks the work item as canceled, and when the work item - // is dequeued, we just skip it. - // If the post execute of work item is set to always or to - // call when the work item is canceled then the StartingWorkItem() - // will return true, so the post execute can run. - if (!workItem.StartingWorkItem()) - { - continue; - } - - // Execute the callback. Make sure to accurately - // record how many callbacks are currently executing. - int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); - _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - - // Mark that the _inUseWorkerThreads incremented, so in the finally{} - // statement we will decrement it correctly. - bInUseWorkerThreadsWasIncremented = true; - - // Set the _currentWorkItem to the current work item - _currentWorkItem = workItem; - - lock(workItem) - { - workItem.currentThread = Thread.CurrentThread; - } - - ExecuteWorkItem(workItem); - - lock(workItem) - { - workItem.currentThread = null; - } - - } - catch(ThreadAbortException ex) - { - lock(workItem) - { - workItem.currentThread = null; - } - ex.GetHashCode(); - Thread.ResetAbort(); - } - catch(Exception ex) - { - ex.GetHashCode(); - // Do nothing - } - finally - { - lock(workItem) - { - workItem.currentThread = null; - } - - if (null != workItem) - { - workItem.DisposeOfState(); - } - - // Set the _currentWorkItem to null, since we - // no longer run user's code. - _currentWorkItem = null; - - // Decrement the _inUseWorkerThreads only if we had - // incremented it. Note the cancelled work items don't - // increment _inUseWorkerThreads. - if (bInUseWorkerThreadsWasIncremented) - { - int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); - _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - } - - // Notify that the work item has been completed. - // WorkItemsGroup may enqueue their next work item. - workItem.FireWorkItemCompleted(); - - // Decrement the number of work items here so the idle - // ManualResetEvent won't fluctuate. - DecrementWorkItemsCount(); - } - } - } - catch(ThreadAbortException tae) - { - tae.GetHashCode(); - // Handle the abort exception gracfully. - Thread.ResetAbort(); - } - catch(Exception e) - { - Debug.Assert(null != e); - } - finally - { - InformCompleted(); - } - } - - private void ExecuteWorkItem(WorkItem workItem) - { - _pcs.SampleWorkItemsWaitTime(workItem.WaitingTime); - try - { - workItem.Execute(); - } - catch - { - throw; - } - finally - { - _pcs.SampleWorkItemsProcessTime(workItem.ProcessTime); - } - } - - - #endregion - - #region Public Methods - - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - ValidateNotDisposed(); - ValidateCallback(callback); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Wait for the thread pool to be idle - /// - public void WaitForIdle() - { - WaitForIdle(Timeout.Infinite); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(TimeSpan timeout) - { - return WaitForIdle((int)timeout.TotalMilliseconds); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(int millisecondsTimeout) - { - ValidateWaitForIdle(); - return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); - } - - private void ValidateWaitForIdle() - { - if(_smartThreadPool == this) - { - throw new NotSupportedException( - "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); - } - } - - internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) - { - ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem); - if ((null != workItemsGroup) && - (null != SmartThreadPool._currentWorkItem) && - SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup)) - { - throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) - { - if ((null != workItemsGroup) && - (null != workItem) && - workItem.WasQueuedBy(workItemsGroup)) - { - throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); - } - } - - - - /// - /// Force the SmartThreadPool to shutdown - /// - public void Shutdown() - { - Shutdown(true, 0); - } - - public void Shutdown(bool forceAbort, TimeSpan timeout) - { - Shutdown(forceAbort, (int)timeout.TotalMilliseconds); - } - - /// - /// Empties the queue of work items and abort the threads in the pool. - /// - public void Shutdown(bool forceAbort, int millisecondsTimeout) - { - ValidateNotDisposed(); - - ISTPInstancePerformanceCounters pcs = _pcs; - - if (NullSTPInstancePerformanceCounters.Instance != _pcs) - { - _pcs.Dispose(); - // Set the _pcs to "null" to stop updating the performance - // counters - _pcs = NullSTPInstancePerformanceCounters.Instance; - } - - Thread [] threads = null; - lock(_workerThreads.SyncRoot) - { - // Shutdown the work items queue - _workItemsQueue.Dispose(); - - // Signal the threads to exit - _shutdown = true; - _shuttingDownEvent.Set(); - - // Make a copy of the threads' references in the pool - threads = new Thread [_workerThreads.Count]; - _workerThreads.Keys.CopyTo(threads, 0); - } - - int millisecondsLeft = millisecondsTimeout; - DateTime start = DateTime.Now; - bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); - bool timeout = false; - - // Each iteration we update the time left for the timeout. - foreach(Thread thread in threads) - { - // Join don't work with negative numbers - if (!waitInfinitely && (millisecondsLeft < 0)) - { - timeout = true; - break; - } - - // Wait for the thread to terminate - bool success = thread.Join(millisecondsLeft); - if(!success) - { - timeout = true; - break; - } - - if(!waitInfinitely) - { - // Update the time left to wait - TimeSpan ts = DateTime.Now - start; - millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds; - } - } - - if (timeout && forceAbort) - { - // Abort the threads in the pool - foreach(Thread thread in threads) - { - if ((thread != null) && thread.IsAlive) - { - try - { - thread.Abort("Shutdown"); - } - catch(SecurityException e) - { - e.GetHashCode(); - } - catch(ThreadStateException ex) - { - ex.GetHashCode(); - // In case the thread has been terminated - // after the check if it is alive. - } - } - } - } - - // Dispose of the performance counters - pcs.Dispose(); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults) - { - return WaitAll(workItemResults, Timeout.Infinite, true); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext) - { - return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext) - { - return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); - } - - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults) - { - return WaitAny(workItemResults, Timeout.Infinite, true); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext) - { - return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext) - { - return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWorkItemResult [] workItemResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) - { - IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); - return workItemsGroup; - } - - public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) - { - IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); - return workItemsGroup; - } - - public event WorkItemsGroupIdleHandler OnIdle - { - add - { - throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); - //_onIdle += value; - } - remove - { - throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); - //_onIdle -= value; - } - } - - public void Cancel() - { - ICollection workItemsGroups = _workItemsGroups.Values; - foreach(WorkItemsGroup workItemsGroup in workItemsGroups) - { - workItemsGroup.Cancel(); - } - } - - public void Start() - { - lock (this) - { - if (!this._stpStartInfo.StartSuspended) - { - return; - } - _stpStartInfo.StartSuspended = false; - } - - ICollection workItemsGroups = _workItemsGroups.Values; - foreach(WorkItemsGroup workItemsGroup in workItemsGroups) - { - workItemsGroup.OnSTPIsStarting(); - } - - StartOptimalNumberOfThreads(); - } - - #endregion - - #region Properties - - /// - /// Get/Set the name of the SmartThreadPool instance - /// - public string Name - { - get - { - return _name; - } - - set - { - _name = value; - } - } - - /// - /// Get the lower limit of threads in the pool. - /// - public int MinThreads - { - get - { - ValidateNotDisposed(); - return _stpStartInfo.MinWorkerThreads; - } - } - - /// - /// Get the upper limit of threads in the pool. - /// - public int MaxThreads - { - get - { - ValidateNotDisposed(); - return _stpStartInfo.MaxWorkerThreads; - } - } - /// - /// Get the number of threads in the thread pool. - /// Should be between the lower and the upper limits. - /// - public int ActiveThreads - { - get - { - ValidateNotDisposed(); - return _workerThreads.Count; - } - } - - /// - /// Get the number of busy (not idle) threads in the thread pool. - /// - public int InUseThreads - { - get - { - ValidateNotDisposed(); - return _inUseWorkerThreads; - } - } - - /// - /// Get the number of work items in the queue. - /// - public int WaitingCallbacks - { - get - { - ValidateNotDisposed(); - return _workItemsQueue.Count; - } - } - - - public event EventHandler Idle - { - add - { - _stpIdle += value; - } - - remove - { - _stpIdle -= value; - } - } - - #endregion - - #region IDisposable Members - -// ~SmartThreadPool() -// { -// Dispose(); -// } - - public void Dispose() - { - if (!_isDisposed) - { - if (!_shutdown) - { - Shutdown(); - } - - if (null != _shuttingDownEvent) - { - _shuttingDownEvent.Close(); - _shuttingDownEvent = null; - } - _workerThreads.Clear(); - _isDisposed = true; - GC.SuppressFinalize(this); - } - } - - private void ValidateNotDisposed() - { - if(_isDisposed) - { - throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); - } - } - #endregion - } - #endregion -} +#region Release History + +// Smart Thread Pool +// 7 Aug 2004 - Initial release +// +// 14 Sep 2004 - Bug fixes +// +// 15 Oct 2004 - Added new features +// - Work items return result. +// - Support waiting synchronization for multiple work items. +// - Work items can be cancelled. +// - Passage of the caller thread’s context to the thread in the pool. +// - Minimal usage of WIN32 handles. +// - Minor bug fixes. +// +// 26 Dec 2004 - Changes: +// - Removed static constructors. +// - Added finalizers. +// - Changed Exceptions so they are serializable. +// - Fixed the bug in one of the SmartThreadPool constructors. +// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. +// The SmartThreadPool.WaitAny() is still limited by the .NET Framework. +// - Added PostExecute with options on which cases to call it. +// - Added option to dispose of the state objects. +// - Added a WaitForIdle() method that waits until the work items queue is empty. +// - Added an STPStartInfo class for the initialization of the thread pool. +// - Changed exception handling so if a work item throws an exception it +// is rethrown at GetResult(), rather then firing an UnhandledException event. +// Note that PostExecute exception are always ignored. +// +// 25 Mar 2005 - Changes: +// - Fixed lost of work items bug +// +// 3 Jul 2005: Changes. +// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. +// +// 16 Aug 2005: Changes. +// - Fixed bug where the InUseThreads becomes negative when canceling work items. +// +// 31 Jan 2006 - Changes: +// - Added work items priority +// - Removed support of chained delegates in callbacks and post executes (nobody really use this) +// - Added work items groups +// - Added work items groups idle event +// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array +// it returns true rather then throwing an exception. +// - Added option to start the STP and the WIG as suspended +// - Exception behavior changed, the real exception is returned by an +// inner exception +// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) +// - Added performance counters +// - Added priority to the threads in the pool +// +// 13 Feb 2006 - Changes: +// - Added a call to the dispose of the Performance Counter so +// their won't be a Performance Counter leak. +// - Added exception catch in case the Performance Counters cannot +// be created. +// +// 17 May 2008 - Changes: +// - Changed the dispose behavior and removed the Finalizers. +// - Enabled the change of the MaxThreads and MinThreads at run time. +// - Enabled the change of the Concurrency of a IWorkItemsGroup at run +// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency +// refers to the MaxThreads. +// - Improved the cancel behavior. +// - Added events for thread creation and termination. +// - Fixed the HttpContext context capture. +// - Changed internal collections so they use generic collections +// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup +// - Added support for WinCE +// - Added support for Action and Func +// +// 07 April 2009 - Changes: +// - Added support for Silverlight and Mono +// - Added Join, Choice, and Pipe to SmartThreadPool. +// - Added local performance counters (for Mono, Silverlight, and WindowsCE) +// - Changed duration measures from DateTime.Now to Stopwatch. +// - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList. +// +// 21 December 2009 - Changes: +// - Added work item timeout (passive) +// +// 20 August 2012 - Changes: +// - Added set name to threads +// - Fixed the WorkItemsQueue.Dequeue. +// Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... } +// - Fixed SmartThreadPool.Pipe +// - Added IsBackground option to threads +// - Added ApartmentState to threads +// - Fixed thread creation when queuing many work items at the same time. +// +// 24 August 2012 - Changes: +// - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan +// - Added option to set MaxStackSize of threads + +#endregion + +using System; +using System.Security; +using System.Threading; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +using Amib.Threading.Internal; + +namespace Amib.Threading +{ + #region SmartThreadPool class + /// + /// Smart thread pool class. + /// + public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable + { + #region Public Default Constants + + /// + /// Default minimum number of threads the thread pool contains. (0) + /// + public const int DefaultMinWorkerThreads = 0; + + /// + /// Default maximum number of threads the thread pool contains. (25) + /// + public const int DefaultMaxWorkerThreads = 25; + + /// + /// Default idle timeout in milliseconds. (One minute) + /// + public const int DefaultIdleTimeout = 60*1000; // One minute + + /// + /// Indicate to copy the security context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerCallContext = false; + + /// + /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerHttpContext = false; + + /// + /// Indicate to dispose of the state objects if they support the IDispose interface. (false) + /// + public const bool DefaultDisposeOfStateObjects = false; + + /// + /// The default option to run the post execute (CallToPostExecute.Always) + /// + public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; + + /// + /// The default post execute method to run. (None) + /// When null it means not to call it. + /// + public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback; + + /// + /// The default work item priority (WorkItemPriority.Normal) + /// + public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; + + /// + /// The default is to work on work items as soon as they arrive + /// and not to wait for the start. (false) + /// + public const bool DefaultStartSuspended = false; + + /// + /// The default name to use for the performance counters instance. (null) + /// + public static readonly string DefaultPerformanceCounterInstanceName; + +#if !(WINDOWS_PHONE) + + /// + /// The default thread priority (ThreadPriority.Normal) + /// + public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; +#endif + /// + /// The default thread pool name. (SmartThreadPool) + /// + public const string DefaultThreadPoolName = "SmartThreadPool"; + + /// + /// The default Max Stack Size. (SmartThreadPool) + /// + public static readonly int? DefaultMaxStackSize = null; + + /// + /// The default fill state with params. (false) + /// It is relevant only to QueueWorkItem of Action<...>/Func<...> + /// + public const bool DefaultFillStateWithArgs = false; + + /// + /// The default thread backgroundness. (true) + /// + public const bool DefaultAreThreadsBackground = true; + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// The default apartment state of a thread in the thread pool. + /// The default is ApartmentState.Unknown which means the STP will not + /// set the apartment of the thread. It will use the .NET default. + /// + public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; +#endif + + #endregion + + #region Member Variables + + /// + /// Dictionary of all the threads in the thread pool. + /// + private readonly SynchronizedDictionary _workerThreads = new SynchronizedDictionary(); + + /// + /// Queue of work items. + /// + private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); + + /// + /// Count the work items handled. + /// Used by the performance counter. + /// + private int _workItemsProcessed; + + /// + /// Number of threads that currently work (not idle). + /// + private int _inUseWorkerThreads; + + /// + /// Stores a copy of the original STPStartInfo. + /// It is used to change the MinThread and MaxThreads + /// + private STPStartInfo _stpStartInfo; + + /// + /// Total number of work items that are stored in the work items queue + /// plus the work items that the threads in the pool are working on. + /// + private int _currentWorkItemsCount; + + /// + /// Signaled when the thread pool is idle, i.e. no thread is busy + /// and the work items queue is empty + /// + //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + + /// + /// An event to signal all the threads to quit immediately. + /// + //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); + private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false); + + /// + /// A flag to indicate if the Smart Thread Pool is now suspended. + /// + private bool _isSuspended; + + /// + /// A flag to indicate the threads to quit. + /// + private bool _shutdown; + + /// + /// Counts the threads created in the pool. + /// It is used to name the threads. + /// + private int _threadCounter; + + /// + /// Indicate that the SmartThreadPool has been disposed + /// + private bool _isDisposed; + + /// + /// Holds all the WorkItemsGroup instaces that have at least one + /// work item int the SmartThreadPool + /// This variable is used in case of Shutdown + /// + private readonly SynchronizedDictionary _workItemsGroups = new SynchronizedDictionary(); + + /// + /// A common object for all the work items int the STP + /// so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup(); + + /// + /// Windows STP performance counters + /// + private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + + /// + /// Local STP performance counters + /// + private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance; + + +#if (WINDOWS_PHONE) + private static readonly Dictionary _threadEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot(); +#else + [ThreadStatic] + private static ThreadEntry _threadEntry; + +#endif + + /// + /// An event to call after a thread is created, but before + /// it's first use. + /// + private event ThreadInitializationHandler _onThreadInitialization; + + /// + /// An event to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + private event ThreadTerminationHandler _onThreadTermination; + + #endregion + + #region Per thread properties + + /// + /// A reference to the current work item a thread from the thread pool + /// is executing. + /// + internal static ThreadEntry CurrentThreadEntry + { +#if (WINDOWS_PHONE) + get + { + lock(_threadEntries) + { + ThreadEntry threadEntry; + if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry)) + { + return threadEntry; + } + } + return null; + } + set + { + lock(_threadEntries) + { + _threadEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + //Thread.CurrentThread.ManagedThreadId + return Thread.GetData(_threadEntrySlot) as ThreadEntry; + } + set + { + Thread.SetData(_threadEntrySlot, value); + } +#else + get + { + return _threadEntry; + } + set + { + _threadEntry = value; + } +#endif + } + #endregion + + #region Construction and Finalization + + /// + /// Constructor + /// + public SmartThreadPool() + { + _stpStartInfo = new STPStartInfo(); + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + public SmartThreadPool(int idleTimeout) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + /// Lower limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads, + int minWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + MinWorkerThreads = minWorkerThreads, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// A SmartThreadPool configuration that overrides the default behavior + public SmartThreadPool(STPStartInfo stpStartInfo) + { + _stpStartInfo = new STPStartInfo(stpStartInfo); + Initialize(); + } + + private void Initialize() + { + Name = _stpStartInfo.ThreadPoolName; + ValidateSTPStartInfo(); + + // _stpStartInfoRW stores a read/write copy of the STPStartInfo. + // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten + + _isSuspended = _stpStartInfo.StartSuspended; + +#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE) + if (null != _stpStartInfo.PerformanceCounterInstanceName) + { + throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); + } +#else + if (null != _stpStartInfo.PerformanceCounterInstanceName) + { + try + { + _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); + } + catch (Exception e) + { + Debug.WriteLine("Unable to create Performance Counters: " + e); + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + } + } +#endif + + if (_stpStartInfo.EnableLocalPerformanceCounters) + { + _localPCs = new LocalSTPInstancePerformanceCounters(); + } + + // If the STP is not started suspended then start the threads. + if (!_isSuspended) + { + StartOptimalNumberOfThreads(); + } + } + + private void StartOptimalNumberOfThreads() + { + int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); + threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); + threadsCount -= _workerThreads.Count; + if (threadsCount > 0) + { + StartThreads(threadsCount); + } + } + + private void ValidateSTPStartInfo() + { + if (_stpStartInfo.MinWorkerThreads < 0) + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads", "MinWorkerThreads cannot be negative"); + } + + if (_stpStartInfo.MaxWorkerThreads <= 0) + { + throw new ArgumentOutOfRangeException( + "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); + } + + if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads, maxWorkerThreads", + "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); + } + } + + private static void ValidateCallback(Delegate callback) + { + if(callback.GetInvocationList().Length > 1) + { + throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); + } + } + + #endregion + + #region Thread Processing + + /// + /// Waits on the queue for a work item, shutdown, or timeout. + /// + /// + /// Returns the WaitingCallback or null in case of timeout or shutdown. + /// + private WorkItem Dequeue() + { + WorkItem workItem = + _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); + + return workItem; + } + + /// + /// Put a new work item in the queue + /// + /// A work item to queue + internal override void Enqueue(WorkItem workItem) + { + // Make sure the workItem is not null + Debug.Assert(null != workItem); + + IncrementWorkItemsCount(); + + workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; + _workItemsQueue.EnqueueWorkItem(workItem); + workItem.WorkItemIsQueued(); + + // If all the threads are busy then try to create a new one + if (_currentWorkItemsCount > _workerThreads.Count) + { + StartThreads(1); + } + } + + private void IncrementWorkItemsCount() + { + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + + int count = Interlocked.Increment(ref _currentWorkItemsCount); + //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); + if (count == 1) + { + IsIdle = false; + _isIdleWaitHandle.Reset(); + } + } + + private void DecrementWorkItemsCount() + { + int count = Interlocked.Decrement(ref _currentWorkItemsCount); + //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); + if (count == 0) + { + IsIdle = true; + _isIdleWaitHandle.Set(); + } + + Interlocked.Increment(ref _workItemsProcessed); + + if (!_shutdown) + { + // The counter counts even if the work item was cancelled + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + } + + } + + internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + _workItemsGroups[workItemsGroup] = workItemsGroup; + } + + internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + if (_workItemsGroups.Contains(workItemsGroup)) + { + _workItemsGroups.Remove(workItemsGroup); + } + } + + /// + /// Inform that the current thread is about to quit or quiting. + /// The same thread may call this method more than once. + /// + private void InformCompleted() + { + // There is no need to lock the two methods together + // since only the current thread removes itself + // and the _workerThreads is a synchronized dictionary + if (_workerThreads.Contains(Thread.CurrentThread)) + { + _workerThreads.Remove(Thread.CurrentThread); + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + + /// + /// Starts new threads + /// + /// The number of threads to start + private void StartThreads(int threadsCount) + { + if (_isSuspended) + { + return; + } + + lock(_workerThreads.SyncRoot) + { + // Don't start threads on shut down + if (_shutdown) + { + return; + } + + for(int i = 0; i < threadsCount; ++i) + { + // Don't create more threads then the upper limit + if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) + { + return; + } + + // Create a new thread + +#if (_SILVERLIGHT) || (WINDOWS_PHONE) + Thread workerThread = new Thread(ProcessQueuedItems); +#else + Thread workerThread = + _stpStartInfo.MaxStackSize.HasValue + ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) + : new Thread(ProcessQueuedItems); +#endif + // Configure the new thread and start it + workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; + workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; + +#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE) + if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) + { + workerThread.SetApartmentState(_stpStartInfo.ApartmentState); + } +#endif + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + workerThread.Priority = _stpStartInfo.ThreadPriority; +#endif + workerThread.Start(); + ++_threadCounter; + + // Add it to the dictionary and update its creation time. + _workerThreads[workerThread] = new ThreadEntry(this); + + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + } + + /// + /// A worker thread method that processes work items from the work items queue. + /// + private void ProcessQueuedItems() + { + // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks + // of the dictionary. + CurrentThreadEntry = _workerThreads[Thread.CurrentThread]; + + FireOnThreadInitialization(); + + try + { + bool bInUseWorkerThreadsWasIncremented = false; + + // Process until shutdown. + while(!_shutdown) + { + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); + + // The following block handles the when the MaxWorkerThreads has been + // incremented by the user at run-time. + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + { + lock (_workerThreads.SyncRoot) + { + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + { + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; + } + } + } + + // Wait for a work item, shutdown, or timeout + WorkItem workItem = Dequeue(); + + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); + + // On timeout or shut down. + if (null == workItem) + { + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + lock(_workerThreads.SyncRoot) + { + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; + } + } + } + } + + // If we didn't quit then skip to the next iteration. + if (null == workItem) + { + continue; + } + + try + { + // Initialize the value to false + bInUseWorkerThreadsWasIncremented = false; + + // Set the Current Work Item of the thread. + // Store the Current Work Item before the workItem.StartingWorkItem() is called, + // so WorkItem.Cancel can work when the work item is between InQueue and InProgress + // states. + // If the work item has been cancelled BEFORE the workItem.StartingWorkItem() + // (work item is in InQueue state) then workItem.StartingWorkItem() will return false. + // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then + // (work item is in InProgress state) then the thread will be aborted + CurrentThreadEntry.CurrentWorkItem = workItem; + + // Change the state of the work item to 'in progress' if possible. + // We do it here so if the work item has been canceled we won't + // increment the _inUseWorkerThreads. + // The cancel mechanism doesn't delete items from the queue, + // it marks the work item as canceled, and when the work item + // is dequeued, we just skip it. + // If the post execute of work item is set to always or to + // call when the work item is canceled then the StartingWorkItem() + // will return true, so the post execute can run. + if (!workItem.StartingWorkItem()) + { + continue; + } + + // Execute the callback. Make sure to accurately + // record how many callbacks are currently executing. + int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + + // Mark that the _inUseWorkerThreads incremented, so in the finally{} + // statement we will decrement it correctly. + bInUseWorkerThreadsWasIncremented = true; + + workItem.FireWorkItemStarted(); + + ExecuteWorkItem(workItem); + } + catch(Exception ex) + { + ex.GetHashCode(); + // Do nothing + } + finally + { + workItem.DisposeOfState(); + + // Set the CurrentWorkItem to null, since we + // no longer run user's code. + CurrentThreadEntry.CurrentWorkItem = null; + + // Decrement the _inUseWorkerThreads only if we had + // incremented it. Note the cancelled work items don't + // increment _inUseWorkerThreads. + if (bInUseWorkerThreadsWasIncremented) + { + int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + } + + // Notify that the work item has been completed. + // WorkItemsGroup may enqueue their next work item. + workItem.FireWorkItemCompleted(); + + // Decrement the number of work items here so the idle + // ManualResetEvent won't fluctuate. + DecrementWorkItemsCount(); + } + } + } + catch(ThreadAbortException tae) + { + tae.GetHashCode(); + // Handle the abort exception gracfully. +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + Thread.ResetAbort(); +#endif + } + catch(Exception e) + { + Debug.Assert(null != e); + } + finally + { + InformCompleted(); + FireOnThreadTermination(); + } + } + + private void ExecuteWorkItem(WorkItem workItem) + { + _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + try + { + workItem.Execute(); + } + finally + { + _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + } + } + + + #endregion + + #region Public Methods + + private void ValidateWaitForIdle() + { + if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this) + { + throw new NotSupportedException( + "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) + { + if (null == CurrentThreadEntry) + { + return; + } + + WorkItem workItem = CurrentThreadEntry.CurrentWorkItem; + ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem); + if ((null != workItemsGroup) && + (null != workItem) && + CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) + { + if ((null != workItemsGroup) && + (null != workItem) && + workItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + /// + /// Force the SmartThreadPool to shutdown + /// + public void Shutdown() + { + Shutdown(true, 0); + } + + /// + /// Force the SmartThreadPool to shutdown with timeout + /// + public void Shutdown(bool forceAbort, TimeSpan timeout) + { + Shutdown(forceAbort, (int)timeout.TotalMilliseconds); + } + + /// + /// Empties the queue of work items and abort the threads in the pool. + /// + public void Shutdown(bool forceAbort, int millisecondsTimeout) + { + ValidateNotDisposed(); + + ISTPInstancePerformanceCounters pcs = _windowsPCs; + + if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs) + { + // Set the _pcs to "null" to stop updating the performance + // counters + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + + pcs.Dispose(); + } + + Thread [] threads; + lock(_workerThreads.SyncRoot) + { + // Shutdown the work items queue + _workItemsQueue.Dispose(); + + // Signal the threads to exit + _shutdown = true; + _shuttingDownEvent.Set(); + + // Make a copy of the threads' references in the pool + threads = new Thread [_workerThreads.Count]; + _workerThreads.Keys.CopyTo(threads, 0); + } + + int millisecondsLeft = millisecondsTimeout; + Stopwatch stopwatch = Stopwatch.StartNew(); + //DateTime start = DateTime.UtcNow; + bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); + bool timeout = false; + + // Each iteration we update the time left for the timeout. + foreach(Thread thread in threads) + { + // Join don't work with negative numbers + if (!waitInfinitely && (millisecondsLeft < 0)) + { + timeout = true; + break; + } + + // Wait for the thread to terminate + bool success = thread.Join(millisecondsLeft); + if(!success) + { + timeout = true; + break; + } + + if(!waitInfinitely) + { + // Update the time left to wait + //TimeSpan ts = DateTime.UtcNow - start; + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; + } + } + + if (timeout && forceAbort) + { + // Abort the threads in the pool + foreach(Thread thread in threads) + { + + if ((thread != null) +#if !(_WINDOWS_CE) + && thread.IsAlive +#endif + ) + { + try + { + thread.Abort(); // Shutdown + } + catch(SecurityException e) + { + e.GetHashCode(); + } + catch(ThreadStateException ex) + { + ex.GetHashCode(); + // In case the thread has been terminated + // after the check if it is alive. + } + } + } + } + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults) + { + return WaitAll(waitableResults, Timeout.Infinite, true); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } + + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults) + { + return WaitAny(waitableResults, Timeout.Infinite, true); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } + + /// + /// Creates a new WorkItemsGroup. + /// + /// The number of work items that can be run concurrently + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); + return workItemsGroup; + } + + /// + /// Creates a new WorkItemsGroup. + /// + /// The number of work items that can be run concurrently + /// A WorkItemsGroup configuration that overrides the default behavior + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); + return workItemsGroup; + } + + #region Fire Thread's Events + + private void FireOnThreadInitialization() + { + if (null != _onThreadInitialization) + { + foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList()) + { + try + { + tih(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; + } + } + } + } + + private void FireOnThreadTermination() + { + if (null != _onThreadTermination) + { + foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList()) + { + try + { + tth(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; + } + } + } + } + + #endregion + + /// + /// This event is fired when a thread is created. + /// Use it to initialize a thread before the work items use it. + /// + public event ThreadInitializationHandler OnThreadInitialization + { + add { _onThreadInitialization += value; } + remove { _onThreadInitialization -= value; } + } + + /// + /// This event is fired when a thread is terminating. + /// Use it for cleanup. + /// + public event ThreadTerminationHandler OnThreadTermination + { + add { _onThreadTermination += value; } + remove { _onThreadTermination -= value; } + } + + + internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + workItem.WasQueuedBy(wig) && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + + + + #endregion + + #region Properties + + /// + /// Get/Set the lower limit of threads in the pool. + /// + public int MinThreads + { + get + { + ValidateNotDisposed(); + return _stpStartInfo.MinWorkerThreads; + } + set + { + Debug.Assert(value >= 0); + Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads); + if (_stpStartInfo.MaxWorkerThreads < value) + { + _stpStartInfo.MaxWorkerThreads = value; + } + _stpStartInfo.MinWorkerThreads = value; + StartOptimalNumberOfThreads(); + } + } + + /// + /// Get/Set the upper limit of threads in the pool. + /// + public int MaxThreads + { + get + { + ValidateNotDisposed(); + return _stpStartInfo.MaxWorkerThreads; + } + + set + { + Debug.Assert(value > 0); + Debug.Assert(value >= _stpStartInfo.MinWorkerThreads); + if (_stpStartInfo.MinWorkerThreads > value) + { + _stpStartInfo.MinWorkerThreads = value; + } + _stpStartInfo.MaxWorkerThreads = value; + StartOptimalNumberOfThreads(); + } + } + /// + /// Get the number of threads in the thread pool. + /// Should be between the lower and the upper limits. + /// + public int ActiveThreads + { + get + { + ValidateNotDisposed(); + return _workerThreads.Count; + } + } + + /// + /// Get the number of busy (not idle) threads in the thread pool. + /// + public int InUseThreads + { + get + { + ValidateNotDisposed(); + return _inUseWorkerThreads; + } + } + + /// + /// Returns true if the current running work item has been cancelled. + /// Must be used within the work item's callback method. + /// The work item should sample this value in order to know if it + /// needs to quit before its completion. + /// + public static bool IsWorkItemCanceled + { + get + { + return CurrentThreadEntry.CurrentWorkItem.IsCanceled; + } + } + + /// + /// Checks if the work item has been cancelled, and if yes then abort the thread. + /// Can be used with Cancel and timeout + /// + public static void AbortOnWorkItemCancel() + { + if (IsWorkItemCanceled) + { + Thread.CurrentThread.Abort(); + } + } + + /// + /// Thread Pool start information (readonly) + /// + public STPStartInfo STPStartInfo + { + get + { + return _stpStartInfo.AsReadOnly(); + } + } + + public bool IsShuttingdown + { + get { return _shutdown; } + } + + /// + /// Return the local calculated performance counters + /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true. + /// + public ISTPPerformanceCountersReader PerformanceCountersReader + { + get { return (ISTPPerformanceCountersReader)_localPCs; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!_isDisposed) + { + if (!_shutdown) + { + Shutdown(); + } + + if (null != _shuttingDownEvent) + { + _shuttingDownEvent.Close(); + _shuttingDownEvent = null; + } + _workerThreads.Clear(); + + if (null != _isIdleWaitHandle) + { + _isIdleWaitHandle.Close(); + _isIdleWaitHandle = null; + } + + _isDisposed = true; + } + } + + private void ValidateNotDisposed() + { + if(_isDisposed) + { + throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); + } + } + #endregion + + #region WorkItemsGroupBase Overrides + + /// + /// Get/Set the maximum number of work items that execute cocurrency on the thread pool + /// + public override int Concurrency + { + get { return MaxThreads; } + set { MaxThreads = value; } + } + + /// + /// Get the number of work items in the queue. + /// + public override int WaitingCallbacks + { + get + { + ValidateNotDisposed(); + return _workItemsQueue.Count; + } + } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + public override object[] GetStates() + { + object[] states = _workItemsQueue.GetStates(); + return states; + } + + /// + /// WorkItemsGroup start information (readonly) + /// + public override WIGStartInfo WIGStartInfo + { + get { return _stpStartInfo.AsReadOnly(); } + } + + /// + /// Start the thread pool if it was started suspended. + /// If it is already running, this method is ignored. + /// + public override void Start() + { + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.OnSTPIsStarting(); + } + + StartOptimalNumberOfThreads(); + } + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + public override void Cancel(bool abortExecution) + { + _canceledSmartThreadPool.IsCanceled = true; + _canceledSmartThreadPool = new CanceledWorkItemsGroup(); + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.Cancel(abortExecution); + } + + if (abortExecution) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + threadEntry.AssociatedSmartThreadPool == this && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + ValidateWaitForIdle(); + return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); + } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + public override event WorkItemsGroupIdleHandler OnIdle + { + add + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle += value; + } + remove + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle -= value; + } + } + + internal override void PreQueueWorkItem() + { + ValidateNotDisposed(); + } + + #endregion + + #region Join, Choice, Pipe, etc. + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + foreach (Action action in actions) + { + workItemsGroup.QueueWorkItem(action); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(params Action[] actions) + { + Join((IEnumerable)actions); + } + + private class ChoiceIndex + { + public int _index = -1; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + + ManualResetEvent anActionCompleted = new ManualResetEvent(false); + + ChoiceIndex choiceIndex = new ChoiceIndex(); + + int i = 0; + foreach (Action action in actions) + { + Action act = action; + int value = i; + workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); }); + ++i; + } + workItemsGroup.Start(); + anActionCompleted.WaitOne(); + + return choiceIndex._index; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(params Action[] actions) + { + return Choice((IEnumerable)actions); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// A state context that passes + /// Actions to execute in the order they should run + public void Pipe(T pipeState, IEnumerable> actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo); + foreach (Action action in actions) + { + Action act = action; + workItemsGroup.QueueWorkItem(() => act(pipeState)); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// + /// Actions to execute in the order they should run + public void Pipe(T pipeState, params Action[] actions) + { + Pipe(pipeState, (IEnumerable>)actions); + } + #endregion + } + #endregion +} diff --git a/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs new file mode 100644 index 0000000..3532cca --- /dev/null +++ b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; + +namespace Amib.Threading.Internal +{ + internal class SynchronizedDictionary + { + private readonly Dictionary _dictionary; + private readonly object _lock; + + public SynchronizedDictionary() + { + _lock = new object(); + _dictionary = new Dictionary(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool Contains(TKey key) + { + lock (_lock) + { + return _dictionary.ContainsKey(key); + } + } + + public void Remove(TKey key) + { + lock (_lock) + { + _dictionary.Remove(key); + } + } + + public object SyncRoot + { + get { return _lock; } + } + + public TValue this[TKey key] + { + get + { + lock (_lock) + { + return _dictionary[key]; + } + } + set + { + lock (_lock) + { + _dictionary[key] = value; + } + } + } + + public Dictionary.KeyCollection Keys + { + get + { + lock (_lock) + { + return _dictionary.Keys; + } + } + } + + public Dictionary.ValueCollection Values + { + get + { + lock (_lock) + { + return _dictionary.Values; + } + } + } + public void Clear() + { + lock (_lock) + { + _dictionary.Clear(); + } + } + } +} diff --git a/ThirdParty/SmartThreadPool/WIGStartInfo.cs b/ThirdParty/SmartThreadPool/WIGStartInfo.cs index 150317f..e5ff150 100644 --- a/ThirdParty/SmartThreadPool/WIGStartInfo.cs +++ b/ThirdParty/SmartThreadPool/WIGStartInfo.cs @@ -1,99 +1,171 @@ -// Ami Bar -// amibar@gmail.com - -namespace Amib.Threading -{ - /// - /// Summary description for WIGStartInfo. - /// - public class WIGStartInfo - { - /// - /// Use the caller's security context - /// - private bool _useCallerCallContext; - - /// - /// Use the caller's HTTP context - /// - private bool _useCallerHttpContext; - - /// - /// Dispose of the state object of a work item - /// - private bool _disposeOfStateObjects; - - /// - /// The option to run the post execute - /// - private CallToPostExecute _callToPostExecute; - - /// - /// A post execute callback to call when none is provided in - /// the QueueWorkItem method. - /// - private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; - - /// - /// Indicate the WorkItemsGroup to suspend the handling of the work items - /// until the Start() method is called. - /// - private bool _startSuspended; - - 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 description for WIGStartInfo. + /// + 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"); + } + } + + /// + /// Get/Set if to use the caller's security context + /// + public virtual bool UseCallerCallContext + { + get { return _useCallerCallContext; } + set + { + ThrowIfReadOnly(); + _useCallerCallContext = value; + } + } + + + /// + /// Get/Set if to use the caller's HTTP context + /// + public virtual bool UseCallerHttpContext + { + get { return _useCallerHttpContext; } + set + { + ThrowIfReadOnly(); + _useCallerHttpContext = value; + } + } + + + /// + /// Get/Set if to dispose of the state object of a work item + /// + public virtual bool DisposeOfStateObjects + { + get { return _disposeOfStateObjects; } + set + { + ThrowIfReadOnly(); + _disposeOfStateObjects = value; + } + } + + + /// + /// Get/Set the run the post execute options + /// + public virtual CallToPostExecute CallToPostExecute + { + get { return _callToPostExecute; } + set + { + ThrowIfReadOnly(); + _callToPostExecute = value; + } + } + + + /// + /// Get/Set the default post execute callback + /// + public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback + { + get { return _postExecuteWorkItemCallback; } + set + { + ThrowIfReadOnly(); + _postExecuteWorkItemCallback = value; + } + } + + + /// + /// Get/Set if the work items execution should be suspended until the Start() + /// method is called. + /// + public virtual bool StartSuspended + { + get { return _startSuspended; } + set + { + ThrowIfReadOnly(); + _startSuspended = value; + } + } + + + /// + /// Get/Set the default priority that a work item gets when it is enqueued + /// + public virtual WorkItemPriority WorkItemPriority + { + get { return _workItemPriority; } + set { _workItemPriority = value; } + } + + /// + /// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the + /// arguments as an object array into the state of the work item. + /// The arguments can be access later by IWorkItemResult.State. + /// + public virtual bool FillStateWithArgs + { + get { return _fillStateWithArgs; } + set + { + ThrowIfReadOnly(); + _fillStateWithArgs = value; + } + } + + /// + /// Get a readonly version of this WIGStartInfo + /// + /// Returns a readonly reference to this WIGStartInfoRO + public WIGStartInfo AsReadOnly() + { + return new WIGStartInfo(this) { _readOnly = true }; + } + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs new file mode 100644 index 0000000..5745c15 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public partial class WorkItem + { + #region WorkItemResult class + + private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult + { + /// + /// A back reference to the work item + /// + private readonly WorkItem _workItem; + + public WorkItemResult(WorkItem workItem) + { + _workItem = workItem; + } + + internal WorkItem GetWorkItem() + { + return _workItem; + } + + #region IWorkItemResult Members + + public bool IsCompleted + { + get + { + return _workItem.IsCompleted; + } + } + + public bool IsCanceled + { + get + { + return _workItem.IsCanceled; + } + } + + public object GetResult() + { + return _workItem.GetResult(Timeout.Infinite, true, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null); + } + + public object GetResult(TimeSpan timeout, bool exitContext) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + public object GetResult(out Exception e) + { + return _workItem.GetResult(Timeout.Infinite, true, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); + } + + public bool Cancel() + { + return Cancel(false); + } + + public bool Cancel(bool abortExecution) + { + return _workItem.Cancel(abortExecution); + } + + public object State + { + get + { + return _workItem._state; + } + } + + public WorkItemPriority WorkItemPriority + { + get + { + return _workItem._workItemInfo.WorkItemPriority; + } + } + + /// + /// Return the result, same as GetResult() + /// + public object Result + { + get { return GetResult(); } + } + + /// + /// Returns the exception if occured otherwise returns null. + /// This value is valid only after the work item completed, + /// before that it is always null. + /// + public object Exception + { + get { return _workItem._exception; } + } + + #endregion + + #region IInternalWorkItemResult Members + + public event WorkItemStateCallback OnWorkItemStarted + { + add + { + _workItem.OnWorkItemStarted += value; + } + remove + { + _workItem.OnWorkItemStarted -= value; + } + } + + + public event WorkItemStateCallback OnWorkItemCompleted + { + add + { + _workItem.OnWorkItemCompleted += value; + } + remove + { + _workItem.OnWorkItemCompleted -= value; + } + } + + #endregion + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return this; + } + + public IWorkItemResult GetWorkItemResultT() + { + return new WorkItemResultTWrapper(this); + } + + #endregion + } + + #endregion + + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItem.cs b/ThirdParty/SmartThreadPool/WorkItem.cs index d0c0524..f229d1f 100644 --- a/ThirdParty/SmartThreadPool/WorkItem.cs +++ b/ThirdParty/SmartThreadPool/WorkItem.cs @@ -1,58 +1,13 @@ -// Ami Bar -// amibar@gmail.com - using System; using System.Threading; using System.Diagnostics; namespace Amib.Threading.Internal { - #region WorkItem Delegate - - /// - /// An internal delegate to call when the WorkItem starts or completes - /// - internal delegate void WorkItemStateCallback(WorkItem workItem); - - #endregion - - #region IInternalWorkItemResult interface - - public class CanceledWorkItemsGroup - { - public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup(); - - private bool _isCanceled = false; - public bool IsCanceled - { - get { return _isCanceled; } - set { _isCanceled = value; } - } - } - - internal interface IInternalWorkItemResult - { - event WorkItemStateCallback OnWorkItemStarted; - event WorkItemStateCallback OnWorkItemCompleted; - } - - #endregion - - #region IWorkItem interface - - public interface IWorkItem - { - - } - - #endregion - - #region WorkItem class - /// /// Holds a callback delegate and the state for that delegate. /// - public class WorkItem : IHasWorkItemPriority, IWorkItem + public partial class WorkItem : IHasWorkItemPriority { #region WorkItemState enum @@ -61,33 +16,57 @@ namespace Amib.Threading.Internal /// private enum WorkItemState { - InQueue, - InProgress, - Completed, - Canceled, + InQueue = 0, // Nexts: InProgress, Canceled + InProgress = 1, // Nexts: Completed, Canceled + Completed = 2, // Stays Completed + Canceled = 3, // Stays Canceled } - #endregion + private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState) + { + bool valid = false; + + switch (currentState) + { + case WorkItemState.InQueue: + valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState); + break; + case WorkItemState.InProgress: + valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState); + break; + case WorkItemState.Completed: + case WorkItemState.Canceled: + // Cannot be changed + break; + default: + // Unknown state + Debug.Assert(false); + break; + } - #region Member Variables + return valid; + } - public Thread currentThread; + #endregion + + #region Fields /// /// Callback delegate for the callback. /// - private WorkItemCallback _callback; + private readonly WorkItemCallback _callback; /// /// State with which to call the callback delegate. /// private object _state; +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) /// /// Stores the caller's context /// - private CallerThreadContext _callerContext; - + private readonly CallerThreadContext _callerContext; +#endif /// /// Holds the result of the mehtod /// @@ -117,12 +96,12 @@ namespace Amib.Threading.Internal /// /// Represents the result state of the work item /// - private WorkItemResult _workItemResult; + private readonly WorkItemResult _workItemResult; /// /// Work item info /// - private WorkItemInfo _workItemInfo; + private readonly WorkItemInfo _workItemInfo; /// /// Called when the WorkItem starts @@ -141,30 +120,41 @@ namespace Amib.Threading.Internal private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; /// + /// A reference to an object that indicates whatever the + /// SmartThreadPool has been canceled + /// + private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; + + /// /// The work item group this work item belong to. - /// /// - private IWorkItemsGroup _workItemsGroup; + private readonly IWorkItemsGroup _workItemsGroup; - #region Performance Counter fields + /// + /// The thread that executes this workitem. + /// This field is available for the period when the work item is executed, before and after it is null. + /// + private Thread _executingThread; /// - /// The time when the work items is queued. - /// Used with the performance counter. + /// The absulote time when the work item will be timeout /// - private DateTime _queuedTime; + private long _expirationTime; + + #region Performance Counter fields + + + /// - /// The time when the work items starts its execution. - /// Used with the performance counter. + /// Stores how long the work item waited on the stp queue /// - private DateTime _beginProcessTime; + private Stopwatch _waitingOnQueueStopwatch; /// - /// The time when the work items ends its execution. - /// Used with the performance counter. + /// Stores how much time it took the work item to execute after it went out of the queue /// - private DateTime _endProcessTime; + private Stopwatch _processingStopwatch; #endregion @@ -174,17 +164,25 @@ namespace Amib.Threading.Internal public TimeSpan WaitingTime { - get + get { - return (_beginProcessTime - _queuedTime); + return _waitingOnQueueStopwatch.Elapsed; } } public TimeSpan ProcessTime { - get + get + { + return _processingStopwatch.Elapsed; + } + } + + internal WorkItemInfo WorkItemInfo + { + get { - return (_endProcessTime - _beginProcessTime); + return _workItemInfo; } } @@ -195,6 +193,8 @@ namespace Amib.Threading.Internal /// /// Initialize the callback holding object. /// + /// The workItemGroup of the workitem + /// The WorkItemInfo of te workitem /// Callback delegate for the callback. /// State with which to call the callback delegate. /// @@ -203,16 +203,18 @@ namespace Amib.Threading.Internal public WorkItem( IWorkItemsGroup workItemsGroup, WorkItemInfo workItemInfo, - WorkItemCallback callback, + WorkItemCallback callback, object state) { _workItemsGroup = workItemsGroup; _workItemInfo = workItemInfo; +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) { _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); } +#endif _callback = callback; _state = state; @@ -222,9 +224,18 @@ namespace Amib.Threading.Internal internal void Initialize() { + // The _workItemState is changed directly instead of using the SetWorkItemState + // method since we don't want to go throught IsValidStateTransition. _workItemState = WorkItemState.InQueue; + _workItemCompleted = null; _workItemCompletedRefCount = 0; + _waitingOnQueueStopwatch = new Stopwatch(); + _processingStopwatch = new Stopwatch(); + _expirationTime = + _workItemInfo.Timeout > 0 ? + DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond : + long.MaxValue; } internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) @@ -237,17 +248,16 @@ namespace Amib.Threading.Internal #region Methods - public CanceledWorkItemsGroup CanceledWorkItemsGroup + internal CanceledWorkItemsGroup CanceledWorkItemsGroup { - get - { - return _canceledWorkItemsGroup; - } + get { return _canceledWorkItemsGroup; } + set { _canceledWorkItemsGroup = value; } + } - set - { - _canceledWorkItemsGroup = value; - } + internal CanceledWorkItemsGroup CanceledSmartThreadPool + { + get { return _canceledSmartThreadPool; } + set { _canceledSmartThreadPool = value; } } /// @@ -259,9 +269,10 @@ namespace Amib.Threading.Internal /// public bool StartingWorkItem() { - _beginProcessTime = DateTime.Now; + _waitingOnQueueStopwatch.Stop(); + _processingStopwatch.Start(); - lock(this) + lock (this) { if (IsCanceled) { @@ -277,6 +288,9 @@ namespace Amib.Threading.Internal Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); + // No need for a lock yet, only after the state has changed to InProgress + _executingThread = Thread.CurrentThread; + SetWorkItemState(WorkItemState.InProgress); } @@ -291,7 +305,7 @@ namespace Amib.Threading.Internal CallToPostExecute currentCallToPostExecute = 0; // Execute the work item if we are in the correct state - switch(GetWorkItemState()) + switch (GetWorkItemState()) { case WorkItemState.InProgress: currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled; @@ -311,7 +325,7 @@ namespace Amib.Threading.Internal PostExecute(); } - _endProcessTime = DateTime.Now; + _processingStopwatch.Stop(); } internal void FireWorkItemCompleted() @@ -323,8 +337,21 @@ namespace Amib.Threading.Internal _workItemCompletedEvent(this); } } - catch // Ignore exceptions - {} + catch // Suppress exceptions + { } + } + + internal void FireWorkItemStarted() + { + try + { + if (null != _workItemStartedEvent) + { + _workItemStartedEvent(this); + } + } + catch // Suppress exceptions + { } } /// @@ -332,32 +359,70 @@ namespace Amib.Threading.Internal /// private void ExecuteWorkItem() { + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) CallerThreadContext ctc = null; if (null != _callerContext) { ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); CallerThreadContext.Apply(_callerContext); } +#endif Exception exception = null; object result = null; try { - result = _callback(_state); + try + { + result = _callback(_state); + } + catch (Exception e) + { + // Save the exception so we can rethrow it later + exception = e; + } + + // Remove the value of the execution thread, so it will be impossible to cancel the work item, + // since it is already completed. + // Cancelling a work item that already completed may cause the abortion of the next work item!!! + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + + if (null == executionThread) + { + // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException + Thread.Sleep(60 * 1000); + + // If after 1 minute this thread was not aborted then let it continue working. + } } - catch (Exception e) + // We must treat the ThreadAbortException or else it will be stored in the exception variable + catch (ThreadAbortException tae) { - // Save the exception so we can rethrow it later - exception = e; + tae.GetHashCode(); + // Check if the work item was cancelled + // If we got a ThreadAbortException and the STP is not shutting down, it means the + // work items was cancelled. + if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown) + { +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + Thread.ResetAbort(); +#endif + } } - + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) if (null != _callerContext) { CallerThreadContext.Apply(ctc); } +#endif - SetResult(result, exception); + if (!SmartThreadPool.IsWorkItemCanceled) + { + SetResult(result, exception); + } } /// @@ -369,9 +434,9 @@ namespace Amib.Threading.Internal { try { - _workItemInfo.PostExecuteWorkItemCallback(this._workItemResult); + _workItemInfo.PostExecuteWorkItemCallback(_workItemResult); } - catch (Exception e) + catch (Exception e) { Debug.Assert(null != e); } @@ -382,6 +447,8 @@ namespace Amib.Threading.Internal /// Set the result of the work item to return /// /// The result of the work item + /// The exception that was throw while the workitem executed, null + /// if there was no exception. internal void SetResult(object result, Exception exception) { _result = result; @@ -401,48 +468,48 @@ namespace Amib.Threading.Internal /// /// Wait for all work items to complete /// - /// Array of work item result objects + /// Array of work item result objects /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. /// /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. /// /// A cancel wait handle to interrupt the wait if needed /// - /// true when every work item in workItemResults has completed; otherwise false. + /// true when every work item in waitableResults has completed; otherwise false. /// internal static bool WaitAll( - IWorkItemResult [] workItemResults, + IWaitableResult[] waitableResults, int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) { - if (0 == workItemResults.Length) + if (0 == waitableResults.Length) { return true; } bool success; - WaitHandle [] waitHandles = new WaitHandle[workItemResults.Length];; - GetWaitHandles(workItemResults, waitHandles); + WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) { - success = WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); } else { success = true; int millisecondsLeft = millisecondsTimeout; - DateTime start = DateTime.Now; + Stopwatch stopwatch = Stopwatch.StartNew(); - WaitHandle [] whs; + WaitHandle[] whs; if (null != cancelWaitHandle) { - whs = new WaitHandle [] { null, cancelWaitHandle }; + whs = new WaitHandle[] { null, cancelWaitHandle }; } else { - whs = new WaitHandle [] { null }; + whs = new WaitHandle[] { null }; } bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); @@ -450,7 +517,7 @@ namespace Amib.Threading.Internal // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle // won't affect it. // Each iteration we update the time left for the timeout. - for(int i = 0; i < workItemResults.Length; ++i) + for (int i = 0; i < waitableResults.Length; ++i) { // WaitAny don't work with negative numbers if (!waitInfinitely && (millisecondsLeft < 0)) @@ -460,23 +527,22 @@ namespace Amib.Threading.Internal } whs[0] = waitHandles[i]; - int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext); - if((result > 0) || (WaitHandle.WaitTimeout == result)) + int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); + if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) { success = false; break; } - if(!waitInfinitely) + if (!waitInfinitely) { // Update the time left to wait - TimeSpan ts = DateTime.Now - start; - millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds; + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; } } } // Release the wait handles - ReleaseWaitHandles(workItemResults); + ReleaseWaitHandles(waitableResults); return success; } @@ -484,7 +550,7 @@ namespace Amib.Threading.Internal /// /// Waits for any of the work items in the specified array to complete, cancel, or timeout /// - /// Array of work item result objects + /// Array of work item result objects /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. /// /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. @@ -493,38 +559,38 @@ namespace Amib.Threading.Internal /// /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. /// - internal static int WaitAny( - IWorkItemResult [] workItemResults, + internal static int WaitAny( + IWaitableResult[] waitableResults, int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) { - WaitHandle [] waitHandles = null; + WaitHandle[] waitHandles; if (null != cancelWaitHandle) { - waitHandles = new WaitHandle[workItemResults.Length+1]; - GetWaitHandles(workItemResults, waitHandles); - waitHandles[workItemResults.Length] = cancelWaitHandle; + waitHandles = new WaitHandle[waitableResults.Length + 1]; + GetWaitHandles(waitableResults, waitHandles); + waitHandles[waitableResults.Length] = cancelWaitHandle; } else { - waitHandles = new WaitHandle[workItemResults.Length]; - GetWaitHandles(workItemResults, waitHandles); + waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); } - int result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); // Treat cancel as timeout if (null != cancelWaitHandle) { - if (result == workItemResults.Length) + if (result == waitableResults.Length) { - result = WaitHandle.WaitTimeout; + result = STPEventWaitHandle.WaitTimeout; } } - ReleaseWaitHandles(workItemResults); + ReleaseWaitHandles(waitableResults); return result; } @@ -532,16 +598,16 @@ namespace Amib.Threading.Internal /// /// Fill an array of wait handles with the work items wait handles. /// - /// An array of work item results + /// An array of work item results /// An array of wait handles to fill private static void GetWaitHandles( - IWorkItemResult [] workItemResults, - WaitHandle [] waitHandles) + IWaitableResult[] waitableResults, + WaitHandle[] waitHandles) { - for(int i = 0; i < workItemResults.Length; ++i) + for (int i = 0; i < waitableResults.Length; ++i) { - WorkItemResult wir = workItemResults[i] as WorkItemResult; - Debug.Assert(null != wir, "All workItemResults must be WorkItemResult objects"); + WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult; + Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects"); waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); } @@ -550,40 +616,64 @@ namespace Amib.Threading.Internal /// /// Release the work items' wait handles /// - /// An array of work item results - private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults) + /// An array of work item results + private static void ReleaseWaitHandles(IWaitableResult[] waitableResults) { - for(int i = 0; i < workItemResults.Length; ++i) + for (int i = 0; i < waitableResults.Length; ++i) { - WorkItemResult wir = workItemResults[i] as WorkItemResult; + WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult(); wir.GetWorkItem().ReleaseWaitHandle(); } } - #endregion - + #region Private Members private WorkItemState GetWorkItemState() { - if (_canceledWorkItemsGroup.IsCanceled) + lock (this) { - return WorkItemState.Canceled; - } - return _workItemState; + if (WorkItemState.Completed == _workItemState) + { + return _workItemState; + } + + long nowTicks = DateTime.UtcNow.Ticks; + if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime) + { + _workItemState = WorkItemState.Canceled; + } + + if (WorkItemState.InProgress == _workItemState) + { + return _workItemState; + } + + if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled) + { + return WorkItemState.Canceled; + } + + return _workItemState; + } } + + /// /// Sets the work item's state /// /// The state to set the work item to private void SetWorkItemState(WorkItemState workItemState) { - lock(this) + lock (this) { - _workItemState = workItemState; + if (IsValidStatesTransition(_workItemState, workItemState)) + { + _workItemState = workItemState; + } } } @@ -594,7 +684,7 @@ namespace Amib.Threading.Internal private void SignalComplete(bool canceled) { SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed); - lock(this) + lock (this) { // If someone is waiting then signal. if (null != _workItemCompleted) @@ -606,40 +696,83 @@ namespace Amib.Threading.Internal internal void WorkItemIsQueued() { - _queuedTime = DateTime.Now; + _waitingOnQueueStopwatch.Start(); } #endregion - + #region Members exposed by WorkItemResult /// /// Cancel the work item if it didn't start running yet. /// /// Returns true on success or false if the work item is in progress or already completed - private bool Cancel() + private bool Cancel(bool abortExecution) { - lock(this) +#if (_WINDOWS_CE) + if(abortExecution) + { + throw new ArgumentOutOfRangeException("abortExecution", "WindowsCE doesn't support this feature"); + } +#endif + bool success = false; + bool signalComplete = false; + + lock (this) { - switch(GetWorkItemState()) + switch (GetWorkItemState()) { case WorkItemState.Canceled: //Debug.WriteLine("Work item already canceled"); - return true; + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + // No need to signalComplete, because we already cancelled this work item + // so it already signaled its completion. + //signalComplete = true; + } + } + success = true; + break; case WorkItemState.Completed: - case WorkItemState.InProgress: //Debug.WriteLine("Work item cannot be canceled"); - return false; + break; + case WorkItemState.InProgress: + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + success = true; + signalComplete = true; + } + } + else + { + success = false; + signalComplete = false; + } + break; case WorkItemState.InQueue: // Signal to the wait for completion that the work // item has been completed (canceled). There is no // reason to wait for it to get out of the queue - SignalComplete(true); + signalComplete = true; //Debug.WriteLine("Work item canceled"); - return true; + success = true; + break; + } + + if (signalComplete) + { + SignalComplete(true); } } - return false; + return success; } /// @@ -653,7 +786,7 @@ namespace Amib.Threading.Internal bool exitContext, WaitHandle cancelWaitHandle) { - Exception e = null; + Exception e; object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); if (null != e) { @@ -694,7 +827,7 @@ namespace Amib.Threading.Internal { WaitHandle wh = GetWaitHandle(); - bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext); + bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); ReleaseWaitHandle(); @@ -706,10 +839,10 @@ namespace Amib.Threading.Internal else { WaitHandle wh = GetWaitHandle(); - int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); + int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); ReleaseWaitHandle(); - switch(result) + switch (result) { case 0: // The work item signaled @@ -717,7 +850,7 @@ namespace Amib.Threading.Internal // work item (not the get result) break; case 1: - case WaitHandle.WaitTimeout: + case STPEventWaitHandle.WaitTimeout: throw new WorkItemTimeoutException("Work item timeout"); default: Debug.Assert(false); @@ -745,11 +878,11 @@ namespace Amib.Threading.Internal /// private WaitHandle GetWaitHandle() { - lock(this) + lock (this) { if (null == _workItemCompleted) { - _workItemCompleted = new ManualResetEvent(IsCompleted); + _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted); } ++_workItemCompletedRefCount; } @@ -758,7 +891,7 @@ namespace Amib.Threading.Internal private void ReleaseWaitHandle() { - lock(this) + lock (this) { if (null != _workItemCompleted) { @@ -779,10 +912,10 @@ namespace Amib.Threading.Internal { get { - lock(this) + lock (this) { WorkItemState workItemState = GetWorkItemState(); - return ((workItemState == WorkItemState.Completed) || + return ((workItemState == WorkItemState.Completed) || (workItemState == WorkItemState.Canceled)); } } @@ -795,7 +928,7 @@ namespace Amib.Threading.Internal { get { - lock(this) + lock (this) { return (GetWorkItemState() == WorkItemState.Canceled); } @@ -843,172 +976,6 @@ namespace Amib.Threading.Internal } } - - #region WorkItemResult class - - private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult - { - /// - /// A back reference to the work item - /// - private WorkItem _workItem; - - public WorkItemResult(WorkItem workItem) - { - _workItem = workItem; - } - - internal WorkItem GetWorkItem() - { - return _workItem; - } - - #region IWorkItemResult Members - - public bool IsCompleted - { - get - { - return _workItem.IsCompleted; - } - } - - public void Abort() - { - _workItem.Abort(); - } - - public bool IsCanceled - { - get - { - return _workItem.IsCanceled; - } - } - - public object GetResult() - { - return _workItem.GetResult(Timeout.Infinite, true, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null); - } - - public object GetResult(TimeSpan timeout, bool exitContext) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - public object GetResult(out Exception e) - { - return _workItem.GetResult(Timeout.Infinite, true, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); - } - - public bool Cancel() - { - return _workItem.Cancel(); - } - - public object State - { - get - { - return _workItem._state; - } - } - - public WorkItemPriority WorkItemPriority - { - get - { - return _workItem._workItemInfo.WorkItemPriority; - } - } - - /// - /// Return the result, same as GetResult() - /// - public object Result - { - get { return GetResult(); } - } - - /// - /// Returns the exception if occured otherwise returns null. - /// This value is valid only after the work item completed, - /// before that it is always null. - /// - public object Exception - { - get { return _workItem._exception; } - } - - #endregion - - #region IInternalWorkItemResult Members - - public event WorkItemStateCallback OnWorkItemStarted - { - add - { - _workItem.OnWorkItemStarted += value; - } - remove - { - _workItem.OnWorkItemStarted -= value; - } - } - - - public event WorkItemStateCallback OnWorkItemCompleted - { - add - { - _workItem.OnWorkItemCompleted += value; - } - remove - { - _workItem.OnWorkItemCompleted -= value; - } - } - - #endregion - } - - #endregion - public void DisposeOfState() { if (_workItemInfo.DisposeOfStateObjects) @@ -1021,15 +988,5 @@ namespace Amib.Threading.Internal } } } - - public void Abort() - { - lock (this) - { - if(currentThread != null) - currentThread.Abort(); - } - } } - #endregion } diff --git a/ThirdParty/SmartThreadPool/WorkItemFactory.cs b/ThirdParty/SmartThreadPool/WorkItemFactory.cs index dfcb54f..2d6601e 100644 --- a/ThirdParty/SmartThreadPool/WorkItemFactory.cs +++ b/ThirdParty/SmartThreadPool/WorkItemFactory.cs @@ -1,333 +1,343 @@ -// Ami Bar -// amibar@gmail.com - -using System; - -namespace Amib.Threading.Internal -{ - #region WorkItemFactory class - - public class WorkItemFactory - { - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback) - { - return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// The priority of the work item - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - WorkItemPriority workItemPriority) - { - return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// Work item info - /// A callback to execute - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemInfo workItemInfo, - WorkItemCallback callback) - { - return CreateWorkItem( - workItemsGroup, - wigStartInfo, - workItemInfo, - callback, - null); - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state) - { - ValidateCallback(callback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - WorkItemPriority workItemPriority) - { - ValidateCallback(callback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = workItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemInfo workItemInfo, - WorkItemCallback callback, - object state) - { - ValidateCallback(callback); - ValidateCallback(workItemInfo.PostExecuteWorkItemCallback); - - WorkItem workItem = new WorkItem( - workItemsGroup, - new WorkItemInfo(workItemInfo), - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = workItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = callToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - 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 + { + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// The priority of the work item + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + WorkItemPriority workItemPriority) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// Work item info + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemInfo workItemInfo, + WorkItemCallback callback) + { + return CreateWorkItem( + workItemsGroup, + wigStartInfo, + workItemInfo, + callback, + null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemInfo workItemInfo, + WorkItemCallback callback, + object state) + { + ValidateCallback(callback); + ValidateCallback(workItemInfo.PostExecuteWorkItemCallback); + + WorkItem workItem = new WorkItem( + workItemsGroup, + new WorkItemInfo(workItemInfo), + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = callToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + 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 +} diff --git a/ThirdParty/SmartThreadPool/WorkItemInfo.cs b/ThirdParty/SmartThreadPool/WorkItemInfo.cs index c259339..5fbceb8 100644 --- a/ThirdParty/SmartThreadPool/WorkItemInfo.cs +++ b/ThirdParty/SmartThreadPool/WorkItemInfo.cs @@ -1,102 +1,69 @@ -// Ami Bar -// amibar@gmail.com - -namespace Amib.Threading -{ - #region WorkItemInfo class - - /// - /// Summary description for WorkItemInfo. - /// - public class WorkItemInfo - { - /// - /// Use the caller's security context - /// - private bool _useCallerCallContext; - - /// - /// Use the caller's security context - /// - private bool _useCallerHttpContext; - - /// - /// Dispose of the state object of a work item - /// - private bool _disposeOfStateObjects; - - /// - /// The option to run the post execute - /// - private CallToPostExecute _callToPostExecute; - - /// - /// A post execute callback to call when none is provided in - /// the QueueWorkItem method. - /// - private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; - - /// - /// The priority of the work item - /// - 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 description for WorkItemInfo. + /// + public class WorkItemInfo + { + public WorkItemInfo() + { + UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; + UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; + DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; + CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute; + PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; + WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority; + } + + 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; + } + + /// + /// Get/Set if to use the caller's security context + /// + public bool UseCallerCallContext { get; set; } + + /// + /// Get/Set if to use the caller's HTTP context + /// + public bool UseCallerHttpContext { get; set; } + + /// + /// Get/Set if to dispose of the state object of a work item + /// + public bool DisposeOfStateObjects { get; set; } + + /// + /// Get/Set the run the post execute options + /// + public CallToPostExecute CallToPostExecute { get; set; } + + /// + /// Get/Set the post execute callback + /// + public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; } + + /// + /// Get/Set the work item's priority + /// + public WorkItemPriority WorkItemPriority { get; set; } + + /// + /// Get/Set the work item's timout in milliseconds. + /// This is a passive timout. When the timout expires the work item won't be actively aborted! + /// + public long Timeout { get; set; } + } + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs new file mode 100644 index 0000000..a0bf8b8 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs @@ -0,0 +1,128 @@ +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + #region WorkItemResultTWrapper class + + internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult + { + private readonly IWorkItemResult _workItemResult; + + public WorkItemResultTWrapper(IWorkItemResult workItemResult) + { + _workItemResult = workItemResult; + } + + #region IWorkItemResult Members + + public TResult GetResult() + { + return (TResult)_workItemResult.GetResult(); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(out Exception e) + { + return (TResult)_workItemResult.GetResult(out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); + } + + public bool IsCompleted + { + get { return _workItemResult.IsCompleted; } + } + + public bool IsCanceled + { + get { return _workItemResult.IsCanceled; } + } + + public object State + { + get { return _workItemResult.State; } + } + + public bool Cancel() + { + return _workItemResult.Cancel(); + } + + public bool Cancel(bool abortExecution) + { + return _workItemResult.Cancel(abortExecution); + } + + public WorkItemPriority WorkItemPriority + { + get { return _workItemResult.WorkItemPriority; } + } + + public TResult Result + { + get { return (TResult)_workItemResult.Result; } + } + + public object Exception + { + get { return (TResult)_workItemResult.Exception; } + } + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return _workItemResult.GetWorkItemResult(); + } + + public IWorkItemResult GetWorkItemResultT() + { + return (IWorkItemResult)this; + } + + #endregion + + #endregion + } + + #endregion + +} diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs index 01ac8dd..67dcbdd 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs @@ -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 description for WorkItemsGroup. - /// - public class WorkItemsGroup : IWorkItemsGroup - { - #region Private members - - private object _lock = new object(); - /// - /// Contains the name of this instance of SmartThreadPool. - /// Can be changed by the user. - /// - private string _name = "WorkItemsGroup"; - - /// - /// A reference to the SmartThreadPool instance that created this - /// WorkItemsGroup. - /// - private SmartThreadPool _stp; - - /// - /// The OnIdle event - /// - private event WorkItemsGroupIdleHandler _onIdle; - - /// - /// Defines how many work items of this WorkItemsGroup can run at once. - /// - private int _concurrency; - - /// - /// Priority queue to hold work items before they are passed - /// to the SmartThreadPool. - /// - private PriorityQueue _workItemsQueue; - - /// - /// Indicate how many work items are waiting in the SmartThreadPool - /// queue. - /// This value is used to apply the concurrency. - /// - private int _workItemsInStpQueue; - - /// - /// Indicate how many work items are currently running in the SmartThreadPool. - /// This value is used with the Cancel, to calculate if we can send new - /// work items to the STP. - /// - private int _workItemsExecutingInStp = 0; - - /// - /// WorkItemsGroup start information - /// - private WIGStartInfo _workItemsGroupStartInfo; - - /// - /// Signaled when all of the WorkItemsGroup's work item completed. - /// - private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); - - /// - /// A common object for all the work items that this work items group - /// generate so we can mark them to cancel in O(1) - /// - private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - - #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 - - /// - /// Get/Set the name of the SmartThreadPool instance - /// - public string Name - { - get - { - return _name; - } - - set - { - _name = value; - } - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); - EnqueueToSTPNextWorkItem(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Wait for the thread pool to be idle - /// - public void WaitForIdle() - { - WaitForIdle(Timeout.Infinite); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(TimeSpan timeout) - { - return WaitForIdle((int)timeout.TotalMilliseconds); - } - - /// - /// Wait for the thread pool to be idle - /// - public bool WaitForIdle(int millisecondsTimeout) - { - _stp.ValidateWorkItemsGroupWaitForIdle(this); - return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); - } - - public int WaitingCallbacks - { - get - { - return _workItemsQueue.Count; - } - } - - public event WorkItemsGroupIdleHandler OnIdle - { - add - { - _onIdle += value; - } - remove - { - _onIdle -= value; - } - } - - public void Cancel() - { - lock(_lock) - { - _canceledWorkItemsGroup.IsCanceled = true; - _workItemsQueue.Clear(); - _workItemsInStpQueue = 0; - _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - } - } - - public void Start() - { - lock (this) - { - if (!_workItemsGroupStartInfo.StartSuspended) - { - 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 description for WorkItemsGroup. + /// + public class WorkItemsGroup : WorkItemsGroupBase + { + #region Private members + + private readonly object _lock = new object(); + + /// + /// A reference to the SmartThreadPool instance that created this + /// WorkItemsGroup. + /// + private readonly SmartThreadPool _stp; + + /// + /// The OnIdle event + /// + private event WorkItemsGroupIdleHandler _onIdle; + + /// + /// A flag to indicate if the Work Items Group is now suspended. + /// + private bool _isSuspended; + + /// + /// Defines how many work items of this WorkItemsGroup can run at once. + /// + private int _concurrency; + + /// + /// Priority queue to hold work items before they are passed + /// to the SmartThreadPool. + /// + private readonly PriorityQueue _workItemsQueue; + + /// + /// Indicate how many work items are waiting in the SmartThreadPool + /// queue. + /// This value is used to apply the concurrency. + /// + private int _workItemsInStpQueue; + + /// + /// Indicate how many work items are currently running in the SmartThreadPool. + /// This value is used with the Cancel, to calculate if we can send new + /// work items to the STP. + /// + private int _workItemsExecutingInStp = 0; + + /// + /// WorkItemsGroup start information + /// + private readonly WIGStartInfo _workItemsGroupStartInfo; + + /// + /// Signaled when all of the WorkItemsGroup's work item completed. + /// + //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + + /// + /// A common object for all the work items that this work items group + /// generate so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + #endregion + + #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; + } + } + + /// + /// WorkItemsGroup start information + /// + public override WIGStartInfo WIGStartInfo + { + get { return _workItemsGroupStartInfo; } + } + + /// + /// Start the Work Items Group if it was started suspended + /// + public override void Start() + { + // If the Work Items Group already started then quit + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); + } + + public override void Cancel(bool abortExecution) + { + lock (_lock) + { + _canceledWorkItemsGroup.IsCanceled = true; + _workItemsQueue.Clear(); + _workItemsInStpQueue = 0; + _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + } + + if (abortExecution) + { + _stp.CancelAbortWorkItemsGroup(this); + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + 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 +} diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs new file mode 100644 index 0000000..429de12 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs @@ -0,0 +1,471 @@ +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public abstract class WorkItemsGroupBase : IWorkItemsGroup + { + #region Private Fields + + /// + /// Contains the name of this instance of SmartThreadPool. + /// Can be changed by the user. + /// + private string _name = "WorkItemsGroupBase"; + + public WorkItemsGroupBase() + { + IsIdle = true; + } + + #endregion + + #region IWorkItemsGroup Members + + #region Public Methods + + /// + /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + #endregion + + #region Abstract Methods + + public abstract int Concurrency { get; set; } + public abstract int WaitingCallbacks { get; } + public abstract object[] GetStates(); + public abstract WIGStartInfo WIGStartInfo { get; } + public abstract void Start(); + public abstract void Cancel(bool abortExecution); + public abstract bool WaitForIdle(int millisecondsTimeout); + public abstract event WorkItemsGroupIdleHandler OnIdle; + + internal abstract void Enqueue(WorkItem workItem); + internal virtual void PreQueueWorkItem() { } + + #endregion + + #region Common Base Methods + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + public virtual void Cancel() + { + Cancel(false); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public void WaitForIdle() + { + WaitForIdle(Timeout.Infinite); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public bool WaitForIdle(TimeSpan timeout) + { + return WaitForIdle((int)timeout.TotalMilliseconds); + } + + /// + /// IsIdle is true when there are no work items running or queued. + /// + public bool IsIdle { get; protected set; } + + #endregion + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + #endregion + + #region QueueWorkItem(Action<...>) + + public IWorkItemResult QueueWorkItem(Action action) + { + return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + delegate + { + action.Invoke (); + return null; + }, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T arg) + { + return QueueWorkItem (action, arg, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2) + { + return QueueWorkItem (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3) + { + return QueueWorkItem (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority); + ; + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + return QueueWorkItem (action, arg1, arg2, arg3, arg4, + SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem ( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3, arg4); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + #endregion + + #region QueueWorkItem(Func<...>) + + public IWorkItemResult QueueWorkItem(Func func) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(); + }); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T arg) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3, arg4); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs index af5af07..156a131 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs @@ -1,600 +1,645 @@ -// Ami Bar -// amibar@gmail.com - -using System; -using System.Threading; - -namespace Amib.Threading.Internal -{ - #region WorkItemsQueue class - - /// - /// WorkItemsQueue class. - /// - public class WorkItemsQueue : IDisposable - { - #region Member variables - - /// - /// Waiters queue (implemented as stack). - /// - private WaiterEntry _headWaiterEntry = new WaiterEntry(); - - /// - /// Waiters count - /// - private int _waitersCount = 0; - - /// - /// Work items queue - /// - private PriorityQueue _workItems = new PriorityQueue(); - - /// - /// Indicate that work items are allowed to be queued - /// - private bool _isWorkItemsQueueActive = true; - - /// - /// Each thread in the thread pool keeps its own waiter entry. - /// - [ThreadStatic] - private static WaiterEntry _waiterEntry; - - /// - /// A flag that indicates if the WorkItemsQueue has been disposed. - /// - private bool _isDisposed = false; - - #endregion - - #region Public properties - - /// - /// Returns the current number of work items in the queue - /// - public int Count - { - get - { - lock(this) - { - ValidateNotDisposed(); - return _workItems.Count; - } - } - } - - /// - /// Returns the current number of waiters - /// - public int WaitersCount - { - get - { - lock(this) - { - ValidateNotDisposed(); - return _waitersCount; - } - } - } - - - #endregion - - #region Public methods - - /// - /// Enqueue a work item to the queue. - /// - public bool EnqueueWorkItem(WorkItem workItem) - { - // A work item cannot be null, since null is used in the - // WaitForWorkItem() method to indicate timeout or cancel - if (null == workItem) - { - throw new ArgumentNullException("workItem" , "workItem cannot be null"); - } - - bool enqueue = true; - - // First check if there is a waiter waiting for work item. During - // the check, timed out waiters are ignored. If there is no - // waiter then the work item is queued. - lock(this) - { - ValidateNotDisposed(); - - if (!_isWorkItemsQueueActive) - { - return false; - } - - while(_waitersCount > 0) - { - // Dequeue a waiter. - WaiterEntry waiterEntry = PopWaiter(); - - // Signal the waiter. On success break the loop - if (waiterEntry.Signal(workItem)) - { - enqueue = false; - break; - } - } - - if (enqueue) - { - // Enqueue the work item - _workItems.Enqueue(workItem); - } - } - return true; - } - - - /// - /// Waits for a work item or exits on timeout or cancel - /// - /// Timeout in milliseconds - /// Cancel wait handle - /// Returns true if the resource was granted - public WorkItem DequeueWorkItem( - int millisecondsTimeout, - WaitHandle cancelEvent) - { - /// This method cause the caller to wait for a work item. - /// If there is at least one waiting work item then the - /// method returns immidiately with true. - /// - /// If there are no waiting work items then the caller - /// is queued between other waiters for a work item to arrive. - /// - /// If a work item didn't come within millisecondsTimeout or - /// the user canceled the wait by signaling the cancelEvent - /// then the method returns false to indicate that the caller - /// didn't get a work item. - - WaiterEntry waiterEntry = null; - WorkItem workItem = null; - - lock(this) - { - ValidateNotDisposed(); - - // If there are waiting work items then take one and return. - if (_workItems.Count > 0) - { - workItem = _workItems.Dequeue() as WorkItem; - return workItem; - } - // No waiting work items ... - else - { - // Get the wait entry for the waiters queue - waiterEntry = GetThreadWaiterEntry(); - - // Put the waiter with the other waiters - PushWaiter(waiterEntry); - } - } - - // Prepare array of wait handle for the WaitHandle.WaitAny() - WaitHandle [] waitHandles = new WaitHandle [] { - waiterEntry.WaitHandle, - cancelEvent }; - - // Wait for an available resource, cancel event, or timeout. - - // During the wait we are supposes to exit the synchronization - // domain. (Placing true as the third argument of the WaitAny()) - // It just doesn't work, I don't know why, so I have lock(this) - // statments insted of one. - - int index = WaitHandle.WaitAny( - waitHandles, - millisecondsTimeout, - true); - - lock(this) - { - // success is true if it got a work item. - bool success = (0 == index); - - // The timeout variable is used only for readability. - // (We treat cancel as timeout) - bool timeout = !success; - - // On timeout update the waiterEntry that it is timed out - if (timeout) - { - // The Timeout() fails if the waiter has already been signaled - timeout = waiterEntry.Timeout(); - - // On timeout remove the waiter from the queue. - // Note that the complexity is O(1). - if(timeout) - { - RemoveWaiter(waiterEntry, false); - } - - // Again readability - success = !timeout; - } - - // On success return the work item - if (success) - { - workItem = waiterEntry.WorkItem; - - if (null == workItem) - { - workItem = _workItems.Dequeue() as WorkItem; - } - } - } - // On failure return null. - return workItem; - } - - /// - /// Cleanup the work items queue, hence no more work - /// items are allowed to be queue - /// - protected virtual void Cleanup() - { - lock(this) - { - // Deactivate only once - if (!_isWorkItemsQueueActive) - { - return; - } - - // Don't queue more work items - _isWorkItemsQueueActive = false; - - foreach(WorkItem workItem in _workItems) - { - workItem.DisposeOfState(); - } - - // Clear the work items that are already queued - _workItems.Clear(); - - // Note: - // I don't iterate over the queue and dispose of work items's states, - // since if a work item has a state object that is still in use in the - // application then I must not dispose it. - - // Tell the waiters that they were timed out. - // It won't signal them to exit, but to ignore their - // next work item. - while(_waitersCount > 0) - { - WaiterEntry waiterEntry = PopWaiter(); - waiterEntry.Timeout(); - } - } - } - - #endregion - - #region Private methods - - /// - /// Returns the WaiterEntry of the current thread - /// - /// - /// In order to avoid creation and destuction of WaiterEntry - /// objects each thread has its own WaiterEntry object. - private WaiterEntry GetThreadWaiterEntry() - { - if (null == _waiterEntry) - { - _waiterEntry = new WaiterEntry(); - } - _waiterEntry.Reset(); - return _waiterEntry; - } - - #region Waiters stack methods - - /// - /// Push a new waiter into the waiter's stack - /// - /// A waiter to put in the stack - public void PushWaiter(WaiterEntry newWaiterEntry) - { - // Remove the waiter if it is already in the stack and - // update waiter's count as needed - RemoveWaiter(newWaiterEntry, false); - - // If the stack is empty then newWaiterEntry is the new head of the stack - if (null == _headWaiterEntry._nextWaiterEntry) - { - _headWaiterEntry._nextWaiterEntry = newWaiterEntry; - newWaiterEntry._prevWaiterEntry = _headWaiterEntry; - - } - // If the stack is not empty then put newWaiterEntry as the new head - // of the stack. - else - { - // Save the old first waiter entry - WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; - - // Update the links - _headWaiterEntry._nextWaiterEntry = newWaiterEntry; - newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; - newWaiterEntry._prevWaiterEntry = _headWaiterEntry; - oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; - } - - // Increment the number of waiters - ++_waitersCount; - } - - /// - /// Pop a waiter from the waiter's stack - /// - /// Returns the first waiter in the stack - private WaiterEntry PopWaiter() - { - // Store the current stack head - WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; - - // Store the new stack head - WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; - - // Update the old stack head list links and decrement the number - // waiters. - RemoveWaiter(oldFirstWaiterEntry, true); - - // Update the new stack head - _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; - if (null != newHeadWaiterEntry) - { - newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; - } - - // Return the old stack head - return oldFirstWaiterEntry; - } - - /// - /// Remove a waiter from the stack - /// - /// A waiter entry to remove - /// If true the waiter count is always decremented - private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) - { - // Store the prev entry in the list - WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; - - // Store the next entry in the list - WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; - - // A flag to indicate if we need to decrement the waiters count. - // If we got here from PopWaiter then we must decrement. - // If we got here from PushWaiter then we decrement only if - // the waiter was already in the stack. - bool decrementCounter = popDecrement; - - // Null the waiter's entry links - waiterEntry._prevWaiterEntry = null; - waiterEntry._nextWaiterEntry = null; - - // If the waiter entry had a prev link then update it. - // It also means that the waiter is already in the list and we - // need to decrement the waiters count. - if (null != prevWaiterEntry) - { - prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; - decrementCounter = true; - } - - // If the waiter entry had a next link then update it. - // It also means that the waiter is already in the list and we - // need to decrement the waiters count. - if (null != nextWaiterEntry) - { - nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; - decrementCounter = true; - } - - // Decrement the waiters count if needed - if (decrementCounter) - { - --_waitersCount; - } - } - - #endregion - - #endregion - - #region WaiterEntry class - - // A waiter entry in the _waiters queue. - public class WaiterEntry : IDisposable - { - #region Member variables - - /// - /// Event to signal the waiter that it got the work item. - /// - private AutoResetEvent _waitHandle = new AutoResetEvent(false); - - /// - /// Flag to know if this waiter already quited from the queue - /// because of a timeout. - /// - private bool _isTimedout = false; - - /// - /// Flag to know if the waiter was signaled and got a work item. - /// - private bool _isSignaled = false; - - /// - /// A work item that passed directly to the waiter withou going - /// through the queue - /// - private WorkItem _workItem = null; - - private bool _isDisposed = false; - - // Linked list members - internal WaiterEntry _nextWaiterEntry = null; - internal WaiterEntry _prevWaiterEntry = null; - - #endregion - - #region Construction - - public WaiterEntry() - { - Reset(); - } - - #endregion - - #region Public methods - - public WaitHandle WaitHandle - { - get { return _waitHandle; } - } - - public WorkItem WorkItem - { - get - { - lock(this) - { - return _workItem; - } - } - } - - /// - /// Signal the waiter that it got a work item. - /// - /// Return true on success - /// The method fails if Timeout() preceded its call - public bool Signal(WorkItem workItem) - { - lock(this) - { - if (!_isTimedout) - { - _workItem = workItem; - _isSignaled = true; - _waitHandle.Set(); - return true; - } - } - return false; - } - - /// - /// Mark the wait entry that it has been timed out - /// - /// Return true on success - /// The method fails if Signal() preceded its call - public bool Timeout() - { - lock(this) - { - // Time out can happen only if the waiter wasn't marked as - // signaled - if (!_isSignaled) - { - // We don't remove the waiter from the queue, the DequeueWorkItem - // method skips _waiters that were timed out. - _isTimedout = true; - return true; - } - } - return false; - } - - /// - /// Reset the wait entry so it can be used again - /// - public void Reset() - { - _workItem = null; - _isTimedout = false; - _isSignaled = false; - _waitHandle.Reset(); - } - - /// - /// Free resources - /// - public void Close() - { - if (null != _waitHandle) - { - _waitHandle.Close(); - _waitHandle = null; - } - } - - #endregion - - #region IDisposable Members - - public void Dispose() - { - if (!_isDisposed) - { - Close(); - _isDisposed = true; - } - } - - ~WaiterEntry() - { - Dispose(); - } - - #endregion - } - - #endregion - - #region IDisposable Members - - public void Dispose() - { - if (!_isDisposed) - { - Cleanup(); - _isDisposed = true; - GC.SuppressFinalize(this); - } - } - - ~WorkItemsQueue() - { - Cleanup(); - } - - private void ValidateNotDisposed() - { - if(_isDisposed) - { - throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); - } - } - - #endregion - } - - #endregion -} - +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Amib.Threading.Internal +{ + #region WorkItemsQueue class + + /// + /// WorkItemsQueue class. + /// + public class WorkItemsQueue : IDisposable + { + #region Member variables + + /// + /// Waiters queue (implemented as stack). + /// + private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); + + /// + /// Waiters count + /// + private int _waitersCount = 0; + + /// + /// Work items queue + /// + private readonly PriorityQueue _workItems = new PriorityQueue(); + + /// + /// Indicate that work items are allowed to be queued + /// + private bool _isWorkItemsQueueActive = true; + + +#if (WINDOWS_PHONE) + private static readonly Dictionary _waiterEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); +#else + + [ThreadStatic] + private static WaiterEntry _waiterEntry; +#endif + + + /// + /// Each thread in the thread pool keeps its own waiter entry. + /// + private static WaiterEntry CurrentWaiterEntry + { +#if (WINDOWS_PHONE) + get + { + lock (_waiterEntries) + { + WaiterEntry waiterEntry; + if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry)) + { + return waiterEntry; + } + } + return null; + } + set + { + lock (_waiterEntries) + { + _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + return Thread.GetData(_waiterEntrySlot) as WaiterEntry; + } + set + { + Thread.SetData(_waiterEntrySlot, value); + } +#else + get + { + return _waiterEntry; + } + set + { + _waiterEntry = value; + } +#endif + } + + /// + /// A flag that indicates if the WorkItemsQueue has been disposed. + /// + private bool _isDisposed = false; + + #endregion + + #region Public properties + + /// + /// Returns the current number of work items in the queue + /// + public int Count + { + get + { + return _workItems.Count; + } + } + + /// + /// Returns the current number of waiters + /// + public int WaitersCount + { + get + { + return _waitersCount; + } + } + + + #endregion + + #region Public methods + + /// + /// Enqueue a work item to the queue. + /// + public bool EnqueueWorkItem(WorkItem workItem) + { + // A work item cannot be null, since null is used in the + // WaitForWorkItem() method to indicate timeout or cancel + if (null == workItem) + { + throw new ArgumentNullException("workItem" , "workItem cannot be null"); + } + + bool enqueue = true; + + // First check if there is a waiter waiting for work item. During + // the check, timed out waiters are ignored. If there is no + // waiter then the work item is queued. + lock(this) + { + ValidateNotDisposed(); + + if (!_isWorkItemsQueueActive) + { + return false; + } + + while(_waitersCount > 0) + { + // Dequeue a waiter. + WaiterEntry waiterEntry = PopWaiter(); + + // Signal the waiter. On success break the loop + if (waiterEntry.Signal(workItem)) + { + enqueue = false; + break; + } + } + + if (enqueue) + { + // Enqueue the work item + _workItems.Enqueue(workItem); + } + } + return true; + } + + + /// + /// Waits for a work item or exits on timeout or cancel + /// + /// Timeout in milliseconds + /// Cancel wait handle + /// Returns true if the resource was granted + public WorkItem DequeueWorkItem( + int millisecondsTimeout, + WaitHandle cancelEvent) + { + // This method cause the caller to wait for a work item. + // If there is at least one waiting work item then the + // method returns immidiately with it. + // + // If there are no waiting work items then the caller + // is queued between other waiters for a work item to arrive. + // + // If a work item didn't come within millisecondsTimeout or + // the user canceled the wait by signaling the cancelEvent + // then the method returns null to indicate that the caller + // didn't get a work item. + + WaiterEntry waiterEntry; + WorkItem workItem = null; + lock (this) + { + ValidateNotDisposed(); + + // If there are waiting work items then take one and return. + if (_workItems.Count > 0) + { + workItem = _workItems.Dequeue() as WorkItem; + return workItem; + } + + // No waiting work items ... + + // Get the waiter entry for the waiters queue + waiterEntry = GetThreadWaiterEntry(); + + // Put the waiter with the other waiters + PushWaiter(waiterEntry); + } + + // Prepare array of wait handle for the WaitHandle.WaitAny() + WaitHandle [] waitHandles = new WaitHandle[] { + waiterEntry.WaitHandle, + cancelEvent }; + + // Wait for an available resource, cancel event, or timeout. + + // During the wait we are supposes to exit the synchronization + // domain. (Placing true as the third argument of the WaitAny()) + // It just doesn't work, I don't know why, so I have two lock(this) + // statments instead of one. + + int index = STPEventWaitHandle.WaitAny( + waitHandles, + millisecondsTimeout, + true); + + lock(this) + { + // success is true if it got a work item. + bool success = (0 == index); + + // The timeout variable is used only for readability. + // (We treat cancel as timeout) + bool timeout = !success; + + // On timeout update the waiterEntry that it is timed out + if (timeout) + { + // The Timeout() fails if the waiter has already been signaled + timeout = waiterEntry.Timeout(); + + // On timeout remove the waiter from the queue. + // Note that the complexity is O(1). + if(timeout) + { + RemoveWaiter(waiterEntry, false); + } + + // Again readability + success = !timeout; + } + + // On success return the work item + if (success) + { + workItem = waiterEntry.WorkItem; + + if (null == workItem) + { + workItem = _workItems.Dequeue() as WorkItem; + } + } + } + // On failure return null. + return workItem; + } + + /// + /// Cleanup the work items queue, hence no more work + /// items are allowed to be queue + /// + private void Cleanup() + { + lock(this) + { + // Deactivate only once + if (!_isWorkItemsQueueActive) + { + return; + } + + // Don't queue more work items + _isWorkItemsQueueActive = false; + + foreach(WorkItem workItem in _workItems) + { + workItem.DisposeOfState(); + } + + // Clear the work items that are already queued + _workItems.Clear(); + + // Note: + // I don't iterate over the queue and dispose of work items's states, + // since if a work item has a state object that is still in use in the + // application then I must not dispose it. + + // Tell the waiters that they were timed out. + // It won't signal them to exit, but to ignore their + // next work item. + while(_waitersCount > 0) + { + WaiterEntry waiterEntry = PopWaiter(); + waiterEntry.Timeout(); + } + } + } + + public object[] GetStates() + { + lock (this) + { + object[] states = new object[_workItems.Count]; + int i = 0; + foreach (WorkItem workItem in _workItems) + { + states[i] = workItem.GetWorkItemResult().State; + ++i; + } + return states; + } + } + + #endregion + + #region Private methods + + /// + /// Returns the WaiterEntry of the current thread + /// + /// + /// In order to avoid creation and destuction of WaiterEntry + /// objects each thread has its own WaiterEntry object. + private static WaiterEntry GetThreadWaiterEntry() + { + if (null == CurrentWaiterEntry) + { + CurrentWaiterEntry = new WaiterEntry(); + } + CurrentWaiterEntry.Reset(); + return CurrentWaiterEntry; + } + + #region Waiters stack methods + + /// + /// Push a new waiter into the waiter's stack + /// + /// A waiter to put in the stack + public void PushWaiter(WaiterEntry newWaiterEntry) + { + // Remove the waiter if it is already in the stack and + // update waiter's count as needed + RemoveWaiter(newWaiterEntry, false); + + // If the stack is empty then newWaiterEntry is the new head of the stack + if (null == _headWaiterEntry._nextWaiterEntry) + { + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + + } + // If the stack is not empty then put newWaiterEntry as the new head + // of the stack. + else + { + // Save the old first waiter entry + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + + // Update the links + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; + } + + // Increment the number of waiters + ++_waitersCount; + } + + /// + /// Pop a waiter from the waiter's stack + /// + /// Returns the first waiter in the stack + private WaiterEntry PopWaiter() + { + // Store the current stack head + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + + // Store the new stack head + WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; + + // Update the old stack head list links and decrement the number + // waiters. + RemoveWaiter(oldFirstWaiterEntry, true); + + // Update the new stack head + _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; + if (null != newHeadWaiterEntry) + { + newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; + } + + // Return the old stack head + return oldFirstWaiterEntry; + } + + /// + /// Remove a waiter from the stack + /// + /// A waiter entry to remove + /// If true the waiter count is always decremented + private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) + { + // Store the prev entry in the list + WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; + + // Store the next entry in the list + WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; + + // A flag to indicate if we need to decrement the waiters count. + // If we got here from PopWaiter then we must decrement. + // If we got here from PushWaiter then we decrement only if + // the waiter was already in the stack. + bool decrementCounter = popDecrement; + + // Null the waiter's entry links + waiterEntry._prevWaiterEntry = null; + waiterEntry._nextWaiterEntry = null; + + // If the waiter entry had a prev link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != prevWaiterEntry) + { + prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; + decrementCounter = true; + } + + // If the waiter entry had a next link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != nextWaiterEntry) + { + nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; + decrementCounter = true; + } + + // Decrement the waiters count if needed + if (decrementCounter) + { + --_waitersCount; + } + } + + #endregion + + #endregion + + #region WaiterEntry class + + // A waiter entry in the _waiters queue. + public sealed class WaiterEntry : IDisposable + { + #region Member variables + + /// + /// Event to signal the waiter that it got the work item. + /// + //private AutoResetEvent _waitHandle = new AutoResetEvent(false); + private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); + + /// + /// Flag to know if this waiter already quited from the queue + /// because of a timeout. + /// + private bool _isTimedout = false; + + /// + /// Flag to know if the waiter was signaled and got a work item. + /// + private bool _isSignaled = false; + + /// + /// A work item that passed directly to the waiter withou going + /// through the queue + /// + private WorkItem _workItem = null; + + private bool _isDisposed = false; + + // Linked list members + internal WaiterEntry _nextWaiterEntry = null; + internal WaiterEntry _prevWaiterEntry = null; + + #endregion + + #region Construction + + public WaiterEntry() + { + Reset(); + } + + #endregion + + #region Public methods + + public WaitHandle WaitHandle + { + get { return _waitHandle; } + } + + public WorkItem WorkItem + { + get + { + return _workItem; + } + } + + /// + /// Signal the waiter that it got a work item. + /// + /// Return true on success + /// The method fails if Timeout() preceded its call + public bool Signal(WorkItem workItem) + { + lock(this) + { + if (!_isTimedout) + { + _workItem = workItem; + _isSignaled = true; + _waitHandle.Set(); + return true; + } + } + return false; + } + + /// + /// Mark the wait entry that it has been timed out + /// + /// Return true on success + /// The method fails if Signal() preceded its call + public bool Timeout() + { + lock(this) + { + // Time out can happen only if the waiter wasn't marked as + // signaled + if (!_isSignaled) + { + // We don't remove the waiter from the queue, the DequeueWorkItem + // method skips _waiters that were timed out. + _isTimedout = true; + return true; + } + } + return false; + } + + /// + /// Reset the wait entry so it can be used again + /// + public void Reset() + { + _workItem = null; + _isTimedout = false; + _isSignaled = false; + _waitHandle.Reset(); + } + + /// + /// Free resources + /// + public void Close() + { + if (null != _waitHandle) + { + _waitHandle.Close(); + _waitHandle = null; + } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + lock (this) + { + if (!_isDisposed) + { + Close(); + } + _isDisposed = true; + } + } + + #endregion + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!_isDisposed) + { + Cleanup(); + } + _isDisposed = true; + } + + private void ValidateNotDisposed() + { + if(_isDisposed) + { + throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); + } + } + + #endregion + } + + #endregion +} + -- cgit v1.1 From 8d3250cee4d16113e955c42d3cce02b0024bea55 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 1 May 2013 19:15:05 +0100 Subject: Add information about creating a PID file for robust to the Robust.ini and Robust.HG.ini example files --- bin/Robust.HG.ini.example | 27 +++++++++++++++------------ bin/Robust.ini.example | 28 +++++++++++++++------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index 581c31d..d74f205 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -21,18 +21,21 @@ ; * [[@]/][:] ; * [Startup] - - ; 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" + ; 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 = "." + + ; 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] diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index b98132e..b7b2524 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -13,19 +13,21 @@ ; * [[@]/][:] ; * [Startup] - - ; 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" + ; 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 = "." + + ; 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" -- cgit v1.1 From 81a90e30c637f84e6ef5d1cf7c188c1f6db12eb3 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 1 May 2013 19:29:46 +0100 Subject: Add in-code exaplanation for the change in cancellation signalling in STP 2.2.3. Remove left in Console.WriteLine accidentally inserted in recent 206fb306 --- OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs | 2 -- ThirdParty/SmartThreadPool/WorkItem.cs | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index f9d3afc..887a317 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -628,8 +628,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance } } - Console.WriteLine("Here9"); - lock (EventQueue) { workItem = m_CurrentWorkItem; diff --git a/ThirdParty/SmartThreadPool/WorkItem.cs b/ThirdParty/SmartThreadPool/WorkItem.cs index f229d1f..185f10c 100644 --- a/ThirdParty/SmartThreadPool/WorkItem.cs +++ b/ThirdParty/SmartThreadPool/WorkItem.cs @@ -753,6 +753,16 @@ namespace Amib.Threading.Internal } else { + // ************************** + // Stock SmartThreadPool 2.2.3 sets these to true and relies on the thread to check the + // WorkItem cancellation status. However, OpenSimulator uses a different mechanism to notify + // scripts of co-operative termination and the abort code also relies on this method + // returning false in order to implement a small wait. + // + // Therefore, as was the case previously with STP, we will not signal successful cancellation + // here. It's possible that OpenSimulator code could be changed in the future to remove + // the need for this change. + // ************************** success = false; signalComplete = false; } -- cgit v1.1 From b26276c8c48a8cf4edb468b2fd428815034a6320 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 1 May 2013 21:35:50 +0100 Subject: Fix the long standing bug of items being delivered to lost and found or trash when takig copy. This bug was recently aggravated through the perms changes required for the export permission. --- .../CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index e0009bb..eb37626 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -646,11 +646,12 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess } else { - if (remoteClient == null || so.OwnerID != remoteClient.AgentId) + if (remoteClient == null || so.RootPart.OwnerID != remoteClient.AgentId) { // Taking copy of another person's item. Take to // Objects folder. folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); + so.FromFolderID = UUID.Zero; } else { @@ -666,7 +667,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess // if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) { - if (so.FromFolderID != UUID.Zero && userID == remoteClient.AgentId) + if (so.FromFolderID != UUID.Zero && so.RootPart.OwnerID == remoteClient.AgentId) { InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID); folder = m_Scene.InventoryService.GetFolder(f); -- cgit v1.1 From 854dcd1abddc3eef33da953592deb61133e5e7ed Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 1 May 2013 23:00:46 +0100 Subject: Fix SmartThreadPool line endings in recent update from dos to unix --- ThirdParty/SmartThreadPool/CallerThreadContext.cs | 276 +- .../SmartThreadPool/CanceledWorkItemsGroup.cs | 26 +- ThirdParty/SmartThreadPool/EventWaitHandle.cs | 206 +- .../SmartThreadPool/EventWaitHandleFactory.cs | 164 +- ThirdParty/SmartThreadPool/Exceptions.cs | 222 +- ThirdParty/SmartThreadPool/Interfaces.cs | 1256 +++---- ThirdParty/SmartThreadPool/InternalInterfaces.cs | 54 +- ThirdParty/SmartThreadPool/PriorityQueue.cs | 478 +-- .../SmartThreadPool/Properties/AssemblyInfo.cs | 46 +- ThirdParty/SmartThreadPool/SLExt.cs | 32 +- ThirdParty/SmartThreadPool/STPEventWaitHandle.cs | 122 +- .../SmartThreadPool/STPPerformanceCounter.cs | 896 ++--- ThirdParty/SmartThreadPool/STPStartInfo.cs | 424 +-- .../SmartThreadPool/SmartThreadPool.ThreadEntry.cs | 118 +- ThirdParty/SmartThreadPool/SmartThreadPool.cs | 3464 ++++++++++---------- .../SmartThreadPool/SynchronizedDictionary.cs | 178 +- ThirdParty/SmartThreadPool/WIGStartInfo.cs | 342 +- .../SmartThreadPool/WorkItem.WorkItemResult.cs | 380 +-- ThirdParty/SmartThreadPool/WorkItemFactory.cs | 686 ++-- ThirdParty/SmartThreadPool/WorkItemInfo.cs | 138 +- .../SmartThreadPool/WorkItemResultTWrapper.cs | 256 +- ThirdParty/SmartThreadPool/WorkItemsGroup.cs | 722 ++-- ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs | 940 +++--- ThirdParty/SmartThreadPool/WorkItemsQueue.cs | 1290 ++++---- 24 files changed, 6358 insertions(+), 6358 deletions(-) diff --git a/ThirdParty/SmartThreadPool/CallerThreadContext.cs b/ThirdParty/SmartThreadPool/CallerThreadContext.cs index 2177241..e63add5 100644 --- a/ThirdParty/SmartThreadPool/CallerThreadContext.cs +++ b/ThirdParty/SmartThreadPool/CallerThreadContext.cs @@ -1,138 +1,138 @@ - -#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 - - /// - /// This class stores the caller call context in order to restore - /// it when the work item is executed in the thread pool environment. - /// - internal class CallerThreadContext - { -#region Prepare reflection information - - // Cached type information. - private static 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 - - /// - /// Constructor - /// - private CallerThreadContext() - { - } - - public bool CapturedCallContext - { - get - { - return (null != _callContext); - } - } - - public bool CapturedHttpContext - { - get - { - return (null != _httpContext); - } - } - - /// - /// Captures the current thread context - /// - /// - public static CallerThreadContext Capture( - bool captureCallContext, - bool captureHttpContext) - { - Debug.Assert(captureCallContext || captureHttpContext); - - CallerThreadContext callerThreadContext = new CallerThreadContext(); - - // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() - // Capture Call Context - if(captureCallContext && (getLogicalCallContextMethodInfo != null)) - { - callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); - if (callerThreadContext._callContext != null) - { - callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); - } - } - - // Capture httpContext - if (captureHttpContext && (null != HttpContext.Current)) - { - callerThreadContext._httpContext = HttpContext.Current; - } - - return callerThreadContext; - } - - /// - /// Applies the thread context stored earlier - /// - /// - public static void Apply(CallerThreadContext callerThreadContext) - { - if (null == callerThreadContext) - { - throw new ArgumentNullException("callerThreadContext"); - } - - // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() - // Restore call context - if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) - { - setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); - } - - // Restore HttpContext - if (callerThreadContext._httpContext != null) - { - HttpContext.Current = callerThreadContext._httpContext; - //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); - } - } - } - - #endregion -} -#endif + +#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 + + /// + /// This class stores the caller call context in order to restore + /// it when the work item is executed in the thread pool environment. + /// + internal class CallerThreadContext + { +#region Prepare reflection information + + // Cached type information. + private static 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 + + /// + /// Constructor + /// + private CallerThreadContext() + { + } + + public bool CapturedCallContext + { + get + { + return (null != _callContext); + } + } + + public bool CapturedHttpContext + { + get + { + return (null != _httpContext); + } + } + + /// + /// Captures the current thread context + /// + /// + public static CallerThreadContext Capture( + bool captureCallContext, + bool captureHttpContext) + { + Debug.Assert(captureCallContext || captureHttpContext); + + CallerThreadContext callerThreadContext = new CallerThreadContext(); + + // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() + // Capture Call Context + if(captureCallContext && (getLogicalCallContextMethodInfo != null)) + { + callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); + if (callerThreadContext._callContext != null) + { + callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); + } + } + + // Capture httpContext + if (captureHttpContext && (null != HttpContext.Current)) + { + callerThreadContext._httpContext = HttpContext.Current; + } + + return callerThreadContext; + } + + /// + /// Applies the thread context stored earlier + /// + /// + public static void Apply(CallerThreadContext callerThreadContext) + { + if (null == callerThreadContext) + { + throw new ArgumentNullException("callerThreadContext"); + } + + // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() + // Restore call context + if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) + { + setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); + } + + // Restore HttpContext + if (callerThreadContext._httpContext != null) + { + HttpContext.Current = callerThreadContext._httpContext; + //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); + } + } + } + + #endregion +} +#endif diff --git a/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs index 4a2a3e7..5752957 100644 --- a/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs +++ b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs @@ -1,14 +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; } - } +namespace Amib.Threading.Internal +{ + internal class CanceledWorkItemsGroup + { + public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + public CanceledWorkItemsGroup() + { + IsCanceled = false; + } + + public bool IsCanceled { get; set; } + } } \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandle.cs b/ThirdParty/SmartThreadPool/EventWaitHandle.cs index 70a1a29..25be07a 100644 --- a/ThirdParty/SmartThreadPool/EventWaitHandle.cs +++ b/ThirdParty/SmartThreadPool/EventWaitHandle.cs @@ -1,104 +1,104 @@ -#if (_WINDOWS_CE) - -using System; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Amib.Threading.Internal -{ - /// - /// EventWaitHandle class - /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation. - /// So I wrote this class to implement these two methods with some of their overloads. - /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny. - /// Note that this class doesn't even inherit from WaitHandle! - /// - public class STPEventWaitHandle - { - #region Public Constants - - public const int WaitTimeout = Timeout.Infinite; - - #endregion - - #region Private External Constants - - private const Int32 WAIT_FAILED = -1; - private const Int32 WAIT_TIMEOUT = 0x102; - private const UInt32 INFINITE = 0xFFFFFFFF; - - #endregion - - #region WaitAll and WaitAny - - internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) - { - return waitHandle.WaitOne(millisecondsTimeout, exitContext); - } - - private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles) - { - IntPtr[] nativeHandles = new IntPtr[waitHandles.Length]; - for (int i = 0; i < waitHandles.Length; i++) - { - nativeHandles[i] = waitHandles[i].Handle; - } - return nativeHandles; - } - - public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) - { - uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; - - IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); - - int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout); - - if (result == WAIT_TIMEOUT || result == WAIT_FAILED) - { - return false; - } - - return true; - } - - - public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) - { - uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; - - IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); - - int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout); - - if (result >= 0 && result < waitHandles.Length) - { - return result; - } - - return -1; - } - - public static int WaitAny(WaitHandle[] waitHandles) - { - return WaitAny(waitHandles, Timeout.Infinite, false); - } - - public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) - { - int millisecondsTimeout = (int)timeout.TotalMilliseconds; - - return WaitAny(waitHandles, millisecondsTimeout, false); - } - - #endregion - - #region External methods - - [DllImport("coredll.dll", SetLastError = true)] - public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds); - - #endregion - } -} +#if (_WINDOWS_CE) + +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandle class + /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation. + /// So I wrote this class to implement these two methods with some of their overloads. + /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny. + /// Note that this class doesn't even inherit from WaitHandle! + /// + public class STPEventWaitHandle + { + #region Public Constants + + public const int WaitTimeout = Timeout.Infinite; + + #endregion + + #region Private External Constants + + private const Int32 WAIT_FAILED = -1; + private const Int32 WAIT_TIMEOUT = 0x102; + private const UInt32 INFINITE = 0xFFFFFFFF; + + #endregion + + #region WaitAll and WaitAny + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + + private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles) + { + IntPtr[] nativeHandles = new IntPtr[waitHandles.Length]; + for (int i = 0; i < waitHandles.Length; i++) + { + nativeHandles[i] = waitHandles[i].Handle; + } + return nativeHandles; + } + + public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout); + + if (result == WAIT_TIMEOUT || result == WAIT_FAILED) + { + return false; + } + + return true; + } + + + public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout); + + if (result >= 0 && result < waitHandles.Length) + { + return result; + } + + return -1; + } + + public static int WaitAny(WaitHandle[] waitHandles) + { + return WaitAny(waitHandles, Timeout.Infinite, false); + } + + public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) + { + int millisecondsTimeout = (int)timeout.TotalMilliseconds; + + return WaitAny(waitHandles, millisecondsTimeout, false); + } + + #endregion + + #region External methods + + [DllImport("coredll.dll", SetLastError = true)] + public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds); + + #endregion + } +} #endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs index 2f8c55b..3c9c849 100644 --- a/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs +++ b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs @@ -1,82 +1,82 @@ -using System.Threading; - -#if (_WINDOWS_CE) -using System; -using System.Runtime.InteropServices; -#endif - -namespace Amib.Threading.Internal -{ - /// - /// EventWaitHandleFactory class. - /// This is a static class that creates AutoResetEvent and ManualResetEvent objects. - /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property - /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API. - /// Consequently this class creates the needed XxxResetEvent and replaces the handle if - /// it's a WindowsCE OS. - /// - public static class EventWaitHandleFactory - { - /// - /// Create a new AutoResetEvent object - /// - /// Return a new AutoResetEvent object - public static AutoResetEvent CreateAutoResetEvent() - { - AutoResetEvent waitHandle = new AutoResetEvent(false); - -#if (_WINDOWS_CE) - ReplaceEventHandle(waitHandle, false, false); -#endif - - return waitHandle; - } - - /// - /// Create a new ManualResetEvent object - /// - /// Return a new ManualResetEvent object - public static ManualResetEvent CreateManualResetEvent(bool initialState) - { - ManualResetEvent waitHandle = new ManualResetEvent(initialState); - -#if (_WINDOWS_CE) - ReplaceEventHandle(waitHandle, true, initialState); -#endif - - return waitHandle; - } - -#if (_WINDOWS_CE) - - /// - /// Replace the event handle - /// - /// The WaitHandle object which its handle needs to be replaced. - /// Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false) - /// The initial state of the event - private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState) - { - // Store the old handle - IntPtr oldHandle = waitHandle.Handle; - - // Create a new event - IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null); - - // Replace the old event with the new event - waitHandle.Handle = newHandle; - - // Close the old event - CloseHandle (oldHandle); - } - - [DllImport("coredll.dll", SetLastError = true)] - public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); - - //Handle - [DllImport("coredll.dll", SetLastError = true)] - public static extern bool CloseHandle(IntPtr hObject); -#endif - - } -} +using System.Threading; + +#if (_WINDOWS_CE) +using System; +using System.Runtime.InteropServices; +#endif + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandleFactory class. + /// This is a static class that creates AutoResetEvent and ManualResetEvent objects. + /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property + /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API. + /// Consequently this class creates the needed XxxResetEvent and replaces the handle if + /// it's a WindowsCE OS. + /// + public static class EventWaitHandleFactory + { + /// + /// Create a new AutoResetEvent object + /// + /// Return a new AutoResetEvent object + public static AutoResetEvent CreateAutoResetEvent() + { + AutoResetEvent waitHandle = new AutoResetEvent(false); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, false, false); +#endif + + return waitHandle; + } + + /// + /// Create a new ManualResetEvent object + /// + /// Return a new ManualResetEvent object + public static ManualResetEvent CreateManualResetEvent(bool initialState) + { + ManualResetEvent waitHandle = new ManualResetEvent(initialState); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, true, initialState); +#endif + + return waitHandle; + } + +#if (_WINDOWS_CE) + + /// + /// Replace the event handle + /// + /// The WaitHandle object which its handle needs to be replaced. + /// Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false) + /// The initial state of the event + private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState) + { + // Store the old handle + IntPtr oldHandle = waitHandle.Handle; + + // Create a new event + IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null); + + // Replace the old event with the new event + waitHandle.Handle = newHandle; + + // Close the old event + CloseHandle (oldHandle); + } + + [DllImport("coredll.dll", SetLastError = true)] + public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); + + //Handle + [DllImport("coredll.dll", SetLastError = true)] + public static extern bool CloseHandle(IntPtr hObject); +#endif + + } +} diff --git a/ThirdParty/SmartThreadPool/Exceptions.cs b/ThirdParty/SmartThreadPool/Exceptions.cs index 8e66ce9..6c6a88b 100644 --- a/ThirdParty/SmartThreadPool/Exceptions.cs +++ b/ThirdParty/SmartThreadPool/Exceptions.cs @@ -1,111 +1,111 @@ -using System; -#if !(_WINDOWS_CE) -using System.Runtime.Serialization; -#endif - -namespace Amib.Threading -{ - #region Exceptions - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been canceled - /// - public sealed partial class WorkItemCancelException : Exception - { - public WorkItemCancelException() - { - } - - public WorkItemCancelException(string message) - : base(message) - { - } - - public WorkItemCancelException(string message, Exception e) - : base(message, e) - { - } - } - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been timed out - /// - public sealed partial class WorkItemTimeoutException : Exception - { - public WorkItemTimeoutException() - { - } - - public WorkItemTimeoutException(string message) - : base(message) - { - } - - public WorkItemTimeoutException(string message, Exception e) - : base(message, e) - { - } - } - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been timed out - /// - public sealed partial class WorkItemResultException : Exception - { - public WorkItemResultException() - { - } - - public WorkItemResultException(string message) - : base(message) - { - } - - public WorkItemResultException(string message, Exception e) - : base(message, e) - { - } - } - - -#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) - /// - /// Represents an exception in case IWorkItemResult.GetResult has been canceled - /// - [Serializable] - public sealed partial class WorkItemCancelException - { - public WorkItemCancelException(SerializationInfo si, StreamingContext sc) - : base(si, sc) - { - } - } - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been timed out - /// - [Serializable] - public sealed partial class WorkItemTimeoutException - { - public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) - : base(si, sc) - { - } - } - - /// - /// Represents an exception in case IWorkItemResult.GetResult has been timed out - /// - [Serializable] - public sealed partial class WorkItemResultException - { - public WorkItemResultException(SerializationInfo si, StreamingContext sc) - : base(si, sc) - { - } - } - -#endif - - #endregion -} +using System; +#if !(_WINDOWS_CE) +using System.Runtime.Serialization; +#endif + +namespace Amib.Threading +{ + #region Exceptions + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been canceled + /// + public sealed partial class WorkItemCancelException : Exception + { + public WorkItemCancelException() + { + } + + public WorkItemCancelException(string message) + : base(message) + { + } + + public WorkItemCancelException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemTimeoutException : Exception + { + public WorkItemTimeoutException() + { + } + + public WorkItemTimeoutException(string message) + : base(message) + { + } + + public WorkItemTimeoutException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemResultException : Exception + { + public WorkItemResultException() + { + } + + public WorkItemResultException(string message) + : base(message) + { + } + + public WorkItemResultException(string message, Exception e) + : base(message, e) + { + } + } + + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// Represents an exception in case IWorkItemResult.GetResult has been canceled + /// + [Serializable] + public sealed partial class WorkItemCancelException + { + public WorkItemCancelException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + [Serializable] + public sealed partial class WorkItemTimeoutException + { + public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + [Serializable] + public sealed partial class WorkItemResultException + { + public WorkItemResultException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + +#endif + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/Interfaces.cs b/ThirdParty/SmartThreadPool/Interfaces.cs index 29c8a3e..513422f 100644 --- a/ThirdParty/SmartThreadPool/Interfaces.cs +++ b/ThirdParty/SmartThreadPool/Interfaces.cs @@ -1,628 +1,628 @@ -using System; -using System.Threading; - -namespace Amib.Threading -{ - #region Delegates - - /// - /// A delegate that represents the method to run as the work item - /// - /// A state object for the method to run - public delegate object WorkItemCallback(object state); - - /// - /// A delegate to call after the WorkItemCallback completed - /// - /// The work item result object - public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); - - /// - /// A delegate to call after the WorkItemCallback completed - /// - /// The work item result object - public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); - - /// - /// A delegate to call when a WorkItemsGroup becomes idle - /// - /// A reference to the WorkItemsGroup that became idle - public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); - - /// - /// A delegate to call after a thread is created, but before - /// it's first use. - /// - public delegate void ThreadInitializationHandler(); - - /// - /// A delegate to call when a thread is about to exit, after - /// it is no longer belong to the pool. - /// - public delegate void ThreadTerminationHandler(); - - #endregion - - #region WorkItem Priority - - /// - /// Defines the availeable priorities of a work item. - /// The higher the priority a work item has, the sooner - /// it will be executed. - /// - public enum WorkItemPriority - { - Lowest, - BelowNormal, - Normal, - AboveNormal, - Highest, - } - - #endregion - - #region IWorkItemsGroup interface - - /// - /// IWorkItemsGroup interface - /// Created by SmartThreadPool.CreateWorkItemsGroup() - /// - public interface IWorkItemsGroup - { - /// - /// Get/Set the name of the WorkItemsGroup - /// - string Name { get; set; } - - /// - /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool - /// - int Concurrency { get; set; } - - /// - /// Get the number of work items waiting in the queue. - /// - int WaitingCallbacks { get; } - - /// - /// Get an array with all the state objects of the currently running items. - /// The array represents a snap shot and impact performance. - /// - object[] GetStates(); - - /// - /// Get the WorkItemsGroup start information - /// - WIGStartInfo WIGStartInfo { get; } - - /// - /// Starts to execute work items - /// - void Start(); - - /// - /// Cancel all the work items. - /// Same as Cancel(false) - /// - void Cancel(); - - /// - /// Cancel all work items using thread abortion - /// - /// True to stop work items by raising ThreadAbortException - void Cancel(bool abortExecution); - - /// - /// Wait for all work item to complete. - /// - void WaitForIdle(); - - /// - /// Wait for all work item to complete, until timeout expired - /// - /// How long to wait for the work items to complete - /// Returns true if work items completed within the timeout, otherwise false. - bool WaitForIdle(TimeSpan timeout); - - /// - /// Wait for all work item to complete, until timeout expired - /// - /// How long to wait for the work items to complete in milliseconds - /// Returns true if work items completed within the timeout, otherwise false. - bool WaitForIdle(int millisecondsTimeout); - - /// - /// IsIdle is true when there are no work items running or queued. - /// - bool IsIdle { get; } - - /// - /// This event is fired when all work items are completed. - /// (When IsIdle changes to true) - /// This event only work on WorkItemsGroup. On SmartThreadPool - /// it throws the NotImplementedException. - /// - event WorkItemsGroupIdleHandler OnIdle; - - #region QueueWorkItem - - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback); - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); - - #endregion - - #region QueueWorkItem(Action<...>) - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem (Action action, T arg); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority); - - #endregion - - #region QueueWorkItem(Func<...>) - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T arg); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4); - - #endregion - } - - #endregion - - #region CallToPostExecute enumerator - - [Flags] - public enum CallToPostExecute - { - /// - /// Never call to the PostExecute call back - /// - Never = 0x00, - - /// - /// Call to the PostExecute only when the work item is cancelled - /// - WhenWorkItemCanceled = 0x01, - - /// - /// Call to the PostExecute only when the work item is not cancelled - /// - WhenWorkItemNotCanceled = 0x02, - - /// - /// Always call to the PostExecute - /// - Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, - } - - #endregion - - #region IWorkItemResult interface - - /// - /// The common interface of IWorkItemResult and IWorkItemResult<T> - /// - public interface IWaitableResult - { - /// - /// This method intent is for internal use. - /// - /// - IWorkItemResult GetWorkItemResult(); - - /// - /// This method intent is for internal use. - /// - /// - IWorkItemResult GetWorkItemResultT(); - } - - /// - /// IWorkItemResult interface. - /// Created when a WorkItemCallback work item is queued. - /// - public interface IWorkItemResult : IWorkItemResult - { - } - - /// - /// IWorkItemResult<TResult> interface. - /// Created when a Func<TResult> work item is queued. - /// - public interface IWorkItemResult : IWaitableResult - { - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. - /// - /// The result of the work item - TResult GetResult(); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - int millisecondsTimeout, - bool exitContext); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - TimeSpan timeout, - bool exitContext); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - TResult GetResult(out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// - /// - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - int millisecondsTimeout, - bool exitContext, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// - /// Filled with the exception if one was thrown - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - TimeSpan timeout, - bool exitContext, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// - /// Filled with the exception if one was thrown - /// - /// - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - bool IsCompleted { get; } - - /// - /// Gets an indication whether the asynchronous operation has been canceled. - /// - bool IsCanceled { get; } - - /// - /// Gets the user-defined object that contains context data - /// for the work item method. - /// - object State { get; } - - /// - /// Same as Cancel(false). - /// - bool Cancel(); - - /// - /// 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 - /// - /// When true send an AbortException to the executing thread. - /// Returns true if the work item was not completed, otherwise false. - bool Cancel(bool abortExecution); - - /// - /// Get the work item's priority - /// - WorkItemPriority WorkItemPriority { get; } - - /// - /// Return the result, same as GetResult() - /// - TResult Result { get; } - - /// - /// Returns the exception if occured otherwise returns null. - /// - object Exception { get; } - } - - #endregion - - #region .NET 3.5 - - // All these delegate are built-in .NET 3.5 - // Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity. - - public delegate void Action(); - public delegate void Action(T1 arg1, T2 arg2); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3); - public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); - - public delegate TResult Func(); - public delegate TResult Func(T arg1); - public delegate TResult Func(T1 arg1, T2 arg2); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); - public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); - - #endregion -} +using System; +using System.Threading; + +namespace Amib.Threading +{ + #region Delegates + + /// + /// A delegate that represents the method to run as the work item + /// + /// A state object for the method to run + public delegate object WorkItemCallback(object state); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call when a WorkItemsGroup becomes idle + /// + /// A reference to the WorkItemsGroup that became idle + public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); + + /// + /// A delegate to call after a thread is created, but before + /// it's first use. + /// + public delegate void ThreadInitializationHandler(); + + /// + /// A delegate to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + public delegate void ThreadTerminationHandler(); + + #endregion + + #region WorkItem Priority + + /// + /// Defines the availeable priorities of a work item. + /// The higher the priority a work item has, the sooner + /// it will be executed. + /// + public enum WorkItemPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } + + #endregion + + #region IWorkItemsGroup interface + + /// + /// IWorkItemsGroup interface + /// Created by SmartThreadPool.CreateWorkItemsGroup() + /// + public interface IWorkItemsGroup + { + /// + /// Get/Set the name of the WorkItemsGroup + /// + string Name { get; set; } + + /// + /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool + /// + int Concurrency { get; set; } + + /// + /// Get the number of work items waiting in the queue. + /// + int WaitingCallbacks { get; } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + object[] GetStates(); + + /// + /// Get the WorkItemsGroup start information + /// + WIGStartInfo WIGStartInfo { get; } + + /// + /// Starts to execute work items + /// + void Start(); + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + void Cancel(); + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + void Cancel(bool abortExecution); + + /// + /// Wait for all work item to complete. + /// + void WaitForIdle(); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(TimeSpan timeout); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete in milliseconds + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(int millisecondsTimeout); + + /// + /// IsIdle is true when there are no work items running or queued. + /// + bool IsIdle { get; } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + event WorkItemsGroupIdleHandler OnIdle; + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); + + #endregion + + #region QueueWorkItem(Action<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority); + + #endregion + + #region QueueWorkItem(Func<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + #endregion + } + + #endregion + + #region CallToPostExecute enumerator + + [Flags] + public enum CallToPostExecute + { + /// + /// Never call to the PostExecute call back + /// + Never = 0x00, + + /// + /// Call to the PostExecute only when the work item is cancelled + /// + WhenWorkItemCanceled = 0x01, + + /// + /// Call to the PostExecute only when the work item is not cancelled + /// + WhenWorkItemNotCanceled = 0x02, + + /// + /// Always call to the PostExecute + /// + Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, + } + + #endregion + + #region IWorkItemResult interface + + /// + /// The common interface of IWorkItemResult and IWorkItemResult<T> + /// + public interface IWaitableResult + { + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResult(); + + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResultT(); + } + + /// + /// IWorkItemResult interface. + /// Created when a WorkItemCallback work item is queued. + /// + public interface IWorkItemResult : IWorkItemResult + { + } + + /// + /// IWorkItemResult<TResult> interface. + /// Created when a Func<TResult> work item is queued. + /// + public interface IWorkItemResult : IWaitableResult + { + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// The result of the work item + TResult GetResult(); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// Filled with the exception if one was thrown + /// The result of the work item + TResult GetResult(out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// Filled with the exception if one was thrown + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// + /// Filled with the exception if one was thrown + /// + /// + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Gets an indication whether the asynchronous operation has completed. + /// + bool IsCompleted { get; } + + /// + /// Gets an indication whether the asynchronous operation has been canceled. + /// + bool IsCanceled { get; } + + /// + /// Gets the user-defined object that contains context data + /// for the work item method. + /// + object State { get; } + + /// + /// Same as Cancel(false). + /// + bool Cancel(); + + /// + /// 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 + /// + /// When true send an AbortException to the executing thread. + /// Returns true if the work item was not completed, otherwise false. + bool Cancel(bool abortExecution); + + /// + /// Get the work item's priority + /// + WorkItemPriority WorkItemPriority { get; } + + /// + /// Return the result, same as GetResult() + /// + TResult Result { get; } + + /// + /// Returns the exception if occured otherwise returns null. + /// + object Exception { get; } + } + + #endregion + + #region .NET 3.5 + + // All these delegate are built-in .NET 3.5 + // Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity. + + public delegate void Action(); + public delegate void Action(T1 arg1, T2 arg2); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + public delegate TResult Func(); + public delegate TResult Func(T arg1); + public delegate TResult Func(T1 arg1, T2 arg2); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/InternalInterfaces.cs b/ThirdParty/SmartThreadPool/InternalInterfaces.cs index 8be7161..0072e10 100644 --- a/ThirdParty/SmartThreadPool/InternalInterfaces.cs +++ b/ThirdParty/SmartThreadPool/InternalInterfaces.cs @@ -1,27 +1,27 @@ - -namespace Amib.Threading.Internal -{ - /// - /// An internal delegate to call when the WorkItem starts or completes - /// - internal delegate void WorkItemStateCallback(WorkItem workItem); - - internal interface IInternalWorkItemResult - { - event WorkItemStateCallback OnWorkItemStarted; - event WorkItemStateCallback OnWorkItemCompleted; - } - - internal interface IInternalWaitableResult - { - /// - /// This method is intent for internal use. - /// - IWorkItemResult GetWorkItemResult(); - } - - public interface IHasWorkItemPriority - { - WorkItemPriority WorkItemPriority { get; } - } -} + +namespace Amib.Threading.Internal +{ + /// + /// An internal delegate to call when the WorkItem starts or completes + /// + internal delegate void WorkItemStateCallback(WorkItem workItem); + + internal interface IInternalWorkItemResult + { + event WorkItemStateCallback OnWorkItemStarted; + event WorkItemStateCallback OnWorkItemCompleted; + } + + internal interface IInternalWaitableResult + { + /// + /// This method is intent for internal use. + /// + IWorkItemResult GetWorkItemResult(); + } + + public interface IHasWorkItemPriority + { + WorkItemPriority WorkItemPriority { get; } + } +} diff --git a/ThirdParty/SmartThreadPool/PriorityQueue.cs b/ThirdParty/SmartThreadPool/PriorityQueue.cs index 6245fd8..409c879 100644 --- a/ThirdParty/SmartThreadPool/PriorityQueue.cs +++ b/ThirdParty/SmartThreadPool/PriorityQueue.cs @@ -1,239 +1,239 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; - -namespace Amib.Threading.Internal -{ - #region PriorityQueue class - - /// - /// PriorityQueue class - /// This class is not thread safe because we use external lock - /// - public sealed class PriorityQueue : IEnumerable - { - #region Private members - - /// - /// The number of queues, there is one for each type of priority - /// - private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; - - /// - /// Work items queues. There is one for each type of priority - /// - private readonly LinkedList[] _queues = new LinkedList[_queuesCount]; - - /// - /// The total number of work items within the queues - /// - private int _workItemsCount; - - /// - /// Use with IEnumerable interface - /// - private int _version; - - #endregion - - #region Contructor - - public PriorityQueue() - { - for(int i = 0; i < _queues.Length; ++i) - { - _queues[i] = new LinkedList(); - } - } - - #endregion - - #region Methods - - /// - /// Enqueue a work item. - /// - /// A work item - public void Enqueue(IHasWorkItemPriority workItem) - { - Debug.Assert(null != workItem); - - int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; - Debug.Assert(queueIndex >= 0); - Debug.Assert(queueIndex < _queuesCount); - - _queues[queueIndex].AddLast(workItem); - ++_workItemsCount; - ++_version; - } - - /// - /// Dequeque a work item. - /// - /// Returns the next work item - public IHasWorkItemPriority Dequeue() - { - IHasWorkItemPriority workItem = null; - - if(_workItemsCount > 0) - { - int queueIndex = GetNextNonEmptyQueue(-1); - Debug.Assert(queueIndex >= 0); - workItem = _queues[queueIndex].First.Value; - _queues[queueIndex].RemoveFirst(); - Debug.Assert(null != workItem); - --_workItemsCount; - ++_version; - } - - return workItem; - } - - /// - /// Find the next non empty queue starting at queue queueIndex+1 - /// - /// The index-1 to start from - /// - /// The index of the next non empty queue or -1 if all the queues are empty - /// - private int GetNextNonEmptyQueue(int queueIndex) - { - for(int i = queueIndex+1; i < _queuesCount; ++i) - { - if(_queues[i].Count > 0) - { - return i; - } - } - return -1; - } - - /// - /// The number of work items - /// - public int Count - { - get - { - return _workItemsCount; - } - } - - /// - /// Clear all the work items - /// - public void Clear() - { - if (_workItemsCount > 0) - { - foreach(LinkedList queue in _queues) - { - queue.Clear(); - } - _workItemsCount = 0; - ++_version; - } - } - - #endregion - - #region IEnumerable Members - - /// - /// Returns an enumerator to iterate over the work items - /// - /// Returns an enumerator - public IEnumerator GetEnumerator() - { - return new PriorityQueueEnumerator(this); - } - - #endregion - - #region PriorityQueueEnumerator - - /// - /// The class the implements the enumerator - /// - private class PriorityQueueEnumerator : IEnumerator - { - private readonly PriorityQueue _priorityQueue; - private int _version; - private int _queueIndex; - private IEnumerator _enumerator; - - public PriorityQueueEnumerator(PriorityQueue priorityQueue) - { - _priorityQueue = priorityQueue; - _version = _priorityQueue._version; - _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); - if (_queueIndex >= 0) - { - _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); - } - else - { - _enumerator = null; - } - } - - #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 + + /// + /// PriorityQueue class + /// This class is not thread safe because we use external lock + /// + public sealed class PriorityQueue : IEnumerable + { + #region Private members + + /// + /// The number of queues, there is one for each type of priority + /// + private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; + + /// + /// Work items queues. There is one for each type of priority + /// + private readonly LinkedList[] _queues = new LinkedList[_queuesCount]; + + /// + /// The total number of work items within the queues + /// + private int _workItemsCount; + + /// + /// Use with IEnumerable interface + /// + private int _version; + + #endregion + + #region Contructor + + public PriorityQueue() + { + for(int i = 0; i < _queues.Length; ++i) + { + _queues[i] = new LinkedList(); + } + } + + #endregion + + #region Methods + + /// + /// Enqueue a work item. + /// + /// A work item + public void Enqueue(IHasWorkItemPriority workItem) + { + Debug.Assert(null != workItem); + + int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; + Debug.Assert(queueIndex >= 0); + Debug.Assert(queueIndex < _queuesCount); + + _queues[queueIndex].AddLast(workItem); + ++_workItemsCount; + ++_version; + } + + /// + /// Dequeque a work item. + /// + /// Returns the next work item + public IHasWorkItemPriority Dequeue() + { + IHasWorkItemPriority workItem = null; + + if(_workItemsCount > 0) + { + int queueIndex = GetNextNonEmptyQueue(-1); + Debug.Assert(queueIndex >= 0); + workItem = _queues[queueIndex].First.Value; + _queues[queueIndex].RemoveFirst(); + Debug.Assert(null != workItem); + --_workItemsCount; + ++_version; + } + + return workItem; + } + + /// + /// Find the next non empty queue starting at queue queueIndex+1 + /// + /// The index-1 to start from + /// + /// The index of the next non empty queue or -1 if all the queues are empty + /// + private int GetNextNonEmptyQueue(int queueIndex) + { + for(int i = queueIndex+1; i < _queuesCount; ++i) + { + if(_queues[i].Count > 0) + { + return i; + } + } + return -1; + } + + /// + /// The number of work items + /// + public int Count + { + get + { + return _workItemsCount; + } + } + + /// + /// Clear all the work items + /// + public void Clear() + { + if (_workItemsCount > 0) + { + foreach(LinkedList queue in _queues) + { + queue.Clear(); + } + _workItemsCount = 0; + ++_version; + } + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator to iterate over the work items + /// + /// Returns an enumerator + public IEnumerator GetEnumerator() + { + return new PriorityQueueEnumerator(this); + } + + #endregion + + #region PriorityQueueEnumerator + + /// + /// The class the implements the enumerator + /// + private class PriorityQueueEnumerator : IEnumerator + { + private readonly PriorityQueue _priorityQueue; + private int _version; + private int _queueIndex; + private IEnumerator _enumerator; + + public PriorityQueueEnumerator(PriorityQueue priorityQueue) + { + _priorityQueue = priorityQueue; + _version = _priorityQueue._version; + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); + if (_queueIndex >= 0) + { + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + } + else + { + _enumerator = null; + } + } + + #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 +} diff --git a/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs index 1651e78..4728c1f 100644 --- a/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs +++ b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs @@ -1,23 +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 - - +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Amib.Threading")] +[assembly: AssemblyDescription("Smart Thread Pool")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Amib.Threading")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")] +[assembly: AssemblyVersion("2.2.3.0")] + +#if (_PUBLISH) +[assembly: InternalsVisibleTo("STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad")] +#else +[assembly: InternalsVisibleTo("STPTests")] +#endif + + diff --git a/ThirdParty/SmartThreadPool/SLExt.cs b/ThirdParty/SmartThreadPool/SLExt.cs index fafff19..23a60bc 100644 --- a/ThirdParty/SmartThreadPool/SLExt.cs +++ b/ThirdParty/SmartThreadPool/SLExt.cs @@ -1,16 +1,16 @@ -#if _SILVERLIGHT - -using System.Threading; - -namespace Amib.Threading -{ - public enum ThreadPriority - { - Lowest, - BelowNormal, - Normal, - AboveNormal, - Highest, - } -} -#endif +#if _SILVERLIGHT + +using System.Threading; + +namespace Amib.Threading +{ + public enum ThreadPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } +} +#endif diff --git a/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs index 3b92645..9b17f69 100644 --- a/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs +++ b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs @@ -1,62 +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 - -} - +#if !(_WINDOWS_CE) + +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ +#if _WINDOWS || WINDOWS_PHONE + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout); + } + } +#else + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + } +#endif + +} + #endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs index 2508661..0663d1d 100644 --- a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs +++ b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs @@ -1,448 +1,448 @@ -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 description for STPPerformanceCounter. - /// - internal class STPPerformanceCounter - { - // Fields - private readonly PerformanceCounterType _pcType; - protected string _counterHelp; - protected string _counterName; - - // Methods - public STPPerformanceCounter( - string counterName, - string counterHelp, - PerformanceCounterType pcType) - { - _counterName = counterName; - _counterHelp = counterHelp; - _pcType = pcType; - } - - public void AddCounterToCollection(CounterCreationDataCollection counterData) - { - CounterCreationData counterCreationData = new CounterCreationData( - _counterName, - _counterHelp, - _pcType); - - counterData.Add(counterCreationData); - } - - // Properties - public string Name - { - get - { - return _counterName; - } - } - } - - internal class STPPerformanceCounters - { - // Fields - internal STPPerformanceCounter[] _stpPerformanceCounters; - private static readonly STPPerformanceCounters _instance; - internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; - internal const string _stpCategoryName = "SmartThreadPool"; - - // Methods - static STPPerformanceCounters() - { - _instance = new STPPerformanceCounters(); - } - - private STPPerformanceCounters() - { - STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] - { - new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), - new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), - - new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), - new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), - - new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), - new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), - - new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), - new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), - - new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), - new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), - - new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), - }; - - _stpPerformanceCounters = stpPerformanceCounters; - SetupCategory(); - } - - private void SetupCategory() - { - if (!PerformanceCounterCategory.Exists(_stpCategoryName)) - { - CounterCreationDataCollection counters = new CounterCreationDataCollection(); - - for (int i = 0; i < _stpPerformanceCounters.Length; i++) - { - _stpPerformanceCounters[i].AddCounterToCollection(counters); - } - - PerformanceCounterCategory.Create( - _stpCategoryName, - _stpCategoryHelp, - PerformanceCounterCategoryType.MultiInstance, - counters); - - } - } - - // Properties - public static STPPerformanceCounters Instance - { - get - { - return _instance; - } - } - } - - internal class STPInstancePerformanceCounter : IDisposable - { - // Fields - private bool _isDisposed; - private PerformanceCounter _pcs; - - // Methods - protected STPInstancePerformanceCounter() - { - _isDisposed = false; - } - - public STPInstancePerformanceCounter( - string instance, - STPPerformanceCounterType spcType) : this() - { - STPPerformanceCounters counters = STPPerformanceCounters.Instance; - _pcs = new PerformanceCounter( - STPPerformanceCounters._stpCategoryName, - counters._stpPerformanceCounters[(int) spcType].Name, - instance, - false); - _pcs.RawValue = _pcs.RawValue; - } - - - public void Close() - { - if (_pcs != null) - { - _pcs.RemoveInstance(); - _pcs.Close(); - _pcs = null; - } - } - - public void Dispose() - { - Dispose(true); - } - - public virtual void Dispose(bool disposing) - { - 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 - } - } -} +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 description for STPPerformanceCounter. + /// + internal class STPPerformanceCounter + { + // Fields + private readonly PerformanceCounterType _pcType; + protected string _counterHelp; + protected string _counterName; + + // Methods + public STPPerformanceCounter( + string counterName, + string counterHelp, + PerformanceCounterType pcType) + { + _counterName = counterName; + _counterHelp = counterHelp; + _pcType = pcType; + } + + public void AddCounterToCollection(CounterCreationDataCollection counterData) + { + CounterCreationData counterCreationData = new CounterCreationData( + _counterName, + _counterHelp, + _pcType); + + counterData.Add(counterCreationData); + } + + // Properties + public string Name + { + get + { + return _counterName; + } + } + } + + internal class STPPerformanceCounters + { + // Fields + internal STPPerformanceCounter[] _stpPerformanceCounters; + private static readonly STPPerformanceCounters _instance; + internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; + internal const string _stpCategoryName = "SmartThreadPool"; + + // Methods + static STPPerformanceCounters() + { + _instance = new STPPerformanceCounters(); + } + + private STPPerformanceCounters() + { + STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] + { + new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), + new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), + + new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), + + new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), + new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), + + new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), + }; + + _stpPerformanceCounters = stpPerformanceCounters; + SetupCategory(); + } + + private void SetupCategory() + { + if (!PerformanceCounterCategory.Exists(_stpCategoryName)) + { + CounterCreationDataCollection counters = new CounterCreationDataCollection(); + + for (int i = 0; i < _stpPerformanceCounters.Length; i++) + { + _stpPerformanceCounters[i].AddCounterToCollection(counters); + } + + PerformanceCounterCategory.Create( + _stpCategoryName, + _stpCategoryHelp, + PerformanceCounterCategoryType.MultiInstance, + counters); + + } + } + + // Properties + public static STPPerformanceCounters Instance + { + get + { + return _instance; + } + } + } + + internal class STPInstancePerformanceCounter : IDisposable + { + // Fields + private bool _isDisposed; + private PerformanceCounter _pcs; + + // Methods + protected STPInstancePerformanceCounter() + { + _isDisposed = false; + } + + public STPInstancePerformanceCounter( + string instance, + STPPerformanceCounterType spcType) : this() + { + STPPerformanceCounters counters = STPPerformanceCounters.Instance; + _pcs = new PerformanceCounter( + STPPerformanceCounters._stpCategoryName, + counters._stpPerformanceCounters[(int) spcType].Name, + instance, + false); + _pcs.RawValue = _pcs.RawValue; + } + + + public void Close() + { + if (_pcs != null) + { + _pcs.RemoveInstance(); + _pcs.Close(); + _pcs = null; + } + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool disposing) + { + 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 + } + } +} diff --git a/ThirdParty/SmartThreadPool/STPStartInfo.cs b/ThirdParty/SmartThreadPool/STPStartInfo.cs index 2ec8dc6..96fa094 100644 --- a/ThirdParty/SmartThreadPool/STPStartInfo.cs +++ b/ThirdParty/SmartThreadPool/STPStartInfo.cs @@ -1,212 +1,212 @@ -using System; -using System.Threading; - -namespace Amib.Threading -{ - /// - /// Summary description for STPStartInfo. - /// - 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 - } - - /// - /// Get/Set the idle timeout in milliseconds. - /// If a thread is idle (starved) longer than IdleTimeout then it may quit. - /// - public virtual int IdleTimeout - { - get { return _idleTimeout; } - set - { - ThrowIfReadOnly(); - _idleTimeout = value; - } - } - - - /// - /// Get/Set the lower limit of threads in the pool. - /// - public virtual int MinWorkerThreads - { - get { return _minWorkerThreads; } - set - { - ThrowIfReadOnly(); - _minWorkerThreads = value; - } - } - - - /// - /// Get/Set the upper limit of threads in the pool. - /// - public virtual int MaxWorkerThreads - { - get { return _maxWorkerThreads; } - set - { - ThrowIfReadOnly(); - _maxWorkerThreads = value; - } - } - -#if !(WINDOWS_PHONE) - /// - /// Get/Set the scheduling priority of the threads in the pool. - /// The Os handles the scheduling. - /// - public virtual ThreadPriority ThreadPriority - { - get { return _threadPriority; } - set - { - ThrowIfReadOnly(); - _threadPriority = value; - } - } -#endif - /// - /// Get/Set the thread pool name. Threads will get names depending on this. - /// - public virtual string ThreadPoolName { - get { return _threadPoolName; } - set - { - ThrowIfReadOnly (); - _threadPoolName = value; - } - } - - /// - /// Get/Set the performance counter instance name of this SmartThreadPool - /// The default is null which indicate not to use performance counters at all. - /// - public virtual string PerformanceCounterInstanceName - { - get { return _performanceCounterInstanceName; } - set - { - ThrowIfReadOnly(); - _performanceCounterInstanceName = value; - } - } - - /// - /// Enable/Disable the local performance counter. - /// This enables the user to get some performance information about the SmartThreadPool - /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.) - /// The default is false. - /// - public virtual bool EnableLocalPerformanceCounters - { - get { return _enableLocalPerformanceCounters; } - set - { - ThrowIfReadOnly(); - _enableLocalPerformanceCounters = value; - } - } - - /// - /// Get/Set backgroundness of thread in thread pool. - /// - public virtual bool AreThreadsBackground - { - get { return _areThreadsBackground; } - set - { - ThrowIfReadOnly (); - _areThreadsBackground = value; - } - } - - /// - /// Get a readonly version of this STPStartInfo. - /// - /// Returns a readonly reference to this STPStartInfo - public new STPStartInfo AsReadOnly() - { - return new STPStartInfo(this) { _readOnly = true }; - } - -#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) - - private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState; - - /// - /// Get/Set the apartment state of threads in the thread pool - /// - public ApartmentState ApartmentState - { - get { return _apartmentState; } - set - { - ThrowIfReadOnly(); - _apartmentState = value; - } - } - -#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) - - /// - /// Get/Set the max stack size of threads in the thread pool - /// - public int? MaxStackSize - { - get { return _maxStackSize; } - set - { - ThrowIfReadOnly(); - if (value.HasValue && value.Value < 0) - { - throw new ArgumentOutOfRangeException("value", "Value must be greater than 0."); - } - _maxStackSize = value; - } - } -#endif - -#endif - } -} +using System; +using System.Threading; + +namespace Amib.Threading +{ + /// + /// Summary description for STPStartInfo. + /// + 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 + } + + /// + /// Get/Set the idle timeout in milliseconds. + /// If a thread is idle (starved) longer than IdleTimeout then it may quit. + /// + public virtual int IdleTimeout + { + get { return _idleTimeout; } + set + { + ThrowIfReadOnly(); + _idleTimeout = value; + } + } + + + /// + /// Get/Set the lower limit of threads in the pool. + /// + public virtual int MinWorkerThreads + { + get { return _minWorkerThreads; } + set + { + ThrowIfReadOnly(); + _minWorkerThreads = value; + } + } + + + /// + /// Get/Set the upper limit of threads in the pool. + /// + public virtual int MaxWorkerThreads + { + get { return _maxWorkerThreads; } + set + { + ThrowIfReadOnly(); + _maxWorkerThreads = value; + } + } + +#if !(WINDOWS_PHONE) + /// + /// Get/Set the scheduling priority of the threads in the pool. + /// The Os handles the scheduling. + /// + public virtual ThreadPriority ThreadPriority + { + get { return _threadPriority; } + set + { + ThrowIfReadOnly(); + _threadPriority = value; + } + } +#endif + /// + /// Get/Set the thread pool name. Threads will get names depending on this. + /// + public virtual string ThreadPoolName { + get { return _threadPoolName; } + set + { + ThrowIfReadOnly (); + _threadPoolName = value; + } + } + + /// + /// Get/Set the performance counter instance name of this SmartThreadPool + /// The default is null which indicate not to use performance counters at all. + /// + public virtual string PerformanceCounterInstanceName + { + get { return _performanceCounterInstanceName; } + set + { + ThrowIfReadOnly(); + _performanceCounterInstanceName = value; + } + } + + /// + /// Enable/Disable the local performance counter. + /// This enables the user to get some performance information about the SmartThreadPool + /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.) + /// The default is false. + /// + public virtual bool EnableLocalPerformanceCounters + { + get { return _enableLocalPerformanceCounters; } + set + { + ThrowIfReadOnly(); + _enableLocalPerformanceCounters = value; + } + } + + /// + /// Get/Set backgroundness of thread in thread pool. + /// + public virtual bool AreThreadsBackground + { + get { return _areThreadsBackground; } + set + { + ThrowIfReadOnly (); + _areThreadsBackground = value; + } + } + + /// + /// Get a readonly version of this STPStartInfo. + /// + /// Returns a readonly reference to this STPStartInfo + public new STPStartInfo AsReadOnly() + { + return new STPStartInfo(this) { _readOnly = true }; + } + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState; + + /// + /// Get/Set the apartment state of threads in the thread pool + /// + public ApartmentState ApartmentState + { + get { return _apartmentState; } + set + { + ThrowIfReadOnly(); + _apartmentState = value; + } + } + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + /// + /// Get/Set the max stack size of threads in the thread pool + /// + public int? MaxStackSize + { + get { return _maxStackSize; } + set + { + ThrowIfReadOnly(); + if (value.HasValue && value.Value < 0) + { + throw new ArgumentOutOfRangeException("value", "Value must be greater than 0."); + } + _maxStackSize = value; + } + } +#endif + +#endif + } +} diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs index ba7d73f..d9502bb 100644 --- a/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs @@ -1,60 +1,60 @@ - -using System; -using Amib.Threading.Internal; - -namespace Amib.Threading -{ - public partial class SmartThreadPool - { - #region ThreadEntry class - - internal class ThreadEntry - { - /// - /// The thread creation time - /// The value is stored as UTC value. - /// - private readonly DateTime _creationTime; - - /// - /// The last time this thread has been running - /// It is updated by IAmAlive() method - /// The value is stored as UTC value. - /// - private DateTime _lastAliveTime; - - /// - /// A reference from each thread in the thread pool to its SmartThreadPool - /// object container. - /// With this variable a thread can know whatever it belongs to a - /// SmartThreadPool. - /// - private readonly SmartThreadPool _associatedSmartThreadPool; - - /// - /// A reference to the current work item a thread from the thread pool - /// is executing. - /// - public WorkItem CurrentWorkItem { get; set; } - - public ThreadEntry(SmartThreadPool stp) - { - _associatedSmartThreadPool = stp; - _creationTime = DateTime.UtcNow; - _lastAliveTime = DateTime.MinValue; - } - - public SmartThreadPool AssociatedSmartThreadPool - { - get { return _associatedSmartThreadPool; } - } - - public void IAmAlive() - { - _lastAliveTime = DateTime.UtcNow; - } - } - - #endregion - } + +using System; +using Amib.Threading.Internal; + +namespace Amib.Threading +{ + public partial class SmartThreadPool + { + #region ThreadEntry class + + internal class ThreadEntry + { + /// + /// The thread creation time + /// The value is stored as UTC value. + /// + private readonly DateTime _creationTime; + + /// + /// The last time this thread has been running + /// It is updated by IAmAlive() method + /// The value is stored as UTC value. + /// + private DateTime _lastAliveTime; + + /// + /// A reference from each thread in the thread pool to its SmartThreadPool + /// object container. + /// With this variable a thread can know whatever it belongs to a + /// SmartThreadPool. + /// + private readonly SmartThreadPool _associatedSmartThreadPool; + + /// + /// A reference to the current work item a thread from the thread pool + /// is executing. + /// + public WorkItem CurrentWorkItem { get; set; } + + public ThreadEntry(SmartThreadPool stp) + { + _associatedSmartThreadPool = stp; + _creationTime = DateTime.UtcNow; + _lastAliveTime = DateTime.MinValue; + } + + public SmartThreadPool AssociatedSmartThreadPool + { + get { return _associatedSmartThreadPool; } + } + + public void IAmAlive() + { + _lastAliveTime = DateTime.UtcNow; + } + } + + #endregion + } } \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs index 9256777..a4f4ce5 100644 --- a/ThirdParty/SmartThreadPool/SmartThreadPool.cs +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs @@ -1,1732 +1,1732 @@ -#region Release History - -// Smart Thread Pool -// 7 Aug 2004 - Initial release -// -// 14 Sep 2004 - Bug fixes -// -// 15 Oct 2004 - Added new features -// - Work items return result. -// - Support waiting synchronization for multiple work items. -// - Work items can be cancelled. -// - Passage of the caller thread’s context to the thread in the pool. -// - Minimal usage of WIN32 handles. -// - Minor bug fixes. -// -// 26 Dec 2004 - Changes: -// - Removed static constructors. -// - Added finalizers. -// - Changed Exceptions so they are serializable. -// - Fixed the bug in one of the SmartThreadPool constructors. -// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. -// The SmartThreadPool.WaitAny() is still limited by the .NET Framework. -// - Added PostExecute with options on which cases to call it. -// - Added option to dispose of the state objects. -// - Added a WaitForIdle() method that waits until the work items queue is empty. -// - Added an STPStartInfo class for the initialization of the thread pool. -// - Changed exception handling so if a work item throws an exception it -// is rethrown at GetResult(), rather then firing an UnhandledException event. -// Note that PostExecute exception are always ignored. -// -// 25 Mar 2005 - Changes: -// - Fixed lost of work items bug -// -// 3 Jul 2005: Changes. -// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. -// -// 16 Aug 2005: Changes. -// - Fixed bug where the InUseThreads becomes negative when canceling work items. -// -// 31 Jan 2006 - Changes: -// - Added work items priority -// - Removed support of chained delegates in callbacks and post executes (nobody really use this) -// - Added work items groups -// - Added work items groups idle event -// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array -// it returns true rather then throwing an exception. -// - Added option to start the STP and the WIG as suspended -// - Exception behavior changed, the real exception is returned by an -// inner exception -// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) -// - Added performance counters -// - Added priority to the threads in the pool -// -// 13 Feb 2006 - Changes: -// - Added a call to the dispose of the Performance Counter so -// their won't be a Performance Counter leak. -// - Added exception catch in case the Performance Counters cannot -// be created. -// -// 17 May 2008 - Changes: -// - Changed the dispose behavior and removed the Finalizers. -// - Enabled the change of the MaxThreads and MinThreads at run time. -// - Enabled the change of the Concurrency of a IWorkItemsGroup at run -// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency -// refers to the MaxThreads. -// - Improved the cancel behavior. -// - Added events for thread creation and termination. -// - Fixed the HttpContext context capture. -// - Changed internal collections so they use generic collections -// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup -// - Added support for WinCE -// - Added support for Action and Func -// -// 07 April 2009 - Changes: -// - Added support for Silverlight and Mono -// - Added Join, Choice, and Pipe to SmartThreadPool. -// - Added local performance counters (for Mono, Silverlight, and WindowsCE) -// - Changed duration measures from DateTime.Now to Stopwatch. -// - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList. -// -// 21 December 2009 - Changes: -// - Added work item timeout (passive) -// -// 20 August 2012 - Changes: -// - Added set name to threads -// - Fixed the WorkItemsQueue.Dequeue. -// Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... } -// - Fixed SmartThreadPool.Pipe -// - Added IsBackground option to threads -// - Added ApartmentState to threads -// - Fixed thread creation when queuing many work items at the same time. -// -// 24 August 2012 - Changes: -// - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan -// - Added option to set MaxStackSize of threads - -#endregion - -using System; -using System.Security; -using System.Threading; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -using Amib.Threading.Internal; - -namespace Amib.Threading -{ - #region SmartThreadPool class - /// - /// Smart thread pool class. - /// - public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable - { - #region Public Default Constants - - /// - /// Default minimum number of threads the thread pool contains. (0) - /// - public const int DefaultMinWorkerThreads = 0; - - /// - /// Default maximum number of threads the thread pool contains. (25) - /// - public const int DefaultMaxWorkerThreads = 25; - - /// - /// Default idle timeout in milliseconds. (One minute) - /// - public const int DefaultIdleTimeout = 60*1000; // One minute - - /// - /// Indicate to copy the security context of the caller and then use it in the call. (false) - /// - public const bool DefaultUseCallerCallContext = false; - - /// - /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) - /// - public const bool DefaultUseCallerHttpContext = false; - - /// - /// Indicate to dispose of the state objects if they support the IDispose interface. (false) - /// - public const bool DefaultDisposeOfStateObjects = false; - - /// - /// The default option to run the post execute (CallToPostExecute.Always) - /// - public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; - - /// - /// The default post execute method to run. (None) - /// When null it means not to call it. - /// - public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback; - - /// - /// The default work item priority (WorkItemPriority.Normal) - /// - public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; - - /// - /// The default is to work on work items as soon as they arrive - /// and not to wait for the start. (false) - /// - public const bool DefaultStartSuspended = false; - - /// - /// The default name to use for the performance counters instance. (null) - /// - public static readonly string DefaultPerformanceCounterInstanceName; - -#if !(WINDOWS_PHONE) - - /// - /// The default thread priority (ThreadPriority.Normal) - /// - public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; -#endif - /// - /// The default thread pool name. (SmartThreadPool) - /// - public const string DefaultThreadPoolName = "SmartThreadPool"; - - /// - /// The default Max Stack Size. (SmartThreadPool) - /// - public static readonly int? DefaultMaxStackSize = null; - - /// - /// The default fill state with params. (false) - /// It is relevant only to QueueWorkItem of Action<...>/Func<...> - /// - public const bool DefaultFillStateWithArgs = false; - - /// - /// The default thread backgroundness. (true) - /// - public const bool DefaultAreThreadsBackground = true; - -#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) - /// - /// The default apartment state of a thread in the thread pool. - /// The default is ApartmentState.Unknown which means the STP will not - /// set the apartment of the thread. It will use the .NET default. - /// - public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; -#endif - - #endregion - - #region Member Variables - - /// - /// Dictionary of all the threads in the thread pool. - /// - private readonly SynchronizedDictionary _workerThreads = new SynchronizedDictionary(); - - /// - /// Queue of work items. - /// - private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); - - /// - /// Count the work items handled. - /// Used by the performance counter. - /// - private int _workItemsProcessed; - - /// - /// Number of threads that currently work (not idle). - /// - private int _inUseWorkerThreads; - - /// - /// Stores a copy of the original STPStartInfo. - /// It is used to change the MinThread and MaxThreads - /// - private STPStartInfo _stpStartInfo; - - /// - /// Total number of work items that are stored in the work items queue - /// plus the work items that the threads in the pool are working on. - /// - private int _currentWorkItemsCount; - - /// - /// Signaled when the thread pool is idle, i.e. no thread is busy - /// and the work items queue is empty - /// - //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); - private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); - - /// - /// An event to signal all the threads to quit immediately. - /// - //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); - private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false); - - /// - /// A flag to indicate if the Smart Thread Pool is now suspended. - /// - private bool _isSuspended; - - /// - /// A flag to indicate the threads to quit. - /// - private bool _shutdown; - - /// - /// Counts the threads created in the pool. - /// It is used to name the threads. - /// - private int _threadCounter; - - /// - /// Indicate that the SmartThreadPool has been disposed - /// - private bool _isDisposed; - - /// - /// Holds all the WorkItemsGroup instaces that have at least one - /// work item int the SmartThreadPool - /// This variable is used in case of Shutdown - /// - private readonly SynchronizedDictionary _workItemsGroups = new SynchronizedDictionary(); - - /// - /// A common object for all the work items int the STP - /// so we can mark them to cancel in O(1) - /// - private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup(); - - /// - /// Windows STP performance counters - /// - private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance; - - /// - /// Local STP performance counters - /// - private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance; - - -#if (WINDOWS_PHONE) - private static readonly Dictionary _threadEntries = new Dictionary(); -#elif (_WINDOWS_CE) - private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot(); -#else - [ThreadStatic] - private static ThreadEntry _threadEntry; - -#endif - - /// - /// An event to call after a thread is created, but before - /// it's first use. - /// - private event ThreadInitializationHandler _onThreadInitialization; - - /// - /// An event to call when a thread is about to exit, after - /// it is no longer belong to the pool. - /// - private event ThreadTerminationHandler _onThreadTermination; - - #endregion - - #region Per thread properties - - /// - /// A reference to the current work item a thread from the thread pool - /// is executing. - /// - internal static ThreadEntry CurrentThreadEntry - { -#if (WINDOWS_PHONE) - get - { - lock(_threadEntries) - { - ThreadEntry threadEntry; - if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry)) - { - return threadEntry; - } - } - return null; - } - set - { - lock(_threadEntries) - { - _threadEntries[Thread.CurrentThread.ManagedThreadId] = value; - } - } -#elif (_WINDOWS_CE) - get - { - //Thread.CurrentThread.ManagedThreadId - return Thread.GetData(_threadEntrySlot) as ThreadEntry; - } - set - { - Thread.SetData(_threadEntrySlot, value); - } -#else - get - { - return _threadEntry; - } - set - { - _threadEntry = value; - } -#endif - } - #endregion - - #region Construction and Finalization - - /// - /// Constructor - /// - public SmartThreadPool() - { - _stpStartInfo = new STPStartInfo(); - Initialize(); - } - - /// - /// Constructor - /// - /// Idle timeout in milliseconds - public SmartThreadPool(int idleTimeout) - { - _stpStartInfo = new STPStartInfo - { - IdleTimeout = idleTimeout, - }; - Initialize(); - } - - /// - /// Constructor - /// - /// Idle timeout in milliseconds - /// Upper limit of threads in the pool - public SmartThreadPool( - int idleTimeout, - int maxWorkerThreads) - { - _stpStartInfo = new STPStartInfo - { - IdleTimeout = idleTimeout, - MaxWorkerThreads = maxWorkerThreads, - }; - Initialize(); - } - - /// - /// Constructor - /// - /// Idle timeout in milliseconds - /// Upper limit of threads in the pool - /// Lower limit of threads in the pool - public SmartThreadPool( - int idleTimeout, - int maxWorkerThreads, - int minWorkerThreads) - { - _stpStartInfo = new STPStartInfo - { - IdleTimeout = idleTimeout, - MaxWorkerThreads = maxWorkerThreads, - MinWorkerThreads = minWorkerThreads, - }; - Initialize(); - } - - /// - /// Constructor - /// - /// A SmartThreadPool configuration that overrides the default behavior - public SmartThreadPool(STPStartInfo stpStartInfo) - { - _stpStartInfo = new STPStartInfo(stpStartInfo); - Initialize(); - } - - private void Initialize() - { - Name = _stpStartInfo.ThreadPoolName; - ValidateSTPStartInfo(); - - // _stpStartInfoRW stores a read/write copy of the STPStartInfo. - // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten - - _isSuspended = _stpStartInfo.StartSuspended; - -#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE) - if (null != _stpStartInfo.PerformanceCounterInstanceName) - { - throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); - } -#else - if (null != _stpStartInfo.PerformanceCounterInstanceName) - { - try - { - _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); - } - catch (Exception e) - { - Debug.WriteLine("Unable to create Performance Counters: " + e); - _windowsPCs = NullSTPInstancePerformanceCounters.Instance; - } - } -#endif - - if (_stpStartInfo.EnableLocalPerformanceCounters) - { - _localPCs = new LocalSTPInstancePerformanceCounters(); - } - - // If the STP is not started suspended then start the threads. - if (!_isSuspended) - { - StartOptimalNumberOfThreads(); - } - } - - private void StartOptimalNumberOfThreads() - { - int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); - threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); - threadsCount -= _workerThreads.Count; - if (threadsCount > 0) - { - StartThreads(threadsCount); - } - } - - private void ValidateSTPStartInfo() - { - if (_stpStartInfo.MinWorkerThreads < 0) - { - throw new ArgumentOutOfRangeException( - "MinWorkerThreads", "MinWorkerThreads cannot be negative"); - } - - if (_stpStartInfo.MaxWorkerThreads <= 0) - { - throw new ArgumentOutOfRangeException( - "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); - } - - if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) - { - throw new ArgumentOutOfRangeException( - "MinWorkerThreads, maxWorkerThreads", - "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); - } - } - - private static void ValidateCallback(Delegate callback) - { - if(callback.GetInvocationList().Length > 1) - { - throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); - } - } - - #endregion - - #region Thread Processing - - /// - /// Waits on the queue for a work item, shutdown, or timeout. - /// - /// - /// Returns the WaitingCallback or null in case of timeout or shutdown. - /// - private WorkItem Dequeue() - { - WorkItem workItem = - _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); - - return workItem; - } - - /// - /// Put a new work item in the queue - /// - /// A work item to queue - internal override void Enqueue(WorkItem workItem) - { - // Make sure the workItem is not null - Debug.Assert(null != workItem); - - IncrementWorkItemsCount(); - - workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; - _workItemsQueue.EnqueueWorkItem(workItem); - workItem.WorkItemIsQueued(); - - // If all the threads are busy then try to create a new one - if (_currentWorkItemsCount > _workerThreads.Count) - { - StartThreads(1); - } - } - - private void IncrementWorkItemsCount() - { - _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - - int count = Interlocked.Increment(ref _currentWorkItemsCount); - //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); - if (count == 1) - { - IsIdle = false; - _isIdleWaitHandle.Reset(); - } - } - - private void DecrementWorkItemsCount() - { - int count = Interlocked.Decrement(ref _currentWorkItemsCount); - //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); - if (count == 0) - { - IsIdle = true; - _isIdleWaitHandle.Set(); - } - - Interlocked.Increment(ref _workItemsProcessed); - - if (!_shutdown) - { - // The counter counts even if the work item was cancelled - _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); - } - - } - - internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) - { - _workItemsGroups[workItemsGroup] = workItemsGroup; - } - - internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) - { - if (_workItemsGroups.Contains(workItemsGroup)) - { - _workItemsGroups.Remove(workItemsGroup); - } - } - - /// - /// Inform that the current thread is about to quit or quiting. - /// The same thread may call this method more than once. - /// - private void InformCompleted() - { - // There is no need to lock the two methods together - // since only the current thread removes itself - // and the _workerThreads is a synchronized dictionary - if (_workerThreads.Contains(Thread.CurrentThread)) - { - _workerThreads.Remove(Thread.CurrentThread); - _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - } - } - - /// - /// Starts new threads - /// - /// The number of threads to start - private void StartThreads(int threadsCount) - { - if (_isSuspended) - { - return; - } - - lock(_workerThreads.SyncRoot) - { - // Don't start threads on shut down - if (_shutdown) - { - return; - } - - for(int i = 0; i < threadsCount; ++i) - { - // Don't create more threads then the upper limit - if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) - { - return; - } - - // Create a new thread - -#if (_SILVERLIGHT) || (WINDOWS_PHONE) - Thread workerThread = new Thread(ProcessQueuedItems); -#else - Thread workerThread = - _stpStartInfo.MaxStackSize.HasValue - ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) - : new Thread(ProcessQueuedItems); -#endif - // Configure the new thread and start it - workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; - workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; - -#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE) - if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) - { - workerThread.SetApartmentState(_stpStartInfo.ApartmentState); - } -#endif - -#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) - workerThread.Priority = _stpStartInfo.ThreadPriority; -#endif - workerThread.Start(); - ++_threadCounter; - - // Add it to the dictionary and update its creation time. - _workerThreads[workerThread] = new ThreadEntry(this); - - _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); - } - } - } - - /// - /// A worker thread method that processes work items from the work items queue. - /// - private void ProcessQueuedItems() - { - // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks - // of the dictionary. - CurrentThreadEntry = _workerThreads[Thread.CurrentThread]; - - FireOnThreadInitialization(); - - try - { - bool bInUseWorkerThreadsWasIncremented = false; - - // Process until shutdown. - while(!_shutdown) - { - // Update the last time this thread was seen alive. - // It's good for debugging. - CurrentThreadEntry.IAmAlive(); - - // The following block handles the when the MaxWorkerThreads has been - // incremented by the user at run-time. - // Double lock for quit. - if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) - { - lock (_workerThreads.SyncRoot) - { - if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) - { - // Inform that the thread is quiting and then quit. - // This method must be called within this lock or else - // more threads will quit and the thread pool will go - // below the lower limit. - InformCompleted(); - break; - } - } - } - - // Wait for a work item, shutdown, or timeout - WorkItem workItem = Dequeue(); - - // Update the last time this thread was seen alive. - // It's good for debugging. - CurrentThreadEntry.IAmAlive(); - - // On timeout or shut down. - if (null == workItem) - { - // Double lock for quit. - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) - { - lock(_workerThreads.SyncRoot) - { - if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) - { - // Inform that the thread is quiting and then quit. - // This method must be called within this lock or else - // more threads will quit and the thread pool will go - // below the lower limit. - InformCompleted(); - break; - } - } - } - } - - // If we didn't quit then skip to the next iteration. - if (null == workItem) - { - continue; - } - - try - { - // Initialize the value to false - bInUseWorkerThreadsWasIncremented = false; - - // Set the Current Work Item of the thread. - // Store the Current Work Item before the workItem.StartingWorkItem() is called, - // so WorkItem.Cancel can work when the work item is between InQueue and InProgress - // states. - // If the work item has been cancelled BEFORE the workItem.StartingWorkItem() - // (work item is in InQueue state) then workItem.StartingWorkItem() will return false. - // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then - // (work item is in InProgress state) then the thread will be aborted - CurrentThreadEntry.CurrentWorkItem = workItem; - - // Change the state of the work item to 'in progress' if possible. - // We do it here so if the work item has been canceled we won't - // increment the _inUseWorkerThreads. - // The cancel mechanism doesn't delete items from the queue, - // it marks the work item as canceled, and when the work item - // is dequeued, we just skip it. - // If the post execute of work item is set to always or to - // call when the work item is canceled then the StartingWorkItem() - // will return true, so the post execute can run. - if (!workItem.StartingWorkItem()) - { - continue; - } - - // Execute the callback. Make sure to accurately - // record how many callbacks are currently executing. - int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); - _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - - // Mark that the _inUseWorkerThreads incremented, so in the finally{} - // statement we will decrement it correctly. - bInUseWorkerThreadsWasIncremented = true; - - workItem.FireWorkItemStarted(); - - ExecuteWorkItem(workItem); - } - catch(Exception ex) - { - ex.GetHashCode(); - // Do nothing - } - finally - { - workItem.DisposeOfState(); - - // Set the CurrentWorkItem to null, since we - // no longer run user's code. - CurrentThreadEntry.CurrentWorkItem = null; - - // Decrement the _inUseWorkerThreads only if we had - // incremented it. Note the cancelled work items don't - // increment _inUseWorkerThreads. - if (bInUseWorkerThreadsWasIncremented) - { - int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); - _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); - } - - // Notify that the work item has been completed. - // WorkItemsGroup may enqueue their next work item. - workItem.FireWorkItemCompleted(); - - // Decrement the number of work items here so the idle - // ManualResetEvent won't fluctuate. - DecrementWorkItemsCount(); - } - } - } - catch(ThreadAbortException tae) - { - tae.GetHashCode(); - // Handle the abort exception gracfully. -#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) - Thread.ResetAbort(); -#endif - } - catch(Exception e) - { - Debug.Assert(null != e); - } - finally - { - InformCompleted(); - FireOnThreadTermination(); - } - } - - private void ExecuteWorkItem(WorkItem workItem) - { - _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); - _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); - try - { - workItem.Execute(); - } - finally - { - _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); - _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); - } - } - - - #endregion - - #region Public Methods - - private void ValidateWaitForIdle() - { - if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this) - { - throw new NotSupportedException( - "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); - } - } - - internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) - { - if (null == CurrentThreadEntry) - { - return; - } - - WorkItem workItem = CurrentThreadEntry.CurrentWorkItem; - ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem); - if ((null != workItemsGroup) && - (null != workItem) && - CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup)) - { - throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) - { - if ((null != workItemsGroup) && - (null != workItem) && - workItem.WasQueuedBy(workItemsGroup)) - { - throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); - } - } - - /// - /// Force the SmartThreadPool to shutdown - /// - public void Shutdown() - { - Shutdown(true, 0); - } - - /// - /// Force the SmartThreadPool to shutdown with timeout - /// - public void Shutdown(bool forceAbort, TimeSpan timeout) - { - Shutdown(forceAbort, (int)timeout.TotalMilliseconds); - } - - /// - /// Empties the queue of work items and abort the threads in the pool. - /// - public void Shutdown(bool forceAbort, int millisecondsTimeout) - { - ValidateNotDisposed(); - - ISTPInstancePerformanceCounters pcs = _windowsPCs; - - if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs) - { - // Set the _pcs to "null" to stop updating the performance - // counters - _windowsPCs = NullSTPInstancePerformanceCounters.Instance; - - pcs.Dispose(); - } - - Thread [] threads; - lock(_workerThreads.SyncRoot) - { - // Shutdown the work items queue - _workItemsQueue.Dispose(); - - // Signal the threads to exit - _shutdown = true; - _shuttingDownEvent.Set(); - - // Make a copy of the threads' references in the pool - threads = new Thread [_workerThreads.Count]; - _workerThreads.Keys.CopyTo(threads, 0); - } - - int millisecondsLeft = millisecondsTimeout; - Stopwatch stopwatch = Stopwatch.StartNew(); - //DateTime start = DateTime.UtcNow; - bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); - bool timeout = false; - - // Each iteration we update the time left for the timeout. - foreach(Thread thread in threads) - { - // Join don't work with negative numbers - if (!waitInfinitely && (millisecondsLeft < 0)) - { - timeout = true; - break; - } - - // Wait for the thread to terminate - bool success = thread.Join(millisecondsLeft); - if(!success) - { - timeout = true; - break; - } - - if(!waitInfinitely) - { - // Update the time left to wait - //TimeSpan ts = DateTime.UtcNow - start; - millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; - } - } - - if (timeout && forceAbort) - { - // Abort the threads in the pool - foreach(Thread thread in threads) - { - - if ((thread != null) -#if !(_WINDOWS_CE) - && thread.IsAlive -#endif - ) - { - try - { - thread.Abort(); // Shutdown - } - catch(SecurityException e) - { - e.GetHashCode(); - } - catch(ThreadStateException ex) - { - ex.GetHashCode(); - // In case the thread has been terminated - // after the check if it is alive. - } - } - } - } - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWaitableResult [] waitableResults) - { - return WaitAll(waitableResults, Timeout.Infinite, true); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWaitableResult [] waitableResults, - TimeSpan timeout, - bool exitContext) - { - return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWaitableResult[] waitableResults, - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWaitableResult [] waitableResults, - int millisecondsTimeout, - bool exitContext) - { - return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null); - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// true when every work item in workItemResults has completed; otherwise false. - /// - public static bool WaitAll( - IWaitableResult[] waitableResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); - } - - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. - /// - public static int WaitAny( - IWaitableResult [] waitableResults) - { - return WaitAny(waitableResults, Timeout.Infinite, true); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWaitableResult[] waitableResults, - TimeSpan timeout, - bool exitContext) - { - return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWaitableResult [] waitableResults, - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWaitableResult [] waitableResults, - int millisecondsTimeout, - bool exitContext) - { - return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null); - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - public static int WaitAny( - IWaitableResult [] waitableResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); - } - - /// - /// Creates a new WorkItemsGroup. - /// - /// The number of work items that can be run concurrently - /// A reference to the WorkItemsGroup - public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) - { - IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); - return workItemsGroup; - } - - /// - /// Creates a new WorkItemsGroup. - /// - /// The number of work items that can be run concurrently - /// A WorkItemsGroup configuration that overrides the default behavior - /// A reference to the WorkItemsGroup - public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) - { - IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); - return workItemsGroup; - } - - #region Fire Thread's Events - - private void FireOnThreadInitialization() - { - if (null != _onThreadInitialization) - { - foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList()) - { - try - { - tih(); - } - catch (Exception e) - { - e.GetHashCode(); - Debug.Assert(false); - throw; - } - } - } - } - - private void FireOnThreadTermination() - { - if (null != _onThreadTermination) - { - foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList()) - { - try - { - tth(); - } - catch (Exception e) - { - e.GetHashCode(); - Debug.Assert(false); - throw; - } - } - } - } - - #endregion - - /// - /// This event is fired when a thread is created. - /// Use it to initialize a thread before the work items use it. - /// - public event ThreadInitializationHandler OnThreadInitialization - { - add { _onThreadInitialization += value; } - remove { _onThreadInitialization -= value; } - } - - /// - /// This event is fired when a thread is terminating. - /// Use it for cleanup. - /// - public event ThreadTerminationHandler OnThreadTermination - { - add { _onThreadTermination += value; } - remove { _onThreadTermination -= value; } - } - - - internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig) - { - foreach (ThreadEntry threadEntry in _workerThreads.Values) - { - WorkItem workItem = threadEntry.CurrentWorkItem; - if (null != workItem && - workItem.WasQueuedBy(wig) && - !workItem.IsCanceled) - { - threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); - } - } - } - - - - #endregion - - #region Properties - - /// - /// Get/Set the lower limit of threads in the pool. - /// - public int MinThreads - { - get - { - ValidateNotDisposed(); - return _stpStartInfo.MinWorkerThreads; - } - set - { - Debug.Assert(value >= 0); - Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads); - if (_stpStartInfo.MaxWorkerThreads < value) - { - _stpStartInfo.MaxWorkerThreads = value; - } - _stpStartInfo.MinWorkerThreads = value; - StartOptimalNumberOfThreads(); - } - } - - /// - /// Get/Set the upper limit of threads in the pool. - /// - public int MaxThreads - { - get - { - ValidateNotDisposed(); - return _stpStartInfo.MaxWorkerThreads; - } - - set - { - Debug.Assert(value > 0); - Debug.Assert(value >= _stpStartInfo.MinWorkerThreads); - if (_stpStartInfo.MinWorkerThreads > value) - { - _stpStartInfo.MinWorkerThreads = value; - } - _stpStartInfo.MaxWorkerThreads = value; - StartOptimalNumberOfThreads(); - } - } - /// - /// Get the number of threads in the thread pool. - /// Should be between the lower and the upper limits. - /// - public int ActiveThreads - { - get - { - ValidateNotDisposed(); - return _workerThreads.Count; - } - } - - /// - /// Get the number of busy (not idle) threads in the thread pool. - /// - public int InUseThreads - { - get - { - ValidateNotDisposed(); - return _inUseWorkerThreads; - } - } - - /// - /// Returns true if the current running work item has been cancelled. - /// Must be used within the work item's callback method. - /// The work item should sample this value in order to know if it - /// needs to quit before its completion. - /// - public static bool IsWorkItemCanceled - { - get - { - return CurrentThreadEntry.CurrentWorkItem.IsCanceled; - } - } - - /// - /// Checks if the work item has been cancelled, and if yes then abort the thread. - /// Can be used with Cancel and timeout - /// - public static void AbortOnWorkItemCancel() - { - if (IsWorkItemCanceled) - { - Thread.CurrentThread.Abort(); - } - } - - /// - /// Thread Pool start information (readonly) - /// - public STPStartInfo STPStartInfo - { - get - { - return _stpStartInfo.AsReadOnly(); - } - } - - public bool IsShuttingdown - { - get { return _shutdown; } - } - - /// - /// Return the local calculated performance counters - /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true. - /// - public ISTPPerformanceCountersReader PerformanceCountersReader - { - get { return (ISTPPerformanceCountersReader)_localPCs; } - } - - #endregion - - #region IDisposable Members - - public void Dispose() - { - if (!_isDisposed) - { - if (!_shutdown) - { - Shutdown(); - } - - if (null != _shuttingDownEvent) - { - _shuttingDownEvent.Close(); - _shuttingDownEvent = null; - } - _workerThreads.Clear(); - - if (null != _isIdleWaitHandle) - { - _isIdleWaitHandle.Close(); - _isIdleWaitHandle = null; - } - - _isDisposed = true; - } - } - - private void ValidateNotDisposed() - { - if(_isDisposed) - { - throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); - } - } - #endregion - - #region WorkItemsGroupBase Overrides - - /// - /// Get/Set the maximum number of work items that execute cocurrency on the thread pool - /// - public override int Concurrency - { - get { return MaxThreads; } - set { MaxThreads = value; } - } - - /// - /// Get the number of work items in the queue. - /// - public override int WaitingCallbacks - { - get - { - ValidateNotDisposed(); - return _workItemsQueue.Count; - } - } - - /// - /// Get an array with all the state objects of the currently running items. - /// The array represents a snap shot and impact performance. - /// - public override object[] GetStates() - { - object[] states = _workItemsQueue.GetStates(); - return states; - } - - /// - /// WorkItemsGroup start information (readonly) - /// - public override WIGStartInfo WIGStartInfo - { - get { return _stpStartInfo.AsReadOnly(); } - } - - /// - /// Start the thread pool if it was started suspended. - /// If it is already running, this method is ignored. - /// - public override void Start() - { - if (!_isSuspended) - { - return; - } - _isSuspended = false; - - ICollection workItemsGroups = _workItemsGroups.Values; - foreach (WorkItemsGroup workItemsGroup in workItemsGroups) - { - workItemsGroup.OnSTPIsStarting(); - } - - StartOptimalNumberOfThreads(); - } - - /// - /// Cancel all work items using thread abortion - /// - /// True to stop work items by raising ThreadAbortException - public override void Cancel(bool abortExecution) - { - _canceledSmartThreadPool.IsCanceled = true; - _canceledSmartThreadPool = new CanceledWorkItemsGroup(); - - ICollection workItemsGroups = _workItemsGroups.Values; - foreach (WorkItemsGroup workItemsGroup in workItemsGroups) - { - workItemsGroup.Cancel(abortExecution); - } - - if (abortExecution) - { - foreach (ThreadEntry threadEntry in _workerThreads.Values) - { - WorkItem workItem = threadEntry.CurrentWorkItem; - if (null != workItem && - threadEntry.AssociatedSmartThreadPool == this && - !workItem.IsCanceled) - { - threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); - } - } - } - } - - /// - /// Wait for the thread pool to be idle - /// - public override bool WaitForIdle(int millisecondsTimeout) - { - ValidateWaitForIdle(); - return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); - } - - /// - /// This event is fired when all work items are completed. - /// (When IsIdle changes to true) - /// This event only work on WorkItemsGroup. On SmartThreadPool - /// it throws the NotImplementedException. - /// - public override event WorkItemsGroupIdleHandler OnIdle - { - add - { - throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); - //_onIdle += value; - } - remove - { - throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); - //_onIdle -= value; - } - } - - internal override void PreQueueWorkItem() - { - ValidateNotDisposed(); - } - - #endregion - - #region Join, Choice, Pipe, etc. - - /// - /// Executes all actions in parallel. - /// Returns when they all finish. - /// - /// Actions to execute - public void Join(IEnumerable actions) - { - WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; - IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); - foreach (Action action in actions) - { - workItemsGroup.QueueWorkItem(action); - } - workItemsGroup.Start(); - workItemsGroup.WaitForIdle(); - } - - /// - /// Executes all actions in parallel. - /// Returns when they all finish. - /// - /// Actions to execute - public void Join(params Action[] actions) - { - Join((IEnumerable)actions); - } - - private class ChoiceIndex - { - public int _index = -1; - } - - /// - /// Executes all actions in parallel - /// Returns when the first one completes - /// - /// Actions to execute - public int Choice(IEnumerable actions) - { - WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; - IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); - - ManualResetEvent anActionCompleted = new ManualResetEvent(false); - - ChoiceIndex choiceIndex = new ChoiceIndex(); - - int i = 0; - foreach (Action action in actions) - { - Action act = action; - int value = i; - workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); }); - ++i; - } - workItemsGroup.Start(); - anActionCompleted.WaitOne(); - - return choiceIndex._index; - } - - /// - /// Executes all actions in parallel - /// Returns when the first one completes - /// - /// Actions to execute - public int Choice(params Action[] actions) - { - return Choice((IEnumerable)actions); - } - - /// - /// Executes actions in sequence asynchronously. - /// Returns immediately. - /// - /// A state context that passes - /// Actions to execute in the order they should run - public void Pipe(T pipeState, IEnumerable> actions) - { - WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; - IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo); - foreach (Action action in actions) - { - Action act = action; - workItemsGroup.QueueWorkItem(() => act(pipeState)); - } - workItemsGroup.Start(); - workItemsGroup.WaitForIdle(); - } - - /// - /// Executes actions in sequence asynchronously. - /// Returns immediately. - /// - /// - /// Actions to execute in the order they should run - public void Pipe(T pipeState, params Action[] actions) - { - Pipe(pipeState, (IEnumerable>)actions); - } - #endregion - } - #endregion -} +#region Release History + +// Smart Thread Pool +// 7 Aug 2004 - Initial release +// +// 14 Sep 2004 - Bug fixes +// +// 15 Oct 2004 - Added new features +// - Work items return result. +// - Support waiting synchronization for multiple work items. +// - Work items can be cancelled. +// - Passage of the caller thread’s context to the thread in the pool. +// - Minimal usage of WIN32 handles. +// - Minor bug fixes. +// +// 26 Dec 2004 - Changes: +// - Removed static constructors. +// - Added finalizers. +// - Changed Exceptions so they are serializable. +// - Fixed the bug in one of the SmartThreadPool constructors. +// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. +// The SmartThreadPool.WaitAny() is still limited by the .NET Framework. +// - Added PostExecute with options on which cases to call it. +// - Added option to dispose of the state objects. +// - Added a WaitForIdle() method that waits until the work items queue is empty. +// - Added an STPStartInfo class for the initialization of the thread pool. +// - Changed exception handling so if a work item throws an exception it +// is rethrown at GetResult(), rather then firing an UnhandledException event. +// Note that PostExecute exception are always ignored. +// +// 25 Mar 2005 - Changes: +// - Fixed lost of work items bug +// +// 3 Jul 2005: Changes. +// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. +// +// 16 Aug 2005: Changes. +// - Fixed bug where the InUseThreads becomes negative when canceling work items. +// +// 31 Jan 2006 - Changes: +// - Added work items priority +// - Removed support of chained delegates in callbacks and post executes (nobody really use this) +// - Added work items groups +// - Added work items groups idle event +// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array +// it returns true rather then throwing an exception. +// - Added option to start the STP and the WIG as suspended +// - Exception behavior changed, the real exception is returned by an +// inner exception +// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) +// - Added performance counters +// - Added priority to the threads in the pool +// +// 13 Feb 2006 - Changes: +// - Added a call to the dispose of the Performance Counter so +// their won't be a Performance Counter leak. +// - Added exception catch in case the Performance Counters cannot +// be created. +// +// 17 May 2008 - Changes: +// - Changed the dispose behavior and removed the Finalizers. +// - Enabled the change of the MaxThreads and MinThreads at run time. +// - Enabled the change of the Concurrency of a IWorkItemsGroup at run +// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency +// refers to the MaxThreads. +// - Improved the cancel behavior. +// - Added events for thread creation and termination. +// - Fixed the HttpContext context capture. +// - Changed internal collections so they use generic collections +// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup +// - Added support for WinCE +// - Added support for Action and Func +// +// 07 April 2009 - Changes: +// - Added support for Silverlight and Mono +// - Added Join, Choice, and Pipe to SmartThreadPool. +// - Added local performance counters (for Mono, Silverlight, and WindowsCE) +// - Changed duration measures from DateTime.Now to Stopwatch. +// - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList. +// +// 21 December 2009 - Changes: +// - Added work item timeout (passive) +// +// 20 August 2012 - Changes: +// - Added set name to threads +// - Fixed the WorkItemsQueue.Dequeue. +// Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... } +// - Fixed SmartThreadPool.Pipe +// - Added IsBackground option to threads +// - Added ApartmentState to threads +// - Fixed thread creation when queuing many work items at the same time. +// +// 24 August 2012 - Changes: +// - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan +// - Added option to set MaxStackSize of threads + +#endregion + +using System; +using System.Security; +using System.Threading; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +using Amib.Threading.Internal; + +namespace Amib.Threading +{ + #region SmartThreadPool class + /// + /// Smart thread pool class. + /// + public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable + { + #region Public Default Constants + + /// + /// Default minimum number of threads the thread pool contains. (0) + /// + public const int DefaultMinWorkerThreads = 0; + + /// + /// Default maximum number of threads the thread pool contains. (25) + /// + public const int DefaultMaxWorkerThreads = 25; + + /// + /// Default idle timeout in milliseconds. (One minute) + /// + public const int DefaultIdleTimeout = 60*1000; // One minute + + /// + /// Indicate to copy the security context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerCallContext = false; + + /// + /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerHttpContext = false; + + /// + /// Indicate to dispose of the state objects if they support the IDispose interface. (false) + /// + public const bool DefaultDisposeOfStateObjects = false; + + /// + /// The default option to run the post execute (CallToPostExecute.Always) + /// + public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; + + /// + /// The default post execute method to run. (None) + /// When null it means not to call it. + /// + public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback; + + /// + /// The default work item priority (WorkItemPriority.Normal) + /// + public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; + + /// + /// The default is to work on work items as soon as they arrive + /// and not to wait for the start. (false) + /// + public const bool DefaultStartSuspended = false; + + /// + /// The default name to use for the performance counters instance. (null) + /// + public static readonly string DefaultPerformanceCounterInstanceName; + +#if !(WINDOWS_PHONE) + + /// + /// The default thread priority (ThreadPriority.Normal) + /// + public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; +#endif + /// + /// The default thread pool name. (SmartThreadPool) + /// + public const string DefaultThreadPoolName = "SmartThreadPool"; + + /// + /// The default Max Stack Size. (SmartThreadPool) + /// + public static readonly int? DefaultMaxStackSize = null; + + /// + /// The default fill state with params. (false) + /// It is relevant only to QueueWorkItem of Action<...>/Func<...> + /// + public const bool DefaultFillStateWithArgs = false; + + /// + /// The default thread backgroundness. (true) + /// + public const bool DefaultAreThreadsBackground = true; + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// The default apartment state of a thread in the thread pool. + /// The default is ApartmentState.Unknown which means the STP will not + /// set the apartment of the thread. It will use the .NET default. + /// + public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; +#endif + + #endregion + + #region Member Variables + + /// + /// Dictionary of all the threads in the thread pool. + /// + private readonly SynchronizedDictionary _workerThreads = new SynchronizedDictionary(); + + /// + /// Queue of work items. + /// + private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); + + /// + /// Count the work items handled. + /// Used by the performance counter. + /// + private int _workItemsProcessed; + + /// + /// Number of threads that currently work (not idle). + /// + private int _inUseWorkerThreads; + + /// + /// Stores a copy of the original STPStartInfo. + /// It is used to change the MinThread and MaxThreads + /// + private STPStartInfo _stpStartInfo; + + /// + /// Total number of work items that are stored in the work items queue + /// plus the work items that the threads in the pool are working on. + /// + private int _currentWorkItemsCount; + + /// + /// Signaled when the thread pool is idle, i.e. no thread is busy + /// and the work items queue is empty + /// + //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + + /// + /// An event to signal all the threads to quit immediately. + /// + //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); + private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false); + + /// + /// A flag to indicate if the Smart Thread Pool is now suspended. + /// + private bool _isSuspended; + + /// + /// A flag to indicate the threads to quit. + /// + private bool _shutdown; + + /// + /// Counts the threads created in the pool. + /// It is used to name the threads. + /// + private int _threadCounter; + + /// + /// Indicate that the SmartThreadPool has been disposed + /// + private bool _isDisposed; + + /// + /// Holds all the WorkItemsGroup instaces that have at least one + /// work item int the SmartThreadPool + /// This variable is used in case of Shutdown + /// + private readonly SynchronizedDictionary _workItemsGroups = new SynchronizedDictionary(); + + /// + /// A common object for all the work items int the STP + /// so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup(); + + /// + /// Windows STP performance counters + /// + private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + + /// + /// Local STP performance counters + /// + private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance; + + +#if (WINDOWS_PHONE) + private static readonly Dictionary _threadEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot(); +#else + [ThreadStatic] + private static ThreadEntry _threadEntry; + +#endif + + /// + /// An event to call after a thread is created, but before + /// it's first use. + /// + private event ThreadInitializationHandler _onThreadInitialization; + + /// + /// An event to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + private event ThreadTerminationHandler _onThreadTermination; + + #endregion + + #region Per thread properties + + /// + /// A reference to the current work item a thread from the thread pool + /// is executing. + /// + internal static ThreadEntry CurrentThreadEntry + { +#if (WINDOWS_PHONE) + get + { + lock(_threadEntries) + { + ThreadEntry threadEntry; + if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry)) + { + return threadEntry; + } + } + return null; + } + set + { + lock(_threadEntries) + { + _threadEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + //Thread.CurrentThread.ManagedThreadId + return Thread.GetData(_threadEntrySlot) as ThreadEntry; + } + set + { + Thread.SetData(_threadEntrySlot, value); + } +#else + get + { + return _threadEntry; + } + set + { + _threadEntry = value; + } +#endif + } + #endregion + + #region Construction and Finalization + + /// + /// Constructor + /// + public SmartThreadPool() + { + _stpStartInfo = new STPStartInfo(); + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + public SmartThreadPool(int idleTimeout) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + /// Lower limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads, + int minWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + MinWorkerThreads = minWorkerThreads, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// A SmartThreadPool configuration that overrides the default behavior + public SmartThreadPool(STPStartInfo stpStartInfo) + { + _stpStartInfo = new STPStartInfo(stpStartInfo); + Initialize(); + } + + private void Initialize() + { + Name = _stpStartInfo.ThreadPoolName; + ValidateSTPStartInfo(); + + // _stpStartInfoRW stores a read/write copy of the STPStartInfo. + // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten + + _isSuspended = _stpStartInfo.StartSuspended; + +#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE) + if (null != _stpStartInfo.PerformanceCounterInstanceName) + { + throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); + } +#else + if (null != _stpStartInfo.PerformanceCounterInstanceName) + { + try + { + _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); + } + catch (Exception e) + { + Debug.WriteLine("Unable to create Performance Counters: " + e); + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + } + } +#endif + + if (_stpStartInfo.EnableLocalPerformanceCounters) + { + _localPCs = new LocalSTPInstancePerformanceCounters(); + } + + // If the STP is not started suspended then start the threads. + if (!_isSuspended) + { + StartOptimalNumberOfThreads(); + } + } + + private void StartOptimalNumberOfThreads() + { + int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); + threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); + threadsCount -= _workerThreads.Count; + if (threadsCount > 0) + { + StartThreads(threadsCount); + } + } + + private void ValidateSTPStartInfo() + { + if (_stpStartInfo.MinWorkerThreads < 0) + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads", "MinWorkerThreads cannot be negative"); + } + + if (_stpStartInfo.MaxWorkerThreads <= 0) + { + throw new ArgumentOutOfRangeException( + "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); + } + + if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads, maxWorkerThreads", + "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); + } + } + + private static void ValidateCallback(Delegate callback) + { + if(callback.GetInvocationList().Length > 1) + { + throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); + } + } + + #endregion + + #region Thread Processing + + /// + /// Waits on the queue for a work item, shutdown, or timeout. + /// + /// + /// Returns the WaitingCallback or null in case of timeout or shutdown. + /// + private WorkItem Dequeue() + { + WorkItem workItem = + _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); + + return workItem; + } + + /// + /// Put a new work item in the queue + /// + /// A work item to queue + internal override void Enqueue(WorkItem workItem) + { + // Make sure the workItem is not null + Debug.Assert(null != workItem); + + IncrementWorkItemsCount(); + + workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; + _workItemsQueue.EnqueueWorkItem(workItem); + workItem.WorkItemIsQueued(); + + // If all the threads are busy then try to create a new one + if (_currentWorkItemsCount > _workerThreads.Count) + { + StartThreads(1); + } + } + + private void IncrementWorkItemsCount() + { + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + + int count = Interlocked.Increment(ref _currentWorkItemsCount); + //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); + if (count == 1) + { + IsIdle = false; + _isIdleWaitHandle.Reset(); + } + } + + private void DecrementWorkItemsCount() + { + int count = Interlocked.Decrement(ref _currentWorkItemsCount); + //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); + if (count == 0) + { + IsIdle = true; + _isIdleWaitHandle.Set(); + } + + Interlocked.Increment(ref _workItemsProcessed); + + if (!_shutdown) + { + // The counter counts even if the work item was cancelled + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + } + + } + + internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + _workItemsGroups[workItemsGroup] = workItemsGroup; + } + + internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + if (_workItemsGroups.Contains(workItemsGroup)) + { + _workItemsGroups.Remove(workItemsGroup); + } + } + + /// + /// Inform that the current thread is about to quit or quiting. + /// The same thread may call this method more than once. + /// + private void InformCompleted() + { + // There is no need to lock the two methods together + // since only the current thread removes itself + // and the _workerThreads is a synchronized dictionary + if (_workerThreads.Contains(Thread.CurrentThread)) + { + _workerThreads.Remove(Thread.CurrentThread); + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + + /// + /// Starts new threads + /// + /// The number of threads to start + private void StartThreads(int threadsCount) + { + if (_isSuspended) + { + return; + } + + lock(_workerThreads.SyncRoot) + { + // Don't start threads on shut down + if (_shutdown) + { + return; + } + + for(int i = 0; i < threadsCount; ++i) + { + // Don't create more threads then the upper limit + if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) + { + return; + } + + // Create a new thread + +#if (_SILVERLIGHT) || (WINDOWS_PHONE) + Thread workerThread = new Thread(ProcessQueuedItems); +#else + Thread workerThread = + _stpStartInfo.MaxStackSize.HasValue + ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) + : new Thread(ProcessQueuedItems); +#endif + // Configure the new thread and start it + workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; + workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; + +#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE) + if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) + { + workerThread.SetApartmentState(_stpStartInfo.ApartmentState); + } +#endif + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + workerThread.Priority = _stpStartInfo.ThreadPriority; +#endif + workerThread.Start(); + ++_threadCounter; + + // Add it to the dictionary and update its creation time. + _workerThreads[workerThread] = new ThreadEntry(this); + + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + } + + /// + /// A worker thread method that processes work items from the work items queue. + /// + private void ProcessQueuedItems() + { + // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks + // of the dictionary. + CurrentThreadEntry = _workerThreads[Thread.CurrentThread]; + + FireOnThreadInitialization(); + + try + { + bool bInUseWorkerThreadsWasIncremented = false; + + // Process until shutdown. + while(!_shutdown) + { + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); + + // The following block handles the when the MaxWorkerThreads has been + // incremented by the user at run-time. + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + { + lock (_workerThreads.SyncRoot) + { + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + { + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; + } + } + } + + // Wait for a work item, shutdown, or timeout + WorkItem workItem = Dequeue(); + + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); + + // On timeout or shut down. + if (null == workItem) + { + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + lock(_workerThreads.SyncRoot) + { + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; + } + } + } + } + + // If we didn't quit then skip to the next iteration. + if (null == workItem) + { + continue; + } + + try + { + // Initialize the value to false + bInUseWorkerThreadsWasIncremented = false; + + // Set the Current Work Item of the thread. + // Store the Current Work Item before the workItem.StartingWorkItem() is called, + // so WorkItem.Cancel can work when the work item is between InQueue and InProgress + // states. + // If the work item has been cancelled BEFORE the workItem.StartingWorkItem() + // (work item is in InQueue state) then workItem.StartingWorkItem() will return false. + // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then + // (work item is in InProgress state) then the thread will be aborted + CurrentThreadEntry.CurrentWorkItem = workItem; + + // Change the state of the work item to 'in progress' if possible. + // We do it here so if the work item has been canceled we won't + // increment the _inUseWorkerThreads. + // The cancel mechanism doesn't delete items from the queue, + // it marks the work item as canceled, and when the work item + // is dequeued, we just skip it. + // If the post execute of work item is set to always or to + // call when the work item is canceled then the StartingWorkItem() + // will return true, so the post execute can run. + if (!workItem.StartingWorkItem()) + { + continue; + } + + // Execute the callback. Make sure to accurately + // record how many callbacks are currently executing. + int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + + // Mark that the _inUseWorkerThreads incremented, so in the finally{} + // statement we will decrement it correctly. + bInUseWorkerThreadsWasIncremented = true; + + workItem.FireWorkItemStarted(); + + ExecuteWorkItem(workItem); + } + catch(Exception ex) + { + ex.GetHashCode(); + // Do nothing + } + finally + { + workItem.DisposeOfState(); + + // Set the CurrentWorkItem to null, since we + // no longer run user's code. + CurrentThreadEntry.CurrentWorkItem = null; + + // Decrement the _inUseWorkerThreads only if we had + // incremented it. Note the cancelled work items don't + // increment _inUseWorkerThreads. + if (bInUseWorkerThreadsWasIncremented) + { + int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + } + + // Notify that the work item has been completed. + // WorkItemsGroup may enqueue their next work item. + workItem.FireWorkItemCompleted(); + + // Decrement the number of work items here so the idle + // ManualResetEvent won't fluctuate. + DecrementWorkItemsCount(); + } + } + } + catch(ThreadAbortException tae) + { + tae.GetHashCode(); + // Handle the abort exception gracfully. +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + Thread.ResetAbort(); +#endif + } + catch(Exception e) + { + Debug.Assert(null != e); + } + finally + { + InformCompleted(); + FireOnThreadTermination(); + } + } + + private void ExecuteWorkItem(WorkItem workItem) + { + _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + try + { + workItem.Execute(); + } + finally + { + _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + } + } + + + #endregion + + #region Public Methods + + private void ValidateWaitForIdle() + { + if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this) + { + throw new NotSupportedException( + "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) + { + if (null == CurrentThreadEntry) + { + return; + } + + WorkItem workItem = CurrentThreadEntry.CurrentWorkItem; + ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem); + if ((null != workItemsGroup) && + (null != workItem) && + CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) + { + if ((null != workItemsGroup) && + (null != workItem) && + workItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + /// + /// Force the SmartThreadPool to shutdown + /// + public void Shutdown() + { + Shutdown(true, 0); + } + + /// + /// Force the SmartThreadPool to shutdown with timeout + /// + public void Shutdown(bool forceAbort, TimeSpan timeout) + { + Shutdown(forceAbort, (int)timeout.TotalMilliseconds); + } + + /// + /// Empties the queue of work items and abort the threads in the pool. + /// + public void Shutdown(bool forceAbort, int millisecondsTimeout) + { + ValidateNotDisposed(); + + ISTPInstancePerformanceCounters pcs = _windowsPCs; + + if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs) + { + // Set the _pcs to "null" to stop updating the performance + // counters + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + + pcs.Dispose(); + } + + Thread [] threads; + lock(_workerThreads.SyncRoot) + { + // Shutdown the work items queue + _workItemsQueue.Dispose(); + + // Signal the threads to exit + _shutdown = true; + _shuttingDownEvent.Set(); + + // Make a copy of the threads' references in the pool + threads = new Thread [_workerThreads.Count]; + _workerThreads.Keys.CopyTo(threads, 0); + } + + int millisecondsLeft = millisecondsTimeout; + Stopwatch stopwatch = Stopwatch.StartNew(); + //DateTime start = DateTime.UtcNow; + bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); + bool timeout = false; + + // Each iteration we update the time left for the timeout. + foreach(Thread thread in threads) + { + // Join don't work with negative numbers + if (!waitInfinitely && (millisecondsLeft < 0)) + { + timeout = true; + break; + } + + // Wait for the thread to terminate + bool success = thread.Join(millisecondsLeft); + if(!success) + { + timeout = true; + break; + } + + if(!waitInfinitely) + { + // Update the time left to wait + //TimeSpan ts = DateTime.UtcNow - start; + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; + } + } + + if (timeout && forceAbort) + { + // Abort the threads in the pool + foreach(Thread thread in threads) + { + + if ((thread != null) +#if !(_WINDOWS_CE) + && thread.IsAlive +#endif + ) + { + try + { + thread.Abort(); // Shutdown + } + catch(SecurityException e) + { + e.GetHashCode(); + } + catch(ThreadStateException ex) + { + ex.GetHashCode(); + // In case the thread has been terminated + // after the check if it is alive. + } + } + } + } + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults) + { + return WaitAll(waitableResults, Timeout.Infinite, true); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } + + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults) + { + return WaitAny(waitableResults, Timeout.Infinite, true); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } + + /// + /// Creates a new WorkItemsGroup. + /// + /// The number of work items that can be run concurrently + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); + return workItemsGroup; + } + + /// + /// Creates a new WorkItemsGroup. + /// + /// The number of work items that can be run concurrently + /// A WorkItemsGroup configuration that overrides the default behavior + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); + return workItemsGroup; + } + + #region Fire Thread's Events + + private void FireOnThreadInitialization() + { + if (null != _onThreadInitialization) + { + foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList()) + { + try + { + tih(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; + } + } + } + } + + private void FireOnThreadTermination() + { + if (null != _onThreadTermination) + { + foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList()) + { + try + { + tth(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; + } + } + } + } + + #endregion + + /// + /// This event is fired when a thread is created. + /// Use it to initialize a thread before the work items use it. + /// + public event ThreadInitializationHandler OnThreadInitialization + { + add { _onThreadInitialization += value; } + remove { _onThreadInitialization -= value; } + } + + /// + /// This event is fired when a thread is terminating. + /// Use it for cleanup. + /// + public event ThreadTerminationHandler OnThreadTermination + { + add { _onThreadTermination += value; } + remove { _onThreadTermination -= value; } + } + + + internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + workItem.WasQueuedBy(wig) && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + + + + #endregion + + #region Properties + + /// + /// Get/Set the lower limit of threads in the pool. + /// + public int MinThreads + { + get + { + ValidateNotDisposed(); + return _stpStartInfo.MinWorkerThreads; + } + set + { + Debug.Assert(value >= 0); + Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads); + if (_stpStartInfo.MaxWorkerThreads < value) + { + _stpStartInfo.MaxWorkerThreads = value; + } + _stpStartInfo.MinWorkerThreads = value; + StartOptimalNumberOfThreads(); + } + } + + /// + /// Get/Set the upper limit of threads in the pool. + /// + public int MaxThreads + { + get + { + ValidateNotDisposed(); + return _stpStartInfo.MaxWorkerThreads; + } + + set + { + Debug.Assert(value > 0); + Debug.Assert(value >= _stpStartInfo.MinWorkerThreads); + if (_stpStartInfo.MinWorkerThreads > value) + { + _stpStartInfo.MinWorkerThreads = value; + } + _stpStartInfo.MaxWorkerThreads = value; + StartOptimalNumberOfThreads(); + } + } + /// + /// Get the number of threads in the thread pool. + /// Should be between the lower and the upper limits. + /// + public int ActiveThreads + { + get + { + ValidateNotDisposed(); + return _workerThreads.Count; + } + } + + /// + /// Get the number of busy (not idle) threads in the thread pool. + /// + public int InUseThreads + { + get + { + ValidateNotDisposed(); + return _inUseWorkerThreads; + } + } + + /// + /// Returns true if the current running work item has been cancelled. + /// Must be used within the work item's callback method. + /// The work item should sample this value in order to know if it + /// needs to quit before its completion. + /// + public static bool IsWorkItemCanceled + { + get + { + return CurrentThreadEntry.CurrentWorkItem.IsCanceled; + } + } + + /// + /// Checks if the work item has been cancelled, and if yes then abort the thread. + /// Can be used with Cancel and timeout + /// + public static void AbortOnWorkItemCancel() + { + if (IsWorkItemCanceled) + { + Thread.CurrentThread.Abort(); + } + } + + /// + /// Thread Pool start information (readonly) + /// + public STPStartInfo STPStartInfo + { + get + { + return _stpStartInfo.AsReadOnly(); + } + } + + public bool IsShuttingdown + { + get { return _shutdown; } + } + + /// + /// Return the local calculated performance counters + /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true. + /// + public ISTPPerformanceCountersReader PerformanceCountersReader + { + get { return (ISTPPerformanceCountersReader)_localPCs; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!_isDisposed) + { + if (!_shutdown) + { + Shutdown(); + } + + if (null != _shuttingDownEvent) + { + _shuttingDownEvent.Close(); + _shuttingDownEvent = null; + } + _workerThreads.Clear(); + + if (null != _isIdleWaitHandle) + { + _isIdleWaitHandle.Close(); + _isIdleWaitHandle = null; + } + + _isDisposed = true; + } + } + + private void ValidateNotDisposed() + { + if(_isDisposed) + { + throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); + } + } + #endregion + + #region WorkItemsGroupBase Overrides + + /// + /// Get/Set the maximum number of work items that execute cocurrency on the thread pool + /// + public override int Concurrency + { + get { return MaxThreads; } + set { MaxThreads = value; } + } + + /// + /// Get the number of work items in the queue. + /// + public override int WaitingCallbacks + { + get + { + ValidateNotDisposed(); + return _workItemsQueue.Count; + } + } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + public override object[] GetStates() + { + object[] states = _workItemsQueue.GetStates(); + return states; + } + + /// + /// WorkItemsGroup start information (readonly) + /// + public override WIGStartInfo WIGStartInfo + { + get { return _stpStartInfo.AsReadOnly(); } + } + + /// + /// Start the thread pool if it was started suspended. + /// If it is already running, this method is ignored. + /// + public override void Start() + { + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.OnSTPIsStarting(); + } + + StartOptimalNumberOfThreads(); + } + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + public override void Cancel(bool abortExecution) + { + _canceledSmartThreadPool.IsCanceled = true; + _canceledSmartThreadPool = new CanceledWorkItemsGroup(); + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.Cancel(abortExecution); + } + + if (abortExecution) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + threadEntry.AssociatedSmartThreadPool == this && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + ValidateWaitForIdle(); + return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); + } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + public override event WorkItemsGroupIdleHandler OnIdle + { + add + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle += value; + } + remove + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle -= value; + } + } + + internal override void PreQueueWorkItem() + { + ValidateNotDisposed(); + } + + #endregion + + #region Join, Choice, Pipe, etc. + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + foreach (Action action in actions) + { + workItemsGroup.QueueWorkItem(action); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(params Action[] actions) + { + Join((IEnumerable)actions); + } + + private class ChoiceIndex + { + public int _index = -1; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + + ManualResetEvent anActionCompleted = new ManualResetEvent(false); + + ChoiceIndex choiceIndex = new ChoiceIndex(); + + int i = 0; + foreach (Action action in actions) + { + Action act = action; + int value = i; + workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); }); + ++i; + } + workItemsGroup.Start(); + anActionCompleted.WaitOne(); + + return choiceIndex._index; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(params Action[] actions) + { + return Choice((IEnumerable)actions); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// A state context that passes + /// Actions to execute in the order they should run + public void Pipe(T pipeState, IEnumerable> actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo); + foreach (Action action in actions) + { + Action act = action; + workItemsGroup.QueueWorkItem(() => act(pipeState)); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// + /// Actions to execute in the order they should run + public void Pipe(T pipeState, params Action[] actions) + { + Pipe(pipeState, (IEnumerable>)actions); + } + #endregion + } + #endregion +} diff --git a/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs index 3532cca..0cce19f 100644 --- a/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs +++ b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs @@ -1,89 +1,89 @@ -using System.Collections.Generic; - -namespace Amib.Threading.Internal -{ - internal class SynchronizedDictionary - { - private readonly Dictionary _dictionary; - private readonly object _lock; - - public SynchronizedDictionary() - { - _lock = new object(); - _dictionary = new Dictionary(); - } - - public int Count - { - get { return _dictionary.Count; } - } - - public bool Contains(TKey key) - { - lock (_lock) - { - return _dictionary.ContainsKey(key); - } - } - - public void Remove(TKey key) - { - lock (_lock) - { - _dictionary.Remove(key); - } - } - - public object SyncRoot - { - get { return _lock; } - } - - public TValue this[TKey key] - { - get - { - lock (_lock) - { - return _dictionary[key]; - } - } - set - { - lock (_lock) - { - _dictionary[key] = value; - } - } - } - - public Dictionary.KeyCollection Keys - { - get - { - lock (_lock) - { - return _dictionary.Keys; - } - } - } - - public Dictionary.ValueCollection Values - { - get - { - lock (_lock) - { - return _dictionary.Values; - } - } - } - public void Clear() - { - lock (_lock) - { - _dictionary.Clear(); - } - } - } -} +using System.Collections.Generic; + +namespace Amib.Threading.Internal +{ + internal class SynchronizedDictionary + { + private readonly Dictionary _dictionary; + private readonly object _lock; + + public SynchronizedDictionary() + { + _lock = new object(); + _dictionary = new Dictionary(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool Contains(TKey key) + { + lock (_lock) + { + return _dictionary.ContainsKey(key); + } + } + + public void Remove(TKey key) + { + lock (_lock) + { + _dictionary.Remove(key); + } + } + + public object SyncRoot + { + get { return _lock; } + } + + public TValue this[TKey key] + { + get + { + lock (_lock) + { + return _dictionary[key]; + } + } + set + { + lock (_lock) + { + _dictionary[key] = value; + } + } + } + + public Dictionary.KeyCollection Keys + { + get + { + lock (_lock) + { + return _dictionary.Keys; + } + } + } + + public Dictionary.ValueCollection Values + { + get + { + lock (_lock) + { + return _dictionary.Values; + } + } + } + public void Clear() + { + lock (_lock) + { + _dictionary.Clear(); + } + } + } +} diff --git a/ThirdParty/SmartThreadPool/WIGStartInfo.cs b/ThirdParty/SmartThreadPool/WIGStartInfo.cs index e5ff150..8af195b 100644 --- a/ThirdParty/SmartThreadPool/WIGStartInfo.cs +++ b/ThirdParty/SmartThreadPool/WIGStartInfo.cs @@ -1,171 +1,171 @@ -using System; - -namespace Amib.Threading -{ - /// - /// Summary description for WIGStartInfo. - /// - 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"); - } - } - - /// - /// Get/Set if to use the caller's security context - /// - public virtual bool UseCallerCallContext - { - get { return _useCallerCallContext; } - set - { - ThrowIfReadOnly(); - _useCallerCallContext = value; - } - } - - - /// - /// Get/Set if to use the caller's HTTP context - /// - public virtual bool UseCallerHttpContext - { - get { return _useCallerHttpContext; } - set - { - ThrowIfReadOnly(); - _useCallerHttpContext = value; - } - } - - - /// - /// Get/Set if to dispose of the state object of a work item - /// - public virtual bool DisposeOfStateObjects - { - get { return _disposeOfStateObjects; } - set - { - ThrowIfReadOnly(); - _disposeOfStateObjects = value; - } - } - - - /// - /// Get/Set the run the post execute options - /// - public virtual CallToPostExecute CallToPostExecute - { - get { return _callToPostExecute; } - set - { - ThrowIfReadOnly(); - _callToPostExecute = value; - } - } - - - /// - /// Get/Set the default post execute callback - /// - public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback - { - get { return _postExecuteWorkItemCallback; } - set - { - ThrowIfReadOnly(); - _postExecuteWorkItemCallback = value; - } - } - - - /// - /// Get/Set if the work items execution should be suspended until the Start() - /// method is called. - /// - public virtual bool StartSuspended - { - get { return _startSuspended; } - set - { - ThrowIfReadOnly(); - _startSuspended = value; - } - } - - - /// - /// Get/Set the default priority that a work item gets when it is enqueued - /// - public virtual WorkItemPriority WorkItemPriority - { - get { return _workItemPriority; } - set { _workItemPriority = value; } - } - - /// - /// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the - /// arguments as an object array into the state of the work item. - /// The arguments can be access later by IWorkItemResult.State. - /// - public virtual bool FillStateWithArgs - { - get { return _fillStateWithArgs; } - set - { - ThrowIfReadOnly(); - _fillStateWithArgs = value; - } - } - - /// - /// Get a readonly version of this WIGStartInfo - /// - /// Returns a readonly reference to this WIGStartInfoRO - public WIGStartInfo AsReadOnly() - { - return new WIGStartInfo(this) { _readOnly = true }; - } - } -} +using System; + +namespace Amib.Threading +{ + /// + /// Summary description for WIGStartInfo. + /// + 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"); + } + } + + /// + /// Get/Set if to use the caller's security context + /// + public virtual bool UseCallerCallContext + { + get { return _useCallerCallContext; } + set + { + ThrowIfReadOnly(); + _useCallerCallContext = value; + } + } + + + /// + /// Get/Set if to use the caller's HTTP context + /// + public virtual bool UseCallerHttpContext + { + get { return _useCallerHttpContext; } + set + { + ThrowIfReadOnly(); + _useCallerHttpContext = value; + } + } + + + /// + /// Get/Set if to dispose of the state object of a work item + /// + public virtual bool DisposeOfStateObjects + { + get { return _disposeOfStateObjects; } + set + { + ThrowIfReadOnly(); + _disposeOfStateObjects = value; + } + } + + + /// + /// Get/Set the run the post execute options + /// + public virtual CallToPostExecute CallToPostExecute + { + get { return _callToPostExecute; } + set + { + ThrowIfReadOnly(); + _callToPostExecute = value; + } + } + + + /// + /// Get/Set the default post execute callback + /// + public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback + { + get { return _postExecuteWorkItemCallback; } + set + { + ThrowIfReadOnly(); + _postExecuteWorkItemCallback = value; + } + } + + + /// + /// Get/Set if the work items execution should be suspended until the Start() + /// method is called. + /// + public virtual bool StartSuspended + { + get { return _startSuspended; } + set + { + ThrowIfReadOnly(); + _startSuspended = value; + } + } + + + /// + /// Get/Set the default priority that a work item gets when it is enqueued + /// + public virtual WorkItemPriority WorkItemPriority + { + get { return _workItemPriority; } + set { _workItemPriority = value; } + } + + /// + /// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the + /// arguments as an object array into the state of the work item. + /// The arguments can be access later by IWorkItemResult.State. + /// + public virtual bool FillStateWithArgs + { + get { return _fillStateWithArgs; } + set + { + ThrowIfReadOnly(); + _fillStateWithArgs = value; + } + } + + /// + /// Get a readonly version of this WIGStartInfo + /// + /// Returns a readonly reference to this WIGStartInfoRO + public WIGStartInfo AsReadOnly() + { + return new WIGStartInfo(this) { _readOnly = true }; + } + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs index 5745c15..435a14b 100644 --- a/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs +++ b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs @@ -1,190 +1,190 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; - -namespace Amib.Threading.Internal -{ - public partial class WorkItem - { - #region WorkItemResult class - - private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult - { - /// - /// A back reference to the work item - /// - private readonly WorkItem _workItem; - - public WorkItemResult(WorkItem workItem) - { - _workItem = workItem; - } - - internal WorkItem GetWorkItem() - { - return _workItem; - } - - #region IWorkItemResult Members - - public bool IsCompleted - { - get - { - return _workItem.IsCompleted; - } - } - - public bool IsCanceled - { - get - { - return _workItem.IsCanceled; - } - } - - public object GetResult() - { - return _workItem.GetResult(Timeout.Infinite, true, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null); - } - - public object GetResult(TimeSpan timeout, bool exitContext) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - public object GetResult(out Exception e) - { - return _workItem.GetResult(Timeout.Infinite, true, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); - } - - public bool Cancel() - { - return Cancel(false); - } - - public bool Cancel(bool abortExecution) - { - return _workItem.Cancel(abortExecution); - } - - public object State - { - get - { - return _workItem._state; - } - } - - public WorkItemPriority WorkItemPriority - { - get - { - return _workItem._workItemInfo.WorkItemPriority; - } - } - - /// - /// Return the result, same as GetResult() - /// - public object Result - { - get { return GetResult(); } - } - - /// - /// Returns the exception if occured otherwise returns null. - /// This value is valid only after the work item completed, - /// before that it is always null. - /// - public object Exception - { - get { return _workItem._exception; } - } - - #endregion - - #region IInternalWorkItemResult Members - - public event WorkItemStateCallback OnWorkItemStarted - { - add - { - _workItem.OnWorkItemStarted += value; - } - remove - { - _workItem.OnWorkItemStarted -= value; - } - } - - - public event WorkItemStateCallback OnWorkItemCompleted - { - add - { - _workItem.OnWorkItemCompleted += value; - } - remove - { - _workItem.OnWorkItemCompleted -= value; - } - } - - #endregion - - #region IInternalWorkItemResult Members - - public IWorkItemResult GetWorkItemResult() - { - return this; - } - - public IWorkItemResult GetWorkItemResultT() - { - return new WorkItemResultTWrapper(this); - } - - #endregion - } - - #endregion - - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public partial class WorkItem + { + #region WorkItemResult class + + private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult + { + /// + /// A back reference to the work item + /// + private readonly WorkItem _workItem; + + public WorkItemResult(WorkItem workItem) + { + _workItem = workItem; + } + + internal WorkItem GetWorkItem() + { + return _workItem; + } + + #region IWorkItemResult Members + + public bool IsCompleted + { + get + { + return _workItem.IsCompleted; + } + } + + public bool IsCanceled + { + get + { + return _workItem.IsCanceled; + } + } + + public object GetResult() + { + return _workItem.GetResult(Timeout.Infinite, true, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null); + } + + public object GetResult(TimeSpan timeout, bool exitContext) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + public object GetResult(out Exception e) + { + return _workItem.GetResult(Timeout.Infinite, true, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); + } + + public bool Cancel() + { + return Cancel(false); + } + + public bool Cancel(bool abortExecution) + { + return _workItem.Cancel(abortExecution); + } + + public object State + { + get + { + return _workItem._state; + } + } + + public WorkItemPriority WorkItemPriority + { + get + { + return _workItem._workItemInfo.WorkItemPriority; + } + } + + /// + /// Return the result, same as GetResult() + /// + public object Result + { + get { return GetResult(); } + } + + /// + /// Returns the exception if occured otherwise returns null. + /// This value is valid only after the work item completed, + /// before that it is always null. + /// + public object Exception + { + get { return _workItem._exception; } + } + + #endregion + + #region IInternalWorkItemResult Members + + public event WorkItemStateCallback OnWorkItemStarted + { + add + { + _workItem.OnWorkItemStarted += value; + } + remove + { + _workItem.OnWorkItemStarted -= value; + } + } + + + public event WorkItemStateCallback OnWorkItemCompleted + { + add + { + _workItem.OnWorkItemCompleted += value; + } + remove + { + _workItem.OnWorkItemCompleted -= value; + } + } + + #endregion + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return this; + } + + public IWorkItemResult GetWorkItemResultT() + { + return new WorkItemResultTWrapper(this); + } + + #endregion + } + + #endregion + + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItemFactory.cs b/ThirdParty/SmartThreadPool/WorkItemFactory.cs index 2d6601e..16ccd81 100644 --- a/ThirdParty/SmartThreadPool/WorkItemFactory.cs +++ b/ThirdParty/SmartThreadPool/WorkItemFactory.cs @@ -1,343 +1,343 @@ -using System; - -namespace Amib.Threading.Internal -{ - #region WorkItemFactory class - - public class WorkItemFactory - { - /// - /// Create a new work item - /// - /// The WorkItemsGroup of this workitem - /// Work item group start information - /// A callback to execute - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback) - { - return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); - } - - /// - /// Create a new work item - /// - /// The WorkItemsGroup of this workitem - /// Work item group start information - /// A callback to execute - /// The priority of the work item - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - WorkItemPriority workItemPriority) - { - return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); - } - - /// - /// Create a new work item - /// - /// The WorkItemsGroup of this workitem - /// Work item group start information - /// Work item info - /// A callback to execute - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemInfo workItemInfo, - WorkItemCallback callback) - { - return CreateWorkItem( - workItemsGroup, - wigStartInfo, - workItemInfo, - callback, - null); - } - - /// - /// Create a new work item - /// - /// The WorkItemsGroup of this workitem - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state) - { - ValidateCallback(callback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - return workItem; - } - - /// - /// Create a new work item - /// - /// The work items group - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - WorkItemPriority workItemPriority) - { - ValidateCallback(callback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = workItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// The work items group - /// Work item group start information - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemInfo workItemInfo, - WorkItemCallback callback, - object state) - { - ValidateCallback(callback); - ValidateCallback(workItemInfo.PostExecuteWorkItemCallback); - - WorkItem workItem = new WorkItem( - workItemsGroup, - new WorkItemInfo(workItemInfo), - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// The work items group - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// The work items group - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = workItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// The work items group - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - WorkItemInfo workItemInfo = new WorkItemInfo(); - workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; - workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; - workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; - workItemInfo.CallToPostExecute = callToPostExecute; - workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; - workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; - - WorkItem workItem = new WorkItem( - workItemsGroup, - workItemInfo, - callback, - state); - - return workItem; - } - - /// - /// Create a new work item - /// - /// The work items group - /// Work item group start information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item - public static WorkItem CreateWorkItem( - IWorkItemsGroup workItemsGroup, - WIGStartInfo wigStartInfo, - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - - ValidateCallback(callback); - ValidateCallback(postExecuteWorkItemCallback); - - 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 -} +using System; + +namespace Amib.Threading.Internal +{ + #region WorkItemFactory class + + public class WorkItemFactory + { + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// The priority of the work item + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + WorkItemPriority workItemPriority) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// Work item info + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemInfo workItemInfo, + WorkItemCallback callback) + { + return CreateWorkItem( + workItemsGroup, + wigStartInfo, + workItemInfo, + callback, + null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemInfo workItemInfo, + WorkItemCallback callback, + object state) + { + ValidateCallback(callback); + ValidateCallback(workItemInfo.PostExecuteWorkItemCallback); + + WorkItem workItem = new WorkItem( + workItemsGroup, + new WorkItemInfo(workItemInfo), + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = callToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + 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 +} diff --git a/ThirdParty/SmartThreadPool/WorkItemInfo.cs b/ThirdParty/SmartThreadPool/WorkItemInfo.cs index 5fbceb8..0d7fc85 100644 --- a/ThirdParty/SmartThreadPool/WorkItemInfo.cs +++ b/ThirdParty/SmartThreadPool/WorkItemInfo.cs @@ -1,69 +1,69 @@ -namespace Amib.Threading -{ - #region WorkItemInfo class - - /// - /// Summary description for WorkItemInfo. - /// - public class WorkItemInfo - { - public WorkItemInfo() - { - UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; - UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; - DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; - CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute; - PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; - WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority; - } - - 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; - } - - /// - /// Get/Set if to use the caller's security context - /// - public bool UseCallerCallContext { get; set; } - - /// - /// Get/Set if to use the caller's HTTP context - /// - public bool UseCallerHttpContext { get; set; } - - /// - /// Get/Set if to dispose of the state object of a work item - /// - public bool DisposeOfStateObjects { get; set; } - - /// - /// Get/Set the run the post execute options - /// - public CallToPostExecute CallToPostExecute { get; set; } - - /// - /// Get/Set the post execute callback - /// - public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; } - - /// - /// Get/Set the work item's priority - /// - public WorkItemPriority WorkItemPriority { get; set; } - - /// - /// Get/Set the work item's timout in milliseconds. - /// This is a passive timout. When the timout expires the work item won't be actively aborted! - /// - public long Timeout { get; set; } - } - - #endregion -} +namespace Amib.Threading +{ + #region WorkItemInfo class + + /// + /// Summary description for WorkItemInfo. + /// + public class WorkItemInfo + { + public WorkItemInfo() + { + UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; + UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; + DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; + CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute; + PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; + WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority; + } + + 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; + } + + /// + /// Get/Set if to use the caller's security context + /// + public bool UseCallerCallContext { get; set; } + + /// + /// Get/Set if to use the caller's HTTP context + /// + public bool UseCallerHttpContext { get; set; } + + /// + /// Get/Set if to dispose of the state object of a work item + /// + public bool DisposeOfStateObjects { get; set; } + + /// + /// Get/Set the run the post execute options + /// + public CallToPostExecute CallToPostExecute { get; set; } + + /// + /// Get/Set the post execute callback + /// + public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; } + + /// + /// Get/Set the work item's priority + /// + public WorkItemPriority WorkItemPriority { get; set; } + + /// + /// Get/Set the work item's timout in milliseconds. + /// This is a passive timout. When the timout expires the work item won't be actively aborted! + /// + public long Timeout { get; set; } + } + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs index a0bf8b8..d1eff95 100644 --- a/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs +++ b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs @@ -1,128 +1,128 @@ -using System; -using System.Threading; - -namespace Amib.Threading.Internal -{ - #region WorkItemResultTWrapper class - - internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult - { - private readonly IWorkItemResult _workItemResult; - - public WorkItemResultTWrapper(IWorkItemResult workItemResult) - { - _workItemResult = workItemResult; - } - - #region IWorkItemResult Members - - public TResult GetResult() - { - return (TResult)_workItemResult.GetResult(); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); - } - - public TResult GetResult(out Exception e) - { - return (TResult)_workItemResult.GetResult(out e); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); - } - - public bool IsCompleted - { - get { return _workItemResult.IsCompleted; } - } - - public bool IsCanceled - { - get { return _workItemResult.IsCanceled; } - } - - public object State - { - get { return _workItemResult.State; } - } - - public bool Cancel() - { - return _workItemResult.Cancel(); - } - - public bool Cancel(bool abortExecution) - { - return _workItemResult.Cancel(abortExecution); - } - - public WorkItemPriority WorkItemPriority - { - get { return _workItemResult.WorkItemPriority; } - } - - public TResult Result - { - get { return (TResult)_workItemResult.Result; } - } - - public object Exception - { - get { return (TResult)_workItemResult.Exception; } - } - - #region IInternalWorkItemResult Members - - public IWorkItemResult GetWorkItemResult() - { - return _workItemResult.GetWorkItemResult(); - } - - public IWorkItemResult GetWorkItemResultT() - { - return (IWorkItemResult)this; - } - - #endregion - - #endregion - } - - #endregion - -} +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + #region WorkItemResultTWrapper class + + internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult + { + private readonly IWorkItemResult _workItemResult; + + public WorkItemResultTWrapper(IWorkItemResult workItemResult) + { + _workItemResult = workItemResult; + } + + #region IWorkItemResult Members + + public TResult GetResult() + { + return (TResult)_workItemResult.GetResult(); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(out Exception e) + { + return (TResult)_workItemResult.GetResult(out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); + } + + public bool IsCompleted + { + get { return _workItemResult.IsCompleted; } + } + + public bool IsCanceled + { + get { return _workItemResult.IsCanceled; } + } + + public object State + { + get { return _workItemResult.State; } + } + + public bool Cancel() + { + return _workItemResult.Cancel(); + } + + public bool Cancel(bool abortExecution) + { + return _workItemResult.Cancel(abortExecution); + } + + public WorkItemPriority WorkItemPriority + { + get { return _workItemResult.WorkItemPriority; } + } + + public TResult Result + { + get { return (TResult)_workItemResult.Result; } + } + + public object Exception + { + get { return (TResult)_workItemResult.Exception; } + } + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return _workItemResult.GetWorkItemResult(); + } + + public IWorkItemResult GetWorkItemResultT() + { + return (IWorkItemResult)this; + } + + #endregion + + #endregion + } + + #endregion + +} diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs index 67dcbdd..d9d34ac 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs @@ -1,361 +1,361 @@ -using System; -using System.Threading; -using System.Runtime.CompilerServices; -using System.Diagnostics; - -namespace Amib.Threading.Internal -{ - - #region WorkItemsGroup class - - /// - /// Summary description for WorkItemsGroup. - /// - public class WorkItemsGroup : WorkItemsGroupBase - { - #region Private members - - private readonly object _lock = new object(); - - /// - /// A reference to the SmartThreadPool instance that created this - /// WorkItemsGroup. - /// - private readonly SmartThreadPool _stp; - - /// - /// The OnIdle event - /// - private event WorkItemsGroupIdleHandler _onIdle; - - /// - /// A flag to indicate if the Work Items Group is now suspended. - /// - private bool _isSuspended; - - /// - /// Defines how many work items of this WorkItemsGroup can run at once. - /// - private int _concurrency; - - /// - /// Priority queue to hold work items before they are passed - /// to the SmartThreadPool. - /// - private readonly PriorityQueue _workItemsQueue; - - /// - /// Indicate how many work items are waiting in the SmartThreadPool - /// queue. - /// This value is used to apply the concurrency. - /// - private int _workItemsInStpQueue; - - /// - /// Indicate how many work items are currently running in the SmartThreadPool. - /// This value is used with the Cancel, to calculate if we can send new - /// work items to the STP. - /// - private int _workItemsExecutingInStp = 0; - - /// - /// WorkItemsGroup start information - /// - private readonly WIGStartInfo _workItemsGroupStartInfo; - - /// - /// Signaled when all of the WorkItemsGroup's work item completed. - /// - //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); - private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); - - /// - /// A common object for all the work items that this work items group - /// generate so we can mark them to cancel in O(1) - /// - private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - - #endregion - - #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; - } - } - - /// - /// WorkItemsGroup start information - /// - public override WIGStartInfo WIGStartInfo - { - get { return _workItemsGroupStartInfo; } - } - - /// - /// Start the Work Items Group if it was started suspended - /// - public override void Start() - { - // If the Work Items Group already started then quit - if (!_isSuspended) - { - return; - } - _isSuspended = false; - - EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); - } - - public override void Cancel(bool abortExecution) - { - lock (_lock) - { - _canceledWorkItemsGroup.IsCanceled = true; - _workItemsQueue.Clear(); - _workItemsInStpQueue = 0; - _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - } - - if (abortExecution) - { - _stp.CancelAbortWorkItemsGroup(this); - } - } - - /// - /// Wait for the thread pool to be idle - /// - public override bool WaitForIdle(int millisecondsTimeout) - { - 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 -} +using System; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +namespace Amib.Threading.Internal +{ + + #region WorkItemsGroup class + + /// + /// Summary description for WorkItemsGroup. + /// + public class WorkItemsGroup : WorkItemsGroupBase + { + #region Private members + + private readonly object _lock = new object(); + + /// + /// A reference to the SmartThreadPool instance that created this + /// WorkItemsGroup. + /// + private readonly SmartThreadPool _stp; + + /// + /// The OnIdle event + /// + private event WorkItemsGroupIdleHandler _onIdle; + + /// + /// A flag to indicate if the Work Items Group is now suspended. + /// + private bool _isSuspended; + + /// + /// Defines how many work items of this WorkItemsGroup can run at once. + /// + private int _concurrency; + + /// + /// Priority queue to hold work items before they are passed + /// to the SmartThreadPool. + /// + private readonly PriorityQueue _workItemsQueue; + + /// + /// Indicate how many work items are waiting in the SmartThreadPool + /// queue. + /// This value is used to apply the concurrency. + /// + private int _workItemsInStpQueue; + + /// + /// Indicate how many work items are currently running in the SmartThreadPool. + /// This value is used with the Cancel, to calculate if we can send new + /// work items to the STP. + /// + private int _workItemsExecutingInStp = 0; + + /// + /// WorkItemsGroup start information + /// + private readonly WIGStartInfo _workItemsGroupStartInfo; + + /// + /// Signaled when all of the WorkItemsGroup's work item completed. + /// + //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + + /// + /// A common object for all the work items that this work items group + /// generate so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + #endregion + + #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; + } + } + + /// + /// WorkItemsGroup start information + /// + public override WIGStartInfo WIGStartInfo + { + get { return _workItemsGroupStartInfo; } + } + + /// + /// Start the Work Items Group if it was started suspended + /// + public override void Start() + { + // If the Work Items Group already started then quit + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); + } + + public override void Cancel(bool abortExecution) + { + lock (_lock) + { + _canceledWorkItemsGroup.IsCanceled = true; + _workItemsQueue.Clear(); + _workItemsInStpQueue = 0; + _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + } + + if (abortExecution) + { + _stp.CancelAbortWorkItemsGroup(this); + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + 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 +} diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs index 429de12..27fae5e 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs @@ -1,471 +1,471 @@ -using System; -using System.Threading; - -namespace Amib.Threading.Internal -{ - public abstract class WorkItemsGroupBase : IWorkItemsGroup - { - #region Private Fields - - /// - /// Contains the name of this instance of SmartThreadPool. - /// Can be changed by the user. - /// - private string _name = "WorkItemsGroupBase"; - - public WorkItemsGroupBase() - { - IsIdle = true; - } - - #endregion - - #region IWorkItemsGroup Members - - #region Public Methods - - /// - /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance - /// - public string Name - { - get { return _name; } - set { _name = value; } - } - - #endregion - - #region Abstract Methods - - public abstract int Concurrency { get; set; } - public abstract int WaitingCallbacks { get; } - public abstract object[] GetStates(); - public abstract WIGStartInfo WIGStartInfo { get; } - public abstract void Start(); - public abstract void Cancel(bool abortExecution); - public abstract bool WaitForIdle(int millisecondsTimeout); - public abstract event WorkItemsGroupIdleHandler OnIdle; - - internal abstract void Enqueue(WorkItem workItem); - internal virtual void PreQueueWorkItem() { } - - #endregion - - #region Common Base Methods - - /// - /// Cancel all the work items. - /// Same as Cancel(false) - /// - public virtual void Cancel() - { - Cancel(false); - } - - /// - /// Wait for the SmartThreadPool/WorkItemsGroup to be idle - /// - public void WaitForIdle() - { - WaitForIdle(Timeout.Infinite); - } - - /// - /// Wait for the SmartThreadPool/WorkItemsGroup to be idle - /// - public bool WaitForIdle(TimeSpan timeout) - { - return WaitForIdle((int)timeout.TotalMilliseconds); - } - - /// - /// IsIdle is true when there are no work items running or queued. - /// - public bool IsIdle { get; protected set; } - - #endregion - - #region QueueWorkItem - - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - #endregion - - #region QueueWorkItem(Action<...>) - - public IWorkItemResult QueueWorkItem(Action action) - { - return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority); - } - - public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - delegate - { - action.Invoke (); - return null; - }, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem(Action action, T arg) - { - return QueueWorkItem (action, arg, SmartThreadPool.DefaultWorkItemPriority); - } - - public IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2) - { - return QueueWorkItem (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority); - } - - public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg1, arg2); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3) - { - return QueueWorkItem (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority); - ; - } - - public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg1, arg2, arg3); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem( - Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - { - return QueueWorkItem (action, arg1, arg2, arg3, arg4, - SmartThreadPool.DefaultWorkItemPriority); - } - - public IWorkItemResult QueueWorkItem ( - Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg1, arg2, arg3, arg4); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - #endregion - - #region QueueWorkItem(Func<...>) - - public IWorkItemResult QueueWorkItem(Func func) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(); - }); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem(Func func, T arg) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg1, arg2); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem( - Func func, T1 arg1, T2 arg2, T3 arg3) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg1, arg2, arg3); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem( - Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg1, arg2, arg3, arg4); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - #endregion - - #endregion - } +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public abstract class WorkItemsGroupBase : IWorkItemsGroup + { + #region Private Fields + + /// + /// Contains the name of this instance of SmartThreadPool. + /// Can be changed by the user. + /// + private string _name = "WorkItemsGroupBase"; + + public WorkItemsGroupBase() + { + IsIdle = true; + } + + #endregion + + #region IWorkItemsGroup Members + + #region Public Methods + + /// + /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + #endregion + + #region Abstract Methods + + public abstract int Concurrency { get; set; } + public abstract int WaitingCallbacks { get; } + public abstract object[] GetStates(); + public abstract WIGStartInfo WIGStartInfo { get; } + public abstract void Start(); + public abstract void Cancel(bool abortExecution); + public abstract bool WaitForIdle(int millisecondsTimeout); + public abstract event WorkItemsGroupIdleHandler OnIdle; + + internal abstract void Enqueue(WorkItem workItem); + internal virtual void PreQueueWorkItem() { } + + #endregion + + #region Common Base Methods + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + public virtual void Cancel() + { + Cancel(false); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public void WaitForIdle() + { + WaitForIdle(Timeout.Infinite); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public bool WaitForIdle(TimeSpan timeout) + { + return WaitForIdle((int)timeout.TotalMilliseconds); + } + + /// + /// IsIdle is true when there are no work items running or queued. + /// + public bool IsIdle { get; protected set; } + + #endregion + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + #endregion + + #region QueueWorkItem(Action<...>) + + public IWorkItemResult QueueWorkItem(Action action) + { + return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + delegate + { + action.Invoke (); + return null; + }, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T arg) + { + return QueueWorkItem (action, arg, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2) + { + return QueueWorkItem (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3) + { + return QueueWorkItem (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority); + ; + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + return QueueWorkItem (action, arg1, arg2, arg3, arg4, + SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem ( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3, arg4); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + #endregion + + #region QueueWorkItem(Func<...>) + + public IWorkItemResult QueueWorkItem(Func func) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(); + }); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T arg) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3, arg4); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + #endregion + + #endregion + } } \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs index 156a131..e0bc916 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs @@ -1,645 +1,645 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace Amib.Threading.Internal -{ - #region WorkItemsQueue class - - /// - /// WorkItemsQueue class. - /// - public class WorkItemsQueue : IDisposable - { - #region Member variables - - /// - /// Waiters queue (implemented as stack). - /// - private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); - - /// - /// Waiters count - /// - private int _waitersCount = 0; - - /// - /// Work items queue - /// - private readonly PriorityQueue _workItems = new PriorityQueue(); - - /// - /// Indicate that work items are allowed to be queued - /// - private bool _isWorkItemsQueueActive = true; - - -#if (WINDOWS_PHONE) - private static readonly Dictionary _waiterEntries = new Dictionary(); -#elif (_WINDOWS_CE) - private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); -#else - - [ThreadStatic] - private static WaiterEntry _waiterEntry; -#endif - - - /// - /// Each thread in the thread pool keeps its own waiter entry. - /// - private static WaiterEntry CurrentWaiterEntry - { -#if (WINDOWS_PHONE) - get - { - lock (_waiterEntries) - { - WaiterEntry waiterEntry; - if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry)) - { - return waiterEntry; - } - } - return null; - } - set - { - lock (_waiterEntries) - { - _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value; - } - } -#elif (_WINDOWS_CE) - get - { - return Thread.GetData(_waiterEntrySlot) as WaiterEntry; - } - set - { - Thread.SetData(_waiterEntrySlot, value); - } -#else - get - { - return _waiterEntry; - } - set - { - _waiterEntry = value; - } -#endif - } - - /// - /// A flag that indicates if the WorkItemsQueue has been disposed. - /// - private bool _isDisposed = false; - - #endregion - - #region Public properties - - /// - /// Returns the current number of work items in the queue - /// - public int Count - { - get - { - return _workItems.Count; - } - } - - /// - /// Returns the current number of waiters - /// - public int WaitersCount - { - get - { - return _waitersCount; - } - } - - - #endregion - - #region Public methods - - /// - /// Enqueue a work item to the queue. - /// - public bool EnqueueWorkItem(WorkItem workItem) - { - // A work item cannot be null, since null is used in the - // WaitForWorkItem() method to indicate timeout or cancel - if (null == workItem) - { - throw new ArgumentNullException("workItem" , "workItem cannot be null"); - } - - bool enqueue = true; - - // First check if there is a waiter waiting for work item. During - // the check, timed out waiters are ignored. If there is no - // waiter then the work item is queued. - lock(this) - { - ValidateNotDisposed(); - - if (!_isWorkItemsQueueActive) - { - return false; - } - - while(_waitersCount > 0) - { - // Dequeue a waiter. - WaiterEntry waiterEntry = PopWaiter(); - - // Signal the waiter. On success break the loop - if (waiterEntry.Signal(workItem)) - { - enqueue = false; - break; - } - } - - if (enqueue) - { - // Enqueue the work item - _workItems.Enqueue(workItem); - } - } - return true; - } - - - /// - /// Waits for a work item or exits on timeout or cancel - /// - /// Timeout in milliseconds - /// Cancel wait handle - /// Returns true if the resource was granted - public WorkItem DequeueWorkItem( - int millisecondsTimeout, - WaitHandle cancelEvent) - { - // This method cause the caller to wait for a work item. - // If there is at least one waiting work item then the - // method returns immidiately with it. - // - // If there are no waiting work items then the caller - // is queued between other waiters for a work item to arrive. - // - // If a work item didn't come within millisecondsTimeout or - // the user canceled the wait by signaling the cancelEvent - // then the method returns null to indicate that the caller - // didn't get a work item. - - WaiterEntry waiterEntry; - WorkItem workItem = null; - lock (this) - { - ValidateNotDisposed(); - - // If there are waiting work items then take one and return. - if (_workItems.Count > 0) - { - workItem = _workItems.Dequeue() as WorkItem; - return workItem; - } - - // No waiting work items ... - - // Get the waiter entry for the waiters queue - waiterEntry = GetThreadWaiterEntry(); - - // Put the waiter with the other waiters - PushWaiter(waiterEntry); - } - - // Prepare array of wait handle for the WaitHandle.WaitAny() - WaitHandle [] waitHandles = new WaitHandle[] { - waiterEntry.WaitHandle, - cancelEvent }; - - // Wait for an available resource, cancel event, or timeout. - - // During the wait we are supposes to exit the synchronization - // domain. (Placing true as the third argument of the WaitAny()) - // It just doesn't work, I don't know why, so I have two lock(this) - // statments instead of one. - - int index = STPEventWaitHandle.WaitAny( - waitHandles, - millisecondsTimeout, - true); - - lock(this) - { - // success is true if it got a work item. - bool success = (0 == index); - - // The timeout variable is used only for readability. - // (We treat cancel as timeout) - bool timeout = !success; - - // On timeout update the waiterEntry that it is timed out - if (timeout) - { - // The Timeout() fails if the waiter has already been signaled - timeout = waiterEntry.Timeout(); - - // On timeout remove the waiter from the queue. - // Note that the complexity is O(1). - if(timeout) - { - RemoveWaiter(waiterEntry, false); - } - - // Again readability - success = !timeout; - } - - // On success return the work item - if (success) - { - workItem = waiterEntry.WorkItem; - - if (null == workItem) - { - workItem = _workItems.Dequeue() as WorkItem; - } - } - } - // On failure return null. - return workItem; - } - - /// - /// Cleanup the work items queue, hence no more work - /// items are allowed to be queue - /// - private void Cleanup() - { - lock(this) - { - // Deactivate only once - if (!_isWorkItemsQueueActive) - { - return; - } - - // Don't queue more work items - _isWorkItemsQueueActive = false; - - foreach(WorkItem workItem in _workItems) - { - workItem.DisposeOfState(); - } - - // Clear the work items that are already queued - _workItems.Clear(); - - // Note: - // I don't iterate over the queue and dispose of work items's states, - // since if a work item has a state object that is still in use in the - // application then I must not dispose it. - - // Tell the waiters that they were timed out. - // It won't signal them to exit, but to ignore their - // next work item. - while(_waitersCount > 0) - { - WaiterEntry waiterEntry = PopWaiter(); - waiterEntry.Timeout(); - } - } - } - - public object[] GetStates() - { - lock (this) - { - object[] states = new object[_workItems.Count]; - int i = 0; - foreach (WorkItem workItem in _workItems) - { - states[i] = workItem.GetWorkItemResult().State; - ++i; - } - return states; - } - } - - #endregion - - #region Private methods - - /// - /// Returns the WaiterEntry of the current thread - /// - /// - /// In order to avoid creation and destuction of WaiterEntry - /// objects each thread has its own WaiterEntry object. - private static WaiterEntry GetThreadWaiterEntry() - { - if (null == CurrentWaiterEntry) - { - CurrentWaiterEntry = new WaiterEntry(); - } - CurrentWaiterEntry.Reset(); - return CurrentWaiterEntry; - } - - #region Waiters stack methods - - /// - /// Push a new waiter into the waiter's stack - /// - /// A waiter to put in the stack - public void PushWaiter(WaiterEntry newWaiterEntry) - { - // Remove the waiter if it is already in the stack and - // update waiter's count as needed - RemoveWaiter(newWaiterEntry, false); - - // If the stack is empty then newWaiterEntry is the new head of the stack - if (null == _headWaiterEntry._nextWaiterEntry) - { - _headWaiterEntry._nextWaiterEntry = newWaiterEntry; - newWaiterEntry._prevWaiterEntry = _headWaiterEntry; - - } - // If the stack is not empty then put newWaiterEntry as the new head - // of the stack. - else - { - // Save the old first waiter entry - WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; - - // Update the links - _headWaiterEntry._nextWaiterEntry = newWaiterEntry; - newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; - newWaiterEntry._prevWaiterEntry = _headWaiterEntry; - oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; - } - - // Increment the number of waiters - ++_waitersCount; - } - - /// - /// Pop a waiter from the waiter's stack - /// - /// Returns the first waiter in the stack - private WaiterEntry PopWaiter() - { - // Store the current stack head - WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; - - // Store the new stack head - WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; - - // Update the old stack head list links and decrement the number - // waiters. - RemoveWaiter(oldFirstWaiterEntry, true); - - // Update the new stack head - _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; - if (null != newHeadWaiterEntry) - { - newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; - } - - // Return the old stack head - return oldFirstWaiterEntry; - } - - /// - /// Remove a waiter from the stack - /// - /// A waiter entry to remove - /// If true the waiter count is always decremented - private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) - { - // Store the prev entry in the list - WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; - - // Store the next entry in the list - WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; - - // A flag to indicate if we need to decrement the waiters count. - // If we got here from PopWaiter then we must decrement. - // If we got here from PushWaiter then we decrement only if - // the waiter was already in the stack. - bool decrementCounter = popDecrement; - - // Null the waiter's entry links - waiterEntry._prevWaiterEntry = null; - waiterEntry._nextWaiterEntry = null; - - // If the waiter entry had a prev link then update it. - // It also means that the waiter is already in the list and we - // need to decrement the waiters count. - if (null != prevWaiterEntry) - { - prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; - decrementCounter = true; - } - - // If the waiter entry had a next link then update it. - // It also means that the waiter is already in the list and we - // need to decrement the waiters count. - if (null != nextWaiterEntry) - { - nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; - decrementCounter = true; - } - - // Decrement the waiters count if needed - if (decrementCounter) - { - --_waitersCount; - } - } - - #endregion - - #endregion - - #region WaiterEntry class - - // A waiter entry in the _waiters queue. - public sealed class WaiterEntry : IDisposable - { - #region Member variables - - /// - /// Event to signal the waiter that it got the work item. - /// - //private AutoResetEvent _waitHandle = new AutoResetEvent(false); - private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); - - /// - /// Flag to know if this waiter already quited from the queue - /// because of a timeout. - /// - private bool _isTimedout = false; - - /// - /// Flag to know if the waiter was signaled and got a work item. - /// - private bool _isSignaled = false; - - /// - /// A work item that passed directly to the waiter withou going - /// through the queue - /// - private WorkItem _workItem = null; - - private bool _isDisposed = false; - - // Linked list members - internal WaiterEntry _nextWaiterEntry = null; - internal WaiterEntry _prevWaiterEntry = null; - - #endregion - - #region Construction - - public WaiterEntry() - { - Reset(); - } - - #endregion - - #region Public methods - - public WaitHandle WaitHandle - { - get { return _waitHandle; } - } - - public WorkItem WorkItem - { - get - { - return _workItem; - } - } - - /// - /// Signal the waiter that it got a work item. - /// - /// Return true on success - /// The method fails if Timeout() preceded its call - public bool Signal(WorkItem workItem) - { - lock(this) - { - if (!_isTimedout) - { - _workItem = workItem; - _isSignaled = true; - _waitHandle.Set(); - return true; - } - } - return false; - } - - /// - /// Mark the wait entry that it has been timed out - /// - /// Return true on success - /// The method fails if Signal() preceded its call - public bool Timeout() - { - lock(this) - { - // Time out can happen only if the waiter wasn't marked as - // signaled - if (!_isSignaled) - { - // We don't remove the waiter from the queue, the DequeueWorkItem - // method skips _waiters that were timed out. - _isTimedout = true; - return true; - } - } - return false; - } - - /// - /// Reset the wait entry so it can be used again - /// - public void Reset() - { - _workItem = null; - _isTimedout = false; - _isSignaled = false; - _waitHandle.Reset(); - } - - /// - /// Free resources - /// - public void Close() - { - if (null != _waitHandle) - { - _waitHandle.Close(); - _waitHandle = null; - } - } - - #endregion - - #region IDisposable Members - - public void Dispose() - { - lock (this) - { - if (!_isDisposed) - { - Close(); - } - _isDisposed = true; - } - } - - #endregion - } - - #endregion - - #region IDisposable Members - - public void Dispose() - { - if (!_isDisposed) - { - Cleanup(); - } - _isDisposed = true; - } - - private void ValidateNotDisposed() - { - if(_isDisposed) - { - throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); - } - } - - #endregion - } - - #endregion -} - +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Amib.Threading.Internal +{ + #region WorkItemsQueue class + + /// + /// WorkItemsQueue class. + /// + public class WorkItemsQueue : IDisposable + { + #region Member variables + + /// + /// Waiters queue (implemented as stack). + /// + private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); + + /// + /// Waiters count + /// + private int _waitersCount = 0; + + /// + /// Work items queue + /// + private readonly PriorityQueue _workItems = new PriorityQueue(); + + /// + /// Indicate that work items are allowed to be queued + /// + private bool _isWorkItemsQueueActive = true; + + +#if (WINDOWS_PHONE) + private static readonly Dictionary _waiterEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); +#else + + [ThreadStatic] + private static WaiterEntry _waiterEntry; +#endif + + + /// + /// Each thread in the thread pool keeps its own waiter entry. + /// + private static WaiterEntry CurrentWaiterEntry + { +#if (WINDOWS_PHONE) + get + { + lock (_waiterEntries) + { + WaiterEntry waiterEntry; + if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry)) + { + return waiterEntry; + } + } + return null; + } + set + { + lock (_waiterEntries) + { + _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + return Thread.GetData(_waiterEntrySlot) as WaiterEntry; + } + set + { + Thread.SetData(_waiterEntrySlot, value); + } +#else + get + { + return _waiterEntry; + } + set + { + _waiterEntry = value; + } +#endif + } + + /// + /// A flag that indicates if the WorkItemsQueue has been disposed. + /// + private bool _isDisposed = false; + + #endregion + + #region Public properties + + /// + /// Returns the current number of work items in the queue + /// + public int Count + { + get + { + return _workItems.Count; + } + } + + /// + /// Returns the current number of waiters + /// + public int WaitersCount + { + get + { + return _waitersCount; + } + } + + + #endregion + + #region Public methods + + /// + /// Enqueue a work item to the queue. + /// + public bool EnqueueWorkItem(WorkItem workItem) + { + // A work item cannot be null, since null is used in the + // WaitForWorkItem() method to indicate timeout or cancel + if (null == workItem) + { + throw new ArgumentNullException("workItem" , "workItem cannot be null"); + } + + bool enqueue = true; + + // First check if there is a waiter waiting for work item. During + // the check, timed out waiters are ignored. If there is no + // waiter then the work item is queued. + lock(this) + { + ValidateNotDisposed(); + + if (!_isWorkItemsQueueActive) + { + return false; + } + + while(_waitersCount > 0) + { + // Dequeue a waiter. + WaiterEntry waiterEntry = PopWaiter(); + + // Signal the waiter. On success break the loop + if (waiterEntry.Signal(workItem)) + { + enqueue = false; + break; + } + } + + if (enqueue) + { + // Enqueue the work item + _workItems.Enqueue(workItem); + } + } + return true; + } + + + /// + /// Waits for a work item or exits on timeout or cancel + /// + /// Timeout in milliseconds + /// Cancel wait handle + /// Returns true if the resource was granted + public WorkItem DequeueWorkItem( + int millisecondsTimeout, + WaitHandle cancelEvent) + { + // This method cause the caller to wait for a work item. + // If there is at least one waiting work item then the + // method returns immidiately with it. + // + // If there are no waiting work items then the caller + // is queued between other waiters for a work item to arrive. + // + // If a work item didn't come within millisecondsTimeout or + // the user canceled the wait by signaling the cancelEvent + // then the method returns null to indicate that the caller + // didn't get a work item. + + WaiterEntry waiterEntry; + WorkItem workItem = null; + lock (this) + { + ValidateNotDisposed(); + + // If there are waiting work items then take one and return. + if (_workItems.Count > 0) + { + workItem = _workItems.Dequeue() as WorkItem; + return workItem; + } + + // No waiting work items ... + + // Get the waiter entry for the waiters queue + waiterEntry = GetThreadWaiterEntry(); + + // Put the waiter with the other waiters + PushWaiter(waiterEntry); + } + + // Prepare array of wait handle for the WaitHandle.WaitAny() + WaitHandle [] waitHandles = new WaitHandle[] { + waiterEntry.WaitHandle, + cancelEvent }; + + // Wait for an available resource, cancel event, or timeout. + + // During the wait we are supposes to exit the synchronization + // domain. (Placing true as the third argument of the WaitAny()) + // It just doesn't work, I don't know why, so I have two lock(this) + // statments instead of one. + + int index = STPEventWaitHandle.WaitAny( + waitHandles, + millisecondsTimeout, + true); + + lock(this) + { + // success is true if it got a work item. + bool success = (0 == index); + + // The timeout variable is used only for readability. + // (We treat cancel as timeout) + bool timeout = !success; + + // On timeout update the waiterEntry that it is timed out + if (timeout) + { + // The Timeout() fails if the waiter has already been signaled + timeout = waiterEntry.Timeout(); + + // On timeout remove the waiter from the queue. + // Note that the complexity is O(1). + if(timeout) + { + RemoveWaiter(waiterEntry, false); + } + + // Again readability + success = !timeout; + } + + // On success return the work item + if (success) + { + workItem = waiterEntry.WorkItem; + + if (null == workItem) + { + workItem = _workItems.Dequeue() as WorkItem; + } + } + } + // On failure return null. + return workItem; + } + + /// + /// Cleanup the work items queue, hence no more work + /// items are allowed to be queue + /// + private void Cleanup() + { + lock(this) + { + // Deactivate only once + if (!_isWorkItemsQueueActive) + { + return; + } + + // Don't queue more work items + _isWorkItemsQueueActive = false; + + foreach(WorkItem workItem in _workItems) + { + workItem.DisposeOfState(); + } + + // Clear the work items that are already queued + _workItems.Clear(); + + // Note: + // I don't iterate over the queue and dispose of work items's states, + // since if a work item has a state object that is still in use in the + // application then I must not dispose it. + + // Tell the waiters that they were timed out. + // It won't signal them to exit, but to ignore their + // next work item. + while(_waitersCount > 0) + { + WaiterEntry waiterEntry = PopWaiter(); + waiterEntry.Timeout(); + } + } + } + + public object[] GetStates() + { + lock (this) + { + object[] states = new object[_workItems.Count]; + int i = 0; + foreach (WorkItem workItem in _workItems) + { + states[i] = workItem.GetWorkItemResult().State; + ++i; + } + return states; + } + } + + #endregion + + #region Private methods + + /// + /// Returns the WaiterEntry of the current thread + /// + /// + /// In order to avoid creation and destuction of WaiterEntry + /// objects each thread has its own WaiterEntry object. + private static WaiterEntry GetThreadWaiterEntry() + { + if (null == CurrentWaiterEntry) + { + CurrentWaiterEntry = new WaiterEntry(); + } + CurrentWaiterEntry.Reset(); + return CurrentWaiterEntry; + } + + #region Waiters stack methods + + /// + /// Push a new waiter into the waiter's stack + /// + /// A waiter to put in the stack + public void PushWaiter(WaiterEntry newWaiterEntry) + { + // Remove the waiter if it is already in the stack and + // update waiter's count as needed + RemoveWaiter(newWaiterEntry, false); + + // If the stack is empty then newWaiterEntry is the new head of the stack + if (null == _headWaiterEntry._nextWaiterEntry) + { + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + + } + // If the stack is not empty then put newWaiterEntry as the new head + // of the stack. + else + { + // Save the old first waiter entry + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + + // Update the links + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; + } + + // Increment the number of waiters + ++_waitersCount; + } + + /// + /// Pop a waiter from the waiter's stack + /// + /// Returns the first waiter in the stack + private WaiterEntry PopWaiter() + { + // Store the current stack head + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + + // Store the new stack head + WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; + + // Update the old stack head list links and decrement the number + // waiters. + RemoveWaiter(oldFirstWaiterEntry, true); + + // Update the new stack head + _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; + if (null != newHeadWaiterEntry) + { + newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; + } + + // Return the old stack head + return oldFirstWaiterEntry; + } + + /// + /// Remove a waiter from the stack + /// + /// A waiter entry to remove + /// If true the waiter count is always decremented + private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) + { + // Store the prev entry in the list + WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; + + // Store the next entry in the list + WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; + + // A flag to indicate if we need to decrement the waiters count. + // If we got here from PopWaiter then we must decrement. + // If we got here from PushWaiter then we decrement only if + // the waiter was already in the stack. + bool decrementCounter = popDecrement; + + // Null the waiter's entry links + waiterEntry._prevWaiterEntry = null; + waiterEntry._nextWaiterEntry = null; + + // If the waiter entry had a prev link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != prevWaiterEntry) + { + prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; + decrementCounter = true; + } + + // If the waiter entry had a next link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != nextWaiterEntry) + { + nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; + decrementCounter = true; + } + + // Decrement the waiters count if needed + if (decrementCounter) + { + --_waitersCount; + } + } + + #endregion + + #endregion + + #region WaiterEntry class + + // A waiter entry in the _waiters queue. + public sealed class WaiterEntry : IDisposable + { + #region Member variables + + /// + /// Event to signal the waiter that it got the work item. + /// + //private AutoResetEvent _waitHandle = new AutoResetEvent(false); + private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); + + /// + /// Flag to know if this waiter already quited from the queue + /// because of a timeout. + /// + private bool _isTimedout = false; + + /// + /// Flag to know if the waiter was signaled and got a work item. + /// + private bool _isSignaled = false; + + /// + /// A work item that passed directly to the waiter withou going + /// through the queue + /// + private WorkItem _workItem = null; + + private bool _isDisposed = false; + + // Linked list members + internal WaiterEntry _nextWaiterEntry = null; + internal WaiterEntry _prevWaiterEntry = null; + + #endregion + + #region Construction + + public WaiterEntry() + { + Reset(); + } + + #endregion + + #region Public methods + + public WaitHandle WaitHandle + { + get { return _waitHandle; } + } + + public WorkItem WorkItem + { + get + { + return _workItem; + } + } + + /// + /// Signal the waiter that it got a work item. + /// + /// Return true on success + /// The method fails if Timeout() preceded its call + public bool Signal(WorkItem workItem) + { + lock(this) + { + if (!_isTimedout) + { + _workItem = workItem; + _isSignaled = true; + _waitHandle.Set(); + return true; + } + } + return false; + } + + /// + /// Mark the wait entry that it has been timed out + /// + /// Return true on success + /// The method fails if Signal() preceded its call + public bool Timeout() + { + lock(this) + { + // Time out can happen only if the waiter wasn't marked as + // signaled + if (!_isSignaled) + { + // We don't remove the waiter from the queue, the DequeueWorkItem + // method skips _waiters that were timed out. + _isTimedout = true; + return true; + } + } + return false; + } + + /// + /// Reset the wait entry so it can be used again + /// + public void Reset() + { + _workItem = null; + _isTimedout = false; + _isSignaled = false; + _waitHandle.Reset(); + } + + /// + /// Free resources + /// + public void Close() + { + if (null != _waitHandle) + { + _waitHandle.Close(); + _waitHandle = null; + } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + lock (this) + { + if (!_isDisposed) + { + Close(); + } + _isDisposed = true; + } + } + + #endregion + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!_isDisposed) + { + Cleanup(); + } + _isDisposed = true; + } + + private void ValidateNotDisposed() + { + if(_isDisposed) + { + throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); + } + } + + #endregion + } + + #endregion +} + -- cgit v1.1 From e3d9d5566a799fdbb4261010b97b8b04dbd7da1e Mon Sep 17 00:00:00 2001 From: BlueWall Date: Wed, 1 May 2013 18:46:57 -0400 Subject: Fix inventory issue Fix issue where objects rezzed from Trash or Lost And Found then be placed back in the respective folder when taking the object or a copy back into inventory. --- .../CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index eb37626..e6d6cbf 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -671,6 +671,12 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess { InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID); folder = m_Scene.InventoryService.GetFolder(f); + + if(folder.Type == 14 || folder.Type == 16) + { + // folder.Type = 6; + folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); + } } } -- cgit v1.1 From 0378baed350b846132b2f501c74bd39a2675e41d Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 1 May 2013 17:15:54 -0700 Subject: BulletSim: rework LinksetCompound to work with new BSShape system. Not all working yet. --- .../Physics/BulletSPlugin/BSLinksetCompound.cs | 146 ++++++--------------- 1 file changed, 40 insertions(+), 106 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index e3ce7fb..20eb871 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -35,6 +35,7 @@ using OMV = OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { + /* // When a child is linked, the relationship position of the child to the parent // is remembered so the child's world position can be recomputed when it is // removed from the linkset. @@ -88,17 +89,15 @@ sealed class BSLinksetCompoundInfo : BSLinksetInfo return buff.ToString(); } }; + */ public sealed class BSLinksetCompound : BSLinkset { private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; - private BSShape LinksetShape; - public BSLinksetCompound(BSScene scene, BSPrimLinkable parent) : base(scene, parent) { - LinksetShape = new BSShapeNull(); } // When physical properties are changed the linkset needs to recalculate @@ -143,25 +142,11 @@ public sealed class BSLinksetCompound : BSLinkset // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. ScheduleRebuild(LinksetRoot); } - - // The origional prims are removed from the world as the shape of the root compound - // shape takes over. - m_physicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); - m_physicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); - // We don't want collisions from the old linkset children. - m_physicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); - - child.PhysBody.collisionType = CollisionType.LinksetChild; - - ret = true; - return ret; } - // The object is going static (non-physical). Do any setup necessary for a static linkset. + // The object is going static (non-physical). We do not do anything for static linksets. // Return 'true' if any properties updated on the passed object. - // This doesn't normally happen -- OpenSim removes the objects from the physical - // world if it is a static linkset. // Called at taint-time! public override bool MakeStatic(BSPrimLinkable child) { @@ -169,19 +154,9 @@ public sealed class BSLinksetCompound : BSLinkset DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); if (IsRoot(child)) { + // Schedule a rebuild to verify that the root shape is set to the real shape. ScheduleRebuild(LinksetRoot); } - else - { - // The non-physical children can come back to life. - m_physicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); - - child.PhysBody.collisionType = CollisionType.LinksetChild; - - // Don't force activation so setting of DISABLE_SIMULATION can stay if used. - m_physicsScene.PE.Activate(child.PhysBody, false); - ret = true; - } return ret; } @@ -283,9 +258,6 @@ public sealed class BSLinksetCompound : BSLinkset if (!IsRoot(child)) { - // Because it is a convenient time, recompute child world position and rotation based on - // its position in the linkset. - RecomputeChildWorldPosition(child, true /* inTaintTime */); child.LinksetInfo = null; } @@ -296,50 +268,6 @@ public sealed class BSLinksetCompound : BSLinkset return ret; } - // When the linkset is built, the child shape is added to the compound shape relative to the - // root shape. The linkset then moves around but this does not move the actual child - // prim. The child prim's location must be recomputed based on the location of the root shape. - private void RecomputeChildWorldPosition(BSPrimLinkable child, bool inTaintTime) - { - // For the moment (20130201), disable this computation (converting the child physical addr back to - // a region address) until we have a good handle on center-of-mass offsets and what the physics - // engine moving a child actually means. - // The simulator keeps track of where children should be as the linkset moves. Setting - // the pos/rot here does not effect that knowledge as there is no good way for the - // physics engine to send the simulator an update for a child. - - /* - BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo; - if (lci != null) - { - if (inTaintTime) - { - OMV.Vector3 oldPos = child.RawPosition; - child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetFromRoot; - child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot; - DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}", - child.LocalID, oldPos, lci, child.RawPosition); - } - else - { - // TaintedObject is not used here so the raw position is set now and not at taint-time. - child.Position = LinksetRoot.RawPosition + lci.OffsetFromRoot; - child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot; - } - } - else - { - // This happens when children have been added to the linkset but the linkset - // has not been constructed yet. So like, at taint time, adding children to a linkset - // and then changing properties of the children (makePhysical, for instance) - // but the post-print action of actually rebuilding the linkset has not yet happened. - // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}", - // LogHeader, child.LocalID); - DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID); - } - */ - } - // ================================================================ // Add a new child to the linkset. @@ -372,7 +300,6 @@ public sealed class BSLinksetCompound : BSLinkset child.LocalID, child.PhysBody.AddrString); // Cause the child's body to be rebuilt and thus restored to normal operation - RecomputeChildWorldPosition(child, false); child.LinksetInfo = null; child.ForceBodyShapeRebuild(false); @@ -399,26 +326,27 @@ public sealed class BSLinksetCompound : BSLinkset private bool disableCOM = true; // For basic linkset debugging, turn off the center-of-mass setting private void RecomputeLinksetCompound() { - if (!LinksetRoot.IsPhysicallyActive) - { - // There is no reason to build all this physical stuff for a non-physical linkset - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysical", LinksetRoot.LocalID); - return; - } - try { - // This replaces the physical shape of the root prim with a compound shape made up of the root - // shape and all the children. + Rebuilding = true; + + // No matter what is being done, force the root prim's PhysBody and PhysShape to get set + // to what they should be as if the root was not in a linkset. + // Not that bad since we only get into this routine if there are children in the linkset and + // something has been updated/changed. + LinksetRoot.ForceBodyShapeRebuild(true); - // RootPrim.PhysShape is the shape of the root prim. - // Here we build the compound shape made up of all the children. + // There is no reason to build all this physical stuff for a non-physical linkset. + if (!LinksetRoot.IsPhysicallyActive) + { + // Clean up any old linkset shape and make sure the root shape is set to the root object. + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysical", LinksetRoot.LocalID); - // Free up any shape we'd previously built. - LinksetShape.Dereference(m_physicsScene); + return; // Note the 'finally' clause at the botton which will get executed. + } // Get a new compound shape to build the linkset shape in. - LinksetShape = BSShapeCompound.GetReference(m_physicsScene); + BSShape linksetShape = BSShapeCompound.GetReference(m_physicsScene); // The center of mass for the linkset is the geometric center of the group. // Compute a displacement for each component so it is relative to the center-of-mass. @@ -445,35 +373,41 @@ public sealed class BSLinksetCompound : BSLinkset int memberIndex = 1; ForEachMember(delegate(BSPrimLinkable cPrim) { - if (IsRoot(cPrim)) - { - cPrim.LinksetChildIndex = 0; - } - else - { - cPrim.LinksetChildIndex = memberIndex; - } + // Root shape is always index zero. + cPrim.LinksetChildIndex = IsRoot(cPrim) ? 0 : memberIndex; + // Get a reference to the shape of the child and add that shape to the linkset compound shape BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim); OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; - m_physicsScene.PE.AddChildShapeToCompoundShape(LinksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); - DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1}cShape={2},offPos={3},offRot={4}", + m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}", LinksetRoot.LocalID, memberIndex, cPrim.PhysShape, offsetPos, offsetRot); + // Since we are borrowing the shape of the child, disable the origional child body + if (!IsRoot(cPrim)) + { + m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION); + // We don't want collisions from the old linkset children. + m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + cPrim.PhysBody.collisionType = CollisionType.LinksetChild; + } + memberIndex++; return false; // 'false' says to move onto the next child in the list }); - // Sneak the built compound shape in as the shape of the root prim. - // Note this doesn't touch the root prim's PhysShape so be sure the manage the difference. + // Replace the root shape with the built compound shape. // Object removed and added to world to get collision cache rebuilt for new shape. + LinksetRoot.PhysShape.Dereference(m_physicsScene); + LinksetRoot.PhysShape = linksetShape; m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody); - m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, LinksetShape.physShapeInfo); + m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo); m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody); DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}", - LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetShape); + LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape); // With all of the linkset packed into the root prim, it has the mass of everyone. LinksetMass = ComputeLinksetMass(); @@ -494,7 +428,7 @@ public sealed class BSLinksetCompound : BSLinkset } // See that the Aabb surrounds the new shape - m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetShape.physShapeInfo); + m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo); } } } \ No newline at end of file -- cgit v1.1 From ed46b42feae4659653ba54a9290dddede6f24f2e Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 1 May 2013 17:16:46 -0700 Subject: BulletSim: fix crash when mesh asset wasn't available when meshing the first time. Debugging added for mesh/hull asset fetch. --- .../Physics/BulletSPlugin/BSShapeCollection.cs | 1 + OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 28 ++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 19855da..a4250be 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -132,6 +132,7 @@ public sealed class BSShapeCollection : IDisposable PrimitiveBaseShape pbs = prim.BaseShape; // Kludge to create the capsule for the avatar. + // TDOD: Remove/redo this when BSShapeAvatar is working!! BSCharacter theChar = prim as BSCharacter; if (theChar != null) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index 3faa484..2962249 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -164,6 +164,8 @@ public abstract class BSShape prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,objNam={1},tex={2}", + prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); } else { @@ -174,29 +176,33 @@ public abstract class BSShape && prim.BaseShape.SculptTexture != OMV.UUID.Zero ) { - physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset", prim.LocalID); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}", + prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); // Multiple requestors will know we're waiting for this asset prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; BSPhysObject xprim = prim; Util.FireAndForget(delegate { + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID); RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; if (assetProvider != null) { BSPhysObject yprim = xprim; // probably not necessary, but, just in case. assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) { + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID); bool assetFound = false; string mismatchIDs = String.Empty; // DEBUG DEBUG if (asset != null && yprim.BaseShape.SculptEntry) { if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) { - yprim.BaseShape.SculptData = asset.Data; + yprim.BaseShape.SculptData = (byte[])asset.Data.Clone(); // This will cause the prim to see that the filler shape is not the right // one and try again to build the object. // No race condition with the normal shape setting since the rebuild is at taint time. + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; yprim.ForceBodyShapeRebuild(false /* inTaintTime */); assetFound = true; } @@ -205,11 +211,11 @@ public abstract class BSShape mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; } } - if (assetFound) - yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; - else + if (!assetFound) + { yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - physicsScene.DetailLog("{0},BSShape,fetchAssetCallback,found={1},isSculpt={2},ids={3}", + } + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}", yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); }); } @@ -227,6 +233,8 @@ public abstract class BSShape { physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,objNam={1},tex={2}", + prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); } } } @@ -359,7 +367,7 @@ public class BSShapeMesh : BSShape // Check to see if mesh was created (might require an asset). newShape = VerifyMeshCreated(physicsScene, newShape, prim); - if (newShape.shapeType == BSPhysicsShapeType.SHAPE_MESH) + if (!newShape.isNativeShape) { // If a mesh was what was created, remember the built shape for later sharing. Meshes.Add(newMeshKey, retMesh); @@ -408,7 +416,7 @@ public class BSShapeMesh : BSShape private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { - BulletShape newShape = null; + BulletShape newShape = new BulletShape(); IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, false, // say it is not physical so a bounding box is not built @@ -507,7 +515,7 @@ public class BSShapeHull : BSShape // Check to see if hull was created (might require an asset). newShape = VerifyMeshCreated(physicsScene, newShape, prim); - if (newShape.shapeType == BSPhysicsShapeType.SHAPE_HULL) + if (!newShape.isNativeShape) { // If a mesh was what was created, remember the built shape for later sharing. Hulls.Add(newHullKey, retHull); @@ -731,7 +739,7 @@ public class BSShapeCompound : BSShape { lock (physShapeInfo) { - Dereference(physicsScene); + this.DecrementReference(); if (referenceCount <= 0) { if (!physicsScene.PE.IsCompound(physShapeInfo)) -- cgit v1.1 From 4cb73192a78f0c8d15c2eb052c7c632bb678ce5f Mon Sep 17 00:00:00 2001 From: BlueWall Date: Wed, 1 May 2013 23:01:33 -0400 Subject: Make default config directory "." --- bin/Robust.HG.ini.example | 2 +- bin/Robust.ini.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index d74f205..fee2a87 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -35,7 +35,7 @@ ; Modular configurations ; Set path to directory for modular ini files... ; The Robust.exe process must have R/W access to the location - ConfigDirectory = "/home/opensim/etc/Configs" + ConfigDirectory = "." [ServiceList] diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index b7b2524..2d5aa8c 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -27,7 +27,7 @@ ; Modular configurations ; Set path to directory for modular ini files... ; The Robust.exe process must have R/W access to the location - ConfigDirectory = "/home/opensim/etc/Configs" + ConfigDirectory = "." [ServiceList] AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector" -- cgit v1.1 From d9c3947824feccd3522c70a72b75c72a84bab3e0 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 2 May 2013 10:06:12 -0700 Subject: BulletSim: Rebuild physical body if physical shape changes for mesh and hull. Properly rebuilds collision caches. Release asset data fetched when building mesh or hulls. --- OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 1 + OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 2 +- .../Physics/BulletSPlugin/BSShapeCollection.cs | 35 +++++++++++-- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 59 +++++++++++++++------- 4 files changed, 73 insertions(+), 24 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index f535e50..b9bd909 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -596,6 +596,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin public override void Refresh() { // If asking for a refresh, reset the physical parameters before the next simulation step. + // Called whether active or not since the active state may be updated before the next step. m_physicsScene.PostTaintObject("BSDynamics.Refresh", ControllingPrim.LocalID, delegate() { SetPhysicalParameters(); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 7e2af78..3d68d7f 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -767,7 +767,7 @@ public class BSPrim : BSPhysObject if (!PhysBody.HasPhysicalBody) { // This would only happen if updates are called for during initialization when the body is not set up yet. - DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); + // DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); return; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index a4250be..809435d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -227,16 +227,41 @@ public sealed class BSShapeCollection : IDisposable if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { // Update prim.BSShape to reference a hull of this shape. - DereferenceExistingShape(prim, shapeCallback); - prim.PhysShape = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + BSShape potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + // If the current shape is not what is on the prim at the moment, time to change. + if (!prim.PhysShape.HasPhysicalShape + || potentialHull.ShapeType != prim.PhysShape.ShapeType + || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = potentialHull; + ret = true; + } + else + { + potentialHull.Dereference(m_physicsScene); + } if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", prim.LocalID, prim.PhysShape, prim.PhysShape.physShapeInfo.shapeKey.ToString("X")); } else { // Update prim.BSShape to reference a mesh of this shape. - DereferenceExistingShape(prim, shapeCallback); - prim.PhysShape = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + // If the current shape is not what is on the prim at the moment, time to change. + if (!prim.PhysShape.HasPhysicalShape + || potentialMesh.ShapeType != prim.PhysShape.ShapeType + || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = potentialMesh; + ret = true; + } + else + { + // We don't need this reference to the mesh that is already being using. + potentialMesh.Dereference(m_physicsScene); + } if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", prim.LocalID, prim.PhysShape, prim.PhysShape.physShapeInfo.shapeKey.ToString("X")); } @@ -320,7 +345,7 @@ public sealed class BSShapeCollection : IDisposable if (prim.IsSolid) { aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); - if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,body={1}", prim.LocalID, aBody); + if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,rigid,body={1}", prim.LocalID, aBody); } else { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index 2962249..3346626 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -115,10 +115,15 @@ public abstract class BSShape public override string ToString() { StringBuilder buff = new StringBuilder(); - buff.Append(""); @@ -184,21 +189,21 @@ public abstract class BSShape BSPhysObject xprim = prim; Util.FireAndForget(delegate { - physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID); + // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID); RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; if (assetProvider != null) { BSPhysObject yprim = xprim; // probably not necessary, but, just in case. assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) { - physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID); + // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID); bool assetFound = false; string mismatchIDs = String.Empty; // DEBUG DEBUG if (asset != null && yprim.BaseShape.SculptEntry) { if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) { - yprim.BaseShape.SculptData = (byte[])asset.Data.Clone(); + yprim.BaseShape.SculptData = asset.Data; // This will cause the prim to see that the filler shape is not the right // one and try again to build the object. // No race condition with the normal shape setting since the rebuild is at taint time. @@ -290,7 +295,7 @@ public class BSShapeNative : BSShape { if (physShapeInfo.HasPhysicalShape) { - physicsScene.DetailLog("{0},BSShapeNative.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); + physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); } physShapeInfo.Clear(); @@ -347,9 +352,8 @@ public class BSShapeMesh : BSShape float lod; System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - physicsScene.DetailLog("{0},BSShapeMesh,getReference,oldKey={1},newKey={2},size={3},lod={4}", - prim.LocalID, prim.PhysShape.physShapeInfo.shapeKey.ToString("X"), - newMeshKey.ToString("X"), prim.Size, lod); + physicsScene.DetailLog("{0},BSShapeMesh,getReference,newKey={1},size={2},lod={3}", + prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); BSShapeMesh retMesh = null; lock (Meshes) @@ -389,6 +393,7 @@ public class BSShapeMesh : BSShape lock (Meshes) { this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeMesh.Dereference,shape={1}", BSScene.DetailLogZero, this); // TODO: schedule aging and destruction of unused meshes. } } @@ -425,6 +430,12 @@ public class BSShapeMesh : BSShape if (meshData != null) { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + // Release the fetched asset data once it has been used. + pbs.SculptData = new byte[0]; + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; + } int[] indices = meshData.getIndexListAsInt(); int realIndicesIndex = indices.Length; @@ -462,8 +473,8 @@ public class BSShapeMesh : BSShape } } } - physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,origTri={1},realTri={2},numVerts={3}", - BSScene.DetailLogZero, indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); + physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}", + BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); if (realIndicesIndex != 0) { @@ -496,8 +507,8 @@ public class BSShapeHull : BSShape float lod; System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - physicsScene.DetailLog("{0},BSShapeHull,getReference,oldKey={1},newKey={2},size={3},lod={4}", - prim.LocalID, prim.PhysShape.physShapeInfo.shapeKey.ToString("X"), newHullKey.ToString("X"), prim.Size, lod); + physicsScene.DetailLog("{0},BSShapeHull,getReference,newKey={1},size={2},lod={3}", + prim.LocalID, newHullKey.ToString("X"), prim.Size, lod); BSShapeHull retHull = null; lock (Hulls) @@ -537,6 +548,7 @@ public class BSShapeHull : BSShape lock (Hulls) { this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this); // TODO: schedule aging and destruction of unused meshes. } } @@ -549,6 +561,8 @@ public class BSShapeHull : BSShape if (BSParam.ShouldUseBulletHACD) { + // Build the hull shape from an existing mesh shape. + // The mesh should have already been created in Bullet. physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID); BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); @@ -568,18 +582,26 @@ public class BSShapeHull : BSShape physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); + + // Now done with the mesh shape. + meshShape.Dereference(physicsScene); } - // Now done with the mesh shape. - meshShape.Dereference(physicsScene); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); } if (!newShape.HasPhysicalShape) { - // Build a new hull in the physical world. + // Build a new hull in the physical world using the C# HACD algorigthm. // Pass true for physicalness as this prevents the creation of bounding box which is not needed IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); if (meshData != null) { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + // Release the fetched asset data once it has been used. + pbs.SculptData = new byte[0]; + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; + } + int[] indices = meshData.getIndexListAsInt(); List vertices = meshData.getVertexList(); @@ -740,6 +762,7 @@ public class BSShapeCompound : BSShape lock (physShapeInfo) { this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this); if (referenceCount <= 0) { if (!physicsScene.PE.IsCompound(physShapeInfo)) -- cgit v1.1 From 4042c82a7293c40955a14d04d9e5ae05d35ef7cf Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 2 May 2013 12:27:30 -0700 Subject: BulletSim: prims with no cuts created with single convex hull shape. Parameter added to enable/disable this feature. --- OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | 5 ++ OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 3 + .../Physics/BulletSPlugin/BSShapeCollection.cs | 19 +++++- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 74 ++++++++++++++++++++++ 4 files changed, 99 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 5ebeace..2ac68e3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -87,6 +87,7 @@ public static class BSParam public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects public static bool ShouldRemoveZeroWidthTriangles { get; private set; } public static bool ShouldUseBulletHACD { get; set; } + public static bool ShouldUseSingleConvexHullForPrims { get; set; } public static float TerrainImplementation { get; private set; } public static int TerrainMeshMagnification { get; private set; } @@ -342,6 +343,10 @@ public static class BSParam false, (s) => { return ShouldUseBulletHACD; }, (s,v) => { ShouldUseBulletHACD = v; } ), + new ParameterDefn("ShouldUseSingleConvexHullForPrims", "If true, use a single convex hull shape for physical prims", + true, + (s) => { return ShouldUseSingleConvexHullForPrims; }, + (s,v) => { ShouldUseSingleConvexHullForPrims = v; } ), new ParameterDefn("CrossingFailuresBeforeOutOfBounds", "How forgiving we are about getting into adjactent regions", 5, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 8e05b58..a4a8794 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -316,7 +316,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters break; case "bulletxna": ret = new BSAPIXNA(engineName, this); + // Disable some features that are not implemented in BulletXNA + m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader); BSParam.ShouldUseBulletHACD = false; + BSParam.ShouldUseSingleConvexHullForPrims = false; break; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 809435d..794857c 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -226,8 +226,23 @@ public sealed class BSShapeCollection : IDisposable // made. Native shapes work in either case. if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { - // Update prim.BSShape to reference a hull of this shape. - BSShape potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + // Use a simple, single mesh convex hull shape if the object is simple enough + BSShape potentialHull = null; + + PrimitiveBaseShape pbs = prim.BaseShape; + if (BSParam.ShouldUseSingleConvexHullForPrims + && pbs != null + && !pbs.SculptEntry + && PrimHasNoCuts(pbs) + ) + { + potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim); + } + else + { + potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + } + // If the current shape is not what is on the prim at the moment, time to change. if (!prim.PhysShape.HasPhysicalShape || potentialHull.ShapeType != prim.PhysShape.ShapeType diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index 3346626..9ef2923 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -832,6 +832,80 @@ public class BSShapeCompound : BSShape } // ============================================================================================================ +public class BSShapeConvexHull : BSShape +{ + private static string LogHeader = "[BULLETSIM SHAPE CONVEX HULL]"; + public static Dictionary ConvexHulls = new Dictionary(); + + public BSShapeConvexHull(BulletShape pShape) : base(pShape) + { + } + public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) + { + float lod; + System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + physicsScene.DetailLog("{0},BSShapeMesh,getReference,newKey={1},size={2},lod={3}", + prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); + + BSShapeConvexHull retConvexHull = null; + lock (ConvexHulls) + { + if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull)) + { + // The mesh has already been created. Return a new reference to same. + retConvexHull.IncrementReference(); + } + else + { + retConvexHull = new BSShapeConvexHull(new BulletShape()); + BulletShape convexShape = null; + + // Get a handle to a mesh to build the hull from + BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim); + if (baseMesh.physShapeInfo.isNativeShape) + { + // We get here if the mesh was not creatable. Could be waiting for an asset from the disk. + // In the short term, we return the native shape and a later ForceBodyShapeRebuild should + // get back to this code with a buildable mesh. + // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed? + convexShape = baseMesh.physShapeInfo; + } + else + { + convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo); + convexShape.shapeKey = newMeshKey; + ConvexHulls.Add(convexShape.shapeKey, retConvexHull); + } + + // Done with the base mesh + baseMesh.Dereference(physicsScene); + + retConvexHull.physShapeInfo = convexShape; + } + } + return retConvexHull; + } + public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) + { + // Calling this reference means we want another handle to an existing shape + // (usually linksets) so return this copy. + IncrementReference(); + return this; + } + // Dereferencing a compound shape releases the hold on all the child shapes. + public override void Dereference(BSScene physicsScene) + { + lock (ConvexHulls) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this); + // TODO: schedule aging and destruction of unused meshes. + } + } +} + +// ============================================================================================================ public class BSShapeAvatar : BSShape { private static string LogHeader = "[BULLETSIM SHAPE AVATAR]"; -- cgit v1.1 From 304c5d4a8b8a1137bac18f7f6443ea85cec86184 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 3 May 2013 18:48:50 +0100 Subject: On startup, start scenes after we're set up all local scenes, rather than starting scenes before others have been created. This aims to avoid a race condition where scenes could look to inform neighbours that they were up before those neighbours had been created. http://opensimulator.org/mantis/view.php?id=6618 --- .../LoadRegions/LoadRegionsPlugin.cs | 23 ++++++++++++++-------- .../RemoteController/RemoteAdminPlugin.cs | 1 + OpenSim/Framework/IScene.cs | 7 ++++++- OpenSim/Region/Application/OpenSimBase.cs | 4 +--- OpenSim/Region/Framework/Scenes/Scene.cs | 16 +++++++++++++-- OpenSim/Region/Framework/Scenes/SceneBase.cs | 4 ++++ 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs index fcb6991..1d63d26 100644 --- a/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs +++ b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs @@ -115,6 +115,8 @@ namespace OpenSim.ApplicationPlugins.LoadRegions Environment.Exit(1); } + List createdScenes = new List(); + for (int i = 0; i < regionsToLoad.Length; i++) { IScene scene; @@ -123,17 +125,22 @@ namespace OpenSim.ApplicationPlugins.LoadRegions ")"); bool changed = m_openSim.PopulateRegionEstateInfo(regionsToLoad[i]); + m_openSim.CreateRegion(regionsToLoad[i], true, out scene); + createdScenes.Add(scene); + if (changed) - regionsToLoad[i].EstateSettings.Save(); - - if (scene != null) + regionsToLoad[i].EstateSettings.Save(); + } + + foreach (IScene scene in createdScenes) + { + scene.Start(); + + m_newRegionCreatedHandler = OnNewRegionCreated; + if (m_newRegionCreatedHandler != null) { - m_newRegionCreatedHandler = OnNewRegionCreated; - if (m_newRegionCreatedHandler != null) - { - m_newRegionCreatedHandler(scene); - } + m_newRegionCreatedHandler(scene); } } } diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index d19b8b6..355f7b3 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -700,6 +700,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController IScene newScene; m_application.CreateRegion(region, out newScene); + newScene.Start(); // If an access specification was provided, use it. // Otherwise accept the default. diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 87ec99e..8164f41 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -136,5 +136,10 @@ namespace OpenSim.Framework ISceneObject DeserializeObject(string representation); bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); + + /// + /// Start the scene and associated scripts within it. + /// + void Start(); } -} +} \ No newline at end of file diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index d86eefe..f9e0cf1 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -425,9 +425,6 @@ namespace OpenSim mscene = scene; - scene.Start(); - scene.StartScripts(); - return clientServers; } @@ -751,6 +748,7 @@ namespace OpenSim ShutdownClientServer(whichRegion); IScene scene; CreateRegion(whichRegion, true, out scene); + scene.Start(); } # region Setup methods diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 829a7e9..4f674a3 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -389,10 +389,12 @@ namespace OpenSim.Region.Framework.Scenes if (value) { if (!m_active) - Start(); + Start(false); } else { + // This appears assymetric with Start() above but is not - setting m_active = false stops the loops + // XXX: Possibly this should be in an explicit Stop() method for symmetry. m_active = false; } } @@ -1331,10 +1333,18 @@ namespace OpenSim.Region.Framework.Scenes } } + public override void Start() + { + Start(true); + } + /// /// Start the scene /// - public void Start() + /// + /// Start the scripts within the scene. + /// + public void Start(bool startScripts) { m_active = true; @@ -1353,6 +1363,8 @@ namespace OpenSim.Region.Framework.Scenes m_heartbeatThread = Watchdog.StartThread( Heartbeat, string.Format("Heartbeat ({0})", RegionInfo.RegionName), ThreadPriority.Normal, false, false); + + StartScripts(); } /// diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index d3e968e..d2097ea 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -561,6 +561,10 @@ namespace OpenSim.Region.Framework.Scenes get { return false; } } + public virtual void Start() + { + } + public void Restart() { // This has to be here to fire the event -- cgit v1.1 From 5d93c99e8cf188b29b4c5265619eb4a4d3eeacf6 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 3 May 2013 18:56:58 +0100 Subject: Fix possible race condition with local region cache if a region was added after startup. --- .../Grid/LocalGridServiceConnector.cs | 43 ++++++++++++++-------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs index c0c2ca7..c32820e 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/LocalGridServiceConnector.cs @@ -142,10 +142,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid scene.RegisterModuleInterface(this); - if (m_LocalCache.ContainsKey(scene.RegionInfo.RegionID)) - m_log.ErrorFormat("[LOCAL GRID SERVICE CONNECTOR]: simulator seems to have more than one region with the same UUID. Please correct this!"); - else - m_LocalCache.Add(scene.RegionInfo.RegionID, new RegionCache(scene)); + lock (m_LocalCache) + { + if (m_LocalCache.ContainsKey(scene.RegionInfo.RegionID)) + m_log.ErrorFormat("[LOCAL GRID SERVICE CONNECTOR]: simulator seems to have more than one region with the same UUID. Please correct this!"); + else + m_LocalCache.Add(scene.RegionInfo.RegionID, new RegionCache(scene)); + } } public void RemoveRegion(Scene scene) @@ -153,8 +156,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid if (!m_Enabled) return; - m_LocalCache[scene.RegionInfo.RegionID].Clear(); - m_LocalCache.Remove(scene.RegionInfo.RegionID); + lock (m_LocalCache) + { + m_LocalCache[scene.RegionInfo.RegionID].Clear(); + m_LocalCache.Remove(scene.RegionInfo.RegionID); + } } public void RegionLoaded(Scene scene) @@ -191,12 +197,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid // First see if it's a neighbour, even if it isn't on this sim. // Neighbour data is cached in memory, so this is fast - foreach (RegionCache rcache in m_LocalCache.Values) + + lock (m_LocalCache) { - region = rcache.GetRegionByPosition(x, y); - if (region != null) + foreach (RegionCache rcache in m_LocalCache.Values) { - return region; + region = rcache.GetRegionByPosition(x, y); + if (region != null) + { + return region; + } } } @@ -245,12 +255,15 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid { System.Text.StringBuilder caps = new System.Text.StringBuilder(); - foreach (KeyValuePair kvp in m_LocalCache) + lock (m_LocalCache) { - caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key); - List regions = kvp.Value.GetNeighbours(); - foreach (GridRegion r in regions) - caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize); + foreach (KeyValuePair kvp in m_LocalCache) + { + caps.AppendFormat("*** Neighbours of {0} ({1}) ***\n", kvp.Value.RegionName, kvp.Key); + List regions = kvp.Value.GetNeighbours(); + foreach (GridRegion r in regions) + caps.AppendFormat(" {0} @ {1}-{2}\n", r.RegionName, r.RegionLocX / Constants.RegionSize, r.RegionLocY / Constants.RegionSize); + } } MainConsole.Instance.Output(caps.ToString()); -- cgit v1.1 From 5d25bb3084937d266375cba61b3a2c802bd57717 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Fri, 3 May 2013 14:23:53 -0700 Subject: BulletSim: zero vehicle motion when changing vehicle type. Rebuild compound linkset of any child in the linkset changes shape. Comments and better detailed logging messages. --- OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 6 +++--- .../Region/Physics/BulletSPlugin/BSLinksetCompound.cs | 16 +++++----------- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs | 4 ++++ .../Region/Physics/BulletSPlugin/BSShapeCollection.cs | 7 +++---- OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 9 ++------- 6 files changed, 18 insertions(+), 26 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index b9bd909..c5bee6d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -559,9 +559,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin break; } - // Update any physical parameters based on this type. - Refresh(); - m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, m_linearMotorDecayTimescale, m_linearFrictionTimescale, 1f); @@ -589,6 +586,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin { RegisterForSceneEvents(); } + + // Update any physical parameters based on this type. + Refresh(); } #endregion // Vehicle parameter setting diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index 20eb871..1f16cc8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -246,7 +246,8 @@ public sealed class BSLinksetCompound : BSLinkset } // Routine called when rebuilding the body of some member of the linkset. - // Since we don't keep in world relationships, do nothing unless it's a child changing. + // If one of the bodies is being changed, the linkset needs rebuilding. + // For instance, a linkset is built and then a mesh asset is read in and the mesh is recreated. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! public override bool RemoveDependencies(BSPrimLinkable child) @@ -256,14 +257,7 @@ public sealed class BSLinksetCompound : BSLinkset DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child)); - if (!IsRoot(child)) - { - child.LinksetInfo = null; - } - - // Cannot schedule a refresh/rebuild here because this routine is called when - // the linkset is being rebuilt. - // InternalRefresh(LinksetRoot); + ScheduleRebuild(child); return ret; } @@ -322,7 +316,7 @@ public sealed class BSLinksetCompound : BSLinkset // Constraint linksets are rebuilt every time. // Note that this works for rebuilding just the root after a linkset is taken apart. // Called at taint time!! - private bool UseBulletSimRootOffsetHack = false; + private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape private bool disableCOM = true; // For basic linkset debugging, turn off the center-of-mass setting private void RecomputeLinksetCompound() { @@ -382,7 +376,7 @@ public sealed class BSLinksetCompound : BSLinkset OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}", - LinksetRoot.LocalID, memberIndex, cPrim.PhysShape, offsetPos, offsetRot); + LinksetRoot.LocalID, memberIndex, childShape, offsetPos, offsetRot); // Since we are borrowing the shape of the child, disable the origional child body if (!IsRoot(cPrim)) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 3d68d7f..d3f3475 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -511,7 +511,7 @@ public class BSPrim : BSPhysObject PhysScene.TaintedObject("setVehicleType", delegate() { - // Vehicle code changes the parameters for this vehicle type. + ZeroMotion(true /* inTaintTime */); VehicleActor.ProcessTypeChange(type); ActivateIfPhysical(false); }); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs index 5236909..235da78 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs @@ -182,6 +182,10 @@ public class BSPrimLinkable : BSPrimDisplaced { return false; } + + // TODO: handle collisions of other objects with with children of linkset. + // This is a problem for LinksetCompound since the children are packed into the root. + return base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 794857c..64aaa15 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -254,10 +254,10 @@ public sealed class BSShapeCollection : IDisposable } else { + // The current shape on the prim is the correct one. We don't need the potential reference. potentialHull.Dereference(m_physicsScene); } - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", - prim.LocalID, prim.PhysShape, prim.PhysShape.physShapeInfo.shapeKey.ToString("X")); + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape); } else { @@ -277,8 +277,7 @@ public sealed class BSShapeCollection : IDisposable // We don't need this reference to the mesh that is already being using. potentialMesh.Dereference(m_physicsScene); } - if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", - prim.LocalID, prim.PhysShape, prim.PhysShape.physShapeInfo.shapeKey.ToString("X")); + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape); } return ret; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index 9ef2923..3e4ee5a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs @@ -352,9 +352,6 @@ public class BSShapeMesh : BSShape float lod; System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - physicsScene.DetailLog("{0},BSShapeMesh,getReference,newKey={1},size={2},lod={3}", - prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); - BSShapeMesh retMesh = null; lock (Meshes) { @@ -380,6 +377,7 @@ public class BSShapeMesh : BSShape retMesh.physShapeInfo = newShape; } } + physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod); return retMesh; } public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) @@ -507,9 +505,6 @@ public class BSShapeHull : BSShape float lod; System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); - physicsScene.DetailLog("{0},BSShapeHull,getReference,newKey={1},size={2},lod={3}", - prim.LocalID, newHullKey.ToString("X"), prim.Size, lod); - BSShapeHull retHull = null; lock (Hulls) { @@ -531,10 +526,10 @@ public class BSShapeHull : BSShape // If a mesh was what was created, remember the built shape for later sharing. Hulls.Add(newHullKey, retHull); } - retHull.physShapeInfo = newShape; } } + physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod); return retHull; } public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) -- cgit v1.1