merge with release
commit
a2a6bf20d7
|
|
@ -74,4 +74,6 @@ glob:indra/newview/filters.xml
|
|||
glob:indra/newview/avatar_icons_cache.txt
|
||||
glob:indra/newview/avatar_lad.log
|
||||
glob:*.diff
|
||||
#*.rej
|
||||
indra/newview/pilot.txt
|
||||
indra/newview/pilot.xml
|
||||
*.rej
|
||||
|
|
|
|||
|
|
@ -498,9 +498,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>10352aab979c333a52dbad21b6e6fba9</string>
|
||||
<string>10352aab979c333a52dbad21b6e6fba9</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-fmodex-private/rev/274403/arch/Darwin/installer/fmodex-4.44-darwin-20130419.tar.bz2</string>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-fmodex-private/rev/274403/arch/Darwin/installer/fmodex-4.44-darwin-20130419.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
|
|
@ -510,7 +510,7 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>79e45527aa9fb90b813599dff5ce01a7</string>
|
||||
<string>79e45527aa9fb90b813599dff5ce01a7</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-fmodex-private/rev/274378/arch/Linux/installer/fmodex-4.44-linux-20130419.tar.bz2</string>
|
||||
</map>
|
||||
|
|
@ -762,9 +762,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>aff5566e04003de0383941981198e04e</string>
|
||||
<string>aff5566e04003de0383941981198e04e</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273073/arch/Darwin/installer/google_breakpad-0.0.0-rev1099-darwin-20130329.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273073/arch/Darwin/installer/google_breakpad-0.0.0-rev1099-darwin-20130329.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin</string>
|
||||
|
|
@ -776,7 +776,7 @@
|
|||
<key>hash</key>
|
||||
<string>52257e5eb166a0b69c9c0c38f6e1920e</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273079/arch/Linux/installer/google_breakpad-0.0.0-rev1099-linux-20130329.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273079/arch/Linux/installer/google_breakpad-0.0.0-rev1099-linux-20130329.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
|
|
@ -788,7 +788,7 @@
|
|||
<key>hash</key>
|
||||
<string>d812a6dfcabe6528198a3191068dac09</string>
|
||||
<key>url</key>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273073/arch/CYGWIN/installer/google_breakpad-0.0.0-rev1099-windows-20130329.tar.bz2</string>
|
||||
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273073/arch/CYGWIN/installer/google_breakpad-0.0.0-rev1099-windows-20130329.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows</string>
|
||||
|
|
@ -1326,9 +1326,9 @@
|
|||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>5bc44db15eb3cca021382e40e04a9a38</string>
|
||||
<string>9e5461d203b8259d3b6eb7df8c18b8fa</string>
|
||||
<key>url</key>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearanceutility-source/rev/271972/arch/Linux/installer/llappearanceutility_source-0.1-linux-20130315.tar.bz2</string>
|
||||
<string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearanceutility-interesting-source/rev/277616/arch/Linux/installer/llappearanceutility_source-0.1-linux-20130620.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>linux</string>
|
||||
|
|
@ -2567,7 +2567,7 @@
|
|||
<string>"Visual Studio 10"</string>
|
||||
<string>-DUNATTENDED:BOOL=ON</string>
|
||||
<string>-DUSE_KDU=FALSE</string>
|
||||
</array>
|
||||
</array>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>DebugOS</string>
|
||||
|
|
@ -2655,7 +2655,7 @@
|
|||
<string>-DUNATTENDED:BOOL=ON</string>
|
||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
||||
<string>-DUSE_KDU=FALSE</string>
|
||||
</array>
|
||||
</array>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>RelWithDebInfoOS</string>
|
||||
|
|
@ -2742,7 +2742,7 @@
|
|||
<string>-DUNATTENDED:BOOL=ON</string>
|
||||
<string>-DINSTALL_PROPRIETARY=FALSE</string>
|
||||
<string>-DUSE_KDU=FALSE</string>
|
||||
</array>
|
||||
</array>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>ReleaseOS</string>
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ if (WINDOWS)
|
|||
|
||||
add_definitions(
|
||||
/DLL_WINDOWS=1
|
||||
/DNOMINMAX
|
||||
/DDOM_DYNAMIC
|
||||
/DUNICODE
|
||||
/D_UNICODE
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
#include "llavatarappearancedefines.h"
|
||||
#include "llavatarjointmesh.h"
|
||||
#include "imageids.h"
|
||||
#include "lldir.h"
|
||||
#include "lldeleteutils.h"
|
||||
#include "lldir.h"
|
||||
#include "llpolymorph.h"
|
||||
#include "llpolymesh.h"
|
||||
#include "llpolyskeletaldistortion.h"
|
||||
|
|
|
|||
|
|
@ -117,7 +117,6 @@ BOOL LLSkinJoint::setupSkinJoint( LLAvatarJoint *joint)
|
|||
//-----------------------------------------------------------------------------
|
||||
|
||||
BOOL LLAvatarJointMesh::sPipelineRender = FALSE;
|
||||
EAvatarRenderPass LLAvatarJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
|
||||
U32 LLAvatarJointMesh::sClothingMaskImageName = 0;
|
||||
LLColor4 LLAvatarJointMesh::sClothingInnerColor;
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ public:
|
|||
static BOOL sPipelineRender;
|
||||
//RN: this is here for testing purposes
|
||||
static U32 sClothingMaskImageName;
|
||||
static EAvatarRenderPass sRenderPass;
|
||||
static LLColor4 sClothingInnerColor;
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "llxmltree.h"
|
||||
#include "llendianswizzle.h"
|
||||
#include "llpolymesh.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
//#include "../tools/imdebug/imdebug.h"
|
||||
|
||||
|
|
@ -619,9 +620,9 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
|
|||
tangent.setCross3(scaled_binormals[vert_index_mesh], norm);
|
||||
LLVector4a& normalized_binormal = binormals[vert_index_mesh];
|
||||
|
||||
normalized_binormal.setCross3(norm, tangent);
|
||||
normalized_binormal.setCross3(norm, tangent);
|
||||
normalized_binormal.normalize3fast();
|
||||
|
||||
|
||||
tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,18 +29,11 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
#include "llpreprocessor.h"
|
||||
#include "llerrorlegacy.h"
|
||||
//#include "llcommon.h"
|
||||
//#include "llmemory.h"
|
||||
#include "llavatarappearance.h"
|
||||
#include "llavatarjoint.h"
|
||||
#include "llpolymorph.h"
|
||||
//#include "llviewercontrol.h"
|
||||
//#include "llxmltree.h"
|
||||
//#include "llvoavatar.h"
|
||||
#include "llwearable.h"
|
||||
//#include "lldir.h"
|
||||
//#include "llvolume.h"
|
||||
//#include "llendianswizzle.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
#include "llpolyskeletaldistortion.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -38,11 +38,13 @@
|
|||
#include "llvfs.h"
|
||||
#include "lltexlayerparams.h"
|
||||
#include "lltexturemanagerbridge.h"
|
||||
#include "lllocaltextureobject.h"
|
||||
#include "../llui/llui.h"
|
||||
#include "llwearable.h"
|
||||
#include "llwearabledata.h"
|
||||
#include "llvertexbuffer.h"
|
||||
#include "llviewervisualparam.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
//#include "../tools/imdebug/imdebug.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "lltexturemanagerbridge.h"
|
||||
#include "../llui/llui.h"
|
||||
#include "llwearable.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LLTexLayerParam
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@
|
|||
#include "llpermissions.h"
|
||||
#include "llsaleinfo.h"
|
||||
#include "llwearabletype.h"
|
||||
#include "lllocaltextureobject.h"
|
||||
|
||||
class LLMD5;
|
||||
class LLVisualParam;
|
||||
class LLTexGlobalColorInfo;
|
||||
class LLTexGlobalColor;
|
||||
class LLLocalTextureObject;
|
||||
class LLAvatarAppearance;
|
||||
|
||||
// Abstract class.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include "vorbis/codec.h"
|
||||
#include "vorbis/vorbisfile.h"
|
||||
#include <iterator>
|
||||
|
||||
extern LLAudioEngine *gAudiop;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "llcharacter.h"
|
||||
#include "llstring.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
#define SKEL_HEADER "Linden Skeleton 1.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
|
|||
mIKSolver.solve();
|
||||
|
||||
// use blending...
|
||||
F32 slerp_amt = LLCriticalDamp::getInterpolant(TARGET_LAG_HALF_LIFE);
|
||||
F32 slerp_amt = LLSmoothInterpolation::getInterpolant(TARGET_LAG_HALF_LIFE);
|
||||
shoulderRot = slerp(slerp_amt, mShoulderJoint.getRotation(), shoulderRot);
|
||||
elbowRot = slerp(slerp_amt, mElbowJoint.getRotation(), elbowRot);
|
||||
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask)
|
|||
LLQuaternion currentRootRotWorld = mRootJoint->getWorldRotation();
|
||||
LLQuaternion currentInvRootRotWorld = ~currentRootRotWorld;
|
||||
|
||||
F32 head_slerp_amt = LLCriticalDamp::getInterpolant(HEAD_LOOKAT_LAG_HALF_LIFE);
|
||||
F32 torso_slerp_amt = LLCriticalDamp::getInterpolant(TORSO_LOOKAT_LAG_HALF_LIFE);
|
||||
F32 head_slerp_amt = LLSmoothInterpolation::getInterpolant(HEAD_LOOKAT_LAG_HALF_LIFE);
|
||||
F32 torso_slerp_amt = LLSmoothInterpolation::getInterpolant(TORSO_LOOKAT_LAG_HALF_LIFE);
|
||||
|
||||
LLVector3* targetPos = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
// Header Files
|
||||
//-----------------------------------------------------------------------------
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include "linked_lists.h"
|
||||
#include "v3math.h"
|
||||
|
|
|
|||
|
|
@ -1031,11 +1031,11 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
|
|||
if (constraint->mSharedData->mChainLength != 0 &&
|
||||
dist_vec_squared(root_pos, target_pos) * 0.95f > constraint->mTotalLength * constraint->mTotalLength)
|
||||
{
|
||||
constraint->mWeight = lerp(constraint->mWeight, 0.f, LLCriticalDamp::getInterpolant(0.1f));
|
||||
constraint->mWeight = LLSmoothInterpolation::lerp(constraint->mWeight, 0.f, 0.1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
constraint->mWeight = lerp(constraint->mWeight, 1.f, LLCriticalDamp::getInterpolant(0.3f));
|
||||
constraint->mWeight = LLSmoothInterpolation::lerp(constraint->mWeight, 1.f, 0.3f);
|
||||
}
|
||||
|
||||
F32 weight = constraint->mWeight * ((shared_data->mEaseOutStopTime == 0.f) ? 1.f :
|
||||
|
|
@ -1082,9 +1082,9 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
|
|||
// convert intermediate joint positions to world coordinates
|
||||
positions[joint_num] = ( constraint->mPositions[joint_num] * mPelvisp->getWorldRotation()) + mPelvisp->getWorldPosition();
|
||||
F32 time_constant = 1.f / clamp_rescale(constraint->mFixupDistanceRMS, 0.f, 0.5f, 0.2f, 8.f);
|
||||
// llinfos << "Interpolant " << LLCriticalDamp::getInterpolant(time_constant, FALSE) << " and fixup distance " << constraint->mFixupDistanceRMS << " on " << mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
|
||||
// llinfos << "Interpolant " << LLSmoothInterpolation::getInterpolant(time_constant, FALSE) << " and fixup distance " << constraint->mFixupDistanceRMS << " on " << mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
|
||||
positions[joint_num] = lerp(positions[joint_num], kinematic_position,
|
||||
LLCriticalDamp::getInterpolant(time_constant, FALSE));
|
||||
LLSmoothInterpolation::getInterpolant(time_constant, FALSE));
|
||||
}
|
||||
|
||||
S32 iteration_count;
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
|
|||
// but this will cause the animation playback rate calculation below to
|
||||
// kick in too slowly and sometimes start playing the animation in reverse.
|
||||
|
||||
//mPelvisOffset -= PELVIS_COMPENSATION_WIEGHT * (foot_slip_vector * world_to_avatar_rot);//lerp(LLVector3::zero, -1.f * (foot_slip_vector * world_to_avatar_rot), LLCriticalDamp::getInterpolant(0.1f));
|
||||
//mPelvisOffset -= PELVIS_COMPENSATION_WIEGHT * (foot_slip_vector * world_to_avatar_rot);//lerp(LLVector3::zero, -1.f * (foot_slip_vector * world_to_avatar_rot), LLSmoothInterpolation::getInterpolant(0.1f));
|
||||
|
||||
////F32 drift_comp_max = DRIFT_COMP_MAX_TOTAL * (llclamp(speed, 0.f, DRIFT_COMP_MAX_SPEED) / DRIFT_COMP_MAX_SPEED);
|
||||
//F32 drift_comp_max = DRIFT_COMP_MAX_TOTAL;
|
||||
|
|
@ -287,7 +287,7 @@ BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
|
|||
F32 desired_speed_multiplier = llclamp(speed / foot_speed, min_speed_multiplier, ANIM_SPEED_MAX);
|
||||
|
||||
// blend towards new speed adjustment value
|
||||
F32 new_speed_adjust = lerp(mAdjustedSpeed, desired_speed_multiplier, LLCriticalDamp::getInterpolant(SPEED_ADJUST_TIME_CONSTANT));
|
||||
F32 new_speed_adjust = LLSmoothInterpolation::lerp(mAdjustedSpeed, desired_speed_multiplier, SPEED_ADJUST_TIME_CONSTANT);
|
||||
|
||||
// limit that rate at which the speed adjustment changes
|
||||
F32 speedDelta = llclamp(new_speed_adjust - mAdjustedSpeed, -SPEED_ADJUST_MAX_SEC * delta_time, SPEED_ADJUST_MAX_SEC * delta_time);
|
||||
|
|
@ -305,8 +305,8 @@ BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
|
|||
{ // standing/turning
|
||||
|
||||
// damp out speed adjustment to 0
|
||||
mAnimSpeed = lerp(mAnimSpeed, 1.f, LLCriticalDamp::getInterpolant(0.2f));
|
||||
//mPelvisOffset = lerp(mPelvisOffset, LLVector3::zero, LLCriticalDamp::getInterpolant(0.2f));
|
||||
mAnimSpeed = LLSmoothInterpolation::lerp(mAnimSpeed, 1.f, 0.2f);
|
||||
//mPelvisOffset = lerp(mPelvisOffset, LLVector3::zero, LLSmoothInterpolation::getInterpolant(0.2f));
|
||||
}
|
||||
|
||||
// broadcast walk speed change
|
||||
|
|
@ -383,7 +383,7 @@ BOOL LLFlyAdjustMotion::onUpdate(F32 time, U8* joint_mask)
|
|||
F32 target_roll = llclamp(ang_vel.mV[VZ], -4.f, 4.f) * roll_factor;
|
||||
|
||||
// roll is critically damped interpolation between current roll and angular velocity-derived target roll
|
||||
mRoll = lerp(mRoll, target_roll, LLCriticalDamp::getInterpolant(0.1f));
|
||||
mRoll = LLSmoothInterpolation::lerp(mRoll, target_roll, LLUnit<F32, LLUnits::Milliseconds>(100));
|
||||
|
||||
LLQuaternion roll(mRoll, LLVector3(0.f, 0.f, 1.f));
|
||||
mPelvisState->setRotation(roll);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ void LLMotion::fadeOut()
|
|||
{
|
||||
if (mFadeWeight > 0.01f)
|
||||
{
|
||||
mFadeWeight = lerp(mFadeWeight, 0.f, LLCriticalDamp::getInterpolant(0.15f));
|
||||
mFadeWeight = lerp(mFadeWeight, 0.f, LLSmoothInterpolation::getInterpolant(0.15f));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -88,7 +88,7 @@ void LLMotion::fadeIn()
|
|||
{
|
||||
if (mFadeWeight < 0.99f)
|
||||
{
|
||||
mFadeWeight = lerp(mFadeWeight, 1.f, LLCriticalDamp::getInterpolant(0.15f));
|
||||
mFadeWeight = lerp(mFadeWeight, 1.f, LLSmoothInterpolation::getInterpolant(0.15f));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "linden_common.h"
|
||||
|
||||
#include "llmotioncontroller.h"
|
||||
#include "llfasttimer.h"
|
||||
#include "llkeyframemotion.h"
|
||||
#include "llmath.h"
|
||||
#include "lltimer.h"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
#include "lluuidhashmap.h"
|
||||
#include "llmotion.h"
|
||||
#include "llpose.h"
|
||||
#include "llframetimer.h"
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ BOOL LLTargetingMotion::onActivate()
|
|||
//-----------------------------------------------------------------------------
|
||||
BOOL LLTargetingMotion::onUpdate(F32 time, U8* joint_mask)
|
||||
{
|
||||
F32 slerp_amt = LLCriticalDamp::getInterpolant(TORSO_TARGET_HALF_LIFE);
|
||||
F32 slerp_amt = LLSmoothInterpolation::getInterpolant(TORSO_TARGET_HALF_LIFE);
|
||||
|
||||
LLVector3 target;
|
||||
LLVector3* lookAtPoint = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
|
||||
|
|
|
|||
|
|
@ -75,8 +75,10 @@ set(llcommon_SOURCE_FILES
|
|||
llmetrics.cpp
|
||||
llmetricperformancetester.cpp
|
||||
llmortician.cpp
|
||||
llmutex.cpp
|
||||
lloptioninterface.cpp
|
||||
llptrto.cpp
|
||||
llpredicate.cpp
|
||||
llprocess.cpp
|
||||
llprocessor.cpp
|
||||
llqueuedthread.cpp
|
||||
|
|
@ -90,7 +92,6 @@ set(llcommon_SOURCE_FILES
|
|||
llsdutil.cpp
|
||||
llsecondlifeurls.cpp
|
||||
llsingleton.cpp
|
||||
llstat.cpp
|
||||
llstacktrace.cpp
|
||||
llstreamqueue.cpp
|
||||
llstreamtools.cpp
|
||||
|
|
@ -98,14 +99,15 @@ set(llcommon_SOURCE_FILES
|
|||
llstringtable.cpp
|
||||
llsys.cpp
|
||||
llthread.cpp
|
||||
llthreadlocalstorage.cpp
|
||||
llthreadsafequeue.cpp
|
||||
lltimer.cpp
|
||||
lltrace.cpp
|
||||
lltracerecording.cpp
|
||||
lltracethreadrecorder.cpp
|
||||
lluri.cpp
|
||||
lluuid.cpp
|
||||
llworkerthread.cpp
|
||||
metaclass.cpp
|
||||
metaproperty.cpp
|
||||
reflective.cpp
|
||||
timing.cpp
|
||||
u64.cpp
|
||||
)
|
||||
|
|
@ -115,7 +117,6 @@ set(llcommon_HEADER_FILES
|
|||
|
||||
bitpack.h
|
||||
ctype_workaround.h
|
||||
doublelinkedlist.h
|
||||
fix_macros.h
|
||||
imageids.h
|
||||
indra_constants.h
|
||||
|
|
@ -129,7 +130,6 @@ set(llcommon_HEADER_FILES
|
|||
llapp.h
|
||||
llapr.h
|
||||
llassettype.h
|
||||
llassoclist.h
|
||||
llavatarconstants.h
|
||||
llbase32.h
|
||||
llbase64.h
|
||||
|
|
@ -143,18 +143,14 @@ set(llcommon_HEADER_FILES
|
|||
llcriticaldamp.h
|
||||
llcursortypes.h
|
||||
lldarray.h
|
||||
lldarrayptr.h
|
||||
lldate.h
|
||||
lldefs.h
|
||||
lldependencies.h
|
||||
lldeleteutils.h
|
||||
lldepthstack.h
|
||||
lldictionary.h
|
||||
lldlinked.h
|
||||
lldoubledispatch.h
|
||||
lldqueueptr.h
|
||||
llendianswizzle.h
|
||||
llenum.h
|
||||
llerror.h
|
||||
llerrorcontrol.h
|
||||
llerrorlegacy.h
|
||||
|
|
@ -178,18 +174,15 @@ set(llcommon_HEADER_FILES
|
|||
llhash.h
|
||||
llheartbeat.h
|
||||
llhttpstatuscodes.h
|
||||
llindexedqueue.h
|
||||
llinitparam.h
|
||||
llinstancetracker.h
|
||||
llkeythrottle.h
|
||||
lllazy.h
|
||||
llleap.h
|
||||
llleaplistener.h
|
||||
lllistenerwrapper.h
|
||||
lllinkedqueue.h
|
||||
llliveappconfig.h
|
||||
lllivefile.h
|
||||
lllocalidhashmap.h
|
||||
lllog.h
|
||||
lllslconstants.h
|
||||
llmap.h
|
||||
|
|
@ -199,15 +192,15 @@ set(llcommon_HEADER_FILES
|
|||
llmetrics.h
|
||||
llmetricperformancetester.h
|
||||
llmortician.h
|
||||
llmutex.h
|
||||
llnametable.h
|
||||
lloptioninterface.h
|
||||
llpointer.h
|
||||
llpredicate.h
|
||||
llpreprocessor.h
|
||||
llpriqueuemap.h
|
||||
llprocess.h
|
||||
llprocessor.h
|
||||
llptrskiplist.h
|
||||
llptrskipmap.h
|
||||
llptrto.h
|
||||
llqueuedthread.h
|
||||
llrand.h
|
||||
|
|
@ -224,12 +217,7 @@ set(llcommon_HEADER_FILES
|
|||
llsecondlifeurls.h
|
||||
llsimplehash.h
|
||||
llsingleton.h
|
||||
llskiplist.h
|
||||
llskipmap.h
|
||||
llsortedvector.h
|
||||
llstack.h
|
||||
llstacktrace.h
|
||||
llstat.h
|
||||
llstatenums.h
|
||||
llstl.h
|
||||
llstreamqueue.h
|
||||
|
|
@ -239,22 +227,21 @@ set(llcommon_HEADER_FILES
|
|||
llstringtable.h
|
||||
llsys.h
|
||||
llthread.h
|
||||
llthreadlocalstorage.h
|
||||
llthreadsafequeue.h
|
||||
lltimer.h
|
||||
lltrace.h
|
||||
lltracerecording.h
|
||||
lltracethreadrecorder.h
|
||||
lltreeiterators.h
|
||||
lltypeinfolookup.h
|
||||
llunit.h
|
||||
lluri.h
|
||||
lluuid.h
|
||||
lluuidhashmap.h
|
||||
llversionserver.h
|
||||
llwin32headers.h
|
||||
llwin32headerslean.h
|
||||
llworkerthread.h
|
||||
ll_template_cast.h
|
||||
metaclass.h
|
||||
metaclasst.h
|
||||
metaproperty.h
|
||||
metapropertyt.h
|
||||
reflective.h
|
||||
reflectivet.h
|
||||
roles_constants.h
|
||||
stdenums.h
|
||||
stdtypes.h
|
||||
|
|
@ -326,7 +313,6 @@ if (LL_TESTS)
|
|||
LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
|
||||
|
|
@ -334,7 +320,7 @@ if (LL_TESTS)
|
|||
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_CONTEXT_LIBRARY}")
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -29,6 +29,7 @@
|
|||
#include "linden_common.h"
|
||||
#include "llapr.h"
|
||||
#include "apr_dso.h"
|
||||
#include "llthreadlocalstorage.h"
|
||||
|
||||
apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
|
||||
LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool.
|
||||
|
|
@ -37,12 +38,15 @@ apr_thread_mutex_t *gCallStacksLogMutexp = NULL;
|
|||
|
||||
const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool
|
||||
|
||||
bool gAPRInitialized = false;
|
||||
|
||||
void ll_init_apr()
|
||||
{
|
||||
// Initialize APR and create the global pool
|
||||
apr_initialize();
|
||||
|
||||
if (!gAPRPoolp)
|
||||
{
|
||||
// Initialize APR and create the global pool
|
||||
apr_initialize();
|
||||
apr_pool_create(&gAPRPoolp, NULL);
|
||||
|
||||
// Initialize the logging mutex
|
||||
|
|
@ -54,11 +58,21 @@ void ll_init_apr()
|
|||
{
|
||||
LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(FALSE) ;
|
||||
}
|
||||
|
||||
LLThreadLocalPointerBase::initAllThreadLocalStorage();
|
||||
gAPRInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
bool ll_apr_is_initialized()
|
||||
{
|
||||
return gAPRInitialized;
|
||||
}
|
||||
|
||||
void ll_cleanup_apr()
|
||||
{
|
||||
gAPRInitialized = false;
|
||||
|
||||
LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
|
||||
|
||||
if (gLogMutexp)
|
||||
|
|
@ -77,6 +91,9 @@ void ll_cleanup_apr()
|
|||
apr_thread_mutex_destroy(gCallStacksLogMutexp);
|
||||
gCallStacksLogMutexp = NULL;
|
||||
}
|
||||
|
||||
LLThreadLocalPointerBase::destroyAllThreadLocalStorage();
|
||||
|
||||
if (gAPRPoolp)
|
||||
{
|
||||
apr_pool_destroy(gAPRPoolp);
|
||||
|
|
|
|||
|
|
@ -32,28 +32,34 @@
|
|||
#if LL_LINUX || LL_SOLARIS
|
||||
#include <sys/param.h> // Need PATH_MAX in APR headers...
|
||||
#endif
|
||||
#if LL_WINDOWS
|
||||
// Limit Windows API to small and manageable set.
|
||||
// If you get undefined symbols, find the appropriate
|
||||
// Windows header file and include that in your .cpp file.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include "llwin32headerslean.h"
|
||||
#include "apr_thread_proc.h"
|
||||
#include "apr_thread_mutex.h"
|
||||
#include "apr_getopt.h"
|
||||
#include "apr_signal.h"
|
||||
#include "apr_atomic.h"
|
||||
|
||||
#include "llstring.h"
|
||||
|
||||
extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
|
||||
extern apr_thread_mutex_t* gCallStacksLogMutexp;
|
||||
|
||||
struct apr_dso_handle_t;
|
||||
/**
|
||||
* @brief Function which appropriately logs error or remains quiet on
|
||||
* APR_SUCCESS.
|
||||
* @return Returns <code>true</code> if status is an error condition.
|
||||
*/
|
||||
bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
|
||||
/// There's a whole other APR error-message function if you pass a DSO handle.
|
||||
bool LL_COMMON_API ll_apr_warn_status(apr_status_t status, apr_dso_handle_t* handle);
|
||||
|
||||
void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
|
||||
void LL_COMMON_API ll_apr_assert_status(apr_status_t status, apr_dso_handle_t* handle);
|
||||
|
||||
extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
|
||||
|
||||
/**
|
||||
* @brief initialize the common apr constructs -- apr itself, the
|
||||
|
|
@ -66,6 +72,9 @@ void LL_COMMON_API ll_init_apr();
|
|||
*/
|
||||
void LL_COMMON_API ll_cleanup_apr();
|
||||
|
||||
bool LL_COMMON_API ll_apr_is_initialized();
|
||||
|
||||
|
||||
//
|
||||
//LL apr_pool
|
||||
//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
|
||||
|
|
@ -163,14 +172,17 @@ public:
|
|||
LLAtomic32<Type>(Type x) {apr_atomic_set32(&mData, apr_uint32_t(x)); };
|
||||
~LLAtomic32<Type>() {};
|
||||
|
||||
operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
|
||||
operator const Type() { return get(); }
|
||||
Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); }
|
||||
void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); }
|
||||
void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); }
|
||||
Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++
|
||||
Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise)
|
||||
Type operator ++() { apr_atomic_inc32(&mData); return get(); } // ++Type
|
||||
Type operator --(int) { const Type result(get()); apr_atomic_dec32(&mData); return result; } // Type--
|
||||
Type operator --() { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise)
|
||||
|
||||
private:
|
||||
const Type get() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
|
||||
apr_uint32_t mData;
|
||||
};
|
||||
|
||||
|
|
@ -257,18 +269,5 @@ public:
|
|||
//*******************************************************************************************************************************
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Function which appropriately logs error or remains quiet on
|
||||
* APR_SUCCESS.
|
||||
* @return Returns <code>true</code> if status is an error condition.
|
||||
*/
|
||||
bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
|
||||
/// There's a whole other APR error-message function if you pass a DSO handle.
|
||||
bool LL_COMMON_API ll_apr_warn_status(apr_status_t status, apr_dso_handle_t* handle);
|
||||
|
||||
void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
|
||||
void LL_COMMON_API ll_apr_assert_status(apr_status_t status, apr_dso_handle_t* handle);
|
||||
|
||||
extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
|
||||
|
||||
#endif // LL_LLAPR_H
|
||||
|
|
|
|||
|
|
@ -1,296 +0,0 @@
|
|||
/**
|
||||
* @file llassoclist.h
|
||||
* @brief LLAssocList class header file
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLASSOCLIST_H
|
||||
#define LL_LLASSOCLIST_H
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// LLAssocList is an associative list container class.
|
||||
//
|
||||
// The implementation is a single linked list.
|
||||
// Both index and value objects are stored by value (not reference).
|
||||
// If pointer values are specified for index and/or value, this
|
||||
// container does NOT assume ownership of the referenced objects,
|
||||
// and does NOT delete() them on removal or destruction of the container.
|
||||
//
|
||||
// Note that operations are generally not optimized, and may of them
|
||||
// are O(n) complexity.
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template<class INDEX_TYPE, class VALUE_TYPE>
|
||||
class LLAssocList
|
||||
{
|
||||
private:
|
||||
// internal list node type
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
Node(const INDEX_TYPE &index, const VALUE_TYPE &value, Node *next)
|
||||
{
|
||||
mIndex = index;
|
||||
mValue = value;
|
||||
mNext = next;
|
||||
}
|
||||
~Node() { }
|
||||
INDEX_TYPE mIndex;
|
||||
VALUE_TYPE mValue;
|
||||
Node *mNext;
|
||||
};
|
||||
|
||||
// head of the linked list
|
||||
Node *mHead;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
LLAssocList()
|
||||
{
|
||||
mHead = NULL;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~LLAssocList()
|
||||
{
|
||||
removeAll();
|
||||
}
|
||||
|
||||
// Returns TRUE if list is empty.
|
||||
BOOL isEmpty()
|
||||
{
|
||||
return (mHead == NULL);
|
||||
}
|
||||
|
||||
// Returns the number of items in the list.
|
||||
U32 length()
|
||||
{
|
||||
U32 count = 0;
|
||||
for ( Node *node = mHead;
|
||||
node;
|
||||
node = node->mNext )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Removes item with the specified index.
|
||||
BOOL remove( const INDEX_TYPE &index )
|
||||
{
|
||||
if (!mHead)
|
||||
return FALSE;
|
||||
|
||||
if (mHead->mIndex == index)
|
||||
{
|
||||
Node *node = mHead;
|
||||
mHead = mHead->mNext;
|
||||
delete node;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
for ( Node *prev = mHead;
|
||||
prev->mNext;
|
||||
prev = prev->mNext )
|
||||
{
|
||||
if (prev->mNext->mIndex == index)
|
||||
{
|
||||
Node *node = prev->mNext;
|
||||
prev->mNext = prev->mNext->mNext;
|
||||
delete node;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Removes all items from the list.
|
||||
void removeAll()
|
||||
{
|
||||
while ( mHead )
|
||||
{
|
||||
Node *node = mHead;
|
||||
mHead = mHead->mNext;
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a new item to the head of the list,
|
||||
// removing any existing item with same index.
|
||||
void addToHead( const INDEX_TYPE &index, const VALUE_TYPE &value )
|
||||
{
|
||||
remove(index);
|
||||
Node *node = new Node(index, value, mHead);
|
||||
mHead = node;
|
||||
}
|
||||
|
||||
// Adds a new item to the end of the list,
|
||||
// removing any existing item with the same index.
|
||||
void addToTail( const INDEX_TYPE &index, const VALUE_TYPE &value )
|
||||
{
|
||||
remove(index);
|
||||
Node *node = new Node(index, value, NULL);
|
||||
if (!mHead)
|
||||
{
|
||||
mHead = node;
|
||||
return;
|
||||
}
|
||||
for ( Node *prev=mHead;
|
||||
prev;
|
||||
prev=prev->mNext )
|
||||
{
|
||||
if (!prev->mNext)
|
||||
{
|
||||
prev->mNext=node;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the value of a specified index.
|
||||
// If index does not exist, a new value will be added only if
|
||||
// 'addIfNotFound' is set to TRUE.
|
||||
// Returns TRUE if successful.
|
||||
BOOL setValue( const INDEX_TYPE &index, const VALUE_TYPE &value, BOOL addIfNotFound=FALSE )
|
||||
{
|
||||
VALUE_TYPE *valueP = getValue(index);
|
||||
if (valueP)
|
||||
{
|
||||
*valueP = value;
|
||||
return TRUE;
|
||||
}
|
||||
if (!addIfNotFound)
|
||||
return FALSE;
|
||||
addToTail(index, value);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Sets the ith value in the list.
|
||||
// A new value will NOT be addded, if the ith value does not exist.
|
||||
// Returns TRUE if successful.
|
||||
BOOL setValueAt( U32 i, const VALUE_TYPE &value )
|
||||
{
|
||||
VALUE_TYPE *valueP = getValueAt(i);
|
||||
if (valueP)
|
||||
{
|
||||
*valueP = value;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Returns a pointer to the value for the specified index,
|
||||
// or NULL if no item found.
|
||||
VALUE_TYPE *getValue( const INDEX_TYPE &index )
|
||||
{
|
||||
for ( Node *node = mHead;
|
||||
node;
|
||||
node = node->mNext )
|
||||
{
|
||||
if (node->mIndex == index)
|
||||
return &node->mValue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns a pointer to the ith value in the list, or
|
||||
// NULL if i is not valid.
|
||||
VALUE_TYPE *getValueAt( U32 i )
|
||||
{
|
||||
U32 count = 0;
|
||||
for ( Node *node = mHead;
|
||||
node;
|
||||
node = node->mNext )
|
||||
{
|
||||
if (count == i)
|
||||
return &node->mValue;
|
||||
count++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns a pointer to the index for the specified index,
|
||||
// or NULL if no item found.
|
||||
INDEX_TYPE *getIndex( const INDEX_TYPE &index )
|
||||
{
|
||||
for ( Node *node = mHead;
|
||||
node;
|
||||
node = node->mNext )
|
||||
{
|
||||
if (node->mIndex == index)
|
||||
return &node->mIndex;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns a pointer to the ith index in the list, or
|
||||
// NULL if i is not valid.
|
||||
INDEX_TYPE *getIndexAt( U32 i )
|
||||
{
|
||||
U32 count = 0;
|
||||
for ( Node *node = mHead;
|
||||
node;
|
||||
node = node->mNext )
|
||||
{
|
||||
if (count == i)
|
||||
return &node->mIndex;
|
||||
count++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns a pointer to the value for the specified index,
|
||||
// or NULL if no item found.
|
||||
VALUE_TYPE *operator[](const INDEX_TYPE &index)
|
||||
{
|
||||
return getValue(index);
|
||||
}
|
||||
|
||||
// Returns a pointer to the ith value in the list, or
|
||||
// NULL if i is not valid.
|
||||
VALUE_TYPE *operator[](U32 i)
|
||||
{
|
||||
return getValueAt(i);
|
||||
}
|
||||
|
||||
// Prints the list contents to the specified stream.
|
||||
friend std::ostream &operator<<( std::ostream &os, LLAssocList &map )
|
||||
{
|
||||
os << "{";
|
||||
for ( Node *node = map.mHead;
|
||||
node;
|
||||
node = node->mNext )
|
||||
{
|
||||
os << "<" << node->mIndex << ", " << node->mValue << ">";
|
||||
if (node->mNext)
|
||||
os << ", ";
|
||||
}
|
||||
os << "}";
|
||||
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // LL_LLASSOCLIST_H
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "llmemory.h"
|
||||
#include "llthread.h"
|
||||
#include "lltrace.h"
|
||||
|
||||
//static
|
||||
BOOL LLCommon::sAprInitialized = FALSE;
|
||||
|
|
@ -44,15 +45,13 @@ void LLCommon::initClass()
|
|||
}
|
||||
LLTimer::initClass();
|
||||
LLThreadSafeRefCount::initThreadSafeRefCount();
|
||||
// LLWorkerThread::initClass();
|
||||
// LLFrameCallbackManager::initClass();
|
||||
LLTrace::init();
|
||||
}
|
||||
|
||||
//static
|
||||
void LLCommon::cleanupClass()
|
||||
{
|
||||
// LLFrameCallbackManager::cleanupClass();
|
||||
// LLWorkerThread::cleanupClass();
|
||||
LLTrace::cleanup();
|
||||
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
|
||||
LLTimer::cleanupClass();
|
||||
if (sAprInitialized)
|
||||
|
|
|
|||
|
|
@ -27,18 +27,38 @@
|
|||
#include "linden_common.h"
|
||||
|
||||
#include "llcriticaldamp.h"
|
||||
#include <algorithm>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// static members
|
||||
//-----------------------------------------------------------------------------
|
||||
LLFrameTimer LLCriticalDamp::sInternalTimer;
|
||||
std::map<F32, F32> LLCriticalDamp::sInterpolants;
|
||||
F32 LLCriticalDamp::sTimeDelta;
|
||||
LLFrameTimer LLSmoothInterpolation::sInternalTimer;
|
||||
std::vector<LLSmoothInterpolation::Interpolant> LLSmoothInterpolation::sInterpolants;
|
||||
F32 LLSmoothInterpolation::sTimeDelta;
|
||||
|
||||
// helper functors
|
||||
struct LLSmoothInterpolation::CompareTimeConstants
|
||||
{
|
||||
bool operator()(const F32& a, const LLSmoothInterpolation::Interpolant& b) const
|
||||
{
|
||||
return a < b.mTimeScale;
|
||||
}
|
||||
|
||||
bool operator()(const LLSmoothInterpolation::Interpolant& a, const F32& b) const
|
||||
{
|
||||
return a.mTimeScale < b; // bottom of a is higher than bottom of b
|
||||
}
|
||||
|
||||
bool operator()(const LLSmoothInterpolation::Interpolant& a, const LLSmoothInterpolation::Interpolant& b) const
|
||||
{
|
||||
return a.mTimeScale < b.mTimeScale; // bottom of a is higher than bottom of b
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LLCriticalDamp()
|
||||
// LLSmoothInterpolation()
|
||||
//-----------------------------------------------------------------------------
|
||||
LLCriticalDamp::LLCriticalDamp()
|
||||
LLSmoothInterpolation::LLSmoothInterpolation()
|
||||
{
|
||||
sTimeDelta = 0.f;
|
||||
}
|
||||
|
|
@ -47,43 +67,54 @@ LLCriticalDamp::LLCriticalDamp()
|
|||
//-----------------------------------------------------------------------------
|
||||
// updateInterpolants()
|
||||
//-----------------------------------------------------------------------------
|
||||
void LLCriticalDamp::updateInterpolants()
|
||||
void LLSmoothInterpolation::updateInterpolants()
|
||||
{
|
||||
sTimeDelta = sInternalTimer.getElapsedTimeAndResetF32();
|
||||
|
||||
F32 time_constant;
|
||||
|
||||
for (std::map<F32, F32>::iterator iter = sInterpolants.begin();
|
||||
iter != sInterpolants.end(); iter++)
|
||||
for (S32 i = 0; i < sInterpolants.size(); i++)
|
||||
{
|
||||
time_constant = iter->first;
|
||||
F32 new_interpolant = 1.f - pow(2.f, -sTimeDelta / time_constant);
|
||||
new_interpolant = llclamp(new_interpolant, 0.f, 1.f);
|
||||
sInterpolants[time_constant] = new_interpolant;
|
||||
Interpolant& interp = sInterpolants[i];
|
||||
interp.mInterpolant = calcInterpolant(interp.mTimeScale);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// getInterpolant()
|
||||
//-----------------------------------------------------------------------------
|
||||
F32 LLCriticalDamp::getInterpolant(const F32 time_constant, BOOL use_cache)
|
||||
F32 LLSmoothInterpolation::getInterpolant(LLUnit<F32, LLUnits::Seconds> time_constant, bool use_cache)
|
||||
{
|
||||
if (time_constant == 0.f)
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
if (use_cache && sInterpolants.count(time_constant))
|
||||
{
|
||||
return sInterpolants[time_constant];
|
||||
}
|
||||
|
||||
F32 interpolant = 1.f - pow(2.f, -sTimeDelta / time_constant);
|
||||
interpolant = llclamp(interpolant, 0.f, 1.f);
|
||||
if (use_cache)
|
||||
{
|
||||
sInterpolants[time_constant] = interpolant;
|
||||
interpolant_vec_t::iterator find_it = std::lower_bound(sInterpolants.begin(), sInterpolants.end(), time_constant.value(), CompareTimeConstants());
|
||||
if (find_it != sInterpolants.end() && find_it->mTimeScale == time_constant)
|
||||
{
|
||||
return find_it->mInterpolant;
|
||||
}
|
||||
else
|
||||
{
|
||||
Interpolant interp;
|
||||
interp.mTimeScale = time_constant.value();
|
||||
interp.mInterpolant = calcInterpolant(time_constant.value());
|
||||
sInterpolants.insert(find_it, interp);
|
||||
return interp.mInterpolant;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return calcInterpolant(time_constant.value());
|
||||
|
||||
return interpolant;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// calcInterpolant()
|
||||
//-----------------------------------------------------------------------------
|
||||
F32 LLSmoothInterpolation::calcInterpolant(F32 time_constant)
|
||||
{
|
||||
return llclamp(1.f - powf(2.f, -sTimeDelta / time_constant), 0.f, 1.f);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,26 +28,46 @@
|
|||
#ifndef LL_LLCRITICALDAMP_H
|
||||
#define LL_LLCRITICALDAMP_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "llframetimer.h"
|
||||
#include "llunit.h"
|
||||
|
||||
class LL_COMMON_API LLCriticalDamp
|
||||
class LL_COMMON_API LLSmoothInterpolation
|
||||
{
|
||||
public:
|
||||
LLCriticalDamp();
|
||||
LLSmoothInterpolation();
|
||||
|
||||
// MANIPULATORS
|
||||
static void updateInterpolants();
|
||||
|
||||
// ACCESSORS
|
||||
static F32 getInterpolant(const F32 time_constant, BOOL use_cache = TRUE);
|
||||
static F32 getInterpolant(LLUnit<F32, LLUnits::Seconds> time_constant, bool use_cache = true);
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
static T lerp(T a, T b, LLUnit<F32, LLUnits::Seconds> time_constant, bool use_cache = true)
|
||||
{
|
||||
F32 interpolant = getInterpolant(time_constant, use_cache);
|
||||
return ((a * (1.f - interpolant))
|
||||
+ (b * interpolant));
|
||||
}
|
||||
|
||||
protected:
|
||||
static F32 calcInterpolant(F32 time_constant);
|
||||
|
||||
struct CompareTimeConstants;
|
||||
static LLFrameTimer sInternalTimer; // frame timer for calculating deltas
|
||||
|
||||
static std::map<F32, F32> sInterpolants;
|
||||
struct Interpolant
|
||||
{
|
||||
F32 mTimeScale;
|
||||
F32 mInterpolant;
|
||||
};
|
||||
typedef std::vector<Interpolant> interpolant_vec_t;
|
||||
static interpolant_vec_t sInterpolants;
|
||||
static F32 sTimeDelta;
|
||||
};
|
||||
|
||||
typedef LLSmoothInterpolation LLCriticalDamp;
|
||||
|
||||
#endif // LL_LLCRITICALDAMP_H
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
#include "lltimer.h"
|
||||
#include "llstring.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
static const F64 DATE_EPOCH = 0.0;
|
||||
|
||||
|
|
@ -48,18 +49,15 @@ static const F64 LL_APR_USEC_PER_SEC = 1000000.0;
|
|||
|
||||
|
||||
LLDate::LLDate() : mSecondsSinceEpoch(DATE_EPOCH)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
LLDate::LLDate(const LLDate& date) :
|
||||
mSecondsSinceEpoch(date.mSecondsSinceEpoch)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
LLDate::LLDate(F64 seconds_since_epoch) :
|
||||
mSecondsSinceEpoch(seconds_since_epoch)
|
||||
{
|
||||
}
|
||||
LLDate::LLDate(LLUnit<F64, LLUnits::Seconds> seconds_since_epoch) :
|
||||
mSecondsSinceEpoch(seconds_since_epoch.value())
|
||||
{}
|
||||
|
||||
LLDate::LLDate(const std::string& iso8601_date)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include "llunit.h"
|
||||
|
||||
/**
|
||||
* @class LLDate
|
||||
|
|
@ -56,9 +57,9 @@ public:
|
|||
/**
|
||||
* @brief Construct a date from a seconds since epoch value.
|
||||
*
|
||||
* @pararm seconds_since_epoch The number of seconds since UTC epoch.
|
||||
* @param seconds_since_epoch The number of seconds since UTC epoch.
|
||||
*/
|
||||
LLDate(F64 seconds_since_epoch);
|
||||
LLDate(LLUnit<F64, LLUnits::Seconds> seconds_since_epoch);
|
||||
|
||||
/**
|
||||
* @brief Construct a date from a string representation
|
||||
|
|
|
|||
|
|
@ -244,5 +244,8 @@ inline void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs)
|
|||
rhs = tmp;
|
||||
}
|
||||
|
||||
#define LL_GLUE_IMPL(x, y) x##y
|
||||
#define LL_GLUE_TOKENS(x, y) LL_GLUE_IMPL(x, y)
|
||||
|
||||
#endif // LL_LLDEFS_H
|
||||
|
||||
|
|
|
|||
|
|
@ -27,17 +27,20 @@
|
|||
#ifndef LL_LLDEPTHSTACK_H
|
||||
#define LL_LLDEPTHSTACK_H
|
||||
|
||||
#include "linked_lists.h"
|
||||
#include "llstl.h"
|
||||
|
||||
template <class DATA_TYPE> class LLDepthStack
|
||||
{
|
||||
private:
|
||||
LLLinkedList<DATA_TYPE> mStack;
|
||||
std::deque<DATA_TYPE*> mStack;
|
||||
U32 mCurrentDepth;
|
||||
U32 mMaxDepth;
|
||||
|
||||
public:
|
||||
LLDepthStack() : mCurrentDepth(0), mMaxDepth(0) {}
|
||||
LLDepthStack()
|
||||
: mCurrentDepth(0), mMaxDepth(0)
|
||||
{}
|
||||
|
||||
~LLDepthStack() {}
|
||||
|
||||
void setDepth(U32 depth)
|
||||
|
|
@ -54,24 +57,27 @@ public:
|
|||
{
|
||||
if (mCurrentDepth < mMaxDepth)
|
||||
{
|
||||
mStack.addData(data);
|
||||
mStack.push_back(data);
|
||||
mCurrentDepth++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the last item falls off stack and is deleted
|
||||
mStack.getLastData();
|
||||
mStack.deleteCurrentData();
|
||||
mStack.addData(data);
|
||||
if (!mStack.empty())
|
||||
{
|
||||
mStack.pop_front();
|
||||
}
|
||||
mStack.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
DATA_TYPE *pop()
|
||||
{
|
||||
DATA_TYPE *tempp = mStack.getFirstData();
|
||||
if (tempp)
|
||||
DATA_TYPE *tempp = NULL;
|
||||
if (!mStack.empty())
|
||||
{
|
||||
mStack.removeCurrentData();
|
||||
tempp = mStack.back();
|
||||
mStack.pop_back();
|
||||
mCurrentDepth--;
|
||||
}
|
||||
return tempp;
|
||||
|
|
@ -79,20 +85,20 @@ public:
|
|||
|
||||
DATA_TYPE *check()
|
||||
{
|
||||
DATA_TYPE *tempp = mStack.getFirstData();
|
||||
return tempp;
|
||||
return mStack.empty() ? NULL : mStack.back();
|
||||
}
|
||||
|
||||
void deleteAllData()
|
||||
{
|
||||
mCurrentDepth = 0;
|
||||
mStack.deleteAllData();
|
||||
std::for_each(mStack.begin(), mStack.end(), DeletePointer());
|
||||
mStack.clear();
|
||||
}
|
||||
|
||||
void removeAllNodes()
|
||||
{
|
||||
mCurrentDepth = 0;
|
||||
mStack.removeAllNodes();
|
||||
mStack.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
/**
|
||||
* @file lldlinked.h
|
||||
* @brief Declaration of the LLDLinked class.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
#ifndef LL_LLDLINKED_H
|
||||
#define LL_LLDLINKED_H
|
||||
|
||||
template <class Type> class LLDLinked
|
||||
{
|
||||
LLDLinked* mNextp;
|
||||
LLDLinked* mPrevp;
|
||||
public:
|
||||
|
||||
Type* getNext() { return (Type*)mNextp; }
|
||||
Type* getPrev() { return (Type*)mPrevp; }
|
||||
Type* getFirst() { return (Type*)mNextp; }
|
||||
|
||||
void init()
|
||||
{
|
||||
mNextp = mPrevp = NULL;
|
||||
}
|
||||
|
||||
void unlink()
|
||||
{
|
||||
if (mPrevp) mPrevp->mNextp = mNextp;
|
||||
if (mNextp) mNextp->mPrevp = mPrevp;
|
||||
}
|
||||
|
||||
LLDLinked() { mNextp = mPrevp = NULL; }
|
||||
virtual ~LLDLinked() { unlink(); }
|
||||
|
||||
virtual void deleteAll()
|
||||
{
|
||||
Type *curp = getFirst();
|
||||
while(curp)
|
||||
{
|
||||
Type *nextp = curp->getNext();
|
||||
curp->unlink();
|
||||
delete curp;
|
||||
curp = nextp;
|
||||
}
|
||||
}
|
||||
|
||||
void relink(Type &after)
|
||||
{
|
||||
LLDLinked *afterp = (LLDLinked*)&after;
|
||||
afterp->mPrevp = this;
|
||||
mNextp = afterp;
|
||||
}
|
||||
|
||||
virtual void append(Type& after)
|
||||
{
|
||||
LLDLinked *afterp = (LLDLinked*)&after;
|
||||
afterp->mPrevp = this;
|
||||
afterp->mNextp = mNextp;
|
||||
if (mNextp) mNextp->mPrevp = afterp;
|
||||
mNextp = afterp;
|
||||
}
|
||||
|
||||
virtual void insert(Type& before)
|
||||
{
|
||||
LLDLinked *beforep = (LLDLinked*)&before;
|
||||
beforep->mNextp = this;
|
||||
beforep->mPrevp = mPrevp;
|
||||
if (mPrevp) mPrevp->mNextp = beforep;
|
||||
mPrevp = beforep;
|
||||
}
|
||||
|
||||
virtual void put(Type& obj) { append(obj); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,352 +0,0 @@
|
|||
/**
|
||||
* @file lldqueueptr.h
|
||||
* @brief LLDynamicQueuePtr declaration
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
#ifndef LL_LLDQUEUEPTR_H
|
||||
#define LL_LLDQUEUEPTR_H
|
||||
|
||||
template <class Type>
|
||||
class LLDynamicQueuePtr
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
OKAY = 0,
|
||||
FAIL = -1
|
||||
};
|
||||
|
||||
LLDynamicQueuePtr(const S32 size=8);
|
||||
~LLDynamicQueuePtr();
|
||||
|
||||
void init();
|
||||
void destroy();
|
||||
void reset();
|
||||
void reallocate(U32 newsize);
|
||||
|
||||
// ACCESSORS
|
||||
const Type& get(const S32 index) const; // no bounds checking
|
||||
Type& get(const S32 index); // no bounds checking
|
||||
const Type& operator [] (const S32 index) const { return get(index); }
|
||||
Type& operator [] (const S32 index) { return get(index); }
|
||||
S32 find(const Type &obj) const;
|
||||
|
||||
S32 count() const { return (mLastObj >= mFirstObj ? mLastObj - mFirstObj : mLastObj + mMaxObj - mFirstObj); }
|
||||
S32 getMax() const { return mMaxObj; }
|
||||
S32 getFirst() const { return mFirstObj; }
|
||||
S32 getLast () const { return mLastObj; }
|
||||
|
||||
// MANIPULATE
|
||||
S32 push(const Type &obj); // add to end of Queue, returns index from start
|
||||
S32 pull( Type &obj); // pull from Queue, returns index from start
|
||||
|
||||
S32 remove (S32 index); // remove by index
|
||||
S32 removeObj(const Type &obj); // remove by object
|
||||
|
||||
protected:
|
||||
S32 mFirstObj, mLastObj, mMaxObj;
|
||||
Type* mMemory;
|
||||
|
||||
public:
|
||||
|
||||
void print()
|
||||
{
|
||||
/*
|
||||
Convert this to llinfos if it's intended to be used - djs 08/30/02
|
||||
|
||||
printf("Printing from %d to %d (of %d): ",mFirstObj, mLastObj, mMaxObj);
|
||||
|
||||
if (mFirstObj <= mLastObj)
|
||||
{
|
||||
for (S32 i=mFirstObj;i<mLastObj;i++)
|
||||
{
|
||||
printf("%d ",mMemory[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (S32 i=mFirstObj;i<mMaxObj;i++)
|
||||
{
|
||||
printf("%d ",mMemory[i]);
|
||||
}
|
||||
for (i=0;i<mLastObj;i++)
|
||||
{
|
||||
printf("%d ",mMemory[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------
|
||||
// LLDynamicQueuePtrPtr implementation
|
||||
//--------------------------------------------------------
|
||||
|
||||
|
||||
template <class Type>
|
||||
inline LLDynamicQueuePtr<Type>::LLDynamicQueuePtr(const S32 size)
|
||||
{
|
||||
init();
|
||||
reallocate(size);
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline LLDynamicQueuePtr<Type>::~LLDynamicQueuePtr()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline void LLDynamicQueuePtr<Type>::init()
|
||||
{
|
||||
mFirstObj = 0;
|
||||
mLastObj = 0;
|
||||
mMaxObj = 0;
|
||||
mMemory = NULL;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline void LLDynamicQueuePtr<Type>::reallocate(U32 newsize)
|
||||
{
|
||||
if (newsize)
|
||||
{
|
||||
if (mFirstObj > mLastObj && newsize > mMaxObj)
|
||||
{
|
||||
Type* new_memory = new Type[newsize];
|
||||
|
||||
llassert(new_memory);
|
||||
|
||||
S32 _count = count();
|
||||
S32 i, m = 0;
|
||||
for (i=mFirstObj; i < mMaxObj; i++)
|
||||
{
|
||||
new_memory[m++] = mMemory[i];
|
||||
}
|
||||
for (i=0; i <=mLastObj; i++)
|
||||
{
|
||||
new_memory[m++] = mMemory[i];
|
||||
}
|
||||
|
||||
delete[] mMemory;
|
||||
mMemory = new_memory;
|
||||
|
||||
mFirstObj = 0;
|
||||
mLastObj = _count;
|
||||
}
|
||||
else
|
||||
{
|
||||
Type* new_memory = new Type[newsize];
|
||||
|
||||
llassert(new_memory);
|
||||
|
||||
S32 i, m = 0;
|
||||
for (i=0; i < mLastObj; i++)
|
||||
{
|
||||
new_memory[m++] = mMemory[i];
|
||||
}
|
||||
delete[] mMemory;
|
||||
mMemory = new_memory;
|
||||
}
|
||||
}
|
||||
else if (mMemory)
|
||||
{
|
||||
delete[] mMemory;
|
||||
mMemory = NULL;
|
||||
}
|
||||
|
||||
mMaxObj = newsize;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline void LLDynamicQueuePtr<Type>::destroy()
|
||||
{
|
||||
reset();
|
||||
delete[] mMemory;
|
||||
mMemory = NULL;
|
||||
}
|
||||
|
||||
|
||||
template <class Type>
|
||||
void LLDynamicQueuePtr<Type>::reset()
|
||||
{
|
||||
for (S32 i=0; i < mMaxObj; i++)
|
||||
{
|
||||
get(i) = NULL; // unrefs for pointers
|
||||
}
|
||||
|
||||
mFirstObj = 0;
|
||||
mLastObj = 0;
|
||||
}
|
||||
|
||||
|
||||
template <class Type>
|
||||
inline S32 LLDynamicQueuePtr<Type>::find(const Type &obj) const
|
||||
{
|
||||
S32 i;
|
||||
if (mFirstObj <= mLastObj)
|
||||
{
|
||||
for ( i = mFirstObj; i < mLastObj; i++ )
|
||||
{
|
||||
if (mMemory[i] == obj)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( i = mFirstObj; i < mMaxObj; i++ )
|
||||
{
|
||||
if (mMemory[i] == obj)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for ( i = 0; i < mLastObj; i++ )
|
||||
{
|
||||
if (mMemory[i] == obj)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline S32 LLDynamicQueuePtr<Type>::remove(S32 i)
|
||||
{
|
||||
if (mFirstObj > mLastObj)
|
||||
{
|
||||
if (i >= mFirstObj && i < mMaxObj)
|
||||
{
|
||||
while( i > mFirstObj)
|
||||
{
|
||||
mMemory[i] = mMemory[i-1];
|
||||
i--;
|
||||
}
|
||||
mMemory[mFirstObj] = NULL;
|
||||
mFirstObj++;
|
||||
if (mFirstObj >= mMaxObj) mFirstObj = 0;
|
||||
|
||||
return count();
|
||||
}
|
||||
else if (i < mLastObj && i >= 0)
|
||||
{
|
||||
while(i < mLastObj)
|
||||
{
|
||||
mMemory[i] = mMemory[i+1];
|
||||
i++;
|
||||
}
|
||||
mMemory[mLastObj] = NULL;
|
||||
mLastObj--;
|
||||
if (mLastObj < 0) mLastObj = mMaxObj-1;
|
||||
|
||||
return count();
|
||||
}
|
||||
}
|
||||
else if (i <= mLastObj && i >= mFirstObj)
|
||||
{
|
||||
while(i < mLastObj)
|
||||
{
|
||||
mMemory[i] = mMemory[i+1];
|
||||
i++;
|
||||
}
|
||||
mMemory[mLastObj] = NULL;
|
||||
mLastObj--;
|
||||
if (mLastObj < 0) mLastObj = mMaxObj-1;
|
||||
|
||||
return count();
|
||||
}
|
||||
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline S32 LLDynamicQueuePtr<Type>::removeObj(const Type& obj)
|
||||
{
|
||||
S32 ind = find(obj);
|
||||
if (ind >= 0)
|
||||
{
|
||||
return remove(ind);
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline S32 LLDynamicQueuePtr<Type>::push(const Type &obj)
|
||||
{
|
||||
if (mMaxObj - count() <= 1)
|
||||
{
|
||||
reallocate(mMaxObj * 2);
|
||||
}
|
||||
|
||||
mMemory[mLastObj++] = obj;
|
||||
|
||||
if (mLastObj >= mMaxObj)
|
||||
{
|
||||
mLastObj = 0;
|
||||
}
|
||||
|
||||
return count();
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline S32 LLDynamicQueuePtr<Type>::pull(Type &obj)
|
||||
{
|
||||
obj = NULL;
|
||||
|
||||
if (count() < 1) return -1;
|
||||
|
||||
obj = mMemory[mFirstObj];
|
||||
mMemory[mFirstObj] = NULL;
|
||||
|
||||
mFirstObj++;
|
||||
|
||||
if (mFirstObj >= mMaxObj)
|
||||
{
|
||||
mFirstObj = 0;
|
||||
}
|
||||
|
||||
return count();
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline const Type& LLDynamicQueuePtr<Type>::get(const S32 i) const
|
||||
{
|
||||
return mMemory[i];
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline Type& LLDynamicQueuePtr<Type>::get(const S32 i)
|
||||
{
|
||||
return mMemory[i];
|
||||
}
|
||||
|
||||
|
||||
#endif // LL_LLDQUEUEPTR_H
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* @file llenum.h
|
||||
* @author Tom Yedwab
|
||||
* @brief Utility class for storing enum value <-> string lookup.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2006&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLENUM_H
|
||||
#define LL_LLENUM_H
|
||||
|
||||
class LLEnum
|
||||
{
|
||||
public:
|
||||
typedef std::pair<const std::string, const U32> enum_t;
|
||||
enum
|
||||
{
|
||||
UNDEFINED = 0xffffffff,
|
||||
};
|
||||
|
||||
LLEnum(const enum_t values_array[], const U32 length)
|
||||
{
|
||||
for (U32 i=0; i<length; ++i)
|
||||
{
|
||||
mEnumMap.insert(values_array[i]);
|
||||
if (values_array[i].second >= mEnumArray.size())
|
||||
{
|
||||
mEnumArray.resize(values_array[i].second+1);
|
||||
}
|
||||
mEnumArray[values_array[i].second] = values_array[i].first;
|
||||
}
|
||||
}
|
||||
|
||||
U32 operator[](std::string str)
|
||||
{
|
||||
std::map<const std::string, const U32>::iterator itor;
|
||||
itor = mEnumMap.find(str);
|
||||
if (itor != mEnumMap.end())
|
||||
{
|
||||
return itor->second;
|
||||
}
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
const std::string operator[](U32 index)
|
||||
{
|
||||
if (index < mEnumArray.size())
|
||||
{
|
||||
return mEnumArray[index];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<const std::string, const U32> mEnumMap;
|
||||
std::vector<std::string> mEnumArray;
|
||||
};
|
||||
|
||||
#endif // LL_LLENUM_H
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
#define LL_LLERRORLEGACY_H
|
||||
|
||||
#include "llpreprocessor.h"
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
/*
|
||||
LEGACY -- DO NOT USE THIS STUFF ANYMORE
|
||||
|
|
@ -111,6 +112,14 @@ const int LL_ERR_PRICE_MISMATCH = -23018;
|
|||
#define llverify(func) do {if (func) {}} while(0)
|
||||
#endif
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
#define LL_STATIC_ASSERT(func, msg) static_assert(func, msg)
|
||||
#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) static_assert(false, msg)
|
||||
#else
|
||||
#define LL_STATIC_ASSERT(func, msg) BOOST_STATIC_ASSERT(func)
|
||||
#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) BOOST_STATIC_ASSERT(sizeof(type) != 0 && false);
|
||||
#endif
|
||||
|
||||
// handy compile-time assert - enforce those template parameters!
|
||||
#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1] /* Flawfinder: ignore */
|
||||
//XXX: used in two places in llcommon/llskipmap.h
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@
|
|||
#include <algorithm>
|
||||
// std headers
|
||||
#include <typeinfo>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
// external library headers
|
||||
|
|
|
|||
|
|
@ -32,8 +32,13 @@
|
|||
#include "llsingleton.h"
|
||||
#include "lltreeiterators.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llunit.h"
|
||||
#include "llsd.h"
|
||||
#include "lltracerecording.h"
|
||||
#include "lltracethreadrecorder.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <queue>
|
||||
|
||||
|
||||
#if LL_WINDOWS
|
||||
|
|
@ -49,37 +54,36 @@
|
|||
#error "architecture not supported"
|
||||
#endif
|
||||
|
||||
namespace LLTrace
|
||||
{
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// statics
|
||||
|
||||
S32 LLFastTimer::sCurFrameIndex = -1;
|
||||
S32 LLFastTimer::sLastFrameIndex = -1;
|
||||
U64 LLFastTimer::sLastFrameTime = LLFastTimer::getCPUClockCount64();
|
||||
bool LLFastTimer::sPauseHistory = 0;
|
||||
bool LLFastTimer::sResetHistory = 0;
|
||||
LLFastTimer::CurTimerData LLFastTimer::sCurTimerData;
|
||||
BOOL LLFastTimer::sLog = FALSE;
|
||||
std::string LLFastTimer::sLogName = "";
|
||||
BOOL LLFastTimer::sMetricLog = FALSE;
|
||||
LLMutex* LLFastTimer::sLogLock = NULL;
|
||||
std::queue<LLSD> LLFastTimer::sLogQueue;
|
||||
bool TimeBlock::sLog = false;
|
||||
std::string TimeBlock::sLogName = "";
|
||||
bool TimeBlock::sMetricLog = false;
|
||||
|
||||
#if LL_LINUX || LL_SOLARIS
|
||||
U64 LLFastTimer::sClockResolution = 1000000000; // Nanosecond resolution
|
||||
U64 TimeBlock::sClockResolution = 1000000000; // Nanosecond resolution
|
||||
#else
|
||||
U64 LLFastTimer::sClockResolution = 1000000; // Microsecond resolution
|
||||
U64 TimeBlock::sClockResolution = 1000000; // Microsecond resolution
|
||||
#endif
|
||||
|
||||
static LLMutex* sLogLock = NULL;
|
||||
static std::queue<LLSD> sLogQueue;
|
||||
|
||||
|
||||
// FIXME: move these declarations to the relevant modules
|
||||
|
||||
// helper functions
|
||||
typedef LLTreeDFSPostIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_bottom_up_iterator_t;
|
||||
typedef LLTreeDFSPostIter<TimeBlock, TimeBlock::child_const_iter> timer_tree_bottom_up_iterator_t;
|
||||
|
||||
static timer_tree_bottom_up_iterator_t begin_timer_tree_bottom_up(LLFastTimer::NamedTimer& id)
|
||||
static timer_tree_bottom_up_iterator_t begin_timer_tree_bottom_up(TimeBlock& id)
|
||||
{
|
||||
return timer_tree_bottom_up_iterator_t(&id,
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
|
||||
boost::bind(boost::mem_fn(&TimeBlock::beginChildren), _1),
|
||||
boost::bind(boost::mem_fn(&TimeBlock::endChildren), _1));
|
||||
}
|
||||
|
||||
static timer_tree_bottom_up_iterator_t end_timer_tree_bottom_up()
|
||||
|
|
@ -87,14 +91,14 @@ static timer_tree_bottom_up_iterator_t end_timer_tree_bottom_up()
|
|||
return timer_tree_bottom_up_iterator_t();
|
||||
}
|
||||
|
||||
typedef LLTreeDFSIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_dfs_iterator_t;
|
||||
typedef LLTreeDFSIter<TimeBlock, TimeBlock::child_const_iter> timer_tree_dfs_iterator_t;
|
||||
|
||||
|
||||
static timer_tree_dfs_iterator_t begin_timer_tree(LLFastTimer::NamedTimer& id)
|
||||
static timer_tree_dfs_iterator_t begin_timer_tree(TimeBlock& id)
|
||||
{
|
||||
return timer_tree_dfs_iterator_t(&id,
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
|
||||
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
|
||||
boost::bind(boost::mem_fn(&TimeBlock::beginChildren), _1),
|
||||
boost::bind(boost::mem_fn(&TimeBlock::endChildren), _1));
|
||||
}
|
||||
|
||||
static timer_tree_dfs_iterator_t end_timer_tree()
|
||||
|
|
@ -102,90 +106,48 @@ static timer_tree_dfs_iterator_t end_timer_tree()
|
|||
return timer_tree_dfs_iterator_t();
|
||||
}
|
||||
|
||||
// factory class that creates NamedTimers via static DeclareTimer objects
|
||||
class NamedTimerFactory : public LLSingleton<NamedTimerFactory>
|
||||
|
||||
// sort child timers by name
|
||||
struct SortTimerByName
|
||||
{
|
||||
public:
|
||||
NamedTimerFactory()
|
||||
: mTimerRoot(NULL)
|
||||
{}
|
||||
|
||||
/*virtual */ void initSingleton()
|
||||
bool operator()(const TimeBlock* i1, const TimeBlock* i2)
|
||||
{
|
||||
mTimerRoot = new LLFastTimer::NamedTimer("root");
|
||||
mRootFrameState.setNamedTimer(mTimerRoot);
|
||||
mTimerRoot->setFrameState(&mRootFrameState);
|
||||
mTimerRoot->mParent = mTimerRoot;
|
||||
mTimerRoot->setCollapsed(false);
|
||||
mRootFrameState.mParent = &mRootFrameState;
|
||||
return i1->getName() < i2->getName();
|
||||
}
|
||||
|
||||
~NamedTimerFactory()
|
||||
{
|
||||
std::for_each(mTimers.begin(), mTimers.end(), DeletePairedPointer());
|
||||
|
||||
delete mTimerRoot;
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer& createNamedTimer(const std::string& name, LLFastTimer::FrameState* state)
|
||||
{
|
||||
LLFastTimer::NamedTimer* timer = new LLFastTimer::NamedTimer(name);
|
||||
timer->setFrameState(state);
|
||||
timer->setParent(mTimerRoot);
|
||||
mTimers.insert(std::make_pair(name, timer));
|
||||
|
||||
return *timer;
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer* getTimerByName(const std::string& name)
|
||||
{
|
||||
timer_map_t::iterator found_it = mTimers.find(name);
|
||||
if (found_it != mTimers.end())
|
||||
{
|
||||
return found_it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer* getRootTimer() { return mTimerRoot; }
|
||||
|
||||
typedef std::multimap<std::string, LLFastTimer::NamedTimer*> timer_map_t;
|
||||
timer_map_t::iterator beginTimers() { return mTimers.begin(); }
|
||||
timer_map_t::iterator endTimers() { return mTimers.end(); }
|
||||
S32 timerCount() { return mTimers.size(); }
|
||||
|
||||
private:
|
||||
timer_map_t mTimers;
|
||||
|
||||
LLFastTimer::NamedTimer* mTimerRoot;
|
||||
LLFastTimer::FrameState mRootFrameState;
|
||||
};
|
||||
|
||||
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
|
||||
: mTimer(NamedTimerFactory::instance().createNamedTimer(name, &mFrameState))
|
||||
{
|
||||
mTimer.setCollapsed(!open);
|
||||
}
|
||||
TimeBlock& TimeBlock::getRootTimeBlock()
|
||||
{
|
||||
static TimeBlock root_timer("root", NULL);
|
||||
return root_timer;
|
||||
}
|
||||
|
||||
void TimeBlock::pushLog(LLSD log)
|
||||
{
|
||||
LLMutexLock lock(sLogLock);
|
||||
|
||||
sLogQueue.push(log);
|
||||
}
|
||||
|
||||
void TimeBlock::setLogLock(LLMutex* lock)
|
||||
{
|
||||
sLogLock = lock;
|
||||
}
|
||||
|
||||
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
|
||||
: mTimer(NamedTimerFactory::instance().createNamedTimer(name, &mFrameState))
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
#if (LL_DARWIN || LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
|
||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||
U64 TimeBlock::countsPerSecond()
|
||||
{
|
||||
return sClockResolution >> 8;
|
||||
return sClockResolution;
|
||||
}
|
||||
#else // windows or x86-mac or x86-linux or x86-solaris
|
||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||
U64 TimeBlock::countsPerSecond()
|
||||
{
|
||||
#if LL_FASTTIMER_USE_RDTSC || !LL_WINDOWS
|
||||
//getCPUFrequency returns MHz and sCPUClockFrequency wants to be in Hz
|
||||
static U64 sCPUClockFrequency = U64(LLProcessorInfo().getCPUFrequency()*1000000.0);
|
||||
|
||||
// we drop the low-order byte in our timers, so report a lower frequency
|
||||
static LLUnit<U64, LLUnits::Hertz> sCPUClockFrequency = LLProcessorInfo().getCPUFrequency();
|
||||
return sCPUClockFrequency.value();
|
||||
#else
|
||||
// If we're not using RDTSC, each fasttimer tick is just a performance counter tick.
|
||||
// Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency())
|
||||
|
|
@ -197,270 +159,207 @@ U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
|||
QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency);
|
||||
firstcall = false;
|
||||
}
|
||||
return sCPUClockFrequency.value();
|
||||
#endif
|
||||
return sCPUClockFrequency >> 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
LLFastTimer::FrameState::FrameState()
|
||||
: mActiveCount(0),
|
||||
mCalls(0),
|
||||
mSelfTimeCounter(0),
|
||||
mParent(NULL),
|
||||
mLastCaller(NULL),
|
||||
mMoveUpTree(false)
|
||||
{}
|
||||
|
||||
|
||||
LLFastTimer::NamedTimer::NamedTimer(const std::string& name)
|
||||
: mName(name),
|
||||
mCollapsed(true),
|
||||
mParent(NULL),
|
||||
mTotalTimeCounter(0),
|
||||
mCountAverage(0),
|
||||
mCallAverage(0),
|
||||
mNeedsSorting(false),
|
||||
mFrameState(NULL)
|
||||
TimeBlock::TimeBlock(const char* name, TimeBlock* parent)
|
||||
: TraceType<TimeBlockAccumulator>(name)
|
||||
{
|
||||
mCountHistory = new U32[HISTORY_NUM];
|
||||
memset(mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
mCallHistory = new U32[HISTORY_NUM];
|
||||
memset(mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
}
|
||||
|
||||
LLFastTimer::NamedTimer::~NamedTimer()
|
||||
TimeBlockTreeNode& TimeBlock::getTreeNode() const
|
||||
{
|
||||
delete[] mCountHistory;
|
||||
delete[] mCallHistory;
|
||||
TimeBlockTreeNode* nodep = LLTrace::get_thread_recorder()->getTimeBlockTreeNode(getIndex());
|
||||
llassert(nodep);
|
||||
return *nodep;
|
||||
}
|
||||
|
||||
std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx)
|
||||
{
|
||||
F64 ms_multiplier = 1000.0 / (F64)LLFastTimer::countsPerSecond();
|
||||
if (history_idx < 0)
|
||||
{
|
||||
// by default, show average number of call
|
||||
return llformat("%s (%d ms, %d calls)", getName().c_str(), (S32)(getCountAverage() * ms_multiplier), (S32)getCallAverage());
|
||||
}
|
||||
else
|
||||
{
|
||||
return llformat("%s (%d ms, %d calls)", getName().c_str(), (S32)(getHistoricalCount(history_idx) * ms_multiplier), (S32)getHistoricalCalls(history_idx));
|
||||
}
|
||||
}
|
||||
|
||||
void LLFastTimer::NamedTimer::setParent(NamedTimer* parent)
|
||||
void TimeBlock::bootstrapTimerTree()
|
||||
{
|
||||
llassert_always(parent != this);
|
||||
llassert_always(parent != NULL);
|
||||
|
||||
if (mParent)
|
||||
{
|
||||
// subtract our accumulated from previous parent
|
||||
for (S32 i = 0; i < HISTORY_NUM; i++)
|
||||
for (LLInstanceTracker<TimeBlock>::instance_iter begin_it = LLInstanceTracker<TimeBlock>::beginInstances(), end_it = LLInstanceTracker<TimeBlock>::endInstances(), it = begin_it;
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
mParent->mCountHistory[i] -= mCountHistory[i];
|
||||
}
|
||||
TimeBlock& timer = *it;
|
||||
if (&timer == &TimeBlock::getRootTimeBlock()) continue;
|
||||
|
||||
// subtract average timing from previous parent
|
||||
mParent->mCountAverage -= mCountAverage;
|
||||
|
||||
std::vector<NamedTimer*>& children = mParent->getChildren();
|
||||
std::vector<NamedTimer*>::iterator found_it = std::find(children.begin(), children.end(), this);
|
||||
if (found_it != children.end())
|
||||
// bootstrap tree construction by attaching to last timer to be on stack
|
||||
// when this timer was called
|
||||
if (timer.getParent() == &TimeBlock::getRootTimeBlock())
|
||||
{
|
||||
children.erase(found_it);
|
||||
}
|
||||
}
|
||||
TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
|
||||
|
||||
mParent = parent;
|
||||
if (parent)
|
||||
if (accumulator->mLastCaller)
|
||||
{
|
||||
getFrameState().mParent = &parent->getFrameState();
|
||||
parent->getChildren().push_back(this);
|
||||
parent->mNeedsSorting = true;
|
||||
timer.setParent(accumulator->mLastCaller);
|
||||
accumulator->mParent = accumulator->mLastCaller;
|
||||
}
|
||||
// no need to push up tree on first use, flag can be set spuriously
|
||||
accumulator->mMoveUpTree = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
S32 LLFastTimer::NamedTimer::getDepth()
|
||||
// bump timers up tree if they have been flagged as being in the wrong place
|
||||
// do this in a bottom up order to promote descendants first before promoting ancestors
|
||||
// this preserves partial order derived from current frame's observations
|
||||
void TimeBlock::incrementalUpdateTimerTree()
|
||||
{
|
||||
S32 depth = 0;
|
||||
NamedTimer* timerp = mParent;
|
||||
while(timerp)
|
||||
{
|
||||
depth++;
|
||||
if (timerp->getParent() == timerp) break;
|
||||
timerp = timerp->mParent;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFastTimer::NamedTimer::processTimes()
|
||||
{
|
||||
if (sCurFrameIndex < 0) return;
|
||||
|
||||
buildHierarchy();
|
||||
accumulateTimings();
|
||||
}
|
||||
|
||||
// sort child timers by name
|
||||
struct SortTimerByName
|
||||
{
|
||||
bool operator()(const LLFastTimer::NamedTimer* i1, const LLFastTimer::NamedTimer* i2)
|
||||
{
|
||||
return i1->getName() < i2->getName();
|
||||
}
|
||||
};
|
||||
|
||||
//static
|
||||
void LLFastTimer::NamedTimer::buildHierarchy()
|
||||
{
|
||||
if (sCurFrameIndex < 0 ) return;
|
||||
|
||||
// set up initial tree
|
||||
{
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
if (&timer == NamedTimerFactory::instance().getRootTimer()) continue;
|
||||
|
||||
// bootstrap tree construction by attaching to last timer to be on stack
|
||||
// when this timer was called
|
||||
if (timer.getFrameState().mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
|
||||
{
|
||||
timer.setParent(timer.getFrameState().mLastCaller->mTimer);
|
||||
// no need to push up tree on first use, flag can be set spuriously
|
||||
timer.getFrameState().mMoveUpTree = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bump timers up tree if they've been flagged as being in the wrong place
|
||||
// do this in a bottom up order to promote descendants first before promoting ancestors
|
||||
// this preserves partial order derived from current frame's observations
|
||||
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getRootTimer());
|
||||
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(TimeBlock::getRootTimeBlock());
|
||||
it != end_timer_tree_bottom_up();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = *it;
|
||||
// skip root timer
|
||||
if (timerp == NamedTimerFactory::instance().getRootTimer()) continue;
|
||||
TimeBlock* timerp = *it;
|
||||
|
||||
if (timerp->getFrameState().mMoveUpTree)
|
||||
// sort timers by time last called, so call graph makes sense
|
||||
TimeBlockTreeNode& tree_node = timerp->getTreeNode();
|
||||
if (tree_node.mNeedsSorting)
|
||||
{
|
||||
std::sort(tree_node.mChildren.begin(), tree_node.mChildren.end(), SortTimerByName());
|
||||
}
|
||||
|
||||
// skip root timer
|
||||
if (timerp != &TimeBlock::getRootTimeBlock())
|
||||
{
|
||||
TimeBlockAccumulator* accumulator = timerp->getPrimaryAccumulator();
|
||||
|
||||
if (accumulator->mMoveUpTree)
|
||||
{
|
||||
// since ancestors have already been visited, reparenting won't affect tree traversal
|
||||
// since ancestors have already been visited, re-parenting won't affect tree traversal
|
||||
//step up tree, bringing our descendants with us
|
||||
LL_DEBUGS("FastTimers") << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
|
||||
" to child of " << timerp->getParent()->getParent()->getName() << LL_ENDL;
|
||||
timerp->setParent(timerp->getParent()->getParent());
|
||||
timerp->getFrameState().mMoveUpTree = false;
|
||||
accumulator->mParent = timerp->getParent();
|
||||
accumulator->mMoveUpTree = false;
|
||||
|
||||
// don't bubble up any ancestors until descendants are done bubbling up
|
||||
// as ancestors may call this timer only on certain paths, so we want to resolve
|
||||
// child-most block locations before their parents
|
||||
it.skipAncestors();
|
||||
}
|
||||
}
|
||||
|
||||
// sort timers by time last called, so call graph makes sense
|
||||
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||
it != end_timer_tree();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = (*it);
|
||||
if (timerp->mNeedsSorting)
|
||||
{
|
||||
std::sort(timerp->getChildren().begin(), timerp->getChildren().end(), SortTimerByName());
|
||||
}
|
||||
timerp->mNeedsSorting = false;
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::NamedTimer::accumulateTimings()
|
||||
{
|
||||
U32 cur_time = getCPUClockCount32();
|
||||
|
||||
void TimeBlock::updateTimes()
|
||||
{
|
||||
U64 cur_time = getCPUClockCount64();
|
||||
|
||||
// walk up stack of active timers and accumulate current time while leaving timing structures active
|
||||
LLFastTimer* cur_timer = sCurTimerData.mCurTimer;
|
||||
// root defined by parent pointing to self
|
||||
CurTimerData* cur_data = &sCurTimerData;
|
||||
while(cur_timer && cur_timer->mLastTimerData.mCurTimer != cur_timer)
|
||||
{
|
||||
U32 cumulative_time_delta = cur_time - cur_timer->mStartTime;
|
||||
U32 self_time_delta = cumulative_time_delta - cur_data->mChildTime;
|
||||
cur_data->mChildTime = 0;
|
||||
cur_timer->mFrameState->mSelfTimeCounter += self_time_delta;
|
||||
BlockTimerStackRecord* stack_record = ThreadTimerStack::getInstance();
|
||||
BlockTimer* cur_timer = stack_record->mActiveTimer;
|
||||
TimeBlockAccumulator* accumulator = stack_record->mTimeBlock->getPrimaryAccumulator();
|
||||
|
||||
while(cur_timer
|
||||
&& cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self
|
||||
{
|
||||
U64 cumulative_time_delta = cur_time - cur_timer->mStartTime;
|
||||
accumulator->mTotalTimeCounter += cumulative_time_delta
|
||||
- (accumulator->mTotalTimeCounter
|
||||
- cur_timer->mBlockStartTotalTimeCounter);
|
||||
accumulator->mSelfTimeCounter += cumulative_time_delta - stack_record->mChildTime;
|
||||
stack_record->mChildTime = 0;
|
||||
|
||||
cur_timer->mStartTime = cur_time;
|
||||
cur_timer->mBlockStartTotalTimeCounter = accumulator->mTotalTimeCounter;
|
||||
|
||||
cur_data = &cur_timer->mLastTimerData;
|
||||
cur_data->mChildTime += cumulative_time_delta;
|
||||
stack_record = &cur_timer->mParentTimerData;
|
||||
accumulator = stack_record->mTimeBlock->getPrimaryAccumulator();
|
||||
cur_timer = stack_record->mActiveTimer;
|
||||
|
||||
cur_timer = cur_timer->mLastTimerData.mCurTimer;
|
||||
stack_record->mChildTime += cumulative_time_delta;
|
||||
}
|
||||
}
|
||||
|
||||
// traverse tree in DFS post order, or bottom up
|
||||
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getRootTimer());
|
||||
it != end_timer_tree_bottom_up();
|
||||
static LLFastTimer::DeclareTimer FTM_PROCESS_TIMES("Process FastTimer Times");
|
||||
|
||||
// not thread safe, so only call on main thread
|
||||
//static
|
||||
void TimeBlock::processTimes()
|
||||
{
|
||||
LLFastTimer _(FTM_PROCESS_TIMES);
|
||||
get_clock_count(); // good place to calculate clock frequency
|
||||
|
||||
// set up initial tree
|
||||
bootstrapTimerTree();
|
||||
|
||||
incrementalUpdateTimerTree();
|
||||
|
||||
updateTimes();
|
||||
|
||||
// reset for next frame
|
||||
for (LLInstanceTracker<TimeBlock>::instance_iter it = LLInstanceTracker<TimeBlock>::beginInstances(),
|
||||
end_it = LLInstanceTracker<TimeBlock>::endInstances();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = (*it);
|
||||
timerp->mTotalTimeCounter = timerp->getFrameState().mSelfTimeCounter;
|
||||
for (child_const_iter child_it = timerp->beginChildren(); child_it != timerp->endChildren(); ++child_it)
|
||||
{
|
||||
timerp->mTotalTimeCounter += (*child_it)->mTotalTimeCounter;
|
||||
}
|
||||
TimeBlock& timer = *it;
|
||||
TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
|
||||
|
||||
S32 cur_frame = sCurFrameIndex;
|
||||
if (cur_frame >= 0)
|
||||
{
|
||||
// update timer history
|
||||
int hidx = cur_frame % HISTORY_NUM;
|
||||
|
||||
timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter;
|
||||
timerp->mCountAverage = ((U64)timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1);
|
||||
timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls;
|
||||
timerp->mCallAverage = ((U64)timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1);
|
||||
}
|
||||
accumulator->mLastCaller = NULL;
|
||||
accumulator->mMoveUpTree = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TimeBlock*>::iterator TimeBlock::beginChildren()
|
||||
{
|
||||
return getTreeNode().mChildren.begin();
|
||||
}
|
||||
|
||||
std::vector<TimeBlock*>::iterator TimeBlock::endChildren()
|
||||
{
|
||||
return getTreeNode().mChildren.end();
|
||||
}
|
||||
|
||||
std::vector<TimeBlock*>& TimeBlock::getChildren()
|
||||
{
|
||||
return getTreeNode().mChildren;
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFastTimer::NamedTimer::resetFrame()
|
||||
void TimeBlock::logStats()
|
||||
{
|
||||
// get ready for next frame
|
||||
if (sLog)
|
||||
{ //output current frame counts to performance log
|
||||
|
||||
static S32 call_count = 0;
|
||||
if (call_count % 100 == 0)
|
||||
{
|
||||
LL_DEBUGS("FastTimers") << "countsPerSecond (32 bit): " << countsPerSecond() << LL_ENDL;
|
||||
LL_DEBUGS("FastTimers") << "get_clock_count (64 bit): " << get_clock_count() << llendl;
|
||||
LL_DEBUGS("FastTimers") << "countsPerSecond: " << countsPerSecond() << LL_ENDL;
|
||||
LL_DEBUGS("FastTimers") << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << LL_ENDL;
|
||||
LL_DEBUGS("FastTimers") << "getCPUClockCount32() " << getCPUClockCount32() << LL_ENDL;
|
||||
LL_DEBUGS("FastTimers") << "getCPUClockCount64() " << getCPUClockCount64() << LL_ENDL;
|
||||
LL_DEBUGS("FastTimers") << "elapsed sec " << ((F64)getCPUClockCount64())/((F64)LLProcessorInfo().getCPUFrequency()*1000000.0) << LL_ENDL;
|
||||
LL_DEBUGS("FastTimers") << "elapsed sec " << ((F64)getCPUClockCount64()) / (LLUnit<F64, LLUnits::Hertz>(LLProcessorInfo().getCPUFrequency())) << LL_ENDL;
|
||||
}
|
||||
call_count++;
|
||||
|
||||
F64 iclock_freq = 1000.0 / countsPerSecond(); // good place to calculate clock frequency
|
||||
|
||||
F64 total_time = 0;
|
||||
LLUnit<F64, LLUnits::Seconds> total_time(0);
|
||||
LLSD sd;
|
||||
|
||||
{
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
for (LLInstanceTracker<TimeBlock>::instance_iter it = LLInstanceTracker<TimeBlock>::beginInstances(),
|
||||
end_it = LLInstanceTracker<TimeBlock>::endInstances();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
FrameState& info = timer.getFrameState();
|
||||
sd[timer.getName()]["Time"] = (LLSD::Real) (info.mSelfTimeCounter*iclock_freq);
|
||||
sd[timer.getName()]["Calls"] = (LLSD::Integer) info.mCalls;
|
||||
TimeBlock& timer = *it;
|
||||
LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording();
|
||||
sd[timer.getName()]["Time"] = (LLSD::Real) (frame_recording.getLastRecording().getSum(timer).value());
|
||||
sd[timer.getName()]["Calls"] = (LLSD::Integer) (frame_recording.getLastRecording().getSum(timer.callCount()));
|
||||
|
||||
// computing total time here because getting the root timer's getCountHistory
|
||||
// doesn't work correctly on the first frame
|
||||
total_time = total_time + info.mSelfTimeCounter * iclock_freq;
|
||||
total_time += frame_recording.getLastRecording().getSum(timer);
|
||||
}
|
||||
}
|
||||
|
||||
sd["Total"]["Time"] = (LLSD::Real) total_time;
|
||||
sd["Total"]["Time"] = (LLSD::Real) total_time.value();
|
||||
sd["Total"]["Calls"] = (LLSD::Integer) 1;
|
||||
|
||||
{
|
||||
|
|
@ -469,168 +368,44 @@ void LLFastTimer::NamedTimer::resetFrame()
|
|||
}
|
||||
}
|
||||
|
||||
// reset for next frame
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
|
||||
FrameState& info = timer.getFrameState();
|
||||
info.mSelfTimeCounter = 0;
|
||||
info.mCalls = 0;
|
||||
info.mLastCaller = NULL;
|
||||
info.mMoveUpTree = false;
|
||||
// update parent pointer in timer state struct
|
||||
if (timer.mParent)
|
||||
{
|
||||
info.mParent = &timer.mParent->getFrameState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::NamedTimer::reset()
|
||||
{
|
||||
resetFrame(); // reset frame data
|
||||
|
||||
// walk up stack of active timers and reset start times to current time
|
||||
// effectively zeroing out any accumulated time
|
||||
U32 cur_time = getCPUClockCount32();
|
||||
|
||||
// root defined by parent pointing to self
|
||||
CurTimerData* cur_data = &sCurTimerData;
|
||||
LLFastTimer* cur_timer = cur_data->mCurTimer;
|
||||
while(cur_timer && cur_timer->mLastTimerData.mCurTimer != cur_timer)
|
||||
void TimeBlock::dumpCurTimes()
|
||||
{
|
||||
cur_timer->mStartTime = cur_time;
|
||||
cur_data->mChildTime = 0;
|
||||
|
||||
cur_data = &cur_timer->mLastTimerData;
|
||||
cur_timer = cur_data->mCurTimer;
|
||||
}
|
||||
|
||||
// reset all history
|
||||
{
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
{
|
||||
NamedTimer& timer = *it;
|
||||
if (&timer != NamedTimerFactory::instance().getRootTimer())
|
||||
{
|
||||
timer.setParent(NamedTimerFactory::instance().getRootTimer());
|
||||
}
|
||||
|
||||
timer.mCountAverage = 0;
|
||||
timer.mCallAverage = 0;
|
||||
memset(timer.mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
memset(timer.mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||
}
|
||||
}
|
||||
|
||||
sLastFrameIndex = 0;
|
||||
sCurFrameIndex = 0;
|
||||
}
|
||||
|
||||
U32 LLFastTimer::NamedTimer::getHistoricalCount(S32 history_index) const
|
||||
{
|
||||
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
|
||||
return mCountHistory[history_idx];
|
||||
}
|
||||
|
||||
U32 LLFastTimer::NamedTimer::getHistoricalCalls(S32 history_index ) const
|
||||
{
|
||||
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
|
||||
return mCallHistory[history_idx];
|
||||
}
|
||||
|
||||
LLFastTimer::FrameState& LLFastTimer::NamedTimer::getFrameState() const
|
||||
{
|
||||
return *mFrameState;
|
||||
}
|
||||
|
||||
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::beginChildren()
|
||||
{
|
||||
return mChildren.begin();
|
||||
}
|
||||
|
||||
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::endChildren()
|
||||
{
|
||||
return mChildren.end();
|
||||
}
|
||||
|
||||
std::vector<LLFastTimer::NamedTimer*>& LLFastTimer::NamedTimer::getChildren()
|
||||
{
|
||||
return mChildren;
|
||||
}
|
||||
|
||||
//static
|
||||
LLFastTimer::NamedTimer& LLFastTimer::NamedTimer::getRootNamedTimer()
|
||||
{
|
||||
return *NamedTimerFactory::instance().getRootTimer();
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::nextFrame()
|
||||
{
|
||||
countsPerSecond(); // good place to calculate clock frequency
|
||||
U64 frame_time = getCPUClockCount64();
|
||||
if ((frame_time - sLastFrameTime) >> 8 > 0xffffffff)
|
||||
{
|
||||
llinfos << "Slow frame, fast timers inaccurate" << llendl;
|
||||
}
|
||||
|
||||
if (!sPauseHistory)
|
||||
{
|
||||
NamedTimer::processTimes();
|
||||
sLastFrameIndex = sCurFrameIndex++;
|
||||
}
|
||||
|
||||
// get ready for next frame
|
||||
NamedTimer::resetFrame();
|
||||
sLastFrameTime = frame_time;
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::dumpCurTimes()
|
||||
{
|
||||
// accumulate timings, etc.
|
||||
NamedTimer::processTimes();
|
||||
|
||||
F64 clock_freq = (F64)countsPerSecond();
|
||||
F64 iclock_freq = 1000.0 / clock_freq; // clock_ticks -> milliseconds
|
||||
LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording();
|
||||
LLTrace::Recording& last_frame_recording = frame_recording.getLastRecording();
|
||||
|
||||
// walk over timers in depth order and output timings
|
||||
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||
for(timer_tree_dfs_iterator_t it = begin_timer_tree(TimeBlock::getRootTimeBlock());
|
||||
it != end_timer_tree();
|
||||
++it)
|
||||
{
|
||||
NamedTimer* timerp = (*it);
|
||||
F64 total_time_ms = ((F64)timerp->getHistoricalCount(0) * iclock_freq);
|
||||
TimeBlock* timerp = (*it);
|
||||
LLUnit<F64, LLUnits::Seconds> total_time = last_frame_recording.getSum(*timerp);
|
||||
U32 num_calls = last_frame_recording.getSum(timerp->callCount());
|
||||
|
||||
// Don't bother with really brief times, keep output concise
|
||||
if (total_time_ms < 0.1) continue;
|
||||
if (total_time < LLUnit<F32, LLUnits::Milliseconds>(0.1)) continue;
|
||||
|
||||
std::ostringstream out_str;
|
||||
for (S32 i = 0; i < timerp->getDepth(); i++)
|
||||
TimeBlock* parent_timerp = timerp;
|
||||
while(parent_timerp && parent_timerp != parent_timerp->getParent())
|
||||
{
|
||||
out_str << "\t";
|
||||
parent_timerp = parent_timerp->getParent();
|
||||
}
|
||||
|
||||
|
||||
out_str << timerp->getName() << " "
|
||||
<< std::setprecision(3) << total_time_ms << " ms, "
|
||||
<< timerp->getHistoricalCalls(0) << " calls";
|
||||
<< std::setprecision(3) << total_time.getAs<LLUnits::Milliseconds>() << " ms, "
|
||||
<< num_calls << " calls";
|
||||
|
||||
llinfos << out_str.str() << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFastTimer::reset()
|
||||
{
|
||||
NamedTimer::reset();
|
||||
}
|
||||
|
||||
|
||||
//static
|
||||
void LLFastTimer::writeLog(std::ostream& os)
|
||||
void TimeBlock::writeLog(std::ostream& os)
|
||||
{
|
||||
while (!sLogQueue.empty())
|
||||
{
|
||||
|
|
@ -641,22 +416,62 @@ void LLFastTimer::writeLog(std::ostream& os)
|
|||
}
|
||||
}
|
||||
|
||||
//static
|
||||
const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& name)
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TimeBlockAccumulator
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TimeBlockAccumulator::TimeBlockAccumulator()
|
||||
: mTotalTimeCounter(0),
|
||||
mSelfTimeCounter(0),
|
||||
mStartTotalTimeCounter(0),
|
||||
mCalls(0),
|
||||
mLastCaller(NULL),
|
||||
mActiveCount(0),
|
||||
mMoveUpTree(false),
|
||||
mParent(NULL)
|
||||
{}
|
||||
|
||||
void TimeBlockAccumulator::addSamples( const TimeBlockAccumulator& other, bool append )
|
||||
{
|
||||
return NamedTimerFactory::instance().getTimerByName(name);
|
||||
// we can't merge two unrelated time block samples, as that will screw with the nested timings
|
||||
// due to the call hierarchy of each thread
|
||||
llassert(append);
|
||||
mTotalTimeCounter += other.mTotalTimeCounter - other.mStartTotalTimeCounter;
|
||||
mSelfTimeCounter += other.mSelfTimeCounter;
|
||||
mCalls += other.mCalls;
|
||||
mLastCaller = other.mLastCaller;
|
||||
mActiveCount = other.mActiveCount;
|
||||
mMoveUpTree = other.mMoveUpTree;
|
||||
mParent = other.mParent;
|
||||
}
|
||||
|
||||
LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
|
||||
: mFrameState(state)
|
||||
void TimeBlockAccumulator::reset( const TimeBlockAccumulator* other )
|
||||
{
|
||||
U32 start_time = getCPUClockCount32();
|
||||
mStartTime = start_time;
|
||||
mFrameState->mActiveCount++;
|
||||
LLFastTimer::sCurTimerData.mCurTimer = this;
|
||||
LLFastTimer::sCurTimerData.mFrameState = mFrameState;
|
||||
LLFastTimer::sCurTimerData.mChildTime = 0;
|
||||
mLastTimerData = LLFastTimer::sCurTimerData;
|
||||
mCalls = 0;
|
||||
mSelfTimeCounter = 0;
|
||||
|
||||
if (other)
|
||||
{
|
||||
mStartTotalTimeCounter = other->mTotalTimeCounter;
|
||||
mTotalTimeCounter = mStartTotalTimeCounter;
|
||||
|
||||
mLastCaller = other->mLastCaller;
|
||||
mActiveCount = other->mActiveCount;
|
||||
mMoveUpTree = other->mMoveUpTree;
|
||||
mParent = other->mParent;
|
||||
}
|
||||
else
|
||||
{
|
||||
mStartTotalTimeCounter = mTotalTimeCounter;
|
||||
}
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> BlockTimer::getElapsedTime()
|
||||
{
|
||||
U64 total_time = TimeBlock::getCPUClockCount64() - mStartTime;
|
||||
|
||||
return (F64)total_time / (F64)TimeBlock::countsPerSecond();
|
||||
}
|
||||
|
||||
|
||||
} // namespace LLTrace
|
||||
|
|
|
|||
|
|
@ -28,216 +28,96 @@
|
|||
#define LL_FASTTIMER_H
|
||||
|
||||
#include "llinstancetracker.h"
|
||||
#include "lltrace.h"
|
||||
|
||||
#define FAST_TIMER_ON 1
|
||||
#define DEBUG_FAST_TIMER_THREADS 1
|
||||
#define LL_FASTTIMER_USE_RDTSC 1
|
||||
|
||||
class LLMutex;
|
||||
|
||||
#include <queue>
|
||||
#include "llsd.h"
|
||||
namespace LLTrace
|
||||
{
|
||||
|
||||
#define LL_FASTTIMER_USE_RDTSC 1
|
||||
struct BlockTimerStackRecord
|
||||
{
|
||||
class BlockTimer* mActiveTimer;
|
||||
class TimeBlock* mTimeBlock;
|
||||
U64 mChildTime;
|
||||
};
|
||||
|
||||
class ThreadTimerStack
|
||||
: public BlockTimerStackRecord,
|
||||
public LLThreadLocalSingleton<ThreadTimerStack>
|
||||
{
|
||||
friend class LLThreadLocalSingleton<ThreadTimerStack>;
|
||||
ThreadTimerStack()
|
||||
{}
|
||||
|
||||
LL_COMMON_API void assert_main_thread();
|
||||
public:
|
||||
ThreadTimerStack& operator=(const BlockTimerStackRecord& other)
|
||||
{
|
||||
BlockTimerStackRecord::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class LL_COMMON_API LLFastTimer
|
||||
class BlockTimer
|
||||
{
|
||||
public:
|
||||
class NamedTimer;
|
||||
friend class TimeBlock;
|
||||
typedef BlockTimer self_t;
|
||||
typedef class TimeBlock DeclareTimer;
|
||||
|
||||
struct LL_COMMON_API FrameState
|
||||
{
|
||||
FrameState();
|
||||
void setNamedTimer(NamedTimer* timerp) { mTimer = timerp; }
|
||||
BlockTimer(TimeBlock& timer);
|
||||
~BlockTimer();
|
||||
|
||||
U32 mSelfTimeCounter;
|
||||
U32 mCalls;
|
||||
FrameState* mParent; // info for caller timer
|
||||
FrameState* mLastCaller; // used to bootstrap tree construction
|
||||
NamedTimer* mTimer;
|
||||
U16 mActiveCount; // number of timers with this ID active on stack
|
||||
bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
|
||||
};
|
||||
LLUnit<F64, LLUnits::Seconds> getElapsedTime();
|
||||
|
||||
// stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
|
||||
class LL_COMMON_API NamedTimer
|
||||
: public LLInstanceTracker<NamedTimer>
|
||||
{
|
||||
friend class DeclareTimer;
|
||||
public:
|
||||
~NamedTimer();
|
||||
private:
|
||||
|
||||
enum { HISTORY_NUM = 300 };
|
||||
|
||||
const std::string& getName() const { return mName; }
|
||||
NamedTimer* getParent() const { return mParent; }
|
||||
void setParent(NamedTimer* parent);
|
||||
S32 getDepth();
|
||||
std::string getToolTip(S32 history_index = -1);
|
||||
|
||||
typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
|
||||
child_const_iter beginChildren();
|
||||
child_const_iter endChildren();
|
||||
std::vector<NamedTimer*>& getChildren();
|
||||
|
||||
void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
|
||||
bool getCollapsed() const { return mCollapsed; }
|
||||
|
||||
U32 getCountAverage() const { return mCountAverage; }
|
||||
U32 getCallAverage() const { return mCallAverage; }
|
||||
|
||||
U32 getHistoricalCount(S32 history_index = 0) const;
|
||||
U32 getHistoricalCalls(S32 history_index = 0) const;
|
||||
|
||||
static NamedTimer& getRootNamedTimer();
|
||||
|
||||
void setFrameState(FrameState* state) { mFrameState = state; state->setNamedTimer(this); }
|
||||
FrameState& getFrameState() const;
|
||||
|
||||
private:
|
||||
friend class LLFastTimer;
|
||||
friend class NamedTimerFactory;
|
||||
|
||||
//
|
||||
// methods
|
||||
//
|
||||
NamedTimer(const std::string& name);
|
||||
// recursive call to gather total time from children
|
||||
static void accumulateTimings();
|
||||
|
||||
// updates cumulative times and hierarchy,
|
||||
// can be called multiple times in a frame, at any point
|
||||
static void processTimes();
|
||||
|
||||
static void buildHierarchy();
|
||||
static void resetFrame();
|
||||
static void reset();
|
||||
|
||||
//
|
||||
// members
|
||||
//
|
||||
FrameState* mFrameState;
|
||||
|
||||
std::string mName;
|
||||
|
||||
U32 mTotalTimeCounter;
|
||||
|
||||
U32 mCountAverage;
|
||||
U32 mCallAverage;
|
||||
|
||||
U32* mCountHistory;
|
||||
U32* mCallHistory;
|
||||
|
||||
// tree structure
|
||||
NamedTimer* mParent; // NamedTimer of caller(parent)
|
||||
std::vector<NamedTimer*> mChildren;
|
||||
bool mCollapsed; // don't show children
|
||||
bool mNeedsSorting; // sort children whenever child added
|
||||
};
|
||||
|
||||
// used to statically declare a new named timer
|
||||
class LL_COMMON_API DeclareTimer
|
||||
: public LLInstanceTracker<DeclareTimer>
|
||||
{
|
||||
friend class LLFastTimer;
|
||||
public:
|
||||
DeclareTimer(const std::string& name, bool open);
|
||||
DeclareTimer(const std::string& name);
|
||||
|
||||
NamedTimer& getNamedTimer() { return mTimer; }
|
||||
|
||||
private:
|
||||
FrameState mFrameState;
|
||||
NamedTimer& mTimer;
|
||||
};
|
||||
U64 mStartTime;
|
||||
U64 mBlockStartTotalTimeCounter;
|
||||
BlockTimerStackRecord mParentTimerData;
|
||||
};
|
||||
|
||||
// stores a "named" timer instance to be reused via multiple BlockTimer stack instances
|
||||
class TimeBlock
|
||||
: public TraceType<TimeBlockAccumulator>,
|
||||
public LLInstanceTracker<TimeBlock>
|
||||
{
|
||||
public:
|
||||
LLFastTimer(LLFastTimer::FrameState* state);
|
||||
TimeBlock(const char* name, TimeBlock* parent = &getRootTimeBlock());
|
||||
|
||||
LL_FORCE_INLINE LLFastTimer(LLFastTimer::DeclareTimer& timer)
|
||||
: mFrameState(&timer.mFrameState)
|
||||
TimeBlockTreeNode& getTreeNode() const;
|
||||
TimeBlock* getParent() const { return getTreeNode().getParent(); }
|
||||
void setParent(TimeBlock* parent) { getTreeNode().setParent(parent); }
|
||||
|
||||
typedef std::vector<TimeBlock*>::iterator child_iter;
|
||||
typedef std::vector<TimeBlock*>::const_iterator child_const_iter;
|
||||
child_iter beginChildren();
|
||||
child_iter endChildren();
|
||||
std::vector<TimeBlock*>& getChildren();
|
||||
|
||||
TraceType<TimeBlockAccumulator::CallCountFacet>& callCount()
|
||||
{
|
||||
#if FAST_TIMER_ON
|
||||
LLFastTimer::FrameState* frame_state = mFrameState;
|
||||
mStartTime = getCPUClockCount32();
|
||||
|
||||
frame_state->mActiveCount++;
|
||||
frame_state->mCalls++;
|
||||
// keep current parent as long as it is active when we are
|
||||
frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
|
||||
|
||||
LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
|
||||
mLastTimerData = *cur_timer_data;
|
||||
cur_timer_data->mCurTimer = this;
|
||||
cur_timer_data->mFrameState = frame_state;
|
||||
cur_timer_data->mChildTime = 0;
|
||||
#endif
|
||||
#if DEBUG_FAST_TIMER_THREADS
|
||||
#if !LL_RELEASE
|
||||
assert_main_thread();
|
||||
#endif
|
||||
#endif
|
||||
return static_cast<TraceType<TimeBlockAccumulator::CallCountFacet>&>(*(TraceType<TimeBlockAccumulator>*)this);
|
||||
}
|
||||
|
||||
LL_FORCE_INLINE ~LLFastTimer()
|
||||
TraceType<TimeBlockAccumulator::SelfTimeFacet>& selfTime()
|
||||
{
|
||||
#if FAST_TIMER_ON
|
||||
LLFastTimer::FrameState* frame_state = mFrameState;
|
||||
U32 total_time = getCPUClockCount32() - mStartTime;
|
||||
|
||||
frame_state->mSelfTimeCounter += total_time - LLFastTimer::sCurTimerData.mChildTime;
|
||||
frame_state->mActiveCount--;
|
||||
|
||||
// store last caller to bootstrap tree creation
|
||||
// do this in the destructor in case of recursion to get topmost caller
|
||||
frame_state->mLastCaller = mLastTimerData.mFrameState;
|
||||
|
||||
// we are only tracking self time, so subtract our total time delta from parents
|
||||
mLastTimerData.mChildTime += total_time;
|
||||
|
||||
LLFastTimer::sCurTimerData = mLastTimerData;
|
||||
#endif
|
||||
return static_cast<TraceType<TimeBlockAccumulator::SelfTimeFacet>&>(*(TraceType<TimeBlockAccumulator>*)this);
|
||||
}
|
||||
|
||||
public:
|
||||
static LLMutex* sLogLock;
|
||||
static std::queue<LLSD> sLogQueue;
|
||||
static BOOL sLog;
|
||||
static BOOL sMetricLog;
|
||||
static std::string sLogName;
|
||||
static bool sPauseHistory;
|
||||
static bool sResetHistory;
|
||||
|
||||
// call this once a frame to reset timers
|
||||
static void nextFrame();
|
||||
static TimeBlock& getRootTimeBlock();
|
||||
static void pushLog(LLSD sd);
|
||||
static void setLogLock(LLMutex* mutex);
|
||||
static void writeLog(std::ostream& os);
|
||||
static void updateTimes();
|
||||
|
||||
// dumps current cumulative frame stats to log
|
||||
// call nextFrame() to reset timers
|
||||
static void dumpCurTimes();
|
||||
|
||||
// call this to reset timer hierarchy, averages, etc.
|
||||
static void reset();
|
||||
|
||||
static U64 countsPerSecond();
|
||||
static S32 getLastFrameIndex() { return sLastFrameIndex; }
|
||||
static S32 getCurFrameIndex() { return sCurFrameIndex; }
|
||||
|
||||
static void writeLog(std::ostream& os);
|
||||
static const NamedTimer* getTimerByName(const std::string& name);
|
||||
|
||||
struct CurTimerData
|
||||
{
|
||||
LLFastTimer* mCurTimer;
|
||||
FrameState* mFrameState;
|
||||
U32 mChildTime;
|
||||
};
|
||||
static CurTimerData sCurTimerData;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Important note: These implementations must be FAST!
|
||||
|
|
@ -260,14 +140,14 @@ private:
|
|||
//#undef _interlockedbittestandset
|
||||
//#undef _interlockedbittestandreset
|
||||
|
||||
//inline U32 LLFastTimer::getCPUClockCount32()
|
||||
//inline U32 TimeBlock::getCPUClockCount32()
|
||||
//{
|
||||
// U64 time_stamp = __rdtsc();
|
||||
// return (U32)(time_stamp >> 8);
|
||||
//}
|
||||
//
|
||||
//// return full timer value, *not* shifted by 8 bits
|
||||
//inline U64 LLFastTimer::getCPUClockCount64()
|
||||
//inline U64 TimeBlock::getCPUClockCount64()
|
||||
//{
|
||||
// return __rdtsc();
|
||||
//}
|
||||
|
|
@ -307,7 +187,7 @@ private:
|
|||
}
|
||||
|
||||
#else
|
||||
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
|
||||
//U64 get_clock_count(); // in lltimer.cpp
|
||||
// These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
|
||||
static U32 getCPUClockCount32()
|
||||
{
|
||||
|
|
@ -374,18 +254,74 @@ private:
|
|||
|
||||
#endif
|
||||
|
||||
static U64 sClockResolution;
|
||||
static U64 countsPerSecond();
|
||||
|
||||
static S32 sCurFrameIndex;
|
||||
static S32 sLastFrameIndex;
|
||||
static U64 sLastFrameTime;
|
||||
// updates cumulative times and hierarchy,
|
||||
// can be called multiple times in a frame, at any point
|
||||
static void processTimes();
|
||||
|
||||
U32 mStartTime;
|
||||
LLFastTimer::FrameState* mFrameState;
|
||||
LLFastTimer::CurTimerData mLastTimerData;
|
||||
static void bootstrapTimerTree();
|
||||
static void incrementalUpdateTimerTree();
|
||||
|
||||
// call this once a frame to periodically log timers
|
||||
static void logStats();
|
||||
|
||||
bool mCollapsed; // don't show children
|
||||
|
||||
// statics
|
||||
static std::string sLogName;
|
||||
static bool sMetricLog,
|
||||
sLog;
|
||||
static U64 sClockResolution;
|
||||
};
|
||||
|
||||
typedef class LLFastTimer LLFastTimer;
|
||||
LL_FORCE_INLINE BlockTimer::BlockTimer(TimeBlock& timer)
|
||||
{
|
||||
#if FAST_TIMER_ON
|
||||
BlockTimerStackRecord* cur_timer_data = ThreadTimerStack::getIfExists();
|
||||
TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
|
||||
accumulator->mActiveCount++;
|
||||
mBlockStartTotalTimeCounter = accumulator->mTotalTimeCounter;
|
||||
// keep current parent as long as it is active when we are
|
||||
accumulator->mMoveUpTree |= (accumulator->mParent->getPrimaryAccumulator()->mActiveCount == 0);
|
||||
|
||||
// store top of stack
|
||||
mParentTimerData = *cur_timer_data;
|
||||
// push new information
|
||||
cur_timer_data->mActiveTimer = this;
|
||||
cur_timer_data->mTimeBlock = &timer;
|
||||
cur_timer_data->mChildTime = 0;
|
||||
|
||||
mStartTime = TimeBlock::getCPUClockCount64();
|
||||
#endif
|
||||
}
|
||||
|
||||
LL_FORCE_INLINE BlockTimer::~BlockTimer()
|
||||
{
|
||||
#if FAST_TIMER_ON
|
||||
U64 total_time = TimeBlock::getCPUClockCount64() - mStartTime;
|
||||
BlockTimerStackRecord* cur_timer_data = ThreadTimerStack::getIfExists();
|
||||
TimeBlockAccumulator* accumulator = cur_timer_data->mTimeBlock->getPrimaryAccumulator();
|
||||
|
||||
accumulator->mCalls++;
|
||||
accumulator->mTotalTimeCounter += total_time - (accumulator->mTotalTimeCounter - mBlockStartTotalTimeCounter);
|
||||
accumulator->mSelfTimeCounter += total_time - cur_timer_data->mChildTime;
|
||||
accumulator->mActiveCount--;
|
||||
|
||||
// store last caller to bootstrap tree creation
|
||||
// do this in the destructor in case of recursion to get topmost caller
|
||||
accumulator->mLastCaller = mParentTimerData.mTimeBlock;
|
||||
|
||||
// we are only tracking self time, so subtract our total time delta from parents
|
||||
mParentTimerData.mChildTime += total_time;
|
||||
|
||||
//pop stack
|
||||
*cur_timer_data = mParentTimerData;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
typedef LLTrace::BlockTimer LLFastTimer;
|
||||
|
||||
#endif // LL_LLFASTTIMER_H
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
*/
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include <windows.h>
|
||||
#include "llwin32headerslean.h"
|
||||
#include <stdlib.h> // Windows errno
|
||||
#else
|
||||
#include <errno.h>
|
||||
|
|
@ -438,7 +438,7 @@ llstdio_filebuf::int_type llstdio_filebuf::overflow(llstdio_filebuf::int_type __
|
|||
_M_set_buffer(0);
|
||||
__ret = traits_type::not_eof(__c);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_M_buf_size > 1)
|
||||
{
|
||||
// Overflow in 'uncommitted' mode: set _M_writing, set
|
||||
|
|
@ -496,11 +496,11 @@ bool llstdio_filebuf::_convert_to_external(char_type* __ibuf,
|
|||
if (__r == codecvt_base::ok || __r == codecvt_base::partial)
|
||||
__blen = __bend - __buf;
|
||||
else if (__r == codecvt_base::noconv)
|
||||
{
|
||||
{
|
||||
// Same as the always_noconv case above.
|
||||
__buf = reinterpret_cast<char*>(__ibuf);
|
||||
__blen = __ilen;
|
||||
}
|
||||
}
|
||||
else
|
||||
__throw_ios_failure(__N("llstdio_filebuf::_convert_to_external "
|
||||
"conversion error"));
|
||||
|
|
@ -643,9 +643,9 @@ llstdio_filebuf::int_type llstdio_filebuf::underflow()
|
|||
_M_ext_end, _M_ext_next,
|
||||
this->eback(),
|
||||
this->eback() + __buflen, __iend);
|
||||
}
|
||||
}
|
||||
if (__r == codecvt_base::noconv)
|
||||
{
|
||||
{
|
||||
size_t __avail = _M_ext_end - _M_ext_buf;
|
||||
__ilen = std::min(__avail, __buflen);
|
||||
traits_type::copy(this->eback(),
|
||||
|
|
@ -806,15 +806,15 @@ std::streamsize llstdio_filebuf::xsputn(char_type* __s, std::streamsize __n)
|
|||
__ret = fwrite(__buf, 1, __buffill, _M_file.file());
|
||||
}
|
||||
if (__ret == __buffill)
|
||||
{
|
||||
{
|
||||
__ret += fwrite(reinterpret_cast<const char*>(__s), 1,
|
||||
__n, _M_file.file());
|
||||
}
|
||||
}
|
||||
if (__ret == __buffill + __n)
|
||||
{
|
||||
_M_set_buffer(0);
|
||||
_M_writing = true;
|
||||
}
|
||||
}
|
||||
if (__ret > __buffill)
|
||||
__ret -= __buffill;
|
||||
else
|
||||
|
|
@ -848,7 +848,7 @@ llifstream::llifstream() : _M_filebuf(),
|
|||
#endif
|
||||
|
||||
// explicit
|
||||
llifstream::llifstream(const std::string& _Filename,
|
||||
llifstream::llifstream(const std::string& _Filename,
|
||||
ios_base::openmode _Mode) : _M_filebuf(),
|
||||
#if LL_WINDOWS
|
||||
std::istream(&_M_filebuf)
|
||||
|
|
@ -877,7 +877,7 @@ llifstream::llifstream(const char* _Filename,
|
|||
if (_M_filebuf.open(wideName.c_str(), _Mode | ios_base::in) == 0)
|
||||
{
|
||||
_Myios::setstate(ios_base::failbit);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::istream()
|
||||
|
|
@ -951,8 +951,8 @@ void llifstream::close()
|
|||
#else
|
||||
this->setstate(ios_base::failbit);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************** output file stream ********************************/
|
||||
|
|
@ -1042,7 +1042,7 @@ void llofstream::open(const char* _Filename, ios_base::openmode _Mode)
|
|||
#if LL_WINDOWS
|
||||
llutf16string wideName = utf8str_to_utf16str( _Filename );
|
||||
if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0)
|
||||
{
|
||||
{
|
||||
_Myios::setstate(ios_base::failbit);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
#include <ctype.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include "llwin32headers.h"
|
||||
#include <winnt.h>
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,155 +0,0 @@
|
|||
/**
|
||||
* @file llindexedqueue.h
|
||||
* @brief An indexed FIFO queue, where only one element with each key
|
||||
* can be in the queue.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLINDEXEDQUEUE_H
|
||||
#define LL_LLINDEXEDQUEUE_H
|
||||
|
||||
// An indexed FIFO queue, where only one element with each key can be in the queue.
|
||||
// This is ONLY used in the interest list, you'll probably want to review this code
|
||||
// carefully if you want to use it elsewhere - Doug
|
||||
|
||||
template <typename Type>
|
||||
class LLIndexedQueue
|
||||
{
|
||||
protected:
|
||||
typedef std::deque<Type> type_deque;
|
||||
type_deque mQueue;
|
||||
std::set<Type> mKeySet;
|
||||
|
||||
public:
|
||||
LLIndexedQueue() {}
|
||||
|
||||
// move_if_there is an O(n) operation
|
||||
bool push_back(const Type &value, bool move_if_there = false)
|
||||
{
|
||||
if (mKeySet.find(value) != mKeySet.end())
|
||||
{
|
||||
// Already on the queue
|
||||
if (move_if_there)
|
||||
{
|
||||
// Remove the existing entry.
|
||||
typename type_deque::iterator it;
|
||||
for (it = mQueue.begin(); it != mQueue.end(); ++it)
|
||||
{
|
||||
if (*it == value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This HAS to succeed, otherwise there's a serious bug in the keyset implementation
|
||||
// (although this isn't thread safe, at all)
|
||||
|
||||
mQueue.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're not moving it, leave it alone
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Doesn't exist, add it to the key set
|
||||
mKeySet.insert(value);
|
||||
}
|
||||
|
||||
mQueue.push_back(value);
|
||||
|
||||
// We succeeded in adding the new element.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push_front(const Type &value, bool move_if_there = false)
|
||||
{
|
||||
if (mKeySet.find(value) != mKeySet.end())
|
||||
{
|
||||
// Already on the queue
|
||||
if (move_if_there)
|
||||
{
|
||||
// Remove the existing entry.
|
||||
typename type_deque::iterator it;
|
||||
for (it = mQueue.begin(); it != mQueue.end(); ++it)
|
||||
{
|
||||
if (*it == value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This HAS to succeed, otherwise there's a serious bug in the keyset implementation
|
||||
// (although this isn't thread safe, at all)
|
||||
|
||||
mQueue.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're not moving it, leave it alone
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Doesn't exist, add it to the key set
|
||||
mKeySet.insert(value);
|
||||
}
|
||||
|
||||
mQueue.push_front(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
Type value = mQueue.front();
|
||||
mKeySet.erase(value);
|
||||
mQueue.pop_front();
|
||||
}
|
||||
|
||||
Type &front()
|
||||
{
|
||||
return mQueue.front();
|
||||
}
|
||||
|
||||
S32 size() const
|
||||
{
|
||||
return mQueue.size();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return mQueue.empty();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
// Clear out all elements on the queue
|
||||
mQueue.clear();
|
||||
mKeySet.clear();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // LL_LLINDEXEDQUEUE_H
|
||||
|
|
@ -28,10 +28,17 @@
|
|||
#include "linden_common.h"
|
||||
|
||||
#include "llinitparam.h"
|
||||
#include "llformat.h"
|
||||
|
||||
|
||||
namespace LLInitParam
|
||||
{
|
||||
|
||||
predicate_rule_t default_parse_rules()
|
||||
{
|
||||
return ll_make_predicate(PROVIDED) && !ll_make_predicate(EMPTY);
|
||||
}
|
||||
|
||||
//
|
||||
// Param
|
||||
//
|
||||
|
|
@ -196,6 +203,9 @@ namespace LLInitParam
|
|||
|
||||
bool BaseBlock::validateBlock(bool emit_errors) const
|
||||
{
|
||||
// only validate block when it hasn't already passed validation with current data
|
||||
if (!mValidated)
|
||||
{
|
||||
const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
|
||||
for (BlockDescriptor::param_validation_list_t::const_iterator it = block_data.mValidationList.begin(); it != block_data.mValidationList.end(); ++it)
|
||||
{
|
||||
|
|
@ -209,11 +219,18 @@ namespace LLInitParam
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
mValidated = true;
|
||||
}
|
||||
return mValidated;
|
||||
}
|
||||
|
||||
void BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const LLInitParam::BaseBlock* diff_block) const
|
||||
bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const LLInitParam::BaseBlock* diff_block) const
|
||||
{
|
||||
bool serialized = false;
|
||||
if (!predicate_rule.check(ll_make_predicate(PROVIDED, isProvided())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// named param is one like LLView::Params::follows
|
||||
// unnamed param is like LLView::Params::rect - implicit
|
||||
const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
|
||||
|
|
@ -225,10 +242,10 @@ namespace LLInitParam
|
|||
param_handle_t param_handle = (*it)->mParamHandle;
|
||||
const Param* param = getParamFromHandle(param_handle);
|
||||
ParamDescriptor::serialize_func_t serialize_func = (*it)->mSerializeFunc;
|
||||
if (serialize_func)
|
||||
if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided())))
|
||||
{
|
||||
const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
|
||||
serialize_func(*param, parser, name_stack, diff_param);
|
||||
serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +256,7 @@ namespace LLInitParam
|
|||
param_handle_t param_handle = it->second->mParamHandle;
|
||||
const Param* param = getParamFromHandle(param_handle);
|
||||
ParamDescriptor::serialize_func_t serialize_func = it->second->mSerializeFunc;
|
||||
if (serialize_func && param->anyProvided())
|
||||
if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided())))
|
||||
{
|
||||
// Ensure this param has not already been serialized
|
||||
// Prevents <rect> from being serialized as its own tag.
|
||||
|
|
@ -264,10 +281,17 @@ namespace LLInitParam
|
|||
|
||||
name_stack.push_back(std::make_pair(it->first, !duplicate));
|
||||
const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
|
||||
serialize_func(*param, parser, name_stack, diff_param);
|
||||
serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param);
|
||||
name_stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY)))
|
||||
{
|
||||
serialized |= parser.writeValue(Flag(), name_stack);
|
||||
}
|
||||
// was anything serialized in this block?
|
||||
return serialized;
|
||||
}
|
||||
|
||||
bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const
|
||||
|
|
|
|||
|
|
@ -29,14 +29,15 @@
|
|||
#define LL_LLPARAM_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/type_traits/is_convertible.hpp>
|
||||
#include <boost/type_traits/is_enum.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llstl.h"
|
||||
#include "llpredicate.h"
|
||||
|
||||
namespace LLTypeTags
|
||||
{
|
||||
|
|
@ -195,6 +196,8 @@ namespace LLInitParam
|
|||
return mValue;
|
||||
}
|
||||
|
||||
bool isValid() const { return true; }
|
||||
|
||||
protected:
|
||||
T mValue;
|
||||
};
|
||||
|
|
@ -209,13 +212,11 @@ namespace LLInitParam
|
|||
typedef T value_t;
|
||||
|
||||
ParamValue()
|
||||
: T(),
|
||||
mValidated(false)
|
||||
: T()
|
||||
{}
|
||||
|
||||
ParamValue(const default_value_t& other)
|
||||
: T(other),
|
||||
mValidated(false)
|
||||
: T(other)
|
||||
{}
|
||||
|
||||
void setValue(const value_t& val)
|
||||
|
|
@ -232,9 +233,6 @@ namespace LLInitParam
|
|||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -497,25 +495,25 @@ namespace LLInitParam
|
|||
virtual ~Parser();
|
||||
|
||||
template <typename T> bool readValue(T& param, typename boost::disable_if<boost::is_enum<T> >::type* dummy = 0)
|
||||
{
|
||||
{
|
||||
parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
|
||||
if (found_it != mParserReadFuncs->end())
|
||||
{
|
||||
{
|
||||
return found_it->second(*this, (void*)¶m);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T> bool readValue(T& param, typename boost::enable_if<boost::is_enum<T> >::type* dummy = 0)
|
||||
{
|
||||
{
|
||||
parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
|
||||
if (found_it != mParserReadFuncs->end())
|
||||
{
|
||||
{
|
||||
return found_it->second(*this, (void*)¶m);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
found_it = mParserReadFuncs->find(&typeid(S32));
|
||||
if (found_it != mParserReadFuncs->end())
|
||||
{
|
||||
|
|
@ -579,6 +577,19 @@ namespace LLInitParam
|
|||
|
||||
class Param;
|
||||
|
||||
enum ESerializePredicates
|
||||
{
|
||||
PROVIDED,
|
||||
REQUIRED,
|
||||
VALID,
|
||||
HAS_DEFAULT_VALUE,
|
||||
EMPTY
|
||||
};
|
||||
|
||||
typedef LLPredicate::Rule<ESerializePredicates> predicate_rule_t;
|
||||
|
||||
predicate_rule_t default_parse_rules();
|
||||
|
||||
// various callbacks and constraints associated with an individual param
|
||||
struct LL_COMMON_API ParamDescriptor
|
||||
{
|
||||
|
|
@ -589,7 +600,7 @@ namespace LLInitParam
|
|||
|
||||
typedef bool(*merge_func_t)(Param&, const Param&, bool);
|
||||
typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool);
|
||||
typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param);
|
||||
typedef bool(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const predicate_rule_t rules, const Param* diff_param);
|
||||
typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
|
||||
typedef bool(*validation_func_t)(const Param*);
|
||||
|
||||
|
|
@ -617,7 +628,7 @@ namespace LLInitParam
|
|||
UserData* mUserData;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<ParamDescriptor> ParamDescriptorPtr;
|
||||
typedef ParamDescriptor* ParamDescriptorPtr;
|
||||
|
||||
// each derived Block class keeps a static data structure maintaining offsets to various params
|
||||
class LL_COMMON_API BlockDescriptor
|
||||
|
|
@ -829,12 +840,28 @@ namespace LLInitParam
|
|||
LOG_CLASS(BaseBlock);
|
||||
friend class Param;
|
||||
|
||||
BaseBlock()
|
||||
: mValidated(false),
|
||||
mParamProvided(false)
|
||||
{}
|
||||
|
||||
virtual ~BaseBlock() {}
|
||||
bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false);
|
||||
|
||||
param_handle_t getHandleFromParam(const Param* param) const;
|
||||
bool validateBlock(bool emit_errors = true) const;
|
||||
|
||||
bool isProvided() const
|
||||
{
|
||||
return mParamProvided;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return validateBlock(false);
|
||||
}
|
||||
|
||||
|
||||
Param* getParamFromHandle(const param_handle_t param_handle)
|
||||
{
|
||||
if (param_handle == 0) return NULL;
|
||||
|
|
@ -852,10 +879,19 @@ namespace LLInitParam
|
|||
void addSynonym(Param& param, const std::string& synonym);
|
||||
|
||||
// Blocks can override this to do custom tracking of changes
|
||||
virtual void paramChanged(const Param& changed_param, bool user_provided) {}
|
||||
virtual void paramChanged(const Param& changed_param, bool user_provided)
|
||||
{
|
||||
if (user_provided)
|
||||
{
|
||||
// a child param has been explicitly changed
|
||||
// so *some* aspect of this block is now provided
|
||||
mValidated = false;
|
||||
mParamProvided = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
|
||||
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
|
||||
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t rule, const BaseBlock* diff_block = NULL) const;
|
||||
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const;
|
||||
|
||||
virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
|
||||
|
|
@ -893,6 +929,9 @@ namespace LLInitParam
|
|||
return mergeBlock(block_data, source, overwrite);
|
||||
}
|
||||
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
bool mParamProvided;
|
||||
|
||||
private:
|
||||
const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
|
||||
};
|
||||
|
|
@ -986,6 +1025,8 @@ namespace LLInitParam
|
|||
|
||||
bool isProvided() const { return Param::anyProvided(); }
|
||||
|
||||
bool isValid() const { return true; }
|
||||
|
||||
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
||||
{
|
||||
self_t& typed_param = static_cast<self_t&>(param);
|
||||
|
|
@ -1014,10 +1055,23 @@ namespace LLInitParam
|
|||
return false;
|
||||
}
|
||||
|
||||
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
||||
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
||||
{
|
||||
bool serialized = false;
|
||||
const self_t& typed_param = static_cast<const self_t&>(param);
|
||||
if (!typed_param.isProvided()) return;
|
||||
const self_t* diff_typed_param = static_cast<const self_t*>(diff_param);
|
||||
|
||||
LLPredicate::Value<ESerializePredicates> predicate;
|
||||
if (diff_typed_param && ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue()))
|
||||
{
|
||||
predicate.set(HAS_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
predicate.set(VALID, typed_param.isValid());
|
||||
predicate.set(PROVIDED, typed_param.anyProvided());
|
||||
predicate.set(EMPTY, false);
|
||||
|
||||
if (!predicate_rule.check(predicate)) return false;
|
||||
|
||||
if (!name_stack.empty())
|
||||
{
|
||||
|
|
@ -1030,25 +1084,27 @@ namespace LLInitParam
|
|||
|
||||
if (!key.empty())
|
||||
{
|
||||
if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), key))
|
||||
if (!diff_typed_param || !ParamCompare<std::string>::equals(diff_typed_param->getValueName(), key))
|
||||
{
|
||||
parser.writeValue(key, name_stack);
|
||||
serialized = parser.writeValue(key, name_stack);
|
||||
}
|
||||
}
|
||||
// then try to serialize value directly
|
||||
else if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), static_cast<const self_t*>(diff_param)->getValue()))
|
||||
else if (!diff_typed_param || ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue()))
|
||||
{
|
||||
if (!parser.writeValue(typed_param.getValue(), name_stack))
|
||||
serialized = parser.writeValue(typed_param.getValue(), name_stack);
|
||||
if (!serialized)
|
||||
{
|
||||
std::string calculated_key = typed_param.calcValueName(typed_param.getValue());
|
||||
if (calculated_key.size()
|
||||
&& (!diff_param
|
||||
&& (!diff_typed_param
|
||||
|| !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), calculated_key)))
|
||||
{
|
||||
parser.writeValue(calculated_key, name_stack);
|
||||
serialized = parser.writeValue(calculated_key, name_stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
||||
|
|
@ -1113,19 +1169,19 @@ namespace LLInitParam
|
|||
};
|
||||
|
||||
// parameter that is a block
|
||||
template <typename T, typename NAME_VALUE_LOOKUP>
|
||||
class TypedParam<T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK>
|
||||
template <typename BLOCK_T, typename NAME_VALUE_LOOKUP>
|
||||
class TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK>
|
||||
: public Param,
|
||||
public NAME_VALUE_LOOKUP::type_value_t
|
||||
{
|
||||
protected:
|
||||
typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
|
||||
typedef typename param_value_t::default_value_t default_value_t;
|
||||
typedef TypedParam<T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> self_t;
|
||||
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
||||
typedef ParamValue<typename LLTypeTags::Sorted<BLOCK_T>::value_t> param_value_t;
|
||||
typedef typename param_value_t::default_value_t default_value_t;
|
||||
typedef TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> self_t;
|
||||
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
||||
public:
|
||||
using named_value_t::operator();
|
||||
typedef typename param_value_t::value_t value_t;
|
||||
typedef typename param_value_t::value_t value_t;
|
||||
|
||||
TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
||||
: Param(block_descriptor.mCurrentBlockPtr),
|
||||
|
|
@ -1148,11 +1204,11 @@ namespace LLInitParam
|
|||
if(named_value_t::valueNamesExist()
|
||||
&& parser.readValue(name)
|
||||
&& named_value_t::getValueFromName(name, typed_param.getValue()))
|
||||
{
|
||||
typed_param.setValueName(name);
|
||||
typed_param.setProvided();
|
||||
return true;
|
||||
}
|
||||
{
|
||||
typed_param.setValueName(name);
|
||||
typed_param.setProvided();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(typed_param.deserializeBlock(parser, name_stack_range, new_name))
|
||||
|
|
@ -1166,10 +1222,16 @@ namespace LLInitParam
|
|||
return false;
|
||||
}
|
||||
|
||||
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
||||
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
||||
{
|
||||
const self_t& typed_param = static_cast<const self_t&>(param);
|
||||
if (!typed_param.isProvided()) return;
|
||||
|
||||
LLPredicate::Value<ESerializePredicates> predicate;
|
||||
|
||||
predicate.set(VALID, typed_param.isValid());
|
||||
predicate.set(PROVIDED, typed_param.anyProvided());
|
||||
|
||||
if (!predicate_rule.check(predicate)) return false;
|
||||
|
||||
if (!name_stack.empty())
|
||||
{
|
||||
|
|
@ -1182,12 +1244,15 @@ namespace LLInitParam
|
|||
if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), key))
|
||||
{
|
||||
parser.writeValue(key, name_stack);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
typed_param.serializeBlock(parser, name_stack, static_cast<const self_t*>(diff_param));
|
||||
return typed_param.serializeBlock(parser, name_stack, predicate_rule, static_cast<const self_t*>(diff_param));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
||||
|
|
@ -1209,13 +1274,12 @@ namespace LLInitParam
|
|||
// *and* the block as a whole validates
|
||||
bool isProvided() const
|
||||
{
|
||||
// only validate block when it hasn't already passed validation with current data
|
||||
if (Param::anyProvided() && !param_value_t::mValidated)
|
||||
{
|
||||
// a sub-block is "provided" when it has been filled in enough to be valid
|
||||
param_value_t::mValidated = param_value_t::validateBlock(false);
|
||||
}
|
||||
return Param::anyProvided() && param_value_t::mValidated;
|
||||
return Param::anyProvided() && isValid();
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return param_value_t::isValid();
|
||||
}
|
||||
|
||||
// assign block contents to this param-that-is-a-block
|
||||
|
|
@ -1223,9 +1287,6 @@ namespace LLInitParam
|
|||
{
|
||||
setValue(val);
|
||||
named_value_t::clearValueName();
|
||||
// force revalidation of block
|
||||
// next call to isProvided() will update provision status based on validity
|
||||
param_value_t::mValidated = false;
|
||||
setProvided(flag_as_provided);
|
||||
}
|
||||
|
||||
|
|
@ -1242,9 +1303,6 @@ namespace LLInitParam
|
|||
|
||||
if (user_provided)
|
||||
{
|
||||
// a child param has been explicitly changed
|
||||
// so *some* aspect of this block is now provided
|
||||
param_value_t::mValidated = false;
|
||||
setProvided();
|
||||
named_value_t::clearValueName();
|
||||
}
|
||||
|
|
@ -1296,13 +1354,13 @@ namespace LLInitParam
|
|||
};
|
||||
|
||||
// list of non-block parameters
|
||||
template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
|
||||
class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, NOT_BLOCK>
|
||||
template <typename MULTI_VALUE_T, typename NAME_VALUE_LOOKUP>
|
||||
class TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK>
|
||||
: public Param
|
||||
{
|
||||
protected:
|
||||
typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, NOT_BLOCK> self_t;
|
||||
typedef ParamValue<typename LLTypeTags::Sorted<VALUE_TYPE>::value_t> param_value_t;
|
||||
typedef TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK> self_t;
|
||||
typedef ParamValue<typename LLTypeTags::Sorted<MULTI_VALUE_T>::value_t> param_value_t;
|
||||
typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t;
|
||||
typedef container_t default_value_t;
|
||||
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
||||
|
|
@ -1311,7 +1369,9 @@ namespace LLInitParam
|
|||
typedef typename param_value_t::value_t value_t;
|
||||
|
||||
TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
||||
: Param(block_descriptor.mCurrentBlockPtr)
|
||||
: Param(block_descriptor.mCurrentBlockPtr),
|
||||
mMinCount(min_count),
|
||||
mMaxCount(max_count)
|
||||
{
|
||||
std::copy(value.begin(), value.end(), std::back_inserter(mValues));
|
||||
|
||||
|
|
@ -1322,7 +1382,13 @@ namespace LLInitParam
|
|||
}
|
||||
}
|
||||
|
||||
bool isProvided() const { return Param::anyProvided(); }
|
||||
bool isProvided() const { return Param::anyProvided() && isValid(); }
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
size_t num_elements = numValidElements();
|
||||
return mMinCount < num_elements && num_elements < mMaxCount;
|
||||
}
|
||||
|
||||
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
||||
{
|
||||
|
|
@ -1337,7 +1403,7 @@ namespace LLInitParam
|
|||
}
|
||||
|
||||
// no further names in stack, attempt to parse value now
|
||||
if (name_stack_range.first == name_stack_range.second)
|
||||
if (new_name_stack_range.first == new_name_stack_range.second)
|
||||
{
|
||||
std::string name;
|
||||
|
||||
|
|
@ -1359,10 +1425,19 @@ namespace LLInitParam
|
|||
return false;
|
||||
}
|
||||
|
||||
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
||||
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
||||
{
|
||||
bool serialized = false;
|
||||
const self_t& typed_param = static_cast<const self_t&>(param);
|
||||
if (!typed_param.isProvided()) return;
|
||||
|
||||
LLPredicate::Value<ESerializePredicates> predicate;
|
||||
|
||||
predicate.set(REQUIRED, typed_param.mMinCount > 0);
|
||||
predicate.set(VALID, typed_param.isValid());
|
||||
predicate.set(PROVIDED, typed_param.anyProvided());
|
||||
predicate.set(EMPTY, typed_param.mValues.empty());
|
||||
|
||||
if (!predicate_rule.check(predicate)) return false;
|
||||
|
||||
for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
|
||||
it != end_it;
|
||||
|
|
@ -1378,7 +1453,11 @@ namespace LLInitParam
|
|||
if (!value_written)
|
||||
{
|
||||
std::string calculated_key = it->calcValueName(it->getValue());
|
||||
if (!parser.writeValue(calculated_key, name_stack))
|
||||
if (parser.writeValue(calculated_key, name_stack))
|
||||
{
|
||||
serialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
@ -1386,19 +1465,23 @@ namespace LLInitParam
|
|||
}
|
||||
else
|
||||
{
|
||||
if(!parser.writeValue(key, name_stack))
|
||||
if(parser.writeValue(key, name_stack))
|
||||
{
|
||||
serialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
name_stack.pop_back();
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
||||
{
|
||||
parser.inspectValue<VALUE_TYPE>(name_stack, min_count, max_count, NULL);
|
||||
parser.inspectValue<MULTI_VALUE_T>(name_stack, min_count, max_count, NULL);
|
||||
if (named_value_t::getPossibleValues())
|
||||
{
|
||||
parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
|
||||
|
|
@ -1453,7 +1536,7 @@ namespace LLInitParam
|
|||
bool empty() const { return mValues.empty(); }
|
||||
size_t size() const { return mValues.size(); }
|
||||
|
||||
U32 numValidElements() const
|
||||
size_t numValidElements() const
|
||||
{
|
||||
return mValues.size();
|
||||
}
|
||||
|
|
@ -1483,6 +1566,8 @@ namespace LLInitParam
|
|||
}
|
||||
|
||||
container_t mValues;
|
||||
size_t mMinCount,
|
||||
mMaxCount;
|
||||
|
||||
private:
|
||||
void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
|
||||
|
|
@ -1500,13 +1585,13 @@ namespace LLInitParam
|
|||
};
|
||||
|
||||
// list of block parameters
|
||||
template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
|
||||
class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, IS_A_BLOCK>
|
||||
template <typename MULTI_BLOCK_T, typename NAME_VALUE_LOOKUP>
|
||||
class TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK>
|
||||
: public Param
|
||||
{
|
||||
protected:
|
||||
typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> self_t;
|
||||
typedef ParamValue<typename LLTypeTags::Sorted<VALUE_TYPE>::value_t> param_value_t;
|
||||
typedef TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> self_t;
|
||||
typedef ParamValue<typename LLTypeTags::Sorted<MULTI_BLOCK_T>::value_t> param_value_t;
|
||||
typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t;
|
||||
typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
|
||||
typedef container_t default_value_t;
|
||||
|
|
@ -1516,7 +1601,9 @@ namespace LLInitParam
|
|||
typedef typename param_value_t::value_t value_t;
|
||||
|
||||
TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
|
||||
: Param(block_descriptor.mCurrentBlockPtr)
|
||||
: Param(block_descriptor.mCurrentBlockPtr),
|
||||
mMinCount(min_count),
|
||||
mMaxCount(max_count)
|
||||
{
|
||||
std::copy(value.begin(), value.end(), back_inserter(mValues));
|
||||
|
||||
|
|
@ -1526,7 +1613,14 @@ namespace LLInitParam
|
|||
}
|
||||
}
|
||||
|
||||
bool isProvided() const { return Param::anyProvided(); }
|
||||
bool isProvided() const { return Param::anyProvided() && isValid(); }
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
size_t num_elements = numValidElements();
|
||||
return mMinCount < num_elements && num_elements < mMaxCount;
|
||||
}
|
||||
|
||||
|
||||
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
||||
{
|
||||
|
|
@ -1549,7 +1643,7 @@ namespace LLInitParam
|
|||
}
|
||||
param_value_t& value = typed_param.mValues.back();
|
||||
|
||||
if (name_stack_range.first == name_stack_range.second)
|
||||
if (new_name_stack_range.first == new_name_stack_range.second)
|
||||
{ // try to parse a known named value
|
||||
std::string name;
|
||||
|
||||
|
|
@ -1559,6 +1653,10 @@ namespace LLInitParam
|
|||
{
|
||||
typed_param.mValues.back().setValueName(name);
|
||||
typed_param.setProvided();
|
||||
if (new_array_value)
|
||||
{
|
||||
name_stack_range.first->second = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1583,10 +1681,18 @@ namespace LLInitParam
|
|||
return false;
|
||||
}
|
||||
|
||||
static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
|
||||
static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
|
||||
{
|
||||
bool serialized = false;
|
||||
const self_t& typed_param = static_cast<const self_t&>(param);
|
||||
if (!typed_param.isProvided()) return;
|
||||
LLPredicate::Value<ESerializePredicates> predicate;
|
||||
|
||||
predicate.set(REQUIRED, typed_param.mMinCount > 0);
|
||||
predicate.set(VALID, typed_param.isValid());
|
||||
predicate.set(PROVIDED, typed_param.anyProvided());
|
||||
predicate.set(EMPTY, typed_param.mValues.empty());
|
||||
|
||||
if (!predicate_rule.check(predicate)) return false;
|
||||
|
||||
for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
|
||||
it != end_it;
|
||||
|
|
@ -1597,17 +1703,24 @@ namespace LLInitParam
|
|||
std::string key = it->getValueName();
|
||||
if (!key.empty())
|
||||
{
|
||||
parser.writeValue(key, name_stack);
|
||||
serialized |= parser.writeValue(key, name_stack);
|
||||
}
|
||||
// Not parsed via named values, write out value directly
|
||||
// NOTE: currently we don't worry about removing default values in Multiple
|
||||
// NOTE: currently we don't do diffing of Multiples
|
||||
else
|
||||
{
|
||||
it->serializeBlock(parser, name_stack, NULL);
|
||||
serialized = it->serializeBlock(parser, name_stack, predicate_rule, NULL);
|
||||
}
|
||||
|
||||
name_stack.pop_back();
|
||||
}
|
||||
|
||||
if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY)))
|
||||
{
|
||||
serialized |= parser.writeValue(Flag(), name_stack);
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
|
||||
|
|
@ -1670,14 +1783,14 @@ namespace LLInitParam
|
|||
bool empty() const { return mValues.empty(); }
|
||||
size_t size() const { return mValues.size(); }
|
||||
|
||||
U32 numValidElements() const
|
||||
size_t numValidElements() const
|
||||
{
|
||||
U32 count = 0;
|
||||
size_t count = 0;
|
||||
for (const_iterator it = mValues.begin(), end_it = mValues.end();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
if(it->validateBlock(false)) count++;
|
||||
if(it->isValid()) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
|
@ -1709,6 +1822,8 @@ namespace LLInitParam
|
|||
}
|
||||
|
||||
container_t mValues;
|
||||
size_t mMinCount,
|
||||
mMaxCount;
|
||||
|
||||
private:
|
||||
void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
|
||||
|
|
@ -2015,7 +2130,7 @@ namespace LLInitParam
|
|||
|
||||
static bool validate(const Param* paramp)
|
||||
{
|
||||
U32 num_valid = ((super_t*)paramp)->numValidElements();
|
||||
size_t num_valid = ((super_t*)paramp)->numValidElements();
|
||||
return RANGE::minCount <= num_valid && num_valid <= RANGE::maxCount;
|
||||
}
|
||||
};
|
||||
|
|
@ -2158,13 +2273,11 @@ namespace LLInitParam
|
|||
typedef T default_value_t;
|
||||
|
||||
ParamValue()
|
||||
: mValue(),
|
||||
mValidated(false)
|
||||
: mValue()
|
||||
{}
|
||||
|
||||
ParamValue(const default_value_t& value)
|
||||
: mValue(value),
|
||||
mValidated(false)
|
||||
: mValue(value)
|
||||
{}
|
||||
|
||||
void setValue(const value_t& val)
|
||||
|
|
@ -2191,18 +2304,18 @@ namespace LLInitParam
|
|||
return mValue.deserializeBlock(p, name_stack_range, new_name);
|
||||
}
|
||||
|
||||
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const
|
||||
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
|
||||
{
|
||||
const BaseBlock* base_block = diff_block
|
||||
? &(diff_block->mValue)
|
||||
: NULL;
|
||||
mValue.serializeBlock(p, name_stack, base_block);
|
||||
return mValue.serializeBlock(p, name_stack, predicate_rule, base_block);
|
||||
}
|
||||
|
||||
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
||||
{
|
||||
{
|
||||
return mValue.inspectBlock(p, name_stack, min_count, max_count);
|
||||
}
|
||||
}
|
||||
|
||||
bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
|
||||
{
|
||||
|
|
@ -2212,9 +2325,7 @@ namespace LLInitParam
|
|||
// clear away what is there and take the new stuff as a whole
|
||||
resetToDefault();
|
||||
return mValue.mergeBlock(block_data, source.getValue(), overwrite);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return mValue.mergeBlock(block_data, source.getValue(), overwrite);
|
||||
}
|
||||
|
||||
|
|
@ -2223,14 +2334,17 @@ namespace LLInitParam
|
|||
return mValue.validateBlock(emit_errors);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return validateBlock(false);
|
||||
}
|
||||
|
||||
static BlockDescriptor& getBlockDescriptor()
|
||||
{
|
||||
return value_t::getBlockDescriptor();
|
||||
}
|
||||
|
||||
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
|
||||
private:
|
||||
void resetToDefault()
|
||||
{
|
||||
|
|
@ -2251,15 +2365,13 @@ namespace LLInitParam
|
|||
typedef T default_value_t;
|
||||
|
||||
ParamValue()
|
||||
: mValue(),
|
||||
mValidated(false)
|
||||
: mValue()
|
||||
{
|
||||
mCurParam = getBlockDescriptor().mAllParams.begin();
|
||||
}
|
||||
|
||||
ParamValue(const default_value_t& value)
|
||||
: mValue(value),
|
||||
mValidated(false)
|
||||
: mValue(value)
|
||||
{
|
||||
mCurParam = getBlockDescriptor().mAllParams.begin();
|
||||
}
|
||||
|
|
@ -2284,7 +2396,7 @@ namespace LLInitParam
|
|||
if (new_name)
|
||||
{
|
||||
mCurParam = getBlockDescriptor().mAllParams.begin();
|
||||
}
|
||||
}
|
||||
if (name_stack_range.first == name_stack_range.second
|
||||
&& mCurParam != getBlockDescriptor().mAllParams.end())
|
||||
{
|
||||
|
|
@ -2296,7 +2408,7 @@ namespace LLInitParam
|
|||
if (deserialize_func
|
||||
&& paramp
|
||||
&& deserialize_func(*paramp, p, name_stack_range, new_name))
|
||||
{
|
||||
{
|
||||
++mCurParam;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2304,19 +2416,19 @@ namespace LLInitParam
|
|||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return mValue.deserializeBlock(p, name_stack_range, new_name);
|
||||
}
|
||||
}
|
||||
|
||||
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const
|
||||
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
|
||||
{
|
||||
const BaseBlock* base_block = diff_block
|
||||
? &(diff_block->mValue)
|
||||
: NULL;
|
||||
mValue.serializeBlock(p, name_stack, base_block);
|
||||
return mValue.serializeBlock(p, name_stack, predicate_rule, base_block);
|
||||
}
|
||||
|
||||
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
||||
|
|
@ -2334,13 +2446,16 @@ namespace LLInitParam
|
|||
return mValue.validateBlock(emit_errors);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return validateBlock(false);
|
||||
}
|
||||
|
||||
static BlockDescriptor& getBlockDescriptor()
|
||||
{
|
||||
return value_t::getBlockDescriptor();
|
||||
}
|
||||
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
|
||||
private:
|
||||
|
||||
BlockDescriptor::all_params_list_t::iterator mCurParam;
|
||||
|
|
@ -2358,16 +2473,14 @@ namespace LLInitParam
|
|||
typedef T default_value_t;
|
||||
|
||||
ParamValue()
|
||||
: T(),
|
||||
mValidated(false)
|
||||
: T()
|
||||
{}
|
||||
|
||||
ParamValue(const default_value_t& value)
|
||||
: T(value.getValue()),
|
||||
mValidated(false)
|
||||
: T(value.getValue())
|
||||
{}
|
||||
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
bool isValid() const { return true; }
|
||||
};
|
||||
|
||||
template<typename T, typename BLOCK_T>
|
||||
|
|
@ -2380,18 +2493,15 @@ namespace LLInitParam
|
|||
typedef LazyValue<T> default_value_t;
|
||||
|
||||
ParamValue()
|
||||
: mValue(),
|
||||
mValidated(false)
|
||||
: mValue()
|
||||
{}
|
||||
|
||||
ParamValue(const default_value_t& other)
|
||||
: mValue(other),
|
||||
mValidated(false)
|
||||
: mValue(other)
|
||||
{}
|
||||
|
||||
ParamValue(const T& value)
|
||||
: mValue(value),
|
||||
mValidated(false)
|
||||
: mValue(value)
|
||||
{}
|
||||
|
||||
void setValue(const value_t& val)
|
||||
|
|
@ -2414,14 +2524,14 @@ namespace LLInitParam
|
|||
return mValue.get().deserializeBlock(p, name_stack_range, new_name);
|
||||
}
|
||||
|
||||
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const
|
||||
bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
|
||||
{
|
||||
if (mValue.empty()) return;
|
||||
if (mValue.empty()) return false;
|
||||
|
||||
const BaseBlock* base_block = (diff_block && !diff_block->mValue.empty())
|
||||
? &(diff_block->mValue.get().getValue())
|
||||
: NULL;
|
||||
mValue.get().serializeBlock(p, name_stack, base_block);
|
||||
return mValue.get().serializeBlock(p, name_stack, predicate_rule, base_block);
|
||||
}
|
||||
|
||||
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
||||
|
|
@ -2433,26 +2543,29 @@ namespace LLInitParam
|
|||
{
|
||||
return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite);
|
||||
}
|
||||
|
||||
|
||||
bool validateBlock(bool emit_errors = true) const
|
||||
{
|
||||
return mValue.empty() || mValue.get().validateBlock(emit_errors);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return validateBlock(false);
|
||||
}
|
||||
|
||||
static BlockDescriptor& getBlockDescriptor()
|
||||
{
|
||||
return value_t::getBlockDescriptor();
|
||||
}
|
||||
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
|
||||
private:
|
||||
LazyValue<T> mValue;
|
||||
};
|
||||
|
||||
template<typename T, typename BLOCK_T>
|
||||
class ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T>
|
||||
{
|
||||
{
|
||||
typedef ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> self_t;
|
||||
|
||||
public:
|
||||
|
|
@ -2460,18 +2573,15 @@ namespace LLInitParam
|
|||
typedef LazyValue<T> default_value_t;
|
||||
|
||||
ParamValue()
|
||||
: mValue(),
|
||||
mValidated(false)
|
||||
: mValue()
|
||||
{}
|
||||
|
||||
ParamValue(const default_value_t& other)
|
||||
: mValue(other),
|
||||
mValidated(false)
|
||||
: mValue(other)
|
||||
{}
|
||||
|
||||
ParamValue(const T& value)
|
||||
: mValue(value),
|
||||
mValidated(false)
|
||||
: mValue(value)
|
||||
{}
|
||||
|
||||
void setValue(const value_t& val)
|
||||
|
|
@ -2489,7 +2599,10 @@ namespace LLInitParam
|
|||
return mValue.get().getValue();
|
||||
}
|
||||
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
bool isValid() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
LazyValue<T> mValue;
|
||||
|
|
@ -2504,12 +2617,10 @@ namespace LLInitParam
|
|||
typedef LLSD default_value_t;
|
||||
|
||||
ParamValue()
|
||||
: mValidated(false)
|
||||
{}
|
||||
|
||||
ParamValue(const default_value_t& other)
|
||||
: mValue(other),
|
||||
mValidated(false)
|
||||
: mValue(other)
|
||||
{}
|
||||
|
||||
void setValue(const value_t& val) { mValue = val; }
|
||||
|
|
@ -2519,16 +2630,13 @@ namespace LLInitParam
|
|||
|
||||
// block param interface
|
||||
LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
|
||||
LL_COMMON_API void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
|
||||
LL_COMMON_API bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const;
|
||||
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
|
||||
{
|
||||
//TODO: implement LLSD params as schema type Any
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
|
||||
private:
|
||||
static void serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack);
|
||||
|
||||
|
|
@ -2547,18 +2655,17 @@ namespace LLInitParam
|
|||
BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative
|
||||
} EValueAge;
|
||||
|
||||
typedef ParamValue<T> derived_t;
|
||||
typedef CustomParamValue<T> self_t;
|
||||
typedef Block<derived_t> block_t;
|
||||
typedef TypeValues<T> derived_t;
|
||||
typedef CustomParamValue<T> self_t;
|
||||
typedef Block<ParamValue<T> > block_t;
|
||||
typedef T default_value_t;
|
||||
typedef T value_t;
|
||||
typedef T value_t;
|
||||
typedef void baseblock_base_class_t;
|
||||
|
||||
|
||||
CustomParamValue(const default_value_t& value = T())
|
||||
: mValue(value),
|
||||
mValueAge(VALUE_AUTHORITATIVE),
|
||||
mValidated(false)
|
||||
mValueAge(VALUE_AUTHORITATIVE)
|
||||
{}
|
||||
|
||||
bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
|
||||
|
|
@ -2580,16 +2687,30 @@ namespace LLInitParam
|
|||
return typed_param.BaseBlock::deserializeBlock(parser, name_stack_range, new_name);
|
||||
}
|
||||
|
||||
void serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const
|
||||
bool serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const
|
||||
{
|
||||
const derived_t& typed_param = static_cast<const derived_t&>(*this);
|
||||
const derived_t* diff_param = static_cast<const derived_t*>(diff_block);
|
||||
|
||||
//std::string key = typed_param.getValueName();
|
||||
|
||||
//// first try to write out name of name/value pair
|
||||
//if (!key.empty())
|
||||
//{
|
||||
// if (!diff_param || !ParamCompare<std::string>::equals(diff_param->getValueName(), key))
|
||||
// {
|
||||
// return parser.writeValue(key, name_stack);
|
||||
// }
|
||||
//}
|
||||
// then try to serialize value directly
|
||||
if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), diff_param->getValue()))
|
||||
{
|
||||
|
||||
if (!parser.writeValue(typed_param.getValue(), name_stack))
|
||||
if (parser.writeValue(typed_param.getValue(), name_stack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//RN: *always* serialize provided components of BlockValue (don't pass diff_param on),
|
||||
// since these tend to be viewed as the constructor arguments for the value T. It seems
|
||||
|
|
@ -2606,14 +2727,15 @@ namespace LLInitParam
|
|||
// and serialize those params
|
||||
derived_t copy(typed_param);
|
||||
copy.updateBlockFromValue(true);
|
||||
copy.block_t::serializeBlock(parser, name_stack, NULL);
|
||||
return copy.block_t::serializeBlock(parser, name_stack, predicate_rule, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
block_t::serializeBlock(parser, name_stack, NULL);
|
||||
return block_t::serializeBlock(parser, name_stack, predicate_rule, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool validateBlock(bool emit_errors = true) const
|
||||
|
|
@ -2705,8 +2827,6 @@ namespace LLInitParam
|
|||
return block_t::mergeBlock(block_data, source, overwrite);
|
||||
}
|
||||
|
||||
mutable bool mValidated; // lazy validation flag
|
||||
|
||||
private:
|
||||
mutable T mValue;
|
||||
mutable EValueAge mValueAge;
|
||||
|
|
|
|||
|
|
@ -27,23 +27,25 @@
|
|||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llinstancetracker.h"
|
||||
#include "llapr.h"
|
||||
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
|
||||
//static
|
||||
void * & LLInstanceTrackerBase::getInstances(std::type_info const & info)
|
||||
void LLInstanceTrackerBase::StaticBase::incrementDepth()
|
||||
{
|
||||
typedef std::map<std::string, void *> InstancesMap;
|
||||
static InstancesMap instances;
|
||||
|
||||
// std::map::insert() is just what we want here. You attempt to insert a
|
||||
// (key, value) pair. If the specified key doesn't yet exist, it inserts
|
||||
// the pair and returns a std::pair of (iterator, true). If the specified
|
||||
// key DOES exist, insert() simply returns (iterator, false). One lookup
|
||||
// handles both cases.
|
||||
return instances.insert(InstancesMap::value_type(info.name(),
|
||||
InstancesMap::mapped_type()))
|
||||
.first->second;
|
||||
apr_atomic_inc32(&sIterationNestDepth);
|
||||
}
|
||||
|
||||
void LLInstanceTrackerBase::StaticBase::decrementDepth()
|
||||
{
|
||||
apr_atomic_dec32(&sIterationNestDepth);
|
||||
}
|
||||
|
||||
U32 LLInstanceTrackerBase::StaticBase::getDepth()
|
||||
{
|
||||
apr_uint32_t data = apr_atomic_read32(&sIterationNestDepth);
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,22 +46,6 @@
|
|||
class LL_COMMON_API LLInstanceTrackerBase
|
||||
{
|
||||
protected:
|
||||
/// Get a process-unique void* pointer slot for the specified type_info
|
||||
static void * & getInstances(std::type_info const & info);
|
||||
|
||||
/// Find or create a STATICDATA instance for the specified TRACKED class.
|
||||
/// STATICDATA must be default-constructible.
|
||||
template<typename STATICDATA, class TRACKED>
|
||||
static STATICDATA& getStatic()
|
||||
{
|
||||
void *& instances = getInstances(typeid(TRACKED));
|
||||
if (! instances)
|
||||
{
|
||||
instances = new STATICDATA;
|
||||
}
|
||||
return *static_cast<STATICDATA*>(instances);
|
||||
}
|
||||
|
||||
/// It's not essential to derive your STATICDATA (for use with
|
||||
/// getStatic()) from StaticBase; it's just that both known
|
||||
/// implementations do.
|
||||
|
|
@ -70,15 +54,21 @@ protected:
|
|||
StaticBase():
|
||||
sIterationNestDepth(0)
|
||||
{}
|
||||
S32 sIterationNestDepth;
|
||||
|
||||
void incrementDepth();
|
||||
void decrementDepth();
|
||||
U32 getDepth();
|
||||
private:
|
||||
U32 sIterationNestDepth;
|
||||
};
|
||||
};
|
||||
|
||||
/// This mix-in class adds support for tracking all instances of the specified class parameter T
|
||||
/// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup
|
||||
/// If KEY is not provided, then instances are stored in a simple set
|
||||
/// @NOTE: see explicit specialization below for default KEY==T* case
|
||||
template<typename T, typename KEY = T*>
|
||||
/// @NOTE: see explicit specialization below for default KEY==void case
|
||||
/// @NOTE: this class is not thread-safe unless used as read-only
|
||||
template<typename T, typename KEY = void>
|
||||
class LLInstanceTracker : public LLInstanceTrackerBase
|
||||
{
|
||||
typedef LLInstanceTracker<T, KEY> MyT;
|
||||
|
|
@ -87,7 +77,7 @@ class LLInstanceTracker : public LLInstanceTrackerBase
|
|||
{
|
||||
InstanceMap sMap;
|
||||
};
|
||||
static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic<StaticData, MyT>(); }
|
||||
static StaticData& getStatic() { static StaticData sData; return sData;}
|
||||
static InstanceMap& getMap_() { return getStatic().sMap; }
|
||||
|
||||
public:
|
||||
|
|
@ -99,12 +89,12 @@ public:
|
|||
instance_iter(const typename InstanceMap::iterator& it)
|
||||
: mIterator(it)
|
||||
{
|
||||
++getStatic().sIterationNestDepth;
|
||||
getStatic().incrementDepth();
|
||||
}
|
||||
|
||||
~instance_iter()
|
||||
{
|
||||
--getStatic().sIterationNestDepth;
|
||||
getStatic().decrementDepth();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -131,20 +121,20 @@ public:
|
|||
typedef boost::iterator_facade<key_iter, KEY, boost::forward_traversal_tag> super_t;
|
||||
|
||||
key_iter(typename InstanceMap::iterator it)
|
||||
: mIterator(it)
|
||||
: mIterator(it)
|
||||
{
|
||||
++getStatic().sIterationNestDepth;
|
||||
getStatic().incrementDepth();
|
||||
}
|
||||
|
||||
key_iter(const key_iter& other)
|
||||
: mIterator(other.mIterator)
|
||||
: mIterator(other.mIterator)
|
||||
{
|
||||
++getStatic().sIterationNestDepth;
|
||||
getStatic().incrementDepth();
|
||||
}
|
||||
|
||||
~key_iter()
|
||||
{
|
||||
--getStatic().sIterationNestDepth;
|
||||
getStatic().decrementDepth();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -182,7 +172,10 @@ public:
|
|||
return instance_iter(getMap_().end());
|
||||
}
|
||||
|
||||
static S32 instanceCount() { return getMap_().size(); }
|
||||
static S32 instanceCount()
|
||||
{
|
||||
return getMap_().size();
|
||||
}
|
||||
|
||||
static key_iter beginKeys()
|
||||
{
|
||||
|
|
@ -203,7 +196,7 @@ protected:
|
|||
virtual ~LLInstanceTracker()
|
||||
{
|
||||
// it's unsafe to delete instances of this type while all instances are being iterated over.
|
||||
llassert_always(getStatic().sIterationNestDepth == 0);
|
||||
llassert_always(getStatic().getDepth() == 0);
|
||||
remove_();
|
||||
}
|
||||
virtual void setKey(KEY key) { remove_(); add_(key); }
|
||||
|
|
@ -227,18 +220,18 @@ private:
|
|||
KEY mInstanceKey;
|
||||
};
|
||||
|
||||
/// explicit specialization for default case where KEY is T*
|
||||
/// explicit specialization for default case where KEY is void
|
||||
/// use a simple std::set<T*>
|
||||
template<typename T>
|
||||
class LLInstanceTracker<T, T*> : public LLInstanceTrackerBase
|
||||
class LLInstanceTracker<T, void> : public LLInstanceTrackerBase
|
||||
{
|
||||
typedef LLInstanceTracker<T, T*> MyT;
|
||||
typedef LLInstanceTracker<T, void> MyT;
|
||||
typedef typename std::set<T*> InstanceSet;
|
||||
struct StaticData: public StaticBase
|
||||
{
|
||||
InstanceSet sSet;
|
||||
};
|
||||
static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic<StaticData, MyT>(); }
|
||||
static StaticData& getStatic() { static StaticData sData; return sData; }
|
||||
static InstanceSet& getSet_() { return getStatic().sSet; }
|
||||
|
||||
public:
|
||||
|
|
@ -265,18 +258,18 @@ public:
|
|||
instance_iter(const typename InstanceSet::iterator& it)
|
||||
: mIterator(it)
|
||||
{
|
||||
++getStatic().sIterationNestDepth;
|
||||
getStatic().incrementDepth();
|
||||
}
|
||||
|
||||
instance_iter(const instance_iter& other)
|
||||
: mIterator(other.mIterator)
|
||||
{
|
||||
++getStatic().sIterationNestDepth;
|
||||
getStatic().incrementDepth();
|
||||
}
|
||||
|
||||
~instance_iter()
|
||||
{
|
||||
--getStatic().sIterationNestDepth;
|
||||
getStatic().decrementDepth();
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -309,7 +302,7 @@ protected:
|
|||
virtual ~LLInstanceTracker()
|
||||
{
|
||||
// it's unsafe to delete instances of this type while all instances are being iterated over.
|
||||
llassert_always(getStatic().sIterationNestDepth == 0);
|
||||
llassert_always(getStatic().getDepth() == 0);
|
||||
getSet_().erase(static_cast<T*>(this));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* @file lllazy.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2009-01-28
|
||||
* @brief Implementation for lllazy.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "lllazy.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
|
||||
// lllazy.h is presently header-only. This file exists only because our CMake
|
||||
// test macro ADD_BUILD_TEST requires it.
|
||||
int dummy = 0;
|
||||
|
|
@ -1,399 +0,0 @@
|
|||
/**
|
||||
* @file lllazy.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2009-01-22
|
||||
* @brief Lazy instantiation of specified type. Useful in conjunction with
|
||||
* Michael Feathers's "Extract and Override Getter" ("Working
|
||||
* Effectively with Legacy Code", p. 352).
|
||||
*
|
||||
* Quoting his synopsis of steps on p.355:
|
||||
*
|
||||
* 1. Identify the object you need a getter for.
|
||||
* 2. Extract all of the logic needed to create the object into a getter.
|
||||
* 3. Replace all uses of the object with calls to the getter, and initialize
|
||||
* the reference that holds the object to null in all constructors.
|
||||
* 4. Add the first-time logic to the getter so that the object is constructed
|
||||
* and assigned to the reference whenever the reference is null.
|
||||
* 5. Subclass the class and override the getter to provide an alternative
|
||||
* object for testing.
|
||||
*
|
||||
* It's the second half of bullet 3 (3b, as it were) that bothers me. I find
|
||||
* it all too easy to imagine adding pointer initializers to all but one
|
||||
* constructor... the one not exercised by my tests. That suggested using
|
||||
* (e.g.) boost::scoped_ptr<MyObject> so you don't have to worry about
|
||||
* destroying it either.
|
||||
*
|
||||
* However, introducing additional machinery allows us to encapsulate bullet 4
|
||||
* as well.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLLAZY_H)
|
||||
#define LL_LLLAZY_H
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/lambda/construct.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
/// LLLazyCommon simply factors out of LLLazy<T> things that don't depend on
|
||||
/// its template parameter.
|
||||
class LLLazyCommon
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This exception is thrown if you try to replace an LLLazy<T>'s factory
|
||||
* (or T* instance) after it already has an instance in hand. Since T
|
||||
* might well be stateful, we can't know the effect of silently discarding
|
||||
* and replacing an existing instance, so we disallow it. This facility is
|
||||
* intended for testing, and in a test scenario we can definitely control
|
||||
* that.
|
||||
*/
|
||||
struct InstanceChange: public std::runtime_error
|
||||
{
|
||||
InstanceChange(const std::string& what): std::runtime_error(what) {}
|
||||
};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* InstanceChange might be appropriate in a couple of different LLLazy<T>
|
||||
* methods. Factor out the common logic.
|
||||
*/
|
||||
template <typename PTR>
|
||||
static void ensureNoInstance(const PTR& ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
// Too late: we've already instantiated the lazy object. We don't
|
||||
// know whether it's stateful or not, so it's not safe to discard
|
||||
// the existing instance in favor of a replacement.
|
||||
throw InstanceChange("Too late to replace LLLazy instance");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* LLLazy<T> is useful when you have an outer class Outer that you're trying
|
||||
* to bring under unit test, that contains a data member difficult to
|
||||
* instantiate in a test harness. Typically the data member's class Inner has
|
||||
* many thorny dependencies. Feathers generally advocates "Extract and
|
||||
* Override Factory Method" (p. 350). But in C++, you can't call a derived
|
||||
* class override of a virtual method from the derived class constructor,
|
||||
* which limits applicability of "Extract and Override Factory Method." For
|
||||
* such cases Feathers presents "Extract and Override Getter" (p. 352).
|
||||
*
|
||||
* So we'll assume that your class Outer contains a member like this:
|
||||
* @code
|
||||
* Inner mInner;
|
||||
* @endcode
|
||||
*
|
||||
* LLLazy<Inner> can be used to replace this member. You can directly declare:
|
||||
* @code
|
||||
* LLLazy<Inner> mInner;
|
||||
* @endcode
|
||||
* and change references to mInner accordingly.
|
||||
*
|
||||
* (Alternatively, you can add a base class of the form
|
||||
* <tt>LLLazyBase<Inner></tt>. This is discussed further in the LLLazyBase<T>
|
||||
* documentation.)
|
||||
*
|
||||
* LLLazy<T> binds a <tt>boost::scoped_ptr<T></tt> and a factory functor
|
||||
* returning T*. You can either bind that functor explicitly or let it default
|
||||
* to the expression <tt>new T()</tt>.
|
||||
*
|
||||
* As long as LLLazy<T> remains unreferenced, its T remains uninstantiated.
|
||||
* The first time you use get(), <tt>operator*()</tt> or <tt>operator->()</tt>
|
||||
* it will instantiate its T and thereafter behave like a pointer to it.
|
||||
*
|
||||
* Thus, any existing reference to <tt>mInner.member</tt> should be replaced
|
||||
* with <tt>mInner->member</tt>. Any simple reference to @c mInner should be
|
||||
* replaced by <tt>*mInner</tt>.
|
||||
*
|
||||
* (If the original declaration was a pointer initialized in Outer's
|
||||
* constructor, e.g. <tt>Inner* mInner</tt>, so much the better. In that case
|
||||
* you should be able to drop in <tt>LLLazy<Inner></tt> without much change.)
|
||||
*
|
||||
* The support for "Extract and Override Getter" lies in the fact that you can
|
||||
* replace the factory functor -- or provide an explicit T*. Presumably this
|
||||
* is most useful from a test subclass -- which suggests that your @c mInner
|
||||
* member should be @c protected.
|
||||
*
|
||||
* Note that <tt>boost::lambda::new_ptr<T>()</tt> makes a dandy factory
|
||||
* functor, for either the set() method or LLLazy<T>'s constructor. If your T
|
||||
* requires constructor arguments, use an expression more like
|
||||
* <tt>boost::lambda::bind(boost::lambda::new_ptr<T>(), arg1, arg2, ...)</tt>.
|
||||
*
|
||||
* Of course the point of replacing the functor is to substitute a class that,
|
||||
* though referenced as Inner*, is not an Inner; presumably this is a testing
|
||||
* subclass of Inner (e.g. TestInner). Thus your test subclass TestOuter for
|
||||
* the containing class Outer will contain something like this:
|
||||
* @code
|
||||
* class TestOuter: public Outer
|
||||
* {
|
||||
* public:
|
||||
* TestOuter()
|
||||
* {
|
||||
* // mInner must be 'protected' rather than 'private'
|
||||
* mInner.set(boost::lambda::new_ptr<TestInner>());
|
||||
* }
|
||||
* ...
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
template <typename T>
|
||||
class LLLazy: public LLLazyCommon
|
||||
{
|
||||
public:
|
||||
/// Any nullary functor returning T* will work as a Factory
|
||||
typedef boost::function<T* ()> Factory;
|
||||
|
||||
/// The default LLLazy constructor uses <tt>new T()</tt> as its Factory
|
||||
LLLazy():
|
||||
mFactory(boost::lambda::new_ptr<T>())
|
||||
{}
|
||||
|
||||
/// Bind an explicit Factory functor
|
||||
LLLazy(const Factory& factory):
|
||||
mFactory(factory)
|
||||
{}
|
||||
|
||||
/// Reference T, instantiating it if this is the first access
|
||||
const T& get() const
|
||||
{
|
||||
if (! mInstance)
|
||||
{
|
||||
// use the bound Factory functor
|
||||
mInstance.reset(mFactory());
|
||||
}
|
||||
return *mInstance;
|
||||
}
|
||||
|
||||
/// non-const get()
|
||||
T& get()
|
||||
{
|
||||
return const_cast<T&>(const_cast<const LLLazy<T>*>(this)->get());
|
||||
}
|
||||
|
||||
/// operator*() is equivalent to get()
|
||||
const T& operator*() const { return get(); }
|
||||
/// operator*() is equivalent to get()
|
||||
T& operator*() { return get(); }
|
||||
|
||||
/**
|
||||
* operator->() must return (something resembling) T*. It's tempting to
|
||||
* return the underlying boost::scoped_ptr<T>, but that would require
|
||||
* breaking out the lazy-instantiation logic from get() into a common
|
||||
* private method. Assume the pointer used for operator->() access is very
|
||||
* short-lived.
|
||||
*/
|
||||
const T* operator->() const { return &get(); }
|
||||
/// non-const operator->()
|
||||
T* operator->() { return &get(); }
|
||||
|
||||
/// set(Factory). This will throw InstanceChange if mInstance has already
|
||||
/// been set.
|
||||
void set(const Factory& factory)
|
||||
{
|
||||
ensureNoInstance(mInstance);
|
||||
mFactory = factory;
|
||||
}
|
||||
|
||||
/// set(T*). This will throw InstanceChange if mInstance has already been
|
||||
/// set.
|
||||
void set(T* instance)
|
||||
{
|
||||
ensureNoInstance(mInstance);
|
||||
mInstance.reset(instance);
|
||||
}
|
||||
|
||||
private:
|
||||
Factory mFactory;
|
||||
// Consider an LLLazy<T> member of a class we're accessing by const
|
||||
// reference. We want to allow even const methods to touch the LLLazy<T>
|
||||
// member. Hence the actual pointer must be mutable because such access
|
||||
// might assign it.
|
||||
mutable boost::scoped_ptr<T> mInstance;
|
||||
};
|
||||
|
||||
#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
|
||||
// Not gcc at all, or a gcc more recent than gcc 3.3
|
||||
#define GCC33 0
|
||||
#else
|
||||
#define GCC33 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* LLLazyBase<T> wraps LLLazy<T>, giving you an alternative way to replace
|
||||
* <tt>Inner mInner;</tt>. Instead of coding <tt>LLLazy<Inner> mInner</tt>,
|
||||
* you can add LLLazyBase<Inner> to your Outer class's bases, e.g.:
|
||||
* @code
|
||||
* class Outer: public LLLazyBase<Inner>
|
||||
* {
|
||||
* ...
|
||||
* };
|
||||
* @endcode
|
||||
*
|
||||
* This gives you @c public get() and @c protected set() methods without
|
||||
* having to make your LLLazy<Inner> member @c protected. The tradeoff is that
|
||||
* you must access the wrapped LLLazy<Inner> using get() and set() rather than
|
||||
* with <tt>operator*()</tt> or <tt>operator->()</tt>.
|
||||
*
|
||||
* This mechanism can be used for more than one member, but only if they're of
|
||||
* different types. That is, you can replace:
|
||||
* @code
|
||||
* DifficultClass mDifficult;
|
||||
* AwkwardType mAwkward;
|
||||
* @endcode
|
||||
* with:
|
||||
* @code
|
||||
* class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
|
||||
* {
|
||||
* ...
|
||||
* };
|
||||
* @endcode
|
||||
* but for a situation like this:
|
||||
* @code
|
||||
* DifficultClass mMainDifficult, mAuxDifficult;
|
||||
* @endcode
|
||||
* you should directly embed LLLazy<DifficultClass> (q.v.).
|
||||
*
|
||||
* For multiple LLLazyBase bases, e.g. the <tt>LLLazyBase<DifficultClass>,
|
||||
* LLLazyBase<AwkwardType></tt> example above, access the relevant get()/set()
|
||||
* as (e.g.) <tt>LLLazyBase<DifficultClass>::get()</tt>. (This is why you
|
||||
* can't have multiple LLLazyBase<T> of the same T.) For a bit of syntactic
|
||||
* sugar, please see getLazy()/setLazy().
|
||||
*/
|
||||
template <typename T>
|
||||
class LLLazyBase
|
||||
{
|
||||
public:
|
||||
/// invoke default LLLazy constructor
|
||||
LLLazyBase() {}
|
||||
/// make wrapped LLLazy bind an explicit Factory
|
||||
LLLazyBase(const typename LLLazy<T>::Factory& factory):
|
||||
mInstance(factory)
|
||||
{}
|
||||
|
||||
/// access to LLLazy::get()
|
||||
T& get() { return *mInstance; }
|
||||
/// access to LLLazy::get()
|
||||
const T& get() const { return *mInstance; }
|
||||
|
||||
protected:
|
||||
// see getLazy()/setLazy()
|
||||
#if (! GCC33)
|
||||
template <typename T2, class MYCLASS> friend T2& getLazy(MYCLASS* this_);
|
||||
template <typename T2, class MYCLASS> friend const T2& getLazy(const MYCLASS* this_);
|
||||
#else // gcc 3.3
|
||||
template <typename T2, class MYCLASS> friend T2& getLazy(const MYCLASS* this_);
|
||||
#endif // gcc 3.3
|
||||
template <typename T2, class MYCLASS> friend void setLazy(MYCLASS* this_, T2* instance);
|
||||
template <typename T2, class MYCLASS>
|
||||
friend void setLazy(MYCLASS* this_, const typename LLLazy<T2>::Factory& factory);
|
||||
|
||||
/// access to LLLazy::set(Factory)
|
||||
void set(const typename LLLazy<T>::Factory& factory)
|
||||
{
|
||||
mInstance.set(factory);
|
||||
}
|
||||
|
||||
/// access to LLLazy::set(T*)
|
||||
void set(T* instance)
|
||||
{
|
||||
mInstance.set(instance);
|
||||
}
|
||||
|
||||
private:
|
||||
LLLazy<T> mInstance;
|
||||
};
|
||||
|
||||
/**
|
||||
* @name getLazy()/setLazy()
|
||||
* Suppose you have something like the following:
|
||||
* @code
|
||||
* class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
|
||||
* {
|
||||
* ...
|
||||
* };
|
||||
* @endcode
|
||||
*
|
||||
* Your methods can reference the @c DifficultClass instance using
|
||||
* <tt>LLLazyBase<DifficultClass>::get()</tt>, which is admittedly a bit ugly.
|
||||
* Alternatively, you can write <tt>getLazy<DifficultClass>(this)</tt>, which
|
||||
* is somewhat more straightforward to read.
|
||||
*
|
||||
* Similarly,
|
||||
* @code
|
||||
* LLLazyBase<DifficultClass>::set(new TestDifficultClass());
|
||||
* @endcode
|
||||
* could instead be written:
|
||||
* @code
|
||||
* setLazy<DifficultClass>(this, new TestDifficultClass());
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* I wanted to provide getLazy() and setLazy() without explicitly passing @c
|
||||
* this. That would imply making them methods on a base class rather than free
|
||||
* functions. But if <tt>LLLazyBase<T></tt> derives normally from (say) @c
|
||||
* LLLazyGrandBase providing those methods, then unqualified getLazy() would
|
||||
* be ambiguous: you'd have to write <tt>LLLazyBase<T>::getLazy<T>()</tt>,
|
||||
* which is even uglier than <tt>LLLazyBase<T>::get()</tt>, and therefore
|
||||
* pointless. You can make the compiler not care which @c LLLazyGrandBase
|
||||
* instance you're talking about by making @c LLLazyGrandBase a @c virtual
|
||||
* base class of @c LLLazyBase. But in that case,
|
||||
* <tt>LLLazyGrandBase::getLazy<T>()</tt> can't access
|
||||
* <tt>LLLazyBase<T>::get()</tt>!
|
||||
*
|
||||
* We want <tt>getLazy<T>()</tt> to access <tt>LLLazyBase<T>::get()</tt> as if
|
||||
* in the lexical context of some subclass method. Ironically, free functions
|
||||
* let us do that better than methods on a @c virtual base class -- but that
|
||||
* implies passing @c this explicitly. So be it.
|
||||
*/
|
||||
//@{
|
||||
#if (! GCC33)
|
||||
template <typename T, class MYCLASS>
|
||||
T& getLazy(MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
|
||||
template <typename T, class MYCLASS>
|
||||
const T& getLazy(const MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
|
||||
#else // gcc 3.3
|
||||
// For const-correctness, we really should have two getLazy() variants: one
|
||||
// accepting const MYCLASS* and returning const T&, the other accepting
|
||||
// non-const MYCLASS* and returning non-const T&. This works fine on the Mac
|
||||
// (gcc 4.0.1) and Windows (MSVC 8.0), but fails on our Linux 32-bit Debian
|
||||
// Sarge stations (gcc 3.3.5). Since I really don't know how to beat that aging
|
||||
// compiler over the head to make it do the right thing, I'm going to have to
|
||||
// move forward with the wrong thing: a single getLazy() function that accepts
|
||||
// const MYCLASS* and returns non-const T&.
|
||||
template <typename T, class MYCLASS>
|
||||
T& getLazy(const MYCLASS* this_) { return const_cast<MYCLASS*>(this_)->LLLazyBase<T>::get(); }
|
||||
#endif // gcc 3.3
|
||||
template <typename T, class MYCLASS>
|
||||
void setLazy(MYCLASS* this_, T* instance) { this_->LLLazyBase<T>::set(instance); }
|
||||
template <typename T, class MYCLASS>
|
||||
void setLazy(MYCLASS* this_, const typename LLLazy<T>::Factory& factory)
|
||||
{
|
||||
this_->LLLazyBase<T>::set(factory);
|
||||
}
|
||||
//@}
|
||||
|
||||
#endif /* ! defined(LL_LLLAZY_H) */
|
||||
|
|
@ -394,7 +394,7 @@ public:
|
|||
LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
|
||||
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
|
||||
LLSD nop;
|
||||
F64 until(LLTimer::getElapsedSeconds() + 2);
|
||||
F64 until = (LLTimer::getElapsedSeconds() + 2).value();
|
||||
while (childin.size() && LLTimer::getElapsedSeconds() < until)
|
||||
{
|
||||
mainloop.post(nop);
|
||||
|
|
|
|||
|
|
@ -1,895 +0,0 @@
|
|||
/**
|
||||
* @file lllocalidhashmap.h
|
||||
* @brief Map specialized for dealing with local ids
|
||||
*
|
||||
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLLOCALIDHASHMAP_H
|
||||
#define LL_LLLOCALIDHASHMAP_H
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include "llerror.h"
|
||||
|
||||
const S32 MAX_ITERS = 4;
|
||||
// LocalID hash map
|
||||
|
||||
//
|
||||
// LLLocalIDHashNode
|
||||
//
|
||||
|
||||
template <class DATA, int SIZE>
|
||||
class LLLocalIDHashNode
|
||||
{
|
||||
public:
|
||||
LLLocalIDHashNode();
|
||||
|
||||
public:
|
||||
S32 mCount;
|
||||
U32 mKey[SIZE];
|
||||
DATA mData[SIZE];
|
||||
LLLocalIDHashNode<DATA, SIZE> *mNextNodep;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// LLLocalIDHashNode implementation
|
||||
//
|
||||
template <class DATA, int SIZE>
|
||||
LLLocalIDHashNode<DATA, SIZE>::LLLocalIDHashNode()
|
||||
{
|
||||
mCount = 0;
|
||||
mNextNodep = NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// LLLocalIDHashMapIter
|
||||
//
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
class LLLocalIDHashMap;
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
class LLLocalIDHashMapIter
|
||||
{
|
||||
public:
|
||||
LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
|
||||
~LLLocalIDHashMapIter();
|
||||
|
||||
void setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
|
||||
inline void first();
|
||||
inline void next();
|
||||
inline DATA_TYPE& current(); // *NOTE: Deprecate? Phoenix 2005-04-15
|
||||
inline BOOL done() const;
|
||||
inline S32 currentBin() const;
|
||||
inline void setBin(S32 bin);
|
||||
|
||||
DATA_TYPE& operator*() const
|
||||
{
|
||||
return mCurHashNodep->mData[mCurHashNodeKey];
|
||||
}
|
||||
DATA_TYPE* operator->() const
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
|
||||
LLLocalIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
|
||||
|
||||
S32 mCurHashMapNodeNum;
|
||||
S32 mCurHashNodeKey;
|
||||
|
||||
DATA_TYPE mNull;
|
||||
|
||||
S32 mIterID;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
class LLLocalIDHashMap
|
||||
{
|
||||
public:
|
||||
friend class LLLocalIDHashMapIter<DATA_TYPE, SIZE>;
|
||||
|
||||
LLLocalIDHashMap(); // DO NOT use this unless you explicitly setNull, or the data type constructs a "null"
|
||||
// object by default
|
||||
// basic constructor including sorter
|
||||
LLLocalIDHashMap(const DATA_TYPE &null_data);
|
||||
// Hack, this should really be a const ref, but I'm not doing it that way because the sim
|
||||
// usually uses pointers.
|
||||
~LLLocalIDHashMap();
|
||||
|
||||
inline DATA_TYPE &get(const U32 local_id);
|
||||
inline BOOL check(const U32 local_id) const;
|
||||
inline DATA_TYPE &set(const U32 local_id, const DATA_TYPE data);
|
||||
inline BOOL remove(const U32 local_id);
|
||||
void removeAll();
|
||||
|
||||
void setNull(const DATA_TYPE data) { mNull = data; }
|
||||
|
||||
inline S32 getLength() const; // Warning, NOT O(1!)
|
||||
|
||||
void dumpIter();
|
||||
void dumpBin(U32 bin);
|
||||
|
||||
protected:
|
||||
// Only used by the iterator.
|
||||
void addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter);
|
||||
void removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter);
|
||||
|
||||
// Remove the item and shift all items afterward down the list,
|
||||
// fixing up iterators as we go.
|
||||
BOOL removeWithShift(const U32 local_id);
|
||||
|
||||
protected:
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE> mNodes[256];
|
||||
|
||||
S32 mIterCount;
|
||||
LLLocalIDHashMapIter<DATA_TYPE, SIZE> *mIters[MAX_ITERS];
|
||||
|
||||
DATA_TYPE mNull;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// LLLocalIDHashMap implementation
|
||||
//
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap()
|
||||
: mIterCount(0),
|
||||
mNull()
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < MAX_ITERS; i++)
|
||||
{
|
||||
mIters[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap(const DATA_TYPE &null_data)
|
||||
: mIterCount(0),
|
||||
mNull(null_data)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < MAX_ITERS; i++)
|
||||
{
|
||||
mIters[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLLocalIDHashMap<DATA_TYPE, SIZE>::~LLLocalIDHashMap()
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < MAX_ITERS; i++)
|
||||
{
|
||||
if (mIters[i])
|
||||
{
|
||||
mIters[i]->mHashMapp = NULL;
|
||||
mIterCount--;
|
||||
}
|
||||
}
|
||||
removeAll();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeAll()
|
||||
{
|
||||
S32 bin;
|
||||
for (bin = 0; bin < 256; bin++)
|
||||
{
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
|
||||
|
||||
BOOL first = TRUE;
|
||||
do // First node guaranteed to be there
|
||||
{
|
||||
S32 i;
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
// Iterate through all members of this node
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
nodep->mData[i] = mNull;
|
||||
}
|
||||
|
||||
nodep->mCount = 0;
|
||||
// Done with all objects in this node, go to the next.
|
||||
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
|
||||
nodep = nodep->mNextNodep;
|
||||
|
||||
// Delete the node if it's not the first node
|
||||
if (first)
|
||||
{
|
||||
first = FALSE;
|
||||
curp->mNextNodep = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete curp;
|
||||
}
|
||||
} while (nodep);
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpIter()
|
||||
{
|
||||
std::cout << "Hash map with " << mIterCount << " iterators" << std::endl;
|
||||
|
||||
std::cout << "Hash Map Iterators:" << std::endl;
|
||||
S32 i;
|
||||
for (i = 0; i < MAX_ITERS; i++)
|
||||
{
|
||||
if (mIters[i])
|
||||
{
|
||||
llinfos << i << " " << mIters[i]->mCurHashNodep << " " << mIters[i]->mCurHashNodeKey << llendl;
|
||||
}
|
||||
else
|
||||
{
|
||||
llinfos << i << "null" << llendl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpBin(U32 bin)
|
||||
{
|
||||
std::cout << "Dump bin " << bin << std::endl;
|
||||
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
|
||||
S32 node = 0;
|
||||
do // First node guaranteed to be there.
|
||||
{
|
||||
std::cout << "Bin " << bin
|
||||
<< " node " << node
|
||||
<< " count " << nodep->mCount
|
||||
<< " contains " << std::flush;
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < nodep->mCount; i++)
|
||||
{
|
||||
std::cout << nodep->mData[i] << " " << std::flush;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
nodep = nodep->mNextNodep;
|
||||
node++;
|
||||
} while (nodep);
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline S32 LLLocalIDHashMap<DATA_TYPE, SIZE>::getLength() const
|
||||
{
|
||||
S32 count = 0;
|
||||
S32 bin;
|
||||
for (bin = 0; bin < 256; bin++)
|
||||
{
|
||||
const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
|
||||
while (nodep)
|
||||
{
|
||||
count += nodep->mCount;
|
||||
nodep = nodep->mNextNodep;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::get(const U32 local_id)
|
||||
{
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
|
||||
|
||||
do // First node guaranteed to be there
|
||||
{
|
||||
S32 i;
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
// Iterate through all members of this node
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (nodep->mKey[i] == local_id)
|
||||
{
|
||||
// We found it.
|
||||
return nodep->mData[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Done with all objects in this node, go to the next.
|
||||
nodep = nodep->mNextNodep;
|
||||
} while (nodep);
|
||||
|
||||
return mNull;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::check(const U32 local_id) const
|
||||
{
|
||||
const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
|
||||
|
||||
do // First node guaranteed to be there
|
||||
{
|
||||
S32 i;
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
// Iterate through all members of this node
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (nodep->mKey[i] == local_id)
|
||||
{
|
||||
// We found it.
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Done with all objects in this node, go to the next.
|
||||
nodep = nodep->mNextNodep;
|
||||
} while (nodep);
|
||||
|
||||
// Didn't find anything
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::set(const U32 local_id, const DATA_TYPE data)
|
||||
{
|
||||
// Set is just like a normal find, except that if we find a match
|
||||
// we replace it with the input value.
|
||||
// If we don't find a match, we append to the end of the list.
|
||||
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
|
||||
|
||||
while (1)
|
||||
{
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (nodep->mKey[i] == local_id)
|
||||
{
|
||||
// We found a match for this key, replace the data with
|
||||
// the incoming data.
|
||||
nodep->mData[i] = data;
|
||||
return nodep->mData[i];
|
||||
}
|
||||
}
|
||||
if (!nodep->mNextNodep)
|
||||
{
|
||||
// We've iterated through all of the keys without finding a match
|
||||
if (i < SIZE)
|
||||
{
|
||||
// There's still some space on this node, append
|
||||
// the key and data to it.
|
||||
nodep->mKey[i] = local_id;
|
||||
nodep->mData[i] = data;
|
||||
nodep->mCount++;
|
||||
|
||||
return nodep->mData[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// This node is full, append a new node to the end.
|
||||
nodep->mNextNodep = new LLLocalIDHashNode<DATA_TYPE, SIZE>;
|
||||
nodep->mNextNodep->mKey[0] = local_id;
|
||||
nodep->mNextNodep->mData[0] = data;
|
||||
nodep->mNextNodep->mCount = 1;
|
||||
|
||||
return nodep->mNextNodep->mData[0];
|
||||
}
|
||||
}
|
||||
|
||||
// No match on this node, go to the next
|
||||
nodep = nodep->mNextNodep;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::remove(const U32 local_id)
|
||||
{
|
||||
// Remove is the trickiest operation.
|
||||
// What we want to do is swap the last element of the last
|
||||
// node if we find the one that we want to remove, but we have
|
||||
// to deal with deleting the node from the tail if it's empty, but
|
||||
// NOT if it's the only node left.
|
||||
|
||||
const S32 node_index = local_id & 0xff;
|
||||
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index];
|
||||
|
||||
// A modification of the standard search algorithm.
|
||||
do // First node guaranteed to be there
|
||||
{
|
||||
const S32 count = nodep->mCount;
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (nodep->mKey[i] == local_id)
|
||||
{
|
||||
// If we're removing the item currently pointed to by one
|
||||
// or more iterators, we can just swap in the last item
|
||||
// and back the iterator(s) up by one.
|
||||
// Otherwise, we need to do a slow and safe shift of all
|
||||
// items back to one position to fill the hole and fix up
|
||||
// all iterators we find.
|
||||
BOOL need_shift = FALSE;
|
||||
S32 cur_iter;
|
||||
if (mIterCount)
|
||||
{
|
||||
for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++)
|
||||
{
|
||||
if (mIters[cur_iter])
|
||||
{
|
||||
// We only care if the hash map node is on the one
|
||||
// that we're working on. If it's before, we've already
|
||||
// traversed it, if it's after, changing the order doesn't
|
||||
// matter.
|
||||
if (mIters[cur_iter]->mCurHashMapNodeNum == node_index)
|
||||
{
|
||||
if ((mIters[cur_iter]->mCurHashNodep == nodep)
|
||||
&& (mIters[cur_iter]->mCurHashNodeKey == i))
|
||||
{
|
||||
// it's on the one we're deleting, we'll
|
||||
// fix the iterator quickly below.
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're trying to remove an item on this
|
||||
// iterator's chain that this
|
||||
// iterator doesn't point to! We need to do
|
||||
// the slow remove-and-shift-down case.
|
||||
need_shift = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removing an item that isn't pointed to by all iterators
|
||||
if (need_shift)
|
||||
{
|
||||
return removeWithShift(local_id);
|
||||
}
|
||||
|
||||
// Fix the iterators that point to this node/i pair, the
|
||||
// one we're deleting
|
||||
for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++)
|
||||
{
|
||||
if (mIters[cur_iter])
|
||||
{
|
||||
// We only care if the hash map node is on the one
|
||||
// that we're working on. If it's before, we've already
|
||||
// traversed it, if it's after, changing the order doesn't
|
||||
// matter.
|
||||
if (mIters[cur_iter]->mCurHashMapNodeNum == node_index)
|
||||
{
|
||||
if ((mIters[cur_iter]->mCurHashNodep == nodep)
|
||||
&& (mIters[cur_iter]->mCurHashNodeKey == i))
|
||||
{
|
||||
// We can handle the case where we're deleting
|
||||
// the element we're on trivially (sort of).
|
||||
if (nodep->mCount > 1)
|
||||
{
|
||||
// If we're not going to delete this node,
|
||||
// it's OK.
|
||||
mIters[cur_iter]->mCurHashNodeKey--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're going to delete this node, because this
|
||||
// is the last element on it.
|
||||
|
||||
// Find the next node, and then back up one.
|
||||
mIters[cur_iter]->next();
|
||||
mIters[cur_iter]->mCurHashNodeKey--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We found the node that we want to remove.
|
||||
// Find the last (and next-to-last) node, and the index of the last
|
||||
// element. We could conceviably start from the node we're on,
|
||||
// but that makes it more complicated, this is easier.
|
||||
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[node_index];
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
|
||||
|
||||
// Find the last and next-to-last
|
||||
while (lastp->mNextNodep)
|
||||
{
|
||||
prevp = lastp;
|
||||
lastp = lastp->mNextNodep;
|
||||
}
|
||||
|
||||
// First, swap in the last to the current location.
|
||||
nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
|
||||
nodep->mData[i] = lastp->mData[lastp->mCount - 1];
|
||||
|
||||
// Now, we delete the entry
|
||||
lastp->mCount--;
|
||||
lastp->mData[lastp->mCount] = mNull;
|
||||
|
||||
if (!lastp->mCount)
|
||||
{
|
||||
// We deleted the last element!
|
||||
if (lastp != &mNodes[local_id & 0xff])
|
||||
{
|
||||
// Only blitz the node if it's not the head
|
||||
// Set the previous node to point to NULL, then
|
||||
// blitz the empty last node
|
||||
prevp->mNextNodep = NULL;
|
||||
delete lastp;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate to the next node, we've scanned all the entries in this one.
|
||||
nodep = nodep->mNextNodep;
|
||||
} while (nodep);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::removeWithShift(const U32 local_id)
|
||||
{
|
||||
const S32 node_index = local_id & 0xFF;
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index];
|
||||
LLLocalIDHashNode<DATA_TYPE, SIZE>* prevp = NULL;
|
||||
BOOL found = FALSE;
|
||||
|
||||
do // First node guaranteed to be there
|
||||
{
|
||||
const S32 count = nodep->mCount;
|
||||
S32 i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (nodep->mKey[i] == local_id)
|
||||
{
|
||||
// Found the item. Start shifting items from later
|
||||
// in the list over this item.
|
||||
found = TRUE;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
// If there is an iterator on this node, we need to
|
||||
// back it up.
|
||||
S32 cur_iter;
|
||||
for (cur_iter = 0; cur_iter <MAX_ITERS; cur_iter++)
|
||||
{
|
||||
LLLocalIDHashMapIter<DATA_TYPE, SIZE>* iter;
|
||||
iter = mIters[cur_iter];
|
||||
// If an iterator is on this node,i pair, then back it up.
|
||||
if (iter
|
||||
&& iter->mCurHashMapNodeNum == node_index
|
||||
&& iter->mCurHashNodep == nodep
|
||||
&& iter->mCurHashNodeKey == i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
// Don't need to move iterator nodep, since
|
||||
// we're in the same node.
|
||||
iter->mCurHashNodeKey--;
|
||||
}
|
||||
else if (prevp)
|
||||
{
|
||||
// need to go the previous node, last item
|
||||
iter->mCurHashNodep = prevp;
|
||||
iter->mCurHashNodeKey = prevp->mCount - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're on the first item in the list, but
|
||||
// need to go back anyhow.
|
||||
|
||||
// BUG: If this deletion empties the list,
|
||||
// iter->done() will be wrong until
|
||||
// iter->next() is called.
|
||||
iter->mCurHashNodeKey = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy data from the next position into this position.
|
||||
if (i < count-1)
|
||||
{
|
||||
// we're not on the last item in the node,
|
||||
// so we can copy within the node
|
||||
nodep->mKey[i] = nodep->mKey[i+1];
|
||||
nodep->mData[i] = nodep->mData[i+1];
|
||||
}
|
||||
else if (nodep->mNextNodep)
|
||||
{
|
||||
// we're on the last item in the node,
|
||||
// but there's a next node we can copy from
|
||||
nodep->mKey[i] = nodep->mNextNodep->mKey[0];
|
||||
nodep->mData[i] = nodep->mNextNodep->mData[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're on the last position in the list.
|
||||
// No one to copy from. Replace with nothing.
|
||||
nodep->mKey[i] = 0;
|
||||
nodep->mData[i] = mNull;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last node in chain, so delete the last node
|
||||
if (found
|
||||
&& !nodep->mNextNodep)
|
||||
{
|
||||
// delete the last item off the last node
|
||||
nodep->mCount--;
|
||||
|
||||
if (nodep->mCount == 0)
|
||||
{
|
||||
// We deleted the last element!
|
||||
if (nodep != &mNodes[node_index])
|
||||
{
|
||||
// Always have a prevp if we're not the head.
|
||||
llassert(prevp);
|
||||
|
||||
// Only blitz the node if it's not the head
|
||||
// Set the previous node to point to NULL, then
|
||||
// blitz the empty last node
|
||||
prevp->mNextNodep = NULL;
|
||||
delete nodep;
|
||||
nodep = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Deleted last item in chain, so we're done.
|
||||
return found;
|
||||
}
|
||||
|
||||
prevp = nodep;
|
||||
nodep = nodep->mNextNodep;
|
||||
} while (nodep);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < MAX_ITERS; i++)
|
||||
{
|
||||
if (mIters[i] == iter)
|
||||
{
|
||||
mIters[i] = NULL;
|
||||
mIterCount--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
llerrs << "Iterator " << iter << " not found for removal in hash map!" << llendl;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
void LLLocalIDHashMap<DATA_TYPE, SIZE>::addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < MAX_ITERS; i++)
|
||||
{
|
||||
if (mIters[i] == NULL)
|
||||
{
|
||||
mIters[i] = iter;
|
||||
mIterCount++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
llerrs << "More than " << MAX_ITERS << " iterating over a map simultaneously!" << llendl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// LLLocalIDHashMapIter Implementation
|
||||
//
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLLocalIDHashMapIter<DATA_TYPE, SIZE>::LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
|
||||
{
|
||||
mHashMapp = NULL;
|
||||
setMap(hash_mapp);
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
LLLocalIDHashMapIter<DATA_TYPE, SIZE>::~LLLocalIDHashMapIter()
|
||||
{
|
||||
if (mHashMapp)
|
||||
{
|
||||
mHashMapp->removeIter(this);
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
|
||||
{
|
||||
if (mHashMapp)
|
||||
{
|
||||
mHashMapp->removeIter(this);
|
||||
}
|
||||
mHashMapp = hash_mapp;
|
||||
if (mHashMapp)
|
||||
{
|
||||
mHashMapp->addIter(this);
|
||||
}
|
||||
|
||||
mCurHashNodep = NULL;
|
||||
mCurHashMapNodeNum = -1;
|
||||
mCurHashNodeKey = 0;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::first()
|
||||
{
|
||||
// Iterate through until we find the first non-empty node;
|
||||
S32 i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
if (mHashMapp->mNodes[i].mCount)
|
||||
{
|
||||
|
||||
mCurHashNodep = &mHashMapp->mNodes[i];
|
||||
mCurHashMapNodeNum = i;
|
||||
mCurHashNodeKey = 0;
|
||||
//return mCurHashNodep->mData[0];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Completely empty!
|
||||
mCurHashNodep = NULL;
|
||||
//return mNull;
|
||||
return;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline BOOL LLLocalIDHashMapIter<DATA_TYPE, SIZE>::done() const
|
||||
{
|
||||
return mCurHashNodep ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline S32 LLLocalIDHashMapIter<DATA_TYPE, SIZE>::currentBin() const
|
||||
{
|
||||
if ( (mCurHashMapNodeNum > 255)
|
||||
||(mCurHashMapNodeNum < 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mCurHashMapNodeNum;
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setBin(S32 bin)
|
||||
{
|
||||
// Iterate through until we find the first non-empty node;
|
||||
S32 i;
|
||||
bin = llclamp(bin, 0, 255);
|
||||
for (i = bin; i < 256; i++)
|
||||
{
|
||||
if (mHashMapp->mNodes[i].mCount)
|
||||
{
|
||||
|
||||
mCurHashNodep = &mHashMapp->mNodes[i];
|
||||
mCurHashMapNodeNum = i;
|
||||
mCurHashNodeKey = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < bin; i++)
|
||||
{
|
||||
if (mHashMapp->mNodes[i].mCount)
|
||||
{
|
||||
|
||||
mCurHashNodep = &mHashMapp->mNodes[i];
|
||||
mCurHashMapNodeNum = i;
|
||||
mCurHashNodeKey = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Completely empty!
|
||||
mCurHashNodep = NULL;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline DATA_TYPE &LLLocalIDHashMapIter<DATA_TYPE, SIZE>::current()
|
||||
{
|
||||
if (!mCurHashNodep)
|
||||
{
|
||||
return mNull;
|
||||
}
|
||||
return mCurHashNodep->mData[mCurHashNodeKey];
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, int SIZE>
|
||||
inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::next()
|
||||
{
|
||||
// No current entry, this iterator is done
|
||||
if (!mCurHashNodep)
|
||||
{
|
||||
//return mNull;
|
||||
return;
|
||||
}
|
||||
|
||||
// Go to the next element
|
||||
mCurHashNodeKey++;
|
||||
if (mCurHashNodeKey < mCurHashNodep->mCount)
|
||||
{
|
||||
// We're not done with this node, return the current element
|
||||
//return mCurHashNodep->mData[mCurHashNodeKey];
|
||||
return;
|
||||
}
|
||||
|
||||
// Done with this node, move to the next
|
||||
mCurHashNodep = mCurHashNodep->mNextNodep;
|
||||
if (mCurHashNodep)
|
||||
{
|
||||
// Return the first element
|
||||
mCurHashNodeKey = 0;
|
||||
//return mCurHashNodep->mData[0];
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the next non-empty node (keyed on the first byte)
|
||||
mCurHashMapNodeNum++;
|
||||
|
||||
S32 i;
|
||||
for (i = mCurHashMapNodeNum; i < 256; i++)
|
||||
{
|
||||
if (mHashMapp->mNodes[i].mCount)
|
||||
{
|
||||
// We found one that wasn't empty
|
||||
mCurHashNodep = &mHashMapp->mNodes[i];
|
||||
mCurHashMapNodeNum = i;
|
||||
mCurHashNodeKey = 0;
|
||||
//return mCurHashNodep->mData[0];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// OK, we're done, nothing else to iterate
|
||||
mCurHashNodep = NULL;
|
||||
mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
|
||||
//return mNull;
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // LL_LLLOCALIDHASHMAP_H
|
||||
|
|
@ -76,7 +76,6 @@ documentation and/or software.
|
|||
|
||||
#include "llmd5.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream> // cerr
|
||||
|
||||
// how many bytes to grab at a time when checking files
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
//#endif
|
||||
|
||||
#if defined(LL_WINDOWS)
|
||||
//# include <windows.h>
|
||||
# include <psapi.h>
|
||||
#elif defined(LL_DARWIN)
|
||||
# include <sys/types.h>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@
|
|||
#define LLMEMORY_H
|
||||
|
||||
#include "linden_common.h"
|
||||
#if !LL_WINDOWS
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
class LLMutex ;
|
||||
|
||||
|
|
@ -36,6 +39,20 @@ class LLMutex ;
|
|||
#define LL_CHECK_MEMORY
|
||||
#endif
|
||||
|
||||
#if LL_WINDOWS
|
||||
#define LL_ALIGN_OF __alignof
|
||||
#else
|
||||
#define LL_ALIGN_OF __align_of__
|
||||
#endif
|
||||
|
||||
#if LL_WINDOWS
|
||||
#define LL_DEFAULT_HEAP_ALIGN 8
|
||||
#elif LL_DARWIN
|
||||
#define LL_DEFAULT_HEAP_ALIGN 16
|
||||
#elif LL_LINUX
|
||||
#define LL_DEFAULT_HEAP_ALIGN 8
|
||||
#endif
|
||||
|
||||
inline void* ll_aligned_malloc( size_t size, int align )
|
||||
{
|
||||
void* mem = malloc( size + (align - 1) + sizeof(void*) );
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@
|
|||
#include "indra_constants.h"
|
||||
#include "llerror.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llstat.h"
|
||||
#include "lltreeiterators.h"
|
||||
#include "llmetricperformancetester.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
// LLMetricPerformanceTesterBasic : static methods and testers management
|
||||
|
|
@ -91,7 +91,7 @@ LLMetricPerformanceTesterBasic* LLMetricPerformanceTesterBasic::getTester(std::s
|
|||
// Return TRUE if this metric is requested or if the general default "catch all" metric is requested
|
||||
BOOL LLMetricPerformanceTesterBasic::isMetricLogRequested(std::string name)
|
||||
{
|
||||
return (LLFastTimer::sMetricLog && ((LLFastTimer::sLogName == name) || (LLFastTimer::sLogName == DEFAULT_METRIC_NAME)));
|
||||
return (LLTrace::TimeBlock::sMetricLog && ((LLTrace::TimeBlock::sLogName == name) || (LLTrace::TimeBlock::sLogName == DEFAULT_METRIC_NAME)));
|
||||
}
|
||||
|
||||
/*static*/
|
||||
|
|
@ -194,8 +194,7 @@ void LLMetricPerformanceTesterBasic::preOutputTestResults(LLSD* sd)
|
|||
|
||||
void LLMetricPerformanceTesterBasic::postOutputTestResults(LLSD* sd)
|
||||
{
|
||||
LLMutexLock lock(LLFastTimer::sLogLock);
|
||||
LLFastTimer::sLogQueue.push((*sd));
|
||||
LLTrace::TimeBlock::pushLog(*sd);
|
||||
}
|
||||
|
||||
void LLMetricPerformanceTesterBasic::outputTestResults()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#define LLMORTICIAN_H
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include <list>
|
||||
|
||||
class LL_COMMON_API LLMortician
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* @file llmutex.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llapr.h"
|
||||
|
||||
#include "apr_portable.h"
|
||||
|
||||
#include "llmutex.h"
|
||||
#include "llthread.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
LLMutex::LLMutex(apr_pool_t *poolp) :
|
||||
mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD)
|
||||
{
|
||||
//if (poolp)
|
||||
//{
|
||||
// mIsLocalPool = FALSE;
|
||||
// mAPRPoolp = poolp;
|
||||
//}
|
||||
//else
|
||||
{
|
||||
mIsLocalPool = TRUE;
|
||||
apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
|
||||
}
|
||||
apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mAPRPoolp);
|
||||
}
|
||||
|
||||
|
||||
LLMutex::~LLMutex()
|
||||
{
|
||||
#if MUTEX_DEBUG
|
||||
//bad assertion, the subclass LLSignal might be "locked", and that's OK
|
||||
//llassert_always(!isLocked()); // better not be locked!
|
||||
#endif
|
||||
if (ll_apr_is_initialized())
|
||||
{
|
||||
apr_thread_mutex_destroy(mAPRMutexp);
|
||||
if (mIsLocalPool)
|
||||
{
|
||||
apr_pool_destroy(mAPRPoolp);
|
||||
}
|
||||
}
|
||||
mAPRMutexp = NULL;
|
||||
}
|
||||
|
||||
|
||||
void LLMutex::lock()
|
||||
{
|
||||
if(isSelfLocked())
|
||||
{ //redundant lock
|
||||
mCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
apr_thread_mutex_lock(mAPRMutexp);
|
||||
|
||||
#if MUTEX_DEBUG
|
||||
// Have to have the lock before we can access the debug info
|
||||
U32 id = LLThread::currentID();
|
||||
if (mIsLocked[id] != FALSE)
|
||||
llerrs << "Already locked in Thread: " << id << llendl;
|
||||
mIsLocked[id] = TRUE;
|
||||
#endif
|
||||
|
||||
mLockingThread = LLThread::currentID();
|
||||
}
|
||||
|
||||
void LLMutex::unlock()
|
||||
{
|
||||
if (mCount > 0)
|
||||
{ //not the root unlock
|
||||
mCount--;
|
||||
return;
|
||||
}
|
||||
|
||||
#if MUTEX_DEBUG
|
||||
// Access the debug info while we have the lock
|
||||
U32 id = LLThread::currentID();
|
||||
if (mIsLocked[id] != TRUE)
|
||||
llerrs << "Not locked in Thread: " << id << llendl;
|
||||
mIsLocked[id] = FALSE;
|
||||
#endif
|
||||
|
||||
mLockingThread = NO_THREAD;
|
||||
apr_thread_mutex_unlock(mAPRMutexp);
|
||||
}
|
||||
|
||||
bool LLMutex::isLocked()
|
||||
{
|
||||
apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp);
|
||||
if (APR_STATUS_IS_EBUSY(status))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
apr_thread_mutex_unlock(mAPRMutexp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LLMutex::isSelfLocked()
|
||||
{
|
||||
return mLockingThread == LLThread::currentID();
|
||||
}
|
||||
|
||||
U32 LLMutex::lockingThread() const
|
||||
{
|
||||
return mLockingThread;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
LLCondition::LLCondition(apr_pool_t *poolp) :
|
||||
LLMutex(poolp)
|
||||
{
|
||||
// base class (LLMutex) has already ensured that mAPRPoolp is set up.
|
||||
|
||||
apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
|
||||
}
|
||||
|
||||
|
||||
LLCondition::~LLCondition()
|
||||
{
|
||||
apr_thread_cond_destroy(mAPRCondp);
|
||||
mAPRCondp = NULL;
|
||||
}
|
||||
|
||||
|
||||
void LLCondition::wait()
|
||||
{
|
||||
if (!isLocked())
|
||||
{ //mAPRMutexp MUST be locked before calling apr_thread_cond_wait
|
||||
apr_thread_mutex_lock(mAPRMutexp);
|
||||
#if MUTEX_DEBUG
|
||||
// avoid asserts on destruction in non-release builds
|
||||
U32 id = LLThread::currentID();
|
||||
mIsLocked[id] = TRUE;
|
||||
#endif
|
||||
}
|
||||
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
|
||||
}
|
||||
|
||||
void LLCondition::signal()
|
||||
{
|
||||
apr_thread_cond_signal(mAPRCondp);
|
||||
}
|
||||
|
||||
void LLCondition::broadcast()
|
||||
{
|
||||
apr_thread_cond_broadcast(mAPRCondp);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* @file llmutex.h
|
||||
* @brief Base classes for mutex and condition handling.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLMUTEX_H
|
||||
#define LL_LLMUTEX_H
|
||||
|
||||
#include "llapr.h"
|
||||
#include "apr_thread_cond.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
|
||||
|
||||
class LL_COMMON_API LLMutex
|
||||
{
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
NO_THREAD = 0xFFFFFFFF
|
||||
} e_locking_thread;
|
||||
|
||||
LLMutex(apr_pool_t *apr_poolp = NULL); // NULL pool constructs a new pool for the mutex
|
||||
virtual ~LLMutex();
|
||||
|
||||
void lock(); // blocks
|
||||
void unlock();
|
||||
bool isLocked(); // non-blocking, but does do a lock/unlock so not free
|
||||
bool isSelfLocked(); //return true if locked in a same thread
|
||||
U32 lockingThread() const; //get ID of locking thread
|
||||
|
||||
protected:
|
||||
apr_thread_mutex_t *mAPRMutexp;
|
||||
mutable U32 mCount;
|
||||
mutable U32 mLockingThread;
|
||||
|
||||
apr_pool_t *mAPRPoolp;
|
||||
BOOL mIsLocalPool;
|
||||
|
||||
#if MUTEX_DEBUG
|
||||
std::map<U32, BOOL> mIsLocked;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
|
||||
class LL_COMMON_API LLCondition : public LLMutex
|
||||
{
|
||||
public:
|
||||
LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
|
||||
~LLCondition();
|
||||
|
||||
void wait(); // blocks
|
||||
void signal();
|
||||
void broadcast();
|
||||
|
||||
protected:
|
||||
apr_thread_cond_t *mAPRCondp;
|
||||
};
|
||||
|
||||
class LLMutexLock
|
||||
{
|
||||
public:
|
||||
LLMutexLock(LLMutex* mutex)
|
||||
{
|
||||
mMutex = mutex;
|
||||
|
||||
if(mMutex)
|
||||
mMutex->lock();
|
||||
}
|
||||
~LLMutexLock()
|
||||
{
|
||||
if(mMutex)
|
||||
mMutex->unlock();
|
||||
}
|
||||
private:
|
||||
LLMutex* mMutex;
|
||||
};
|
||||
|
||||
#endif // LL_LLTHREAD_H
|
||||
|
|
@ -97,24 +97,13 @@ public:
|
|||
|
||||
LLPointer<Type>& operator =(Type* ptr)
|
||||
{
|
||||
if( mPointer != ptr )
|
||||
{
|
||||
unref();
|
||||
mPointer = ptr;
|
||||
ref();
|
||||
}
|
||||
|
||||
assign(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LLPointer<Type>& operator =(const LLPointer<Type>& ptr)
|
||||
{
|
||||
if( mPointer != ptr.mPointer )
|
||||
{
|
||||
unref();
|
||||
mPointer = ptr.mPointer;
|
||||
ref();
|
||||
}
|
||||
assign(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -122,12 +111,7 @@ public:
|
|||
template<typename Subclass>
|
||||
LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr)
|
||||
{
|
||||
if( mPointer != ptr.get() )
|
||||
{
|
||||
unref();
|
||||
mPointer = ptr.get();
|
||||
ref();
|
||||
}
|
||||
assign(ptr.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +128,16 @@ protected:
|
|||
void ref();
|
||||
void unref();
|
||||
#else
|
||||
|
||||
void assign(const LLPointer<Type>& ptr)
|
||||
{
|
||||
if( mPointer != ptr.mPointer )
|
||||
{
|
||||
unref();
|
||||
mPointer = ptr.mPointer;
|
||||
ref();
|
||||
}
|
||||
}
|
||||
void ref()
|
||||
{
|
||||
if (mPointer)
|
||||
|
|
@ -156,9 +150,9 @@ protected:
|
|||
{
|
||||
if (mPointer)
|
||||
{
|
||||
Type *tempp = mPointer;
|
||||
Type *temp = mPointer;
|
||||
mPointer = NULL;
|
||||
tempp->unref();
|
||||
temp->unref();
|
||||
if (mPointer != NULL)
|
||||
{
|
||||
llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl;
|
||||
|
|
@ -171,4 +165,40 @@ protected:
|
|||
Type* mPointer;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class LLCopyOnWritePointer : public LLPointer<Type>
|
||||
{
|
||||
public:
|
||||
typedef LLCopyOnWritePointer<Type> self_t;
|
||||
typedef LLPointer<Type> pointer_t;
|
||||
|
||||
LLCopyOnWritePointer()
|
||||
{}
|
||||
|
||||
LLCopyOnWritePointer(Type* ptr)
|
||||
: LLPointer<Type>(ptr)
|
||||
{}
|
||||
|
||||
LLCopyOnWritePointer(LLPointer<Type>& ptr)
|
||||
: LLPointer<Type>(ptr)
|
||||
{}
|
||||
|
||||
Type* write()
|
||||
{
|
||||
makeUnique();
|
||||
return pointer_t::mPointer;
|
||||
}
|
||||
|
||||
void makeUnique()
|
||||
{
|
||||
if (pointer_t::notNull() && pointer_t::mPointer->getNumRefs() > 1)
|
||||
{
|
||||
*(pointer_t* )(this) = new Type(*pointer_t::mPointer);
|
||||
}
|
||||
}
|
||||
|
||||
const Type* operator->() const { return pointer_t::mPointer; }
|
||||
const Type& operator*() const { return *pointer_t::mPointer; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* @file llfunctorregistry.cpp
|
||||
* @author Kent Quirk
|
||||
* @brief Maintains a registry of named callback functors taking a single LLSD parameter
|
||||
/**
|
||||
* @file llpredicate.cpp
|
||||
* @brief abstraction for filtering objects by predicates, with arbitrary boolean expressions
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
|
|
@ -23,11 +22,20 @@
|
|||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
**/
|
||||
|
||||
*/
|
||||
#include "linden_common.h"
|
||||
#include "llfunctorregistry.h"
|
||||
|
||||
// This is a default functor always resident in the system.
|
||||
// It's used whenever a functor isn't found in the registry, so that
|
||||
// we at least log the data relating to the user response.
|
||||
#include "llpredicate.h"
|
||||
|
||||
namespace LLPredicate
|
||||
{
|
||||
const U32 cPredicateFlagsFromEnum[5] =
|
||||
{
|
||||
0xAAAAaaaa, // 10101010101010101010101010101010
|
||||
0xCCCCcccc, // 11001100110011001100110011001100
|
||||
0xF0F0F0F0, // 11110000111100001111000011110000
|
||||
0xFF00FF00, // 11111111000000001111111100000000
|
||||
0xFFFF0000 // 11111111111111110000000000000000
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
/**
|
||||
* @file llpredicate.h
|
||||
* @brief abstraction for filtering objects by predicates, with arbitrary boolean expressions
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPREDICATE_H
|
||||
#define LL_LLPREDICATE_H
|
||||
|
||||
#include "llerror.h"
|
||||
|
||||
namespace LLPredicate
|
||||
{
|
||||
template<typename ENUM> class Rule;
|
||||
|
||||
extern const U32 cPredicateFlagsFromEnum[5];
|
||||
|
||||
template<typename ENUM>
|
||||
class Value
|
||||
{
|
||||
public:
|
||||
typedef U32 predicate_flag_t;
|
||||
static const S32 cMaxEnum = 5;
|
||||
|
||||
Value(ENUM e, bool predicate_value = true)
|
||||
: mPredicateFlags(predicate_value ? cPredicateFlagsFromEnum[e] : ~cPredicateFlagsFromEnum[e])
|
||||
{
|
||||
llassert(0 <= e && e < cMaxEnum);
|
||||
}
|
||||
|
||||
Value()
|
||||
: mPredicateFlags(0xFFFFffff)
|
||||
{}
|
||||
|
||||
Value operator!() const
|
||||
{
|
||||
Value new_value;
|
||||
new_value.mPredicateFlags = ~mPredicateFlags;
|
||||
return new_value;
|
||||
}
|
||||
|
||||
Value operator &&(const Value other) const
|
||||
{
|
||||
Value new_value;
|
||||
new_value.mPredicateFlags = mPredicateFlags & other.mPredicateFlags;
|
||||
return new_value;
|
||||
}
|
||||
|
||||
Value operator ||(const Value other) const
|
||||
{
|
||||
Value new_value;
|
||||
new_value.mPredicateFlags = mPredicateFlags | other.mPredicateFlags;
|
||||
return new_value;
|
||||
}
|
||||
|
||||
void set(ENUM e, bool value = true)
|
||||
{
|
||||
llassert(0 <= e && e < cMaxEnum);
|
||||
predicate_flag_t flags_to_modify;
|
||||
predicate_flag_t mask = cPredicateFlagsFromEnum[e];
|
||||
if (value)
|
||||
{ // add predicate "e" to flags that don't contain it already
|
||||
flags_to_modify = (mPredicateFlags & ~mask);
|
||||
// clear flags not containing e
|
||||
mPredicateFlags &= mask;
|
||||
// add back flags shifted to contain e
|
||||
mPredicateFlags |= flags_to_modify << (0x1 << e);
|
||||
}
|
||||
else
|
||||
{ // remove predicate "e" from flags that contain it
|
||||
flags_to_modify = (mPredicateFlags & mask);
|
||||
// clear flags containing e
|
||||
mPredicateFlags &= ~mask;
|
||||
// add back flags shifted to not contain e
|
||||
mPredicateFlags |= flags_to_modify >> (0x1 << e);
|
||||
}
|
||||
}
|
||||
|
||||
void forget(ENUM e)
|
||||
{
|
||||
set(e, true);
|
||||
U32 flags_with_predicate = mPredicateFlags;
|
||||
set(e, false);
|
||||
// ambiguous value is result of adding and removing predicate at the same time!
|
||||
mPredicateFlags |= flags_with_predicate;
|
||||
}
|
||||
|
||||
bool allSet() const
|
||||
{
|
||||
return mPredicateFlags == ~0;
|
||||
}
|
||||
|
||||
bool noneSet() const
|
||||
{
|
||||
return mPredicateFlags == 0;
|
||||
}
|
||||
|
||||
bool someSet() const
|
||||
{
|
||||
return mPredicateFlags != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
predicate_flag_t mPredicateFlags;
|
||||
};
|
||||
|
||||
template<typename ENUM>
|
||||
class Rule
|
||||
{
|
||||
public:
|
||||
Rule(ENUM value)
|
||||
: mRule(value)
|
||||
{}
|
||||
|
||||
Rule(const Value<ENUM> other)
|
||||
: mRule(other)
|
||||
{}
|
||||
|
||||
Rule()
|
||||
{}
|
||||
|
||||
void require(ENUM e)
|
||||
{
|
||||
mRule.set(e, require);
|
||||
}
|
||||
|
||||
void allow(ENUM e)
|
||||
{
|
||||
mRule.forget(e);
|
||||
}
|
||||
|
||||
bool check(const Value<ENUM> value) const
|
||||
{
|
||||
return (mRule && value).someSet();
|
||||
}
|
||||
|
||||
bool requires(const Value<ENUM> value) const
|
||||
{
|
||||
return (mRule && value).someSet() && (!mRule && value).noneSet();
|
||||
}
|
||||
|
||||
bool isAmbivalent(const Value<ENUM> value) const
|
||||
{
|
||||
return (mRule && value).someSet() && (!mRule && value).someSet();
|
||||
}
|
||||
|
||||
bool acceptsAll() const
|
||||
{
|
||||
return mRule.allSet();
|
||||
}
|
||||
|
||||
bool acceptsNone() const
|
||||
{
|
||||
return mRule.noneSet();
|
||||
}
|
||||
|
||||
Rule operator!() const
|
||||
{
|
||||
Rule new_rule;
|
||||
new_rule.mRule = !mRule;
|
||||
return new_rule;
|
||||
}
|
||||
|
||||
Rule operator &&(const Rule other) const
|
||||
{
|
||||
Rule new_rule;
|
||||
new_rule.mRule = mRule && other.mRule;
|
||||
return new_rule;
|
||||
}
|
||||
|
||||
Rule operator ||(const Rule other) const
|
||||
{
|
||||
Rule new_rule;
|
||||
new_rule.mRule = mRule || other.mRule;
|
||||
return new_rule;
|
||||
}
|
||||
|
||||
private:
|
||||
Value<ENUM> mRule;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ENUM>
|
||||
LLPredicate::Value<ENUM> ll_make_predicate(ENUM e, bool predicate_value = true)
|
||||
{
|
||||
return LLPredicate::Value<ENUM>(e, predicate_value);
|
||||
}
|
||||
|
||||
|
||||
#endif // LL_LLPREDICATE_H
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "llinitparam.h"
|
||||
#include "llsdparam.h"
|
||||
#include "llwin32headerslean.h"
|
||||
#include "apr_thread_proc.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
|
|
@ -38,8 +39,7 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#if LL_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h> // HANDLE (eye roll)
|
||||
#include "llwin32headerslean.h" // for HANDLE
|
||||
#elif LL_LINUX
|
||||
#if defined(Status)
|
||||
#undef Status
|
||||
|
|
|
|||
|
|
@ -32,9 +32,7 @@
|
|||
//#include <memory>
|
||||
|
||||
#if LL_WINDOWS
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
# include "llwin32headerslean.h"
|
||||
# define _interlockedbittestandset _renamed_interlockedbittestandset
|
||||
# define _interlockedbittestandreset _renamed_interlockedbittestandreset
|
||||
# include <intrin.h>
|
||||
|
|
@ -877,7 +875,7 @@ LLProcessorInfo::LLProcessorInfo() : mImpl(NULL)
|
|||
|
||||
|
||||
LLProcessorInfo::~LLProcessorInfo() {}
|
||||
F64 LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); }
|
||||
LLUnitImplicit<F64, LLUnits::Megahertz> LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); }
|
||||
bool LLProcessorInfo::hasSSE() const { return mImpl->hasSSE(); }
|
||||
bool LLProcessorInfo::hasSSE2() const { return mImpl->hasSSE2(); }
|
||||
bool LLProcessorInfo::hasAltivec() const { return mImpl->hasAltivec(); }
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#ifndef LLPROCESSOR_H
|
||||
#define LLPROCESSOR_H
|
||||
#include "llunit.h"
|
||||
|
||||
class LLProcessorInfoImpl;
|
||||
|
||||
class LL_COMMON_API LLProcessorInfo
|
||||
|
|
@ -35,7 +37,7 @@ public:
|
|||
LLProcessorInfo();
|
||||
~LLProcessorInfo();
|
||||
|
||||
F64 getCPUFrequency() const;
|
||||
LLUnitImplicit<F64, LLUnits::Megahertz> getCPUFrequency() const;
|
||||
bool hasSSE() const;
|
||||
bool hasSSE2() const;
|
||||
bool hasAltivec() const;
|
||||
|
|
|
|||
|
|
@ -1,724 +0,0 @@
|
|||
/**
|
||||
* @file llptrskiplist.h
|
||||
* @brief Skip list implementation.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPTRSKIPLIST_H
|
||||
#define LL_LLPTRSKIPLIST_H
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llrand.h"
|
||||
//#include "vmath.h"
|
||||
#include "llrand.h"
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//
|
||||
// LLPtrSkipList implementation - skip list for pointers to objects
|
||||
//
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH = 8>
|
||||
class LLPtrSkipList
|
||||
{
|
||||
public:
|
||||
friend class LLPtrSkipNode;
|
||||
|
||||
// basic constructor
|
||||
LLPtrSkipList();
|
||||
// basic constructor including sorter
|
||||
LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
|
||||
BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second));
|
||||
~LLPtrSkipList();
|
||||
|
||||
inline void setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second));
|
||||
inline void setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second));
|
||||
|
||||
inline BOOL addData(DATA_TYPE *data);
|
||||
|
||||
inline BOOL checkData(const DATA_TYPE *data);
|
||||
|
||||
inline S32 getLength(); // returns number of items in the list - NOT constant time!
|
||||
|
||||
inline BOOL removeData(const DATA_TYPE *data);
|
||||
|
||||
// note that b_sort is ignored
|
||||
inline BOOL moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort);
|
||||
|
||||
inline BOOL moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort);
|
||||
|
||||
// resort -- use when the value we're sorting by changes
|
||||
/* IW 12/6/02 - This doesn't work!
|
||||
Instead, remove the data BEFORE you change it
|
||||
Then re-insert it after you change it
|
||||
BOOL resortData(DATA_TYPE *data)
|
||||
*/
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
inline void removeAllNodes();
|
||||
|
||||
inline BOOL deleteData(const DATA_TYPE *data);
|
||||
|
||||
// remove all nodes from the list and delete data
|
||||
inline void deleteAllData();
|
||||
|
||||
// place mCurrentp on first node
|
||||
inline void resetList();
|
||||
|
||||
// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
inline DATA_TYPE *getCurrentData();
|
||||
|
||||
// same as getCurrentData() but a more intuitive name for the operation
|
||||
inline DATA_TYPE *getNextData();
|
||||
|
||||
// remove the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
inline void removeCurrentData();
|
||||
|
||||
// delete the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
inline void deleteCurrentData();
|
||||
|
||||
// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
inline DATA_TYPE *getFirstData();
|
||||
|
||||
// TRUE if nodes are not in sorted order
|
||||
inline BOOL corrupt();
|
||||
|
||||
protected:
|
||||
class LLPtrSkipNode
|
||||
{
|
||||
public:
|
||||
LLPtrSkipNode()
|
||||
: mData(NULL)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LLPtrSkipNode(DATA_TYPE *data)
|
||||
: mData(data)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
~LLPtrSkipNode()
|
||||
{
|
||||
if (mData)
|
||||
{
|
||||
llerror("Attempting to call LLPtrSkipNode destructor with a non-null mDatap!", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// delete associated data and NULLs out pointer
|
||||
void deleteData()
|
||||
{
|
||||
delete mData;
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
// NULLs out pointer
|
||||
void removeData()
|
||||
{
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
DATA_TYPE *mData;
|
||||
LLPtrSkipNode *mForward[BINARY_DEPTH];
|
||||
};
|
||||
|
||||
static BOOL defaultEquals(const DATA_TYPE *first, const DATA_TYPE *second)
|
||||
{
|
||||
return first == second;
|
||||
}
|
||||
|
||||
|
||||
LLPtrSkipNode mHead;
|
||||
LLPtrSkipNode *mUpdate[BINARY_DEPTH];
|
||||
LLPtrSkipNode *mCurrentp;
|
||||
LLPtrSkipNode *mCurrentOperatingp;
|
||||
S32 mLevel;
|
||||
BOOL (*mInsertFirst)(const DATA_TYPE *first, const DATA_TYPE *second);
|
||||
BOOL (*mEquals)(const DATA_TYPE *first, const DATA_TYPE *second);
|
||||
};
|
||||
|
||||
|
||||
// basic constructor
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList()
|
||||
: mInsertFirst(NULL), mEquals(defaultEquals)
|
||||
{
|
||||
if (BINARY_DEPTH < 2)
|
||||
{
|
||||
llerrs << "Trying to create skip list with too little depth, "
|
||||
"must be 2 or greater" << llendl;
|
||||
}
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
mLevel = 1;
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// basic constructor including sorter
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
|
||||
BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second))
|
||||
:mInsertFirst(insert_first), mEquals(equals)
|
||||
{
|
||||
if (BINARY_DEPTH < 2)
|
||||
{
|
||||
llerrs << "Trying to create skip list with too little depth, "
|
||||
"must be 2 or greater" << llendl;
|
||||
}
|
||||
mLevel = 1;
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::~LLPtrSkipList()
|
||||
{
|
||||
removeAllNodes();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second))
|
||||
{
|
||||
mInsertFirst = insert_first;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second))
|
||||
{
|
||||
mEquals = equals;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::addData(DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *temp;
|
||||
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
// now add the new node
|
||||
S32 newlevel;
|
||||
for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
|
||||
{
|
||||
if (ll_frand() < 0.5f)
|
||||
break;
|
||||
}
|
||||
|
||||
LLPtrSkipNode *snode = new LLPtrSkipNode(data);
|
||||
|
||||
if (newlevel > mLevel)
|
||||
{
|
||||
mHead.mForward[mLevel] = NULL;
|
||||
mUpdate[mLevel] = &mHead;
|
||||
mLevel = newlevel;
|
||||
}
|
||||
|
||||
for (level = 0; level < newlevel; level++)
|
||||
{
|
||||
snode->mForward[level] = mUpdate[level]->mForward[level];
|
||||
mUpdate[level]->mForward[level] = snode;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *temp;
|
||||
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
if (current)
|
||||
{
|
||||
return mEquals(current->mData, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// returns number of items in the list
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline S32 LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getLength()
|
||||
{
|
||||
U32 length = 0;
|
||||
for (LLPtrSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *temp;
|
||||
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
if (!current)
|
||||
{
|
||||
// empty list or beyond the end!
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// is this the one we want?
|
||||
if (!mEquals(current->mData, data))
|
||||
{
|
||||
// nope!
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// yes it is! change pointers as required
|
||||
for (level = 0; level < mLevel; level++)
|
||||
{
|
||||
if (mUpdate[level]->mForward[level] != current)
|
||||
{
|
||||
// cool, we've fixed all the pointers!
|
||||
break;
|
||||
}
|
||||
mUpdate[level]->mForward[level] = current->mForward[level];
|
||||
}
|
||||
|
||||
// clean up cuurent
|
||||
current->removeData();
|
||||
delete current;
|
||||
|
||||
// clean up mHead
|
||||
while ( (mLevel > 1)
|
||||
&&(!mHead.mForward[mLevel - 1]))
|
||||
{
|
||||
mLevel--;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// note that b_sort is ignored
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort)
|
||||
{
|
||||
BOOL removed = removeData(data);
|
||||
BOOL added = newlist->addData(data);
|
||||
return removed && added;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort)
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
mCurrentp = mCurrentOperatingp->mForward[0];
|
||||
BOOL removed = removeData(mCurrentOperatingp);
|
||||
BOOL added = newlist->addData(mCurrentOperatingp);
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
return removed && added;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// resort -- use when the value we're sorting by changes
|
||||
/* IW 12/6/02 - This doesn't work!
|
||||
Instead, remove the data BEFORE you change it
|
||||
Then re-insert it after you change it
|
||||
BOOL resortData(DATA_TYPE *data)
|
||||
{
|
||||
removeData(data);
|
||||
addData(data);
|
||||
}
|
||||
*/
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
|
||||
{
|
||||
LLPtrSkipNode *temp;
|
||||
// reset mCurrentp
|
||||
mCurrentp = *(mHead.mForward);
|
||||
|
||||
while (mCurrentp)
|
||||
{
|
||||
temp = mCurrentp->mForward[0];
|
||||
mCurrentp->removeData();
|
||||
delete mCurrentp;
|
||||
mCurrentp = temp;
|
||||
}
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteData(const DATA_TYPE *data)
|
||||
{
|
||||
S32 level;
|
||||
LLPtrSkipNode *current = &mHead;
|
||||
LLPtrSkipNode *temp;
|
||||
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
if (!current)
|
||||
{
|
||||
// empty list or beyond the end!
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// is this the one we want?
|
||||
if (!mEquals(current->mData, data))
|
||||
{
|
||||
// nope!
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do we need to fix current or currentop?
|
||||
if (current == mCurrentp)
|
||||
{
|
||||
mCurrentp = current->mForward[0];
|
||||
}
|
||||
|
||||
if (current == mCurrentOperatingp)
|
||||
{
|
||||
mCurrentOperatingp = current->mForward[0];
|
||||
}
|
||||
|
||||
// yes it is! change pointers as required
|
||||
for (level = 0; level < mLevel; level++)
|
||||
{
|
||||
if (mUpdate[level]->mForward[level] != current)
|
||||
{
|
||||
// cool, we've fixed all the pointers!
|
||||
break;
|
||||
}
|
||||
mUpdate[level]->mForward[level] = current->mForward[level];
|
||||
}
|
||||
|
||||
// clean up cuurent
|
||||
current->deleteData();
|
||||
delete current;
|
||||
|
||||
// clean up mHead
|
||||
while ( (mLevel > 1)
|
||||
&&(!mHead.mForward[mLevel - 1]))
|
||||
{
|
||||
mLevel--;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// remove all nodes from the list and delete data
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteAllData()
|
||||
{
|
||||
LLPtrSkipNode *temp;
|
||||
// reset mCurrentp
|
||||
mCurrentp = *(mHead.mForward);
|
||||
|
||||
while (mCurrentp)
|
||||
{
|
||||
temp = mCurrentp->mForward[0];
|
||||
mCurrentp->deleteData();
|
||||
delete mCurrentp;
|
||||
mCurrentp = temp;
|
||||
}
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// place mCurrentp on first node
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
|
||||
{
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = *mCurrentp->mForward;
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return 0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
// same as getCurrentData() but a more intuitive name for the operation
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = *mCurrentp->mForward;
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return 0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
// remove the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
removeData(mCurrentOperatingp->mData);
|
||||
}
|
||||
}
|
||||
|
||||
// delete the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteCurrentData()
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
deleteData(mCurrentOperatingp->mData);
|
||||
}
|
||||
}
|
||||
|
||||
// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
|
||||
{
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = mCurrentp->mForward[0];
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return 0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::corrupt()
|
||||
{
|
||||
LLPtrSkipNode *previous = mHead.mForward[0];
|
||||
|
||||
// Empty lists are not corrupt.
|
||||
if (!previous) return FALSE;
|
||||
|
||||
LLPtrSkipNode *current = previous->mForward[0];
|
||||
while(current)
|
||||
{
|
||||
if (!mInsertFirst(previous->mData, current->mData))
|
||||
{
|
||||
// prev shouldn't be in front of cur!
|
||||
return TRUE;
|
||||
}
|
||||
current = current->mForward[0];
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "llstl.h"
|
||||
#include "lltimer.h" // ms_sleep()
|
||||
#include "lltracethreadrecorder.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
|
@ -134,8 +135,8 @@ S32 LLQueuedThread::updateQueue(F32 max_time_ms)
|
|||
pending = getPending();
|
||||
if(pending > 0)
|
||||
{
|
||||
unpause();
|
||||
}
|
||||
unpause();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -403,6 +404,7 @@ S32 LLQueuedThread::processNextRequest()
|
|||
QueuedRequest *req;
|
||||
// Get next request from pool
|
||||
lockData();
|
||||
|
||||
while(1)
|
||||
{
|
||||
req = NULL;
|
||||
|
|
@ -467,10 +469,11 @@ S32 LLQueuedThread::processNextRequest()
|
|||
ms_sleep(1); // sleep the thread a little
|
||||
}
|
||||
}
|
||||
|
||||
LLTrace::get_thread_recorder()->pushToMaster();
|
||||
}
|
||||
|
||||
S32 pending = getPending();
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
|
|
@ -499,6 +502,7 @@ void LLQueuedThread::run()
|
|||
|
||||
if (isQuitting())
|
||||
{
|
||||
LLTrace::get_thread_recorder()->pushToMaster();
|
||||
endThread();
|
||||
break;
|
||||
}
|
||||
|
|
@ -507,8 +511,9 @@ void LLQueuedThread::run()
|
|||
|
||||
threadedUpdate();
|
||||
|
||||
int res = processNextRequest();
|
||||
if (res == 0)
|
||||
int pending_work = processNextRequest();
|
||||
|
||||
if (pending_work == 0)
|
||||
{
|
||||
mIdleThread = TRUE;
|
||||
ms_sleep(1);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
#include <list>
|
||||
|
||||
#include <boost/type_traits.hpp>
|
||||
#include "llsingleton.h"
|
||||
#include "llstl.h"
|
||||
|
||||
|
|
@ -47,12 +46,11 @@ template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultC
|
|||
class LLRegistry
|
||||
{
|
||||
public:
|
||||
typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
|
||||
typedef typename boost::add_reference<typename boost::add_const<KEY>::type>::type ref_const_key_t;
|
||||
typedef typename boost::add_reference<typename boost::add_const<VALUE>::type>::type ref_const_value_t;
|
||||
typedef typename boost::add_reference<VALUE>::type ref_value_t;
|
||||
typedef typename boost::add_pointer<typename boost::add_const<VALUE>::type>::type ptr_const_value_t;
|
||||
typedef typename boost::add_pointer<VALUE>::type ptr_value_t;
|
||||
typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
|
||||
typedef const KEY& ref_const_key_t;
|
||||
typedef const VALUE& ref_const_value_t;
|
||||
typedef const VALUE* ptr_const_value_t;
|
||||
typedef VALUE* ptr_value_t;
|
||||
|
||||
class Registrar
|
||||
{
|
||||
|
|
|
|||
|
|
@ -102,13 +102,13 @@ void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool
|
|||
//readSDValues(sd, block);
|
||||
}
|
||||
|
||||
void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block)
|
||||
void LLParamSDParser::writeSDImpl(LLSD& sd, const LLInitParam::BaseBlock& block, const LLInitParam::predicate_rule_t rules, const LLInitParam::BaseBlock* diff_block)
|
||||
{
|
||||
mNameStack.clear();
|
||||
mWriteRootSD = &sd;
|
||||
|
||||
name_stack_t name_stack;
|
||||
block.serializeBlock(*this, name_stack);
|
||||
block.serializeBlock(*this, name_stack, rules, diff_block);
|
||||
}
|
||||
|
||||
/*virtual*/ std::string LLParamSDParser::getCurrentElementName()
|
||||
|
|
@ -299,6 +299,7 @@ void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd)
|
|||
LLInitParam::Parser::name_stack_t stack = LLInitParam::Parser::name_stack_t();
|
||||
readSDValues(cb, sd, stack);
|
||||
}
|
||||
|
||||
namespace LLInitParam
|
||||
{
|
||||
// LLSD specialization
|
||||
|
|
@ -329,13 +330,14 @@ namespace LLInitParam
|
|||
p.writeValue<LLSD::String>(sd.asString(), name_stack);
|
||||
}
|
||||
|
||||
void ParamValue<LLSD, NOT_BLOCK>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const
|
||||
bool ParamValue<LLSD, NOT_BLOCK>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack_range, const predicate_rule_t predicate_rule, const BaseBlock* diff_block) const
|
||||
{
|
||||
// attempt to write LLSD out directly
|
||||
if (!p.writeValue<LLSD>(mValue, name_stack))
|
||||
if (!p.writeValue<LLSD>(mValue, name_stack_range))
|
||||
{
|
||||
// otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc)
|
||||
LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack);
|
||||
LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack_range);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "llinitparam.h"
|
||||
#include "boost/function.hpp"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
struct LL_COMMON_API LLParamSDParserUtilities
|
||||
{
|
||||
|
|
@ -50,11 +51,28 @@ typedef LLInitParam::Parser parser_t;
|
|||
public:
|
||||
LLParamSDParser();
|
||||
void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false);
|
||||
void writeSD(LLSD& sd, const LLInitParam::BaseBlock& block);
|
||||
template<typename BLOCK>
|
||||
void writeSD(LLSD& sd,
|
||||
const BLOCK& block,
|
||||
const LLInitParam::predicate_rule_t rules = LLInitParam::default_parse_rules(),
|
||||
const LLInitParam::BaseBlock* diff_block = NULL)
|
||||
{
|
||||
if (!diff_block
|
||||
&& !rules.isAmbivalent(LLInitParam::HAS_DEFAULT_VALUE))
|
||||
{
|
||||
diff_block = &LLInitParam::defaultValue<BLOCK>();
|
||||
}
|
||||
writeSDImpl(sd, block, rules, diff_block);
|
||||
}
|
||||
|
||||
/*virtual*/ std::string getCurrentElementName();
|
||||
|
||||
private:
|
||||
void writeSDImpl(LLSD& sd,
|
||||
const LLInitParam::BaseBlock& block,
|
||||
const LLInitParam::predicate_rule_t,
|
||||
const LLInitParam::BaseBlock* diff_block);
|
||||
|
||||
void submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack);
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -28,5 +28,4 @@
|
|||
|
||||
#include "llsingleton.h"
|
||||
|
||||
std::map<std::string, void *> * LLSingletonRegistry::sSingletonMap = NULL;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,38 +30,6 @@
|
|||
#include <typeinfo>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
/// @brief A global registry of all singletons to prevent duplicate allocations
|
||||
/// across shared library boundaries
|
||||
class LL_COMMON_API LLSingletonRegistry {
|
||||
private:
|
||||
typedef std::map<std::string, void *> TypeMap;
|
||||
static TypeMap * sSingletonMap;
|
||||
|
||||
static void checkInit()
|
||||
{
|
||||
if(sSingletonMap == NULL)
|
||||
{
|
||||
sSingletonMap = new TypeMap();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename T> static void * & get()
|
||||
{
|
||||
std::string name(typeid(T).name());
|
||||
|
||||
checkInit();
|
||||
|
||||
// the first entry of the pair returned by insert will be either the existing
|
||||
// iterator matching our key, or the newly inserted NULL initialized entry
|
||||
// see "Insert element" in http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html
|
||||
TypeMap::iterator result =
|
||||
sSingletonMap->insert(std::make_pair(name, (void*)NULL)).first;
|
||||
|
||||
return result->second;
|
||||
}
|
||||
};
|
||||
|
||||
// LLSingleton implements the getInstance() method part of the Singleton
|
||||
// pattern. It can't make the derived class constructors protected, though, so
|
||||
// you have to do that yourself.
|
||||
|
|
@ -99,22 +67,30 @@ private:
|
|||
INITIALIZED,
|
||||
DELETED
|
||||
} EInitState;
|
||||
|
||||
static DERIVED_TYPE* constructSingleton()
|
||||
{
|
||||
return new DERIVED_TYPE();
|
||||
}
|
||||
|
||||
// stores pointer to singleton instance
|
||||
// and tracks initialization state of singleton
|
||||
struct SingletonInstanceData
|
||||
struct SingletonLifetimeManager
|
||||
{
|
||||
EInitState mInitState;
|
||||
DERIVED_TYPE* mSingletonInstance;
|
||||
|
||||
SingletonInstanceData()
|
||||
: mSingletonInstance(NULL),
|
||||
mInitState(UNINITIALIZED)
|
||||
{}
|
||||
|
||||
~SingletonInstanceData()
|
||||
SingletonLifetimeManager()
|
||||
{
|
||||
if (mInitState != DELETED)
|
||||
construct();
|
||||
}
|
||||
|
||||
static void construct()
|
||||
{
|
||||
sData.mInitState = CONSTRUCTING;
|
||||
sData.mInstance = constructSingleton();
|
||||
sData.mInitState = INITIALIZING;
|
||||
}
|
||||
|
||||
~SingletonLifetimeManager()
|
||||
{
|
||||
if (sData.mInitState != DELETED)
|
||||
{
|
||||
deleteSingleton();
|
||||
}
|
||||
|
|
@ -124,9 +100,8 @@ private:
|
|||
public:
|
||||
virtual ~LLSingleton()
|
||||
{
|
||||
SingletonInstanceData& data = getData();
|
||||
data.mSingletonInstance = NULL;
|
||||
data.mInitState = DELETED;
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -151,50 +126,49 @@ public:
|
|||
*/
|
||||
static void deleteSingleton()
|
||||
{
|
||||
delete getData().mSingletonInstance;
|
||||
getData().mSingletonInstance = NULL;
|
||||
getData().mInitState = DELETED;
|
||||
delete sData.mInstance;
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
|
||||
static SingletonInstanceData& getData()
|
||||
{
|
||||
// this is static to cache the lookup results
|
||||
static void * & registry = LLSingletonRegistry::get<DERIVED_TYPE>();
|
||||
|
||||
// *TODO - look into making this threadsafe
|
||||
if(NULL == registry)
|
||||
{
|
||||
static SingletonInstanceData data;
|
||||
registry = &data;
|
||||
}
|
||||
|
||||
return *static_cast<SingletonInstanceData *>(registry);
|
||||
}
|
||||
|
||||
static DERIVED_TYPE* getInstance()
|
||||
{
|
||||
SingletonInstanceData& data = getData();
|
||||
static SingletonLifetimeManager sLifeTimeMgr;
|
||||
|
||||
if (data.mInitState == CONSTRUCTING)
|
||||
switch (sData.mInitState)
|
||||
{
|
||||
case UNINITIALIZED:
|
||||
// should never be uninitialized at this point
|
||||
llassert(false);
|
||||
return NULL;
|
||||
case CONSTRUCTING:
|
||||
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
|
||||
return NULL;
|
||||
case INITIALIZING:
|
||||
// go ahead and flag ourselves as initialized so we can be reentrant during initialization
|
||||
sData.mInitState = INITIALIZED;
|
||||
// initialize singleton after constructing it so that it can reference other singletons which in turn depend on it,
|
||||
// thus breaking cyclic dependencies
|
||||
sData.mInstance->initSingleton();
|
||||
return sData.mInstance;
|
||||
case INITIALIZED:
|
||||
return sData.mInstance;
|
||||
case DELETED:
|
||||
llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl;
|
||||
SingletonLifetimeManager::construct();
|
||||
// same as first time construction
|
||||
sData.mInitState = INITIALIZED;
|
||||
sData.mInstance->initSingleton();
|
||||
return sData.mInstance;
|
||||
}
|
||||
|
||||
if (data.mInitState == DELETED)
|
||||
{
|
||||
llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl;
|
||||
}
|
||||
|
||||
if (!data.mSingletonInstance)
|
||||
{
|
||||
data.mInitState = CONSTRUCTING;
|
||||
data.mSingletonInstance = new DERIVED_TYPE();
|
||||
data.mInitState = INITIALIZING;
|
||||
data.mSingletonInstance->initSingleton();
|
||||
data.mInitState = INITIALIZED;
|
||||
}
|
||||
|
||||
return data.mSingletonInstance;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DERIVED_TYPE* getIfExists()
|
||||
{
|
||||
return sData.mInstance;
|
||||
}
|
||||
|
||||
// Reference version of getInstance()
|
||||
|
|
@ -208,18 +182,31 @@ public:
|
|||
// Use this to avoid accessing singletons before the can safely be constructed
|
||||
static bool instanceExists()
|
||||
{
|
||||
return getData().mInitState == INITIALIZED;
|
||||
return sData.mInitState == INITIALIZED;
|
||||
}
|
||||
|
||||
// Has this singleton already been deleted?
|
||||
// Use this to avoid accessing singletons from a static object's destructor
|
||||
static bool destroyed()
|
||||
{
|
||||
return getData().mInitState == DELETED;
|
||||
return sData.mInitState == DELETED;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual void initSingleton() {}
|
||||
|
||||
struct SingletonData
|
||||
{
|
||||
// explicitly has a default constructor so that member variables are zero initialized in BSS
|
||||
// and only changed by singleton logic, not constructor running during startup
|
||||
EInitState mInitState;
|
||||
DERIVED_TYPE* mInstance;
|
||||
};
|
||||
static SingletonData sData;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
typename LLSingleton<T>::SingletonData LLSingleton<T>::sData;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,517 +0,0 @@
|
|||
/**
|
||||
* @file llskiplist.h
|
||||
* @brief skip list implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
#ifndef LL_LLSKIPLIST_H
|
||||
#define LL_LLSKIPLIST_H
|
||||
|
||||
#include "llrand.h"
|
||||
#include "llrand.h"
|
||||
|
||||
// NOTA BENE: Insert first needs to be < NOT <=
|
||||
// Binary depth must be >= 2
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH = 10>
|
||||
class LLSkipList
|
||||
{
|
||||
public:
|
||||
typedef BOOL (*compare)(const DATA_TYPE& first, const DATA_TYPE& second);
|
||||
typedef compare insert_func;
|
||||
typedef compare equals_func;
|
||||
|
||||
void init();
|
||||
|
||||
// basic constructor
|
||||
LLSkipList();
|
||||
|
||||
// basic constructor including sorter
|
||||
LLSkipList(insert_func insert_first, equals_func equals);
|
||||
~LLSkipList();
|
||||
|
||||
inline void setInsertFirst(insert_func insert_first);
|
||||
inline void setEquals(equals_func equals);
|
||||
|
||||
inline BOOL addData(const DATA_TYPE& data);
|
||||
inline BOOL checkData(const DATA_TYPE& data);
|
||||
|
||||
// returns number of items in the list
|
||||
inline S32 getLength() const; // NOT a constant time operation, traverses entire list!
|
||||
|
||||
inline BOOL moveData(const DATA_TYPE& data, LLSkipList *newlist);
|
||||
|
||||
inline BOOL removeData(const DATA_TYPE& data);
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
inline void removeAllNodes();
|
||||
|
||||
// place mCurrentp on first node
|
||||
inline void resetList();
|
||||
|
||||
// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
inline DATA_TYPE getCurrentData();
|
||||
|
||||
// same as getCurrentData() but a more intuitive name for the operation
|
||||
inline DATA_TYPE getNextData();
|
||||
|
||||
// remove the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
inline void removeCurrentData();
|
||||
|
||||
// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
inline DATA_TYPE getFirstData();
|
||||
|
||||
class LLSkipNode
|
||||
{
|
||||
public:
|
||||
LLSkipNode()
|
||||
: mData(0)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LLSkipNode(DATA_TYPE data)
|
||||
: mData(data)
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mForward[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
~LLSkipNode()
|
||||
{
|
||||
}
|
||||
|
||||
DATA_TYPE mData;
|
||||
LLSkipNode *mForward[BINARY_DEPTH];
|
||||
|
||||
private:
|
||||
// Disallow copying of LLSkipNodes by not implementing these methods.
|
||||
LLSkipNode(const LLSkipNode &);
|
||||
LLSkipNode &operator=(const LLSkipNode &);
|
||||
};
|
||||
|
||||
static BOOL defaultEquals(const DATA_TYPE& first, const DATA_TYPE& second)
|
||||
{
|
||||
return first == second;
|
||||
}
|
||||
|
||||
private:
|
||||
LLSkipNode mHead;
|
||||
LLSkipNode *mUpdate[BINARY_DEPTH];
|
||||
LLSkipNode *mCurrentp;
|
||||
LLSkipNode *mCurrentOperatingp;
|
||||
S32 mLevel;
|
||||
insert_func mInsertFirst;
|
||||
equals_func mEquals;
|
||||
|
||||
private:
|
||||
// Disallow copying of LLSkipNodes by not implementing these methods.
|
||||
LLSkipList(const LLSkipList &);
|
||||
LLSkipList &operator=(const LLSkipList &);
|
||||
};
|
||||
|
||||
|
||||
///////////////////////
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
|
||||
// Binary depth must be >= 2
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::init()
|
||||
{
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
mLevel = 1;
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
|
||||
// basic constructor
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList()
|
||||
: mInsertFirst(NULL),
|
||||
mEquals(defaultEquals)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
// basic constructor including sorter
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList(insert_func insert,
|
||||
equals_func equals)
|
||||
: mInsertFirst(insert),
|
||||
mEquals(equals)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::~LLSkipList()
|
||||
{
|
||||
removeAllNodes();
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(insert_func insert_first)
|
||||
{
|
||||
mInsertFirst = insert_first;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(equals_func equals)
|
||||
{
|
||||
mEquals = equals;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::addData(const DATA_TYPE& data)
|
||||
{
|
||||
S32 level;
|
||||
LLSkipNode *current = &mHead;
|
||||
LLSkipNode *temp;
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
// now add the new node
|
||||
S32 newlevel;
|
||||
for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
|
||||
{
|
||||
if (ll_frand() < 0.5f)
|
||||
break;
|
||||
}
|
||||
|
||||
LLSkipNode *snode = new LLSkipNode(data);
|
||||
|
||||
if (newlevel > mLevel)
|
||||
{
|
||||
mHead.mForward[mLevel] = NULL;
|
||||
mUpdate[mLevel] = &mHead;
|
||||
mLevel = newlevel;
|
||||
}
|
||||
|
||||
for (level = 0; level < newlevel; level++)
|
||||
{
|
||||
snode->mForward[level] = mUpdate[level]->mForward[level];
|
||||
mUpdate[level]->mForward[level] = snode;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE& data)
|
||||
{
|
||||
S32 level;
|
||||
LLSkipNode *current = &mHead;
|
||||
LLSkipNode *temp;
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
|
||||
if (current)
|
||||
{
|
||||
return mEquals(current->mData, data);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// returns number of items in the list
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline S32 LLSkipList<DATA_TYPE, BINARY_DEPTH>::getLength() const
|
||||
{
|
||||
U32 length = 0;
|
||||
for (LLSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
|
||||
{
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE& data, LLSkipList *newlist)
|
||||
{
|
||||
BOOL removed = removeData(data);
|
||||
BOOL added = newlist->addData(data);
|
||||
return removed && added;
|
||||
}
|
||||
|
||||
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE& data)
|
||||
{
|
||||
S32 level;
|
||||
LLSkipNode *current = &mHead;
|
||||
LLSkipNode *temp;
|
||||
// find the pointer one in front of the one we want
|
||||
if (mInsertFirst)
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(mInsertFirst(temp->mData, data)))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (level = mLevel - 1; level >= 0; level--)
|
||||
{
|
||||
temp = *(current->mForward + level);
|
||||
while ( (temp)
|
||||
&&(temp->mData < data))
|
||||
{
|
||||
current = temp;
|
||||
temp = *(current->mForward + level);
|
||||
}
|
||||
*(mUpdate + level) = current;
|
||||
}
|
||||
}
|
||||
// we're now just in front of where we want to be . . . take one step forward
|
||||
current = *current->mForward;
|
||||
|
||||
|
||||
if (!current)
|
||||
{
|
||||
// empty list or beyond the end!
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// is this the one we want?
|
||||
if (!mEquals(current->mData, data))
|
||||
{
|
||||
// nope!
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do we need to fix current or currentop?
|
||||
if (current == mCurrentp)
|
||||
{
|
||||
mCurrentp = current->mForward[0];
|
||||
}
|
||||
|
||||
if (current == mCurrentOperatingp)
|
||||
{
|
||||
mCurrentOperatingp = current->mForward[0];
|
||||
}
|
||||
// yes it is! change pointers as required
|
||||
for (level = 0; level < mLevel; level++)
|
||||
{
|
||||
if (mUpdate[level]->mForward[level] != current)
|
||||
{
|
||||
// cool, we've fixed all the pointers!
|
||||
break;
|
||||
}
|
||||
mUpdate[level]->mForward[level] = current->mForward[level];
|
||||
}
|
||||
|
||||
// clean up cuurent
|
||||
delete current;
|
||||
|
||||
// clean up mHead
|
||||
while ( (mLevel > 1)
|
||||
&&(!mHead.mForward[mLevel - 1]))
|
||||
{
|
||||
mLevel--;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// remove all nodes from the list but do not delete data
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
|
||||
{
|
||||
LLSkipNode *temp;
|
||||
// reset mCurrentp
|
||||
mCurrentp = *(mHead.mForward);
|
||||
|
||||
while (mCurrentp)
|
||||
{
|
||||
temp = mCurrentp->mForward[0];
|
||||
delete mCurrentp;
|
||||
mCurrentp = temp;
|
||||
}
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < BINARY_DEPTH; i++)
|
||||
{
|
||||
mHead.mForward[i] = NULL;
|
||||
mUpdate[i] = NULL;
|
||||
}
|
||||
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// place mCurrentp on first node
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
|
||||
{
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
}
|
||||
|
||||
// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = mCurrentp->mForward[0];
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return (DATA_TYPE)0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
// same as getCurrentData() but a more intuitive name for the operation
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
|
||||
{
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = mCurrentp->mForward[0];
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return (DATA_TYPE)0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
// remove the Node at mCurentOperatingp
|
||||
// leave mCurrentp and mCurentOperatingp on the next entry
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
|
||||
{
|
||||
if (mCurrentOperatingp)
|
||||
{
|
||||
removeData(mCurrentOperatingp->mData);
|
||||
}
|
||||
}
|
||||
|
||||
// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
|
||||
template <class DATA_TYPE, S32 BINARY_DEPTH>
|
||||
inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
|
||||
{
|
||||
mCurrentp = *(mHead.mForward);
|
||||
mCurrentOperatingp = *(mHead.mForward);
|
||||
if (mCurrentp)
|
||||
{
|
||||
mCurrentOperatingp = mCurrentp;
|
||||
mCurrentp = mCurrentp->mForward[0];
|
||||
return mCurrentOperatingp->mData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return NULL; // causes compile warning
|
||||
return (DATA_TYPE)0; // equivalent, but no warning
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,152 +0,0 @@
|
|||
/**
|
||||
* @file llsortedvector.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2012-04-08
|
||||
* @brief LLSortedVector class wraps a vector that we maintain in sorted
|
||||
* order so we can perform binary-search lookups.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Copyright (c) 2012, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLSORTEDVECTOR_H)
|
||||
#define LL_LLSORTEDVECTOR_H
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* LLSortedVector contains a std::vector<std::pair> that we keep sorted on the
|
||||
* first of the pair. This makes insertion somewhat more expensive than simple
|
||||
* std::vector::push_back(), but allows us to use binary search for lookups.
|
||||
* It's intended for small aggregates where lookup is far more performance-
|
||||
* critical than insertion; in such cases a binary search on a small, sorted
|
||||
* std::vector can be more performant than a std::map lookup.
|
||||
*/
|
||||
template <typename KEY, typename VALUE>
|
||||
class LLSortedVector
|
||||
{
|
||||
public:
|
||||
typedef LLSortedVector<KEY, VALUE> self;
|
||||
typedef KEY key_type;
|
||||
typedef VALUE mapped_type;
|
||||
typedef std::pair<key_type, mapped_type> value_type;
|
||||
typedef std::vector<value_type> PairVector;
|
||||
typedef typename PairVector::iterator iterator;
|
||||
typedef typename PairVector::const_iterator const_iterator;
|
||||
|
||||
/// Empty
|
||||
LLSortedVector() {}
|
||||
|
||||
/// Fixed initial size
|
||||
LLSortedVector(std::size_t size):
|
||||
mVector(size)
|
||||
{}
|
||||
|
||||
/// Bulk load
|
||||
template <typename ITER>
|
||||
LLSortedVector(ITER begin, ITER end):
|
||||
mVector(begin, end)
|
||||
{
|
||||
// Allow caller to dump in a bunch of (pairs convertible to)
|
||||
// value_type if desired, but make sure we sort afterwards.
|
||||
std::sort(mVector.begin(), mVector.end());
|
||||
}
|
||||
|
||||
/// insert(key, value)
|
||||
std::pair<iterator, bool> insert(const key_type& key, const mapped_type& value)
|
||||
{
|
||||
return insert(value_type(key, value));
|
||||
}
|
||||
|
||||
/// insert(value_type)
|
||||
std::pair<iterator, bool> insert(const value_type& pair)
|
||||
{
|
||||
typedef std::pair<iterator, bool> iterbool;
|
||||
iterator found = std::lower_bound(mVector.begin(), mVector.end(), pair,
|
||||
less<value_type>());
|
||||
// have to check for end() before it's even valid to dereference
|
||||
if (found == mVector.end())
|
||||
{
|
||||
std::size_t index(mVector.size());
|
||||
mVector.push_back(pair);
|
||||
// don't forget that push_back() invalidates 'found'
|
||||
return iterbool(mVector.begin() + index, true);
|
||||
}
|
||||
if (found->first == pair.first)
|
||||
{
|
||||
return iterbool(found, false);
|
||||
}
|
||||
// remember that insert() invalidates 'found' -- save index
|
||||
std::size_t index(found - mVector.begin());
|
||||
mVector.insert(found, pair);
|
||||
// okay, convert from index back to iterator
|
||||
return iterbool(mVector.begin() + index, true);
|
||||
}
|
||||
|
||||
iterator begin() { return mVector.begin(); }
|
||||
iterator end() { return mVector.end(); }
|
||||
const_iterator begin() const { return mVector.begin(); }
|
||||
const_iterator end() const { return mVector.end(); }
|
||||
|
||||
bool empty() const { return mVector.empty(); }
|
||||
std::size_t size() const { return mVector.size(); }
|
||||
|
||||
/// find
|
||||
iterator find(const key_type& key)
|
||||
{
|
||||
iterator found = std::lower_bound(mVector.begin(), mVector.end(),
|
||||
value_type(key, mapped_type()),
|
||||
less<value_type>());
|
||||
if (found == mVector.end() || found->first != key)
|
||||
return mVector.end();
|
||||
return found;
|
||||
}
|
||||
|
||||
const_iterator find(const key_type& key) const
|
||||
{
|
||||
return const_cast<self*>(this)->find(key);
|
||||
}
|
||||
|
||||
private:
|
||||
// Define our own 'less' comparator so we can specialize without messing
|
||||
// with std::less.
|
||||
template <typename T>
|
||||
struct less: public std::less<T> {};
|
||||
|
||||
// Specialize 'less' for an LLSortedVector::value_type involving
|
||||
// std::type_info*. This is one of LLSortedVector's foremost use cases. We
|
||||
// specialize 'less' rather than just defining a specific comparator
|
||||
// because LLSortedVector should be usable for other key_types as well.
|
||||
template <typename T>
|
||||
struct less< std::pair<std::type_info*, T> >:
|
||||
public std::binary_function<std::pair<std::type_info*, T>,
|
||||
std::pair<std::type_info*, T>,
|
||||
bool>
|
||||
{
|
||||
bool operator()(const std::pair<std::type_info*, T>& lhs,
|
||||
const std::pair<std::type_info*, T>& rhs) const
|
||||
{
|
||||
return lhs.first->before(*rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
// Same as above, but with const std::type_info*.
|
||||
template <typename T>
|
||||
struct less< std::pair<const std::type_info*, T> >:
|
||||
public std::binary_function<std::pair<const std::type_info*, T>,
|
||||
std::pair<const std::type_info*, T>,
|
||||
bool>
|
||||
{
|
||||
bool operator()(const std::pair<const std::type_info*, T>& lhs,
|
||||
const std::pair<const std::type_info*, T>& rhs) const
|
||||
{
|
||||
return lhs.first->before(*rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
PairVector mVector;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLSORTEDVECTOR_H) */
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* @file llstack.h
|
||||
* @brief LLStack template class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLSTACK_H
|
||||
#define LL_LLSTACK_H
|
||||
|
||||
#include "linked_lists.h"
|
||||
|
||||
template <class DATA_TYPE> class LLStack
|
||||
{
|
||||
private:
|
||||
LLLinkedList<DATA_TYPE> mStack;
|
||||
|
||||
public:
|
||||
LLStack() {}
|
||||
~LLStack() {}
|
||||
|
||||
void push(DATA_TYPE *data) { mStack.addData(data); }
|
||||
DATA_TYPE *pop() { DATA_TYPE *tempp = mStack.getFirstData(); mStack.removeCurrentData(); return tempp; }
|
||||
void deleteAllData() { mStack.deleteAllData(); }
|
||||
void removeAllNodes() { mStack.removeAllNodes(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "windows.h"
|
||||
#include "llwin32headerslean.h"
|
||||
#include "Dbghelp.h"
|
||||
|
||||
typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
|
||||
|
|
|
|||
|
|
@ -1,404 +0,0 @@
|
|||
/**
|
||||
* @file llstat.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llstat.h"
|
||||
#include "lllivefile.h"
|
||||
#include "llerrorcontrol.h"
|
||||
#include "llframetimer.h"
|
||||
#include "timing.h"
|
||||
#include "llsd.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llstl.h"
|
||||
#include "u64.h"
|
||||
|
||||
|
||||
// statics
|
||||
//------------------------------------------------------------------------
|
||||
LLTimer LLStat::sTimer;
|
||||
LLFrameTimer LLStat::sFrameTimer;
|
||||
|
||||
void LLStat::reset()
|
||||
{
|
||||
mNumValues = 0;
|
||||
mLastValue = 0.f;
|
||||
delete[] mBins;
|
||||
mBins = new ValueEntry[mNumBins];
|
||||
mCurBin = mNumBins-1;
|
||||
mNextBin = 0;
|
||||
}
|
||||
|
||||
LLStat::LLStat(std::string name, S32 num_bins, BOOL use_frame_timer)
|
||||
: mUseFrameTimer(use_frame_timer),
|
||||
mNumBins(num_bins),
|
||||
mName(name),
|
||||
mBins(NULL)
|
||||
{
|
||||
llassert(mNumBins > 0);
|
||||
mLastTime = 0.f;
|
||||
|
||||
reset();
|
||||
|
||||
if (!mName.empty())
|
||||
{
|
||||
stat_map_t::iterator iter = getStatList().find(mName);
|
||||
if (iter != getStatList().end())
|
||||
llwarns << "LLStat with duplicate name: " << mName << llendl;
|
||||
getStatList().insert(std::make_pair(mName, this));
|
||||
}
|
||||
}
|
||||
|
||||
LLStat::stat_map_t& LLStat::getStatList()
|
||||
{
|
||||
static LLStat::stat_map_t stat_list;
|
||||
return stat_list;
|
||||
}
|
||||
|
||||
|
||||
LLStat::~LLStat()
|
||||
{
|
||||
delete[] mBins;
|
||||
|
||||
if (!mName.empty())
|
||||
{
|
||||
// handle multiple entries with the same name
|
||||
stat_map_t::iterator iter = getStatList().find(mName);
|
||||
while (iter != getStatList().end() && iter->second != this)
|
||||
++iter;
|
||||
getStatList().erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void LLStat::start()
|
||||
{
|
||||
if (mUseFrameTimer)
|
||||
{
|
||||
mBins[mNextBin].mBeginTime = sFrameTimer.getElapsedSeconds();
|
||||
}
|
||||
else
|
||||
{
|
||||
mBins[mNextBin].mBeginTime = sTimer.getElapsedTimeF64();
|
||||
}
|
||||
}
|
||||
|
||||
void LLStat::addValue(const F32 value)
|
||||
{
|
||||
if (mNumValues < mNumBins)
|
||||
{
|
||||
mNumValues++;
|
||||
}
|
||||
|
||||
// Increment the bin counters.
|
||||
mCurBin++;
|
||||
if (mCurBin >= mNumBins)
|
||||
{
|
||||
mCurBin = 0;
|
||||
}
|
||||
mNextBin++;
|
||||
if (mNextBin >= mNumBins)
|
||||
{
|
||||
mNextBin = 0;
|
||||
}
|
||||
|
||||
mBins[mCurBin].mValue = value;
|
||||
if (mUseFrameTimer)
|
||||
{
|
||||
mBins[mCurBin].mTime = sFrameTimer.getElapsedSeconds();
|
||||
}
|
||||
else
|
||||
{
|
||||
mBins[mCurBin].mTime = sTimer.getElapsedTimeF64();
|
||||
}
|
||||
mBins[mCurBin].mDT = (F32)(mBins[mCurBin].mTime - mBins[mCurBin].mBeginTime);
|
||||
|
||||
//this value is used to prime the min/max calls
|
||||
mLastTime = mBins[mCurBin].mTime;
|
||||
mLastValue = value;
|
||||
|
||||
// Set the begin time for the next stat segment.
|
||||
mBins[mNextBin].mBeginTime = mBins[mCurBin].mTime;
|
||||
mBins[mNextBin].mTime = mBins[mCurBin].mTime;
|
||||
mBins[mNextBin].mDT = 0.f;
|
||||
}
|
||||
|
||||
|
||||
F32 LLStat::getMax() const
|
||||
{
|
||||
S32 i;
|
||||
F32 current_max = mLastValue;
|
||||
if (mNumBins == 0)
|
||||
{
|
||||
current_max = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
|
||||
{
|
||||
// Skip the bin we're currently filling.
|
||||
if (i == mNextBin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (mBins[i].mValue > current_max)
|
||||
{
|
||||
current_max = mBins[i].mValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return current_max;
|
||||
}
|
||||
|
||||
F32 LLStat::getMean() const
|
||||
{
|
||||
S32 i;
|
||||
F32 current_mean = 0.f;
|
||||
S32 samples = 0;
|
||||
for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
|
||||
{
|
||||
// Skip the bin we're currently filling.
|
||||
if (i == mNextBin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
current_mean += mBins[i].mValue;
|
||||
samples++;
|
||||
}
|
||||
|
||||
// There will be a wrap error at 2^32. :)
|
||||
if (samples != 0)
|
||||
{
|
||||
current_mean /= samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_mean = 0.f;
|
||||
}
|
||||
return current_mean;
|
||||
}
|
||||
|
||||
F32 LLStat::getMin() const
|
||||
{
|
||||
S32 i;
|
||||
F32 current_min = mLastValue;
|
||||
|
||||
if (mNumBins == 0)
|
||||
{
|
||||
current_min = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
|
||||
{
|
||||
// Skip the bin we're currently filling.
|
||||
if (i == mNextBin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (mBins[i].mValue < current_min)
|
||||
{
|
||||
current_min = mBins[i].mValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return current_min;
|
||||
}
|
||||
|
||||
F32 LLStat::getPrev(S32 age) const
|
||||
{
|
||||
S32 bin;
|
||||
bin = mCurBin - age;
|
||||
|
||||
while (bin < 0)
|
||||
{
|
||||
bin += mNumBins;
|
||||
}
|
||||
|
||||
if (bin == mNextBin)
|
||||
{
|
||||
// Bogus for bin we're currently working on.
|
||||
return 0.f;
|
||||
}
|
||||
return mBins[bin].mValue;
|
||||
}
|
||||
|
||||
F32 LLStat::getPrevPerSec(S32 age) const
|
||||
{
|
||||
S32 bin;
|
||||
bin = mCurBin - age;
|
||||
|
||||
while (bin < 0)
|
||||
{
|
||||
bin += mNumBins;
|
||||
}
|
||||
|
||||
if (bin == mNextBin)
|
||||
{
|
||||
// Bogus for bin we're currently working on.
|
||||
return 0.f;
|
||||
}
|
||||
return mBins[bin].mValue / mBins[bin].mDT;
|
||||
}
|
||||
|
||||
F32 LLStat::getCurrent() const
|
||||
{
|
||||
return mBins[mCurBin].mValue;
|
||||
}
|
||||
|
||||
F32 LLStat::getCurrentPerSec() const
|
||||
{
|
||||
return mBins[mCurBin].mValue / mBins[mCurBin].mDT;
|
||||
}
|
||||
|
||||
F32 LLStat::getMeanPerSec() const
|
||||
{
|
||||
S32 i;
|
||||
F32 value = 0.f;
|
||||
F32 dt = 0.f;
|
||||
|
||||
for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
|
||||
{
|
||||
// Skip the bin we're currently filling.
|
||||
if (i == mNextBin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
value += mBins[i].mValue;
|
||||
dt += mBins[i].mDT;
|
||||
}
|
||||
|
||||
if (dt > 0.f)
|
||||
{
|
||||
return value/dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
F32 LLStat::getMeanDuration() const
|
||||
{
|
||||
F32 dur = 0.0f;
|
||||
S32 count = 0;
|
||||
for (S32 i=0; (i < mNumBins) && (i < mNumValues); i++)
|
||||
{
|
||||
if (i == mNextBin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
dur += mBins[i].mDT;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
dur /= F32(count);
|
||||
return dur;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
F32 LLStat::getMaxPerSec() const
|
||||
{
|
||||
F32 value;
|
||||
|
||||
if (mNextBin != 0)
|
||||
{
|
||||
value = mBins[0].mValue/mBins[0].mDT;
|
||||
}
|
||||
else if (mNumValues > 0)
|
||||
{
|
||||
value = mBins[1].mValue/mBins[1].mDT;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0.f;
|
||||
}
|
||||
|
||||
for (S32 i = 0; (i < mNumBins) && (i < mNumValues); i++)
|
||||
{
|
||||
// Skip the bin we're currently filling.
|
||||
if (i == mNextBin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
value = llmax(value, mBins[i].mValue/mBins[i].mDT);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
F32 LLStat::getMinPerSec() const
|
||||
{
|
||||
S32 i;
|
||||
F32 value;
|
||||
|
||||
if (mNextBin != 0)
|
||||
{
|
||||
value = mBins[0].mValue/mBins[0].mDT;
|
||||
}
|
||||
else if (mNumValues > 0)
|
||||
{
|
||||
value = mBins[1].mValue/mBins[0].mDT;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0.f;
|
||||
}
|
||||
|
||||
for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
|
||||
{
|
||||
// Skip the bin we're currently filling.
|
||||
if (i == mNextBin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
value = llmin(value, mBins[i].mValue/mBins[i].mDT);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
U32 LLStat::getNumValues() const
|
||||
{
|
||||
return mNumValues;
|
||||
}
|
||||
|
||||
S32 LLStat::getNumBins() const
|
||||
{
|
||||
return mNumBins;
|
||||
}
|
||||
|
||||
S32 LLStat::getNextBin() const
|
||||
{
|
||||
return mNextBin;
|
||||
}
|
||||
|
||||
F64 LLStat::getLastTime() const
|
||||
{
|
||||
return mLastTime;
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
/**
|
||||
* @file llstat.h
|
||||
* @brief Runtime statistics accumulation.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLSTAT_H
|
||||
#define LL_LLSTAT_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "lltimer.h"
|
||||
#include "llframetimer.h"
|
||||
|
||||
class LLSD;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
class LL_COMMON_API LLStat
|
||||
{
|
||||
private:
|
||||
typedef std::multimap<std::string, LLStat*> stat_map_t;
|
||||
|
||||
static stat_map_t& getStatList();
|
||||
|
||||
public:
|
||||
LLStat(std::string name = std::string(), S32 num_bins = 32, BOOL use_frame_timer = FALSE);
|
||||
~LLStat();
|
||||
|
||||
void start(); // Start the timer for the current "frame", otherwise uses the time tracked from
|
||||
// the last addValue
|
||||
void reset();
|
||||
void addValue(const F32 value = 1.f); // Adds the current value being tracked, and tracks the DT.
|
||||
void addValue(const S32 value) { addValue((F32)value); }
|
||||
void addValue(const U32 value) { addValue((F32)value); }
|
||||
|
||||
S32 getNextBin() const;
|
||||
|
||||
F32 getPrev(S32 age) const; // Age is how many "addValues" previously - zero is current
|
||||
F32 getPrevPerSec(S32 age) const; // Age is how many "addValues" previously - zero is current
|
||||
F32 getCurrent() const;
|
||||
F32 getCurrentPerSec() const;
|
||||
|
||||
F32 getMin() const;
|
||||
F32 getMinPerSec() const;
|
||||
F32 getMean() const;
|
||||
F32 getMeanPerSec() const;
|
||||
F32 getMeanDuration() const;
|
||||
F32 getMax() const;
|
||||
F32 getMaxPerSec() const;
|
||||
|
||||
U32 getNumValues() const;
|
||||
S32 getNumBins() const;
|
||||
|
||||
F64 getLastTime() const;
|
||||
private:
|
||||
BOOL mUseFrameTimer;
|
||||
U32 mNumValues;
|
||||
U32 mNumBins;
|
||||
F32 mLastValue;
|
||||
F64 mLastTime;
|
||||
|
||||
struct ValueEntry
|
||||
{
|
||||
ValueEntry()
|
||||
: mValue(0.f),
|
||||
mBeginTime(0.0),
|
||||
mTime(0.0),
|
||||
mDT(0.f)
|
||||
{}
|
||||
F32 mValue;
|
||||
F64 mBeginTime;
|
||||
F64 mTime;
|
||||
F32 mDT;
|
||||
};
|
||||
ValueEntry* mBins;
|
||||
S32 mCurBin;
|
||||
S32 mNextBin;
|
||||
|
||||
std::string mName;
|
||||
|
||||
static LLTimer sTimer;
|
||||
static LLFrameTimer sFrameTimer;
|
||||
|
||||
public:
|
||||
static LLStat* getStat(const std::string& name)
|
||||
{
|
||||
// return the first stat that matches 'name'
|
||||
stat_map_t::iterator iter = getStatList().find(name);
|
||||
if (iter != getStatList().end())
|
||||
return iter->second;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // LL_STAT_
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
#ifndef LL_LLSTATENUMS_H
|
||||
#define LL_LLSTATENUMS_H
|
||||
|
||||
enum
|
||||
enum ESimStatID
|
||||
{
|
||||
LL_SIM_STAT_TIME_DILATION = 0,
|
||||
LL_SIM_STAT_FPS = 1,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <typeinfo>
|
||||
|
|
|
|||
|
|
@ -28,11 +28,10 @@
|
|||
|
||||
#include "llstring.h"
|
||||
#include "llerror.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include "llwin32headerslean.h"
|
||||
#include <winnls.h> // for WideCharToMultiByte
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@
|
|||
#include <cstdio>
|
||||
#include <locale>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include "llsd.h"
|
||||
#include "llfasttimer.h"
|
||||
#include "llformat.h"
|
||||
|
||||
#if LL_LINUX || LL_SOLARIS
|
||||
#include <wctype.h>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "llprocessor.h"
|
||||
#include "llerrorcontrol.h"
|
||||
#include "llevents.h"
|
||||
#include "llformat.h"
|
||||
#include "lltimer.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llsdutil.h"
|
||||
|
|
@ -58,9 +59,7 @@
|
|||
using namespace llsd;
|
||||
|
||||
#if LL_WINDOWS
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
# include "llwin32headerslean.h"
|
||||
# include <psapi.h> // GetPerformanceInfo() et al.
|
||||
#elif LL_DARWIN
|
||||
# include <errno.h>
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@
|
|||
#include "apr_portable.h"
|
||||
|
||||
#include "llthread.h"
|
||||
#include "llmutex.h"
|
||||
|
||||
#include "lltimer.h"
|
||||
#include "lltrace.h"
|
||||
#include "lltracethreadrecorder.h"
|
||||
|
||||
#if LL_LINUX || LL_SOLARIS
|
||||
#include <sched.h>
|
||||
|
|
@ -56,12 +59,17 @@
|
|||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#if !LL_DARWIN
|
||||
U32 ll_thread_local sThreadID = 0;
|
||||
#if LL_DARWIN
|
||||
// statically allocated thread local storage not supported in Darwin executable formats
|
||||
#elif LL_WINDOWS
|
||||
U32 __declspec(thread) sThreadID = 0;
|
||||
#elif LL_LINUX
|
||||
U32 __thread sThreadID = 0;
|
||||
#endif
|
||||
|
||||
U32 LLThread::sIDIter = 0;
|
||||
|
||||
|
||||
LL_COMMON_API void assert_main_thread()
|
||||
{
|
||||
static U32 s_thread_id = LLThread::currentID();
|
||||
|
|
@ -85,6 +93,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
|
|||
{
|
||||
LLThread *threadp = (LLThread *)datap;
|
||||
|
||||
LLTrace::ThreadRecorder* thread_recorder = new LLTrace::SlaveThreadRecorder(LLTrace::getUIThreadRecorder());
|
||||
|
||||
#if !LL_DARWIN
|
||||
sThreadID = threadp->mID;
|
||||
#endif
|
||||
|
|
@ -97,16 +107,18 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
|
|||
// We're done with the run function, this thread is done executing now.
|
||||
threadp->mStatus = STOPPED;
|
||||
|
||||
delete thread_recorder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
|
||||
mPaused(FALSE),
|
||||
mName(name),
|
||||
mAPRThreadp(NULL),
|
||||
mStatus(STOPPED)
|
||||
{
|
||||
|
||||
mID = ++sIDIter;
|
||||
|
||||
// Thread creation probably CAN be paranoid about APR being initialized, if necessary
|
||||
|
|
@ -152,7 +164,7 @@ void LLThread::shutdown()
|
|||
//llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl;
|
||||
// Now wait a bit for the thread to exit
|
||||
// It's unclear whether I should even bother doing this - this destructor
|
||||
// should netver get called unless we're already stopped, really...
|
||||
// should never get called unless we're already stopped, really...
|
||||
S32 counter = 0;
|
||||
const S32 MAX_WAIT = 600;
|
||||
while (counter < MAX_WAIT)
|
||||
|
|
@ -282,7 +294,13 @@ void LLThread::setQuitting()
|
|||
// static
|
||||
U32 LLThread::currentID()
|
||||
{
|
||||
#if LL_DARWIN
|
||||
// statically allocated thread local storage not supported in Darwin executable formats
|
||||
return (U32)apr_os_thread_current();
|
||||
#else
|
||||
return sThreadID;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
@ -315,155 +333,6 @@ void LLThread::wakeLocked()
|
|||
|
||||
//============================================================================
|
||||
|
||||
LLMutex::LLMutex(apr_pool_t *poolp) :
|
||||
mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD)
|
||||
{
|
||||
//if (poolp)
|
||||
//{
|
||||
// mIsLocalPool = FALSE;
|
||||
// mAPRPoolp = poolp;
|
||||
//}
|
||||
//else
|
||||
{
|
||||
mIsLocalPool = TRUE;
|
||||
apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
|
||||
}
|
||||
apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_UNNESTED, mAPRPoolp);
|
||||
}
|
||||
|
||||
|
||||
LLMutex::~LLMutex()
|
||||
{
|
||||
#if MUTEX_DEBUG
|
||||
//bad assertion, the subclass LLSignal might be "locked", and that's OK
|
||||
//llassert_always(!isLocked()); // better not be locked!
|
||||
#endif
|
||||
apr_thread_mutex_destroy(mAPRMutexp);
|
||||
mAPRMutexp = NULL;
|
||||
if (mIsLocalPool)
|
||||
{
|
||||
apr_pool_destroy(mAPRPoolp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLMutex::lock()
|
||||
{
|
||||
if(isSelfLocked())
|
||||
{ //redundant lock
|
||||
mCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
apr_thread_mutex_lock(mAPRMutexp);
|
||||
|
||||
#if MUTEX_DEBUG
|
||||
// Have to have the lock before we can access the debug info
|
||||
U32 id = LLThread::currentID();
|
||||
if (mIsLocked[id] != FALSE)
|
||||
llerrs << "Already locked in Thread: " << id << llendl;
|
||||
mIsLocked[id] = TRUE;
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
mLockingThread = LLThread::currentID();
|
||||
#else
|
||||
mLockingThread = sThreadID;
|
||||
#endif
|
||||
}
|
||||
|
||||
void LLMutex::unlock()
|
||||
{
|
||||
if (mCount > 0)
|
||||
{ //not the root unlock
|
||||
mCount--;
|
||||
return;
|
||||
}
|
||||
|
||||
#if MUTEX_DEBUG
|
||||
// Access the debug info while we have the lock
|
||||
U32 id = LLThread::currentID();
|
||||
if (mIsLocked[id] != TRUE)
|
||||
llerrs << "Not locked in Thread: " << id << llendl;
|
||||
mIsLocked[id] = FALSE;
|
||||
#endif
|
||||
|
||||
mLockingThread = NO_THREAD;
|
||||
apr_thread_mutex_unlock(mAPRMutexp);
|
||||
}
|
||||
|
||||
bool LLMutex::isLocked()
|
||||
{
|
||||
apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp);
|
||||
if (APR_STATUS_IS_EBUSY(status))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
apr_thread_mutex_unlock(mAPRMutexp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LLMutex::isSelfLocked()
|
||||
{
|
||||
#if LL_DARWIN
|
||||
return mLockingThread == LLThread::currentID();
|
||||
#else
|
||||
return mLockingThread == sThreadID;
|
||||
#endif
|
||||
}
|
||||
|
||||
U32 LLMutex::lockingThread() const
|
||||
{
|
||||
return mLockingThread;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
LLCondition::LLCondition(apr_pool_t *poolp) :
|
||||
LLMutex(poolp)
|
||||
{
|
||||
// base class (LLMutex) has already ensured that mAPRPoolp is set up.
|
||||
|
||||
apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
|
||||
}
|
||||
|
||||
|
||||
LLCondition::~LLCondition()
|
||||
{
|
||||
apr_thread_cond_destroy(mAPRCondp);
|
||||
mAPRCondp = NULL;
|
||||
}
|
||||
|
||||
|
||||
void LLCondition::wait()
|
||||
{
|
||||
if (!isLocked())
|
||||
{ //mAPRMutexp MUST be locked before calling apr_thread_cond_wait
|
||||
apr_thread_mutex_lock(mAPRMutexp);
|
||||
#if MUTEX_DEBUG
|
||||
// avoid asserts on destruction in non-release builds
|
||||
U32 id = LLThread::currentID();
|
||||
mIsLocked[id] = TRUE;
|
||||
#endif
|
||||
}
|
||||
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
|
||||
}
|
||||
|
||||
void LLCondition::signal()
|
||||
{
|
||||
apr_thread_cond_signal(mAPRCondp);
|
||||
}
|
||||
|
||||
void LLCondition::broadcast()
|
||||
{
|
||||
apr_thread_cond_broadcast(mAPRCondp);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//static
|
||||
|
|
@ -514,7 +383,6 @@ LLThreadSafeRefCount::~LLThreadSafeRefCount()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
LLResponder::~LLResponder()
|
||||
|
|
|
|||
|
|
@ -31,20 +31,14 @@
|
|||
#include "llapr.h"
|
||||
#include "apr_thread_cond.h"
|
||||
#include "boost/intrusive_ptr.hpp"
|
||||
#include "llmutex.h"
|
||||
|
||||
class LLThread;
|
||||
class LLMutex;
|
||||
class LLCondition;
|
||||
|
||||
#if LL_WINDOWS
|
||||
#define ll_thread_local __declspec(thread)
|
||||
#else
|
||||
#define ll_thread_local __thread
|
||||
#endif
|
||||
LL_COMMON_API void assert_main_thread();
|
||||
|
||||
class LL_COMMON_API LLThread
|
||||
{
|
||||
private:
|
||||
friend class LLMutex;
|
||||
static U32 sIDIter;
|
||||
|
||||
public:
|
||||
|
|
@ -98,15 +92,15 @@ private:
|
|||
BOOL mPaused;
|
||||
|
||||
// static function passed to APR thread creation routine
|
||||
static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
|
||||
static void *APR_THREAD_FUNC staticRun(struct apr_thread_t *apr_threadp, void *datap);
|
||||
|
||||
protected:
|
||||
std::string mName;
|
||||
LLCondition* mRunCondition;
|
||||
class LLCondition* mRunCondition;
|
||||
LLMutex* mDataLock;
|
||||
|
||||
apr_thread_t *mAPRThreadp;
|
||||
apr_pool_t *mAPRPoolp;
|
||||
apr_thread_t* mAPRThreadp;
|
||||
apr_pool_t* mAPRPoolp;
|
||||
BOOL mIsLocalPool;
|
||||
EThreadStatus mStatus;
|
||||
U32 mID;
|
||||
|
|
@ -114,7 +108,7 @@ protected:
|
|||
//a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used.
|
||||
//Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes.
|
||||
// otherwise it will cause severe memory leaking!!! --bao
|
||||
LLVolatileAPRPool *mLocalAPRFilePoolp ;
|
||||
LLVolatileAPRPool* mLocalAPRFilePoolp ;
|
||||
|
||||
void setQuitting();
|
||||
|
||||
|
|
@ -140,75 +134,6 @@ protected:
|
|||
// mDataLock->unlock();
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
|
||||
|
||||
class LL_COMMON_API LLMutex
|
||||
{
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
NO_THREAD = 0xFFFFFFFF
|
||||
} e_locking_thread;
|
||||
|
||||
LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
|
||||
virtual ~LLMutex();
|
||||
|
||||
void lock(); // blocks
|
||||
void unlock();
|
||||
bool isLocked(); // non-blocking, but does do a lock/unlock so not free
|
||||
bool isSelfLocked(); //return true if locked in a same thread
|
||||
U32 lockingThread() const; //get ID of locking thread
|
||||
|
||||
protected:
|
||||
apr_thread_mutex_t *mAPRMutexp;
|
||||
mutable U32 mCount;
|
||||
mutable U32 mLockingThread;
|
||||
|
||||
apr_pool_t *mAPRPoolp;
|
||||
BOOL mIsLocalPool;
|
||||
|
||||
#if MUTEX_DEBUG
|
||||
std::map<U32, BOOL> mIsLocked;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
|
||||
class LL_COMMON_API LLCondition : public LLMutex
|
||||
{
|
||||
public:
|
||||
LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
|
||||
~LLCondition();
|
||||
|
||||
void wait(); // blocks
|
||||
void signal();
|
||||
void broadcast();
|
||||
|
||||
protected:
|
||||
apr_thread_cond_t *mAPRCondp;
|
||||
};
|
||||
|
||||
class LLMutexLock
|
||||
{
|
||||
public:
|
||||
LLMutexLock(LLMutex* mutex)
|
||||
{
|
||||
mMutex = mutex;
|
||||
|
||||
if(mMutex)
|
||||
mMutex->lock();
|
||||
}
|
||||
~LLMutexLock()
|
||||
{
|
||||
if(mMutex)
|
||||
mMutex->unlock();
|
||||
}
|
||||
private:
|
||||
LLMutex* mMutex;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
void LLThread::lockData()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* @file llthreadlocalstorage.cpp
|
||||
* @author Richard
|
||||
* @date 2013-1-11
|
||||
* @brief implementation of thread local storage utility classes
|
||||
*
|
||||
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llthreadlocalstorage.h"
|
||||
#include "llapr.h"
|
||||
|
||||
//
|
||||
//LLThreadLocalPointerBase
|
||||
//
|
||||
bool LLThreadLocalPointerBase::sInitialized = false;
|
||||
|
||||
void LLThreadLocalPointerBase::set( void* value )
|
||||
{
|
||||
llassert(sInitialized && mThreadKey);
|
||||
|
||||
apr_status_t result = apr_threadkey_private_set((void*)value, mThreadKey);
|
||||
if (result != APR_SUCCESS)
|
||||
{
|
||||
ll_apr_warn_status(result);
|
||||
llerrs << "Failed to set thread local data" << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
void* LLThreadLocalPointerBase::get() const
|
||||
{
|
||||
// llassert(sInitialized);
|
||||
void* ptr;
|
||||
apr_status_t result =
|
||||
apr_threadkey_private_get(&ptr, mThreadKey);
|
||||
if (result != APR_SUCCESS)
|
||||
{
|
||||
ll_apr_warn_status(result);
|
||||
llerrs << "Failed to get thread local data" << llendl;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
void LLThreadLocalPointerBase::initStorage( )
|
||||
{
|
||||
apr_status_t result = apr_threadkey_private_create(&mThreadKey, NULL, gAPRPoolp);
|
||||
if (result != APR_SUCCESS)
|
||||
{
|
||||
ll_apr_warn_status(result);
|
||||
llerrs << "Failed to allocate thread local data" << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
void LLThreadLocalPointerBase::destroyStorage()
|
||||
{
|
||||
if (sInitialized)
|
||||
{
|
||||
if (mThreadKey)
|
||||
{
|
||||
apr_status_t result = apr_threadkey_private_delete(mThreadKey);
|
||||
if (result != APR_SUCCESS)
|
||||
{
|
||||
ll_apr_warn_status(result);
|
||||
llerrs << "Failed to delete thread local data" << llendl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLThreadLocalPointerBase::initAllThreadLocalStorage()
|
||||
{
|
||||
if (!sInitialized)
|
||||
{
|
||||
for (LLInstanceTracker<LLThreadLocalPointerBase>::instance_iter it = beginInstances(), end_it = endInstances();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
(*it).initStorage();
|
||||
}
|
||||
sInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void LLThreadLocalPointerBase::destroyAllThreadLocalStorage()
|
||||
{
|
||||
if (sInitialized)
|
||||
{
|
||||
//for (LLInstanceTracker<LLThreadLocalPointerBase>::instance_iter it = beginInstances(), end_it = endInstances();
|
||||
// it != end_it;
|
||||
// ++it)
|
||||
//{
|
||||
// (*it).destroyStorage();
|
||||
//}
|
||||
sInitialized = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
/**
|
||||
* @file llthreadlocalstorage.h
|
||||
* @author Richard
|
||||
* @brief Class wrappers for thread local storage
|
||||
*
|
||||
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLTHREADLOCALSTORAGE_H
|
||||
#define LL_LLTHREADLOCALSTORAGE_H
|
||||
|
||||
#include "llinstancetracker.h"
|
||||
|
||||
class LLThreadLocalPointerBase : public LLInstanceTracker<LLThreadLocalPointerBase>
|
||||
{
|
||||
public:
|
||||
LLThreadLocalPointerBase()
|
||||
: mThreadKey(NULL)
|
||||
{
|
||||
if (sInitialized)
|
||||
{
|
||||
initStorage();
|
||||
}
|
||||
}
|
||||
|
||||
LLThreadLocalPointerBase( const LLThreadLocalPointerBase& other)
|
||||
: mThreadKey(NULL)
|
||||
{
|
||||
if (sInitialized)
|
||||
{
|
||||
initStorage();
|
||||
}
|
||||
}
|
||||
|
||||
~LLThreadLocalPointerBase()
|
||||
{
|
||||
destroyStorage();
|
||||
}
|
||||
|
||||
static void initAllThreadLocalStorage();
|
||||
static void destroyAllThreadLocalStorage();
|
||||
|
||||
protected:
|
||||
void set(void* value);
|
||||
|
||||
void* get() const;
|
||||
|
||||
void initStorage();
|
||||
void destroyStorage();
|
||||
|
||||
protected:
|
||||
struct apr_threadkey_t* mThreadKey;
|
||||
static bool sInitialized;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class LLThreadLocalPointer : public LLThreadLocalPointerBase
|
||||
{
|
||||
public:
|
||||
|
||||
LLThreadLocalPointer()
|
||||
{}
|
||||
|
||||
explicit LLThreadLocalPointer(T* value)
|
||||
{
|
||||
set(value);
|
||||
}
|
||||
|
||||
|
||||
LLThreadLocalPointer(const LLThreadLocalPointer<T>& other)
|
||||
: LLThreadLocalPointerBase(other)
|
||||
{
|
||||
set(other.get());
|
||||
}
|
||||
|
||||
LL_FORCE_INLINE T* get() const
|
||||
{
|
||||
return (T*)LLThreadLocalPointerBase::get();
|
||||
}
|
||||
|
||||
T* operator -> () const
|
||||
{
|
||||
return (T*)get();
|
||||
}
|
||||
|
||||
T& operator*() const
|
||||
{
|
||||
return *(T*)get();
|
||||
}
|
||||
|
||||
LLThreadLocalPointer<T>& operator = (T* value)
|
||||
{
|
||||
set((void*)value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator ==(const T* other) const
|
||||
{
|
||||
if (!sInitialized) return false;
|
||||
return get() == other;
|
||||
}
|
||||
|
||||
bool isNull() const { return !sInitialized || get() == NULL; }
|
||||
|
||||
bool notNull() const { return sInitialized && get() != NULL; }
|
||||
};
|
||||
|
||||
template<typename DERIVED_TYPE>
|
||||
class LLThreadLocalSingleton
|
||||
{
|
||||
typedef enum e_init_state
|
||||
{
|
||||
UNINITIALIZED = 0,
|
||||
CONSTRUCTING,
|
||||
INITIALIZING,
|
||||
INITIALIZED,
|
||||
DELETED
|
||||
} EInitState;
|
||||
|
||||
public:
|
||||
LLThreadLocalSingleton()
|
||||
{}
|
||||
|
||||
virtual ~LLThreadLocalSingleton()
|
||||
{
|
||||
#if LL_DARWIN
|
||||
pthread_setspecific(sInstanceKey, NULL);
|
||||
#else
|
||||
sInstance = NULL;
|
||||
#endif
|
||||
setInitState(DELETED);
|
||||
}
|
||||
|
||||
static void deleteSingleton()
|
||||
{
|
||||
delete getIfExists();
|
||||
}
|
||||
|
||||
static DERIVED_TYPE* getInstance()
|
||||
{
|
||||
EInitState init_state = getInitState();
|
||||
if (init_state == CONSTRUCTING)
|
||||
{
|
||||
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
|
||||
}
|
||||
|
||||
if (init_state == DELETED)
|
||||
{
|
||||
llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl;
|
||||
}
|
||||
|
||||
#if LL_DARWIN
|
||||
createTLSInstance();
|
||||
#endif
|
||||
if (!getIfExists())
|
||||
{
|
||||
setInitState(CONSTRUCTING);
|
||||
DERIVED_TYPE* instancep = new DERIVED_TYPE();
|
||||
#if LL_DARWIN
|
||||
S32 result = pthread_setspecific(sInstanceKey, (void*)instancep);
|
||||
if (result != 0)
|
||||
{
|
||||
llerrs << "Could not set thread local storage" << llendl;
|
||||
}
|
||||
#else
|
||||
sInstance = instancep;
|
||||
#endif
|
||||
setInitState(INITIALIZING);
|
||||
instancep->initSingleton();
|
||||
setInitState(INITIALIZED);
|
||||
}
|
||||
|
||||
return getIfExists();
|
||||
}
|
||||
|
||||
static DERIVED_TYPE* getIfExists()
|
||||
{
|
||||
#if LL_DARWIN
|
||||
return (DERIVED_TYPE*)pthread_getspecific(sInstanceKey);
|
||||
#else
|
||||
return sInstance;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reference version of getInstance()
|
||||
// Preferred over getInstance() as it disallows checking for NULL
|
||||
static DERIVED_TYPE& instance()
|
||||
{
|
||||
return *getInstance();
|
||||
}
|
||||
|
||||
// Has this singleton been created uet?
|
||||
// Use this to avoid accessing singletons before the can safely be constructed
|
||||
static bool instanceExists()
|
||||
{
|
||||
return getInitState() == INITIALIZED;
|
||||
}
|
||||
|
||||
// Has this singleton already been deleted?
|
||||
// Use this to avoid accessing singletons from a static object's destructor
|
||||
static bool destroyed()
|
||||
{
|
||||
return getInitState() == DELETED;
|
||||
}
|
||||
private:
|
||||
#if LL_DARWIN
|
||||
static void createTLSInitState()
|
||||
{
|
||||
static S32 key_created = pthread_key_create(&sInitStateKey, NULL);
|
||||
if (key_created != 0)
|
||||
{
|
||||
llerrs << "Could not create thread local storage" << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
static void createTLSInstance()
|
||||
{
|
||||
static S32 key_created = pthread_key_create(&sInstanceKey, NULL);
|
||||
if (key_created != 0)
|
||||
{
|
||||
llerrs << "Could not create thread local storage" << llendl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static EInitState getInitState()
|
||||
{
|
||||
#if LL_DARWIN
|
||||
createTLSInitState();
|
||||
return (EInitState)(int)pthread_getspecific(sInitStateKey);
|
||||
#else
|
||||
return sInitState;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void setInitState(EInitState state)
|
||||
{
|
||||
#if LL_DARWIN
|
||||
createTLSInitState();
|
||||
pthread_setspecific(sInitStateKey, (void*)state);
|
||||
#else
|
||||
sInitState = state;
|
||||
#endif
|
||||
}
|
||||
LLThreadLocalSingleton(const LLThreadLocalSingleton& other);
|
||||
virtual void initSingleton() {}
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
static __declspec(thread) DERIVED_TYPE* sInstance;
|
||||
static __declspec(thread) EInitState sInitState;
|
||||
#elif LL_LINUX
|
||||
static __thread DERIVED_TYPE* sInstance;
|
||||
static __thread EInitState sInitState;
|
||||
#elif LL_DARWIN
|
||||
static pthread_key_t sInstanceKey;
|
||||
static pthread_key_t sInitStateKey;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if LL_WINDOWS
|
||||
template<typename DERIVED_TYPE>
|
||||
__declspec(thread) DERIVED_TYPE* LLThreadLocalSingleton<DERIVED_TYPE>::sInstance = NULL;
|
||||
|
||||
template<typename DERIVED_TYPE>
|
||||
__declspec(thread) typename LLThreadLocalSingleton<DERIVED_TYPE>::EInitState LLThreadLocalSingleton<DERIVED_TYPE>::sInitState = LLThreadLocalSingleton<DERIVED_TYPE>::UNINITIALIZED;
|
||||
#elif LL_LINUX
|
||||
template<typename DERIVED_TYPE>
|
||||
__thread DERIVED_TYPE* LLThreadLocalSingleton<DERIVED_TYPE>::sInstance = NULL;
|
||||
|
||||
template<typename DERIVED_TYPE>
|
||||
__thread typename LLThreadLocalSingleton<DERIVED_TYPE>::EInitState LLThreadLocalSingleton<DERIVED_TYPE>::sInitState = LLThreadLocalSingleton<DERIVED_TYPE>::UNINITIALIZED;
|
||||
#elif LL_DARWIN
|
||||
template<typename DERIVED_TYPE>
|
||||
pthread_key_t LLThreadLocalSingleton<DERIVED_TYPE>::sInstanceKey;
|
||||
|
||||
template<typename DERIVED_TYPE>
|
||||
pthread_key_t LLThreadLocalSingleton<DERIVED_TYPE>::sInitStateKey;
|
||||
|
||||
#endif
|
||||
|
||||
template<typename DERIVED_TYPE>
|
||||
class LLThreadLocalSingletonPointer
|
||||
{
|
||||
public:
|
||||
LL_FORCE_INLINE static DERIVED_TYPE* getInstance()
|
||||
{
|
||||
#if LL_DARWIN
|
||||
createTLSKey();
|
||||
return (DERIVED_TYPE*)pthread_getspecific(sInstanceKey);
|
||||
#else
|
||||
return sInstance;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void setInstance(DERIVED_TYPE* instance)
|
||||
{
|
||||
#if LL_DARWIN
|
||||
createTLSKey();
|
||||
pthread_setspecific(sInstanceKey, (void*)instance);
|
||||
#else
|
||||
sInstance = instance;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
#if LL_WINDOWS
|
||||
static __declspec(thread) DERIVED_TYPE* sInstance;
|
||||
#elif LL_LINUX
|
||||
static __thread DERIVED_TYPE* sInstance;
|
||||
#elif LL_DARWIN
|
||||
static void TLSError()
|
||||
{
|
||||
llerrs << "Could not create thread local storage" << llendl;
|
||||
}
|
||||
static void createTLSKey()
|
||||
{
|
||||
static S32 key_created = pthread_key_create(&sInstanceKey, NULL);
|
||||
if (key_created != 0)
|
||||
{
|
||||
llerrs << "Could not create thread local storage" << llendl;
|
||||
}
|
||||
}
|
||||
static pthread_key_t sInstanceKey;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if LL_WINDOWS
|
||||
template<typename DERIVED_TYPE>
|
||||
__declspec(thread) DERIVED_TYPE* LLThreadLocalSingletonPointer<DERIVED_TYPE>::sInstance = NULL;
|
||||
#elif LL_LINUX
|
||||
template<typename DERIVED_TYPE>
|
||||
__thread DERIVED_TYPE* LLThreadLocalSingletonPointer<DERIVED_TYPE>::sInstance = NULL;
|
||||
#elif LL_DARWIN
|
||||
template<typename DERIVED_TYPE>
|
||||
pthread_key_t LLThreadLocalSingletonPointer<DERIVED_TYPE>::sInstanceKey;
|
||||
#endif
|
||||
|
||||
#endif // LL_LLTHREADLOCALSTORAGE_H
|
||||
|
|
@ -31,11 +31,9 @@
|
|||
#include "u64.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
# include "llwin32headerslean.h"
|
||||
#elif LL_LINUX || LL_SOLARIS || LL_DARWIN
|
||||
# include <errno.h>
|
||||
# include <errno.h>
|
||||
# include <sys/time.h>
|
||||
#else
|
||||
# error "architecture not supported"
|
||||
|
|
@ -287,14 +285,14 @@ LLTimer::~LLTimer()
|
|||
}
|
||||
|
||||
// static
|
||||
U64 LLTimer::getTotalTime()
|
||||
LLUnitImplicit<U64, LLUnits::Microseconds> LLTimer::getTotalTime()
|
||||
{
|
||||
// simply call into the implementation function.
|
||||
return totalTime();
|
||||
}
|
||||
|
||||
// static
|
||||
F64 LLTimer::getTotalSeconds()
|
||||
LLUnitImplicit<F64, LLUnits::Seconds> LLTimer::getTotalSeconds()
|
||||
{
|
||||
return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
|
||||
}
|
||||
|
|
@ -343,23 +341,23 @@ U64 getElapsedTimeAndUpdate(U64& lastClockCount)
|
|||
}
|
||||
|
||||
|
||||
F64 LLTimer::getElapsedTimeF64() const
|
||||
LLUnitImplicit<F64, LLUnits::Seconds> LLTimer::getElapsedTimeF64() const
|
||||
{
|
||||
U64 last = mLastClockCount;
|
||||
return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
|
||||
}
|
||||
|
||||
F32 LLTimer::getElapsedTimeF32() const
|
||||
LLUnitImplicit<F32, LLUnits::Seconds> LLTimer::getElapsedTimeF32() const
|
||||
{
|
||||
return (F32)getElapsedTimeF64();
|
||||
}
|
||||
|
||||
F64 LLTimer::getElapsedTimeAndResetF64()
|
||||
LLUnitImplicit<F64, LLUnits::Seconds> LLTimer::getElapsedTimeAndResetF64()
|
||||
{
|
||||
return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
|
||||
}
|
||||
|
||||
F32 LLTimer::getElapsedTimeAndResetF32()
|
||||
LLUnitImplicit<F32, LLUnits::Seconds> LLTimer::getElapsedTimeAndResetF32()
|
||||
{
|
||||
return (F32)getElapsedTimeAndResetF64();
|
||||
}
|
||||
|
|
@ -372,7 +370,7 @@ void LLTimer::setTimerExpirySec(F32 expiration)
|
|||
+ (U64)((F32)(expiration * gClockFrequency));
|
||||
}
|
||||
|
||||
F32 LLTimer::getRemainingTimeF32() const
|
||||
LLUnitImplicit<F32, LLUnits::Seconds> LLTimer::getRemainingTimeF32() const
|
||||
{
|
||||
U64 cur_ticks = get_clock_count();
|
||||
if (cur_ticks > mExpirationTicks)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <string>
|
||||
#include <list>
|
||||
// units conversions
|
||||
#include "llunit.h"
|
||||
#ifndef USEC_PER_SEC
|
||||
const U32 USEC_PER_SEC = 1000000;
|
||||
#endif
|
||||
|
|
@ -55,7 +56,7 @@ public:
|
|||
protected:
|
||||
U64 mLastClockCount;
|
||||
U64 mExpirationTicks;
|
||||
BOOL mStarted;
|
||||
bool mStarted;
|
||||
|
||||
public:
|
||||
LLTimer();
|
||||
|
|
@ -66,16 +67,16 @@ public:
|
|||
|
||||
// Return a high precision number of seconds since the start of
|
||||
// this application instance.
|
||||
static F64 getElapsedSeconds()
|
||||
static LLUnitImplicit<F64, LLUnits::Seconds> getElapsedSeconds()
|
||||
{
|
||||
return sTimer->getElapsedTimeF64();
|
||||
}
|
||||
|
||||
// Return a high precision usec since epoch
|
||||
static U64 getTotalTime();
|
||||
static LLUnitImplicit<U64, LLUnits::Microseconds> getTotalTime();
|
||||
|
||||
// Return a high precision seconds since epoch
|
||||
static F64 getTotalSeconds();
|
||||
static LLUnitImplicit<F64, LLUnits::Seconds> getTotalSeconds();
|
||||
|
||||
|
||||
// MANIPULATORS
|
||||
|
|
@ -86,18 +87,18 @@ public:
|
|||
void setTimerExpirySec(F32 expiration);
|
||||
BOOL checkExpirationAndReset(F32 expiration);
|
||||
BOOL hasExpired() const;
|
||||
F32 getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
|
||||
F64 getElapsedTimeAndResetF64();
|
||||
LLUnitImplicit<F32, LLUnits::Seconds> getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
|
||||
LLUnitImplicit<F64, LLUnits::Seconds> getElapsedTimeAndResetF64();
|
||||
|
||||
F32 getRemainingTimeF32() const;
|
||||
LLUnitImplicit<F32, LLUnits::Seconds> getRemainingTimeF32() const;
|
||||
|
||||
static BOOL knownBadTimer();
|
||||
|
||||
// ACCESSORS
|
||||
F32 getElapsedTimeF32() const; // Returns elapsed time in seconds
|
||||
F64 getElapsedTimeF64() const; // Returns elapsed time in seconds
|
||||
LLUnitImplicit<F32, LLUnits::Seconds> getElapsedTimeF32() const; // Returns elapsed time in seconds
|
||||
LLUnitImplicit<F64, LLUnits::Seconds> getElapsedTimeF64() const; // Returns elapsed time in seconds
|
||||
|
||||
BOOL getStarted() const { return mStarted; }
|
||||
bool getStarted() const { return mStarted; }
|
||||
|
||||
|
||||
static U64 getCurrentClockCount(); // Returns the raw clockticks
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* @file lltrace.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "lltrace.h"
|
||||
#include "lltracerecording.h"
|
||||
#include "lltracethreadrecorder.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
static S32 sInitializationCount = 0;
|
||||
|
||||
namespace LLTrace
|
||||
{
|
||||
|
||||
static MasterThreadRecorder* gUIThreadRecorder = NULL;
|
||||
|
||||
void init()
|
||||
{
|
||||
if (sInitializationCount++ == 0)
|
||||
{
|
||||
gUIThreadRecorder = new MasterThreadRecorder();
|
||||
}
|
||||
}
|
||||
|
||||
bool isInitialized()
|
||||
{
|
||||
return sInitializationCount > 0;
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
if (--sInitializationCount == 0)
|
||||
{
|
||||
delete gUIThreadRecorder;
|
||||
gUIThreadRecorder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MasterThreadRecorder& getUIThreadRecorder()
|
||||
{
|
||||
llassert(gUIThreadRecorder != NULL);
|
||||
return *gUIThreadRecorder;
|
||||
}
|
||||
|
||||
LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder_ptr()
|
||||
{
|
||||
static LLThreadLocalPointer<ThreadRecorder> s_thread_recorder;
|
||||
return s_thread_recorder;
|
||||
}
|
||||
|
||||
const LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder()
|
||||
{
|
||||
return get_thread_recorder_ptr();
|
||||
}
|
||||
|
||||
void set_thread_recorder(ThreadRecorder* recorder)
|
||||
{
|
||||
get_thread_recorder_ptr() = recorder;
|
||||
}
|
||||
|
||||
|
||||
TimeBlockTreeNode::TimeBlockTreeNode()
|
||||
: mBlock(NULL),
|
||||
mParent(NULL),
|
||||
mNeedsSorting(false),
|
||||
mCollapsed(true)
|
||||
{}
|
||||
|
||||
void TimeBlockTreeNode::setParent( TimeBlock* parent )
|
||||
{
|
||||
llassert_always(parent != mBlock);
|
||||
llassert_always(parent != NULL);
|
||||
|
||||
TimeBlockTreeNode* parent_tree_node = get_thread_recorder()->getTimeBlockTreeNode(parent->getIndex());
|
||||
if (!parent_tree_node) return;
|
||||
|
||||
if (mParent)
|
||||
{
|
||||
std::vector<TimeBlock*>& children = mParent->getChildren();
|
||||
std::vector<TimeBlock*>::iterator found_it = std::find(children.begin(), children.end(), mBlock);
|
||||
if (found_it != children.end())
|
||||
{
|
||||
children.erase(found_it);
|
||||
}
|
||||
}
|
||||
|
||||
mParent = parent;
|
||||
mBlock->getPrimaryAccumulator()->mParent = parent;
|
||||
parent_tree_node->mChildren.push_back(mBlock);
|
||||
parent_tree_node->mNeedsSorting = true;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,914 @@
|
|||
/**
|
||||
* @file lltracesampler.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "lltrace.h"
|
||||
#include "llfasttimer.h"
|
||||
#include "lltracerecording.h"
|
||||
#include "lltracethreadrecorder.h"
|
||||
#include "llthread.h"
|
||||
|
||||
namespace LLTrace
|
||||
{
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// RecordingBuffers
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
RecordingBuffers::RecordingBuffers()
|
||||
{}
|
||||
|
||||
void RecordingBuffers::handOffTo(RecordingBuffers& other)
|
||||
{
|
||||
other.mCounts.reset(&mCounts);
|
||||
other.mSamples.reset(&mSamples);
|
||||
other.mEvents.reset(&mEvents);
|
||||
other.mStackTimers.reset(&mStackTimers);
|
||||
other.mMemStats.reset(&mMemStats);
|
||||
}
|
||||
|
||||
void RecordingBuffers::makePrimary()
|
||||
{
|
||||
mCounts.makePrimary();
|
||||
mSamples.makePrimary();
|
||||
mEvents.makePrimary();
|
||||
mStackTimers.makePrimary();
|
||||
mMemStats.makePrimary();
|
||||
|
||||
ThreadRecorder* thread_recorder = get_thread_recorder().get();
|
||||
AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers;
|
||||
// update stacktimer parent pointers
|
||||
for (S32 i = 0, end_i = mStackTimers.size(); i < end_i; i++)
|
||||
{
|
||||
TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(i);
|
||||
if (tree_node)
|
||||
{
|
||||
timer_accumulator_buffer[i].mParent = tree_node->mParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RecordingBuffers::isPrimary() const
|
||||
{
|
||||
return mCounts.isPrimary();
|
||||
}
|
||||
|
||||
void RecordingBuffers::append( const RecordingBuffers& other )
|
||||
{
|
||||
mCounts.addSamples(other.mCounts);
|
||||
mSamples.addSamples(other.mSamples);
|
||||
mEvents.addSamples(other.mEvents);
|
||||
mMemStats.addSamples(other.mMemStats);
|
||||
mStackTimers.addSamples(other.mStackTimers);
|
||||
}
|
||||
|
||||
void RecordingBuffers::merge( const RecordingBuffers& other)
|
||||
{
|
||||
mCounts.addSamples(other.mCounts, false);
|
||||
mSamples.addSamples(other.mSamples, false);
|
||||
mEvents.addSamples(other.mEvents, false);
|
||||
mMemStats.addSamples(other.mMemStats, false);
|
||||
// for now, hold out timers from merge, need to be displayed per thread
|
||||
//mStackTimers.addSamples(other.mStackTimers, false);
|
||||
}
|
||||
|
||||
void RecordingBuffers::reset(RecordingBuffers* other)
|
||||
{
|
||||
mCounts.reset(other ? &other->mCounts : NULL);
|
||||
mSamples.reset(other ? &other->mSamples : NULL);
|
||||
mEvents.reset(other ? &other->mEvents : NULL);
|
||||
mStackTimers.reset(other ? &other->mStackTimers : NULL);
|
||||
mMemStats.reset(other ? &other->mMemStats : NULL);
|
||||
}
|
||||
|
||||
void RecordingBuffers::flush()
|
||||
{
|
||||
LLUnitImplicit<F64, LLUnits::Seconds> time_stamp = LLTimer::getTotalSeconds();
|
||||
|
||||
mSamples.flush(time_stamp);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Recording
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
Recording::Recording()
|
||||
: mElapsedSeconds(0)
|
||||
{
|
||||
mBuffers = new RecordingBuffers();
|
||||
}
|
||||
|
||||
Recording::Recording( const Recording& other )
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
Recording& Recording::operator = (const Recording& other)
|
||||
{
|
||||
// this will allow us to seamlessly start without affecting any data we've acquired from other
|
||||
setPlayState(PAUSED);
|
||||
|
||||
Recording& mutable_other = const_cast<Recording&>(other);
|
||||
mutable_other.update();
|
||||
EPlayState other_play_state = other.getPlayState();
|
||||
|
||||
mBuffers = mutable_other.mBuffers;
|
||||
|
||||
LLStopWatchControlsMixin<Recording>::setPlayState(other_play_state);
|
||||
|
||||
// above call will clear mElapsedSeconds as a side effect, so copy it here
|
||||
mElapsedSeconds = other.mElapsedSeconds;
|
||||
mSamplingTimer = other.mSamplingTimer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Recording::~Recording()
|
||||
{
|
||||
if (isStarted() && LLTrace::get_thread_recorder().notNull())
|
||||
{
|
||||
LLTrace::get_thread_recorder()->deactivate(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Recording::update()
|
||||
{
|
||||
if (isStarted())
|
||||
{
|
||||
mBuffers.write()->flush();
|
||||
LLTrace::get_thread_recorder()->bringUpToDate(this);
|
||||
mSamplingTimer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Recording::handleReset()
|
||||
{
|
||||
mBuffers.write()->reset();
|
||||
|
||||
mElapsedSeconds = 0.0;
|
||||
mSamplingTimer.reset();
|
||||
}
|
||||
|
||||
void Recording::handleStart()
|
||||
{
|
||||
mSamplingTimer.reset();
|
||||
LLTrace::get_thread_recorder()->activate(this);
|
||||
}
|
||||
|
||||
void Recording::handleStop()
|
||||
{
|
||||
mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
|
||||
mBuffers.write()->flush();
|
||||
LLTrace::get_thread_recorder()->deactivate(this);
|
||||
}
|
||||
|
||||
void Recording::handleSplitTo(Recording& other)
|
||||
{
|
||||
mBuffers.write()->handOffTo(*other.mBuffers.write());
|
||||
}
|
||||
|
||||
void Recording::appendRecording( const Recording& other )
|
||||
{
|
||||
update();
|
||||
mBuffers.write()->append(*other.mBuffers);
|
||||
mElapsedSeconds += other.mElapsedSeconds;
|
||||
}
|
||||
|
||||
void Recording::mergeRecording( const Recording& other)
|
||||
{
|
||||
update();
|
||||
mBuffers.write()->merge(*other.mBuffers);
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> Recording::getSum(const TraceType<TimeBlockAccumulator>& stat)
|
||||
{
|
||||
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
||||
return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter)
|
||||
/ (F64)LLTrace::TimeBlock::countsPerSecond();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> Recording::getSum(const TraceType<TimeBlockAccumulator::SelfTimeFacet>& stat)
|
||||
{
|
||||
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
||||
return (F64)(accumulator.mSelfTimeCounter) / (F64)LLTrace::TimeBlock::countsPerSecond();
|
||||
}
|
||||
|
||||
|
||||
U32 Recording::getSum(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat)
|
||||
{
|
||||
return mBuffers->mStackTimers[stat.getIndex()].mCalls;
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> Recording::getPerSec(const TraceType<TimeBlockAccumulator>& stat)
|
||||
{
|
||||
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
||||
|
||||
return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter)
|
||||
/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds.value());
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> Recording::getPerSec(const TraceType<TimeBlockAccumulator::SelfTimeFacet>& stat)
|
||||
{
|
||||
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
||||
|
||||
return (F64)(accumulator.mSelfTimeCounter)
|
||||
/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds.value());
|
||||
}
|
||||
|
||||
F32 Recording::getPerSec(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat)
|
||||
{
|
||||
return (F32)mBuffers->mStackTimers[stat.getIndex()].mCalls / mElapsedSeconds.value();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getMin(const TraceType<MemStatAccumulator>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mSize.getMin();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getMean(const TraceType<MemStatAccumulator>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mSize.getMean();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getMax(const TraceType<MemStatAccumulator>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mSize.getMax();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getStandardDeviation(const TraceType<MemStatAccumulator>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mSize.getStandardDeviation();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getLastValue(const TraceType<MemStatAccumulator>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mSize.getLastValue();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getMin(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMin();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getMean(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMean();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getMax(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMax();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getStandardDeviation(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getStandardDeviation();
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> Recording::getLastValue(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getLastValue();
|
||||
}
|
||||
|
||||
U32 Recording::getSum(const TraceType<MemStatAccumulator::AllocationCountFacet>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mAllocatedCount;
|
||||
}
|
||||
|
||||
U32 Recording::getSum(const TraceType<MemStatAccumulator::DeallocationCountFacet>& stat)
|
||||
{
|
||||
return mBuffers->mMemStats[stat.getIndex()].mAllocatedCount;
|
||||
}
|
||||
|
||||
|
||||
F64 Recording::getSum( const TraceType<CountAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mCounts[stat.getIndex()].getSum();
|
||||
}
|
||||
|
||||
F64 Recording::getSum( const TraceType<EventAccumulator>& stat )
|
||||
{
|
||||
return (F64)mBuffers->mEvents[stat.getIndex()].getSum();
|
||||
}
|
||||
|
||||
F64 Recording::getPerSec( const TraceType<CountAccumulator>& stat )
|
||||
{
|
||||
F64 sum = mBuffers->mCounts[stat.getIndex()].getSum();
|
||||
return (sum != 0.0)
|
||||
? (sum / mElapsedSeconds.value())
|
||||
: 0.0;
|
||||
}
|
||||
|
||||
U32 Recording::getSampleCount( const TraceType<CountAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mCounts[stat.getIndex()].getSampleCount();
|
||||
}
|
||||
|
||||
F64 Recording::getMin( const TraceType<SampleAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mSamples[stat.getIndex()].getMin();
|
||||
}
|
||||
|
||||
F64 Recording::getMax( const TraceType<SampleAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mSamples[stat.getIndex()].getMax();
|
||||
}
|
||||
|
||||
F64 Recording::getMean( const TraceType<SampleAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mSamples[stat.getIndex()].getMean();
|
||||
}
|
||||
|
||||
F64 Recording::getStandardDeviation( const TraceType<SampleAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mSamples[stat.getIndex()].getStandardDeviation();
|
||||
}
|
||||
|
||||
F64 Recording::getLastValue( const TraceType<SampleAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mSamples[stat.getIndex()].getLastValue();
|
||||
}
|
||||
|
||||
U32 Recording::getSampleCount( const TraceType<SampleAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mSamples[stat.getIndex()].getSampleCount();
|
||||
}
|
||||
|
||||
F64 Recording::getMin( const TraceType<EventAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mEvents[stat.getIndex()].getMin();
|
||||
}
|
||||
|
||||
F64 Recording::getMax( const TraceType<EventAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mEvents[stat.getIndex()].getMax();
|
||||
}
|
||||
|
||||
F64 Recording::getMean( const TraceType<EventAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mEvents[stat.getIndex()].getMean();
|
||||
}
|
||||
|
||||
F64 Recording::getStandardDeviation( const TraceType<EventAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mEvents[stat.getIndex()].getStandardDeviation();
|
||||
}
|
||||
|
||||
F64 Recording::getLastValue( const TraceType<EventAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mEvents[stat.getIndex()].getLastValue();
|
||||
}
|
||||
|
||||
U32 Recording::getSampleCount( const TraceType<EventAccumulator>& stat )
|
||||
{
|
||||
return mBuffers->mEvents[stat.getIndex()].getSampleCount();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// PeriodicRecording
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
PeriodicRecording::PeriodicRecording( U32 num_periods, EPlayState state)
|
||||
: mAutoResize(num_periods == 0),
|
||||
mCurPeriod(0),
|
||||
mNumPeriods(0),
|
||||
mRecordingPeriods(num_periods ? num_periods : 1)
|
||||
{
|
||||
setPlayState(state);
|
||||
}
|
||||
|
||||
void PeriodicRecording::nextPeriod()
|
||||
{
|
||||
if (mAutoResize)
|
||||
{
|
||||
mRecordingPeriods.push_back(Recording());
|
||||
}
|
||||
|
||||
Recording& old_recording = getCurRecording();
|
||||
mCurPeriod = (mCurPeriod + 1) % mRecordingPeriods.size();
|
||||
old_recording.splitTo(getCurRecording());
|
||||
|
||||
mNumPeriods = llmin(mRecordingPeriods.size(), mNumPeriods + 1);
|
||||
}
|
||||
|
||||
void PeriodicRecording::appendRecording(Recording& recording)
|
||||
{
|
||||
getCurRecording().appendRecording(recording);
|
||||
nextPeriod();
|
||||
}
|
||||
|
||||
|
||||
void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other )
|
||||
{
|
||||
if (other.mRecordingPeriods.empty()) return;
|
||||
|
||||
getCurRecording().update();
|
||||
other.getCurRecording().update();
|
||||
|
||||
const U32 other_recording_slots = other.mRecordingPeriods.size();
|
||||
const U32 other_num_recordings = other.getNumRecordedPeriods();
|
||||
const U32 other_current_recording_index = other.mCurPeriod;
|
||||
const U32 other_oldest_recording_index = (other_current_recording_index + other_recording_slots - other_num_recordings + 1) % other_recording_slots;
|
||||
|
||||
// append first recording into our current slot
|
||||
getCurRecording().appendRecording(other.mRecordingPeriods[other_oldest_recording_index]);
|
||||
|
||||
// from now on, add new recordings for everything after the first
|
||||
U32 other_index = (other_oldest_recording_index + 1) % other_recording_slots;
|
||||
|
||||
if (mAutoResize)
|
||||
{
|
||||
// append first recording into our current slot
|
||||
getCurRecording().appendRecording(other.mRecordingPeriods[other_oldest_recording_index]);
|
||||
|
||||
// push back recordings for everything in the middle
|
||||
U32 other_index = (other_oldest_recording_index + 1) % other_recording_slots;
|
||||
while (other_index != other_current_recording_index)
|
||||
{
|
||||
mRecordingPeriods.push_back(other.mRecordingPeriods[other_index]);
|
||||
other_index = (other_index + 1) % other_recording_slots;
|
||||
}
|
||||
|
||||
// add final recording, if it wasn't already added as the first
|
||||
if (other_num_recordings > 1)
|
||||
{
|
||||
mRecordingPeriods.push_back(other.mRecordingPeriods[other_current_recording_index]);
|
||||
}
|
||||
|
||||
mCurPeriod = mRecordingPeriods.size() - 1;
|
||||
mNumPeriods = mRecordingPeriods.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t num_to_copy = llmin( mRecordingPeriods.size(), (size_t)other_num_recordings);
|
||||
|
||||
std::vector<Recording>::iterator src_it = other.mRecordingPeriods.begin() + other_index ;
|
||||
std::vector<Recording>::iterator dest_it = mRecordingPeriods.begin() + mCurPeriod;
|
||||
|
||||
// already consumed the first recording from other, so start counting at 1
|
||||
for(size_t i = 1; i < num_to_copy; i++)
|
||||
{
|
||||
*dest_it = *src_it;
|
||||
|
||||
if (++src_it == other.mRecordingPeriods.end())
|
||||
{
|
||||
src_it = other.mRecordingPeriods.begin();
|
||||
}
|
||||
|
||||
if (++dest_it == mRecordingPeriods.end())
|
||||
{
|
||||
dest_it = mRecordingPeriods.begin();
|
||||
}
|
||||
}
|
||||
|
||||
// want argument to % to be positive, otherwise result could be negative and thus out of bounds
|
||||
llassert(num_to_copy >= 1);
|
||||
// advance to last recording period copied, and make that our current period
|
||||
mCurPeriod = (mCurPeriod + num_to_copy - 1) % mRecordingPeriods.size();
|
||||
mNumPeriods = llmin(mRecordingPeriods.size(), mNumPeriods + num_to_copy - 1);
|
||||
}
|
||||
|
||||
// end with fresh period, otherwise next appendPeriodicRecording() will merge the first
|
||||
// recording period with the last one appended here
|
||||
nextPeriod();
|
||||
getCurRecording().setPlayState(getPlayState());
|
||||
}
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> PeriodicRecording::getDuration() const
|
||||
{
|
||||
LLUnit<F64, LLUnits::Seconds> duration;
|
||||
size_t num_periods = mRecordingPeriods.size();
|
||||
for (size_t i = 1; i <= num_periods; i++)
|
||||
{
|
||||
size_t index = (mCurPeriod + num_periods - i) % num_periods;
|
||||
duration += mRecordingPeriods[index].getDuration();
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
|
||||
LLTrace::Recording PeriodicRecording::snapshotCurRecording() const
|
||||
{
|
||||
Recording recording_copy(getCurRecording());
|
||||
recording_copy.stop();
|
||||
return recording_copy;
|
||||
}
|
||||
|
||||
|
||||
Recording& PeriodicRecording::getLastRecording()
|
||||
{
|
||||
return getPrevRecording(1);
|
||||
}
|
||||
|
||||
const Recording& PeriodicRecording::getLastRecording() const
|
||||
{
|
||||
return getPrevRecording(1);
|
||||
}
|
||||
|
||||
Recording& PeriodicRecording::getCurRecording()
|
||||
{
|
||||
return mRecordingPeriods[mCurPeriod];
|
||||
}
|
||||
|
||||
const Recording& PeriodicRecording::getCurRecording() const
|
||||
{
|
||||
return mRecordingPeriods[mCurPeriod];
|
||||
}
|
||||
|
||||
Recording& PeriodicRecording::getPrevRecording( U32 offset )
|
||||
{
|
||||
U32 num_periods = mRecordingPeriods.size();
|
||||
offset = llclamp(offset, 0u, num_periods - 1);
|
||||
return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods];
|
||||
}
|
||||
|
||||
const Recording& PeriodicRecording::getPrevRecording( U32 offset ) const
|
||||
{
|
||||
U32 num_periods = mRecordingPeriods.size();
|
||||
offset = llclamp(offset, 0u, num_periods - 1);
|
||||
return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods];
|
||||
}
|
||||
|
||||
void PeriodicRecording::handleStart()
|
||||
{
|
||||
getCurRecording().start();
|
||||
}
|
||||
|
||||
void PeriodicRecording::handleStop()
|
||||
{
|
||||
getCurRecording().pause();
|
||||
}
|
||||
|
||||
void PeriodicRecording::handleReset()
|
||||
{
|
||||
if (mAutoResize)
|
||||
{
|
||||
mRecordingPeriods.clear();
|
||||
mRecordingPeriods.push_back(Recording());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::vector<Recording>::iterator it = mRecordingPeriods.begin(), end_it = mRecordingPeriods.end();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
it->reset();
|
||||
}
|
||||
}
|
||||
mCurPeriod = 0;
|
||||
getCurRecording().setPlayState(getPlayState());
|
||||
}
|
||||
|
||||
void PeriodicRecording::handleSplitTo(PeriodicRecording& other)
|
||||
{
|
||||
getCurRecording().splitTo(other.getCurRecording());
|
||||
}
|
||||
|
||||
|
||||
F64 PeriodicRecording::getPeriodMean( const TraceType<EventAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
F64 mean = 0;
|
||||
if (num_periods <= 0) { return mean; }
|
||||
|
||||
S32 total_sample_count = 0;
|
||||
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
if (mRecordingPeriods[index].getDuration() > 0.f)
|
||||
{
|
||||
S32 period_sample_count = mRecordingPeriods[index].getSampleCount(stat);
|
||||
mean += mRecordingPeriods[index].getMean(stat) * period_sample_count;
|
||||
total_sample_count += period_sample_count;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_sample_count)
|
||||
{
|
||||
mean = mean / total_sample_count;
|
||||
}
|
||||
return mean;
|
||||
}
|
||||
|
||||
F64 PeriodicRecording::getPeriodMin( const TraceType<EventAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
F64 min_val = std::numeric_limits<F64>::max();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
min_val = llmin(min_val, mRecordingPeriods[index].getMin(stat));
|
||||
}
|
||||
return min_val;
|
||||
}
|
||||
|
||||
F64 PeriodicRecording::getPeriodMax( const TraceType<EventAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
F64 max_val = std::numeric_limits<F64>::min();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
max_val = llmax(max_val, mRecordingPeriods[index].getMax(stat));
|
||||
}
|
||||
return max_val;
|
||||
}
|
||||
|
||||
F64 PeriodicRecording::getPeriodMin( const TraceType<SampleAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
F64 min_val = std::numeric_limits<F64>::max();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
min_val = llmin(min_val, mRecordingPeriods[index].getMin(stat));
|
||||
}
|
||||
return min_val;
|
||||
}
|
||||
|
||||
F64 PeriodicRecording::getPeriodMax(const TraceType<SampleAccumulator>& stat, size_t num_periods /*= U32_MAX*/)
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
F64 max_val = std::numeric_limits<F64>::min();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
max_val = llmax(max_val, mRecordingPeriods[index].getMax(stat));
|
||||
}
|
||||
return max_val;
|
||||
}
|
||||
|
||||
|
||||
F64 PeriodicRecording::getPeriodMean( const TraceType<SampleAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> total_duration = 0.f;
|
||||
|
||||
F64 mean = 0;
|
||||
if (num_periods <= 0) { return mean; }
|
||||
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
if (mRecordingPeriods[index].getDuration() > 0.f)
|
||||
{
|
||||
LLUnit<F64, LLUnits::Seconds> recording_duration = mRecordingPeriods[index].getDuration();
|
||||
mean += mRecordingPeriods[index].getMean(stat) * recording_duration.value();
|
||||
total_duration += recording_duration;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_duration.value())
|
||||
{
|
||||
mean = mean / total_duration;
|
||||
}
|
||||
return mean;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// ExtendableRecording
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ExtendableRecording::extend()
|
||||
{
|
||||
// stop recording to get latest data
|
||||
mPotentialRecording.update();
|
||||
// push the data back to accepted recording
|
||||
mAcceptedRecording.appendRecording(mPotentialRecording);
|
||||
// flush data, so we can start from scratch
|
||||
mPotentialRecording.reset();
|
||||
}
|
||||
|
||||
void ExtendableRecording::handleStart()
|
||||
{
|
||||
mPotentialRecording.start();
|
||||
}
|
||||
|
||||
void ExtendableRecording::handleStop()
|
||||
{
|
||||
mPotentialRecording.pause();
|
||||
}
|
||||
|
||||
void ExtendableRecording::handleReset()
|
||||
{
|
||||
mAcceptedRecording.reset();
|
||||
mPotentialRecording.reset();
|
||||
}
|
||||
|
||||
void ExtendableRecording::handleSplitTo(ExtendableRecording& other)
|
||||
{
|
||||
mPotentialRecording.splitTo(other.mPotentialRecording);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// ExtendablePeriodicRecording
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
ExtendablePeriodicRecording::ExtendablePeriodicRecording()
|
||||
: mAcceptedRecording(0),
|
||||
mPotentialRecording(0)
|
||||
{}
|
||||
|
||||
void ExtendablePeriodicRecording::extend()
|
||||
{
|
||||
// push the data back to accepted recording
|
||||
mAcceptedRecording.appendPeriodicRecording(mPotentialRecording);
|
||||
// flush data, so we can start from scratch
|
||||
mPotentialRecording.reset();
|
||||
}
|
||||
|
||||
|
||||
void ExtendablePeriodicRecording::handleStart()
|
||||
{
|
||||
mPotentialRecording.start();
|
||||
}
|
||||
|
||||
void ExtendablePeriodicRecording::handleStop()
|
||||
{
|
||||
mPotentialRecording.pause();
|
||||
}
|
||||
|
||||
void ExtendablePeriodicRecording::handleReset()
|
||||
{
|
||||
mAcceptedRecording.reset();
|
||||
mPotentialRecording.reset();
|
||||
}
|
||||
|
||||
void ExtendablePeriodicRecording::handleSplitTo(ExtendablePeriodicRecording& other)
|
||||
{
|
||||
mPotentialRecording.splitTo(other.mPotentialRecording);
|
||||
}
|
||||
|
||||
|
||||
PeriodicRecording& get_frame_recording()
|
||||
{
|
||||
static LLThreadLocalPointer<PeriodicRecording> sRecording(new PeriodicRecording(1000, PeriodicRecording::STARTED));
|
||||
return *sRecording;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LLStopWatchControlsMixinCommon::start()
|
||||
{
|
||||
switch (mPlayState)
|
||||
{
|
||||
case STOPPED:
|
||||
handleReset();
|
||||
handleStart();
|
||||
break;
|
||||
case PAUSED:
|
||||
handleStart();
|
||||
break;
|
||||
case STARTED:
|
||||
handleReset();
|
||||
break;
|
||||
default:
|
||||
llassert(false);
|
||||
break;
|
||||
}
|
||||
mPlayState = STARTED;
|
||||
}
|
||||
|
||||
void LLStopWatchControlsMixinCommon::stop()
|
||||
{
|
||||
switch (mPlayState)
|
||||
{
|
||||
case STOPPED:
|
||||
break;
|
||||
case PAUSED:
|
||||
break;
|
||||
case STARTED:
|
||||
handleStop();
|
||||
break;
|
||||
default:
|
||||
llassert(false);
|
||||
break;
|
||||
}
|
||||
mPlayState = STOPPED;
|
||||
}
|
||||
|
||||
void LLStopWatchControlsMixinCommon::pause()
|
||||
{
|
||||
switch (mPlayState)
|
||||
{
|
||||
case STOPPED:
|
||||
break;
|
||||
case PAUSED:
|
||||
break;
|
||||
case STARTED:
|
||||
handleStop();
|
||||
break;
|
||||
default:
|
||||
llassert(false);
|
||||
break;
|
||||
}
|
||||
mPlayState = PAUSED;
|
||||
}
|
||||
|
||||
void LLStopWatchControlsMixinCommon::resume()
|
||||
{
|
||||
switch (mPlayState)
|
||||
{
|
||||
case STOPPED:
|
||||
handleStart();
|
||||
break;
|
||||
case PAUSED:
|
||||
handleStart();
|
||||
break;
|
||||
case STARTED:
|
||||
break;
|
||||
default:
|
||||
llassert(false);
|
||||
break;
|
||||
}
|
||||
mPlayState = STARTED;
|
||||
}
|
||||
|
||||
void LLStopWatchControlsMixinCommon::restart()
|
||||
{
|
||||
switch (mPlayState)
|
||||
{
|
||||
case STOPPED:
|
||||
handleReset();
|
||||
handleStart();
|
||||
break;
|
||||
case PAUSED:
|
||||
handleReset();
|
||||
handleStart();
|
||||
break;
|
||||
case STARTED:
|
||||
handleReset();
|
||||
break;
|
||||
default:
|
||||
llassert(false);
|
||||
break;
|
||||
}
|
||||
mPlayState = STARTED;
|
||||
}
|
||||
|
||||
void LLStopWatchControlsMixinCommon::reset()
|
||||
{
|
||||
handleReset();
|
||||
}
|
||||
|
||||
void LLStopWatchControlsMixinCommon::setPlayState( EPlayState state )
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case STOPPED:
|
||||
stop();
|
||||
break;
|
||||
case PAUSED:
|
||||
pause();
|
||||
break;
|
||||
case STARTED:
|
||||
start();
|
||||
break;
|
||||
default:
|
||||
llassert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
mPlayState = state;
|
||||
}
|
||||
|
|
@ -0,0 +1,531 @@
|
|||
/**
|
||||
* @file lltracerecording.h
|
||||
* @brief Sampling object for collecting runtime statistics originating from lltrace.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLTRACERECORDING_H
|
||||
#define LL_LLTRACERECORDING_H
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include "llpreprocessor.h"
|
||||
|
||||
#include "llpointer.h"
|
||||
#include "lltimer.h"
|
||||
#include "lltrace.h"
|
||||
|
||||
class LLStopWatchControlsMixinCommon
|
||||
{
|
||||
public:
|
||||
virtual ~LLStopWatchControlsMixinCommon() {}
|
||||
|
||||
enum EPlayState
|
||||
{
|
||||
STOPPED,
|
||||
PAUSED,
|
||||
STARTED
|
||||
};
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void pause();
|
||||
void resume();
|
||||
void restart();
|
||||
void reset();
|
||||
|
||||
bool isStarted() const { return mPlayState == STARTED; }
|
||||
bool isPaused() const { return mPlayState == PAUSED; }
|
||||
bool isStopped() const { return mPlayState == STOPPED; }
|
||||
EPlayState getPlayState() const { return mPlayState; }
|
||||
// force play state to specific value by calling appropriate handle* methods
|
||||
void setPlayState(EPlayState state);
|
||||
|
||||
protected:
|
||||
LLStopWatchControlsMixinCommon()
|
||||
: mPlayState(STOPPED)
|
||||
{}
|
||||
|
||||
private:
|
||||
// trigger active behavior (without reset)
|
||||
virtual void handleStart() = 0;
|
||||
// stop active behavior
|
||||
virtual void handleStop() = 0;
|
||||
// clear accumulated state, can be called while started
|
||||
virtual void handleReset() = 0;
|
||||
|
||||
EPlayState mPlayState;
|
||||
};
|
||||
|
||||
template<typename DERIVED>
|
||||
class LLStopWatchControlsMixin
|
||||
: public LLStopWatchControlsMixinCommon
|
||||
{
|
||||
public:
|
||||
typedef LLStopWatchControlsMixin<DERIVED> self_t;
|
||||
virtual void splitTo(DERIVED& other)
|
||||
{
|
||||
EPlayState play_state = getPlayState();
|
||||
stop();
|
||||
other.reset();
|
||||
|
||||
handleSplitTo(other);
|
||||
|
||||
other.setPlayState(play_state);
|
||||
}
|
||||
|
||||
virtual void splitFrom(DERIVED& other)
|
||||
{
|
||||
static_cast<self_t&>(other).handleSplitTo(*static_cast<DERIVED*>(this));
|
||||
}
|
||||
private:
|
||||
// atomically stop this object while starting the other
|
||||
// no data can be missed in between stop and start
|
||||
virtual void handleSplitTo(DERIVED& other) {};
|
||||
|
||||
};
|
||||
|
||||
namespace LLTrace
|
||||
{
|
||||
struct RecordingBuffers : public LLRefCount
|
||||
{
|
||||
RecordingBuffers();
|
||||
|
||||
void handOffTo(RecordingBuffers& other);
|
||||
void makePrimary();
|
||||
bool isPrimary() const;
|
||||
|
||||
void append(const RecordingBuffers& other);
|
||||
void merge(const RecordingBuffers& other);
|
||||
void reset(RecordingBuffers* other = NULL);
|
||||
void flush();
|
||||
|
||||
AccumulatorBuffer<CountAccumulator> mCounts;
|
||||
AccumulatorBuffer<SampleAccumulator> mSamples;
|
||||
AccumulatorBuffer<EventAccumulator> mEvents;
|
||||
AccumulatorBuffer<TimeBlockAccumulator> mStackTimers;
|
||||
AccumulatorBuffer<MemStatAccumulator> mMemStats;
|
||||
};
|
||||
|
||||
class Recording
|
||||
: public LLStopWatchControlsMixin<Recording>
|
||||
{
|
||||
public:
|
||||
Recording();
|
||||
|
||||
Recording(const Recording& other);
|
||||
~Recording();
|
||||
|
||||
Recording& operator = (const Recording& other);
|
||||
|
||||
// accumulate data from subsequent, non-overlapping recording
|
||||
void appendRecording(const Recording& other);
|
||||
|
||||
// gather data from recording, ignoring time relationship (for example, pulling data from slave threads)
|
||||
void mergeRecording(const Recording& other);
|
||||
|
||||
// grab latest recorded data
|
||||
void update();
|
||||
|
||||
// ensure that buffers are exclusively owned by this recording
|
||||
void makeUnique() { mBuffers.makeUnique(); }
|
||||
|
||||
// Timer accessors
|
||||
LLUnit<F64, LLUnits::Seconds> getSum(const TraceType<TimeBlockAccumulator>& stat);
|
||||
LLUnit<F64, LLUnits::Seconds> getSum(const TraceType<TimeBlockAccumulator::SelfTimeFacet>& stat);
|
||||
U32 getSum(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat);
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> getPerSec(const TraceType<TimeBlockAccumulator>& stat);
|
||||
LLUnit<F64, LLUnits::Seconds> getPerSec(const TraceType<TimeBlockAccumulator::SelfTimeFacet>& stat);
|
||||
F32 getPerSec(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat);
|
||||
|
||||
// Memory accessors
|
||||
LLUnit<F64, LLUnits::Bytes> getMin(const TraceType<MemStatAccumulator>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getMean(const TraceType<MemStatAccumulator>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getMax(const TraceType<MemStatAccumulator>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getStandardDeviation(const TraceType<MemStatAccumulator>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getLastValue(const TraceType<MemStatAccumulator>& stat);
|
||||
|
||||
LLUnit<F64, LLUnits::Bytes> getMin(const TraceType<MemStatAccumulator::ChildMemFacet>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getMean(const TraceType<MemStatAccumulator::ChildMemFacet>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getMax(const TraceType<MemStatAccumulator::ChildMemFacet>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getStandardDeviation(const TraceType<MemStatAccumulator::ChildMemFacet>& stat);
|
||||
LLUnit<F64, LLUnits::Bytes> getLastValue(const TraceType<MemStatAccumulator::ChildMemFacet>& stat);
|
||||
|
||||
U32 getSum(const TraceType<MemStatAccumulator::AllocationCountFacet>& stat);
|
||||
U32 getSum(const TraceType<MemStatAccumulator::DeallocationCountFacet>& stat);
|
||||
|
||||
// CountStatHandle accessors
|
||||
F64 getSum(const TraceType<CountAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getSum(const CountStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getSum(static_cast<const TraceType<CountAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getPerSec(const TraceType<CountAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getPerSec(const CountStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getPerSec(static_cast<const TraceType<CountAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
U32 getSampleCount(const TraceType<CountAccumulator>& stat);
|
||||
|
||||
|
||||
// SampleStatHandle accessors
|
||||
F64 getMin(const TraceType<SampleAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getMin(const SampleStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getMin(static_cast<const TraceType<SampleAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getMean(const TraceType<SampleAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getMean(SampleStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getMean(static_cast<const TraceType<SampleAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getMax(const TraceType<SampleAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getMax(const SampleStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getMax(static_cast<const TraceType<SampleAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getStandardDeviation(const TraceType<SampleAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getStandardDeviation(const SampleStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getStandardDeviation(static_cast<const TraceType<SampleAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getLastValue(const TraceType<SampleAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getLastValue(const SampleStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getLastValue(static_cast<const TraceType<SampleAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
U32 getSampleCount(const TraceType<SampleAccumulator>& stat);
|
||||
|
||||
// EventStatHandle accessors
|
||||
F64 getSum(const TraceType<EventAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getSum(const EventStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getSum(static_cast<const TraceType<EventAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getMin(const TraceType<EventAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getMin(const EventStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getMin(static_cast<const TraceType<EventAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getMax(const TraceType<EventAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getMax(const EventStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getMax(static_cast<const TraceType<EventAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getMean(const TraceType<EventAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getMean(EventStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getMean(static_cast<const TraceType<EventAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getStandardDeviation(const TraceType<EventAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getStandardDeviation(const EventStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getStandardDeviation(static_cast<const TraceType<EventAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
F64 getLastValue(const TraceType<EventAccumulator>& stat);
|
||||
template <typename T>
|
||||
T getLastValue(const EventStatHandle<T>& stat)
|
||||
{
|
||||
return (T)getLastValue(static_cast<const TraceType<EventAccumulator>&> (stat));
|
||||
}
|
||||
|
||||
U32 getSampleCount(const TraceType<EventAccumulator>& stat);
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> getDuration() const { return mElapsedSeconds; }
|
||||
|
||||
protected:
|
||||
friend class ThreadRecorder;
|
||||
|
||||
// implementation for LLStopWatchControlsMixin
|
||||
/*virtual*/ void handleStart();
|
||||
/*virtual*/ void handleStop();
|
||||
/*virtual*/ void handleReset();
|
||||
/*virtual*/ void handleSplitTo(Recording& other);
|
||||
|
||||
// returns data for current thread
|
||||
class ThreadRecorder* getThreadRecorder();
|
||||
|
||||
LLTimer mSamplingTimer;
|
||||
LLUnit<F64, LLUnits::Seconds> mElapsedSeconds;
|
||||
LLCopyOnWritePointer<RecordingBuffers> mBuffers;
|
||||
};
|
||||
|
||||
class LL_COMMON_API PeriodicRecording
|
||||
: public LLStopWatchControlsMixin<PeriodicRecording>
|
||||
{
|
||||
public:
|
||||
PeriodicRecording(U32 num_periods, EPlayState state = STOPPED);
|
||||
|
||||
void nextPeriod();
|
||||
size_t getNumRecordedPeriods() { return mNumPeriods; }
|
||||
|
||||
LLUnit<F64, LLUnits::Seconds> getDuration() const;
|
||||
|
||||
void appendPeriodicRecording(PeriodicRecording& other);
|
||||
void appendRecording(Recording& recording);
|
||||
Recording& getLastRecording();
|
||||
const Recording& getLastRecording() const;
|
||||
Recording& getCurRecording();
|
||||
const Recording& getCurRecording() const;
|
||||
Recording& getPrevRecording(U32 offset);
|
||||
const Recording& getPrevRecording(U32 offset) const;
|
||||
Recording snapshotCurRecording() const;
|
||||
|
||||
// catch all for stats that have a defined sum
|
||||
template <typename T>
|
||||
typename T::value_t getPeriodMin(const TraceType<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
typename T::value_t min_val = std::numeric_limits<typename T::value_t>::max();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
min_val = llmin(min_val, mRecordingPeriods[index].getSum(stat));
|
||||
}
|
||||
return min_val;
|
||||
}
|
||||
|
||||
F64 getPeriodMin(const TraceType<SampleAccumulator>& stat, size_t num_periods = U32_MAX);
|
||||
template<typename T>
|
||||
T getPeriodMin(const SampleStatHandle<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
return T(getPeriodMin(static_cast<const TraceType<SampleAccumulator>&>(stat), num_periods));
|
||||
}
|
||||
|
||||
F64 getPeriodMin(const TraceType<EventAccumulator>& stat, size_t num_periods = U32_MAX);
|
||||
template<typename T>
|
||||
T getPeriodMin(const EventStatHandle<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
return T(getPeriodMin(static_cast<const TraceType<EventAccumulator>&>(stat), num_periods));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
F64 getPeriodMinPerSec(const TraceType<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
F64 min_val = std::numeric_limits<F64>::max();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
min_val = llmin(min_val, mRecordingPeriods[index].getPerSec(stat));
|
||||
}
|
||||
return min_val;
|
||||
}
|
||||
|
||||
// catch all for stats that have a defined sum
|
||||
template <typename T>
|
||||
typename T::value_t getPeriodMax(const TraceType<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
typename T::value_t max_val = std::numeric_limits<typename T::value_t>::min();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
max_val = llmax(max_val, mRecordingPeriods[index].getSum(stat));
|
||||
}
|
||||
return max_val;
|
||||
}
|
||||
|
||||
F64 getPeriodMax(const TraceType<SampleAccumulator>& stat, size_t num_periods = U32_MAX);
|
||||
template<typename T>
|
||||
T getPeriodMax(const SampleStatHandle<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
return T(getPeriodMax(static_cast<const TraceType<SampleAccumulator>&>(stat), num_periods));
|
||||
}
|
||||
|
||||
F64 getPeriodMax(const TraceType<EventAccumulator>& stat, size_t num_periods = U32_MAX);
|
||||
template<typename T>
|
||||
T getPeriodMax(const EventStatHandle<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
return T(getPeriodMax(static_cast<const TraceType<EventAccumulator>&>(stat), num_periods));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
F64 getPeriodMaxPerSec(const TraceType<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
F64 max_val = std::numeric_limits<F64>::min();
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
max_val = llmax(max_val, mRecordingPeriods[index].getPerSec(stat));
|
||||
}
|
||||
return max_val;
|
||||
}
|
||||
|
||||
// catch all for stats that have a defined sum
|
||||
template <typename T>
|
||||
typename T::mean_t getPeriodMean(const TraceType<T >& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
typename T::mean_t mean = 0;
|
||||
if (num_periods <= 0) { return mean; }
|
||||
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
if (mRecordingPeriods[index].getDuration() > 0.f)
|
||||
{
|
||||
mean += mRecordingPeriods[index].getSum(stat);
|
||||
}
|
||||
}
|
||||
mean = mean / num_periods;
|
||||
return mean;
|
||||
}
|
||||
|
||||
F64 getPeriodMean(const TraceType<SampleAccumulator>& stat, size_t num_periods = U32_MAX);
|
||||
template<typename T>
|
||||
T getPeriodMean(const SampleStatHandle<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
return T(getPeriodMean(static_cast<const TraceType<SampleAccumulator>&>(stat), num_periods));
|
||||
}
|
||||
|
||||
F64 getPeriodMean(const TraceType<EventAccumulator>& stat, size_t num_periods = U32_MAX);
|
||||
template<typename T>
|
||||
T getPeriodMean(const EventStatHandle<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
return T(getPeriodMean(static_cast<const TraceType<EventAccumulator>&>(stat), num_periods));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename T::mean_t getPeriodMeanPerSec(const TraceType<T>& stat, size_t num_periods = U32_MAX)
|
||||
{
|
||||
size_t total_periods = mRecordingPeriods.size();
|
||||
num_periods = llmin(num_periods, isStarted() ? total_periods - 1 : total_periods);
|
||||
|
||||
typename T::mean_t mean = 0;
|
||||
if (num_periods <= 0) { return mean; }
|
||||
|
||||
for (S32 i = 1; i <= num_periods; i++)
|
||||
{
|
||||
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
||||
if (mRecordingPeriods[index].getDuration() > 0.f)
|
||||
{
|
||||
mean += mRecordingPeriods[index].getPerSec(stat);
|
||||
}
|
||||
}
|
||||
mean = mean / num_periods;
|
||||
return mean;
|
||||
}
|
||||
|
||||
private:
|
||||
// implementation for LLStopWatchControlsMixin
|
||||
/*virtual*/ void handleStart();
|
||||
/*virtual*/ void handleStop();
|
||||
/*virtual*/ void handleReset();
|
||||
/*virtual*/ void handleSplitTo(PeriodicRecording& other);
|
||||
|
||||
private:
|
||||
std::vector<Recording> mRecordingPeriods;
|
||||
const bool mAutoResize;
|
||||
size_t mCurPeriod;
|
||||
size_t mNumPeriods;
|
||||
};
|
||||
|
||||
PeriodicRecording& get_frame_recording();
|
||||
|
||||
class ExtendableRecording
|
||||
: public LLStopWatchControlsMixin<ExtendableRecording>
|
||||
{
|
||||
public:
|
||||
void extend();
|
||||
|
||||
Recording& getAcceptedRecording() { return mAcceptedRecording; }
|
||||
const Recording& getAcceptedRecording() const {return mAcceptedRecording;}
|
||||
|
||||
Recording& getPotentialRecording() { return mPotentialRecording; }
|
||||
const Recording& getPotentialRecording() const { return mPotentialRecording;}
|
||||
|
||||
private:
|
||||
// implementation for LLStopWatchControlsMixin
|
||||
/*virtual*/ void handleStart();
|
||||
/*virtual*/ void handleStop();
|
||||
/*virtual*/ void handleReset();
|
||||
/*virtual*/ void handleSplitTo(ExtendableRecording& other);
|
||||
|
||||
private:
|
||||
Recording mAcceptedRecording;
|
||||
Recording mPotentialRecording;
|
||||
};
|
||||
|
||||
class ExtendablePeriodicRecording
|
||||
: public LLStopWatchControlsMixin<ExtendablePeriodicRecording>
|
||||
{
|
||||
public:
|
||||
ExtendablePeriodicRecording();
|
||||
void extend();
|
||||
|
||||
PeriodicRecording& getAcceptedRecording() { return mAcceptedRecording; }
|
||||
const PeriodicRecording& getAcceptedRecording() const {return mAcceptedRecording;}
|
||||
|
||||
PeriodicRecording& getPotentialRecording() { return mPotentialRecording; }
|
||||
const PeriodicRecording& getPotentialRecording() const {return mPotentialRecording;}
|
||||
|
||||
private:
|
||||
// implementation for LLStopWatchControlsMixin
|
||||
/*virtual*/ void handleStart();
|
||||
/*virtual*/ void handleStop();
|
||||
/*virtual*/ void handleReset();
|
||||
/*virtual*/ void handleSplitTo(ExtendablePeriodicRecording& other);
|
||||
|
||||
private:
|
||||
PeriodicRecording mAcceptedRecording;
|
||||
PeriodicRecording mPotentialRecording;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LL_LLTRACERECORDING_H
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/**
|
||||
* @file lltracethreadrecorder.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "lltracethreadrecorder.h"
|
||||
#include "llfasttimer.h"
|
||||
|
||||
namespace LLTrace
|
||||
{
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// ThreadRecorder
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
ThreadRecorder::ThreadRecorder()
|
||||
{
|
||||
//NB: the ordering of initialization in this function is very fragile due to a large number of implicit dependencies
|
||||
set_thread_recorder(this);
|
||||
TimeBlock& root_time_block = TimeBlock::getRootTimeBlock();
|
||||
|
||||
ThreadTimerStack* timer_stack = ThreadTimerStack::getInstance();
|
||||
timer_stack->mTimeBlock = &root_time_block;
|
||||
timer_stack->mActiveTimer = NULL;
|
||||
|
||||
mNumTimeBlockTreeNodes = AccumulatorBuffer<TimeBlockAccumulator>::getDefaultBuffer()->size();
|
||||
mTimeBlockTreeNodes = new TimeBlockTreeNode[mNumTimeBlockTreeNodes];
|
||||
|
||||
mThreadRecording.start();
|
||||
|
||||
// initialize time block parent pointers
|
||||
for (LLInstanceTracker<TimeBlock>::instance_iter it = LLInstanceTracker<TimeBlock>::beginInstances(), end_it = LLInstanceTracker<TimeBlock>::endInstances();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
TimeBlock& time_block = *it;
|
||||
TimeBlockTreeNode& tree_node = mTimeBlockTreeNodes[it->getIndex()];
|
||||
tree_node.mBlock = &time_block;
|
||||
tree_node.mParent = &root_time_block;
|
||||
|
||||
it->getPrimaryAccumulator()->mParent = &root_time_block;
|
||||
}
|
||||
|
||||
mRootTimer = new BlockTimer(root_time_block);
|
||||
timer_stack->mActiveTimer = mRootTimer;
|
||||
|
||||
TimeBlock::getRootTimeBlock().getPrimaryAccumulator()->mActiveCount = 1;
|
||||
}
|
||||
|
||||
ThreadRecorder::~ThreadRecorder()
|
||||
{
|
||||
delete mRootTimer;
|
||||
|
||||
if (!mActiveRecordings.empty())
|
||||
{
|
||||
std::for_each(mActiveRecordings.begin(), mActiveRecordings.end(), DeletePointer());
|
||||
mActiveRecordings.clear();
|
||||
}
|
||||
|
||||
set_thread_recorder(NULL);
|
||||
delete[] mTimeBlockTreeNodes;
|
||||
}
|
||||
|
||||
TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index)
|
||||
{
|
||||
if (0 <= index && index < mNumTimeBlockTreeNodes)
|
||||
{
|
||||
return &mTimeBlockTreeNodes[index];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void ThreadRecorder::activate( Recording* recording )
|
||||
{
|
||||
ActiveRecording* active_recording = new ActiveRecording(recording);
|
||||
if (!mActiveRecordings.empty())
|
||||
{
|
||||
mActiveRecordings.back()->mPartialRecording.handOffTo(active_recording->mPartialRecording);
|
||||
}
|
||||
mActiveRecordings.push_back(active_recording);
|
||||
|
||||
mActiveRecordings.back()->mPartialRecording.makePrimary();
|
||||
}
|
||||
|
||||
ThreadRecorder::active_recording_list_t::reverse_iterator ThreadRecorder::bringUpToDate( Recording* recording )
|
||||
{
|
||||
if (mActiveRecordings.empty()) return mActiveRecordings.rend();
|
||||
|
||||
mActiveRecordings.back()->mPartialRecording.flush();
|
||||
TimeBlock::updateTimes();
|
||||
|
||||
active_recording_list_t::reverse_iterator it, end_it;
|
||||
for (it = mActiveRecordings.rbegin(), end_it = mActiveRecordings.rend();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
ActiveRecording* cur_recording = *it;
|
||||
|
||||
active_recording_list_t::reverse_iterator next_it = it;
|
||||
++next_it;
|
||||
|
||||
// if we have another recording further down in the stack...
|
||||
if (next_it != mActiveRecordings.rend())
|
||||
{
|
||||
// ...push our gathered data down to it
|
||||
(*next_it)->mPartialRecording.append(cur_recording->mPartialRecording);
|
||||
}
|
||||
|
||||
// copy accumulated measurements into result buffer and clear accumulator (mPartialRecording)
|
||||
cur_recording->movePartialToTarget();
|
||||
|
||||
if (cur_recording->mTargetRecording == recording)
|
||||
{
|
||||
// found the recording, so return it
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == end_it)
|
||||
{
|
||||
llwarns << "Recording not active on this thread" << llendl;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
void ThreadRecorder::deactivate( Recording* recording )
|
||||
{
|
||||
active_recording_list_t::reverse_iterator it = bringUpToDate(recording);
|
||||
if (it != mActiveRecordings.rend())
|
||||
{
|
||||
// and if we've found the recording we wanted to update
|
||||
active_recording_list_t::reverse_iterator next_it = it;
|
||||
++next_it;
|
||||
if (next_it != mActiveRecordings.rend())
|
||||
{
|
||||
(*next_it)->mPartialRecording.makePrimary();
|
||||
}
|
||||
|
||||
active_recording_list_t::iterator recording_to_remove = (++it).base();
|
||||
llassert((*recording_to_remove)->mTargetRecording == recording);
|
||||
delete *recording_to_remove;
|
||||
mActiveRecordings.erase(recording_to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadRecorder::ActiveRecording::ActiveRecording( Recording* target )
|
||||
: mTargetRecording(target)
|
||||
{
|
||||
}
|
||||
|
||||
void ThreadRecorder::ActiveRecording::movePartialToTarget()
|
||||
{
|
||||
mTargetRecording->mBuffers.write()->append(mPartialRecording);
|
||||
// reset based on self to keep history
|
||||
mPartialRecording.reset(&mPartialRecording);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// SlaveThreadRecorder
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
SlaveThreadRecorder::SlaveThreadRecorder(MasterThreadRecorder& master)
|
||||
: mMasterRecorder(master)
|
||||
{
|
||||
mMasterRecorder.addSlaveThread(this);
|
||||
}
|
||||
|
||||
SlaveThreadRecorder::~SlaveThreadRecorder()
|
||||
{
|
||||
mMasterRecorder.removeSlaveThread(this);
|
||||
}
|
||||
|
||||
void SlaveThreadRecorder::pushToMaster()
|
||||
{
|
||||
mThreadRecording.stop();
|
||||
{
|
||||
LLMutexLock(mMasterRecorder.getSlaveListMutex());
|
||||
mSharedData.appendFrom(mThreadRecording);
|
||||
}
|
||||
mThreadRecording.start();
|
||||
}
|
||||
|
||||
void SlaveThreadRecorder::SharedData::appendFrom( const Recording& source )
|
||||
{
|
||||
LLMutexLock lock(&mRecordingMutex);
|
||||
appendRecording(source);
|
||||
}
|
||||
|
||||
void SlaveThreadRecorder::SharedData::appendTo( Recording& sink )
|
||||
{
|
||||
LLMutexLock lock(&mRecordingMutex);
|
||||
sink.appendRecording(*this);
|
||||
}
|
||||
|
||||
void SlaveThreadRecorder::SharedData::mergeFrom( const RecordingBuffers& source )
|
||||
{
|
||||
LLMutexLock lock(&mRecordingMutex);
|
||||
mBuffers.write()->merge(source);
|
||||
}
|
||||
|
||||
void SlaveThreadRecorder::SharedData::mergeTo( RecordingBuffers& sink )
|
||||
{
|
||||
LLMutexLock lock(&mRecordingMutex);
|
||||
sink.merge(*mBuffers);
|
||||
}
|
||||
|
||||
void SlaveThreadRecorder::SharedData::reset()
|
||||
{
|
||||
LLMutexLock lock(&mRecordingMutex);
|
||||
Recording::reset();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// MasterThreadRecorder
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static LLFastTimer::DeclareTimer FTM_PULL_TRACE_DATA_FROM_SLAVES("Pull slave trace data");
|
||||
void MasterThreadRecorder::pullFromSlaveThreads()
|
||||
{
|
||||
LLFastTimer _(FTM_PULL_TRACE_DATA_FROM_SLAVES);
|
||||
if (mActiveRecordings.empty()) return;
|
||||
|
||||
LLMutexLock lock(&mSlaveListMutex);
|
||||
|
||||
RecordingBuffers& target_recording_buffers = mActiveRecordings.back()->mPartialRecording;
|
||||
for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
// ignore block timing info for now
|
||||
(*it)->mSharedData.mergeTo(target_recording_buffers);
|
||||
(*it)->mSharedData.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void MasterThreadRecorder::addSlaveThread( class SlaveThreadRecorder* child )
|
||||
{
|
||||
LLMutexLock lock(&mSlaveListMutex);
|
||||
|
||||
mSlaveThreadRecorders.push_back(child);
|
||||
}
|
||||
|
||||
void MasterThreadRecorder::removeSlaveThread( class SlaveThreadRecorder* child )
|
||||
{
|
||||
LLMutexLock lock(&mSlaveListMutex);
|
||||
|
||||
for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
if ((*it) == child)
|
||||
{
|
||||
mSlaveThreadRecorders.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MasterThreadRecorder::pushToMaster()
|
||||
{}
|
||||
|
||||
MasterThreadRecorder::MasterThreadRecorder()
|
||||
{}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* @file lltrace.h
|
||||
* @brief Runtime statistics accumulation.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLTRACETHREADRECORDER_H
|
||||
#define LL_LLTRACETHREADRECORDER_H
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include "llpreprocessor.h"
|
||||
|
||||
#include "llmutex.h"
|
||||
#include "lltracerecording.h"
|
||||
|
||||
namespace LLTrace
|
||||
{
|
||||
class LL_COMMON_API ThreadRecorder
|
||||
{
|
||||
protected:
|
||||
struct ActiveRecording;
|
||||
typedef std::vector<ActiveRecording*> active_recording_list_t;
|
||||
public:
|
||||
ThreadRecorder();
|
||||
|
||||
virtual ~ThreadRecorder();
|
||||
|
||||
void activate(Recording* recording);
|
||||
void deactivate(Recording* recording);
|
||||
active_recording_list_t::reverse_iterator bringUpToDate(Recording* recording);
|
||||
|
||||
virtual void pushToMaster() = 0;
|
||||
|
||||
TimeBlockTreeNode* getTimeBlockTreeNode(S32 index);
|
||||
|
||||
protected:
|
||||
struct ActiveRecording
|
||||
{
|
||||
ActiveRecording(Recording* target);
|
||||
|
||||
Recording* mTargetRecording;
|
||||
RecordingBuffers mPartialRecording;
|
||||
|
||||
void movePartialToTarget();
|
||||
};
|
||||
Recording mThreadRecording;
|
||||
|
||||
active_recording_list_t mActiveRecordings;
|
||||
|
||||
class BlockTimer* mRootTimer;
|
||||
TimeBlockTreeNode* mTimeBlockTreeNodes;
|
||||
size_t mNumTimeBlockTreeNodes;
|
||||
};
|
||||
|
||||
class LL_COMMON_API MasterThreadRecorder : public ThreadRecorder
|
||||
{
|
||||
public:
|
||||
MasterThreadRecorder();
|
||||
|
||||
void addSlaveThread(class SlaveThreadRecorder* child);
|
||||
void removeSlaveThread(class SlaveThreadRecorder* child);
|
||||
|
||||
/*virtual */ void pushToMaster();
|
||||
|
||||
// call this periodically to gather stats data from slave threads
|
||||
void pullFromSlaveThreads();
|
||||
|
||||
LLMutex* getSlaveListMutex() { return &mSlaveListMutex; }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
typedef std::list<class SlaveThreadRecorder*> slave_thread_recorder_list_t;
|
||||
|
||||
slave_thread_recorder_list_t mSlaveThreadRecorders; // list of slave thread recorders associated with this master
|
||||
LLMutex mSlaveListMutex; // protects access to slave list
|
||||
};
|
||||
|
||||
class LL_COMMON_API SlaveThreadRecorder : public ThreadRecorder
|
||||
{
|
||||
public:
|
||||
SlaveThreadRecorder(MasterThreadRecorder& master);
|
||||
~SlaveThreadRecorder();
|
||||
|
||||
// call this periodically to gather stats data for master thread to consume
|
||||
/*virtual*/ void pushToMaster();
|
||||
|
||||
MasterThreadRecorder* mMaster;
|
||||
|
||||
class SharedData : public Recording
|
||||
{
|
||||
public:
|
||||
void appendFrom(const Recording& source);
|
||||
void appendTo(Recording& sink);
|
||||
void mergeFrom(const RecordingBuffers& source);
|
||||
void mergeTo(RecordingBuffers& sink);
|
||||
void reset();
|
||||
private:
|
||||
LLMutex mRecordingMutex;
|
||||
};
|
||||
SharedData mSharedData;
|
||||
MasterThreadRecorder& mMasterRecorder;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LL_LLTRACETHREADRECORDER_H
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/**
|
||||
* @file lltypeinfolookup.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2012-04-08
|
||||
* @brief Template data structure like std::map<std::type_info*, T>
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Copyright (c) 2012, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLTYPEINFOLOOKUP_H)
|
||||
#define LL_LLTYPEINFOLOOKUP_H
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <functional> // std::binary_function
|
||||
#include <typeinfo>
|
||||
|
||||
/**
|
||||
* The following helper classes are based on the Boost.Unordered documentation:
|
||||
* http://www.boost.org/doc/libs/1_45_0/doc/html/unordered/hash_equality.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compute hash for a string passed as const char*
|
||||
*/
|
||||
struct const_char_star_hash: public std::unary_function<const char*, std::size_t>
|
||||
{
|
||||
std::size_t operator()(const char* str) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
for ( ; *str; ++str)
|
||||
{
|
||||
boost::hash_combine(seed, *str);
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute equality for strings passed as const char*
|
||||
*
|
||||
* I (nat) suspect that this is where the default behavior breaks for the
|
||||
* const char* values returned from std::type_info::name(). If you compare the
|
||||
* two const char* pointer values, as a naive, unspecialized implementation
|
||||
* will surely do, they'll compare unequal.
|
||||
*/
|
||||
struct const_char_star_equal: public std::binary_function<const char*, const char*, bool>
|
||||
{
|
||||
bool operator()(const char* lhs, const char* rhs) const
|
||||
{
|
||||
return strcmp(lhs, rhs) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* LLTypeInfoLookup is specifically designed for use cases for which you might
|
||||
* consider std::map<std::type_info*, VALUE>. We have several such data
|
||||
* structures in the viewer. The trouble with them is that at least on Linux,
|
||||
* you can't rely on always getting the same std::type_info* for a given type:
|
||||
* different load modules will produce different std::type_info*.
|
||||
* LLTypeInfoLookup contains a workaround to address this issue.
|
||||
*
|
||||
* The API deliberately diverges from std::map in several respects:
|
||||
* * It avoids iterators, not only begin()/end() but also as return values
|
||||
* from insert() and find(). This bypasses transform_iterator overhead.
|
||||
* * Since we literally use compile-time types as keys, the essential insert()
|
||||
* and find() methods accept the key type as a @em template parameter,
|
||||
* accepting and returning value_type as a normal runtime value. This is to
|
||||
* permit future optimization (e.g. compile-time type hashing) without
|
||||
* changing the API.
|
||||
*/
|
||||
template <typename VALUE>
|
||||
class LLTypeInfoLookup
|
||||
{
|
||||
// Use this for our underlying implementation: lookup by
|
||||
// std::type_info::name() string. This is one of the rare cases in which I
|
||||
// dare use const char* directly, rather than std::string, because I'm
|
||||
// sure that every value returned by std::type_info::name() is static.
|
||||
// HOWEVER, specify our own hash + equality functors: naively comparing
|
||||
// distinct const char* values won't work.
|
||||
typedef boost::unordered_map<const char*, VALUE,
|
||||
const_char_star_hash, const_char_star_equal> impl_map_type;
|
||||
|
||||
public:
|
||||
typedef VALUE value_type;
|
||||
|
||||
LLTypeInfoLookup() {}
|
||||
|
||||
bool empty() const { return mMap.empty(); }
|
||||
std::size_t size() const { return mMap.size(); }
|
||||
|
||||
template <typename KEY>
|
||||
bool insert(const value_type& value)
|
||||
{
|
||||
// Obtain and store the std::type_info::name() string as the key.
|
||||
// Return just the bool from std::map::insert()'s return pair.
|
||||
return mMap.insert(typename impl_map_type::value_type(typeid(KEY).name(), value)).second;
|
||||
}
|
||||
|
||||
template <typename KEY>
|
||||
boost::optional<value_type> find() const
|
||||
{
|
||||
// Use the std::type_info::name() string as the key.
|
||||
typename impl_map_type::const_iterator found = mMap.find(typeid(KEY).name());
|
||||
if (found == mMap.end())
|
||||
return boost::optional<value_type>();
|
||||
return found->second;
|
||||
}
|
||||
|
||||
private:
|
||||
impl_map_type mMap;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLTYPEINFOLOOKUP_H) */
|
||||
|
|
@ -0,0 +1,563 @@
|
|||
/**
|
||||
* @file llunit.h
|
||||
* @brief Unit conversion classes
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLUNIT_H
|
||||
#define LL_LLUNIT_H
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include "llpreprocessor.h"
|
||||
#include "llerrorlegacy.h"
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE>
|
||||
struct LLUnit
|
||||
{
|
||||
typedef LLUnit<STORAGE_TYPE, UNIT_TYPE> self_t;
|
||||
typedef STORAGE_TYPE storage_t;
|
||||
|
||||
// value initialization
|
||||
LLUnit(storage_t value = storage_t())
|
||||
: mValue(value)
|
||||
{}
|
||||
|
||||
// unit initialization and conversion
|
||||
template<typename OTHER_STORAGE, typename OTHER_UNIT>
|
||||
LLUnit(LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
|
||||
: mValue(convert(other).mValue)
|
||||
{}
|
||||
|
||||
bool operator == (const self_t& other)
|
||||
{
|
||||
return mValue = other.mValue;
|
||||
}
|
||||
|
||||
// value assignment
|
||||
self_t& operator = (storage_t value)
|
||||
{
|
||||
mValue = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// unit assignment
|
||||
template<typename OTHER_STORAGE, typename OTHER_UNIT>
|
||||
self_t& operator = (LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
|
||||
{
|
||||
mValue = convert(other).mValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
storage_t value() const
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
template<typename NEW_UNIT_TYPE>
|
||||
STORAGE_TYPE getAs()
|
||||
{
|
||||
return LLUnit<STORAGE_TYPE, NEW_UNIT_TYPE>(*this).value();
|
||||
}
|
||||
|
||||
template<typename NEW_UNIT_TYPE>
|
||||
STORAGE_TYPE setAs(STORAGE_TYPE val)
|
||||
{
|
||||
*this = LLUnit<STORAGE_TYPE, NEW_UNIT_TYPE>(val);
|
||||
}
|
||||
|
||||
void operator += (storage_t value)
|
||||
{
|
||||
mValue += value;
|
||||
}
|
||||
|
||||
template<typename OTHER_STORAGE, typename OTHER_UNIT>
|
||||
void operator += (LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
|
||||
{
|
||||
mValue += convert(other).mValue;
|
||||
}
|
||||
|
||||
void operator -= (storage_t value)
|
||||
{
|
||||
mValue -= value;
|
||||
}
|
||||
|
||||
template<typename OTHER_STORAGE, typename OTHER_UNIT>
|
||||
void operator -= (LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
|
||||
{
|
||||
mValue -= convert(other).mValue;
|
||||
}
|
||||
|
||||
void operator *= (storage_t multiplicand)
|
||||
{
|
||||
mValue *= multiplicand;
|
||||
}
|
||||
|
||||
template<typename OTHER_UNIT, typename OTHER_STORAGE>
|
||||
void operator *= (LLUnit<OTHER_STORAGE, OTHER_UNIT> multiplicand)
|
||||
{
|
||||
// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
|
||||
LL_BAD_TEMPLATE_INSTANTIATION(OTHER_UNIT, "Multiplication of unit types not supported.");
|
||||
}
|
||||
|
||||
void operator /= (storage_t divisor)
|
||||
{
|
||||
mValue /= divisor;
|
||||
}
|
||||
|
||||
template<typename OTHER_UNIT, typename OTHER_STORAGE>
|
||||
void operator /= (LLUnit<OTHER_STORAGE, OTHER_UNIT> divisor)
|
||||
{
|
||||
// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
|
||||
LL_BAD_TEMPLATE_INSTANTIATION(OTHER_UNIT, "Illegal in-place division of unit types.");
|
||||
}
|
||||
|
||||
template<typename SOURCE_STORAGE, typename SOURCE_UNITS>
|
||||
static self_t convert(LLUnit<SOURCE_STORAGE, SOURCE_UNITS> v)
|
||||
{
|
||||
self_t result;
|
||||
ll_convert_units(v, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
storage_t mValue;
|
||||
};
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE>
|
||||
struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNIT_TYPE>
|
||||
{
|
||||
typedef LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> self_t;
|
||||
typedef typename LLUnit<STORAGE_TYPE, UNIT_TYPE>::storage_t storage_t;
|
||||
typedef LLUnit<STORAGE_TYPE, UNIT_TYPE> base_t;
|
||||
|
||||
LLUnitImplicit(storage_t value = storage_t())
|
||||
: base_t(value)
|
||||
{}
|
||||
|
||||
template<typename OTHER_STORAGE, typename OTHER_UNIT>
|
||||
LLUnitImplicit(LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
|
||||
: base_t(convert(other))
|
||||
{}
|
||||
|
||||
// unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD scalar (F32, S32, etc)
|
||||
// this allows for interoperability with legacy code
|
||||
operator storage_t() const
|
||||
{
|
||||
return base_t::value();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename S1, typename T1, typename S2, typename T2>
|
||||
LL_FORCE_INLINE void ll_convert_units(LLUnit<S1, T1> in, LLUnit<S2, T2>& out, ...)
|
||||
{
|
||||
typedef boost::integral_constant<bool,
|
||||
boost::is_same<T1, T2>::value
|
||||
|| !boost::is_same<T1, typename T1::base_unit_t>::value
|
||||
|| !boost::is_same<T2, typename T2::base_unit_t>::value> conversion_valid_t;
|
||||
LL_STATIC_ASSERT(conversion_valid_t::value, "invalid conversion");
|
||||
|
||||
if (boost::is_same<T1, typename T1::base_unit_t>::value)
|
||||
{
|
||||
if (boost::is_same<T2, typename T2::base_unit_t>::value)
|
||||
{
|
||||
// T1 and T2 fully reduced and equal...just copy
|
||||
out = (S2)in.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// reduce T2
|
||||
LLUnit<S2, typename T2::base_unit_t> new_out;
|
||||
ll_convert_units(in, new_out);
|
||||
ll_convert_units(new_out, out);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// reduce T1
|
||||
LLUnit<S1, typename T1::base_unit_t> new_in;
|
||||
ll_convert_units(in, new_in);
|
||||
ll_convert_units(new_in, out);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// operator +
|
||||
//
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
|
||||
{
|
||||
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
|
||||
result += second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator + (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
|
||||
result += second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator + (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
|
||||
{
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
|
||||
result += second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
|
||||
{
|
||||
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
|
||||
result += second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator + (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> result(first);
|
||||
result += second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
|
||||
{
|
||||
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
|
||||
result += second;
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// operator -
|
||||
//
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> operator - (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
|
||||
{
|
||||
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
|
||||
result -= second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator - (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
|
||||
result -= second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator - (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
|
||||
{
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
|
||||
result -= second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
|
||||
{
|
||||
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
|
||||
result -= second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator - (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> result(first);
|
||||
result -= second;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator - (SCALAR_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> second)
|
||||
{
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> result(first);
|
||||
result -= second;
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// operator *
|
||||
//
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator * (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
|
||||
{
|
||||
return LLUnit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first * second.value()));
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator * (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
return LLUnit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first.value() * second));
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> operator * (LLUnit<STORAGE_TYPE1, UNIT_TYPE1>, LLUnit<STORAGE_TYPE2, UNIT_TYPE2>)
|
||||
{
|
||||
// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
|
||||
LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "Multiplication of unit types results in new unit type - not supported.");
|
||||
return LLUnit<STORAGE_TYPE1, UNIT_TYPE1>();
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator * (SCALAR_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> second)
|
||||
{
|
||||
return LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>(first * second.value());
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator * (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
return LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>(first.value() * second);
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator * (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1>, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2>)
|
||||
{
|
||||
// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
|
||||
LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "Multiplication of unit types results in new unit type - not supported.");
|
||||
return LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1>();
|
||||
}
|
||||
|
||||
//
|
||||
// operator /
|
||||
//
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
SCALAR_TYPE operator / (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
|
||||
{
|
||||
return SCALAR_TYPE(first / second.value());
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator / (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
return LLUnit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first.value() / second));
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
STORAGE_TYPE1 operator / (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
|
||||
{
|
||||
return STORAGE_TYPE1(first.value() / first.convert(second));
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
|
||||
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator / (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
|
||||
{
|
||||
return LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first.value() / second));
|
||||
}
|
||||
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
|
||||
STORAGE_TYPE1 operator / (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
|
||||
{
|
||||
return STORAGE_TYPE1(first.value() / first.convert(second));
|
||||
}
|
||||
|
||||
#define COMPARISON_OPERATORS(op) \
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE> \
|
||||
bool operator op (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second) \
|
||||
{ \
|
||||
return first op second.value(); \
|
||||
} \
|
||||
\
|
||||
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE> \
|
||||
bool operator op (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second) \
|
||||
{ \
|
||||
return first.value() op second; \
|
||||
} \
|
||||
\
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2> \
|
||||
bool operator op (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second) \
|
||||
{ \
|
||||
return first.value() op first.convert(second); \
|
||||
} \
|
||||
\
|
||||
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2> \
|
||||
bool operator op (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second) \
|
||||
{ \
|
||||
return first.value() op first.convert(second); \
|
||||
}
|
||||
|
||||
COMPARISON_OPERATORS(<)
|
||||
COMPARISON_OPERATORS(<=)
|
||||
COMPARISON_OPERATORS(>)
|
||||
COMPARISON_OPERATORS(>=)
|
||||
COMPARISON_OPERATORS(==)
|
||||
COMPARISON_OPERATORS(!=)
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct LLGetUnitLabel
|
||||
{
|
||||
static const char* getUnitLabel() { return ""; }
|
||||
};
|
||||
|
||||
template<typename T, typename STORAGE_T>
|
||||
struct LLGetUnitLabel<LLUnit<STORAGE_T, T> >
|
||||
{
|
||||
static const char* getUnitLabel() { return T::getUnitLabel(); }
|
||||
};
|
||||
|
||||
template<typename VALUE_TYPE>
|
||||
struct LLUnitLinearOps
|
||||
{
|
||||
typedef LLUnitLinearOps<VALUE_TYPE> self_t;
|
||||
LLUnitLinearOps(VALUE_TYPE val) : mValue (val) {}
|
||||
|
||||
operator VALUE_TYPE() const { return mValue; }
|
||||
VALUE_TYPE mValue;
|
||||
|
||||
template<typename T>
|
||||
self_t operator * (T other)
|
||||
{
|
||||
return mValue * other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t operator / (T other)
|
||||
{
|
||||
return mValue / other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t operator + (T other)
|
||||
{
|
||||
return mValue + other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t operator - (T other)
|
||||
{
|
||||
return mValue - other;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename VALUE_TYPE>
|
||||
struct LLUnitInverseLinearOps
|
||||
{
|
||||
typedef LLUnitInverseLinearOps<VALUE_TYPE> self_t;
|
||||
|
||||
LLUnitInverseLinearOps(VALUE_TYPE val) : mValue (val) {}
|
||||
operator VALUE_TYPE() const { return mValue; }
|
||||
VALUE_TYPE mValue;
|
||||
|
||||
template<typename T>
|
||||
self_t operator * (T other)
|
||||
{
|
||||
return mValue / other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t operator / (T other)
|
||||
{
|
||||
return mValue * other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t operator + (T other)
|
||||
{
|
||||
return mValue - other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t operator - (T other)
|
||||
{
|
||||
return mValue + other;
|
||||
}
|
||||
};
|
||||
|
||||
#define LL_DECLARE_BASE_UNIT(base_unit_name, unit_label) \
|
||||
struct base_unit_name { typedef base_unit_name base_unit_t; static const char* getUnitLabel() { return unit_label; }}
|
||||
|
||||
#define LL_DECLARE_DERIVED_UNIT(unit_name, unit_label, base_unit_name, conversion_operation) \
|
||||
struct unit_name \
|
||||
{ \
|
||||
typedef base_unit_name base_unit_t; \
|
||||
static const char* getUnitLabel() { return unit_label; } \
|
||||
}; \
|
||||
\
|
||||
template<typename S1, typename S2> \
|
||||
void ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out) \
|
||||
{ \
|
||||
out = (S2)(LLUnitLinearOps<S1>(in.value()) conversion_operation).mValue; \
|
||||
} \
|
||||
\
|
||||
template<typename S1, typename S2> \
|
||||
void ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out) \
|
||||
{ \
|
||||
out = (S2)(LLUnitInverseLinearOps<S1>(in.value()) conversion_operation).mValue; \
|
||||
}
|
||||
|
||||
//
|
||||
// Unit declarations
|
||||
//
|
||||
|
||||
namespace LLUnits
|
||||
{
|
||||
LL_DECLARE_BASE_UNIT(Bytes, "B");
|
||||
LL_DECLARE_DERIVED_UNIT(Kilobytes, "KB", Bytes, * 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Megabytes, "MB", Kilobytes, * 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Gigabytes, "GB", Megabytes, * 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Kibibytes, "KiB", Bytes, * 1024);
|
||||
LL_DECLARE_DERIVED_UNIT(Mibibytes, "MiB", Kibibytes, * 1024);
|
||||
LL_DECLARE_DERIVED_UNIT(Gibibytes, "GiB", Mibibytes, * 1024);
|
||||
|
||||
LL_DECLARE_DERIVED_UNIT(Bits, "b", Bytes, / 8);
|
||||
LL_DECLARE_DERIVED_UNIT(Kilobits, "Kb", Bytes, * 1000 / 8);
|
||||
LL_DECLARE_DERIVED_UNIT(Megabits, "Mb", Kilobits, * 1000 / 8);
|
||||
LL_DECLARE_DERIVED_UNIT(Gigabits, "Gb", Megabits, * 1000 / 8);
|
||||
LL_DECLARE_DERIVED_UNIT(Kibibits, "Kib", Bytes, * 1024 / 8);
|
||||
LL_DECLARE_DERIVED_UNIT(Mibibits, "Mib", Kibibits, * 1024 / 8);
|
||||
LL_DECLARE_DERIVED_UNIT(Gibibits, "Gib", Mibibits, * 1024 / 8);
|
||||
|
||||
LL_DECLARE_BASE_UNIT(Seconds, "s");
|
||||
LL_DECLARE_DERIVED_UNIT(Minutes, "min", Seconds, * 60);
|
||||
LL_DECLARE_DERIVED_UNIT(Hours, "h", Seconds, * 60 * 60);
|
||||
LL_DECLARE_DERIVED_UNIT(Milliseconds, "ms", Seconds, / 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Microseconds, "\x09\x3cs", Milliseconds, / 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Nanoseconds, "ns", Microseconds, / 1000);
|
||||
|
||||
LL_DECLARE_BASE_UNIT(Meters, "m");
|
||||
LL_DECLARE_DERIVED_UNIT(Kilometers, "km", Meters, * 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Centimeters, "cm", Meters, / 100);
|
||||
LL_DECLARE_DERIVED_UNIT(Millimeters, "mm", Meters, / 1000);
|
||||
|
||||
LL_DECLARE_BASE_UNIT(Hertz, "Hz");
|
||||
LL_DECLARE_DERIVED_UNIT(Kilohertz, "KHz", Hertz, * 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Megahertz, "MHz", Kilohertz, * 1000);
|
||||
LL_DECLARE_DERIVED_UNIT(Gigahertz, "GHz", Megahertz, * 1000);
|
||||
|
||||
LL_DECLARE_BASE_UNIT(Radians, "rad");
|
||||
LL_DECLARE_DERIVED_UNIT(Degrees, "deg", Radians, * 0.01745329251994);
|
||||
|
||||
|
||||
} // namespace LLUnits
|
||||
|
||||
#endif // LL_LLUNIT_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue