Compare commits

..

9 Commits

Author SHA1 Message Date
Siana Gearz
8e19372adf Merge branch 'V2Baking' of git://github.com/Shyotl/SingularityViewer into V2Baking
Conflicts:
	indra/llrender/llshadermgr.cpp
	indra/newview/llmorphview.cpp
	indra/newview/lltexlayer.cpp
	indra/newview/lltexlayer.h
	indra/newview/lltoolmorph.cpp
	indra/newview/llvoavatar.cpp
	indra/newview/llvoavatar.h
2012-01-06 14:18:46 +01:00
Shyotl
6381e4d9a4 Merge branch 'future' into V2Baking 2011-10-31 20:41:58 -05:00
Shyotl
d9f674218f Lineending fixup 2011-10-31 20:38:41 -05:00
Shyotl
693e3a0555 vary_texture_index only needed in shaders when batching. 2011-10-31 20:38:29 -05:00
Shyotl
8376e890b2 Hide the mouse cursor a bit better when grabbing items in mouselook. 2011-10-31 20:37:11 -05:00
Shyotl
33827bf8b0 Added a bit of debug output for viewer tool states. 2011-10-31 20:36:47 -05:00
Shyotl
06349951d2 Added tracking of requested(and pending) attachments. Moved Ascent code out of attach/detach functions and into their own for better manageability. 2011-10-27 21:41:19 -05:00
Shyotl
4d68dc7c57 LLTexLayer refactored. Tried to keep behavior changes as minimal as possible. Consider as base slate for changes required for multi-wearables. 2011-10-25 16:50:39 -05:00
Shyotl
722e7d09ff Innitial commit. Majority of texture baking moved into LLVOAvatarSelf. Very WIP. Not advisable to build off of this, although it does compile and mostly work. 2011-10-22 20:36:04 -05:00
658 changed files with 32585 additions and 51808 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,4 @@
/installed.xml /installed.xml
/indra/llcommon/llversionviewer.h
/indra/build-* /indra/build-*
/indra/tools/vstool/obj/ /indra/tools/vstool/obj/
*.aps *.aps
@@ -12,13 +11,11 @@
/indra/viewer-* /indra/viewer-*
/indra/newview/vivox-runtime/ /indra/newview/vivox-runtime/
/libraries/ /libraries/
/lib/
*.pyc *.pyc
*.orig *.orig
*.rej *.rej
*.bak *.bak
*~ *~
*.DS_Store
/LICENSES/ /LICENSES/
/edited-files.txt /edited-files.txt
qtcreator-build/ qtcreator-build/

23
LICENSES/fontconfig.txt Normal file
View File

@@ -0,0 +1,23 @@
http://www.jclark.com/xml/copying.txt
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -670,19 +670,19 @@
<key>EstateChangeInfo</key> <key>EstateChangeInfo</key>
<boolean>true</boolean> <boolean>true</boolean>
<key>FetchInventoryDescendents2</key> <key>FetchInventoryDescendents</key>
<boolean>false</boolean> <boolean>false</boolean>
<key>WebFetchInventoryDescendents</key> <key>WebFetchInventoryDescendents</key>
<boolean>false</boolean> <boolean>false</boolean>
<key>FetchInventory2</key> <key>FetchInventory</key>
<boolean>true</boolean> <boolean>true</boolean>
<key>FetchLibDescendents2</key> <key>FetchLibDescendents</key>
<boolean>true</boolean> <boolean>true</boolean>
<key>FetchLib2</key> <key>FetchLib</key>
<boolean>true</boolean> <boolean>true</boolean>
</map> </map>

View File

@@ -26,7 +26,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(Variables) include(Variables)
# Load versions now. Install locations need them. # Load versions now. Install locations need them.
include(BuildVersion) include(Versions)
include(UnixInstall) include(UnixInstall)

View File

@@ -28,9 +28,6 @@ set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;ReleaseSSE2;Debug" CACHE S
# Platform-specific compilation flags. # Platform-specific compilation flags.
if (WINDOWS) if (WINDOWS)
# Remove default /Zm1000 flag that cmake inserts
string (REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# Don't build DLLs. # Don't build DLLs.
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF)
@@ -70,10 +67,6 @@ if (WINDOWS)
/Oy- /Oy-
) )
# configure win32 API for windows XP+ compatibility
set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)")
add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}")
if(MSVC80 OR MSVC90 OR MSVC10) if(MSVC80 OR MSVC90 OR MSVC10)
set(CMAKE_CXX_FLAGS_RELEASE set(CMAKE_CXX_FLAGS_RELEASE
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0" "${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
@@ -120,7 +113,7 @@ if (WINDOWS)
endif (WINDOWS) endif (WINDOWS)
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math") set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math -frounding-math")
if (LINUX) if (LINUX)
set(CMAKE_SKIP_RPATH TRUE) set(CMAKE_SKIP_RPATH TRUE)
@@ -163,7 +156,7 @@ if (LINUX)
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat") endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION}) endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
#Lets actually get a numerical version of gxx's version #Lets actualy get a numerical version of gxx's version
STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION}) STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION})
#gcc 4.3 and above don't like the LL boost #gcc 4.3 and above don't like the LL boost
@@ -189,8 +182,6 @@ if (LINUX)
-pthread -pthread
) )
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
add_definitions(-DAPPID=secondlife) add_definitions(-DAPPID=secondlife)
add_definitions(-fvisibility=hidden) add_definitions(-fvisibility=hidden)
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway. # don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
@@ -209,10 +200,10 @@ if (LINUX)
if (NOT STANDALONE) if (NOT STANDALONE)
set(MARCH_FLAG " -march=pentium4") set(MARCH_FLAG " -march=pentium4")
endif (NOT STANDALONE) endif (NOT STANDALONE)
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
endif (${ARCH} STREQUAL "x86_64") endif (${ARCH} STREQUAL "x86_64")
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2") set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")

View File

@@ -29,12 +29,12 @@ else (STANDALONE)
) )
elseif (DARWIN) elseif (DARWIN)
set(APR_LIBRARIES set(APR_LIBRARIES
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.dylib debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.3.7.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.dylib optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib
) )
set(APRUTIL_LIBRARIES set(APRUTIL_LIBRARIES
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.dylib debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.3.8.dylib
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.dylib optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib
) )
set(APRICONV_LIBRARIES iconv) set(APRICONV_LIBRARIES iconv)
else (WINDOWS) else (WINDOWS)

View File

@@ -1,45 +1,23 @@
# -*- cmake -*- # -*- cmake -*-
# Read version components from the header file. function (build_version _target)
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversionviewer.h.in lines # Read version components from the header file.
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversion${_target}.h lines
REGEX " LL_VERSION_") REGEX " LL_VERSION_")
foreach(line ${lines}) foreach(line ${lines})
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}") string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}") string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
set(v${comp} "${value}") set(v${comp} "${value}")
endforeach(line) endforeach(line)
execute_process( # Compose the version.
COMMAND git rev-list HEAD set(${_target}_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
OUTPUT_VARIABLE GIT_REV_LIST_STR if (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} message(STATUS "Version of ${_target} is ${${_target}_VERSION}")
OUTPUT_STRIP_TRAILING_WHITESPACE else (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
) message(FATAL_ERROR "Could not determine ${_target} version (${${_target}_VERSION})")
endif (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
if(GIT_REV_LIST_STR) # Report version to caller.
string(REPLACE "\n" ";" GIT_REV_LIST ${GIT_REV_LIST_STR}) set(${_target}_VERSION "${${_target}_VERSION}" PARENT_SCOPE)
else() endfunction (build_version)
string(REPLACE "\n" ";" GIT_REV_LIST "")
endif()
if(GIT_REV_LIST)
list(LENGTH GIT_REV_LIST vBUILD)
else()
set(vBUILD 99)
endif()
configure_file(
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h.in
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h
)
# Compose the version.
set(viewer_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
if (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(STATUS "Version is ${viewer_VERSION}")
else (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
message(FATAL_ERROR "Could not determine version (${viewer_VERSION})")
endif (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
# Report version to caller.
#set(viewer_VERSION "${viewer_VERSION}" PARENT_SCOPE)

View File

@@ -19,5 +19,5 @@ else (STANDALONE)
else (WINDOWS) else (WINDOWS)
set(CARES_LIBRARIES cares) set(CARES_LIBRARIES cares)
endif (WINDOWS) endif (WINDOWS)
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares ${LIBS_PREBUILT_DIR}/include/ares ) set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares)
endif (STANDALONE) endif (STANDALONE)

View File

@@ -79,6 +79,7 @@ set(cmake_SOURCE_FILES
UI.cmake UI.cmake
UnixInstall.cmake UnixInstall.cmake
Variables.cmake Variables.cmake
Versions.cmake
XmlRpcEpi.cmake XmlRpcEpi.cmake
ZLIB.cmake ZLIB.cmake
) )

View File

@@ -7,13 +7,26 @@ if(INSTALL_PROPRIETARY)
use_prebuilt_binary(fmod) use_prebuilt_binary(fmod)
endif(INSTALL_PROPRIETARY) endif(INSTALL_PROPRIETARY)
find_library(FMOD_LIBRARY find_library(FMOD_LIBRARY_RELEASE
NAMES fmod fmodvc fmod-3.75 NAMES fmod fmodvc fmod-3.75
PATHS PATHS
optimized ${ARCH_PREBUILT_DIRS_RELEASE} ${ARCH_PREBUILT_DIRS_RELEASE}
debug ${ARCH_PREBUILT_DIRS_DEBUG}
) )
find_library(FMOD_LIBRARY_DEBUG
NAMES fmod fmodvc fmod-3.75
PATHS
${ARCH_PREBUILT_DIRS_DEBUG}
)
if (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
set(FMOD_LIBRARY
debug ${FMOD_LIBRARY_DEBUG}
optimized ${FMOD_LIBRARY_RELEASE})
elseif (FMOD_LIBRARY_RELEASE)
set(FMOD_LIBRARY ${FMOD_LIBRARY_RELEASE})
endif (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
if (NOT FMOD_LIBRARY) if (NOT FMOD_LIBRARY)
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.") set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
if (FMOD_SDK_DIR) if (FMOD_SDK_DIR)

View File

@@ -7,13 +7,26 @@ if(INSTALL_PROPRIETARY)
use_prebuilt_binary(fmodex) use_prebuilt_binary(fmodex)
endif(INSTALL_PROPRIETARY) endif(INSTALL_PROPRIETARY)
find_library(FMODEX_LIBRARY find_library(FMODEX_LIBRARY_RELEASE
NAMES fmodex fmodex_vc fmodexL_vc NAMES fmodex fmodex_vc fmodexL_vc
PATHS PATHS
optimized ${ARCH_PREBUILT_DIRS_RELEASE} ${ARCH_PREBUILT_DIRS_RELEASE}
debug ${ARCH_PREBUILT_DIRS_DEBUG}
) )
find_library(FMODEX_LIBRARY_DEBUG
NAMES fmodex fmodex_vc fmodexL_vc
PATHS
${ARCH_PREBUILT_DIRS_DEBUG}
)
if (FMODEX_LIBRARY_RELEASE AND FMODEX_LIBRARY_DEBUG)
set(FMODEX_LIBRARY
debug ${FMODEX_LIBRARY_DEBUG}
optimized ${FMODEX_LIBRARY_RELEASE})
elseif (FMODEX_LIBRARY_RELEASE)
set(FMODEX_LIBRARY ${FMODEX_LIBRARY_RELEASE})
endif (FMODEX_LIBRARY_RELEASE AND FMODEX_LIBRARY_DEBUG)
if (NOT FMODEX_LIBRARY) if (NOT FMODEX_LIBRARY)
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
if (FMODEX_SDK_DIR) if (FMODEX_SDK_DIR)

View File

@@ -9,7 +9,7 @@
# also defined, but not for general use are # also defined, but not for general use are
# JSONCPP_LIBRARY, where to find the jsoncpp library. # JSONCPP_LIBRARY, where to find the jsoncpp library.
FIND_PATH(JSONCPP_INCLUDE_DIR json/json.h FIND_PATH(JSONCPP_INCLUDE_DIR jsoncpp/json.h
/usr/local/include /usr/local/include
/usr/include /usr/include
) )

View File

@@ -1,14 +1,10 @@
# -*- cmake -*- # -*- cmake -*-
include(Prebuilt) include(Prebuilt)
if(WORD_SIZE EQUAL 64)
set(DISABLE_TCMALLOC TRUE)
endif()
if (STANDALONE) if (STANDALONE)
include(FindGooglePerfTools) include(FindGooglePerfTools)
else (STANDALONE) else (STANDALONE)
if (LINUX OR WINDOWS AND NOT WORD_SIZE EQUAL 64) if (LINUX OR WINDOWS)
use_prebuilt_binary(google) use_prebuilt_binary(google)
endif (LINUX OR WINDOWS) endif (LINUX OR WINDOWS)
if (WINDOWS) if (WINDOWS)
@@ -34,6 +30,10 @@ else ()
set(USE_GOOGLE_PERFTOOLS OFF) set(USE_GOOGLE_PERFTOOLS OFF)
endif () endif ()
# XXX Disable temporarily, until we have compilation issues on 64-bit
# Etch sorted.
#set(USE_GOOGLE_PERFTOOLS OFF)
if (USE_GOOGLE_PERFTOOLS) if (USE_GOOGLE_PERFTOOLS)
set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1) set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR}) include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})

View File

@@ -14,7 +14,6 @@ endif (DARWIN)
set(LLCOMMON_INCLUDE_DIRS set(LLCOMMON_INCLUDE_DIRS
${LIBS_OPEN_DIR}/cwdebug ${LIBS_OPEN_DIR}/cwdebug
${LIBS_OPEN_DIR}/llcommon ${LIBS_OPEN_DIR}/llcommon
${APRUTIL_INCLUDE_DIR}
${APR_INCLUDE_DIR} ${APR_INCLUDE_DIR}
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
) )

View File

@@ -19,6 +19,7 @@ if (WINDOWS)
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
) )
elseif (EXISTS /etc/debian_version) elseif (EXISTS /etc/debian_version)
# On Debian and Ubuntu, avoid Python 2.4 if possible. # On Debian and Ubuntu, avoid Python 2.4 if possible.

View File

@@ -38,7 +38,6 @@ else (STANDALONE)
if (LINUX) if (LINUX)
set(UI_LIBRARIES set(UI_LIBRARIES
atk-1.0 atk-1.0
X11
gdk-x11-2.0 gdk-x11-2.0
gdk_pixbuf-2.0 gdk_pixbuf-2.0
Xinerama Xinerama

View File

@@ -0,0 +1,9 @@
include(BuildVersion)
if(VIEWER)
build_version(viewer)
endif(VIEWER)
if(SERVER)
build_version(server)
endif(SERVER)

View File

@@ -67,19 +67,12 @@ elseif (LINUX)
QtNetwork QtNetwork
QtGui QtGui
QtCore QtCore
crypto jscore
ssl
# qgif
# qjpeg
jpeg jpeg
fontconfig fontconfig
X11 X11
Xrender Xrender
Xext
GL GL
) )
if (WORD_SIZE EQUAL 32)
set(WEBKIT_PLUGIN_LIBRARIES ${WEBKIT_PLUGIN_LIBRARIES} jscore)
endif (WORD_SIZE EQUAL 32)
endif (STANDALONE) endif (STANDALONE)
endif (WINDOWS) endif (WINDOWS)

View File

@@ -171,8 +171,7 @@ void stop_recording_backtraces(void)
channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk. channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk.
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking. channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces. channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine. channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used class AIStateMachine.
channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities.
} // namespace dc } // namespace dc
} // namespace DEBUGCHANNELS } // namespace DEBUGCHANNELS

View File

@@ -117,7 +117,6 @@ extern CWD_API channel_ct gtk;
extern CWD_API channel_ct sdl; extern CWD_API channel_ct sdl;
extern CWD_API channel_ct backtrace; extern CWD_API channel_ct backtrace;
extern CWD_API channel_ct statemachine; extern CWD_API channel_ct statemachine;
extern CWD_API channel_ct caps;
#endif #endif

View File

@@ -777,6 +777,7 @@ Options:
Commands: Commands:
build configure and build default target build configure and build default target
clean delete all build directories, does not affect sources clean delete all build directories, does not affect sources
configure configure project by running cmake (default command if none given)
configure configure project by running cmake (default if none given) configure configure project by running cmake (default if none given)
printbuilddirs print the build directory that will be used printbuilddirs print the build directory that will be used

View File

@@ -168,10 +168,7 @@ ARGUMENTS=[
dict(name='version', dict(name='version',
description="""This specifies the version of Second Life that is description="""This specifies the version of Second Life that is
being packaged up.""", being packaged up.""",
default=get_default_version), default=get_default_version)
dict(name='extra_libraries',
description="""List of extra libraries to include in package""",
default=None)
] ]
def usage(srctree=""): def usage(srctree=""):

View File

@@ -570,7 +570,7 @@ static void v4dwt_interleave_h(v4dwt_t* restrict w, float* restrict a, int x, in
int count = w->sn; int count = w->sn;
int i, k; int i, k;
for(k = 0; k < 2; ++k){ for(k = 0; k < 2; ++k){
if (count + 3 * x < size && ((long) a & 0x0f) == 0 && ((long) bi & 0x0f) == 0 && (x & 0x0f) == 0) { if (count + 3 * x < size && ((int) a & 0x0f) == 0 && ((int) bi & 0x0f) == 0 && (x & 0x0f) == 0) {
/* Fast code path */ /* Fast code path */
for(i = 0; i < count; ++i){ for(i = 0; i < count; ++i){
int j = i; int j = i;

View File

@@ -47,11 +47,18 @@ target_link_libraries(linux-crash-logger
${LLVFS_LIBRARIES} ${LLVFS_LIBRARIES}
${LLXML_LIBRARIES} ${LLXML_LIBRARIES}
${LLMESSAGE_LIBRARIES} ${LLMESSAGE_LIBRARIES}
${LLUI_LIBRARIES}
${LLVFS_LIBRARIES} ${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES} ${LLMATH_LIBRARIES}
${LLCOMMON_LIBRARIES} ${LLCOMMON_LIBRARIES}
${UI_LIBRARIES} ${UI_LIBRARIES}
${DB_LIBRARIES} ${DB_LIBRARIES}
${XMLRPCEPI_LIBRARIES}
${CURL_LIBRARIES}
${APR_LIBRARIES}
${APRUTIL_LIBRARIES}
${CRYPTO_LIBRARIES}
rt
) )
add_custom_command( add_custom_command(

View File

@@ -239,15 +239,12 @@ protected:
// A list of all audio sources that are known to the viewer at this time. // A list of all audio sources that are known to the viewer at this time.
// This is most likely a superset of the ones that we actually have audio // This is most likely a superset of the ones that we actually have audio
// data for, or are playing back. // data for, or are playing back.
public://Jay: IDGAF
typedef std::map<LLUUID, LLAudioSource *> source_map; typedef std::map<LLUUID, LLAudioSource *> source_map;
protected:
typedef std::map<LLUUID, LLAudioData *> data_map; typedef std::map<LLUUID, LLAudioData *> data_map;
public://Jay: IDGAF
source_map mAllSources; source_map mAllSources;
protected:
data_map mAllData; data_map mAllData;
LLAudioChannel *mChannels[MAX_CHANNELS]; LLAudioChannel *mChannels[MAX_CHANNELS];
// Buffers needs to change into a different data structure, as the number of buffers // Buffers needs to change into a different data structure, as the number of buffers
@@ -354,9 +351,6 @@ public:
protected: protected:
LLUUID mID; // The ID of the source is that of the object if it's attached to an object. LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
LLUUID mOwnerID; // owner of the object playing the sound LLUUID mOwnerID; // owner of the object playing the sound
public:
const LLUUID &getOwnerID() { return mOwnerID; }
protected:
F32 mPriority; F32 mPriority;
F32 mGain; F32 mGain;
bool mSourceMuted; bool mSourceMuted;

View File

@@ -53,11 +53,7 @@ class LLStreamingAudioInterface
virtual void setGain(F32 vol) = 0; virtual void setGain(F32 vol) = 0;
virtual F32 getGain() = 0; virtual F32 getGain() = 0;
virtual std::string getURL() = 0; virtual std::string getURL() = 0;
virtual const LLSD *getMetaData() = 0; //return NULL if not supported.
virtual bool supportsMetaData() = 0;
virtual const LLSD *getMetaData() = 0;
virtual bool supportsWaveData() = 0;
virtual bool getWaveData(float* arr, S32 count, S32 stride = 1) = 0;
}; };
#endif // LL_STREAMINGAUDIO_H #endif // LL_STREAMINGAUDIO_H

View File

@@ -54,11 +54,8 @@ class LLStreamingAudio_FMOD : public LLStreamingAudioInterface
/*virtual*/ void setGain(F32 vol); /*virtual*/ void setGain(F32 vol);
/*virtual*/ F32 getGain(); /*virtual*/ F32 getGain();
/*virtual*/ std::string getURL(); /*virtual*/ std::string getURL();
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not supported.
/*virtual*/ bool supportsMetaData(){return true;}
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing.
/*virtual*/ bool supportsWaveData(){return false;}
/*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1){return false;}
private: private:
LLAudioStreamManagerFMOD *mCurrentInternetStreamp; LLAudioStreamManagerFMOD *mCurrentInternetStreamp;
int mFMODInternetStreamChannel; int mFMODInternetStreamChannel;

View File

@@ -74,9 +74,7 @@ LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
{ {
// Number of milliseconds of audio to buffer for the audio card. // Number of milliseconds of audio to buffer for the audio card.
// Must be larger than the usual Second Life frame stutter time. // Must be larger than the usual Second Life frame stutter time.
const U32 buffer_seconds = 5; //sec mSystem->setStreamBufferSize(200, FMOD_TIMEUNIT_MS);
const U32 estimated_bitrate = 128; //kbit/sec
mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
// Here's where we set the size of the network buffer and some buffering // Here's where we set the size of the network buffer and some buffering
// parameters. In this case we want a network buffer of 16k, we want it // parameters. In this case we want a network buffer of 16k, we want it
@@ -336,27 +334,6 @@ void LLStreamingAudio_FMODEX::setGain(F32 vol)
} }
} }
/*virtual*/ bool LLStreamingAudio_FMODEX::getWaveData(float* arr, S32 count, S32 stride/*=1*/)
{
if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp)
return false;
static std::vector<float> local_array(count); //Have to have an extra buffer to mix channels. Bleh.
if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink.
local_array.resize(count);
if( mFMODInternetStreamChannelp->getWaveData(&local_array[0],count,0) == FMOD_OK &&
mFMODInternetStreamChannelp->getWaveData(&arr[0],count,1) == FMOD_OK )
{
for(S32 i = count-1;i>=0;i-=stride)
{
arr[i] += local_array[i];
arr[i] *= .5f;
}
return true;
}
return false;
}
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// manager of possibly-multiple internet audio streams // manager of possibly-multiple internet audio streams
@@ -374,7 +351,7 @@ LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, con
exinfo.cbsize = sizeof(exinfo); exinfo.cbsize = sizeof(exinfo);
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG; //Hint to speed up loading. exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG; //Hint to speed up loading.
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, &exinfo, &mInternetStream); FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING, &exinfo, &mInternetStream);
if (result!= FMOD_OK) if (result!= FMOD_OK)
{ {

View File

@@ -61,11 +61,8 @@ class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface
/*virtual*/ void setGain(F32 vol); /*virtual*/ void setGain(F32 vol);
/*virtual*/ F32 getGain(); /*virtual*/ F32 getGain();
/*virtual*/ std::string getURL(); /*virtual*/ std::string getURL();
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not supported.
/*virtual*/ bool supportsMetaData(){return true;}
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing.
/*virtual*/ bool supportsWaveData(){return true;}
/*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1);
private: private:
FMOD::System *mSystem; FMOD::System *mSystem;

View File

@@ -133,10 +133,10 @@ BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
return(FALSE); return(FALSE);
} }
// ********************************** //**********************************
LLAPRFile outfile ; LLAPRFile outfile ;
outfile.open(out_fname,LL_APR_WPB); outfile.open(out_fname,LL_APR_WPB);
// ********************************** //**********************************
if (!outfile.getFileHandle()) if (!outfile.getFileHandle())
{ {
llwarning("unable to open vorbis destination file for writing",0); llwarning("unable to open vorbis destination file for writing",0);
@@ -308,9 +308,9 @@ BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
outfile.seek(SEEK_END,-fade_length*2); outfile.seek(SEEK_END,-fade_length*2);
outfile.write(pcmout,2*fade_length); //write back xfaded last 16 samples outfile.write(pcmout,2*fade_length); //write back xfaded last 16 samples
// ******************* //*******************
outfile.close(); outfile.close();
// ******************* //*******************
if ((36 == data_length) || (!(eof))) if ((36 == data_length) || (!(eof)))
{ {

View File

@@ -87,10 +87,10 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
error_msg.clear(); error_msg.clear();
// ******************************** //********************************
LLAPRFile infile ; LLAPRFile infile ;
infile.open(in_fname,LL_APR_RB); infile.open(in_fname,LL_APR_RB);
// ******************************** //********************************
if (!infile.getFileHandle()) if (!infile.getFileHandle())
{ {
error_msg = "CannotUploadSoundFile"; error_msg = "CannotUploadSoundFile";
@@ -159,9 +159,9 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
file_pos += (chunk_length + 8); file_pos += (chunk_length + 8);
chunk_length = 0; chunk_length = 0;
} }
// **************** //****************
infile.close(); infile.close();
// **************** //****************
if (!uncompressed_pcm) if (!uncompressed_pcm)
{ {

View File

@@ -195,19 +195,15 @@ void LLCharacter::requestStopMotion( LLMotion* motion)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// updateMotions() // updateMotions()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static LLFastTimer::DeclareTimer FTM_UPDATE_ANIMATION("Update Animation");
static LLFastTimer::DeclareTimer FTM_UPDATE_HIDDEN_ANIMATION("Update Hidden Anim");
void LLCharacter::updateMotions(e_update_t update_type) void LLCharacter::updateMotions(e_update_t update_type)
{ {
LLFastTimer t(LLFastTimer::FTM_UPDATE_ANIMATION);
if (update_type == HIDDEN_UPDATE) if (update_type == HIDDEN_UPDATE)
{ {
LLFastTimer t(FTM_UPDATE_HIDDEN_ANIMATION);
mMotionController.updateMotionsMinimal(); mMotionController.updateMotionsMinimal();
} }
else else
{ {
LLFastTimer t(FTM_UPDATE_ANIMATION);
// unpause if the number of outstanding pause requests has dropped to the initial one // unpause if the number of outstanding pause requests has dropped to the initial one
if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1) if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1)
{ {

View File

@@ -637,9 +637,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
motionp->fadeIn(); motionp->fadeIn();
} }
// ********************** //**********************
// MOTION INACTIVE // MOTION INACTIVE
// ********************** //**********************
if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration()) if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration())
{ {
// this motion has gone on too long, deactivate it // this motion has gone on too long, deactivate it
@@ -659,9 +659,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
} }
} }
// ********************** //**********************
// MOTION EASE OUT // MOTION EASE OUT
// ********************** //**********************
else if (motionp->isStopped() && mAnimTime > motionp->getStopTime()) else if (motionp->isStopped() && mAnimTime > motionp->getStopTime())
{ {
// is this the first iteration in the ease out phase? // is this the first iteration in the ease out phase?
@@ -684,9 +684,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature); update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
} }
// ********************** //**********************
// MOTION ACTIVE // MOTION ACTIVE
// ********************** //**********************
else if (mAnimTime > motionp->mActivationTimestamp + motionp->getEaseInDuration()) else if (mAnimTime > motionp->mActivationTimestamp + motionp->getEaseInDuration())
{ {
posep->setWeight(motionp->getFadeWeight()); posep->setWeight(motionp->getFadeWeight());
@@ -707,9 +707,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature); update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
} }
// ********************** //**********************
// MOTION EASE IN // MOTION EASE IN
// ********************** //**********************
else if (mAnimTime >= motionp->mActivationTimestamp) else if (mAnimTime >= motionp->mActivationTimestamp)
{ {
if (mLastTime < motionp->mActivationTimestamp) if (mLastTime < motionp->mActivationTimestamp)

View File

@@ -15,11 +15,8 @@ include_directories(
) )
set(llcommon_SOURCE_FILES set(llcommon_SOURCE_FILES
aiframetimer.cpp
imageids.cpp imageids.cpp
indra_constants.cpp indra_constants.cpp
llallocator.cpp
llallocator_heap_profile.cpp
llapp.cpp llapp.cpp
llapr.cpp llapr.cpp
llaprpool.cpp llaprpool.cpp
@@ -29,23 +26,16 @@ set(llcommon_SOURCE_FILES
llbase64.cpp llbase64.cpp
llcommon.cpp llcommon.cpp
llcommonutils.cpp llcommonutils.cpp
llcoros.cpp
llcrc.cpp llcrc.cpp
llcriticaldamp.cpp llcriticaldamp.cpp
llcursortypes.cpp llcursortypes.cpp
lldate.cpp lldate.cpp
lldependencies.cpp
lldictionary.cpp lldictionary.cpp
llerror.cpp llerror.cpp
llerrorthread.cpp llerrorthread.cpp
llevent.cpp llevent.cpp
lleventapi.cpp
lleventcoro.cpp
lleventdispatcher.cpp
lleventfilter.cpp
llevents.cpp
lleventtimer.cpp lleventtimer.cpp
llfasttimer_class.cpp llfasttimer.cpp
llfile.cpp llfile.cpp
llfindlocale.cpp llfindlocale.cpp
llfixedbuffer.cpp llfixedbuffer.cpp
@@ -61,11 +51,9 @@ set(llcommon_SOURCE_FILES
llmd5.cpp llmd5.cpp
llmemory.cpp llmemory.cpp
llmemorystream.cpp llmemorystream.cpp
llmemtype.cpp
llmetrics.cpp llmetrics.cpp
llmortician.cpp llmortician.cpp
lloptioninterface.cpp lloptioninterface.cpp
llptrto.cpp
llprocesslauncher.cpp llprocesslauncher.cpp
llprocessor.cpp llprocessor.cpp
llqueuedthread.cpp llqueuedthread.cpp
@@ -91,7 +79,6 @@ set(llcommon_SOURCE_FILES
lluri.cpp lluri.cpp
lluuid.cpp lluuid.cpp
llworkerthread.cpp llworkerthread.cpp
ll_template_cast.h
metaclass.cpp metaclass.cpp
metaproperty.cpp metaproperty.cpp
reflective.cpp reflective.cpp
@@ -102,7 +89,6 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES set(llcommon_HEADER_FILES
CMakeLists.txt CMakeLists.txt
aiframetimer.h
aithreadsafe.h aithreadsafe.h
bitpack.h bitpack.h
ctype_workaround.h ctype_workaround.h
@@ -112,8 +98,6 @@ set(llcommon_HEADER_FILES
linden_common.h linden_common.h
linked_lists.h linked_lists.h
llaccountingcost.h llaccountingcost.h
llallocator.h
llallocator_heap_profile.h
llagentconstants.h llagentconstants.h
llavatarname.h llavatarname.h
llapp.h llapp.h
@@ -129,7 +113,6 @@ set(llcommon_HEADER_FILES
llclickaction.h llclickaction.h
llcommon.h llcommon.h
llcommonutils.h llcommonutils.h
llcoros.h
llcrc.h llcrc.h
llcriticaldamp.h llcriticaldamp.h
llcursortypes.h llcursortypes.h
@@ -137,7 +120,6 @@ set(llcommon_HEADER_FILES
lldarrayptr.h lldarrayptr.h
lldate.h lldate.h
lldefs.h lldefs.h
lldependencies.h
lldeleteutils.h lldeleteutils.h
lldepthstack.h lldepthstack.h
lldictionary.h lldictionary.h
@@ -150,16 +132,10 @@ set(llcommon_HEADER_FILES
llerrorlegacy.h llerrorlegacy.h
llerrorthread.h llerrorthread.h
llevent.h llevent.h
lleventapi.h
lleventcoro.h
lleventdispatcher.h
lleventfilter.h
llevents.h
lleventemitter.h lleventemitter.h
llextendedstatus.h llextendedstatus.h
lleventtimer.h lleventtimer.h
llfasttimer.h llfasttimer.h
llfasttimer_class.h
llfile.h llfile.h
llfindlocale.h llfindlocale.h
llfixedbuffer.h llfixedbuffer.h
@@ -195,7 +171,6 @@ set(llcommon_HEADER_FILES
llprocessor.h llprocessor.h
llptrskiplist.h llptrskiplist.h
llptrskipmap.h llptrskipmap.h
llptrto.h
llqueuedthread.h llqueuedthread.h
llrand.h llrand.h
llrefcount.h llrefcount.h
@@ -228,7 +203,8 @@ set(llcommon_HEADER_FILES
lluri.h lluri.h
lluuid.h lluuid.h
lluuidhashmap.h lluuidhashmap.h
llversionviewer.h.in llversionserver.h
llversionviewer.h
llworkerthread.h llworkerthread.h
metaclass.h metaclass.h
metaclasst.h metaclasst.h

View File

@@ -1,180 +0,0 @@
/**
* @file aiframetimer.cpp
*
* Copyright (c) 2011, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 06/08/2011
* - Initial version, written by Aleric Inglewood @ SL
*/
// An AIFrameTimer object provides a callback API for timer events.
//
// Typical usage:
//
// // Any thread.
// AIFrameTimer timer;
//
// ...
// // Any thread (after successful construction is guaranteed).
// timer.create(5.5, boost::bind(&the_callback, <optional params>)); // Call the_callback(<optional params>) in 5.5 seconds.
//
// The callback function is always called by the main thread and should therefore
// be light weight.
//
// If timer.cancel() is called before the timer expires, then the callback
// function isn't called. If cancel() is called by another thread than the
// main thread, then it is possible that the callback function is called
// even while still inside cancel(), but as soon as cancel() returned it
// is guarenteed that the callback function won't be called anymore.
// Hence, if the callback function is a member of some object then
// cancel() must be called before the destruction of that object (ie from
// it's destructor). Calling cancel() multiple times is not a problem.
// Note: if cancel() is called while the callback function is being
// called then cancel() will block until the callback function returned.
//
// The timer object can be reused (by calling create() again), but
// only after either the callback function was called, or after cancel()
// returned. Most notably, you can call create() again from inside the
// callback function to "restart" the timer.
//
#include "linden_common.h"
#include "aiframetimer.h"
static F64 const NEVER = 1e16; // 317 million years.
F64 AIFrameTimer::sNextExpiration;
AIFrameTimer::timer_list_type AIFrameTimer::sTimerList;
LLMutex AIFrameTimer::sMutex;
// Notes on thread-safety of AIRunningFrameTimer (continued from aiframetimer.h)
//
// Most notably, the constructor and init() should be called as follows:
// 1) The object is constructed (AIRunningFrameTimer::AIRunningFrameTimer).
// 2) The lock is obtained.
// 3) The object is inserted in the list (operator<(AIRunningFrameTimer const&, AIRunningFrameTimer const&)).
// 4) The object is initialized (AIRunningFrameTimer::init).
// 5) The lock is released.
// This assures that the object is not yet shared at the moment that it is initialized.
void AIFrameTimer::create(F64 expiration, signal_type::slot_type const& slot)
{
AIRunningFrameTimer new_timer(expiration, this);
sMutex.lock();
llassert(mHandle.mRunningTimer == sTimerList.end()); // Create may only be called when the timer isn't already running.
mHandle.init(sTimerList.insert(new_timer), slot);
sNextExpiration = sTimerList.begin()->expiration();
sMutex.unlock();
}
void AIFrameTimer::cancel(void)
{
// In order to stop us from returning from cancel() while
// the callback function is being called (which is done
// in AIFrameTimer::handleExpiration after obtaining the
// mHandle.mMutex lock), we start with trying to obtain
// it here and as such wait till the callback function
// returned.
mHandle.mMutex.lock();
// Next we have to grab this lock in order to stop
// AIFrameTimer::handleExpiration from even entering
// in the case we manage to get it first.
sMutex.lock();
if (mHandle.mRunningTimer != sTimerList.end())
{
sTimerList.erase(mHandle.mRunningTimer);
mHandle.mRunningTimer = sTimerList.end();
sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration();
}
sMutex.unlock();
mHandle.mMutex.unlock();
}
void AIFrameTimer::handleExpiration(F64 current_frame_time)
{
sMutex.lock();
for(;;)
{
if (sTimerList.empty())
{
// No running timers left.
sNextExpiration = NEVER;
break;
}
timer_list_type::iterator running_timer = sTimerList.begin();
sNextExpiration = running_timer->expiration();
if (sNextExpiration > current_frame_time)
{
// Didn't expire yet.
break;
}
// Obtain handle of running timer through the associated AIFrameTimer object.
// Note that if the AIFrameTimer object was destructed (when running_timer->getTimer()
// would return an invalid pointer) then it called cancel(), so we can't be here.
Handle& handle(running_timer->getTimer()->mHandle);
llassert_always(running_timer == handle.mRunningTimer);
// We're going to erase this timer, so stop cancel() from doing the same.
handle.mRunningTimer = sTimerList.end();
// We keep handle.mMutex during the callback to prevent the thread that
// owns the AIFrameTimer from deleting the callback function while we
// call it: in order to do so it first has to call cancel(), which will
// block until we release this mutex again, or we won't call the callback
// function here because the trylock fails.
//
// Assuming the main thread arrived here, there are two possible states
// for the other thread that tries to delete the callback function,
// right after calling the cancel() function too:
//
// 1. It hasn't obtained the first lock yet, we obtain the handle.mMutex
// lock and the other thread will stall on the first line of cancel().
// After do_callback returns, the other thread will do nothing because
// handle.mRunningTimer equals sTimerList.end(), exit the function and
// (possibly) delete the callback object, but that is ok as we already
// returned from the callback function.
//
// 2. It already called cancel() and hangs on the second line trying to
// obtain sMutex.lock(). The trylock below fails and we never call the
// callback function. We erase the running timer here and release sMutex
// at the end, after which the other thread does nothing because
// handle.mRunningTimer equals sTimerList.end(), exits the function and
// (possibly) deletes the callback object.
//
// Note that if the other thread actually obtained the sMutex then we
// can't be here: this is still inside the critical area of sMutex.
if (handle.mMutex.tryLock()) // If this fails then another thread is in the process of cancelling this timer, so do nothing.
{
sMutex.unlock();
running_timer->do_callback(); // May not throw exceptions.
sMutex.lock();
handle.mMutex.unlock(); // Allow other thread to return from cancel() and possibly delete the callback object.
}
// Erase the timer from the running list.
sTimerList.erase(running_timer);
}
sMutex.unlock();
}

View File

@@ -1,155 +0,0 @@
/**
* @file aiframetimer.h
* @brief Implementation of AIFrameTimer.
*
* Copyright (c) 2011, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 05/08/2011
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AIFRAMETIMER_H
#define AIFRAMETIMER_H
#include "llframetimer.h"
#include "llthread.h"
#include <boost/signals2.hpp>
#include <set>
class LL_COMMON_API AIFrameTimer
{
protected:
typedef boost::signals2::signal<void (void)> signal_type;
private:
// Use separate struct for this object because it is non-copyable.
struct Signal {
signal_type mSignal;
};
// Notes on Thread-Safety
//
// This is the type of the objects stored in AIFrameTimer::sTimerList, and as such leans
// for it's thread-safety on the same lock as is used for that std::multiset as follows.
// An arbitrary thread can create, insert and initialize this object. Other threads can
// not access it until that has completed.
//
// After creation two threads can access it: the thread that created it (owns the
// AIFrameTimer object, which has an mHandle that points to this object), or the main
// thread by finding it in sTimerList.
//
// See aiframetimer.cpp for more notes.
class AIRunningFrameTimer {
private:
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
AIFrameTimer* mTimer; // The actual timer.
// Can be mutable, since only the mExpire is used for ordering this object in the multiset AIFrameTimer::sTimerList.
mutable Signal* mCallback; // Pointer to callback struct, or NULL when the object wasn't added to sTimerList yet.
public:
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(NULL), mTimer(timer) { }
~AIRunningFrameTimer() { delete mCallback; }
// This function is called after the final object was added to sTimerList (where it is initialized in-place).
void init(signal_type::slot_type const& slot) const
{
// We may only call init() once.
llassert(!mCallback);
mCallback = new Signal;
mCallback->mSignal.connect(slot);
}
// Order AIFrameTimer::sTimerList so that the timer that expires first is up front.
friend bool operator<(AIRunningFrameTimer const& ft1, AIRunningFrameTimer const& ft2) { return ft1.mExpire < ft2.mExpire; }
void do_callback(void) const { mCallback->mSignal(); }
F64 expiration(void) const { return mExpire; }
AIFrameTimer* getTimer(void) const { return mTimer; }
#if LL_DEBUG
// May not copy this object after it was initialized.
AIRunningFrameTimer(AIRunningFrameTimer const& running_frame_timer) :
mExpire(running_frame_timer.mExpire), mCallback(running_frame_timer.mCallback), mTimer(running_frame_timer.mTimer)
{ llassert(!mCallback); }
#endif
};
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
static LLMutex sMutex; // Mutex for the two global variables below.
static timer_list_type sTimerList; // List with all running timers.
static F64 sNextExpiration; // Cache of smallest value in sTimerList.
friend class LLFrameTimer; // Access to sNextExpiration.
class Handle {
public:
timer_list_type::iterator mRunningTimer; // Points to the running timer, or to sTimerList.end() when not running.
// Access to this iterator is protected by the AIFrameTimer::sMutex!
LLMutex mMutex; // A mutex used to protect us from deletion of the callback object while
// calling the callback function in the case of simultaneous expiration
// and cancellation by the thread owning the AIFrameTimer (by calling
// AIFrameTimer::cancel).
// Constructor for a not-running timer.
Handle(void) : mRunningTimer(sTimerList.end()) { }
// Actual initialization used by AIFrameTimer::create.
void init(timer_list_type::iterator const& running_timer, signal_type::slot_type const& slot)
{
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
// the object and no other thread knows of mRunningTimer at this point.
mRunningTimer = running_timer;
mRunningTimer->init(slot);
}
private:
// LLMutex has no assignment operator.
Handle& operator=(Handle const&) { return *this; }
};
Handle mHandle;
public:
// Construct an AIFrameTimer that is not running.
AIFrameTimer(void) { }
// Construction of a running AIFrameTimer with expiration time expiration in seconds, and callback slot.
AIFrameTimer(F64 expiration, signal_type::slot_type const& slot) { create(expiration, slot); }
// Destructing the AIFrameTimer object terminates the running timer (if still running).
// Note that cancel() must have returned BEFORE anything is destructed that would disallow the callback function to be called.
// So, if the AIFrameTimer is a member of an object whose callback function is called then cancel() has
// to be called manually (or from the destructor of THAT object), before that object is destructed.
// Cancel may be called multiple times.
~AIFrameTimer() { cancel(); }
void create(F64 expiration, signal_type::slot_type const& slot);
void cancel(void);
bool isRunning(void) const { bool running; sMutex.lock(); running = mHandle.mRunningTimer != sTimerList.end(); sMutex.unlock(); return running; }
protected:
static void handleExpiration(F64 current_frame_time);
};
#endif

View File

@@ -124,7 +124,7 @@ public:
// NOTE: This next two funtions are only included here // NOTE: This next two funtions are only included here
// for those too familiar with the LLLinkedList template class. // for those too familiar with the LLLinkedList template class.
// They are deprecated. resetList() is unecessary while // They are depreciated. resetList() is unecessary while
// getCurrentData() is identical to getNextData() and has // getCurrentData() is identical to getNextData() and has
// a misleading name. // a misleading name.
// //
@@ -604,7 +604,7 @@ BOOL LLDoubleLinkedList<DATA_TYPE>::checkData(const DATA_TYPE *data)
// NOTE: This next two funtions are only included here // NOTE: This next two funtions are only included here
// for those too familiar with the LLLinkedList template class. // for those too familiar with the LLLinkedList template class.
// They are deprecated. resetList() is unecessary while // They are depreciated. resetList() is unecessary while
// getCurrentData() is identical to getNextData() and has // getCurrentData() is identical to getNextData() and has
// a misleading name. // a misleading name.
// //

View File

@@ -153,6 +153,11 @@ const char LAND_LAYER_CODE = 'L';
const char WATER_LAYER_CODE = 'W'; const char WATER_LAYER_CODE = 'W';
const char WIND_LAYER_CODE = '7'; const char WIND_LAYER_CODE = '7';
const char CLOUD_LAYER_CODE = '8'; const char CLOUD_LAYER_CODE = '8';
// Extended land layer for Aurora Sim
const char AURORA_LAND_LAYER_CODE = 'M';
const char AURORA_WATER_LAYER_CODE = 'X';
const char AURORA_WIND_LAYER_CODE = '9';
const char AURORA_CLOUD_LAYER_CODE = ':';
// keys // keys
// Bit masks for various keyboard modifier keys. // Bit masks for various keyboard modifier keys.

View File

@@ -1,177 +0,0 @@
/**
* @file ll_template_cast.h
* @author Nat Goodspeed
* @date 2009-11-21
* @brief Define ll_template_cast function
*
* $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_LL_TEMPLATE_CAST_H)
#define LL_LL_TEMPLATE_CAST_H
/**
* Implementation for ll_template_cast() (q.v.).
*
* Default implementation: trying to cast two completely unrelated types
* returns 0. Typically you'd specify T and U as pointer types, but in fact T
* can be any type that can be initialized with 0.
*/
template <typename T, typename U>
struct ll_template_cast_impl
{
T operator()(U)
{
return 0;
}
};
/**
* ll_template_cast<T>(some_value) is for use in a template function when
* some_value might be of arbitrary type, but you want to recognize type T
* specially.
*
* It's designed for use with pointer types. Example:
* @code
* struct SpecialClass
* {
* void someMethod(const std::string&) const;
* };
*
* template <class REALCLASS>
* void somefunc(const REALCLASS& instance)
* {
* const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance);
* if (ptr)
* {
* ptr->someMethod("Call method only available on SpecialClass");
* }
* }
* @endcode
*
* Why is this better than dynamic_cast<>? Because unless OtherClass is
* polymorphic, the following won't even compile (gcc 4.0.1):
* @code
* OtherClass other;
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other);
* @endcode
* to say nothing of this:
* @code
* void function(int);
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function);
* @endcode
* ll_template_cast handles these kinds of cases by returning 0.
*/
template <typename T, typename U>
T ll_template_cast(U value)
{
return ll_template_cast_impl<T, U>()(value);
}
/**
* Implementation for ll_template_cast() (q.v.).
*
* Implementation for identical types: return same value.
*/
template <typename T>
struct ll_template_cast_impl<T, T>
{
T operator()(T value)
{
return value;
}
};
/**
* LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of
* type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s --
* presuming that @c source can be converted to @c dest by the normal rules of
* C++.
*
* By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's
* type is literally identical to @c dest. (This is because of the
* straightforward application of template specialization rules.) That can
* lead to surprising results, e.g.:
*
* @code
* Foo myFoo;
* const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo);
* @endcode
*
* Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt>
* -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would
* force the compiler to do the right thing.)
*
* More disappointingly:
* @code
* struct Base {};
* struct Subclass: public Base {};
* Subclass object;
* Base* ptr = ll_template_cast<Base*>(&object);
* @endcode
*
* Here @c ptr will be 0 because <tt>&object</tt> is of type
* <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to
* succeed, but without our help ll_template_cast can't recognize it.
*
* The following would suffice:
* @code
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
* ...
* Base* ptr = ll_template_cast<Base*>(&object);
* @endcode
*
* However, as noted earlier, this is easily fooled:
* @code
* const Base* ptr = ll_template_cast<const Base*>(&object);
* @endcode
* would still produce 0 because we haven't yet seen:
* @code
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
* @endcode
*
* @TODO
* This macro should use Boost type_traits facilities for stripping and
* re-adding @c const and @c volatile qualifiers so that invoking
* LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all
* permitted permutations. It's really not fair to the coder to require
* separate:
* @code
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
* LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*);
* @endcode
*
* (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt>
* because that's not permitted by normal C++ assignment anyway.)
*/
#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \
template <> \
struct ll_template_cast_impl<DEST, SOURCE> \
{ \
DEST operator()(SOURCE wrapper) \
{ \
return wrapper; \
} \
}
#endif /* ! defined(LL_LL_TEMPLATE_CAST_H) */

View File

@@ -1,134 +0,0 @@
/**
* @file llallocator.cpp
* @brief Implementation of the LLAllocator class.
*
* $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$
*/
#include "linden_common.h"
#include "llallocator.h"
#if LL_USE_TCMALLOC
#include "google/heap-profiler.h"
#include "google/commandlineflags_public.h"
DECLARE_bool(heap_profile_use_stack_trace);
//DECLARE_double(tcmalloc_release_rate);
// static
void LLAllocator::pushMemType(S32 type)
{
if(isProfiling())
{
PushMemType(type);
}
}
// static
S32 LLAllocator::popMemType()
{
if (isProfiling())
{
return PopMemType();
}
else
{
return -1;
}
}
void LLAllocator::setProfilingEnabled(bool should_enable)
{
// NULL disables dumping to disk
static char const * const PREFIX = NULL;
if(should_enable)
{
HeapProfilerSetUseStackTrace(false);
HeapProfilerStart(PREFIX);
}
else
{
HeapProfilerStop();
}
}
// static
bool LLAllocator::isProfiling()
{
return IsHeapProfilerRunning();
}
std::string LLAllocator::getRawProfile()
{
// *TODO - fix google-perftools to accept an buffer to avoid this
// malloc-copy-free cycle.
char * buffer = GetHeapProfile();
std::string ret = buffer;
free(buffer);
return ret;
}
#else // LL_USE_TCMALLOC
//
// stub implementations for when tcmalloc is disabled
//
// static
void LLAllocator::pushMemType(S32 type)
{
}
// static
S32 LLAllocator::popMemType()
{
return -1;
}
void LLAllocator::setProfilingEnabled(bool should_enable)
{
}
// static
bool LLAllocator::isProfiling()
{
return false;
}
std::string LLAllocator::getRawProfile()
{
return std::string();
}
#endif // LL_USE_TCMALLOC
LLAllocatorHeapProfile const & LLAllocator::getProfile()
{
mProf.mLines.clear();
// *TODO - avoid making all these extra copies of things...
std::string prof_text = getRawProfile();
//std::cout << prof_text << std::endl;
mProf.parse(prof_text);
return mProf;
}

View File

@@ -1,57 +0,0 @@
/**
* @file llallocator.h
* @brief Declaration of the LLAllocator class.
*
* $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$
*/
#ifndef LL_LLALLOCATOR_H
#define LL_LLALLOCATOR_H
#include <string>
#include "llmemtype.h"
#include "llallocator_heap_profile.h"
class LL_COMMON_API LLAllocator {
friend class LLMemoryView;
friend class LLMemType;
private:
static void pushMemType(S32 type);
static S32 popMemType();
public:
void setProfilingEnabled(bool should_enable);
static bool isProfiling();
LLAllocatorHeapProfile const & getProfile();
private:
std::string getRawProfile();
private:
LLAllocatorHeapProfile mProf;
};
#endif // LL_LLALLOCATOR_H

View File

@@ -1,147 +0,0 @@
/**
* @file llallocator_heap_profile.cpp
* @brief Implementation of the parser for tcmalloc heap profile data.
* @author Brad Kittenbrink
*
* $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$
*/
#include "linden_common.h"
#include "llallocator_heap_profile.h"
#if LL_MSVC
// disable warning about boost::lexical_cast returning uninitialized data
// when it fails to parse the string
#pragma warning (disable:4701)
#pragma warning (disable:4702)
#endif
#include <boost/algorithm/string/split.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/range/iterator_range.hpp>
static const std::string HEAP_PROFILE_MAGIC_STR = "heap profile:";
static bool is_separator(char c)
{
return isspace(c) || c == '[' || c == ']' || c == ':';
}
void LLAllocatorHeapProfile::parse(std::string const & prof_text)
{
// a typedef for handling a token in the string buffer
// it's a begin/end pair of string::const_iterators
typedef boost::iterator_range<std::string::const_iterator> range_t;
mLines.clear();
if(prof_text.compare(0, HEAP_PROFILE_MAGIC_STR.length(), HEAP_PROFILE_MAGIC_STR) != 0)
{
// *TODO - determine if there should be some better error state than
// mLines being empty. -brad
llwarns << "invalid heap profile data passed into parser." << llendl;
return;
}
std::vector< range_t > prof_lines;
std::string::const_iterator prof_begin = prof_text.begin() + HEAP_PROFILE_MAGIC_STR.length();
range_t prof_range(prof_begin, prof_text.end());
boost::algorithm::split(prof_lines,
prof_range,
boost::bind(std::equal_to<llwchar>(), '\n', _1));
std::vector< range_t >::const_iterator i;
for(i = prof_lines.begin(); i != prof_lines.end() && !i->empty(); ++i)
{
range_t const & line_text = *i;
std::vector<range_t> line_elems;
boost::algorithm::split(line_elems,
line_text,
is_separator);
std::vector< range_t >::iterator j;
j = line_elems.begin();
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U32 live_count = boost::lexical_cast<U32>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U64 live_size = boost::lexical_cast<U64>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U32 tot_count = boost::lexical_cast<U32>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert_always(j != line_elems.end());
U64 tot_size = boost::lexical_cast<U64>(*j);
++j;
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
llassert(j != line_elems.end());
if (j != line_elems.end())
{
++j; // skip the '@'
mLines.push_back(line(live_count, live_size, tot_count, tot_size));
line & current_line = mLines.back();
for(; j != line_elems.end(); ++j)
{
if(!j->empty())
{
U32 marker = boost::lexical_cast<U32>(*j);
current_line.mTrace.push_back(marker);
}
}
}
}
// *TODO - parse MAPPED_LIBRARIES section here if we're ever interested in it
}
void LLAllocatorHeapProfile::dump(std::ostream & out) const
{
lines_t::const_iterator i;
for(i = mLines.begin(); i != mLines.end(); ++i)
{
out << i->mLiveCount << ": " << i->mLiveSize << '[' << i->mTotalCount << ": " << i->mTotalSize << "] @";
stack_trace::const_iterator j;
for(j = i->mTrace.begin(); j != i->mTrace.end(); ++j)
{
out << ' ' << *j;
}
out << '\n';
}
out.flush();
}

View File

@@ -1,71 +0,0 @@
/**
* @file llallocator_heap_profile.h
* @brief Declaration of the parser for tcmalloc heap profile data.
* @author Brad Kittenbrink
*
* $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$
*/
#ifndef LL_LLALLOCATOR_HEAP_PROFILE_H
#define LL_LLALLOCATOR_HEAP_PROFILE_H
#include "stdtypes.h"
#include <map>
#include <vector>
class LLAllocatorHeapProfile
{
public:
typedef int stack_marker;
typedef std::vector<stack_marker> stack_trace;
struct line {
line(U32 live_count, U64 live_size, U32 tot_count, U64 tot_size) :
mLiveSize(live_size),
mTotalSize(tot_size),
mLiveCount(live_count),
mTotalCount(tot_count)
{
}
U64 mLiveSize, mTotalSize;
U32 mLiveCount, mTotalCount;
stack_trace mTrace;
};
typedef std::vector<line> lines_t;
LLAllocatorHeapProfile()
{
}
void parse(std::string const & prof_text);
void dump(std::ostream & out) const;
public:
lines_t mLines;
};
#endif // LL_LLALLOCATOR_HEAP_PROFILE_H

View File

@@ -401,18 +401,6 @@ bool LLApp::isExiting()
return isQuitting() || isError(); return isQuitting() || isError();
} }
void LLApp::disableCrashlogger()
{
// Disable Breakpad exception handler.
sDisableCrashlogger = TRUE;
}
// static
bool LLApp::isCrashloggerDisabled()
{
return (sDisableCrashlogger == TRUE);
}
#if !LL_WINDOWS #if !LL_WINDOWS
// static // static
U32 LLApp::getSigChildCount() U32 LLApp::getSigChildCount()
@@ -746,7 +734,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl; llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
} }
if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem if(LLApp::sDisableCrashlogger) //Don't gracefully handle any signals crash and core for a gdb post mortum
{ {
clear_signals(); clear_signals();
llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl; llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl;

View File

@@ -191,11 +191,6 @@ public:
// //
virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
//
// Crash logging
//
void disableCrashlogger(); // Let the OS handle the crashes
static bool isCrashloggerDisabled(); // Get the here above set value
// //
// Application status // Application status
@@ -219,6 +214,9 @@ public:
// //
void setErrorHandler(LLAppErrorHandler handler); void setErrorHandler(LLAppErrorHandler handler);
void setSyncErrorHandler(LLAppErrorHandler handler); void setSyncErrorHandler(LLAppErrorHandler handler);
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
#if !LL_WINDOWS #if !LL_WINDOWS
// //
// Child process handling (Unix only for now) // Child process handling (Unix only for now)
@@ -247,7 +245,6 @@ protected:
static void setStatus(EAppStatus status); // Use this to change the application status. static void setStatus(EAppStatus status); // Use this to change the application status.
static EAppStatus sStatus; // Reflects current application status static EAppStatus sStatus; // Reflects current application status
static BOOL sErrorThreadRunning; // Set while the error thread is running static BOOL sErrorThreadRunning; // Set while the error thread is running
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
#if !LL_WINDOWS #if !LL_WINDOWS
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received. static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.

View File

@@ -249,7 +249,7 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
} }
// //
// ******************************************************************************************************************************* //*******************************************************************************************************************************
//static components of LLAPRFile //static components of LLAPRFile
// //
@@ -504,5 +504,5 @@ bool LLAPRFile::removeDir(const std::string& dirname)
} }
// //
//end of static components of LLAPRFile //end of static components of LLAPRFile
// ******************************************************************************************************************************* //*******************************************************************************************************************************
// //

View File

@@ -168,7 +168,7 @@ public:
apr_file_t* getFileHandle() {return mFile;} apr_file_t* getFileHandle() {return mFile;}
// //
// ******************************************************************************************************************************* //*******************************************************************************************************************************
//static components //static components
// //
private: private:
@@ -185,7 +185,7 @@ public:
// Returns bytes read/written, 0 if read/write fails: // Returns bytes read/written, 0 if read/write fails:
static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes); static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes); static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
// ******************************************************************************************************************************* //*******************************************************************************************************************************
}; };
/** /**

View File

@@ -214,16 +214,17 @@ LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_
bool LLAssetType::lookupCanLink(EType asset_type) bool LLAssetType::lookupCanLink(EType asset_type)
{ {
//Check that enabling all these other types as linkable doesn't break things. //Check that enabling all these other types as linkable doesn't break things.
const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); /*const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
const AssetEntry *entry = dict->lookup(asset_type); const AssetEntry *entry = dict->lookup(asset_type);
if (entry) if (entry)
{ {
return entry->mCanLink; return entry->mCanLink;
} }
return false; return false;
*/
/*return (asset_type == AT_CLOTHING || asset_type == AT_OBJECT || asset_type == AT_CATEGORY || return (asset_type == AT_CLOTHING || asset_type == AT_OBJECT || asset_type == AT_CATEGORY ||
asset_type == AT_BODYPART || asset_type == AT_GESTURE);*/ asset_type == AT_BODYPART || asset_type == AT_GESTURE);
} }
// static // static

View File

@@ -1,154 +0,0 @@
/**
* @file llcoros.cpp
* @author Nat Goodspeed
* @date 2009-06-03
* @brief Implementation for llcoros.
*
* $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 "llcoros.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "llevents.h"
#include "llerror.h"
#include "stringize.h"
LLCoros::LLCoros()
{
// Register our cleanup() method for "mainloop" ticks
LLEventPumps::instance().obtain("mainloop").listen(
"LLCoros", boost::bind(&LLCoros::cleanup, this, _1));
}
bool LLCoros::cleanup(const LLSD&)
{
// Walk the mCoros map, checking and removing completed coroutines.
for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; )
{
// Has this coroutine exited (normal return, exception, exit() call)
// since last tick?
if (mi->second->exited())
{
LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
// The erase() call will invalidate its passed iterator value --
// so increment mi FIRST -- but pass its original value to
// erase(). This is what postincrement is all about.
mCoros.erase(mi++);
}
else
{
// Still live, just skip this entry as if incrementing at the top
// of the loop as usual.
++mi;
}
}
return false;
}
std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
// Allowing empty name would make getName()'s not-found return ambiguous.
if (prefix.empty())
{
LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL;
}
// If the specified name isn't already in the map, just use that.
std::string name(prefix);
// Find the lowest numeric suffix that doesn't collide with an existing
// entry. Start with 2 just to make it more intuitive for any interested
// parties: e.g. "joe", "joe2", "joe3"...
for (int i = 2; ; name = STRINGIZE(prefix << i++))
{
if (mCoros.find(name) == mCoros.end())
{
LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
return name;
}
}
}
bool LLCoros::kill(const std::string& name)
{
CoroMap::iterator found = mCoros.find(name);
if (found == mCoros.end())
{
return false;
}
// Because this is a boost::ptr_map, erasing the map entry also destroys
// the referenced heap object, in this case the boost::coroutine object,
// which will terminate the coroutine.
mCoros.erase(found);
return true;
}
std::string LLCoros::getNameByID(const void* self_id) const
{
// Walk the existing coroutines, looking for one from which the 'self_id'
// passed to us comes.
for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
{
namespace coro_private = boost::coroutines::detail;
if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
== self_id)
{
return mi->first;
}
}
return "";
}
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
// does for warning suppression, and we really don't want to force
// optimization ON for other code even in Debug or RelWithDebInfo builds.
#if LL_MSVC
// work around broken optimizations
#pragma warning(disable: 4748)
#pragma optimize("", off)
#endif // LL_MSVC
std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
{
std::string name(generateDistinctName(prefix));
mCoros.insert(name, newCoro);
/* Run the coroutine until its first wait, then return here */
(*newCoro)(std::nothrow);
return name;
}
#if LL_MSVC
// reenable optimizations
#pragma optimize("", on)
#endif // LL_MSVC

View File

@@ -1,166 +0,0 @@
/**
* @file llcoros.h
* @author Nat Goodspeed
* @date 2009-06-02
* @brief Manage running boost::coroutine instances
*
* $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_LLCOROS_H)
#define LL_LLCOROS_H
#include <boost/coroutine/coroutine.hpp>
#include "llsingleton.h"
#include <boost/ptr_container/ptr_map.hpp>
#include <string>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <stdexcept>
/**
* Registry of named Boost.Coroutine instances
*
* The Boost.Coroutine library supports the general case of a coroutine
* accepting arbitrary parameters and yielding multiple (sets of) results. For
* such use cases, it's natural for the invoking code to retain the coroutine
* instance: the consumer repeatedly calls into the coroutine, perhaps passing
* new parameter values, prompting it to yield its next result.
*
* Our typical coroutine usage is different, though. For us, coroutines
* provide an alternative to the @c Responder pattern. Our typical coroutine
* has @c void return, invoked in fire-and-forget mode: the handler for some
* user gesture launches the coroutine and promptly returns to the main loop.
* The coroutine initiates some action that will take multiple frames (e.g. a
* capability request), waits for its result, processes it and silently steals
* away.
*
* This usage poses two (related) problems:
*
* # Who should own the coroutine instance? If it's simply local to the
* handler code that launches it, return from the handler will destroy the
* coroutine object, terminating the coroutine.
* # Once the coroutine terminates, in whatever way, who's responsible for
* cleaning up the coroutine object?
*
* LLCoros is a Singleton collection of currently-active coroutine instances.
* Each has a name. You ask LLCoros to launch a new coroutine with a suggested
* name prefix; from your prefix it generates a distinct name, registers the
* new coroutine and returns the actual name.
*
* The name can be used to kill off the coroutine prematurely, if needed. It
* can also provide diagnostic info: we can look up the name of the
* currently-running coroutine.
*
* Finally, the next frame ("mainloop" event) after the coroutine terminates,
* LLCoros will notice its demise and destroy it.
*/
class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
{
public:
/// Canonical boost::coroutines::coroutine signature we use
typedef boost::coroutines::coroutine<void()> coro;
/// Canonical 'self' type
typedef coro::self self;
/**
* Create and start running a new coroutine with specified name. The name
* string you pass is a suggestion; it will be tweaked for uniqueness. The
* actual name is returned to you.
*
* Usage looks like this, for (e.g.) two coroutine parameters:
* @code
* class MyClass
* {
* public:
* ...
* // Do NOT NOT NOT accept reference params other than 'self'!
* // Pass by value only!
* void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
* ...
* };
* ...
* std::string name = LLCoros::instance().launch(
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
* "somestring", LLSD(17));
* @endcode
*
* Your function/method must accept LLCoros::self& as its first parameter.
* It can accept any other parameters you want -- but ONLY BY VALUE!
* Other reference parameters are a BAD IDEA! You Have Been Warned. See
* DEV-32777 comments for an explanation.
*
* Pass a callable that accepts the single LLCoros::self& parameter. It
* may work to pass a free function whose only parameter is 'self'; for
* all other cases use boost::bind(). Of course, for a non-static class
* method, the first parameter must be the class instance. Use the
* placeholder _1 for the 'self' parameter. Any other parameters should be
* passed via the bind() expression.
*
* launch() tweaks the suggested name so it won't collide with any
* existing coroutine instance, creates the coroutine instance, registers
* it with the tweaked name and runs it until its first wait. At that
* point it returns the tweaked name.
*/
template <typename CALLABLE>
std::string launch(const std::string& prefix, const CALLABLE& callable)
{
return launchImpl(prefix, new coro(callable));
}
/**
* Abort a running coroutine by name. Normally, when a coroutine either
* runs to completion or terminates with an exception, LLCoros quietly
* cleans it up. This is for use only when you must explicitly interrupt
* one prematurely. Returns @c true if the specified name was found and
* still running at the time.
*/
bool kill(const std::string& name);
/**
* From within a coroutine, pass its @c self object to look up the
* (tweaked) name string by which this coroutine is registered. Returns
* the empty string if not found (e.g. if the coroutine was launched by
* hand rather than using LLCoros::launch()).
*/
template <typename COROUTINE_SELF>
std::string getName(const COROUTINE_SELF& self) const
{
return getNameByID(self.get_id());
}
/// getName() by self.get_id()
std::string getNameByID(const void* self_id) const;
private:
friend class LLSingleton<LLCoros>;
LLCoros();
std::string launchImpl(const std::string& prefix, coro* newCoro);
std::string generateDistinctName(const std::string& prefix) const;
bool cleanup(const LLSD&);
typedef boost::ptr_map<std::string, coro> CoroMap;
CoroMap mCoros;
};
#endif /* ! defined(LL_LLCOROS_H) */

View File

@@ -43,7 +43,6 @@
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include "llfasttimer.h"
#include "lltimer.h" #include "lltimer.h"
#include "llstring.h" #include "llstring.h"
@@ -95,11 +94,9 @@ std::string LLDate::asRFC1123() const
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT")); return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
} }
LLFastTimer::DeclareTimer FT_DATE_FORMAT("Date Format");
std::string LLDate::toHTTPDateString (std::string fmt) const std::string LLDate::toHTTPDateString (std::string fmt) const
{ {
LLFastTimer ft1(FT_DATE_FORMAT);
time_t locSeconds = (time_t) mSecondsSinceEpoch; time_t locSeconds = (time_t) mSecondsSinceEpoch;
struct tm * gmt = gmtime (&locSeconds); struct tm * gmt = gmtime (&locSeconds);
@@ -108,7 +105,7 @@ std::string LLDate::toHTTPDateString (std::string fmt) const
std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt) std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
{ {
LLFastTimer ft1(FT_DATE_FORMAT); // Return Epoch UTC date
// avoid calling setlocale() unnecessarily - it's expensive. // avoid calling setlocale() unnecessarily - it's expensive.
static std::string prev_locale = ""; static std::string prev_locale = "";

View File

@@ -1,103 +0,0 @@
/**
* @file lldependencies.cpp
* @author Nat Goodspeed
* @date 2008-09-17
* @brief Implementation for lldependencies.
*
* $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$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lldependencies.h"
// STL headers
#include <map>
#include <sstream>
// std headers
// external library headers
#include <boost/graph/graph_traits.hpp> // for boost::graph_traits
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/exception.hpp>
// other Linden headers
LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const
{
// Construct a Boost Graph Library graph according to the constraints
// we've collected. It seems as though we ought to be able to capture
// the uniqueness of vertex keys using a setS of vertices with a
// string property -- but I don't yet understand adjacency_list well
// enough to get there. All the examples I've seen so far use integers
// for vertices.
// Define the Graph type. Use a vector for vertices so we can use the
// default topological_sort vertex lookup by int index. Use a set for
// edges because the same dependency may be stated twice: Node "a" may
// specify that it must precede "b", while "b" may also state that it
// must follow "a".
typedef boost::adjacency_list<boost::setS, boost::vecS, boost::directedS,
boost::no_property> Graph;
// Instantiate the graph. Without vertex properties, we need say no
// more about vertices than the total number.
Graph g(edges.begin(), edges.end(), vertices);
// topo sort
typedef boost::graph_traits<Graph>::vertex_descriptor VertexDesc;
typedef std::vector<VertexDesc> SortedList;
SortedList sorted;
// note that it throws not_a_dag if it finds a cycle
try
{
boost::topological_sort(g, std::back_inserter(sorted));
}
catch (const boost::not_a_dag& e)
{
// translate to the exception we define
std::ostringstream out;
out << "LLDependencies cycle: " << e.what() << '\n';
// Omit independent nodes: display only those that might contribute to
// the cycle.
describe(out, false);
throw Cycle(out.str());
}
// A peculiarity of boost::topological_sort() is that it emits results in
// REVERSE topological order: to get the result you want, you must
// traverse the SortedList using reverse iterators.
return VertexList(sorted.rbegin(), sorted.rend());
}
std::ostream& LLDependenciesBase::describe(std::ostream& out, bool full) const
{
// Should never encounter this base-class implementation; may mean that
// the KEY type doesn't have a suitable ostream operator<<().
out << "<no description available>";
return out;
}
std::string LLDependenciesBase::describe(bool full) const
{
// Just use the ostream-based describe() on a std::ostringstream. The
// implementation is here mostly so that we can avoid #include <sstream>
// in the header file.
std::ostringstream out;
describe(out, full);
return out.str();
}

View File

@@ -1,799 +0,0 @@
/**
* @file lldependencies.h
* @author Nat Goodspeed
* @date 2008-09-17
* @brief LLDependencies: a generic mechanism for expressing "b must follow a,
* but precede c"
*
* $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$
*/
#if ! defined(LL_LLDEPENDENCIES_H)
#define LL_LLDEPENDENCIES_H
#include <string>
#include <vector>
#include <set>
#include <map>
#include <stdexcept>
#include <iosfwd>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
/*****************************************************************************
* Utilities
*****************************************************************************/
/**
* generic range transformer: given a range acceptable to Boost.Range (e.g. a
* standard container, an iterator pair, ...) and a unary function to apply to
* each element of the range, make a corresponding range that lazily applies
* that function to each element on dereferencing.
*/
template<typename FUNCTION, typename RANGE>
inline
boost::iterator_range<boost::transform_iterator<FUNCTION,
typename boost::range_const_iterator<RANGE>::type> >
make_transform_range(const RANGE& range, FUNCTION function)
{
// shorthand for the iterator type embedded in our return type
typedef boost::transform_iterator<FUNCTION, typename boost::range_const_iterator<RANGE>::type>
transform_iterator;
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
transform_iterator(boost::end(range), function));
}
/// non-const version of make_transform_range()
template<typename FUNCTION, typename RANGE>
inline
boost::iterator_range<boost::transform_iterator<FUNCTION,
typename boost::range_iterator<RANGE>::type> >
make_transform_range(RANGE& range, FUNCTION function)
{
// shorthand for the iterator type embedded in our return type
typedef boost::transform_iterator<FUNCTION, typename boost::range_iterator<RANGE>::type>
transform_iterator;
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
transform_iterator(boost::end(range), function));
}
/**
* From any range compatible with Boost.Range, instantiate any class capable
* of accepting an iterator pair.
*/
template<class TYPE>
struct instance_from_range: public TYPE
{
template<typename RANGE>
instance_from_range(RANGE range):
TYPE(boost::begin(range), boost::end(range))
{}
};
/*****************************************************************************
* LLDependencies
*****************************************************************************/
/**
* LLDependencies components that should not be reinstantiated for each KEY,
* NODE specialization
*/
class LL_COMMON_API LLDependenciesBase
{
public:
virtual ~LLDependenciesBase() {}
/**
* Exception thrown by sort() if there's a cycle
*/
struct Cycle: public std::runtime_error
{
Cycle(const std::string& what): std::runtime_error(what) {}
};
/**
* Provide a short description of this LLDependencies instance on the
* specified output stream, assuming that its KEY type has an operator<<()
* that works with std::ostream.
*
* Pass @a full as @c false to omit any keys without dependency constraints.
*/
virtual std::ostream& describe(std::ostream& out, bool full=true) const;
/// describe() to a string
virtual std::string describe(bool full=true) const;
protected:
typedef std::vector< std::pair<int, int> > EdgeList;
typedef std::vector<int> VertexList;
VertexList topo_sort(int vertices, const EdgeList& edges) const;
/**
* refpair is specifically intended to capture a pair of references. This
* is better than std::pair<T1&, T2&> because some implementations of
* std::pair's ctor accept const references to the two types. If the
* types are themselves references, this results in an illegal reference-
* to-reference.
*/
template<typename T1, typename T2>
struct refpair
{
refpair(T1 value1, T2 value2):
first(value1),
second(value2)
{}
T1 first;
T2 second;
};
};
/// describe() helper: for most types, report the type as usual
template<typename T>
inline
std::ostream& LLDependencies_describe(std::ostream& out, const T& key)
{
out << key;
return out;
}
/// specialize LLDependencies_describe() for std::string
template<>
inline
std::ostream& LLDependencies_describe(std::ostream& out, const std::string& key)
{
out << '"' << key << '"';
return out;
}
/**
* It's reasonable to use LLDependencies in a keys-only way, more or less like
* std::set. For that case, the default NODE type is an empty struct.
*/
struct LLDependenciesEmpty
{
LLDependenciesEmpty() {}
/**
* Give it a constructor accepting void* so caller can pass placeholder
* values such as NULL or 0 rather than having to write
* LLDependenciesEmpty().
*/
LLDependenciesEmpty(void*) {}
};
/**
* This class manages abstract dependencies between node types of your
* choosing. As with a std::map, nodes are copied when add()ed, so the node
* type should be relatively lightweight; to manipulate dependencies between
* expensive objects, use a pointer type.
*
* For a given node, you may state the keys of nodes that must precede it
* and/or nodes that must follow it. The sort() method will produce an order
* that should work, or throw an exception if the constraints are impossible.
* We cache results to minimize the cost of repeated sort() calls.
*/
template<typename KEY = std::string,
typename NODE = LLDependenciesEmpty>
class LLDependencies: public LLDependenciesBase
{
typedef LLDependencies<KEY, NODE> self_type;
/**
* Internally, we bundle the client's NODE with its before/after keys.
*/
struct DepNode
{
typedef std::set<KEY> dep_set;
DepNode(const NODE& node_, const dep_set& after_, const dep_set& before_):
node(node_),
after(after_),
before(before_)
{}
NODE node;
dep_set after, before;
};
typedef std::map<KEY, DepNode> DepNodeMap;
typedef typename DepNodeMap::value_type DepNodeMapEntry;
/// We have various ways to get the dependencies for a given DepNode.
/// Rather than having to restate each one for 'after' and 'before'
/// separately, pass a dep_selector so we can apply each to either.
typedef boost::function<const typename DepNode::dep_set&(const DepNode&)> dep_selector;
public:
LLDependencies() {}
typedef KEY key_type;
typedef NODE node_type;
/// param type used to express lists of other node keys -- note that such
/// lists can be initialized with boost::assign::list_of()
typedef std::vector<KEY> KeyList;
/**
* Add a new node. State its dependencies on other nodes (which may not
* yet have been added) by listing the keys of nodes this new one must
* follow, and separately the keys of nodes this new one must precede.
*
* The node you pass is @em copied into an internal data structure. If you
* want to modify the node value after add()ing it, capture the returned
* NODE& reference.
*
* @note
* Actual dependency analysis is deferred to the sort() method, so
* you can add an arbitrary number of nodes without incurring analysis
* overhead for each. The flip side of this is that add()ing nodes that
* define a cycle leaves this object in a state in which sort() will
* always throw the Cycle exception.
*
* Two distinct use cases are anticipated:
* * The data used to load this object are completely known at compile
* time (e.g. LLEventPump listener names). A Cycle exception represents a
* bug which can be corrected by the coder. The program need neither catch
* Cycle nor attempt to salvage the state of this object.
* * The data are loaded at runtime, therefore the universe of
* dependencies cannot be known at compile time. The client code should
* catch Cycle.
* ** If a Cycle exception indicates fatally-flawed input data, this
* object can simply be discarded, possibly with the entire program run.
* ** If it is essential to restore this object to a working state, the
* simplest workaround is to remove() nodes in LIFO order.
* *** It may be useful to add functionality to this class to track the
* add() chronology, providing a pop() method to remove the most recently
* added node.
* *** It may further be useful to add a restore() method which would
* pop() until sort() no longer throws Cycle. This method would be
* expensive -- but it's not clear that client code could resolve the
* problem more cheaply.
*/
NODE& add(const KEY& key, const NODE& node = NODE(),
const KeyList& after = KeyList(),
const KeyList& before = KeyList())
{
// Get the passed-in lists as sets for equality comparison
typename DepNode::dep_set
after_set(after.begin(), after.end()),
before_set(before.begin(), before.end());
// Try to insert the new node; if it already exists, find the old
// node instead.
std::pair<typename DepNodeMap::iterator, bool> inserted =
mNodes.insert(typename DepNodeMap::value_type(key,
DepNode(node, after_set, before_set)));
if (! inserted.second) // bool indicating success of insert()
{
// We already have a node by this name. Have its dependencies
// changed? If the existing node's dependencies are identical, the
// result will be unchanged, so we can leave the cache intact.
// Regardless of inserted.second, inserted.first is the iterator
// to the newly-inserted (or existing) map entry. Of course, that
// entry's second is the DepNode of interest.
if (inserted.first->second.after != after_set ||
inserted.first->second.before != before_set)
{
// Dependencies have changed: clear the cached result.
mCache.clear();
// save the new dependencies
inserted.first->second.after = after_set;
inserted.first->second.before = before_set;
}
}
else // this node is new
{
// This will change results.
mCache.clear();
}
return inserted.first->second.node;
}
/// the value of an iterator, showing both KEY and its NODE
typedef refpair<const KEY&, NODE&> value_type;
/// the value of a const_iterator
typedef refpair<const KEY&, const NODE&> const_value_type;
private:
// Extract functors
static value_type value_extract(DepNodeMapEntry& entry)
{
return value_type(entry.first, entry.second.node);
}
static const_value_type const_value_extract(const DepNodeMapEntry& entry)
{
return const_value_type(entry.first, entry.second.node);
}
// All the iterator access methods return iterator ranges just to cut down
// on the friggin' boilerplate!!
/// generic mNodes range method
template<typename ITERATOR, typename FUNCTION>
boost::iterator_range<ITERATOR> generic_range(FUNCTION function)
{
return make_transform_range(mNodes, function);
}
/// generic mNodes const range method
template<typename ITERATOR, typename FUNCTION>
boost::iterator_range<ITERATOR> generic_range(FUNCTION function) const
{
return make_transform_range(mNodes, function);
}
public:
/// iterator over value_type entries
typedef boost::transform_iterator<boost::function<value_type(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> iterator;
/// range over value_type entries
typedef boost::iterator_range<iterator> range;
/// iterate over value_type <i>in @c KEY order</i> rather than dependency order
range get_range()
{
return generic_range<iterator>(value_extract);
}
/// iterator over const_value_type entries
typedef boost::transform_iterator<boost::function<const_value_type(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_iterator;
/// range over const_value_type entries
typedef boost::iterator_range<const_iterator> const_range;
/// iterate over const_value_type <i>in @c KEY order</i> rather than dependency order
const_range get_range() const
{
return generic_range<const_iterator>(const_value_extract);
}
/// iterator over stored NODEs
typedef boost::transform_iterator<boost::function<NODE&(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> node_iterator;
/// range over stored NODEs
typedef boost::iterator_range<node_iterator> node_range;
/// iterate over NODE <i>in @c KEY order</i> rather than dependency order
node_range get_node_range()
{
// First take a DepNodeMapEntry and extract a reference to its
// DepNode, then from that extract a reference to its NODE.
return generic_range<node_iterator>(
boost::bind<NODE&>(&DepNode::node,
boost::bind<DepNode&>(&DepNodeMapEntry::second, _1)));
}
/// const iterator over stored NODEs
typedef boost::transform_iterator<boost::function<const NODE&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_node_iterator;
/// const range over stored NODEs
typedef boost::iterator_range<const_node_iterator> const_node_range;
/// iterate over const NODE <i>in @c KEY order</i> rather than dependency order
const_node_range get_node_range() const
{
// First take a DepNodeMapEntry and extract a reference to its
// DepNode, then from that extract a reference to its NODE.
return generic_range<const_node_iterator>(
boost::bind<const NODE&>(&DepNode::node,
boost::bind<const DepNode&>(&DepNodeMapEntry::second, _1)));
}
/// const iterator over stored KEYs
typedef boost::transform_iterator<boost::function<const KEY&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_key_iterator;
/// const range over stored KEYs
typedef boost::iterator_range<const_key_iterator> const_key_range;
// We don't provide a non-const iterator over KEYs because they should be
// immutable, and in fact our underlying std::map won't give us non-const
// references.
/// iterate over const KEY <i>in @c KEY order</i> rather than dependency order
const_key_range get_key_range() const
{
// From a DepNodeMapEntry, extract a reference to its KEY.
return generic_range<const_key_iterator>(
boost::bind<const KEY&>(&DepNodeMapEntry::first, _1));
}
/**
* Find an existing NODE, or return NULL. We decided to avoid providing a
* method analogous to std::map::find(), for a couple of reasons:
*
* * For a find-by-key, getting back an iterator to the (key, value) pair
* is less than useful, since you already have the key in hand.
* * For a failed request, comparing to end() is problematic. First, we
* provide range accessors, so it's more code to get end(). Second, we
* provide a number of different ranges -- quick, to which one's end()
* should we compare the iterator returned by find()?
*
* The returned pointer is solely to allow expressing the not-found
* condition. LLDependencies still owns the found NODE.
*/
const NODE* get(const KEY& key) const
{
typename DepNodeMap::const_iterator found = mNodes.find(key);
if (found != mNodes.end())
{
return &found->second.node;
}
return NULL;
}
/**
* non-const get()
*/
NODE* get(const KEY& key)
{
// Use const implementation, then cast away const-ness of return
return const_cast<NODE*>(const_cast<const self_type*>(this)->get(key));
}
/**
* Remove a node with specified key. This operation is the major reason
* we rebuild the graph on the fly instead of storing it.
*/
bool remove(const KEY& key)
{
typename DepNodeMap::iterator found = mNodes.find(key);
if (found != mNodes.end())
{
mNodes.erase(found);
return true;
}
return false;
}
private:
/// cached list of iterators
typedef std::vector<iterator> iterator_list;
typedef typename iterator_list::iterator iterator_list_iterator;
public:
/**
* The return type of the sort() method needs some explanation. Provide a
* public typedef to facilitate storing the result.
*
* * We will prepare mCache by looking up DepNodeMap iterators.
* * We want to return a range containing iterators that will walk mCache.
* * If we simply stored DepNodeMap iterators and returned
* (mCache.begin(), mCache.end()), dereferencing each iterator would
* obtain a DepNodeMap iterator.
* * We want the caller to loop over @c value_type: pair<KEY, NODE>.
* * This requires two transformations:
* ** mCache must contain @c LLDependencies::iterator so that
* dereferencing each entry will obtain an @c LLDependencies::value_type
* rather than a DepNodeMapEntry.
* ** We must wrap mCache's iterators in boost::indirect_iterator so that
* dereferencing one of our returned iterators will also dereference the
* iterator contained in mCache.
*/
typedef boost::iterator_range<boost::indirect_iterator<iterator_list_iterator> > sorted_range;
/// for convenience in looping over a sorted_range
typedef typename sorted_range::iterator sorted_iterator;
/**
* Once we've loaded in the dependencies of interest, arrange them into an
* order that works -- or throw Cycle exception.
*
* Return an iterator range over (key, node) pairs that traverses them in
* the desired order.
*/
sorted_range sort() const
{
// Changes to mNodes cause us to clear our cache, so empty mCache
// means it's invalid and should be recomputed. However, if mNodes is
// also empty, then an empty mCache represents a valid order, so don't
// bother sorting.
if (mCache.empty() && ! mNodes.empty())
{
// Construct a map of node keys to distinct vertex numbers -- even for
// nodes mentioned only in before/after constraints, that haven't yet
// been explicitly added. Rely on std::map rejecting a second attempt
// to insert the same key. Use the map's size() as the vertex number
// to get a distinct value for each successful insertion.
typedef std::map<KEY, int> VertexMap;
VertexMap vmap;
// Nest each of these loops because !@#$%? MSVC warns us that its
// former broken behavior has finally been fixed -- and our builds
// treat warnings as errors.
{
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
nmi != nmend; ++nmi)
{
vmap.insert(typename VertexMap::value_type(nmi->first, vmap.size()));
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
aend = nmi->second.after.end();
ai != aend; ++ai)
{
vmap.insert(typename VertexMap::value_type(*ai, vmap.size()));
}
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
bend = nmi->second.before.end();
bi != bend; ++bi)
{
vmap.insert(typename VertexMap::value_type(*bi, vmap.size()));
}
}
}
// Define the edges. For this we must traverse mNodes again, mapping
// all the known key dependencies to integer pairs.
EdgeList edges;
{
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
nmi != nmend; ++nmi)
{
int thisnode = vmap[nmi->first];
// after dependencies: build edges from the named node to this one
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
aend = nmi->second.after.end();
ai != aend; ++ai)
{
edges.push_back(EdgeList::value_type(vmap[*ai], thisnode));
}
// before dependencies: build edges from this node to the
// named one
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
bend = nmi->second.before.end();
bi != bend; ++bi)
{
edges.push_back(EdgeList::value_type(thisnode, vmap[*bi]));
}
}
}
// Hide the gory details of our topological sort, since they shouldn't
// get reinstantiated for each distinct NODE type.
VertexList sorted(topo_sort(vmap.size(), edges));
// Build the reverse of vmap to look up the key for each vertex
// descriptor. vmap contains exactly one entry for each distinct key,
// and we're certain that the associated int values are distinct
// indexes. The fact that they're not in order is irrelevant.
KeyList vkeys(vmap.size());
for (typename VertexMap::const_iterator vmi = vmap.begin(), vmend = vmap.end();
vmi != vmend; ++vmi)
{
vkeys[vmi->second] = vmi->first;
}
// Walk the sorted output list, building the result into mCache so
// we'll have it next time someone asks.
mCache.clear();
for (VertexList::const_iterator svi = sorted.begin(), svend = sorted.end();
svi != svend; ++svi)
{
// We're certain that vkeys[*svi] exists. However, there might not
// yet be a corresponding entry in mNodes.
self_type* non_const_this(const_cast<self_type*>(this));
typename DepNodeMap::iterator found = non_const_this->mNodes.find(vkeys[*svi]);
if (found != non_const_this->mNodes.end())
{
// Make an iterator of appropriate type.
mCache.push_back(iterator(found, value_extract));
}
}
}
// Whether or not we've just recomputed mCache, it should now contain
// the results we want. Return a range of indirect_iterators over it
// so that dereferencing a returned iterator will dereference the
// iterator stored in mCache and directly reference the (key, node)
// pair.
boost::indirect_iterator<iterator_list_iterator>
begin(mCache.begin()),
end(mCache.end());
return sorted_range(begin, end);
}
using LLDependenciesBase::describe; // unhide virtual std::string describe(bool full=true) const;
/// Override base-class describe() with actual implementation
virtual std::ostream& describe(std::ostream& out, bool full=true) const
{
typename DepNodeMap::const_iterator dmi(mNodes.begin()), dmend(mNodes.end());
if (dmi != dmend)
{
std::string sep;
describe(out, sep, *dmi, full);
while (++dmi != dmend)
{
describe(out, sep, *dmi, full);
}
}
return out;
}
/// describe() helper: report a DepNodeEntry
static std::ostream& describe(std::ostream& out, std::string& sep,
const DepNodeMapEntry& entry, bool full)
{
// If we were asked for a full report, describe every node regardless
// of whether it has dependencies. If we were asked to suppress
// independent nodes, describe this one if either after or before is
// non-empty.
if (full || (! entry.second.after.empty()) || (! entry.second.before.empty()))
{
out << sep;
sep = "\n";
if (! entry.second.after.empty())
{
out << "after ";
describe(out, entry.second.after);
out << " -> ";
}
LLDependencies_describe(out, entry.first);
if (! entry.second.before.empty())
{
out << " -> before ";
describe(out, entry.second.before);
}
}
return out;
}
/// describe() helper: report a dep_set
static std::ostream& describe(std::ostream& out, const typename DepNode::dep_set& keys)
{
out << '(';
typename DepNode::dep_set::const_iterator ki(keys.begin()), kend(keys.end());
if (ki != kend)
{
LLDependencies_describe(out, *ki);
while (++ki != kend)
{
out << ", ";
LLDependencies_describe(out, *ki);
}
}
out << ')';
return out;
}
/// Iterator over the before/after KEYs on which a given NODE depends
typedef typename DepNode::dep_set::const_iterator dep_iterator;
/// range over the before/after KEYs on which a given NODE depends
typedef boost::iterator_range<dep_iterator> dep_range;
/// dependencies access from key
dep_range get_dep_range_from_key(const KEY& key, const dep_selector& selector) const
{
typename DepNodeMap::const_iterator found = mNodes.find(key);
if (found != mNodes.end())
{
return dep_range(selector(found->second));
}
// We want to return an empty range. On some platforms a default-
// constructed range (e.g. dep_range()) does NOT suffice! The client
// is likely to try to iterate from boost::begin(range) to
// boost::end(range); yet these iterators might not be valid. Instead
// return a range over a valid, empty container.
static const typename DepNode::dep_set empty_deps;
return dep_range(empty_deps.begin(), empty_deps.end());
}
/// dependencies access from any one of our key-order iterators
template<typename ITERATOR>
dep_range get_dep_range_from_xform(const ITERATOR& iterator, const dep_selector& selector) const
{
return dep_range(selector(iterator.base()->second));
}
/// dependencies access from sorted_iterator
dep_range get_dep_range_from_sorted(const sorted_iterator& sortiter,
const dep_selector& selector) const
{
// sorted_iterator is a boost::indirect_iterator wrapping an mCache
// iterator, which we can obtain by sortiter.base(). Deferencing that
// gets us an mCache entry, an 'iterator' -- one of our traversal
// iterators -- on which we can use get_dep_range_from_xform().
return get_dep_range_from_xform(*sortiter.base(), selector);
}
/**
* Get a range over the after KEYs stored for the passed KEY or iterator,
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
* range -- same as a KEY with no after KEYs. Detect existence of a KEY
* using get() instead.
*/
template<typename KEY_OR_ITER>
dep_range get_after_range(const KEY_OR_ITER& key) const;
/**
* Get a range over the before KEYs stored for the passed KEY or iterator,
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
* range -- same as a KEY with no before KEYs. Detect existence of a KEY
* using get() instead.
*/
template<typename KEY_OR_ITER>
dep_range get_before_range(const KEY_OR_ITER& key) const;
private:
DepNodeMap mNodes;
mutable iterator_list mCache;
};
/**
* Functor to get a dep_range from a KEY or iterator -- generic case. If the
* passed value isn't one of our iterator specializations, assume it's
* convertible to the KEY type.
*/
template<typename KEY_ITER>
struct LLDependencies_dep_range_from
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const KEY_ITER& key,
const SELECTOR& selector)
{
return deps.get_dep_range_from_key(key, selector);
}
};
/// Specialize LLDependencies_dep_range_from for our key-order iterators
template<typename FUNCTION, typename ITERATOR>
struct LLDependencies_dep_range_from< boost::transform_iterator<FUNCTION, ITERATOR> >
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const boost::transform_iterator<FUNCTION, ITERATOR>& iter,
const SELECTOR& selector)
{
return deps.get_dep_range_from_xform(iter, selector);
}
};
/// Specialize LLDependencies_dep_range_from for sorted_iterator
template<typename BASEITER>
struct LLDependencies_dep_range_from< boost::indirect_iterator<BASEITER> >
{
template<typename KEY, typename NODE, typename SELECTOR>
typename LLDependencies<KEY, NODE>::dep_range
operator()(const LLDependencies<KEY, NODE>& deps,
const boost::indirect_iterator<BASEITER>& iter,
const SELECTOR& selector)
{
return deps.get_dep_range_from_sorted(iter, selector);
}
};
/// generic get_after_range() implementation
template<typename KEY, typename NODE>
template<typename KEY_OR_ITER>
typename LLDependencies<KEY, NODE>::dep_range
LLDependencies<KEY, NODE>::get_after_range(const KEY_OR_ITER& key_iter) const
{
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
*this,
key_iter,
boost::bind<const typename DepNode::dep_set&>(&DepNode::after, _1));
}
/// generic get_before_range() implementation
template<typename KEY, typename NODE>
template<typename KEY_OR_ITER>
typename LLDependencies<KEY, NODE>::dep_range
LLDependencies<KEY, NODE>::get_before_range(const KEY_OR_ITER& key_iter) const
{
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
*this,
key_iter,
boost::bind<const typename DepNode::dep_set&>(&DepNode::before, _1));
}
#endif /* ! defined(LL_LLDEPENDENCIES_H) */

View File

@@ -107,14 +107,7 @@ const int LL_ERR_PRICE_MISMATCH = -23018;
#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl; #define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
#define liru_slashpos std::string(__FILE__).find_last_of("/\\") #define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs << "ASSERT (" << #func << ")" << llendl;
#define liru_slashpos2 std::string(__FILE__).substr(0,liru_slashpos).find_last_of("/\\")
#define liru_assert_strip /*strip path down to lastlevel directory and filename for assert.*/\
(liru_slashpos == std::string::npos ? std::string(__FILE__)/*just filename, print as is*/\
: liru_slashpos2 == std::string::npos ? std::string(__FILE__)/*Apparently, we're in / or perhaps the top of the drive, print as is*/\
: std::string(__FILE__).substr(1+liru_slashpos2))/*print foo/bar.cpp or perhaps foo\bar.cpp*/
#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs <<"\nASSERT(" #func ")\nfile:"<<liru_assert_strip<<" line:"<<__LINE__ << llendl;
#ifdef SHOW_ASSERT #ifdef SHOW_ASSERT
#define llassert(func) llassert_always(func) #define llassert(func) llassert_always(func)

View File

@@ -34,8 +34,6 @@
#include "llevent.h" #include "llevent.h"
using namespace LLOldEvents;
/************************************************ /************************************************
Events Events
************************************************/ ************************************************/

View File

@@ -35,12 +35,9 @@
#define LL_EVENT_H #define LL_EVENT_H
#include "llsd.h" #include "llsd.h"
#include "llpointer.h" #include "llmemory.h"
#include "llthread.h" #include "llthread.h"
namespace LLOldEvents
{
class LLEventListener; class LLEventListener;
class LLEvent; class LLEvent;
class LLEventDispatcher; class LLEventDispatcher;
@@ -197,6 +194,4 @@ public:
LLSD mValue; LLSD mValue;
}; };
} // LLOldEvents
#endif // LL_EVENT_H #endif // LL_EVENT_H

View File

@@ -1,77 +0,0 @@
/**
* @file lleventapi.cpp
* @author Nat Goodspeed
* @date 2009-11-10
* @brief Implementation for lleventapi.
*
* $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 "lleventapi.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llerror.h"
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
lbase(name, field),
ibase(name),
mDesc(desc)
{
}
LLEventAPI::~LLEventAPI()
{
}
LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
mResp(seed),
mReq(request),
mKey(replyKey)
{}
LLEventAPI::Response::~Response()
{
// When you instantiate a stack Response object, if the original
// request requested a reply, send it when we leave this block, no
// matter how.
sendReply(mResp, mReq, mKey);
}
void LLEventAPI::Response::warn(const std::string& warning)
{
LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL;
mResp["warnings"].append(warning);
}
void LLEventAPI::Response::error(const std::string& error)
{
// Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut
// down altogether.
LL_WARNS("LLEventAPI::Response") << error << LL_ENDL;
mResp["error"] = error;
}

View File

@@ -1,166 +0,0 @@
/**
* @file lleventapi.h
* @author Nat Goodspeed
* @date 2009-10-28
* @brief LLEventAPI is the base class for every class that wraps a C++ API
* in an event API
* (see https://wiki.lindenlab.com/wiki/Incremental_Viewer_Automation/Event_API).
*
* $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_LLEVENTAPI_H)
#define LL_LLEVENTAPI_H
#include "lleventdispatcher.h"
#include "llinstancetracker.h"
#include <string>
/**
* LLEventAPI not only provides operation dispatch functionality, inherited
* from LLDispatchListener -- it also gives us event API introspection.
* Deriving from LLInstanceTracker lets us enumerate instances.
*/
class LL_COMMON_API LLEventAPI: public LLDispatchListener,
public LLInstanceTracker<LLEventAPI, std::string>
{
typedef LLDispatchListener lbase;
typedef LLInstanceTracker<LLEventAPI, std::string> ibase;
public:
/**
* @param name LLEventPump name on which this LLEventAPI will listen. This
* also serves as the LLInstanceTracker instance key.
* @param desc Documentation string shown to a client trying to discover
* available event APIs.
* @param field LLSD::Map key used by LLDispatchListener to look up the
* subclass method to invoke [default "op"].
*/
LLEventAPI(const std::string& name, const std::string& desc, const std::string& field="op");
virtual ~LLEventAPI();
/// Get the string name of this LLEventAPI
std::string getName() const { return ibase::getKey(); }
/// Get the documentation string
std::string getDesc() const { return mDesc; }
/**
* Publish only selected add() methods from LLEventDispatcher.
* Every LLEventAPI add() @em must have a description string.
*/
template <typename CALLABLE>
void add(const std::string& name,
const std::string& desc,
CALLABLE callable,
const LLSD& required=LLSD())
{
LLEventDispatcher::add(name, desc, callable, required);
}
/**
* Instantiate a Response object in any LLEventAPI subclass method that
* wants to guarantee a reply (if requested) will be sent on exit from the
* method. The reply will be sent if request.has(@a replyKey), default
* "reply". If specified, the value of request[replyKey] is the name of
* the LLEventPump on which to send the reply. Conventionally you might
* code something like:
*
* @code
* void MyEventAPI::someMethod(const LLSD& request)
* {
* // Send a reply event as long as request.has("reply")
* Response response(LLSD(), request);
* // ...
* // will be sent in reply event
* response["somekey"] = some_data;
* }
* @endcode
*/
class LL_COMMON_API Response
{
public:
/**
* Instantiating a Response object in an LLEventAPI subclass method
* ensures that, if desired, a reply event will be sent.
*
* @a seed is the initial reply LLSD that will be further decorated before
* being sent as the reply
*
* @a request is the incoming request LLSD; we particularly care about
* [replyKey] and ["reqid"]
*
* @a replyKey [default "reply"] is the string name of the LLEventPump
* on which the caller wants a reply. If <tt>(!
* request.has(replyKey))</tt>, no reply will be sent.
*/
Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply");
~Response();
/**
* @code
* if (some condition)
* {
* response.warn("warnings are logged and collected in [\"warnings\"]");
* }
* @endcode
*/
void warn(const std::string& warning);
/**
* @code
* if (some condition isn't met)
* {
* // In a function returning void, you can validly 'return
* // expression' if the expression is itself of type void. But
* // returning is up to you; response.error() has no effect on
* // flow of control.
* return response.error("error message, logged and also sent as [\"error\"]");
* }
* @endcode
*/
void error(const std::string& error);
/**
* set other keys...
*
* @code
* // set any attributes you want to be sent in the reply
* response["info"] = some_value;
* // ...
* response["ok"] = went_well;
* @endcode
*/
LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
/**
* set the response to the given data
*/
void setResponse(LLSD const & response){ mResp = response; }
LLSD mResp, mReq;
LLSD::String mKey;
};
private:
std::string mDesc;
};
#endif /* ! defined(LL_LLEVENTAPI_H) */

View File

@@ -1,146 +0,0 @@
/**
* @file lleventcoro.cpp
* @author Nat Goodspeed
* @date 2009-04-29
* @brief Implementation for lleventcoro.
*
* $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 "lleventcoro.h"
// STL headers
#include <map>
// std headers
// external library headers
// other Linden headers
#include "llsdserialize.h"
#include "llerror.h"
#include "llcoros.h"
std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
{
// First, if this coroutine was launched by LLCoros::launch(), find that name.
std::string name(LLCoros::instance().getNameByID(self_id));
if (! name.empty())
{
return name;
}
// Apparently this coroutine wasn't launched by LLCoros::launch(). Check
// whether we have a memo for this self_id.
typedef std::map<const void*, std::string> MapType;
static MapType memo;
MapType::const_iterator found = memo.find(self_id);
if (found != memo.end())
{
// this coroutine instance has called us before, reuse same name
return found->second;
}
// this is the first time we've been called for this coroutine instance
name = LLEventPump::inventName("coro");
memo[self_id] = name;
LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '"
<< name << "'" << LL_ENDL;
return name;
}
void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
{
if (rawPath.isUndefined())
{
// no-op case
return;
}
// Arrange to treat rawPath uniformly as an array. If it's not already an
// array, store it as the only entry in one.
LLSD path;
if (rawPath.isArray())
{
path = rawPath;
}
else
{
path.append(rawPath);
}
// Need to indicate a current destination -- but that current destination
// needs to change as we step through the path array. Where normally we'd
// use an LLSD& to capture a subscripted LLSD lvalue, this time we must
// instead use a pointer -- since it must be reassigned.
LLSD* pdest = &dest;
// Now loop through that array
for (LLSD::Integer i = 0; i < path.size(); ++i)
{
if (path[i].isString())
{
// *pdest is an LLSD map
pdest = &((*pdest)[path[i].asString()]);
}
else if (path[i].isInteger())
{
// *pdest is an LLSD array
pdest = &((*pdest)[path[i].asInteger()]);
}
else
{
// What do we do with Real or Array or Map or ...?
// As it's a coder error -- not a user error -- rub the coder's
// face in it so it gets fixed.
LL_ERRS("lleventcoro") << "storeToLLSDPath(" << dest << ", " << rawPath << ", " << value
<< "): path[" << i << "] bad type " << path[i].type() << LL_ENDL;
}
}
// Here *pdest is where we should store value.
*pdest = value;
}
LLSD errorException(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), instead of
// returning it, deliver it via exception.
if (result.second)
{
throw LLErrorEvent(desc, result.first);
}
// That way, our caller knows a simple return must be from the reply
// pump (pump 0).
return result.first;
}
LLSD errorLog(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), log it as a fatal
// error.
if (result.second)
{
LL_ERRS("errorLog") << desc << ":" << std::endl;
LLSDSerialize::toPrettyXML(result.first, LL_CONT);
LL_CONT << LL_ENDL;
}
// A simple return must therefore be from the reply pump (pump 0).
return result.first;
}

View File

@@ -1,569 +0,0 @@
/**
* @file lleventcoro.h
* @author Nat Goodspeed
* @date 2009-04-29
* @brief Utilities to interface between coroutines and events.
*
* $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_LLEVENTCORO_H)
#define LL_LLEVENTCORO_H
#include <boost/coroutine/coroutine.hpp>
#include <boost/coroutine/future.hpp>
#include <boost/optional.hpp>
#include <string>
#include <stdexcept>
#include "llevents.h"
#include "llerror.h"
/**
* Like LLListenerOrPumpName, this is a class intended for parameter lists:
* accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an
* <tt>LLEventPump&</tt> or its string name. For a single parameter that could
* be either, it's not hard to overload the function -- but as soon as you
* want to accept two such parameters, this is cheaper than four overloads.
*/
class LLEventPumpOrPumpName
{
public:
/// Pass an actual LLEventPump&
LLEventPumpOrPumpName(LLEventPump& pump):
mPump(pump)
{}
/// Pass the string name of an LLEventPump
LLEventPumpOrPumpName(const std::string& pumpname):
mPump(LLEventPumps::instance().obtain(pumpname))
{}
/// Pass string constant name of an LLEventPump. This override must be
/// explicit, since otherwise passing <tt>const char*</tt> to a function
/// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two
/// different implicit conversions: <tt>const char*</tt> -> <tt>const
/// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>.
LLEventPumpOrPumpName(const char* pumpname):
mPump(LLEventPumps::instance().obtain(pumpname))
{}
/// Unspecified: "I choose not to identify an LLEventPump."
LLEventPumpOrPumpName() {}
operator LLEventPump& () const { return *mPump; }
LLEventPump& getPump() const { return *mPump; }
operator bool() const { return mPump; }
bool operator!() const { return ! mPump; }
private:
boost::optional<LLEventPump&> mPump;
};
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
/// isn't a valid LLEventPump listener: such listeners should return bool.
template <typename LISTENER>
class LLVoidListener
{
public:
LLVoidListener(const LISTENER& listener):
mListener(listener)
{}
bool operator()(const LLSD& event)
{
mListener(event);
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
};
/// LLVoidListener helper function to infer the type of the LISTENER
template <typename LISTENER>
LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
{
return LLVoidListener<LISTENER>(listener);
}
namespace LLEventDetail
{
/**
* waitForEventOn() permits a coroutine to temporarily listen on an
* LLEventPump any number of times. We don't really want to have to ask
* the caller to label each such call with a distinct string; the whole
* point of waitForEventOn() is to present a nice sequential interface to
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
* LLEventPump::inventName() to generate a distinct name for each
* temporary listener. On the other hand, because a given coroutine might
* call waitForEventOn() any number of times, we don't really want to
* consume an arbitrary number of generated inventName()s: that namespace,
* though large, is nonetheless finite. So we memoize an invented name for
* each distinct coroutine instance (each different 'self' object). We
* can't know the type of 'self', because it depends on the coroutine
* body's signature. So we cast its address to void*, looking for distinct
* pointer values. Yes, that means that an early coroutine could cache a
* value here, then be destroyed, only to be supplanted by a later
* coroutine (of the same or different type), and we'll end up
* "recognizing" the second one and reusing the listener name -- but
* that's okay, since it won't collide with any listener name used by the
* earlier coroutine since that earlier coroutine no longer exists.
*/
template <typename COROUTINE_SELF>
std::string listenerNameForCoro(COROUTINE_SELF& self)
{
return listenerNameForCoroImpl(self.get_id());
}
/// Implementation for listenerNameForCoro()
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
/**
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
* parameter:
*
* * If <tt>path.isUndefined()</tt>, do nothing.
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
* into <tt>dest[path.asString()]</tt>.
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
* value into <tt>dest[path.asInteger()]</tt>.
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
* down through the structure of @a dest. The last array entry in @a
* path specifies the entry in the lowest-level structure in @a dest
* into which to store @a value.
*
* @note
* In the degenerate case in which @a path is an empty array, @a dest will
* @em become @a value rather than @em containing it.
*/
LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
} // namespace LLEventDetail
/**
* Post specified LLSD event on the specified LLEventPump, then wait for a
* response on specified other LLEventPump. This is more than mere
* convenience: the difference between this function and the sequence
* @code
* requestPump.post(myEvent);
* LLSD reply = waitForEventOn(self, replyPump);
* @endcode
* is that the sequence above fails if the reply is posted immediately on
* @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
* sequence above, the running coroutine isn't even listening on @a replyPump
* until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
* entered. Therefore, the coroutine completely misses an immediate reply
* event, making it wait indefinitely.
*
* By contrast, postAndWait() listens on the @a replyPump @em before posting
* the specified LLSD event on the specified @a requestPump.
*
* @param self The @c self object passed into a coroutine
* @param event LLSD data to be posted on @a requestPump
* @param requestPump an LLEventPump on which to post @a event. Pass either
* the LLEventPump& or its string name. However, if you pass a
* default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
* @param replyPump an LLEventPump on which postAndWait() will listen for a
* reply. Pass either the LLEventPump& or its string name. The calling
* coroutine will wait until that reply arrives. (If you're concerned about a
* reply that might not arrive, please see also LLEventTimeout.)
* @param replyPumpNamePath specifies the location within @a event in which to
* store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
* feature; obviously you can store the name in @a event "by hand" if desired.
* @a replyPumpNamePath can be specified in any of four forms:
* * @c isUndefined() (default-constructed LLSD object): do nothing. This is
* the default behavior if you omit @a replyPumpNamePath.
* * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt>
* in <tt>event[replyPumpNamePath.asInteger()]</tt>.
* * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in
* <tt>event[replyPumpNamePath.asString()]</tt>.
* * @c isArray(): @a event has several levels of structure, e.g. map of
* maps, array of arrays, array of maps, map of arrays, ... Store
* <tt>replyPump.getName()</tt> in
* <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other
* words, examine each array entry in @a replyPumpNamePath in turn. If it's an
* <tt>LLSD::String</tt>, the current level of @a event is a map; step down to
* that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a
* event is an array; step down to that array entry. The last array entry in
* @a replyPumpNamePath specifies the entry in the lowest-level structure in
* @a event into which to store <tt>replyPump.getName()</tt>.
*/
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
{
// declare the future
boost::coroutines::future<LLSD> future(self);
// make a callback that will assign a value to the future, and listen on
// the specified LLEventPump with that callback
std::string listenerName(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection(
replyPump.getPump().listen(listenerName,
voidlistener(boost::coroutines::make_callback(future))));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If replyPumpNamePath is non-empty, store the replyPump name in the
// request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " posting to " << requestPump.getPump().getName()
<< LL_ENDL;
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
// << ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
<< LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLSD value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
<< " resuming with " << value << LL_ENDL;
// returning should disconnect the connection
return value;
}
/// Wait for the next event on the specified LLEventPump. Pass either the
/// LLEventPump& or its string name.
template <typename SELF>
LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
{
// This is now a convenience wrapper for postAndWait().
return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
}
/// return type for two-pump variant of waitForEventOn()
typedef std::pair<LLSD, int> LLEventWithID;
namespace LLEventDetail
{
/**
* This helper is specifically for the two-pump version of waitForEventOn().
* We use a single future object, but we want to listen on two pumps with it.
* Since we must still adapt from (the callable constructed by)
* boost::coroutines::make_callback() (void return) to provide an event
* listener (bool return), we've adapted LLVoidListener for the purpose. The
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
* -- binding different instance data -- for each of the pumps. Then, when a
* pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
* that LLSD with its discriminator to feed the future object.
*/
template <typename LISTENER>
class WaitForEventOnHelper
{
public:
WaitForEventOnHelper(const LISTENER& listener, int discriminator):
mListener(listener),
mDiscrim(discriminator)
{}
// this signature is required for an LLEventPump listener
bool operator()(const LLSD& event)
{
// our future object is defined to accept LLEventWithID
mListener(LLEventWithID(event, mDiscrim));
// don't swallow the event, let other listeners see it
return false;
}
private:
LISTENER mListener;
const int mDiscrim;
};
/// WaitForEventOnHelper type-inference helper
template <typename LISTENER>
WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
{
return WaitForEventOnHelper<LISTENER>(listener, discriminator);
}
} // namespace LLEventDetail
/**
* This function waits for a reply on either of two specified LLEventPumps.
* Otherwise, it closely resembles postAndWait(); please see the documentation
* for that function for detailed parameter info.
*
* While we could have implemented the single-pump variant in terms of this
* one, there's enough added complexity here to make it worthwhile to give the
* single-pump variant its own straightforward implementation. Conversely,
* though we could use preprocessor logic to generate n-pump overloads up to
* BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump
* overload exists because certain event APIs are defined in terms of a reply
* LLEventPump and an error LLEventPump.
*
* The LLEventWithID return value provides not only the received event, but
* the index of the pump on which it arrived (0 or 1).
*
* @note
* I'd have preferred to overload the name postAndWait() for both signatures.
* But consider the following ambiguous call:
* @code
* postAndWait(self, LLSD(), requestPump, replyPump, "someString");
* @endcode
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
* function).
*
* It seems less burdensome to write postAndWait2() than to write either
* LLSD("someString") or LLEventOrPumpName("someString").
*/
template <typename SELF>
LLEventWithID postAndWait2(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump0,
const LLEventPumpOrPumpName& replyPump1,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
// declare the future
boost::coroutines::future<LLEventWithID> future(self);
// either callback will assign a value to this future; listen on
// each specified LLEventPump with a callback
std::string name(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection0(
replyPump0.getPump().listen(name + "a",
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0)));
LLTempBoundListener connection1(
replyPump1.getPump().listen(name + "b",
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1)));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
// If either replyPumpNamePath is non-empty, store the corresponding
// replyPump name in the request event.
LLSD modevent(event);
LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
replyPump0.getPump().getName());
LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
replyPump1.getPump().getName());
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " posting to " << requestPump.getPump().getName()
<< ": " << modevent << LL_ENDL;
requestPump.getPump().post(modevent);
}
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
LLEventWithID value(*future);
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
<< " resuming with (" << value.first << ", " << value.second << ")"
<< LL_ENDL;
// returning should disconnect both connections
return value;
}
/**
* Wait for the next event on either of two specified LLEventPumps.
*/
template <typename SELF>
LLEventWithID
waitForEventOn(SELF& self,
const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
{
// This is now a convenience wrapper for postAndWait2().
return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
}
/**
* Helper for the two-pump variant of waitForEventOn(), e.g.:
*
* @code
* LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
* "error response from login.cgi");
* @endcode
*
* Examines an LLEventWithID, assuming that the second pump (pump 1) is
* listening for an error indication. If the incoming data arrived on pump 1,
* throw an LLErrorEvent exception. If the incoming data arrived on pump 0,
* just return it. Since a normal return can only be from pump 0, we no longer
* need the LLEventWithID's discriminator int; we can just return the LLSD.
*
* @note I'm not worried about introducing the (fairly generic) name
* errorException() into global namespace, because how many other overloads of
* the same name are going to accept an LLEventWithID parameter?
*/
LLSD errorException(const LLEventWithID& result, const std::string& desc);
/**
* Exception thrown by errorException(). We don't call this LLEventError
* because it's not an error in event processing: rather, this exception
* announces an event that bears error information (for some other API).
*/
class LL_COMMON_API LLErrorEvent: public std::runtime_error
{
public:
LLErrorEvent(const std::string& what, const LLSD& data):
std::runtime_error(what),
mData(data)
{}
virtual ~LLErrorEvent() throw() {}
LLSD getData() const { return mData; }
private:
LLSD mData;
};
/**
* Like errorException(), save that this trips a fatal error using LL_ERRS
* rather than throwing an exception.
*/
LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
/**
* Certain event APIs require the name of an LLEventPump on which they should
* post results. While it works to invent a distinct name and let
* LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
* in a certain sense it's more robust to instantiate a local LLEventPump and
* provide its name instead. This class packages the following idiom:
*
* 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
* 2. Provide its actual name to the event API in question as the name of the
* reply LLEventPump.
* 3. Initiate the request to the event API.
* 4. Call your LLEventTempStream's wait() method to wait for the reply.
* 5. Let the LLCoroEventPump go out of scope.
*/
class LL_COMMON_API LLCoroEventPump
{
public:
LLCoroEventPump(const std::string& name="coro"):
mPump(name, true) // allow tweaking the pump instance name
{}
/// It's typical to request the LLEventPump name to direct an event API to
/// send its response to this pump.
std::string getName() const { return mPump.getName(); }
/// Less typically, we'd request the pump itself for some reason.
LLEventPump& getPump() { return mPump; }
/**
* Wait for an event on this LLEventPump.
*
* @note
* The other major usage pattern we considered was to bind @c self at
* LLCoroEventPump construction time, which would avoid passing the
* parameter to each wait() call. But if we were going to bind @c self as
* a class member, we'd need to specify a class template parameter
* indicating its type. The big advantage of passing it to the wait() call
* is that the type can be implicit.
*/
template <typename SELF>
LLSD wait(SELF& self)
{
return waitForEventOn(self, mPump);
}
template <typename SELF>
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPumpNamePath=LLSD())
{
return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
}
private:
LLEventStream mPump;
};
/**
* Other event APIs require the names of two different LLEventPumps: one for
* success response, the other for error response. Extend LLCoroEventPump
* for the two-pump use case.
*/
class LL_COMMON_API LLCoroEventPumps
{
public:
LLCoroEventPumps(const std::string& name="coro",
const std::string& suff0="Reply",
const std::string& suff1="Error"):
mPump0(name + suff0, true), // allow tweaking the pump instance name
mPump1(name + suff1, true)
{}
/// request pump 0's name
std::string getName0() const { return mPump0.getName(); }
/// request pump 1's name
std::string getName1() const { return mPump1.getName(); }
/// request both names
std::pair<std::string, std::string> getNames() const
{
return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName());
}
/// request pump 0
LLEventPump& getPump0() { return mPump0; }
/// request pump 1
LLEventPump& getPump1() { return mPump1; }
/// waitForEventOn(self, either of our two LLEventPumps)
template <typename SELF>
LLEventWithID wait(SELF& self)
{
return waitForEventOn(self, mPump0, mPump1);
}
/// errorException(wait(self))
template <typename SELF>
LLSD waitWithException(SELF& self)
{
return errorException(wait(self), std::string("Error event on ") + getName1());
}
/// errorLog(wait(self))
template <typename SELF>
LLSD waitWithLog(SELF& self)
{
return errorLog(wait(self), std::string("Error event on ") + getName1());
}
template <typename SELF>
LLEventWithID postAndWait(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return postAndWait2(self, event, requestPump, mPump0, mPump1,
replyPump0NamePath, replyPump1NamePath);
}
template <typename SELF>
LLSD postAndWaitWithException(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorException(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
template <typename SELF>
LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
return errorLog(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
private:
LLEventStream mPump0, mPump1;
};
#endif /* ! defined(LL_LLEVENTCORO_H) */

View File

@@ -1,672 +0,0 @@
/**
* @file lleventdispatcher.cpp
* @author Nat Goodspeed
* @date 2009-06-18
* @brief Implementation for lleventdispatcher.
*
* $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 LL_WINDOWS
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
// Precompiled header
#include "linden_common.h"
// associated header
#include "lleventdispatcher.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llevents.h"
#include "llerror.h"
#include "llsdutil.h"
#include "stringize.h"
#include <memory> // std::auto_ptr
/*****************************************************************************
* LLSDArgsSource
*****************************************************************************/
/**
* Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
* if the consumer requests more elements than the array contains.
*/
class LL_COMMON_API LLSDArgsSource
{
public:
LLSDArgsSource(const std::string function, const LLSD& args);
~LLSDArgsSource();
LLSD next();
void done() const;
private:
std::string _function;
LLSD _args;
LLSD::Integer _index;
};
LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
_function(function),
_args(args),
_index(0)
{
if (! (_args.isUndefined() || _args.isArray()))
{
LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
<< _args << LL_ENDL;
}
}
LLSDArgsSource::~LLSDArgsSource()
{
done();
}
LLSD LLSDArgsSource::next()
{
if (_index >= _args.size())
{
LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
<< _args.size() << " provided: " << _args << LL_ENDL;
}
return _args[_index++];
}
void LLSDArgsSource::done() const
{
if (_index < _args.size())
{
LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
<< " of the " << _args.size() << " arguments provided: "
<< _args << LL_ENDL;
}
}
/*****************************************************************************
* LLSDArgsMapper
*****************************************************************************/
/**
* From a formal parameters description and a map of arguments, construct an
* arguments array.
*
* That is, given:
* - an LLSD array of length n containing parameter-name strings,
* corresponding to the arguments of a function of interest
* - an LLSD collection specifying default parameter values, either:
* - an LLSD array of length m <= n, matching the rightmost m params, or
* - an LLSD map explicitly stating default name=value pairs
* - an LLSD map of parameter names and actual values for a particular
* function call
* construct an LLSD array of actual argument values for this function call.
*
* The parameter-names array and the defaults collection describe the function
* being called. The map might vary with every call, providing argument values
* for the described parameters.
*
* The array of parameter names must match the number of parameters expected
* by the function of interest.
*
* If you pass a map of default parameter values, it provides default values
* as you might expect. It is an error to specify a default value for a name
* not listed in the parameters array.
*
* If you pass an array of default parameter values, it is mapped to the
* rightmost m of the n parameter names. It is an error if the default-values
* array is longer than the parameter-names array. Consider the following
* parameter names: ["a", "b", "c", "d"].
*
* - An empty array of default values (or an isUndefined() value) asserts that
* every one of the above parameter names is required.
* - An array of four default values [1, 2, 3, 4] asserts that every one of
* the above parameters is optional. If the current parameter map is empty,
* they will be passed to the function as [1, 2, 3, 4].
* - An array of two default values [11, 12] asserts that parameters "a" and
* "b" are required, while "c" and "d" are optional, having default values
* "c"=11 and "d"=12.
*
* The arguments array is constructed as follows:
*
* - Arguments-map keys not found in the parameter-names array are ignored.
* - Entries from the map provide values for an improper subset of the
* parameters named in the parameter-names array. This results in a
* tentative values array with "holes." (size of map) + (number of holes) =
* (size of names array)
* - Holes are filled with the default values.
* - Any remaining holes constitute an error.
*/
class LL_COMMON_API LLSDArgsMapper
{
public:
/// Accept description of function: function name, param names, param
/// default values
LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
/// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
LLSD map(const LLSD& argsmap) const;
private:
static std::string formatlist(const LLSD&);
// The function-name string is purely descriptive. We want error messages
// to be able to indicate which function's LLSDArgsMapper has the problem.
std::string _function;
// Store the names array pretty much as given.
LLSD _names;
// Though we're handed an array of name strings, it's more useful to us to
// store it as a map from name string to position index. Of course that's
// easy to generate from the incoming names array, but why do it more than
// once?
typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
IndexMap _indexes;
// Generated array of default values, aligned with the array of param names.
LLSD _defaults;
// Indicate whether we have a default value for each param.
typedef std::vector<char> FilledVector;
FilledVector _has_dft;
};
LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
const LLSD& names, const LLSD& defaults):
_function(function),
_names(names),
_has_dft(names.size())
{
if (! (_names.isUndefined() || _names.isArray()))
{
LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
}
LLSD::Integer nparams(_names.size());
// From _names generate _indexes.
for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
{
_indexes[_names[ni]] = ni;
}
// Presize _defaults() array so we don't have to resize it more than once.
// All entries are initialized to LLSD(); but since _has_dft is still all
// 0, they're all "holes" for now.
if (nparams)
{
_defaults[nparams - 1] = LLSD();
}
if (defaults.isUndefined() || defaults.isArray())
{
LLSD::Integer ndefaults = defaults.size();
// defaults is a (possibly empty) array. Right-align it with names.
if (ndefaults > nparams)
{
LL_ERRS("LLSDArgsMapper") << function << " names array " << names
<< " shorter than defaults array " << defaults << LL_ENDL;
}
// Offset by which we slide defaults array right to right-align with
// _names array
LLSD::Integer offset = nparams - ndefaults;
// Fill rightmost _defaults entries from defaults, and mark them as
// filled
for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
{
_defaults[i + offset] = defaults[i];
_has_dft[i + offset] = 1;
}
}
else if (defaults.isMap())
{
// defaults is a map. Use it to populate the _defaults array.
LLSD bogus;
for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
mi != mend; ++mi)
{
IndexMap::const_iterator ixit(_indexes.find(mi->first));
if (ixit == _indexes.end())
{
bogus.append(mi->first);
continue;
}
LLSD::Integer pos = ixit->second;
// Store default value at that position in the _defaults array.
_defaults[pos] = mi->second;
// Don't forget to record the fact that we've filled this
// position.
_has_dft[pos] = 1;
}
if (bogus.size())
{
LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
<< formatlist(bogus) << LL_ENDL;
}
}
else
{
LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
<< defaults << LL_ENDL;
}
}
LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
{
if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
{
LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
<< argsmap << LL_ENDL;
}
// Initialize the args array. Indexing a non-const LLSD array grows it
// to appropriate size, but we don't want to resize this one on each
// new operation. Just make it as big as we need before we start
// stuffing values into it.
LLSD args(LLSD::emptyArray());
if (_defaults.size() == 0)
{
// If this function requires no arguments, fast exit. (Don't try to
// assign to args[-1].)
return args;
}
args[_defaults.size() - 1] = LLSD();
// Get a vector of chars to indicate holes. It's tempting to just scan
// for LLSD::isUndefined() values after filling the args array from
// the map, but it's plausible for caller to explicitly pass
// isUndefined() as the value of some parameter name. That's legal
// since isUndefined() has well-defined conversions (default value)
// for LLSD data types. So use a whole separate array for detecting
// holes. (Avoid std::vector<bool> which is known to be odd -- can we
// iterate?)
FilledVector filled(args.size());
if (argsmap.isArray())
{
// Fill args from array. If there are too many args in passed array,
// ignore the rest.
LLSD::Integer size(argsmap.size());
if (size > args.size())
{
// We don't just use std::min() because we want to sneak in this
// warning if caller passes too many args.
LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
<< " params, ignoring last " << (size - args.size())
<< " of passed " << size << ": " << argsmap << LL_ENDL;
size = args.size();
}
for (LLSD::Integer i(0); i < size; ++i)
{
// Copy the actual argument from argsmap
args[i] = argsmap[i];
// Note that it's been filled
filled[i] = 1;
}
}
else
{
// argsmap is in fact a map. Walk the map.
for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
mi != mend; ++mi)
{
// mi->first is a parameter-name string, with mi->second its
// value. Look up the name's position index in _indexes.
IndexMap::const_iterator ixit(_indexes.find(mi->first));
if (ixit == _indexes.end())
{
// Allow for a map containing more params than were passed in
// our names array. Caller typically receives a map containing
// the function name, cruft such as reqid, etc. Ignore keys
// not defined in _indexes.
LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
<< mi->first << "=" << mi->second << LL_ENDL;
continue;
}
LLSD::Integer pos = ixit->second;
// Store the value at that position in the args array.
args[pos] = mi->second;
// Don't forget to record the fact that we've filled this
// position.
filled[pos] = 1;
}
}
// Fill any remaining holes from _defaults.
LLSD unfilled(LLSD::emptyArray());
for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
{
if (! filled[i])
{
// If there's no default value for this parameter, that's an
// error.
if (! _has_dft[i])
{
unfilled.append(_names[i]);
}
else
{
args[i] = _defaults[i];
}
}
}
// If any required args -- args without defaults -- were left unfilled
// by argsmap, that's a problem.
if (unfilled.size())
{
LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
<< formatlist(unfilled) << " from " << argsmap << LL_ENDL;
}
// done
return args;
}
std::string LLSDArgsMapper::formatlist(const LLSD& list)
{
std::ostringstream out;
const char* delim = "";
for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
li != lend; ++li)
{
out << delim << li->asString();
delim = ", ";
}
return out.str();
}
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
mDesc(desc),
mKey(key)
{
}
LLEventDispatcher::~LLEventDispatcher()
{
}
/**
* DispatchEntry subclass used for callables accepting(const LLSD&)
*/
struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
{
LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
DispatchEntry(desc),
mFunc(func),
mRequired(required)
{}
Callable mFunc;
LLSD mRequired;
virtual void call(const std::string& desc, const LLSD& event) const
{
// Validate the syntax of the event itself.
std::string mismatch(llsd_matches(mRequired, event));
if (! mismatch.empty())
{
LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
}
// Event syntax looks good, go for it!
mFunc(event);
}
virtual LLSD addMetadata(LLSD meta) const
{
meta["required"] = mRequired;
return meta;
}
};
/**
* DispatchEntry subclass for passing LLSD to functions accepting
* arbitrary argument types (convertible via LLSDParam)
*/
struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
{
ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
DispatchEntry(desc),
mInvoker(func)
{}
invoker_function mInvoker;
virtual void call(const std::string& desc, const LLSD& event) const
{
LLSDArgsSource src(desc, event);
mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
}
};
/**
* DispatchEntry subclass for dispatching LLSD::Array to functions accepting
* arbitrary argument types (convertible via LLSDParam)
*/
struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
{
ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
LLSD::Integer arity):
ParamsDispatchEntry(desc, func),
mArity(arity)
{}
LLSD::Integer mArity;
virtual LLSD addMetadata(LLSD meta) const
{
LLSD array(LLSD::emptyArray());
// Resize to number of arguments required
if (mArity)
array[mArity - 1] = LLSD();
llassert_always(array.size() == mArity);
meta["required"] = array;
return meta;
}
};
/**
* DispatchEntry subclass for dispatching LLSD::Map to functions accepting
* arbitrary argument types (convertible via LLSDParam)
*/
struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
{
MapParamsDispatchEntry(const std::string& name, const std::string& desc,
const invoker_function& func,
const LLSD& params, const LLSD& defaults):
ParamsDispatchEntry(desc, func),
mMapper(name, params, defaults),
mRequired(LLSD::emptyMap())
{
// Build the set of all param keys, then delete the ones that are
// optional. What's left are the ones that are required.
for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
pi != pend; ++pi)
{
mRequired[pi->asString()] = LLSD();
}
if (defaults.isArray() || defaults.isUndefined())
{
// Right-align the params and defaults arrays.
LLSD::Integer offset = params.size() - defaults.size();
// Now the name of every defaults[i] is at params[i + offset].
for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
{
// Erase this optional param from mRequired.
mRequired.erase(params[i + offset].asString());
// Instead, make an entry in mOptional with the default
// param's name and value.
mOptional[params[i + offset].asString()] = defaults[i];
}
}
else if (defaults.isMap())
{
// if defaults is already a map, then it's already in the form we
// intend to deliver in metadata
mOptional = defaults;
// Just delete from mRequired every key appearing in mOptional.
for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
mi != mend; ++mi)
{
mRequired.erase(mi->first);
}
}
}
LLSDArgsMapper mMapper;
LLSD mRequired;
LLSD mOptional;
virtual void call(const std::string& desc, const LLSD& event) const
{
// Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
// to base-class call() method.
ParamsDispatchEntry::call(desc, mMapper.map(event));
}
virtual LLSD addMetadata(LLSD meta) const
{
meta["required"] = mRequired;
meta["optional"] = mOptional;
return meta;
}
};
void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
LLSD::Integer arity)
{
mDispatch.insert(
DispatchMap::value_type(name, DispatchMap::mapped_type(
new ArrayParamsDispatchEntry(desc, invoker, arity))));
}
void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
const LLSD& params,
const LLSD& defaults)
{
mDispatch.insert(
DispatchMap::value_type(name, DispatchMap::mapped_type(
new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
}
/// Register a callable by name
void LLEventDispatcher::add(const std::string& name, const std::string& desc,
const Callable& callable, const LLSD& required)
{
mDispatch.insert(
DispatchMap::value_type(name, DispatchMap::mapped_type(
new LLSDDispatchEntry(desc, callable, required))));
}
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
<< "): " << classname << " is not a subclass "
<< "of LLEventDispatcher" << LL_ENDL;
}
/// Unregister a callable
bool LLEventDispatcher::remove(const std::string& name)
{
DispatchMap::iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return false;
}
mDispatch.erase(found);
return true;
}
/// Call a registered callable with an explicitly-specified name. If no
/// such callable exists, die with LL_ERRS.
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
{
if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
<< "' not found" << LL_ENDL;
}
}
/// Extract the @a key value from the incoming @a event, and call the
/// callable whose name is specified by that map @a key. If no such
/// callable exists, die with LL_ERRS.
void LLEventDispatcher::operator()(const LLSD& event) const
{
// This could/should be implemented in terms of the two-arg overload.
// However -- we can produce a more informative error message.
std::string name(event[mKey]);
if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
<< " value '" << name << "'" << LL_ENDL;
}
}
bool LLEventDispatcher::try_call(const LLSD& event) const
{
return try_call(event[mKey], event);
}
bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return false;
}
// Found the name, so it's plausible to even attempt the call.
found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
event);
return true; // tell caller we were able to call
}
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
return LLSD();
}
LLSD meta;
meta["name"] = name;
meta["desc"] = found->second->mDesc;
return found->second->addMetadata(meta);
}
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
LLEventDispatcher(pumpname, key),
mPump(pumpname, true), // allow tweaking for uniqueness
mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
{
}
bool LLDispatchListener::process(const LLSD& event)
{
(*this)(event);
return false;
}
LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
mDesc(desc)
{}

View File

@@ -1,541 +0,0 @@
/**
* @file lleventdispatcher.h
* @author Nat Goodspeed
* @date 2009-06-18
* @brief Central mechanism for dispatching events by string name. This is
* useful when you have a single LLEventPump listener on which you can
* request different operations, vs. instantiating a different
* LLEventPump for each such operation.
*
* $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$
*
* The invoker machinery that constructs a boost::fusion argument list for use
* with boost::fusion::invoke() is derived from
* http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
* whose license information is copied below:
*
* "(C) Copyright Tobias Schwinger
*
* Use modification and distribution are subject to the boost Software License,
* Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
*/
#if ! defined(LL_LLEVENTDISPATCHER_H)
#define LL_LLEVENTDISPATCHER_H
// nil is too generic a term to be allowed to be a global macro. In
// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
// namespace) that a global 'nil' macro breaks badly.
#if defined(nil)
// Capture the value of the macro 'nil', hoping int is an appropriate type.
static const int nil_(nil);
// Now forget the macro.
#undef nil
// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
// defined const 'nil_'. Make it static since otherwise it produces duplicate-
// symbol link errors later.
static const int& nil(nil_);
#endif
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/function_types/is_nonmember_callable_builtin.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/function_arity.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/fusion/include/push_back.hpp>
#include <boost/fusion/include/cons.hpp>
#include <boost/fusion/include/invoke.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/next.hpp>
#include <boost/mpl/deref.hpp>
#include <typeinfo>
#include "llevents.h"
#include "llsdutil.h"
class LLSD;
/**
* Given an LLSD map, examine a string-valued key and call a corresponding
* callable. This class is designed to be contained by an LLEventPump
* listener class that will register some of its own methods, though any
* callable can be used.
*/
class LL_COMMON_API LLEventDispatcher
{
public:
LLEventDispatcher(const std::string& desc, const std::string& key);
virtual ~LLEventDispatcher();
/// @name Register functions accepting(const LLSD&)
//@{
/// Accept any C++ callable with the right signature, typically a
/// boost::bind() expression
typedef boost::function<void(const LLSD&)> Callable;
/**
* Register a @a callable by @a name. The passed @a callable accepts a
* single LLSD value and uses it in any way desired, e.g. extract
* parameters and call some other function. The optional @a required
* parameter is used to validate the structure of each incoming event (see
* llsd_matches()).
*/
void add(const std::string& name,
const std::string& desc,
const Callable& callable,
const LLSD& required=LLSD());
/**
* The case of a free function (or static method) accepting(const LLSD&)
* could also be intercepted by the arbitrary-args overload below. Ensure
* that it's directed to the Callable overload above instead.
*/
void add(const std::string& name,
const std::string& desc,
void (*f)(const LLSD&),
const LLSD& required=LLSD())
{
add(name, desc, Callable(f), required);
}
/**
* Special case: a subclass of this class can pass an unbound member
* function pointer (of an LLEventDispatcher subclass) without explicitly
* specifying the <tt>boost::bind()</tt> expression. The passed @a method
* accepts a single LLSD value, presumably containing other parameters.
*/
template <class CLASS>
void add(const std::string& name,
const std::string& desc,
void (CLASS::*method)(const LLSD&),
const LLSD& required=LLSD())
{
addMethod<CLASS>(name, desc, method, required);
}
/// Overload for both const and non-const methods. The passed @a method
/// accepts a single LLSD value, presumably containing other parameters.
template <class CLASS>
void add(const std::string& name,
const std::string& desc,
void (CLASS::*method)(const LLSD&) const,
const LLSD& required=LLSD())
{
addMethod<CLASS>(name, desc, method, required);
}
//@}
/// @name Register functions with arbitrary param lists
//@{
/**
* Register a free function with arbitrary parameters. (This also works
* for static class methods.)
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* When calling this name, pass an LLSD::Array. Each entry in turn will be
* converted to the corresponding parameter type using LLSDParam.
*/
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
>::type add(const std::string& name,
const std::string& desc,
Function f);
/**
* Register a nonstatic class method with arbitrary parameters.
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* To cover cases such as a method on an LLSingleton we don't yet want to
* instantiate, instead of directly storing an instance pointer, accept a
* nullary callable returning a pointer/reference to the desired class
* instance. If you already have an instance in hand,
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
* produce suitable callables.
*
* When calling this name, pass an LLSD::Array. Each entry in turn will be
* converted to the corresponding parameter type using LLSDParam.
*/
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
>::type add(const std::string& name,
const std::string& desc,
Method f,
const InstanceGetter& getter);
/**
* Register a free function with arbitrary parameters. (This also works
* for static class methods.)
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* Pass an LLSD::Array of parameter names, and optionally another
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
*
* When calling this name, pass an LLSD::Map. We will internally generate
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
* to the corresponding parameter type using LLSDParam.
*/
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
>::type add(const std::string& name,
const std::string& desc,
Function f,
const LLSD& params,
const LLSD& defaults=LLSD());
/**
* Register a nonstatic class method with arbitrary parameters.
*
* @note This supports functions with up to about 6 parameters -- after
* that you start getting dismaying compile errors in which
* boost::fusion::joint_view is mentioned a surprising number of times.
*
* To cover cases such as a method on an LLSingleton we don't yet want to
* instantiate, instead of directly storing an instance pointer, accept a
* nullary callable returning a pointer/reference to the desired class
* instance. If you already have an instance in hand,
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
* produce suitable callables.
*
* Pass an LLSD::Array of parameter names, and optionally another
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
*
* When calling this name, pass an LLSD::Map. We will internally generate
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
* to the corresponding parameter type using LLSDParam.
*/
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
>::type add(const std::string& name,
const std::string& desc,
Method f,
const InstanceGetter& getter,
const LLSD& params,
const LLSD& defaults=LLSD());
//@}
/// Unregister a callable
bool remove(const std::string& name);
/// Call a registered callable with an explicitly-specified name. If no
/// such callable exists, die with LL_ERRS. If the @a event fails to match
/// the @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const std::string& name, const LLSD& event) const;
/// Call a registered callable with an explicitly-specified name and
/// return <tt>true</tt>. If no such callable exists, return
/// <tt>false</tt>. If the @a event fails to match the @a required
/// prototype specified at add() time, die with LL_ERRS.
bool try_call(const std::string& name, const LLSD& event) const;
/// Extract the @a key value from the incoming @a event, and call the
/// callable whose name is specified by that map @a key. If no such
/// callable exists, die with LL_ERRS. If the @a event fails to match the
/// @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const LLSD& event) const;
/// Extract the @a key value from the incoming @a event, call the callable
/// whose name is specified by that map @a key and return <tt>true</tt>.
/// If no such callable exists, return <tt>false</tt>. If the @a event
/// fails to match the @a required prototype specified at add() time, die
/// with LL_ERRS.
bool try_call(const LLSD& event) const;
/// @name Iterate over defined names
//@{
typedef std::pair<std::string, std::string> NameDesc;
private:
struct DispatchEntry
{
DispatchEntry(const std::string& desc);
virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
std::string mDesc;
virtual void call(const std::string& desc, const LLSD& event) const = 0;
virtual LLSD addMetadata(LLSD) const = 0;
};
// Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
// wants its value type to be "clonable," even just to dereference an
// iterator. I don't want to clone entries -- if I have to copy an entry
// around, I want it to continue pointing to the same DispatchEntry
// subclass object. However, I definitely want DispatchMap to destroy
// DispatchEntry if no references are outstanding at the time an entry is
// removed. This looks like a job for boost::shared_ptr.
typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
public:
/// We want the flexibility to redefine what data we store per name,
/// therefore our public interface doesn't expose DispatchMap iterators,
/// or DispatchMap itself, or DispatchEntry. Instead we explicitly
/// transform each DispatchMap item to NameDesc on dereferencing.
typedef boost::transform_iterator<NameDesc(*)(const DispatchMap::value_type&), DispatchMap::const_iterator> const_iterator;
const_iterator begin() const
{
return boost::make_transform_iterator(mDispatch.begin(), makeNameDesc);
}
const_iterator end() const
{
return boost::make_transform_iterator(mDispatch.end(), makeNameDesc);
}
//@}
/// Get information about a specific Callable
LLSD getMetadata(const std::string& name) const;
/// Retrieve the LLSD key we use for one-arg <tt>operator()</tt> method
std::string getDispatchKey() const { return mKey; }
private:
template <class CLASS, typename METHOD>
void addMethod(const std::string& name, const std::string& desc,
const METHOD& method, const LLSD& required)
{
CLASS* downcast = dynamic_cast<CLASS*>(this);
if (! downcast)
{
addFail(name, typeid(CLASS).name());
}
else
{
add(name, desc, boost::bind(method, downcast, _1), required);
}
}
void addFail(const std::string& name, const std::string& classname) const;
std::string mDesc, mKey;
DispatchMap mDispatch;
static NameDesc makeNameDesc(const DispatchMap::value_type& item)
{
return NameDesc(item.first, item.second->mDesc);
}
struct LLSDDispatchEntry;
struct ParamsDispatchEntry;
struct ArrayParamsDispatchEntry;
struct MapParamsDispatchEntry;
// Step 2 of parameter analysis. Instantiating invoker<some_function_type>
// implicitly sets its From and To parameters to the (compile time) begin
// and end iterators over that function's parameter types.
template< typename Function
, class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
, class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
>
struct invoker;
// deliver LLSD arguments one at a time
typedef boost::function<LLSD()> args_source;
// obtain args from an args_source to build param list and call target
// function
typedef boost::function<void(const args_source&)> invoker_function;
template <typename Function>
invoker_function make_invoker(Function f);
template <typename Method, typename InstanceGetter>
invoker_function make_invoker(Method f, const InstanceGetter& getter);
void addArrayParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
LLSD::Integer arity);
void addMapParamsDispatchEntry(const std::string& name,
const std::string& desc,
const invoker_function& invoker,
const LLSD& params,
const LLSD& defaults);
};
/*****************************************************************************
* LLEventDispatcher template implementation details
*****************************************************************************/
// Step 3 of parameter analysis, the recursive case.
template<typename Function, class From, class To>
struct LLEventDispatcher::invoker
{
template<typename T>
struct remove_cv_ref
: boost::remove_cv< typename boost::remove_reference<T>::type >
{ };
// apply() accepts an arbitrary boost::fusion sequence as args. It
// examines the next parameter type in the parameter-types sequence
// bounded by From and To, obtains the next LLSD object from the passed
// args_source and constructs an LLSDParam of appropriate type to try
// to convert the value. It then recurs with the next parameter-types
// iterator, passing the args sequence thus far.
template<typename Args>
static inline
void apply(Function func, const args_source& argsrc, Args const & args)
{
typedef typename boost::mpl::deref<From>::type arg_type;
typedef typename boost::mpl::next<From>::type next_iter_type;
typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
invoker<Function, next_iter_type, To>::apply
( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
}
// Special treatment for instance (first) parameter of a non-static member
// function. Accept the instance-getter callable, calling that to produce
// the first args value. Since we know we're at the top of the recursion
// chain, we need not also require a partial args sequence from our caller.
template <typename InstanceGetter>
static inline
void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
{
typedef typename boost::mpl::next<From>::type next_iter_type;
// Instead of grabbing the first item from argsrc and making an
// LLSDParam of it, call getter() and pass that as the instance param.
invoker<Function, next_iter_type, To>::apply
( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
}
};
// Step 4 of parameter analysis, the leaf case. When the general
// invoker<Function, From, To> logic has advanced From until it matches To,
// the compiler will pick this template specialization.
template<typename Function, class To>
struct LLEventDispatcher::invoker<Function,To,To>
{
// the argument list is complete, now call the function
template<typename Args>
static inline
void apply(Function func, const args_source&, Args const & args)
{
boost::fusion::invoke(func, args);
}
};
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
{
// Construct an invoker_function, a callable accepting const args_source&.
// Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
// caller's LLSD::Array.
addArrayParamsDispatchEntry(name, desc, make_invoker(f),
boost::function_types::function_arity<Function>::value);
}
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
const InstanceGetter& getter)
{
// Subtract 1 from the compile-time arity because the getter takes care of
// the first parameter. We only need (arity - 1) additional arguments.
addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
boost::function_types::function_arity<Method>::value - 1);
}
template<typename Function>
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
const LLSD& params, const LLSD& defaults)
{
// See comments for previous is_nonmember_callable_builtin add().
addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
}
template<typename Method, typename InstanceGetter>
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
const InstanceGetter& getter,
const LLSD& params, const LLSD& defaults)
{
addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
}
template <typename Function>
LLEventDispatcher::invoker_function
LLEventDispatcher::make_invoker(Function f)
{
// Step 1 of parameter analysis, the top of the recursion. Passing a
// suitable f (see add()'s enable_if condition) to this method causes it
// to infer the function type; specifying that function type to invoker<>
// causes it to fill in the begin/end MPL iterators over the function's
// list of parameter types.
// While normally invoker::apply() could infer its template type from the
// boost::fusion::nil parameter value, here we must be explicit since
// we're boost::bind()ing it rather than calling it directly.
return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
f,
_1,
boost::fusion::nil());
}
template <typename Method, typename InstanceGetter>
LLEventDispatcher::invoker_function
LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
{
// Use invoker::method_apply() to treat the instance (first) arg specially.
return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
f,
_1,
getter);
}
/*****************************************************************************
* LLDispatchListener
*****************************************************************************/
/**
* Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
* that contains (or derives from) LLDispatchListener need only specify the
* LLEventPump name and dispatch key, and add() its methods. Incoming events
* will automatically be dispatched.
*/
class LL_COMMON_API LLDispatchListener: public LLEventDispatcher
{
public:
LLDispatchListener(const std::string& pumpname, const std::string& key);
std::string getPumpName() const { return mPump.getName(); }
private:
bool process(const LLSD& event);
LLEventStream mPump;
LLTempBoundListener mBoundListener;
};
#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */

View File

@@ -1,166 +0,0 @@
/**
* @file lleventfilter.cpp
* @author Nat Goodspeed
* @date 2009-03-05
* @brief Implementation for lleventfilter.
*
* $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 "lleventfilter.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "llerror.h" // LL_ERRS
#include "llsdutil.h" // llsd_matches()
LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
LLEventStream(name, tweak)
{
source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1));
}
LLEventMatching::LLEventMatching(const LLSD& pattern):
LLEventFilter("matching"),
mPattern(pattern)
{
}
LLEventMatching::LLEventMatching(LLEventPump& source, const LLSD& pattern):
LLEventFilter(source, "matching"),
mPattern(pattern)
{
}
bool LLEventMatching::post(const LLSD& event)
{
if (! llsd_matches(mPattern, event).empty())
return false;
return LLEventStream::post(event);
}
LLEventTimeoutBase::LLEventTimeoutBase():
LLEventFilter("timeout")
{
}
LLEventTimeoutBase::LLEventTimeoutBase(LLEventPump& source):
LLEventFilter(source, "timeout")
{
}
void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action)
{
setCountdown(seconds);
mAction = action;
if (! mMainloop.connected())
{
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1));
}
}
class ErrorAfter
{
public:
ErrorAfter(const std::string& message): mMessage(message) {}
void operator()()
{
LL_ERRS("LLEventTimeout") << mMessage << LL_ENDL;
}
private:
std::string mMessage;
};
void LLEventTimeoutBase::errorAfter(F32 seconds, const std::string& message)
{
actionAfter(seconds, ErrorAfter(message));
}
class EventAfter
{
public:
EventAfter(LLEventPump& pump, const LLSD& event):
mPump(pump),
mEvent(event)
{}
void operator()()
{
mPump.post(mEvent);
}
private:
LLEventPump& mPump;
LLSD mEvent;
};
void LLEventTimeoutBase::eventAfter(F32 seconds, const LLSD& event)
{
actionAfter(seconds, EventAfter(*this, event));
}
bool LLEventTimeoutBase::post(const LLSD& event)
{
cancel();
return LLEventStream::post(event);
}
void LLEventTimeoutBase::cancel()
{
mMainloop.disconnect();
}
bool LLEventTimeoutBase::tick(const LLSD&)
{
if (countdownElapsed())
{
cancel();
mAction();
}
return false; // show event to other listeners
}
LLEventTimeout::LLEventTimeout() {}
LLEventTimeout::LLEventTimeout(LLEventPump& source):
LLEventTimeoutBase(source)
{
}
void LLEventTimeout::setCountdown(F32 seconds)
{
mTimer.setTimerExpirySec(seconds);
}
bool LLEventTimeout::countdownElapsed() const
{
return mTimer.hasExpired();
}

View File

@@ -1,203 +0,0 @@
/**
* @file lleventfilter.h
* @author Nat Goodspeed
* @date 2009-03-05
* @brief Define LLEventFilter: LLEventStream subclass with conditions
*
* $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_LLEVENTFILTER_H)
#define LL_LLEVENTFILTER_H
#include "llevents.h"
#include "stdtypes.h"
#include "lltimer.h"
#include <boost/function.hpp>
/**
* Generic base class
*/
class LL_COMMON_API LLEventFilter: public LLEventStream
{
public:
/// construct a standalone LLEventFilter
LLEventFilter(const std::string& name="filter", bool tweak=true):
LLEventStream(name, tweak)
{}
/// construct LLEventFilter and connect it to the specified LLEventPump
LLEventFilter(LLEventPump& source, const std::string& name="filter", bool tweak=true);
/// Post an event to all listeners
virtual bool post(const LLSD& event) = 0;
};
/**
* Pass through only events matching a specified pattern
*/
class LLEventMatching: public LLEventFilter
{
public:
/// Pass an LLSD map with keys and values the incoming event must match
LLEventMatching(const LLSD& pattern);
/// instantiate and connect
LLEventMatching(LLEventPump& source, const LLSD& pattern);
/// Only pass through events matching the pattern
virtual bool post(const LLSD& event);
private:
LLSD mPattern;
};
/**
* Wait for an event to be posted. If no such event arrives within a specified
* time, take a specified action. See LLEventTimeout for production
* implementation.
*
* @NOTE This is an abstract base class so that, for testing, we can use an
* alternate "timer" that doesn't actually consume real time.
*/
class LL_COMMON_API LLEventTimeoutBase: public LLEventFilter
{
public:
/// construct standalone
LLEventTimeoutBase();
/// construct and connect
LLEventTimeoutBase(LLEventPump& source);
/// Callable, can be constructed with boost::bind()
typedef boost::function<void()> Action;
/**
* Start countdown timer for the specified number of @a seconds. Forward
* all events. If any event arrives before timer expires, cancel timer. If
* no event arrives before timer expires, take specified @a action.
*
* This is a one-shot timer. Once it has either expired or been canceled,
* it is inert until another call to actionAfter().
*
* Calling actionAfter() while an existing timer is running cheaply
* replaces that original timer. Thus, a valid use case is to detect
* idleness of some event source by calling actionAfter() on each new
* event. A rapid sequence of events will keep the timer from expiring;
* the first gap in events longer than the specified timer will fire the
* specified Action.
*
* Any post() call cancels the timer. To be satisfied with only a
* particular event, chain on an LLEventMatching that only passes such
* events:
*
* @code
* event ultimate
* source ---> LLEventMatching ---> LLEventTimeout ---> listener
* @endcode
*
* @NOTE
* The implementation relies on frequent events on the LLEventPump named
* "mainloop".
*/
void actionAfter(F32 seconds, const Action& action);
/**
* Like actionAfter(), but where the desired Action is LL_ERRS
* termination. Pass the timeout time and the desired LL_ERRS @a message.
*
* This method is useful when, for instance, some async API guarantees an
* event, whether success or failure, within a stated time window.
* Instantiate an LLEventTimeout listening to that API and call
* errorAfter() on each async request with a timeout comfortably longer
* than the API's time guarantee (much longer than the anticipated
* "mainloop" granularity).
*
* Then if the async API breaks its promise, the program terminates with
* the specified LL_ERRS @a message. The client of the async API can
* therefore assume the guarantee is upheld.
*
* @NOTE
* errorAfter() is implemented in terms of actionAfter(), so all remarks
* about calling actionAfter() also apply to errorAfter().
*/
void errorAfter(F32 seconds, const std::string& message);
/**
* Like actionAfter(), but where the desired Action is a particular event
* for all listeners. Pass the timeout time and the desired @a event data.
*
* Suppose the timeout should only be satisfied by a particular event, but
* the ultimate listener must see all other incoming events as well, plus
* the timeout @a event if any:
*
* @code
* some LLEventMatching LLEventMatching
* event ---> for particular ---> LLEventTimeout ---> for timeout
* source event event \
* \ \ ultimate
* `-----------------------------------------------------> listener
* @endcode
*
* Since a given listener can listen on more than one LLEventPump, we can
* set things up so it sees the set union of events from LLEventTimeout
* and the original event source. However, as LLEventTimeout passes
* through all incoming events, the "particular event" that satisfies the
* left LLEventMatching would reach the ultimate listener twice. So we add
* an LLEventMatching that only passes timeout events.
*
* @NOTE
* eventAfter() is implemented in terms of actionAfter(), so all remarks
* about calling actionAfter() also apply to eventAfter().
*/
void eventAfter(F32 seconds, const LLSD& event);
/// Pass event through, canceling the countdown timer
virtual bool post(const LLSD& event);
/// Cancel timer without event
void cancel();
protected:
virtual void setCountdown(F32 seconds) = 0;
virtual bool countdownElapsed() const = 0;
private:
bool tick(const LLSD&);
LLBoundListener mMainloop;
Action mAction;
};
/// Production implementation of LLEventTimoutBase
class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase
{
public:
LLEventTimeout();
LLEventTimeout(LLEventPump& source);
protected:
virtual void setCountdown(F32 seconds);
virtual bool countdownElapsed() const;
private:
LLTimer mTimer;
};
#endif /* ! defined(LL_LLEVENTFILTER_H) */

View File

@@ -1,628 +0,0 @@
/**
* @file llevents.cpp
* @author Nat Goodspeed
* @date 2008-09-12
* @brief Implementation for llevents.
*
* $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$
*/
// Precompiled header
#include "linden_common.h"
#if LL_WINDOWS
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
#endif
// associated header
#include "llevents.h"
// STL headers
#include <set>
#include <sstream>
#include <algorithm>
// std headers
#include <typeinfo>
#include <cassert>
#include <cmath>
#include <cctype>
// external library headers
#include <boost/range/iterator_range.hpp>
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
#endif
#include <boost/lexical_cast.hpp>
#if LL_WINDOWS
#pragma warning (pop)
#endif
// other Linden headers
#include "stringize.h"
#include "llerror.h"
#include "llsdutil.h"
#if LL_MSVC
#pragma warning (disable : 4702)
#endif
/*****************************************************************************
* queue_names: specify LLEventPump names that should be instantiated as
* LLEventQueue
*****************************************************************************/
/**
* At present, we recognize particular requested LLEventPump names as needing
* LLEventQueues. Later on we'll migrate this information to an external
* configuration file.
*/
const char* queue_names[] =
{
"placeholder - replace with first real name string"
};
/*****************************************************************************
* If there's a "mainloop" pump, listen on that to flush all LLEventQueues
*****************************************************************************/
struct RegisterFlush : public LLEventTrackable
{
RegisterFlush():
pumps(LLEventPumps::instance())
{
pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
}
bool flush(const LLSD&)
{
pumps.flush();
return false;
}
~RegisterFlush()
{
// LLEventTrackable handles stopListening for us.
}
LLEventPumps& pumps;
};
static RegisterFlush registerFlush;
/*****************************************************************************
* LLEventPumps
*****************************************************************************/
LLEventPumps::LLEventPumps():
// Until we migrate this information to an external config file,
// initialize mQueueNames from the static queue_names array.
mQueueNames(boost::begin(queue_names), boost::end(queue_names))
{
}
LLEventPump& LLEventPumps::obtain(const std::string& name)
{
PumpMap::iterator found = mPumpMap.find(name);
if (found != mPumpMap.end())
{
// Here we already have an LLEventPump instance with the requested
// name.
return *found->second;
}
// Here we must instantiate an LLEventPump subclass.
LLEventPump* newInstance;
// Should this name be an LLEventQueue?
PumpNames::const_iterator nfound = mQueueNames.find(name);
if (nfound != mQueueNames.end())
newInstance = new LLEventQueue(name);
else
newInstance = new LLEventStream(name);
// LLEventPump's constructor implicitly registers each new instance in
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
// delete it later.
mOurPumps.insert(newInstance);
return *newInstance;
}
void LLEventPumps::flush()
{
// Flush every known LLEventPump instance. Leave it up to each instance to
// decide what to do with the flush() call.
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
{
pmi->second->flush();
}
}
void LLEventPumps::reset()
{
// Reset every known LLEventPump instance. Leave it up to each instance to
// decide what to do with the reset() call.
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
{
pmi->second->reset();
}
}
std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
{
std::pair<PumpMap::iterator, bool> inserted =
mPumpMap.insert(PumpMap::value_type(name, const_cast<LLEventPump*>(&pump)));
// If the insert worked, then the name is unique; return that.
if (inserted.second)
return name;
// Here the new entry was NOT inserted, and therefore name isn't unique.
// Unless we're permitted to tweak it, that's Bad.
if (! tweak)
{
throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
}
// The passed name isn't unique, but we're permitted to tweak it. Find the
// first decimal-integer suffix not already taken. The insert() attempt
// above will have set inserted.first to the iterator of the existing
// entry by that name. Starting there, walk forward until we reach an
// entry that doesn't start with 'name'. For each entry consisting of name
// + integer suffix, capture the integer suffix in a set. Use a set
// because we're going to encounter string suffixes in the order: name1,
// name10, name11, name2, ... Walking those possibilities in that order
// isn't convenient to detect the first available "hole."
std::set<int> suffixes;
PumpMap::iterator pmi(inserted.first), pmend(mPumpMap.end());
// We already know inserted.first references the existing entry with
// 'name' as the key; skip that one and start with the next.
while (++pmi != pmend)
{
if (pmi->first.substr(0, name.length()) != name)
{
// Found the first entry beyond the entries starting with 'name':
// stop looping.
break;
}
// Here we're looking at an entry that starts with 'name'. Is the rest
// of it an integer?
// Dubious (?) assumption: in the local character set, decimal digits
// are in increasing order such that '9' is the last of them. This
// test deals with 'name' values such as 'a', where there might be a
// very large number of entries starting with 'a' whose suffixes
// aren't integers. A secondary assumption is that digit characters
// precede most common name characters (true in ASCII, false in
// EBCDIC). The test below is correct either way, but it's worth more
// if the assumption holds.
if (pmi->first[name.length()] > '9')
break;
// It should be cheaper to detect that we're not looking at a digit
// character -- and therefore the suffix can't possibly be an integer
// -- than to attempt the lexical_cast and catch the exception.
if (! std::isdigit(pmi->first[name.length()]))
continue;
// Okay, the first character of the suffix is a digit, it's worth at
// least attempting to convert to int.
try
{
suffixes.insert(boost::lexical_cast<int>(pmi->first.substr(name.length())));
}
catch (const boost::bad_lexical_cast&)
{
// If the rest of pmi->first isn't an int, just ignore it.
}
}
// Here we've accumulated in 'suffixes' all existing int suffixes of the
// entries starting with 'name'. Find the first unused one.
int suffix = 1;
for ( ; suffixes.find(suffix) != suffixes.end(); ++suffix)
;
// Here 'suffix' is not in 'suffixes'. Construct a new name based on that
// suffix, insert it and return it.
std::ostringstream out;
out << name << suffix;
return registerNew(pump, out.str(), tweak);
}
void LLEventPumps::unregister(const LLEventPump& pump)
{
// Remove this instance from mPumpMap
PumpMap::iterator found = mPumpMap.find(pump.getName());
if (found != mPumpMap.end())
{
mPumpMap.erase(found);
}
// If this instance is one we created, also remove it from mOurPumps so we
// won't try again to delete it later!
PumpSet::iterator psfound = mOurPumps.find(const_cast<LLEventPump*>(&pump));
if (psfound != mOurPumps.end())
{
mOurPumps.erase(psfound);
}
}
//static
bool LLEventPumps::sDeleted;
//static
void LLEventPumps::maybe_unregister(const LLEventPump& pump)
{
if (!sDeleted)
{
LLEventPumps::instance().unregister(pump);
}
}
LLEventPumps::~LLEventPumps()
{
// Deleting an LLEventPump calls its destructor, which calls maybe_unregister(),
// which would try to remove that LLEventPump instance from a NEWLY created LLEventPumps
// singleton (as we're already being destructed). Therefore, mark that we're not
// home anymore... --Aleric
sDeleted = true;
// Subsequently we can delete every LLEventPump we instantiated (via obtain()).
// We're not clearing mPumpMap or mOurPumps here... their destructors will.
for (LLEventPumps::PumpSet::iterator pump = mOurPumps.begin(); pump != mOurPumps.end(); ++pump)
{
delete *pump;
}
}
/*****************************************************************************
* LLEventPump
*****************************************************************************/
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
LLEventPump::LLEventPump(const std::string& name, bool tweak):
// Register every new instance with LLEventPumps
mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
mSignal(new LLStandardSignal()),
mEnabled(true)
{}
#if LL_WINDOWS
#pragma warning (pop)
#endif
LLEventPump::~LLEventPump()
{
// Unregister this doomed instance from LLEventPumps
LLEventPumps::maybe_unregister(*this);
}
// static data member
const LLEventPump::NameList LLEventPump::empty;
std::string LLEventPump::inventName(const std::string& pfx)
{
static long suffix = 0;
return STRINGIZE(pfx << suffix++);
}
void LLEventPump::reset()
{
mSignal.reset();
mConnections.clear();
//mDeps.clear();
}
LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
const NameList& after,
const NameList& before)
{
// Check for duplicate name before connecting listener to mSignal
ConnectionMap::const_iterator found = mConnections.find(name);
// In some cases the user might disconnect a connection explicitly -- or
// might use LLEventTrackable to disconnect implicitly. Either way, we can
// end up retaining in mConnections a zombie connection object that's
// already been disconnected. Such a connection object can't be
// reconnected -- nor, in the case of LLEventTrackable, would we want to
// try, since disconnection happens with the destruction of the listener
// object. That means it's safe to overwrite a disconnected connection
// object with the new one we're attempting. The case we want to prevent
// is only when the existing connection object is still connected.
if (found != mConnections.end() && found->second.connected())
{
throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
"' on " + typeid(*this).name() + " '" + getName() + "'");
}
// Okay, name is unique, try to reconcile its dependencies. Specify a new
// "node" value that we never use for an mSignal placement; we'll fix it
// later.
DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
// What if this listener has been added, removed and re-added? In that
// case newNode already has a non-negative value because we never remove a
// listener from mDeps. But keep processing uniformly anyway in case the
// listener was added back with different dependencies. Then mDeps.sort()
// would put it in a different position, and the old newNode placement
// value would be wrong, so we'd have to reassign it anyway. Trust that
// re-adding a listener with the same dependencies is the trivial case for
// mDeps.sort(): it can just replay its cache.
DependencyMap::sorted_range sorted_range;
try
{
// Can we pick an order that works including this new entry?
sorted_range = mDeps.sort();
}
catch (const DependencyMap::Cycle& e)
{
// No: the new node's after/before dependencies have made mDeps
// unsortable. If we leave the new node in mDeps, it will continue
// to screw up all future attempts to sort()! Pull it out.
mDeps.remove(name);
throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
" '" + getName() + "' would cause cycle: " + e.what());
}
// Walk the list to verify that we haven't changed the order.
float previous = 0.0, myprev = 0.0;
DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
dmi != sorted_range.end(); ++dmi)
{
// Since we've added the new entry with an invalid placement,
// recognize it and skip it.
if (dmi->first == name)
{
// Remember the iterator belonging to our new node, and which
// placement value was 'previous' at that point.
mydmi = dmi;
myprev = previous;
continue;
}
// If the new node has rearranged the existing nodes, we'll find
// that their placement values are no longer in increasing order.
if (dmi->second < previous)
{
// This is another scenario in which we'd better back out the
// newly-added node from mDeps -- but don't do it yet, we want to
// traverse the existing mDeps to report on it!
// Describe the change to the order of our listeners. Copy
// everything but the newest listener to a vector we can sort to
// obtain the old order.
typedef std::vector< std::pair<float, std::string> > SortNameList;
SortNameList sortnames;
for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
cdmi != cdmend; ++cdmi)
{
if (cdmi->first != name)
{
sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
}
}
std::sort(sortnames.begin(), sortnames.end());
std::ostringstream out;
out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
<< "' would move previous listener '" << dmi->first << "'\nwas: ";
SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
if (sni != snend)
{
out << sni->second;
while (++sni != snend)
{
out << ", " << sni->second;
}
}
out << "\nnow: ";
DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
if (ddmi != ddmend)
{
out << ddmi->first;
while (++ddmi != ddmend)
{
out << ", " << ddmi->first;
}
}
// NOW remove the offending listener node.
mDeps.remove(name);
// Having constructed a description of the order change, inform caller.
throw OrderChange(out.str());
}
// This node becomes the previous one.
previous = dmi->second;
}
// We just got done with a successful mDeps.add(name, ...) call. We'd
// better have found 'name' somewhere in that sorted list!
assert(mydmi != sorted_range.end());
// Four cases:
// 0. name is the only entry: placement 1.0
// 1. name is the first of several entries: placement (next placement)/2
// 2. name is between two other entries: placement (myprev + (next placement))/2
// 3. name is the last entry: placement ceil(myprev) + 1.0
// Since we've cleverly arranged for myprev to be 0.0 if name is the
// first entry, this folds down to two cases. Case 1 is subsumed by
// case 2, and case 0 is subsumed by case 3. So we need only handle
// cases 2 and 3, which means we need only detect whether name is the
// last entry. Increment mydmi to see if there's anything beyond.
if (++mydmi != sorted_range.end())
{
// The new node isn't last. Place it between the previous node and
// the successor.
newNode = (myprev + mydmi->second)/2.0;
}
else
{
// The new node is last. Bump myprev up to the next integer, add
// 1.0 and use that.
newNode = std::ceil(myprev) + 1.0;
}
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
LLBoundListener bound = mSignal->connect(newNode, listener);
mConnections[name] = bound;
return bound;
}
LLBoundListener LLEventPump::getListener(const std::string& name) const
{
ConnectionMap::const_iterator found = mConnections.find(name);
if (found != mConnections.end())
{
return found->second;
}
// not found, return dummy LLBoundListener
return LLBoundListener();
}
void LLEventPump::stopListening(const std::string& name)
{
ConnectionMap::iterator found = mConnections.find(name);
if (found != mConnections.end())
{
found->second.disconnect();
mConnections.erase(found);
}
// We intentionally do NOT remove this name from mDeps. It may happen that
// the same listener with the same name and dependencies will jump on and
// off this LLEventPump repeatedly. Keeping a cache of dependencies will
// avoid a new dependency sort in such cases.
}
/*****************************************************************************
* LLEventStream
*****************************************************************************/
bool LLEventStream::post(const LLSD& event)
{
if (! mEnabled || !mSignal)
{
return false;
}
// NOTE NOTE NOTE: Any new access to member data beyond this point should
// cause us to move our LLStandardSignal object to a pimpl class along
// with said member data. Then the local shared_ptr will preserve both.
// DEV-43463: capture a local copy of mSignal. We've turned up a
// cross-coroutine scenario (described in the Jira) in which this post()
// call could end up destroying 'this', the LLEventPump subclass instance
// containing mSignal, during the call through *mSignal. So -- capture a
// *stack* instance of the shared_ptr, ensuring that our heap
// LLStandardSignal object will live at least until post() returns, even
// if 'this' gets destroyed during the call.
boost::shared_ptr<LLStandardSignal> signal(mSignal);
// Let caller know if any one listener handled the event. This is mostly
// useful when using LLEventStream as a listener for an upstream
// LLEventPump.
return (*signal)(event);
}
/*****************************************************************************
* LLEventQueue
*****************************************************************************/
bool LLEventQueue::post(const LLSD& event)
{
if (mEnabled)
{
// Defer sending this event by queueing it until flush()
mEventQueue.push_back(event);
}
// Unconditionally return false. We won't know until flush() whether a
// listener claims to have handled the event -- meanwhile, don't block
// other listeners.
return false;
}
void LLEventQueue::flush()
{
if(!mSignal) return;
// Consider the case when a given listener on this LLEventQueue posts yet
// another event on the same queue. If we loop over mEventQueue directly,
// we'll end up processing all those events during the same flush() call
// -- rather like an EventStream. Instead, copy mEventQueue and clear it,
// so that any new events posted to this LLEventQueue during flush() will
// be processed in the *next* flush() call.
EventQueue queue(mEventQueue);
mEventQueue.clear();
// NOTE NOTE NOTE: Any new access to member data beyond this point should
// cause us to move our LLStandardSignal object to a pimpl class along
// with said member data. Then the local shared_ptr will preserve both.
// DEV-43463: capture a local copy of mSignal. See LLEventStream::post()
// for detailed comments.
boost::shared_ptr<LLStandardSignal> signal(mSignal);
for ( ; ! queue.empty(); queue.pop_front())
{
(*signal)(queue.front());
}
}
/*****************************************************************************
* LLListenerOrPumpName
*****************************************************************************/
LLListenerOrPumpName::LLListenerOrPumpName(const std::string& pumpname):
// Look up the specified pumpname, and bind its post() method as our listener
mListener(boost::bind(&LLEventPump::post,
boost::ref(LLEventPumps::instance().obtain(pumpname)),
_1))
{
}
LLListenerOrPumpName::LLListenerOrPumpName(const char* pumpname):
// Look up the specified pumpname, and bind its post() method as our listener
mListener(boost::bind(&LLEventPump::post,
boost::ref(LLEventPumps::instance().obtain(pumpname)),
_1))
{
}
bool LLListenerOrPumpName::operator()(const LLSD& event) const
{
if (! mListener)
{
throw Empty("attempting to call uninitialized");
}
return (*mListener)(event);
}
void LLReqID::stamp(LLSD& response) const
{
if (! (response.isUndefined() || response.isMap()))
{
// If 'response' was previously completely empty, it's okay to
// turn it into a map. If it was already a map, then it should be
// okay to add a key. But if it was anything else (e.g. a scalar),
// assigning a ["reqid"] key will DISCARD the previous value,
// replacing it with a map. That would be Bad.
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
<< response << LL_ENDL;
return;
}
LLSD oldReqid(response["reqid"]);
if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
{
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value "
<< oldReqid << " in response: " << response << LL_ENDL;
return;
}
response["reqid"] = mReqid;
}
bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
{
// If the original request has no value for replyKey, it's pointless to
// construct or send a reply event: on which LLEventPump should we send
// it? Allow that to be optional: if the caller wants to require replyKey,
// it can so specify when registering the operation method.
if (! request.has(replyKey))
{
return false;
}
// Here the request definitely contains replyKey; reasonable to proceed.
// Copy 'reply' to modify it.
LLSD newreply(reply);
// Get the ["reqid"] element from request
LLReqID reqID(request);
// and copy it to 'newreply'.
reqID.stamp(newreply);
// Send reply on LLEventPump named in request[replyKey]. Don't forget to
// send the modified 'newreply' instead of the original 'reply'.
return LLEventPumps::instance().obtain(request[replyKey]).post(newreply);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,135 @@
/**
* @file llfasttimer.cpp
* @brief Implementation of the fast timer.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llfasttimer.h"
#include "llmemory.h"
#include "llprocessor.h"
#if LL_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "lltimer.h"
//////////////////////////////////////////////////////////////////////////////
// statics
LLFastTimer::EFastTimerType LLFastTimer::sCurType = LLFastTimer::FTM_OTHER;
int LLFastTimer::sCurDepth = 0;
U64 LLFastTimer::sStart[LLFastTimer::FTM_MAX_DEPTH];
U64 LLFastTimer::sCounter[LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCountHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCountAverage[LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCalls[LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCallHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
U64 LLFastTimer::sCallAverage[LLFastTimer::FTM_NUM_TYPES];
S32 LLFastTimer::sCurFrameIndex = -1;
S32 LLFastTimer::sLastFrameIndex = -1;
int LLFastTimer::sPauseHistory = 0;
int LLFastTimer::sResetHistory = 0;
U64 LLFastTimer::sClockResolution = calc_clock_frequency(50U); // Resolution of get_clock_count()
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
{
return sClockResolution >> 8;
}
void LLFastTimer::reset()
{
countsPerSecond(); // good place to calculate clock frequency
if (sCurDepth != 0)
{
llerrs << "LLFastTimer::Reset() when sCurDepth != 0" << llendl;
}
if (sPauseHistory)
{
sResetHistory = 1;
}
else if (sResetHistory)
{
sCurFrameIndex = -1;
sResetHistory = 0;
}
else if (sCurFrameIndex >= 0)
{
int hidx = sCurFrameIndex % FTM_HISTORY_NUM;
for (S32 i=0; i<FTM_NUM_TYPES; i++)
{
sCountHistory[hidx][i] = sCounter[i];
sCountAverage[i] = (sCountAverage[i]*sCurFrameIndex + sCounter[i]) / (sCurFrameIndex+1);
sCallHistory[hidx][i] = sCalls[i];
sCallAverage[i] = (sCallAverage[i]*sCurFrameIndex + sCalls[i]) / (sCurFrameIndex+1);
}
sLastFrameIndex = sCurFrameIndex;
}
else
{
for (S32 i=0; i<FTM_NUM_TYPES; i++)
{
sCountAverage[i] = 0;
sCallAverage[i] = 0;
}
}
sCurFrameIndex++;
for (S32 i=0; i<FTM_NUM_TYPES; i++)
{
sCounter[i] = 0;
sCalls[i] = 0;
}
sCurDepth = 0;
}
//////////////////////////////////////////////////////////////////////////////
//
// Important note: These implementations must be FAST!
//
// shift off lower 8 bits for lower resolution but longer term timing
// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
// On windows these use QueryPerformanceCounter, which is arguably fine and also works on amd architectures.
U32 LLFastTimer::getCPUClockCount32()
{
return get_clock_count() >> 8;
}
U64 LLFastTimer::getCPUClockCount64()
{
return get_clock_count();
}

View File

@@ -1,35 +1,306 @@
/** /**
* @file llfasttimer.h * @file llfasttimer.h
* @brief Inline implementations of fast timers. * @brief Declaration of a fast timer.
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
* *
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code * Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc. * The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* *
* This library is free software; you can redistribute it and/or * There are special exceptions to the terms and conditions of the GPL as
* modify it under the terms of the GNU Lesser General Public * it is applied to this Source Code. View the full text of the exception
* License as published by the Free Software Foundation; * in the file doc/FLOSS-exception.txt in this software distribution, or
* version 2.1 of the License only. * online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* *
* This library is distributed in the hope that it will be useful, * By copying, modifying or distributing this software, you acknowledge
* but WITHOUT ANY WARRANTY; without even the implied warranty of * that you have read and understood your obligations described above,
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * and agree to abide by those obligations.
* Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* License along with this library; if not, write to the Free Software * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * COMPLETENESS OR PERFORMANCE.
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$ * $/LicenseInfo$
*/ */
#ifndef LL_FASTTIMER_H #ifndef LL_LLFASTTIMER_H
#define LL_FASTTIMER_H #define LL_LLFASTTIMER_H
// Implementation of getCPUClockCount32() and getCPUClockCount64 are now in llfastertimer_class.cpp. #define FAST_TIMER_ON 1
class LL_COMMON_API LLFastTimer
{
public:
enum EFastTimerType
{
// high level
FTM_FRAME,
FTM_UPDATE,
FTM_RENDER,
FTM_SWAP,
FTM_CLIENT_COPY,
FTM_IDLE,
FTM_SLEEP,
// general timers
FT_STRING_FORMAT,
// common messaging components
FTM_PUMP,
FTM_CURL,
FTM_PUMPIO,
// common simulation components
FTM_UPDATE_ANIMATION,
FTM_UPDATE_TERRAIN,
FTM_UPDATE_PRIMITIVES,
FTM_UPDATE_PARTICLES,
FTM_SIMULATE_PARTICLES,
FTM_UPDATE_SKY,
FTM_UPDATE_TEXTURES,
FTM_UPDATE_WLPARAM,
FTM_UPDATE_WATER,
FTM_UPDATE_CLOUDS,
FTM_UPDATE_GRASS,
FTM_UPDATE_TREE,
FTM_UPDATE_AVATAR,
FTM_UPDATE_RIGGED_VOLUME,
FTM_SKIN_RIGGED,
FTM_RIGGED_OCTREE,
// common render components
FTM_SHADOW_GEOMETRY,
FTM_SHADOW_RENDER,
FTM_SHADOW_TERRAIN,
FTM_SHADOW_AVATAR,
FTM_SHADOW_SIMPLE,
FTM_SHADOW_ALPHA,
FTM_SHADOW_TREE,
FTM_RENDER_GEOMETRY,
FTM_RENDER_TERRAIN,
FTM_RENDER_SIMPLE,
FTM_RENDER_FULLBRIGHT,
FTM_RENDER_GLOW,
FTM_RENDER_GRASS,
FTM_RENDER_INVISIBLE,
FTM_RENDER_SHINY,
FTM_RENDER_BUMP,
FTM_RENDER_TREES,
FTM_RENDER_CHARACTERS,
FTM_RENDER_OCCLUSION,
FTM_RENDER_ALPHA,
FTM_RENDER_CLOUDS,
FTM_RENDER_HUD,
FTM_RENDER_PARTICLES,
FTM_RENDER_WATER,
FTM_RENDER_WL_SKY,
FTM_RENDER_FAKE_VBO_UPDATE,
FTM_RENDER_TIMER,
FTM_RENDER_UI,
FTM_RENDER_BLOOM,
FTM_RENDER_BLOOM_FBO,
FTM_RENDER_FONTS,
// deferred rendering
FTM_RENDER_DEFERRED,
FTM_BIND_DEFERRED,
FTM_SUN_SHADOW,
FTM_SOFTEN_SHADOW,
FTM_EDGE_DETECTION,
FTM_GI_TRACE,
FTM_GI_GATHER,
FTM_ATMOSPHERICS,
FTM_LOCAL_LIGHTS,
FTM_FULLSCREEN_LIGHTS,
FTM_PROJECTORS,
FTM_POST,
FTM_VISIBLE_CLOUD,
// newview specific
FTM_MESSAGES,
FTM_MOUSEHANDLER,
FTM_KEYHANDLER,
FTM_REBUILD,
FTM_STATESORT,
FTM_STATESORT_DRAWABLE,
FTM_STATESORT_POSTSORT,
FTM_MESH_UPDATE,
FTM_MESH_LOCK1,
FTM_MESH_LOCK2,
FTM_LOAD_MESH_LOD,
FTM_REBUILD_VBO,
FTM_REBUILD_VOLUME_VB,
FTM_FACE_GET_GEOM,
FTM_FACE_GEOM_POSITION,
FTM_FACE_GEOM_NORMAL,
FTM_FACE_GEOM_TEXTURE,
FTM_FACE_GEOM_COLOR,
FTM_FACE_GEOM_EMISSIVE,
FTM_FACE_GEOM_WEIGHTS,
FTM_FACE_GEOM_BINORMAL,
FTM_FACE_GEOM_INDEX,
FTM_REBUILD_BRIDGE_VB,
FTM_REBUILD_HUD_VB,
FTM_REBUILD_TERRAIN_VB,
FTM_REBUILD_WATER_VB,
FTM_REBUILD_TREE_VB,
FTM_REBUILD_PARTICLE_VB,
FTM_REBUILD_CLOUD_VB,
FTM_REBUILD_GRASS_VB,
FTM_REBUILD_NONE_VB,
FTM_REBUILD_OCCLUSION_VB,
FTM_POOLS,
FTM_POOLRENDER,
FTM_IDLE_CB,
FTM_WORLD_UPDATE,
FTM_UPDATE_MOVE,
FTM_OCTREE_BALANCE,
FTM_UPDATE_LIGHTS,
FTM_CULL,
FTM_CULL_REBOUND,
FTM_FRUSTUM_CULL,
FTM_GEO_UPDATE,
FTM_GEO_RESERVE,
FTM_GEO_LIGHT,
FTM_GEO_SHADOW,
FTM_GEO_SKY,
FTM_GEN_VOLUME,
FTM_GEN_TRIANGLES,
FTM_GEN_FLEX,
FTM_AUDIO_UPDATE,
FTM_RESET_DRAWORDER,
FTM_OBJECTLIST_UPDATE,
FTM_AVATAR_UPDATE,
FTM_JOINT_UPDATE,
FTM_ATTACHMENT_UPDATE,
FTM_LOD_UPDATE,
FTM_REGION_UPDATE,
FTM_CLEANUP,
FTM_NETWORK,
FTM_IDLE_NETWORK,
FTM_CREATE_OBJECT,
FTM_LOAD_AVATAR,
FTM_PROCESS_MESSAGES,
FTM_PROCESS_OBJECTS,
FTM_PROCESS_IMAGES,
FTM_IMAGE_UPDATE,
FTM_IMAGE_CREATE,
FTM_IMAGE_DECODE,
FTM_IMAGE_READBACK,
FTM_IMAGE_MARK_DIRTY,
FTM_PIPELINE,
FTM_VFILE_WAIT,
FTM_FLEXIBLE_UPDATE,
FTM_OCCLUSION_READBACK,
FTM_HUD_EFFECTS,
FTM_HUD_UPDATE,
FTM_INVENTORY,
FTM_AUTO_SELECT,
FTM_ARRANGE,
FTM_FILTER,
FTM_REFRESH,
FTM_SORT,
FTM_PICK,
FTM_STATEMACHINE,
// Temp
FTM_TEMP1,
FTM_TEMP2,
FTM_TEMP3,
FTM_TEMP4,
FTM_TEMP5,
FTM_TEMP6,
FTM_TEMP7,
FTM_TEMP8,
FTM_TEMP9,
FTM_TEMP10,
FTM_TEMP11,
FTM_TEMP12,
FTM_TEMP13,
FTM_TEMP14,
FTM_TEMP15,
FTM_OTHER, // Special, used by display code
FTM_NUM_TYPES
};
enum { FTM_HISTORY_NUM = 60 };
enum { FTM_MAX_DEPTH = 64 };
public:
static EFastTimerType sCurType;
LLFastTimer(EFastTimerType type)
{
#if FAST_TIMER_ON
mType = type;
sCurType = type;
// These don't get counted, because they use CPU clockticks
//gTimerBins[gCurTimerBin]++;
//LLTimer::sNumTimerCalls++;
U64 cpu_clocks = getCPUClockCount32();
sStart[sCurDepth] = cpu_clocks;
sCurDepth++;
#endif
};
~LLFastTimer()
{
#if FAST_TIMER_ON
U64 end,delta;
int i;
// These don't get counted, because they use CPU clockticks
//gTimerBins[gCurTimerBin]++;
//LLTimer::sNumTimerCalls++;
end = getCPUClockCount32();
sCurDepth--;
delta = end - sStart[sCurDepth];
sCounter[mType] += delta;
sCalls[mType]++;
// Subtract delta from parents
for (i=0; i<sCurDepth; i++)
sStart[i] += delta;
#endif
}
static void reset();
static U64 countsPerSecond();
public:
static int sCurDepth;
static U64 sStart[FTM_MAX_DEPTH];
static U64 sCounter[FTM_NUM_TYPES];
static U64 sCalls[FTM_NUM_TYPES];
static U64 sCountAverage[FTM_NUM_TYPES];
static U64 sCallAverage[FTM_NUM_TYPES];
static U64 sCountHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
static U64 sCallHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
static int sPauseHistory;
static int sResetHistory;
static U32 getCPUClockCount32();
static U64 getCPUClockCount64();
static U64 sClockResolution;
static S32 sCurFrameIndex;
static S32 sLastFrameIndex;
EFastTimerType mType;
};
// pull in the actual class definition
#include "llfasttimer_class.h"
#endif // LL_LLFASTTIMER_H #endif // LL_LLFASTTIMER_H

View File

@@ -1,951 +0,0 @@
/**
* @file llfasttimer_class.cpp
* @brief Implementation of the fast timer.
*
* $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$
*/
//
// LLFastTimer documentation, written by Aleric (Feb 2012).
//
// Disclaimer: this is horrible code and I distantiate myself from its design.
// It's neither robust nor object oriented. I just document what I find, in
// order to be able to fix the bugs (that logically result from such a design).
//
// Note that the choosen names of the variables are non-intuitive and make
// understanding the code harder. However, I didn't change them in order to
// make merging less of a nightmare in the future -- Aleric.
//
//
// First of all, absolutely nothing in this code is even remotely thread-safe:
// FastTimers should only be used from the main thread and never from another
// thread.
//
// NamedTimerFactory is a singleton, accessed through NamedTimerFactory::instance().
//
// It has four pointer members which are initialized once to point to
// four objects with a life-time equal to the application/singleton:
//
// mTimerRoot --> NamedTimer("root")
// mActiveTimerRoot --> NamedTimer("Frame")
// mRootFrameState --> FrameState(mActiveTimerRoot)
// mAppTimer --> LLFastTimer(mRootFrameState)
//
// A NamedTimer has a name and a life-time of approximately that of the application.
// There is exactly one instance per unique name.
// NamedTimer's are ordered in a hierarchy with each one parent and zero or more
// children (the "root" has parent NULL).
// The parent of mActiveTimerRoot is mTimerRoot, which has one child: mActiveTimerRoot.
// NamedTimer::getDepth() returns the number of parents; mTimerRoot has a depth of 0,
// mActiveTimerRoot has a depth of 1 and so on. NamedTimer::getRootNamedTimer() just
// returns mActiveTimerRoot.
//
// Each NamedTimer is linked to exactly one FrameState object, namely
// LLFastTimer::getFrameStateList()[named_timer.getFrameStateIndex()], where
// getFrameStateList() is a static function returning a global std::vector<FrameState>.
// This vector is ordered "Depth First" (the FrameState objects (belonging to
// NamedTimer objects) with smallest depth first). The vector is re-sorted a few
// times in the beginning (and indexes in FrameState updated) since timers are added
// whenever they are first used, not in "Depth First" order, but stabilizes after a
// while. This implies that FrameState pointers can't really be used: FrameState
// objects move around in memory whenever something is inserted or removed from the
// std::vector and/or when the vector is re-sorted. However, FrameState pointers ARE
// being used and code exists that tries to update those pointers in the above
// mentioned cases (this part had bugs, which I now fixed).
//
// FrameState objects point back to their corresponding NamedTimer through mTimer.
// They have also parents: the FrameState object corresponding to the parent of mTimer.
//
// Thus, so far we have (assuming "namedtimerX" was created first):
//
// NamedTimer's: FrameState's:
//
// NULL
// ^
// |
// depth=0: "root" (mTimerRoot) <-------> getFrameStateList()[0]
// ^ ^
// | (parent) | (parent)
// | |
// depth=1: "Frame" (mActiveTimerRoot) <-------> mRootFrameState
// ^ ^ ^ ^
// | | | |
// | (parent) | (parent) | (parent) | (parent)
// | | | |
// depth=2: "namedtimerX" | <-------> getFrameStateList()[2] |
// "namedtimerY" <-------> getFrameStateList()[3]
//
// where the NamedTimer's point to the corresponding FrameState's by means of
// NamedTimer::mFrameStateIndex, and the FrameState's point back through FrameState::mTimer.
//
// Note the missing getFrameStateList()[1], which is ignored and replaced by
// a specific call to 'new FrameState' in initSingleton(). The reason for that is
// probably because otherwise mRootFrameState has to be updated every time the
// frame state list vector is moved in memory. This special case adds some complexity to,
// for instance, getFrameState() which now needs to test if the caller is mActiveTimerRoot.
//
// DeclareTimer objects are NameTimer/FrameState pointer pairs with again a lifetime
// of approximately that of the application. The are usually static, even global,
// and are passed an name as string; the name is looked up and added if not already
// existing, or else the previously created pair is returned. Obviously, "root" and
// "Frame" are the only ones that don't have a corresponding DeclareTimer object.
//
// LLFastTimer objects are short lived objects, created in a scope and destroyed
// at the end in order to measure the time that the application spent in that
// scope. They are passed DeclareTimer objects to know which timer to append to.
// LLFastTimer::mFrameState is a pointer to the corresponding timer.
// The static LLFastTimer::sCurTimerData is a CurTimerData struct that has
// a duplicate of that pointer as well as a pointer to the corresponding NamedTimer,
// of the last LLFastTimer object that was created (and not destroyed again);
// in other words: the running timer with the largest depth.
// When a new LLFastTimer object is created while one is already running,
// then this sCurTimerData is saved in the already running one (as
// LLFastTimer::mLastTimerData) and restored upon destruction of that child timer.
//
// The following FrameState pointers are being used:
//
// FrameState::mParent
// DeclareTimer::mFrameState
// CurTimerData::mFrameState
// LLFastTimer::mFrameState
//
// All of those can be invalidated whenever something is added to the std::vector<FrameState>,
// and when that vector is sorted.
//
// Adding new FrameState objects is done in NamedTimer(std::string const& name), called from
// createNamedTimer(), called whenever a DeclareTimer is constructed. At the end of the
// DeclareTimer constructor update_cached_pointers_if_changed() is called, which calls
// updateCachedPointers() if the std::vector moved in memory since last time it was called.
//
// Sorting is done in NamedTimer::resetFrame(), which theoretically can be called from
// anywhere. Also here updateCachedPointers() is called, directly after sorting the vector.
//
// I fixed updateCachedPointers() to correct all of the above pointers and removed
// another FrameState pointer that was unnecessary.
#include "linden_common.h"
#include "llfasttimer.h"
#include "llmemory.h"
#include "llprocessor.h"
#include "llsingleton.h"
#include "lltreeiterators.h"
#include "llsdserialize.h"
#include <boost/bind.hpp>
#if LL_WINDOWS
#include "lltimer.h"
#elif LL_LINUX || LL_SOLARIS
#include <sys/time.h>
#include <sched.h>
#include "lltimer.h"
#elif LL_DARWIN
#include <sys/time.h>
#include "lltimer.h" // get_clock_count()
#else
#error "architecture not supported"
#endif
//////////////////////////////////////////////////////////////////////////////
// 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;
std::vector<LLFastTimer::FrameState>* LLFastTimer::sTimerInfos = NULL;
U64 LLFastTimer::sTimerCycles = 0;
U32 LLFastTimer::sTimerCalls = 0;
// 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;
static timer_tree_bottom_up_iterator_t begin_timer_tree_bottom_up(LLFastTimer::NamedTimer& 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));
}
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;
static timer_tree_dfs_iterator_t begin_timer_tree(LLFastTimer::NamedTimer& 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));
}
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>
{
public:
NamedTimerFactory()
: mActiveTimerRoot(NULL),
mTimerRoot(NULL),
mAppTimer(NULL),
mRootFrameState(NULL)
{}
/*virtual */ void initSingleton()
{
mTimerRoot = new LLFastTimer::NamedTimer("root");
mActiveTimerRoot = new LLFastTimer::NamedTimer("Frame");
mActiveTimerRoot->setCollapsed(false);
mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
// getFrameState and setParent recursively call this function,
// so we have to work around that by using a specialized implementation
// for the special case were mTimerRoot != mActiveTimerRoot -- Aleric
mRootFrameState->mParent = &LLFastTimer::getFrameStateList()[0]; // &mTimerRoot->getFrameState()
mRootFrameState->mParent->mActiveCount = 1;
// And the following four lines are mActiveTimerRoot->setParent(mTimerRoot);
llassert(!mActiveTimerRoot->mParent);
mActiveTimerRoot->mParent = mTimerRoot; // mParent = parent;
//mRootFrameState->mParent = mRootFrameState->mParent; // getFrameState().mParent = &parent->getFrameState();
mTimerRoot->getChildren().push_back(mActiveTimerRoot); // parent->getChildren().push_back(this);
mTimerRoot->mNeedsSorting = true; // parent->mNeedsSorting = true;
mAppTimer = new LLFastTimer(mRootFrameState);
}
~NamedTimerFactory()
{
std::for_each(mTimers.begin(), mTimers.end(), DeletePairedPointer());
delete mAppTimer;
delete mActiveTimerRoot;
delete mTimerRoot;
delete mRootFrameState;
}
LLFastTimer::NamedTimer& createNamedTimer(const std::string& name)
{
timer_map_t::iterator found_it = mTimers.find(name);
if (found_it != mTimers.end())
{
return *found_it->second;
}
LLFastTimer::NamedTimer* timer = new LLFastTimer::NamedTimer(name);
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* getActiveRootTimer() { return mActiveTimerRoot; }
LLFastTimer::NamedTimer* getRootTimer() { return mTimerRoot; }
const LLFastTimer* getAppTimer() { return mAppTimer; }
LLFastTimer::FrameState& getRootFrameState() { return *mRootFrameState; }
typedef std::map<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* mActiveTimerRoot;
LLFastTimer::NamedTimer* mTimerRoot;
LLFastTimer* mAppTimer;
LLFastTimer::FrameState* mRootFrameState; // Points to memory allocated with new, so this pointer is not invalidated.
};
void update_cached_pointers_if_changed()
{
// detect when elements have moved and update cached pointers
static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
if (&*(LLFastTimer::getFrameStateList().begin()) != sFirstTimerAddress)
{
LLFastTimer::updateCachedPointers();
sFirstTimerAddress = &*(LLFastTimer::getFrameStateList().begin());
}
}
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
{
mTimer.setCollapsed(!open);
mFrameState = &mTimer.getFrameState();
update_cached_pointers_if_changed();
}
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
{
mFrameState = &mTimer.getFrameState();
update_cached_pointers_if_changed();
}
// static
void LLFastTimer::updateCachedPointers()
{
// Update DeclareTimer::mFrameState pointers.
for (DeclareTimer::instance_iter it = DeclareTimer::beginInstances(); it != DeclareTimer::endInstances(); ++it)
{
// update cached pointer
it->mFrameState = &it->mTimer.getFrameState();
}
// Update CurTimerData::mFrameState and LLFastTimer::mFrameState of timers on the stack.
FrameState& root_frame_state(NamedTimerFactory::instance().getRootFrameState()); // This one is not invalidated.
CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
// If the the following condition holds then cur_timer_data->mCurTimer == mAppTimer and
// we can stop since mAppTimer->mFrameState is allocated with new and does not invalidate.
while(cur_timer_data->mFrameState != &root_frame_state)
{
cur_timer_data->mFrameState = cur_timer_data->mCurTimer->mFrameState = &cur_timer_data->mNamedTimer->getFrameState();
cur_timer_data = &cur_timer_data->mCurTimer->mLastTimerData;
}
// Update FrameState::mParent
info_list_t& frame_state_list(getFrameStateList());
FrameState* const vector_start = &*frame_state_list.begin();
int const vector_size = frame_state_list.size();
FrameState const* const old_vector_start = root_frame_state.mParent;
if (vector_start != old_vector_start)
{
// Vector was moved; if it was sorted then FrameState::mParent will get fixed after returning from this function (see LLFastTimer::NamedTimer::resetFrame).
root_frame_state.mParent = vector_start;
ptrdiff_t offset = vector_start - old_vector_start;
llassert(frame_state_list[vector_size - 1].mParent == vector_start); // The one that was added at the end is already OK.
for (int i = 2; i < vector_size - 1; ++i)
{
FrameState*& parent = frame_state_list[i].mParent;
if (parent != &root_frame_state)
{
parent += offset;
}
}
}
}
// See lltimer.cpp.
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
std::string LLFastTimer::sClockType = "gettimeofday";
#elif LL_WINDOWS
std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
#else
#error "Platform not supported"
#endif
//static
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
{
static bool firstcall = true;
static U64 sCPUClockFrequency;
if (firstcall)
{
sCPUClockFrequency = calc_clock_frequency();
firstcall = false;
}
return sCPUClockFrequency >> 8;
}
LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
: mActiveCount(0),
mCalls(0),
mSelfTimeCounter(0),
mParent(NULL),
mLastCaller(NULL),
mMoveUpTree(false),
mTimer(timerp)
{}
LLFastTimer::NamedTimer::NamedTimer(const std::string& name)
: mName(name),
mCollapsed(true),
mParent(NULL),
mTotalTimeCounter(0),
mCountAverage(0),
mCallAverage(0),
mNeedsSorting(false)
{
info_list_t& frame_state_list = getFrameStateList();
mFrameStateIndex = frame_state_list.size();
getFrameStateList().push_back(FrameState(this));
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()
{
delete[] mCountHistory;
delete[] mCallHistory;
}
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 (%.2f ms, %d calls)", getName().c_str(), (F32)((F32)getCountAverage() * ms_multiplier), (S32)getCallAverage());
}
else
{
return llformat("%s (%.2f ms, %d calls)", getName().c_str(), (F32)((F32)getHistoricalCount(history_idx) * ms_multiplier), (S32)getHistoricalCalls(history_idx));
}
}
void LLFastTimer::NamedTimer::setParent(NamedTimer* parent)
{
llassert_always(parent != this);
llassert_always(parent != NULL);
if (mParent)
{
// subtract our accumulated from previous parent
for (S32 i = 0; i < HISTORY_NUM; i++)
{
mParent->mCountHistory[i] -= mCountHistory[i];
}
// 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())
{
children.erase(found_it);
}
}
mParent = parent;
if (parent)
{
getFrameState().mParent = &parent->getFrameState();
parent->getChildren().push_back(this);
parent->mNeedsSorting = true;
}
}
S32 LLFastTimer::NamedTimer::getDepth()
{
S32 depth = 0;
NamedTimer* timerp = mParent;
while(timerp)
{
depth++;
timerp = timerp->mParent;
}
return depth;
}
// static
void LLFastTimer::NamedTimer::processTimes()
{
if (sCurFrameIndex < 0) return;
buildHierarchy();
accumulateTimings();
}
// sort timer info structs by depth first traversal order
struct SortTimersDFS
{
bool operator()(const LLFastTimer::FrameState& i1, const LLFastTimer::FrameState& i2)
{
return i1.mTimer->getFrameStateIndex() < i2.mTimer->getFrameStateIndex();
}
};
// 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
FrameState& frame_state(timer.getFrameState());
if (frame_state.mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
{
timer.setParent(frame_state.mLastCaller);
// no need to push up tree on first use, flag can be set spuriously
frame_state.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());
it != end_timer_tree_bottom_up();
++it)
{
NamedTimer* timerp = *it;
// skip root timer
if (timerp == NamedTimerFactory::instance().getRootTimer()) continue;
if (timerp->getFrameState().mMoveUpTree)
{
// since ancestors have already been visited, reparenting won't affect tree traversal
//step up tree, bringing our descendants with us
//llinfos << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
// " to child of " << timerp->getParent()->getParent()->getName() << llendl;
timerp->setParent(timerp->getParent()->getParent());
timerp->getFrameState().mMoveUpTree = false;
// don't bubble up any ancestors until descendants are done bubbling up
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();
// 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->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;
cur_timer->mStartTime = cur_time;
cur_data = &cur_timer->mLastTimerData;
cur_data->mChildTime += cumulative_time_delta;
cur_timer = cur_timer->mLastTimerData.mCurTimer;
}
// traverse tree in DFS post order, or bottom up
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getActiveRootTimer());
it != end_timer_tree_bottom_up();
++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;
}
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);
}
}
}
U32 LLFastTimer::NamedTimer::getCountAverage() const
{
return mCountAverage;// (sCurFrameIndex <= 0 || mCountAverage <= 0) ? 0 : mCountAverage / llmin(sCurFrameIndex,(S32)HISTORY_NUM);
}
U32 LLFastTimer::NamedTimer::getCallAverage() const
{
return mCallAverage;// (sCurFrameIndex <= 0 || mCallAverage <= 0) ? 0 : mCallAverage / llmin(sCurFrameIndex,(S32)HISTORY_NUM);
}
// static
void LLFastTimer::NamedTimer::resetFrame()
{
if (sLog)
{ //output current frame counts to performance log
static S32 call_count = 0;
if (call_count % 100 == 0)
{
llinfos << "countsPerSecond (32 bit): " << countsPerSecond() << llendl;
llinfos << "get_clock_count (64 bit): " << get_clock_count() << llendl;
llinfos << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << llendl;
llinfos << "getCPUClockCount32() " << getCPUClockCount32() << llendl;
llinfos << "getCPUClockCount64() " << getCPUClockCount64() << llendl;
llinfos << "elapsed sec " << ((F64)getCPUClockCount64())/((F64)LLProcessorInfo().getCPUFrequency()*1000000.0) << llendl;
}
call_count++;
F64 iclock_freq = 1000.0 / countsPerSecond(); // good place to calculate clock frequency
F64 total_time = 0;
LLSD sd;
{
for (instance_iter it = beginInstances(); it != endInstances(); ++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;
// 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;
}
}
sd["Total"]["Time"] = (LLSD::Real) total_time;
sd["Total"]["Calls"] = (LLSD::Integer) 1;
{
LLMutexLock lock(sLogLock);
sLogQueue.push(sd);
}
}
// tag timers by position in depth first traversal of tree
S32 index = 0;
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
it != end_timer_tree();
++it)
{
NamedTimer* timerp = (*it);
timerp->mFrameStateIndex = index;
index++;
}
llassert(index == (S32)getFrameStateList().size());
// sort timers by DFS traversal order to improve cache coherency
std::sort(getFrameStateList().begin(), getFrameStateList().end(), SortTimersDFS());
// update pointers into framestatelist now that we've sorted it
updateCachedPointers();
// 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();
}
}
}
//sTimerCycles = 0;
//sTimerCalls = 0;
}
//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->mLastTimerData.mCurTimer != cur_timer)
{
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;
}
//static
LLFastTimer::info_list_t& LLFastTimer::getFrameStateList()
{
if (!sTimerInfos)
{
sTimerInfos = new info_list_t;
#if 0
// Avoid the vector being moved in memory by reserving enough memory right away.
sTimerInfos->reserve(1024);
#endif
}
return *sTimerInfos;
}
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
{
llassert_always(mFrameStateIndex >= 0);
if (this == NamedTimerFactory::instance().getActiveRootTimer())
{
return NamedTimerFactory::instance().getRootFrameState();
}
return getFrameStateList()[mFrameStateIndex];
}
// static
LLFastTimer::NamedTimer& LLFastTimer::NamedTimer::getRootNamedTimer()
{
return *NamedTimerFactory::instance().getActiveRootTimer();
}
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
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
// walk over timers in depth order and output timings
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
it != end_timer_tree();
++it)
{
NamedTimer* timerp = (*it);
F64 total_time_ms = ((F64)timerp->getHistoricalCount(0) * iclock_freq);
// Don't bother with really brief times, keep output concise
if (total_time_ms < 0.1) continue;
std::ostringstream out_str;
for (S32 i = 0; i < timerp->getDepth(); i++)
{
out_str << "\t";
}
out_str << timerp->getName() << " "
<< std::setprecision(3) << total_time_ms << " ms, "
<< timerp->getHistoricalCalls(0) << " calls";
llinfos << out_str.str() << llendl;
}
}
//static
void LLFastTimer::reset()
{
NamedTimer::reset();
}
//static
void LLFastTimer::writeLog(std::ostream& os)
{
while (!sLogQueue.empty())
{
LLSD& sd = sLogQueue.front();
LLSDSerialize::toXML(sd, os);
LLMutexLock lock(sLogLock);
sLogQueue.pop();
}
}
//static
const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& name)
{
return NamedTimerFactory::instance().getTimerByName(name);
}
LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
: mFrameState(state)
{
// Only called for mAppTimer with mRootFrameState, which never invalidates.
llassert(state == &NamedTimerFactory::instance().getRootFrameState());
U32 start_time = getCPUClockCount32();
mStartTime = start_time;
mFrameState->mActiveCount++;
LLFastTimer::sCurTimerData.mCurTimer = this;
LLFastTimer::sCurTimerData.mNamedTimer = mFrameState->mTimer;
LLFastTimer::sCurTimerData.mFrameState = mFrameState;
LLFastTimer::sCurTimerData.mChildTime = 0;
// This is the root FastTimer (mAppTimer), mark it as such by having
// mLastTimerData be equal to sCurTimerData (which is a rather arbitrary
// and not very logical way to do that --Aleric).
mLastTimerData = LLFastTimer::sCurTimerData;
}
//////////////////////////////////////////////////////////////////////////////
//
// Important note: These implementations must be FAST!
//
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
// These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
U32 LLFastTimer::getCPUClockCount32()
{
return (U32)(get_clock_count()>>8);
}
U64 LLFastTimer::getCPUClockCount64()
{
return get_clock_count();
}

View File

@@ -1,277 +0,0 @@
/**
* @file llfasttimer_class.h
* @brief Declaration of a fast timer.
*
* $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_FASTTIMER_CLASS_H
#define LL_FASTTIMER_CLASS_H
#include "llinstancetracker.h"
#define FAST_TIMER_ON 1
#define TIME_FAST_TIMERS 0
#define DEBUG_FAST_TIMER_THREADS 1
class LLMutex;
#include <queue>
#include "llsd.h"
LL_COMMON_API void assert_main_thread();
class LL_COMMON_API LLFastTimer
{
public:
class NamedTimer;
struct LL_COMMON_API FrameState
{
FrameState(NamedTimer* timerp);
U32 mSelfTimeCounter;
U32 mCalls;
FrameState* mParent; // info for caller timer
NamedTimer* 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
};
// 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();
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();
S32 getFrameStateIndex() const { return mFrameStateIndex; }
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
//
S32 mFrameStateIndex;
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);
private:
NamedTimer& mTimer;
FrameState* mFrameState;
};
public:
LLFastTimer(LLFastTimer::FrameState* state);
LL_FORCE_INLINE LLFastTimer(LLFastTimer::DeclareTimer& timer)
: mFrameState(timer.mFrameState)
{
#if TIME_FAST_TIMERS
U64 timer_start = getCPUClockCount64();
#endif
#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->mNamedTimer = &timer.mTimer;
cur_timer_data->mFrameState = frame_state;
cur_timer_data->mChildTime = 0;
#endif
#if TIME_FAST_TIMERS
U64 timer_end = getCPUClockCount64();
sTimerCycles += timer_end - timer_start;
#endif
#if DEBUG_FAST_TIMER_THREADS
#if !LL_RELEASE
assert_main_thread();
#endif
#endif
}
LL_FORCE_INLINE ~LLFastTimer()
{
#if TIME_FAST_TIMERS
U64 timer_start = getCPUClockCount64();
#endif
#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.mNamedTimer;
// we are only tracking self time, so subtract our total time delta from parents
mLastTimerData.mChildTime += total_time;
LLFastTimer::sCurTimerData = mLastTimerData;
#endif
#if TIME_FAST_TIMERS
U64 timer_end = getCPUClockCount64();
sTimerCycles += timer_end - timer_start;
sTimerCalls++;
#endif
}
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;
static U64 sTimerCycles;
static U32 sTimerCalls;
typedef std::vector<FrameState> info_list_t;
static info_list_t& getFrameStateList();
// call this once a frame to reset timers
static void nextFrame();
// 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();
// called to update all FrameState pointers.
static void updateCachedPointers();
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;
NamedTimer* mNamedTimer;
FrameState* mFrameState;
U32 mChildTime;
};
static CurTimerData sCurTimerData;
static std::string sClockType;
public:
static U32 getCPUClockCount32();
static U64 getCPUClockCount64();
private:
static S32 sCurFrameIndex;
static S32 sLastFrameIndex;
static U64 sLastFrameTime;
static info_list_t* sTimerInfos;
U32 mStartTime;
LLFastTimer::FrameState* mFrameState;
LLFastTimer::CurTimerData mLastTimerData;
};
#endif // LL_LLFASTTIMER_H

View File

@@ -35,13 +35,10 @@
#include "u64.h" #include "u64.h"
#include "llframetimer.h" #include "llframetimer.h"
#include "aiframetimer.h"
#include "llaprpool.h"
// Local constants. // Local constants.
static F64 const USEC_PER_SECOND = 1000000.0; static F64 const USEC_PER_SECOND = 1000000.0;
static F64 const USEC_TO_SEC_F64 = 0.000001; static F64 const USEC_TO_SEC_F64 = 0.000001;
static F64 const NEVER = 1e16;
// Static members // Static members
U64 const LLFrameTimer::sStartTotalTime = totalTime(); // Application start in microseconds since epoch. U64 const LLFrameTimer::sStartTotalTime = totalTime(); // Application start in microseconds since epoch.
@@ -53,41 +50,41 @@ F64 LLFrameTimer::sFrameTime = 0.0; // Current time in seconds since app
S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of frames since application start). S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of frames since application start).
U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame. U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame.
U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount. U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount.
// Mutex for the above.
apr_thread_mutex_t* LLFrameTimer::sGlobalMutex;
// static // static
void LLFrameTimer::global_initialization(void) void LLFrameTimer::updateFrameTime()
{ {
apr_thread_mutex_create(&sGlobalMutex, APR_THREAD_MUTEX_UNNESTED, LLAPRRootPool::get()());
AIFrameTimer::sNextExpiration = NEVER;
}
// static
void LLFrameTimer::updateFrameTime(void)
{
llassert(is_main_thread());
sTotalTime = totalTime(); sTotalTime = totalTime();
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64; sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
F64 new_frame_time = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64; sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
apr_thread_mutex_lock(sGlobalMutex);
sFrameTime = new_frame_time;
apr_thread_mutex_unlock(sGlobalMutex);
} }
// static // static
void LLFrameTimer::updateFrameTimeAndCount(void) void LLFrameTimer::updateFrameTimeAndCount()
{ {
updateFrameTime(); updateFrameTime();
sFrameDeltaTime = sTotalTime - sPrevTotalTime; sFrameDeltaTime = sTotalTime - sPrevTotalTime;
sPrevTotalTime = sTotalTime; sPrevTotalTime = sTotalTime;
++sFrameCount; ++sFrameCount;
}
// Handle AIFrameTimer expiration and callbacks. void LLFrameTimer::reset(F32 expiration)
if (AIFrameTimer::sNextExpiration <= sFrameTime) {
{ llassert(!mPaused);
AIFrameTimer::handleExpiration(sFrameTime); mStartTime = sFrameTime;
} mExpiry = sFrameTime + expiration;
}
void LLFrameTimer::start(F32 expiration)
{
reset(expiration);
mRunning = true; // Start, if not already started.
}
void LLFrameTimer::stop()
{
llassert(!mPaused);
mRunning = false;
} }
// Don't combine pause/unpause with start/stop // Don't combine pause/unpause with start/stop
@@ -98,38 +95,38 @@ void LLFrameTimer::updateFrameTimeAndCount(void)
// foo.unpause() // unpauses // foo.unpause() // unpauses
// F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause() // F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause()
// Note: elapsed would also be valid with no unpause() call (= time run until pause() called) // Note: elapsed would also be valid with no unpause() call (= time run until pause() called)
void LLFrameTimer::pause(void) void LLFrameTimer::pause()
{ {
llassert(is_main_thread());
if (!mPaused) if (!mPaused)
{ {
// Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime - mStartTime; // Abuse mStartTime to store the elapsed time so far. mStartTime = sFrameTime - mStartTime; // Abuse mStartTime to store the elapsed time so far.
} }
mPaused = true; mPaused = true;
} }
void LLFrameTimer::unpause(void) void LLFrameTimer::unpause()
{ {
llassert(is_main_thread());
if (mPaused) if (mPaused)
{ {
// Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime - mStartTime; // Set mStartTime consistent with the elapsed time so far. mStartTime = sFrameTime - mStartTime; // Set mStartTime consistent with the elapsed time so far.
} }
mPaused = false; mPaused = false;
} }
void LLFrameTimer::setTimerExpirySec(F32 expiration)
{
llassert(!mPaused);
mExpiry = mStartTime + expiration;
}
void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch) void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
{ {
llassert(is_main_thread());
llassert(!mPaused); llassert(!mPaused);
// Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime; mStartTime = sFrameTime;
mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime); mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
} }
F64 LLFrameTimer::expiresAt(void) const F64 LLFrameTimer::expiresAt() const
{ {
F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64; F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
expires_at += mExpiry; expires_at += mExpiry;
@@ -138,47 +135,31 @@ F64 LLFrameTimer::expiresAt(void) const
bool LLFrameTimer::checkExpirationAndReset(F32 expiration) bool LLFrameTimer::checkExpirationAndReset(F32 expiration)
{ {
llassert(!mPaused); if (hasExpired())
F64 frame_time = getElapsedSeconds();
if (frame_time >= mExpiry)
{ {
mStartTime = frame_time; reset(expiration);
mExpiry = mStartTime + expiration;
return true; return true;
} }
return false; return false;
} }
F32 LLFrameTimer::getElapsedTimeAndResetF32(void)
{
llassert(mRunning && !mPaused);
F64 frame_time = getElapsedSeconds();
F32 elapsed_time = (F32)(frame_time - mStartTime);
mExpiry = mStartTime = frame_time;
return elapsed_time;
}
// static // static
// Return number of seconds between the last two frames. F32 LLFrameTimer::getFrameDeltaTimeF32()
F32 LLFrameTimer::getFrameDeltaTimeF32(void)
{ {
llassert(is_main_thread());
// Only the main thread writes to sFrameDeltaTime, so there is no need for locking.
return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64); return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
} }
// static // static
// Return seconds since the current frame started // Return seconds since the current frame started
F32 LLFrameTimer::getCurrentFrameTime(void) F32 LLFrameTimer::getCurrentFrameTime()
{ {
llassert(is_main_thread());
// Only the main thread writes to sTotalTime, so there is no need for locking.
U64 frame_time = totalTime() - sTotalTime; U64 frame_time = totalTime() - sTotalTime;
return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64); return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64);
} }
// Glue code to avoid full class .h file #includes // Glue code to avoid full class .h file #includes
F32 getCurrentFrameTime(void) F32 getCurrentFrameTime()
{ {
return (F32)(LLFrameTimer::getCurrentFrameTime()); return (F32)(LLFrameTimer::getCurrentFrameTime());
} }

View File

@@ -43,131 +43,88 @@
#include "lltimer.h" #include "lltimer.h"
#include "timing.h" #include "timing.h"
#include <apr_thread_mutex.h>
class LL_COMMON_API LLFrameTimer class LL_COMMON_API LLFrameTimer
{ {
public: public:
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true). // Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); } LLFrameTimer(void) : mStartTime(sFrameTime), mExpiry(0), mRunning(true), mPaused(false) { }
// Atomic reads of static variables.
// Return the number of seconds since the start of the application. // Return the number of seconds since the start of the application.
static F64 getElapsedSeconds(void) static F64 getElapsedSeconds()
{ {
// Loses msec precision after ~4.5 hours... // Loses msec precision after ~4.5 hours...
apr_thread_mutex_lock(sGlobalMutex); return sFrameTime;
F64 res = sFrameTime;
apr_thread_mutex_unlock(sGlobalMutex);
return res;
} }
// Return a low precision usec since epoch. // Return a low precision usec since epoch.
static U64 getTotalTime(void) static U64 getTotalTime()
{ {
// sTotalTime is only accessed by the main thread, so no locking is necessary. llassert(sTotalTime);
llassert(is_main_thread()); return sTotalTime;
//apr_thread_mutex_lock(sGlobalMutex);
U64 res = sTotalTime;
//apr_thread_mutex_unlock(sGlobalMutex);
llassert(res);
return res;
} }
// Return a low precision seconds since epoch. // Return a low precision seconds since epoch.
static F64 getTotalSeconds(void) static F64 getTotalSeconds()
{ {
// sTotalSeconds is only accessed by the main thread, so no locking is necessary. return sTotalSeconds;
llassert(is_main_thread());
//apr_thread_mutex_lock(sGlobalMutex);
F64 res = sTotalSeconds;
//apr_thread_mutex_unlock(sGlobalMutex);
return res;
}
// Return current frame number (the number of frames since application start).
static U32 getFrameCount(void)
{
// sFrameCount is only accessed by the main thread, so no locking is necessary.
llassert(is_main_thread());
//apr_thread_mutex_lock(sGlobalMutex);
U32 res = sFrameCount;
//apr_thread_mutex_unlock(sGlobalMutex);
return res;
} }
// Call this method once per frame to update the current frame time. // Call this method once per frame to update the current frame time.
// This is actually called at some other times as well. // This is actually called at some other times as well.
static void updateFrameTime(void); static void updateFrameTime();
// Call this method once, and only once, per frame to update the current frame count and sFrameDeltaTime. // Call this method once, and only once, per frame to update the current frame count and sFrameDeltaTime.
static void updateFrameTimeAndCount(void); static void updateFrameTimeAndCount();
// Return current frame number (the number of frames since application start).
static U32 getFrameCount() { return sFrameCount; }
// Return duration of last frame in seconds. // Return duration of last frame in seconds.
static F32 getFrameDeltaTimeF32(void); static F32 getFrameDeltaTimeF32();
// Return seconds since the current frame started // Return seconds since the current frame started
static F32 getCurrentFrameTime(void); static F32 getCurrentFrameTime();
// MANIPULATORS // MANIPULATORS
void reset(F32 expiration = 0.f) // Same as start() but leaves mRunning off when called after stop(). void reset(F32 expiration = 0.f); // Same as start() but leaves mRunning off when called after stop().
{ void start(F32 expiration = 0.f); // Reset and (re)start with expiration.
llassert(!mPaused); void stop(); // Stop running.
mStartTime = getElapsedSeconds();
mExpiry = mStartTime + expiration;
}
void start(F32 expiration = 0.f) // Reset and (re)start with expiration.
{
reset(expiration);
mRunning = true; // Start, if not already started.
}
void stop(void) // Stop running.
{
llassert(!mPaused);
mRunning = false;
}
void pause(); // Mark elapsed time so far. void pause(); // Mark elapsed time so far.
void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime. void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime.
void setTimerExpirySec(F32 expiration) { llassert(!mPaused); mExpiry = mStartTime + expiration; } void setTimerExpirySec(F32 expiration);
void setExpiryAt(F64 seconds_since_epoch); void setExpiryAt(F64 seconds_since_epoch);
bool checkExpirationAndReset(F32 expiration); // Returns true when expired. Only resets if expired. bool checkExpirationAndReset(F32 expiration); // Returns true when expired. Only resets if expired.
F32 getElapsedTimeAndResetF32(void); F32 getElapsedTimeAndResetF32() { F32 t = getElapsedTimeF32(); reset(); return t; }
void setAge(const F64 age) { llassert(!mPaused); mStartTime = getElapsedSeconds() - age; } void setAge(const F64 age) { llassert(!mPaused); mStartTime = sFrameTime - age; }
// ACCESSORS // ACCESSORS
bool hasExpired() const { return getElapsedSeconds() >= mExpiry; } bool hasExpired() const { return sFrameTime >= mExpiry; }
F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(getElapsedSeconds() - mStartTime); } F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(sFrameTime - mStartTime); }
bool getStarted() const { return mRunning; } bool getStarted() const { return mRunning; }
// return the seconds since epoch when this timer will expire. // return the seconds since epoch when this timer will expire.
F64 expiresAt() const; F64 expiresAt() const;
public:
// Do one-time initialization of the static members.
static void global_initialization(void);
protected: protected:
// A single, high resolution timer that drives all LLFrameTimers
// *NOTE: no longer used.
//static LLTimer sInternalTimer;
// //
// Application constants // Aplication constants
// //
// Application start in microseconds since epoch. // Application start in microseconds since epoch.
static U64 const sStartTotalTime; static U64 const sStartTotalTime;
// //
// Global data. // Data updated per frame
// //
// More than one thread are accessing (some of) these variables, therefore we need locking.
static apr_thread_mutex_t* sGlobalMutex;
// Current time in seconds since application start, updated together with sTotalTime. // Current time in seconds since application start, updated together with sTotalTime.
static F64 sFrameTime; static F64 sFrameTime;

View File

@@ -134,7 +134,4 @@ private:
LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b); LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b); LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
#endif // LL_LLMD5_H #endif // LL_LLMD5_H

View File

@@ -32,13 +32,9 @@
#include "linden_common.h" #include "linden_common.h"
//#if MEM_TRACK_MEM
#include "llthread.h"
//#endif
#if defined(LL_WINDOWS) #if defined(LL_WINDOWS)
//# include <windows.h> #define _WINSOCKAPI_
# include <windows.h>
# include <psapi.h> # include <psapi.h>
#elif defined(LL_DARWIN) #elif defined(LL_DARWIN)
# include <sys/types.h> # include <sys/types.h>
@@ -49,9 +45,10 @@
#endif #endif
#include "llmemory.h" #include "llmemory.h"
#include "llmemtype.h"
#include "llsys.h" #include "llsys.h"
#include "llframetimer.h" #include "llthread.h"
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
//static //static
@@ -165,7 +162,6 @@ void LLMemory::logMemoryInfo(BOOL update)
if(update) if(update)
{ {
updateMemoryInfo() ; updateMemoryInfo() ;
LLPrivateMemoryPoolManager::getInstance()->updateStatistics() ;
} }
llinfos << "Current allocated physical memory(KB): " << sAllocatedMemInKB << llendl ; llinfos << "Current allocated physical memory(KB): " << sAllocatedMemInKB << llendl ;
@@ -246,6 +242,161 @@ U32 LLMemory::getAllocatedMemKB()
return sAllocatedMemInKB ; return sAllocatedMemInKB ;
} }
//----------------------------------------------------------------------------
//static
#if MEM_TRACK_TYPE
S32 LLMemType::sCurDepth = 0;
S32 LLMemType::sCurType = LLMemType::MTYPE_INIT;
S32 LLMemType::sType[LLMemType::MTYPE_MAX_DEPTH];
S32 LLMemType::sMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
S32 LLMemType::sMaxMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
S32 LLMemType::sNewCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
S32 LLMemType::sOverheadMem = 0;
const char* LLMemType::sTypeDesc[LLMemType::MTYPE_NUM_TYPES] =
{
"INIT",
"STARTUP",
"MAIN",
"IMAGEBASE",
"IMAGERAW",
"IMAGEFORMATTED",
"APPFMTIMAGE",
"APPRAWIMAGE",
"APPAUXRAWIMAGE",
"DRAWABLE",
"OBJECT",
"PIPELINE",
"AVATAR",
"PARTICLES",
"REGIONS",
"INVENTORY",
"ANIMATION",
"NETWORK",
"PHYSICS",
"INTERESTLIST",
"SCRIPT",
"SCRIPT_RUN",
"SCRIPT_BYTECODE",
"IO_PUMP",
"IO_TCP",
"IO_BUFFER",
"IO_HTTP_SERVER"
"IO_SD_SERVER",
"IO_SD_CLIENT",
"IO_URL_REQUEST",
"TEMP1",
"TEMP2",
"TEMP3",
"TEMP4",
"TEMP5",
"TEMP6",
"TEMP7",
"TEMP8",
"TEMP9"
};
#endif
S32 LLMemType::sTotalMem = 0;
S32 LLMemType::sMaxTotalMem = 0;
//static
void LLMemType::printMem()
{
S32 misc_mem = sTotalMem;
#if MEM_TRACK_TYPE
for (S32 i=0; i<MTYPE_NUM_TYPES; i++)
{
if (sMemCount[i])
{
llinfos << llformat("MEM: % 20s %03d MB (%03d MB) in %06d News",sTypeDesc[i],sMemCount[i]>>20,sMaxMemCount[i]>>20, sNewCount[i]) << llendl;
}
misc_mem -= sMemCount[i];
}
#endif
llinfos << llformat("MEM: % 20s %03d MB","MISC",misc_mem>>20) << llendl;
llinfos << llformat("MEM: % 20s %03d MB (Max=%d MB)","TOTAL",sTotalMem>>20,sMaxTotalMem>>20) << llendl;
}
#if MEM_TRACK_MEM
void* ll_allocate (size_t size)
{
if (size == 0)
{
llwarns << "Null allocation" << llendl;
}
size = (size+3)&~3;
S32 alloc_size = size + 4;
#if MEM_TRACK_TYPE
alloc_size += 4;
#endif
char* p = (char*)malloc(alloc_size);
if (p == NULL)
{
LLMemory::freeReserve();
llerrs << "Out of memory Error" << llendl;
}
LLMemType::sTotalMem += size;
LLMemType::sMaxTotalMem = llmax(LLMemType::sTotalMem, LLMemType::sMaxTotalMem);
LLMemType::sOverheadMem += 4;
*(size_t*)p = size;
p += 4;
#if MEM_TRACK_TYPE
if (LLMemType::sCurType < 0 || LLMemType::sCurType >= LLMemType::MTYPE_NUM_TYPES)
{
llerrs << "Memory Type Error: new" << llendl;
}
LLMemType::sOverheadMem += 4;
*(S32*)p = LLMemType::sCurType;
p += 4;
LLMemType::sMemCount[LLMemType::sCurType] += size;
if (LLMemType::sMemCount[LLMemType::sCurType] > LLMemType::sMaxMemCount[LLMemType::sCurType])
{
LLMemType::sMaxMemCount[LLMemType::sCurType] = LLMemType::sMemCount[LLMemType::sCurType];
}
LLMemType::sNewCount[LLMemType::sCurType]++;
#endif
return (void*)p;
}
void ll_release (void *pin)
{
if (!pin)
{
return;
}
char* p = (char*)pin;
#if MEM_TRACK_TYPE
p -= 4;
S32 type = *(S32*)p;
if (type < 0 || type >= LLMemType::MTYPE_NUM_TYPES)
{
llerrs << "Memory Type Error: delete" << llendl;
}
#endif
p -= 4;
S32 size = *(size_t*)p;
LLMemType::sOverheadMem -= 4;
#if MEM_TRACK_TYPE
LLMemType::sMemCount[type] -= size;
LLMemType::sOverheadMem -= 4;
LLMemType::sNewCount[type]--;
#endif
LLMemType::sTotalMem -= size;
free(p);
}
#else
void* ll_allocate (size_t size) void* ll_allocate (size_t size)
{ {
if (size == 0) if (size == 0)
@@ -261,6 +412,38 @@ void* ll_allocate (size_t size)
return p; return p;
} }
void ll_release (void *p)
{
free(p);
}
#endif
#if MEM_TRACK_MEM
void* operator new (size_t size)
{
return ll_allocate(size);
}
void* operator new[] (size_t size)
{
return ll_allocate(size);
}
void operator delete (void *p)
{
ll_release(p);
}
void operator delete[] (void *p)
{
ll_release(p);
}
#endif
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
#if defined(LL_WINDOWS) #if defined(LL_WINDOWS)
@@ -430,141 +613,6 @@ U32 LLMemory::getWorkingSetSize()
#endif #endif
//--------------------------------------------------------------------------------------------------
#if MEM_TRACK_MEM
#include "llframetimer.h"
//static
LLMemTracker* LLMemTracker::sInstance = NULL ;
LLMemTracker::LLMemTracker()
{
mLastAllocatedMem = LLMemory::getWorkingSetSize() ;
mCapacity = 128 ;
mCurIndex = 0 ;
mCounter = 0 ;
mDrawnIndex = 0 ;
mPaused = FALSE ;
mMutexp = new LLMutex() ;
mStringBuffer = new char*[128] ;
mStringBuffer[0] = new char[mCapacity * 128] ;
for(S32 i = 1 ; i < mCapacity ; i++)
{
mStringBuffer[i] = mStringBuffer[i-1] + 128 ;
}
}
LLMemTracker::~LLMemTracker()
{
delete[] mStringBuffer[0] ;
delete[] mStringBuffer;
delete mMutexp ;
}
//static
LLMemTracker* LLMemTracker::getInstance()
{
if(!sInstance)
{
sInstance = new LLMemTracker() ;
}
return sInstance ;
}
//static
void LLMemTracker::release()
{
if(sInstance)
{
delete sInstance ;
sInstance = NULL ;
}
}
//static
void LLMemTracker::track(const char* function, const int line)
{
static const S32 MIN_ALLOCATION = 0 ; //1KB
if(mPaused)
{
return ;
}
U32 allocated_mem = LLMemory::getWorkingSetSize() ;
LLMutexLock lock(mMutexp) ;
S32 delta_mem = allocated_mem - mLastAllocatedMem ;
mLastAllocatedMem = allocated_mem ;
if(delta_mem <= 0)
{
return ; //occupied memory does not grow
}
if(delta_mem < MIN_ALLOCATION)
{
return ;
}
char* buffer = mStringBuffer[mCurIndex++] ;
F32 time = (F32)LLFrameTimer::getElapsedSeconds() ;
S32 hours = (S32)(time / (60*60));
S32 mins = (S32)((time - hours*(60*60)) / 60);
S32 secs = (S32)((time - hours*(60*60) - mins*60));
strcpy(buffer, function) ;
sprintf(buffer + strlen(function), " line: %d DeltaMem: %d (bytes) Time: %d:%02d:%02d", line, delta_mem, hours,mins,secs) ;
if(mCounter < mCapacity)
{
mCounter++ ;
}
if(mCurIndex >= mCapacity)
{
mCurIndex = 0 ;
}
}
//static
void LLMemTracker::preDraw(BOOL pause)
{
mMutexp->lock() ;
mPaused = pause ;
mDrawnIndex = mCurIndex - 1;
mNumOfDrawn = 0 ;
}
//static
void LLMemTracker::postDraw()
{
mMutexp->unlock() ;
}
//static
const char* LLMemTracker::getNextLine()
{
if(mNumOfDrawn >= mCounter)
{
return NULL ;
}
mNumOfDrawn++;
if(mDrawnIndex < 0)
{
mDrawnIndex = mCapacity - 1 ;
}
return mStringBuffer[mDrawnIndex--] ;
}
#endif //MEM_TRACK_MEM
//--------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
//minimum slot size and minimal slot size interval //minimum slot size and minimal slot size interval
@@ -727,7 +775,7 @@ char* LLPrivateMemoryPool::LLMemoryBlock::allocate()
void LLPrivateMemoryPool::LLMemoryBlock::freeMem(void* addr) void LLPrivateMemoryPool::LLMemoryBlock::freeMem(void* addr)
{ {
//bit index //bit index
U32 idx = (static_cast<U32>((char*)addr - mBuffer) - mDummySize) / mSlotSize ; U32 idx = ((U32)addr - (U32)mBuffer - mDummySize) / mSlotSize ;
U32* bits = &mUsageBits ; U32* bits = &mUsageBits ;
if(idx >= 32) if(idx >= 32)
@@ -909,7 +957,7 @@ char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size)
void LLPrivateMemoryPool::LLMemoryChunk::freeMem(void* addr) void LLPrivateMemoryPool::LLMemoryChunk::freeMem(void* addr)
{ {
U32 blk_idx = getPageIndex((char*)addr); U32 blk_idx = getPageIndex((U32)addr) ;
LLMemoryBlock* blk = (LLMemoryBlock*)(mMetaBuffer + blk_idx * sizeof(LLMemoryBlock)) ; LLMemoryBlock* blk = (LLMemoryBlock*)(mMetaBuffer + blk_idx * sizeof(LLMemoryBlock)) ;
blk = blk->mSelf ; blk = blk->mSelf ;
@@ -934,7 +982,7 @@ bool LLPrivateMemoryPool::LLMemoryChunk::empty()
bool LLPrivateMemoryPool::LLMemoryChunk::containsAddress(const char* addr) const bool LLPrivateMemoryPool::LLMemoryChunk::containsAddress(const char* addr) const
{ {
return (size_t)mBuffer <= (size_t)addr && (size_t)mBuffer + mBufferSize > (size_t)addr ; return (U32)mBuffer <= (U32)addr && (U32)mBuffer + mBufferSize > (U32)addr ;
} }
//debug use //debug use
@@ -1291,9 +1339,9 @@ void LLPrivateMemoryPool::LLMemoryChunk::addToAvailBlockList(LLMemoryBlock* blk)
return ; return ;
} }
U32 LLPrivateMemoryPool::LLMemoryChunk::getPageIndex(char const* addr) U32 LLPrivateMemoryPool::LLMemoryChunk::getPageIndex(U32 addr)
{ {
return static_cast<U32>(addr - mDataBuffer) / mMinBlockSize ; return (addr - (U32)mDataBuffer) / mMinBlockSize ;
} }
//for mAvailBlockList //for mAvailBlockList
@@ -1631,7 +1679,7 @@ void LLPrivateMemoryPool::removeChunk(LLMemoryChunk* chunk)
U16 LLPrivateMemoryPool::findHashKey(const char* addr) U16 LLPrivateMemoryPool::findHashKey(const char* addr)
{ {
return (((size_t)addr) / CHUNK_SIZE) % mHashFactor ; return (((U32)addr) / CHUNK_SIZE) % mHashFactor ;
} }
LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::findChunk(const char* addr) LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::findChunk(const char* addr)

View File

@@ -40,7 +40,6 @@
#endif #endif
#include "llerror.h" #include "llerror.h"
#include "llmemtype.h"
#if LL_DEBUG #if LL_DEBUG
inline void* ll_aligned_malloc( size_t size, int align ) inline void* ll_aligned_malloc( size_t size, int align )
{ {
@@ -251,7 +250,7 @@ public:
{ {
bool operator()(const LLMemoryBlock* const& lhs, const LLMemoryBlock* const& rhs) bool operator()(const LLMemoryBlock* const& lhs, const LLMemoryBlock* const& rhs)
{ {
return (size_t)lhs->getBuffer() < (size_t)rhs->getBuffer(); return (U32)lhs->getBuffer() < (U32)rhs->getBuffer();
} }
}; };
}; };
@@ -282,7 +281,7 @@ public:
void dump() ; void dump() ;
private: private:
U32 getPageIndex(char const* addr) ; U32 getPageIndex(U32 addr) ;
U32 getBlockLevel(U32 size) ; U32 getBlockLevel(U32 size) ;
U16 getPageLevel(U32 size) ; U16 getPageLevel(U32 size) ;
LLMemoryBlock* addBlock(U32 blk_idx) ; LLMemoryBlock* addBlock(U32 blk_idx) ;

View File

@@ -1,232 +0,0 @@
/**
* @file llmemtype.cpp
* @brief Simple memory allocation/deallocation tracking stuff here
*
* $LicenseInfo:firstyear=2002&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 "llmemtype.h"
#include "llallocator.h"
std::vector<char const *> LLMemType::DeclareMemType::mNameList;
LLMemType::DeclareMemType LLMemType::MTYPE_INIT("Init");
LLMemType::DeclareMemType LLMemType::MTYPE_STARTUP("Startup");
LLMemType::DeclareMemType LLMemType::MTYPE_MAIN("Main");
LLMemType::DeclareMemType LLMemType::MTYPE_FRAME("Frame");
LLMemType::DeclareMemType LLMemType::MTYPE_GATHER_INPUT("GatherInput");
LLMemType::DeclareMemType LLMemType::MTYPE_JOY_KEY("JoyKey");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE("Idle");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_PUMP("IdlePump");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_NETWORK("IdleNetwork");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_REGIONS("IdleUpdateRegions");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_VIEWER_REGION("IdleUpdateViewerRegion");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_SURFACE("IdleUpdateSurface");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_PARCEL_OVERLAY("IdleUpdateParcelOverlay");
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_AUDIO("IdleAudio");
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING("CacheProcessPending");
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS("CacheProcessPendingAsks");
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES("CacheProcessPendingReplies");
LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_CHECK_ALL("MessageCheckAll");
LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_PROCESS_ACKS("MessageProcessAcks");
LLMemType::DeclareMemType LLMemType::MTYPE_RENDER("Render");
LLMemType::DeclareMemType LLMemType::MTYPE_SLEEP("Sleep");
LLMemType::DeclareMemType LLMemType::MTYPE_NETWORK("Network");
LLMemType::DeclareMemType LLMemType::MTYPE_PHYSICS("Physics");
LLMemType::DeclareMemType LLMemType::MTYPE_INTERESTLIST("InterestList");
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEBASE("ImageBase");
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGERAW("ImageRaw");
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEFORMATTED("ImageFormatted");
LLMemType::DeclareMemType LLMemType::MTYPE_APPFMTIMAGE("AppFmtImage");
LLMemType::DeclareMemType LLMemType::MTYPE_APPRAWIMAGE("AppRawImage");
LLMemType::DeclareMemType LLMemType::MTYPE_APPAUXRAWIMAGE("AppAuxRawImage");
LLMemType::DeclareMemType LLMemType::MTYPE_DRAWABLE("Drawable");
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT("Object");
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE("ObjectProcessUpdate");
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE_CORE("ObjectProcessUpdateCore");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY("Display");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE("DisplayUpdate");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_CAMERA("DisplayUpdateCam");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_GEOM("DisplayUpdateGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SWAP("DisplaySwap");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_HUD("DisplayUpdateHud");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_GEN_REFLECTION("DisplayGenRefl");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE("DisplayImageUpdate");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_STATE_SORT("DisplayStateSort");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SKY("DisplaySky");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_GEOM("DisplayRenderGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_FLUSH("DisplayRenderFlush");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_UI("DisplayRenderUI");
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_ATTACHMENTS("DisplayRenderAttach");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DATA("VertexData");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CONSTRUCTOR("VertexConstr");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTRUCTOR("VertexDestr");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_VERTICES("VertexCreateVerts");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_INDICES("VertexCreateIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_BUFFER("VertexDestroyBuff");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_INDICES("VertexDestroyIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_VERTS("VertexUpdateVerts");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_INDICES("VertexUpdateIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER("VertexAllocateBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_RESIZE_BUFFER("VertexResizeBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER("VertexMapBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES("VertexMapBufferVerts");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES("VertexMapBufferIndices");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UNMAP_BUFFER("VertexUnmapBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_STRIDE("VertexSetStride");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_BUFFER("VertexSetBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER("VertexSetupVertBuff");
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CLEANUP_CLASS("VertexCleanupClass");
LLMemType::DeclareMemType LLMemType::MTYPE_SPACE_PARTITION("SpacePartition");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE("Pipeline");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_INIT("PipelineInit");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_BUFFERS("PipelineCreateBuffs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RESTORE_GL("PipelineRestroGL");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UNLOAD_SHADERS("PipelineUnloadShaders");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL("PipelineLightingDetail");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GET_POOL_TYPE("PipelineGetPoolType");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_POOL("PipelineAddPool");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ALLOCATE_DRAWABLE("PipelineAllocDrawable");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_OBJECT("PipelineAddObj");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS("PipelineCreateObjs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_MOVE("PipelineUpdateMove");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_GEOM("PipelineUpdateGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_VISIBLE("PipelineMarkVisible");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_MOVED("PipelineMarkMoved");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_SHIFT("PipelineMarkShift");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_SHIFT_OBJECTS("PipelineShiftObjs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_TEXTURED("PipelineMarkTextured");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_REBUILD("PipelineMarkRebuild");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_CULL("PipelineUpdateCull");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_STATE_SORT("PipelineStateSort");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_POST_SORT("PipelinePostSort");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS("PipelineHudEls");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HL("PipelineRenderHL");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM("PipelineRenderGeom");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED("PipelineRenderGeomDef");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF("PipelineRenderGeomPostDef");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_SHADOW("PipelineRenderGeomShadow");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_SELECT("PipelineRenderSelect");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_REBUILD_POOLS("PipelineRebuildPools");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_QUICK_LOOKUP("PipelineQuickLookup");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_OBJECTS("PipelineRenderObjs");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GENERATE_IMPOSTOR("PipelineGenImpostors");
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_BLOOM("PipelineRenderBloom");
LLMemType::DeclareMemType LLMemType::MTYPE_UPKEEP_POOLS("UpkeepPools");
LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR("Avatar");
LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR_MESH("AvatarMesh");
LLMemType::DeclareMemType LLMemType::MTYPE_PARTICLES("Particles");
LLMemType::DeclareMemType LLMemType::MTYPE_REGIONS("Regions");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY("Inventory");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DRAW("InventoryDraw");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS("InventoryBuildNewViews");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DO_FOLDER("InventoryDoFolder");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_POST_BUILD("InventoryPostBuild");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_FROM_XML("InventoryFromXML");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_CREATE_NEW_ITEM("InventoryCreateNewItem");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_INIT("InventoryViewInit");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_SHOW("InventoryViewShow");
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE("InventoryViewToggle");
LLMemType::DeclareMemType LLMemType::MTYPE_ANIMATION("Animation");
LLMemType::DeclareMemType LLMemType::MTYPE_VOLUME("Volume");
LLMemType::DeclareMemType LLMemType::MTYPE_PRIMITIVE("Primitive");
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT("Script");
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_RUN("ScriptRun");
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_BYTECODE("ScriptByteCode");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_PUMP("IoPump");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_TCP("IoTCP");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_BUFFER("IoBuffer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_HTTP_SERVER("IoHttpServer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_SERVER("IoSDServer");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_CLIENT("IoSDClient");
LLMemType::DeclareMemType LLMemType::MTYPE_IO_URL_REQUEST("IOUrlRequest");
LLMemType::DeclareMemType LLMemType::MTYPE_DIRECTX_INIT("DirectXInit");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP1("Temp1");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP2("Temp2");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP3("Temp3");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP4("Temp4");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP5("Temp5");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP6("Temp6");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP7("Temp7");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP8("Temp8");
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP9("Temp9");
LLMemType::DeclareMemType LLMemType::MTYPE_OTHER("Other");
LLMemType::DeclareMemType::DeclareMemType(char const * st)
{
mID = (S32)mNameList.size();
mName = st;
mNameList.push_back(mName);
}
LLMemType::DeclareMemType::~DeclareMemType()
{
}
LLMemType::LLMemType(LLMemType::DeclareMemType& dt)
{
mTypeIndex = dt.mID;
LLAllocator::pushMemType(dt.mID);
}
LLMemType::~LLMemType()
{
LLAllocator::popMemType();
}
char const * LLMemType::getNameFromID(S32 id)
{
if (id < 0 || id >= (S32)DeclareMemType::mNameList.size())
{
return "INVALID";
}
return DeclareMemType::mNameList[id];
}
//--------------------------------------------------------------------------------------------------

View File

@@ -36,210 +36,128 @@
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
//---------------------------------------------------------------------------- class LLMemType;
#include "linden_common.h" extern void* ll_allocate (size_t size);
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! extern void ll_release (void *p);
// WARNING: Never commit with MEM_TRACK_MEM == 1
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#define MEM_TRACK_MEM (0 && LL_WINDOWS)
#include <vector> #define MEM_TRACK_MEM 0
#define MEM_TRACK_TYPE (1 && MEM_TRACK_MEM)
#if MEM_TRACK_TYPE
#define MEM_DUMP_DATA 1
#define MEM_TYPE_NEW(T) \
static void* operator new(size_t s) { LLMemType mt(T); return ll_allocate(s); } \
static void operator delete(void* p) { ll_release(p); }
#else
#define MEM_TYPE_NEW(T) #define MEM_TYPE_NEW(T)
#endif // MEM_TRACK_TYPE
//----------------------------------------------------------------------------
class LL_COMMON_API LLMemType class LL_COMMON_API LLMemType
{ {
public: public:
// Also update sTypeDesc in llmemory.cpp
// class we'll initialize all instances of as enum EMemType
// static members of MemType. Then use
// to construct any new mem type.
class LL_COMMON_API DeclareMemType
{ {
public: MTYPE_INIT,
DeclareMemType(char const * st); MTYPE_STARTUP,
~DeclareMemType(); MTYPE_MAIN,
S32 mID; MTYPE_IMAGEBASE,
char const * mName; MTYPE_IMAGERAW,
MTYPE_IMAGEFORMATTED,
// array so we can map an index ID to Name MTYPE_APPFMTIMAGE,
static std::vector<char const *> mNameList; MTYPE_APPRAWIMAGE,
MTYPE_APPAUXRAWIMAGE,
MTYPE_DRAWABLE,
MTYPE_OBJECT,
MTYPE_VERTEX_DATA,
MTYPE_SPACE_PARTITION,
MTYPE_PIPELINE,
MTYPE_AVATAR,
MTYPE_AVATAR_MESH,
MTYPE_PARTICLES,
MTYPE_REGIONS,
MTYPE_INVENTORY,
MTYPE_ANIMATION,
MTYPE_VOLUME,
MTYPE_PRIMITIVE,
MTYPE_NETWORK,
MTYPE_PHYSICS,
MTYPE_INTERESTLIST,
MTYPE_SCRIPT,
MTYPE_SCRIPT_RUN,
MTYPE_SCRIPT_BYTECODE,
MTYPE_IO_PUMP,
MTYPE_IO_TCP,
MTYPE_IO_BUFFER,
MTYPE_IO_HTTP_SERVER,
MTYPE_IO_SD_SERVER,
MTYPE_IO_SD_CLIENT,
MTYPE_IO_URL_REQUEST,
MTYPE_TEMP1,
MTYPE_TEMP2,
MTYPE_TEMP3,
MTYPE_TEMP4,
MTYPE_TEMP5,
MTYPE_TEMP6,
MTYPE_TEMP7,
MTYPE_TEMP8,
MTYPE_TEMP9,
MTYPE_OTHER, // Special, used by display code
MTYPE_NUM_TYPES
}; };
enum { MTYPE_MAX_DEPTH = 64 };
LLMemType(DeclareMemType& dt); public:
~LLMemType(); LLMemType(EMemType type)
{
#if MEM_TRACK_TYPE
if (type < 0 || type >= MTYPE_NUM_TYPES)
llerrs << "LLMemType error" << llendl;
if (sCurDepth < 0 || sCurDepth >= MTYPE_MAX_DEPTH)
llerrs << "LLMemType error" << llendl;
sType[sCurDepth] = sCurType;
sCurDepth++;
sCurType = type;
#endif
}
~LLMemType()
{
#if MEM_TRACK_TYPE
sCurDepth--;
sCurType = sType[sCurDepth];
#endif
}
static char const * getNameFromID(S32 id); static void reset();
static void printMem();
static DeclareMemType MTYPE_INIT; public:
static DeclareMemType MTYPE_STARTUP; #if MEM_TRACK_TYPE
static DeclareMemType MTYPE_MAIN; static S32 sCurDepth;
static DeclareMemType MTYPE_FRAME; static S32 sCurType;
static S32 sType[MTYPE_MAX_DEPTH];
static DeclareMemType MTYPE_GATHER_INPUT; static S32 sMemCount[MTYPE_NUM_TYPES];
static DeclareMemType MTYPE_JOY_KEY; static S32 sMaxMemCount[MTYPE_NUM_TYPES];
static S32 sNewCount[MTYPE_NUM_TYPES];
static DeclareMemType MTYPE_IDLE; static S32 sOverheadMem;
static DeclareMemType MTYPE_IDLE_PUMP; static const char* sTypeDesc[MTYPE_NUM_TYPES];
static DeclareMemType MTYPE_IDLE_NETWORK; #endif
static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS; static S32 sTotalMem;
static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION; static S32 sMaxTotalMem;
static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE;
static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY;
static DeclareMemType MTYPE_IDLE_AUDIO;
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING;
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS;
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES;
static DeclareMemType MTYPE_MESSAGE_CHECK_ALL;
static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS;
static DeclareMemType MTYPE_RENDER;
static DeclareMemType MTYPE_SLEEP;
static DeclareMemType MTYPE_NETWORK;
static DeclareMemType MTYPE_PHYSICS;
static DeclareMemType MTYPE_INTERESTLIST;
static DeclareMemType MTYPE_IMAGEBASE;
static DeclareMemType MTYPE_IMAGERAW;
static DeclareMemType MTYPE_IMAGEFORMATTED;
static DeclareMemType MTYPE_APPFMTIMAGE;
static DeclareMemType MTYPE_APPRAWIMAGE;
static DeclareMemType MTYPE_APPAUXRAWIMAGE;
static DeclareMemType MTYPE_DRAWABLE;
static DeclareMemType MTYPE_OBJECT;
static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE;
static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE;
static DeclareMemType MTYPE_DISPLAY;
static DeclareMemType MTYPE_DISPLAY_UPDATE;
static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA;
static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM;
static DeclareMemType MTYPE_DISPLAY_SWAP;
static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD;
static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION;
static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE;
static DeclareMemType MTYPE_DISPLAY_STATE_SORT;
static DeclareMemType MTYPE_DISPLAY_SKY;
static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM;
static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH;
static DeclareMemType MTYPE_DISPLAY_RENDER_UI;
static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS;
static DeclareMemType MTYPE_VERTEX_DATA;
static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR;
static DeclareMemType MTYPE_VERTEX_DESTRUCTOR;
static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES;
static DeclareMemType MTYPE_VERTEX_CREATE_INDICES;
static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER;
static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES;
static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS;
static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES;
static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER;
static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER;
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER;
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES;
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES;
static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER;
static DeclareMemType MTYPE_VERTEX_SET_STRIDE;
static DeclareMemType MTYPE_VERTEX_SET_BUFFER;
static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER;
static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS;
static DeclareMemType MTYPE_SPACE_PARTITION;
static DeclareMemType MTYPE_PIPELINE;
static DeclareMemType MTYPE_PIPELINE_INIT;
static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS;
static DeclareMemType MTYPE_PIPELINE_RESTORE_GL;
static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS;
static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL;
static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE;
static DeclareMemType MTYPE_PIPELINE_ADD_POOL;
static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE;
static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT;
static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS;
static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE;
static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM;
static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE;
static DeclareMemType MTYPE_PIPELINE_MARK_MOVED;
static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT;
static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS;
static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED;
static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD;
static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL;
static DeclareMemType MTYPE_PIPELINE_STATE_SORT;
static DeclareMemType MTYPE_PIPELINE_POST_SORT;
static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS;
static DeclareMemType MTYPE_PIPELINE_RENDER_HL;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF;
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW;
static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT;
static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS;
static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP;
static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS;
static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR;
static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM;
static DeclareMemType MTYPE_UPKEEP_POOLS;
static DeclareMemType MTYPE_AVATAR;
static DeclareMemType MTYPE_AVATAR_MESH;
static DeclareMemType MTYPE_PARTICLES;
static DeclareMemType MTYPE_REGIONS;
static DeclareMemType MTYPE_INVENTORY;
static DeclareMemType MTYPE_INVENTORY_DRAW;
static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS;
static DeclareMemType MTYPE_INVENTORY_DO_FOLDER;
static DeclareMemType MTYPE_INVENTORY_POST_BUILD;
static DeclareMemType MTYPE_INVENTORY_FROM_XML;
static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM;
static DeclareMemType MTYPE_INVENTORY_VIEW_INIT;
static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW;
static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE;
static DeclareMemType MTYPE_ANIMATION;
static DeclareMemType MTYPE_VOLUME;
static DeclareMemType MTYPE_PRIMITIVE;
static DeclareMemType MTYPE_SCRIPT;
static DeclareMemType MTYPE_SCRIPT_RUN;
static DeclareMemType MTYPE_SCRIPT_BYTECODE;
static DeclareMemType MTYPE_IO_PUMP;
static DeclareMemType MTYPE_IO_TCP;
static DeclareMemType MTYPE_IO_BUFFER;
static DeclareMemType MTYPE_IO_HTTP_SERVER;
static DeclareMemType MTYPE_IO_SD_SERVER;
static DeclareMemType MTYPE_IO_SD_CLIENT;
static DeclareMemType MTYPE_IO_URL_REQUEST;
static DeclareMemType MTYPE_DIRECTX_INIT;
static DeclareMemType MTYPE_TEMP1;
static DeclareMemType MTYPE_TEMP2;
static DeclareMemType MTYPE_TEMP3;
static DeclareMemType MTYPE_TEMP4;
static DeclareMemType MTYPE_TEMP5;
static DeclareMemType MTYPE_TEMP6;
static DeclareMemType MTYPE_TEMP7;
static DeclareMemType MTYPE_TEMP8;
static DeclareMemType MTYPE_TEMP9;
static DeclareMemType MTYPE_OTHER; // Special; used by display code
S32 mTypeIndex;
}; };
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@@ -1,102 +0,0 @@
/**
* @file llptrto.cpp
* @author Nat Goodspeed
* @date 2008-08-20
* @brief Test for llptrto.h
*
* $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$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llptrto.h"
// STL headers
// std headers
// external library headers
#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>
// other Linden headers
#include "llmemory.h"
// a refcounted class
class RCFoo: public LLRefCount
{
public:
RCFoo() {}
};
// a refcounted subclass
class RCSubFoo: public RCFoo
{
public:
RCSubFoo() {}
};
// a refcounted class using the other refcount base class
class TSRCFoo: public LLThreadSafeRefCount
{
public:
TSRCFoo() {}
};
// a non-refcounted class
class Bar
{
public:
Bar() {}
};
// a non-refcounted subclass
class SubBar: public Bar
{
public:
SubBar() {}
};
int main(int argc, char *argv[])
{
// test LLPtrTo<>
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCFoo>::type, LLPointer<RCFoo> >::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCSubFoo>::type, LLPointer<RCSubFoo> >::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<TSRCFoo>::type, LLPointer<TSRCFoo> >::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<Bar>::type, Bar*>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<SubBar>::type, SubBar*>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<int>::type, int*>::value));
// Test LLRemovePointer<>. Note that we remove both pointer variants from
// each kind of type, regardless of whether the variant makes sense.
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCFoo*>::type, RCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCFoo> >::type, RCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCSubFoo*>::type, RCSubFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCSubFoo> >::type, RCSubFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<TSRCFoo*>::type, TSRCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<TSRCFoo> >::type, TSRCFoo>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<Bar*>::type, Bar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<Bar> >::type, Bar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<SubBar*>::type, SubBar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<SubBar> >::type, SubBar>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<int*>::type, int>::value));
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<int> >::type, int>::value));
return 0;
}

View File

@@ -1,87 +0,0 @@
/**
* @file llptrto.h
* @author Nat Goodspeed
* @date 2008-08-19
* @brief LLPtrTo<TARGET> is a template helper to pick either TARGET* or -- when
* TARGET is a subclass of LLRefCount or LLThreadSafeRefCount --
* LLPointer<TARGET>. LLPtrTo<> chooses whichever pointer type is best.
*
* $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$
*/
#if ! defined(LL_LLPTRTO_H)
#define LL_LLPTRTO_H
#include "llpointer.h"
#include "llrefcount.h" // LLRefCount
#include "llthread.h" // LLThreadSafeRefCount
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/enable_if.hpp>
/**
* LLPtrTo<TARGET>::type is either of two things:
*
* * When TARGET is a subclass of either LLRefCount or LLThreadSafeRefCount,
* LLPtrTo<TARGET>::type is LLPointer<TARGET>.
* * Otherwise, LLPtrTo<TARGET>::type is TARGET*.
*
* This way, a class template can use LLPtrTo<TARGET>::type to select an
* appropriate pointer type to store.
*/
template <class T, class ENABLE=void>
struct LLPtrTo
{
typedef T* type;
};
/// specialize for subclasses of LLRefCount
template <class T>
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLRefCount, T> >::type>
{
typedef LLPointer<T> type;
};
/// specialize for subclasses of LLThreadSafeRefCount
template <class T>
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLThreadSafeRefCount, T> >::type>
{
typedef LLPointer<T> type;
};
/**
* LLRemovePointer<PTRTYPE>::type gets you the underlying (pointee) type.
*/
template <typename PTRTYPE>
struct LLRemovePointer
{
typedef typename boost::remove_pointer<PTRTYPE>::type type;
};
/// specialize for LLPointer<SOMECLASS>
template <typename SOMECLASS>
struct LLRemovePointer< LLPointer<SOMECLASS> >
{
typedef SOMECLASS type;
};
#endif /* ! defined(LL_LLPTRTO_H) */

View File

@@ -151,10 +151,9 @@ public:
*/ */
static void deleteSingleton() static void deleteSingleton()
{ {
DERIVED_TYPE* instance = getData().mSingletonInstance; delete getData().mSingletonInstance;
getData().mInitState = DELETED;
getData().mSingletonInstance = NULL; getData().mSingletonInstance = NULL;
delete instance; getData().mInitState = DELETED;
} }
static SingletonInstanceData& getData() static SingletonInstanceData& getData()
@@ -176,9 +175,23 @@ public:
{ {
SingletonInstanceData& data = getData(); SingletonInstanceData& data = getData();
if (data.mInitState != INITIALIZED) if (data.mInitState == CONSTRUCTING)
{ {
createInstance(data); llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
}
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 data.mSingletonInstance;
@@ -206,35 +219,7 @@ public:
} }
private: private:
static void createInstance(SingletonInstanceData& data);
virtual void initSingleton() {} virtual void initSingleton() {}
}; };
// Moved this here cause it's too big to be inlined --Aleric.
template<typename DERIVED_TYPE>
void LLSingleton<DERIVED_TYPE>::createInstance(SingletonInstanceData& data)
{
if (data.mInitState == CONSTRUCTING)
{
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
}
if (data.mInitState == DELETED)
{
llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl;
}
if (data.mInitState == INITIALIZING)
{
llwarns << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from initSingleton(), using half-initialized object" << llendl;
return;
}
data.mInitState = CONSTRUCTING;
data.mSingletonInstance = new DERIVED_TYPE();
data.mInitState = INITIALIZING;
data.mSingletonInstance->initSingleton();
data.mInitState = INITIALIZED;
}
#endif #endif

View File

@@ -43,9 +43,6 @@
#include <winnls.h> // for WideCharToMultiByte #include <winnls.h> // for WideCharToMultiByte
#endif #endif
LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format");
std::string ll_safe_string(const char* in) std::string ll_safe_string(const char* in)
{ {
if(in) return std::string(in); if(in) return std::string(in);
@@ -1199,7 +1196,7 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
template<> template<>
S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
{ {
LLFastTimer ft(FT_STRING_FORMAT); LLFastTimer ft(LLFastTimer::FT_STRING_FORMAT);
S32 res = 0; S32 res = 0;
std::string output; std::string output;
@@ -1272,7 +1269,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
template<> template<>
S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
{ {
LLFastTimer ft(FT_STRING_FORMAT); LLFastTimer ft(LLFastTimer::FT_STRING_FORMAT);
S32 res = 0; S32 res = 0;
if (!substitutions.isMap()) if (!substitutions.isMap())

View File

@@ -198,7 +198,7 @@ U64 get_clock_count()
return clock_count.QuadPart - offset; return clock_count.QuadPart - offset;
} }
F64 calc_clock_frequency(void) F64 calc_clock_frequency(U32 uiMeasureMSecs)
{ {
__int64 freq; __int64 freq;
QueryPerformanceFrequency((LARGE_INTEGER *) &freq); QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
@@ -209,7 +209,7 @@ F64 calc_clock_frequency(void)
#if LL_LINUX || LL_DARWIN || LL_SOLARIS #if LL_LINUX || LL_DARWIN || LL_SOLARIS
// Both Linux and Mac use gettimeofday for accurate time // Both Linux and Mac use gettimeofday for accurate time
F64 calc_clock_frequency(void) F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
{ {
return 1000000.0; // microseconds, so 1 Mhz. return 1000000.0; // microseconds, so 1 Mhz.
} }
@@ -226,7 +226,7 @@ U64 get_clock_count()
void update_clock_frequencies() void update_clock_frequencies()
{ {
gClockFrequency = calc_clock_frequency(); gClockFrequency = calc_clock_frequency(50U);
gClockFrequencyInv = 1.0/gClockFrequency; gClockFrequencyInv = 1.0/gClockFrequency;
gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC; gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
} }

View File

@@ -113,7 +113,7 @@ public:
// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work. // Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work.
// //
LL_COMMON_API U64 get_clock_count(); LL_COMMON_API U64 get_clock_count();
LL_COMMON_API F64 calc_clock_frequency(); LL_COMMON_API F64 calc_clock_frequency(U32 msecs);
LL_COMMON_API void update_clock_frequencies(); LL_COMMON_API void update_clock_frequencies();
// Sleep for milliseconds // Sleep for milliseconds

View File

@@ -0,0 +1,44 @@
/**
* @file llversionserver.h
* @brief
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLVERSIONSERVER_H
#define LL_LLVERSIONSERVER_H
const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 27;
const S32 LL_VERSION_PATCH = 0;
const S32 LL_VERSION_BUILD = 132845;
const char * const LL_CHANNEL = "Second Life Server";
#endif

View File

@@ -36,13 +36,12 @@
const S32 LL_VERSION_MAJOR = 1; const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 6; const S32 LL_VERSION_MINOR = 6;
const S32 LL_VERSION_PATCH = 0; const S32 LL_VERSION_PATCH = 0;
const S32 LL_VERSION_BUILD = ${vBUILD}; const S32 LL_VERSION_BUILD = 3;
const char * const LL_CHANNEL = "${VIEWER_CHANNEL}"; const char * const LL_CHANNEL = "Singularity";
#if LL_DARWIN #if LL_DARWIN
const char * const LL_VERSION_BUNDLE_ID = "org.singularityviewer.singularity"; const char * const LL_VERSION_BUNDLE_ID = "com.secondlife.singularity.viewer";
#endif #endif
#endif #endif

View File

@@ -347,7 +347,7 @@ bool LLCrashLogger::sendCrashLogs()
bool sent = false; bool sent = false;
// *TODO: Translate //*TODO: Translate
if(mCrashHost != "") if(mCrashHost != "")
{ {
sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5); sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5);

View File

@@ -170,7 +170,7 @@ void LLImageBase::deleteData()
// virtual // virtual
U8* LLImageBase::allocateData(S32 size) U8* LLImageBase::allocateData(S32 size)
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
if (size < 0) if (size < 0)
{ {
@@ -209,7 +209,7 @@ U8* LLImageBase::reallocateData(S32 size)
if(mData && (mDataSize == size)) if(mData && (mDataSize == size))
return mData; return mData;
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
U8 *new_datap = (U8*)ALLOCATE_MEM(sPrivatePoolp, size); U8 *new_datap = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
if (!new_datap) if (!new_datap)
{ {
@@ -457,7 +457,7 @@ void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
// Reverses the order of the rows in the image // Reverses the order of the rows in the image
void LLImageRaw::verticalFlip() void LLImageRaw::verticalFlip()
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
S32 row_bytes = getWidth() * getComponents(); S32 row_bytes = getWidth() * getComponents();
U8* line_buffer = new (std::nothrow) U8[row_bytes]; U8* line_buffer = new (std::nothrow) U8[row_bytes];
if (!line_buffer ) if (!line_buffer )
@@ -595,7 +595,7 @@ void LLImageRaw::composite( LLImageRaw* src )
// Src and dst can be any size. Src has 4 components. Dst has 3 components. // Src and dst can be any size. Src has 4 components. Dst has 3 components.
void LLImageRaw::compositeScaled4onto3(LLImageRaw* src) void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
llinfos << "compositeScaled4onto3" << llendl; llinfos << "compositeScaled4onto3" << llendl;
LLImageRaw* dst = this; // Just for clarity. LLImageRaw* dst = this; // Just for clarity.
@@ -846,7 +846,7 @@ void LLImageRaw::copyUnscaled3onto4( LLImageRaw* src )
// Src and dst can be any size. Src and dst have same number of components. // Src and dst can be any size. Src and dst have same number of components.
void LLImageRaw::copyScaled( LLImageRaw* src ) void LLImageRaw::copyScaled( LLImageRaw* src )
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
LLImageRaw* dst = this; // Just for clarity. LLImageRaw* dst = this; // Just for clarity.
llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) ); llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) );
@@ -932,7 +932,7 @@ BOOL LLImageRaw::scaleDownWithoutBlending( S32 new_width, S32 new_height)
BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data ) BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) ); llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) );
S32 old_width = getWidth(); S32 old_width = getWidth();

View File

@@ -35,7 +35,7 @@
#include "lluuid.h" #include "lluuid.h"
#include "llstring.h" #include "llstring.h"
#include "llmemtype.h" #include "llmemory.h"
#include "llthread.h" #include "llthread.h"
#include "aithreadsafe.h" #include "aithreadsafe.h"
@@ -162,7 +162,7 @@ private:
static LLPrivateMemoryPool* sPrivatePoolp ; static LLPrivateMemoryPool* sPrivatePoolp ;
public: public:
LLMemType::DeclareMemType& mMemType; // debug S16 mMemType; // debug
}; };
// Raw representation of an image (used for textures, and other uncompressed formats // Raw representation of an image (used for textures, and other uncompressed formats
@@ -330,7 +330,7 @@ public:
virtual void setLastError(const std::string& message, const std::string& filename = std::string()); virtual void setLastError(const std::string& message, const std::string& filename = std::string());
protected: protected:
BOOL copyData(U8 *data, S32 size); BOOL copyData(U8 *data, S32 size); // calls updateData()
protected: protected:
S8 mCodec; S8 mCodec;

View File

@@ -297,7 +297,7 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
// Returns TRUE to mean done, whether successful or not. // Returns TRUE to mean done, whether successful or not.
BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count ) BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
BOOL res = TRUE; BOOL res = TRUE;
@@ -347,7 +347,7 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time)
BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time) BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time)
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
resetLastError(); resetLastError();
BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible); BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible);
if (!mLastError.empty()) if (!mLastError.empty())
@@ -529,7 +529,7 @@ BOOL LLImageJ2C::loadAndValidate(const std::string &filename)
BOOL LLImageJ2C::validate(U8 *data, U32 file_size) BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
{ {
LLMemType mt1(mMemType); LLMemType mt1((LLMemType::EMemType)mMemType);
resetLastError(); resetLastError();

View File

@@ -168,7 +168,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
// dereference the array. // dereference the array.
if(!image || !image->numcomps) if(!image || !image->numcomps)
{ {
llwarns << "failed to decode image!" << llendl; llwarns << "ERROR -> decodeImpl: failed to decode image!" << llendl;
if (image) if (image)
{ {
opj_image_destroy(image); opj_image_destroy(image);
@@ -183,7 +183,6 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
{ {
if (image->comps[i].factor != base.getRawDiscardLevel()) if (image->comps[i].factor != base.getRawDiscardLevel())
{ {
llwarns << "Expected discard level not reached!" << llendl;
// if we didn't get the discard level we're expecting, fail // if we didn't get the discard level we're expecting, fail
opj_image_destroy(image); opj_image_destroy(image);
base.decodeFailed(); base.decodeFailed();

View File

@@ -1,25 +1,31 @@
/** /**
* @file lleconomy.cpp * @file lleconomy.cpp
* *
* $LicenseInfo:firstyear=2002&license=viewerlgpl$ * $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code * Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc. * The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* *
* This library is free software; you can redistribute it and/or * There are special exceptions to the terms and conditions of the GPL as
* modify it under the terms of the GNU Lesser General Public * it is applied to this Source Code. View the full text of the exception
* License as published by the Free Software Foundation; * in the file doc/FLOSS-exception.txt in this software distribution, or
* version 2.1 of the License only. * online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* *
* This library is distributed in the hope that it will be useful, * By copying, modifying or distributing this software, you acknowledge
* but WITHOUT ANY WARRANTY; without even the implied warranty of * that you have read and understood your obligations described above,
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * and agree to abide by those obligations.
* Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* License along with this library; if not, write to the Free Software * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * COMPLETENESS OR PERFORMANCE.
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$ * $/LicenseInfo$
*/ */
@@ -48,31 +54,6 @@ LLGlobalEconomy::LLGlobalEconomy()
LLGlobalEconomy::~LLGlobalEconomy() LLGlobalEconomy::~LLGlobalEconomy()
{ } { }
void LLGlobalEconomy::addObserver(LLEconomyObserver* observer)
{
mObservers.push_back(observer);
}
void LLGlobalEconomy::removeObserver(LLEconomyObserver* observer)
{
std::list<LLEconomyObserver*>::iterator it =
std::find(mObservers.begin(), mObservers.end(), observer);
if (it != mObservers.end())
{
mObservers.erase(it);
}
}
void LLGlobalEconomy::notifyObservers()
{
for (std::list<LLEconomyObserver*>::iterator it = mObservers.begin();
it != mObservers.end();
++it)
{
(*it)->onEconomyDataChange();
}
}
// static // static
void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data) void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data)
{ {
@@ -113,8 +94,6 @@ void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy*
econ_data->setTeleportPriceExponent(f); econ_data->setTeleportPriceExponent(f);
msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceGroupCreate, i); msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceGroupCreate, i);
econ_data->setPriceGroupCreate(i); econ_data->setPriceGroupCreate(i);
econ_data->notifyObservers();
} }
S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const

View File

@@ -1,25 +1,31 @@
/** /**
* @file lleconomy.h * @file lleconomy.h
* *
* $LicenseInfo:firstyear=2002&license=viewerlgpl$ * $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code * Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc. * The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* *
* This library is free software; you can redistribute it and/or * There are special exceptions to the terms and conditions of the GPL as
* modify it under the terms of the GNU Lesser General Public * it is applied to this Source Code. View the full text of the exception
* License as published by the Free Software Foundation; * in the file doc/FLOSS-exception.txt in this software distribution, or
* version 2.1 of the License only. * online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* *
* This library is distributed in the hope that it will be useful, * By copying, modifying or distributing this software, you acknowledge
* but WITHOUT ANY WARRANTY; without even the implied warranty of * that you have read and understood your obligations described above,
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * and agree to abide by those obligations.
* Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* License along with this library; if not, write to the Free Software * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * COMPLETENESS OR PERFORMANCE.
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$ * $/LicenseInfo$
*/ */
@@ -31,16 +37,6 @@
class LLMessageSystem; class LLMessageSystem;
class LLVector3; class LLVector3;
/**
* Register an observer to be notified of economy data updates coming from server.
*/
class LLEconomyObserver
{
public:
virtual ~LLEconomyObserver() {}
virtual void onEconomyDataChange() = 0;
};
class LLGlobalEconomy class LLGlobalEconomy
{ {
public: public:
@@ -56,10 +52,6 @@ public:
virtual void print(); virtual void print();
void addObserver(LLEconomyObserver* observer);
void removeObserver(LLEconomyObserver* observer);
void notifyObservers();
static void processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data); static void processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data);
S32 calculateTeleportCost(F32 distance) const; S32 calculateTeleportCost(F32 distance) const;
@@ -103,8 +95,6 @@ private:
S32 mTeleportMinPrice; S32 mTeleportMinPrice;
F32 mTeleportPriceExponent; F32 mTeleportPriceExponent;
S32 mPriceGroupCreate; S32 mPriceGroupCreate;
std::list<LLEconomyObserver*> mObservers;
}; };

View File

@@ -34,7 +34,6 @@
#include "llinventory.h" #include "llinventory.h"
#include "lldbstrings.h" #include "lldbstrings.h"
#include "llfasttimer.h"
#include "llinventorydefines.h" #include "llinventorydefines.h"
#include "llxorcipher.h" #include "llxorcipher.h"
#include "llsd.h" #include "llsd.h"
@@ -1056,11 +1055,8 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const
sd[INV_CREATION_DATE_LABEL] = (S32) mCreationDate; sd[INV_CREATION_DATE_LABEL] = (S32) mCreationDate;
} }
LLFastTimer::DeclareTimer FTM_INVENTORY_SD_DESERIALIZE("Inventory SD Deserialize");
bool LLInventoryItem::fromLLSD(const LLSD& sd) bool LLInventoryItem::fromLLSD(const LLSD& sd)
{ {
LLFastTimer _(FTM_INVENTORY_SD_DESERIALIZE);
mInventoryType = LLInventoryType::IT_NONE; mInventoryType = LLInventoryType::IT_NONE;
mAssetUUID.setNull(); mAssetUUID.setNull();
std::string w; std::string w;

View File

@@ -67,6 +67,7 @@ const F32 PARCEL_PASS_HOURS_DEFAULT = 1.f;
// Number of "chunks" in which parcel overlay data is sent // Number of "chunks" in which parcel overlay data is sent
// Chunk 0 = southern rows, entire width // Chunk 0 = southern rows, entire width
// NOTE: NOT USABLE FOR VAR SIZED REGIONS!
const S32 PARCEL_OVERLAY_CHUNKS = 4; const S32 PARCEL_OVERLAY_CHUNKS = 4;
// Bottom three bits are a color index for the land overlay // Bottom three bits are a color index for the land overlay

View File

@@ -30,8 +30,8 @@
* $/LicenseInfo$ * $/LicenseInfo$
*/ */
#include "linden_common.h"
#include <iostream> #include <iostream>
#include "linden_common.h"
#include "llsaleinfo.h" #include "llsaleinfo.h"

View File

@@ -531,13 +531,13 @@ inline void ll_remove_outliers(std::vector<VEC_TYPE>& data, F32 k)
i++; i++;
} }
S32 j = (S32)data.size()-1; S32 j = data.size()-1;
while (j > 0 && data[j] > max) while (j > 0 && data[j] > max)
{ {
j--; j--;
} }
if (j < (S32)data.size()-1) if (j < data.size()-1)
{ {
data.erase(data.begin()+j, data.end()); data.erase(data.begin()+j, data.end());
} }

View File

@@ -90,8 +90,8 @@ public:
typedef LLOctreeTraveler<T> oct_traveler; typedef LLOctreeTraveler<T> oct_traveler;
typedef LLTreeTraveler<T> tree_traveler; typedef LLTreeTraveler<T> tree_traveler;
typedef typename std::set<LLPointer<T> > element_list; typedef typename std::set<LLPointer<T> > element_list;
typedef typename element_list::iterator element_iter; typedef typename std::set<LLPointer<T> >::iterator element_iter;
typedef typename element_list::const_iterator const_element_iter; typedef typename std::set<LLPointer<T> >::const_iterator const_element_iter;
typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter; typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
typedef typename std::vector<LLOctreeNode<T>* > child_list; typedef typename std::vector<LLOctreeNode<T>* > child_list;
typedef LLTreeNode<T> BaseType; typedef LLTreeNode<T> BaseType;
@@ -124,8 +124,6 @@ public:
mOctant = ((oct_node*) mParent)->getOctant(mCenter); mOctant = ((oct_node*) mParent)->getOctant(mCenter);
} }
mElementCount = 0;
clearChildren(); clearChildren();
} }
@@ -231,11 +229,11 @@ public:
void accept(oct_traveler* visitor) { visitor->visit(this); } void accept(oct_traveler* visitor) { visitor->visit(this); }
virtual bool isLeaf() const { return mChild.empty(); } virtual bool isLeaf() const { return mChild.empty(); }
U32 getElementCount() const { return mElementCount; } U32 getElementCount() const { return mData.size(); }
element_list& getData() { return mData; } element_list& getData() { return mData; }
const element_list& getData() const { return mData; } const element_list& getData() const { return mData; }
U32 getChildCount() const { return mChildCount; } U32 getChildCount() const { return mChild.size(); }
oct_node* getChild(U32 index) { return mChild[index]; } oct_node* getChild(U32 index) { return mChild[index]; }
const oct_node* getChild(U32 index) const { return mChild[index]; } const oct_node* getChild(U32 index) const { return mChild[index]; }
child_list& getChildren() { return mChild; } child_list& getChildren() { return mChild; }
@@ -323,8 +321,6 @@ public:
mData.insert(data); mData.insert(data);
BaseType::insert(data); BaseType::insert(data);
mElementCount = mData.size();
return true; return true;
} }
else else
@@ -360,8 +356,6 @@ public:
{ {
mData.insert(data); mData.insert(data);
BaseType::insert(data); BaseType::insert(data);
mElementCount = mData.size();
return true; return true;
} }
@@ -415,7 +409,6 @@ public:
if (mData.find(data) != mData.end()) if (mData.find(data) != mData.end())
{ //we have data { //we have data
mData.erase(data); mData.erase(data);
mElementCount = mData.size();
notifyRemoval(data); notifyRemoval(data);
checkAlive(); checkAlive();
return true; return true;
@@ -453,7 +446,6 @@ public:
if (mData.find(data) != mData.end()) if (mData.find(data) != mData.end())
{ {
mData.erase(data); mData.erase(data);
mElementCount = mData.size();
notifyRemoval(data); notifyRemoval(data);
llwarns << "FOUND!" << llendl; llwarns << "FOUND!" << llendl;
checkAlive(); checkAlive();
@@ -470,7 +462,7 @@ public:
void clearChildren() void clearChildren()
{ {
mChild.clear(); mChild.clear();
mChildCount = 0;
U32* foo = (U32*) mChildMap; U32* foo = (U32*) mChildMap;
foo[0] = foo[1] = 0xFFFFFFFF; foo[0] = foo[1] = 0xFFFFFFFF;
} }
@@ -530,10 +522,9 @@ public:
} }
#endif #endif
mChildMap[child->getOctant()] = mChildCount; mChildMap[child->getOctant()] = (U8) mChild.size();
mChild.push_back(child); mChild.push_back(child);
++mChildCount;
child->setParent(this); child->setParent(this);
if (!silent) if (!silent)
@@ -562,13 +553,12 @@ public:
delete mChild[index]; delete mChild[index];
} }
mChild.erase(mChild.begin() + index); mChild.erase(mChild.begin() + index);
--mChildCount;
//rebuild child map //rebuild child map
U32* foo = (U32*) mChildMap; U32* foo = (U32*) mChildMap;
foo[0] = foo[1] = 0xFFFFFFFF; foo[0] = foo[1] = 0xFFFFFFFF;
for (U32 i = 0; i < mChildCount; ++i) for (U32 i = 0; i < mChild.size(); ++i)
{ {
mChildMap[mChild[i]->getOctant()] = i; mChildMap[mChild[i]->getOctant()] = i;
} }
@@ -621,10 +611,8 @@ protected:
child_list mChild; child_list mChild;
U8 mChildMap[8]; U8 mChildMap[8];
U32 mChildCount;
element_list mData; element_list mData;
U32 mElementCount;
}; };
@@ -703,7 +691,7 @@ public:
if (lt != 0x7) if (lt != 0x7)
{ {
//OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << llendl; OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << llendl;
return false; return false;
} }

View File

@@ -2079,7 +2079,6 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
mFaceMask = 0x0; mFaceMask = 0x0;
mDetail = detail; mDetail = detail;
mSculptLevel = -2; mSculptLevel = -2;
mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims
mIsMeshAssetLoaded = FALSE; mIsMeshAssetLoaded = FALSE;
mLODScaleBias.setVec(1,1,1); mLODScaleBias.setVec(1,1,1);
mHullPoints = NULL; mHullPoints = NULL;
@@ -2183,7 +2182,7 @@ BOOL LLVolume::generate()
mLODScaleBias.setVec(0.6f, 0.6f, 0.6f); mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
} }
// ******************************************************************** //********************************************************************
//debug info, to be removed //debug info, to be removed
if((U32)(mPathp->mPath.size() * mProfilep->mProfile.size()) > (1u << 20)) if((U32)(mPathp->mPath.size() * mProfilep->mProfile.size()) > (1u << 20))
{ {
@@ -2195,7 +2194,7 @@ BOOL LLVolume::generate()
llerrs << "LLVolume corrupted!" << llendl ; llerrs << "LLVolume corrupted!" << llendl ;
} }
// ******************************************************************** //********************************************************************
BOOL regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split); BOOL regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split);
BOOL regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split); BOOL regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split);
@@ -2205,7 +2204,7 @@ BOOL LLVolume::generate()
S32 sizeS = mPathp->mPath.size(); S32 sizeS = mPathp->mPath.size();
S32 sizeT = mProfilep->mProfile.size(); S32 sizeT = mProfilep->mProfile.size();
// ******************************************************************** //********************************************************************
//debug info, to be removed //debug info, to be removed
if((U32)(sizeS * sizeT) > (1u << 20)) if((U32)(sizeS * sizeT) > (1u << 20))
{ {
@@ -2218,7 +2217,7 @@ BOOL LLVolume::generate()
llerrs << "LLVolume corrupted!" << llendl ; llerrs << "LLVolume corrupted!" << llendl ;
} }
// ******************************************************************** //********************************************************************
sNumMeshPoints -= mMesh.size(); sNumMeshPoints -= mMesh.size();
mMesh.resize(sizeT * sizeS); mMesh.resize(sizeT * sizeS);
@@ -3153,8 +3152,6 @@ void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components,
{ {
F32 area = sculptGetSurfaceArea(); F32 area = sculptGetSurfaceArea();
mSurfaceArea = area;
const F32 SCULPT_MAX_AREA = 384.f; const F32 SCULPT_MAX_AREA = 384.f;
if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA) if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA)
@@ -4626,77 +4623,13 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
genBinormals(i); genBinormals(i);
} }
if (isUnique())
{ //don't bother with an octree for flexi volumes
U32 tri_count = face.mNumIndices/3;
for (U32 j = 0; j < tri_count; ++j)
{
U16 idx0 = face.mIndices[j*3+0];
U16 idx1 = face.mIndices[j*3+1];
U16 idx2 = face.mIndices[j*3+2];
const LLVector4a& v0 = face.mPositions[idx0];
const LLVector4a& v1 = face.mPositions[idx1];
const LLVector4a& v2 = face.mPositions[idx2];
F32 a,b,t;
if (LLTriangleRayIntersect(v0, v1, v2,
start, dir, a, b, t))
{
if ((t >= 0.f) && // if hit is after start
(t <= 1.f) && // and before end
(t < closest_t)) // and this hit is closer
{
closest_t = t;
hit_face = i;
if (intersection != NULL)
{
LLVector4a intersect = dir;
intersect.mul(closest_t);
intersect.add(start);
intersection->set(intersect.getF32ptr());
}
if (tex_coord != NULL)
{
LLVector2* tc = (LLVector2*) face.mTexCoords;
*tex_coord = ((1.f - a - b) * tc[idx0] +
a * tc[idx1] +
b * tc[idx2]);
}
if (normal!= NULL)
{
LLVector4* norm = (LLVector4*) face.mNormals;
*normal = ((1.f - a - b) * LLVector3(norm[idx0]) +
a * LLVector3(norm[idx1]) +
b * LLVector3(norm[idx2]));
}
if (bi_normal != NULL)
{
LLVector4* binormal = (LLVector4*) face.mBinormals;
*bi_normal = ((1.f - a - b) * LLVector3(binormal[idx0]) +
a * LLVector3(binormal[idx1]) +
b * LLVector3(binormal[idx2]));
}
}
}
}
}
else
{
if (!face.mOctree) if (!face.mOctree)
{ {
face.createOctree(); face.createOctree();
} }
//LLVector4a* p = (LLVector4a*) face.mPositions;
LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, bi_normal); LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, bi_normal);
intersect.traverse(face.mOctree); intersect.traverse(face.mOctree);
if (intersect.mHitFace) if (intersect.mHitFace)
@@ -4705,7 +4638,6 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
} }
} }
} }
}
return hit_face; return hit_face;

View File

@@ -971,7 +971,6 @@ public:
S32 getNumFaces() const; S32 getNumFaces() const;
S32 getNumVolumeFaces() const { return mVolumeFaces.size(); } S32 getNumVolumeFaces() const { return mVolumeFaces.size(); }
F32 getDetail() const { return mDetail; } F32 getDetail() const { return mDetail; }
F32 getSurfaceArea() const { return mSurfaceArea; }
const LLVolumeParams& getParams() const { return mParams; } const LLVolumeParams& getParams() const { return mParams; }
LLVolumeParams getCopyOfParams() const { return mParams; } LLVolumeParams getCopyOfParams() const { return mParams; }
const LLProfile& getProfile() const { return *mProfilep; } const LLProfile& getProfile() const { return *mProfilep; }
@@ -1074,7 +1073,6 @@ public:
BOOL mUnique; BOOL mUnique;
F32 mDetail; F32 mDetail;
S32 mSculptLevel; S32 mSculptLevel;
F32 mSurfaceArea; //unscaled surface area
BOOL mIsMeshAssetLoaded; BOOL mIsMeshAssetLoaded;
LLVolumeParams mParams; LLVolumeParams mParams;

View File

@@ -57,8 +57,8 @@ public:
{ {
U8 mV[LENGTHOFCOLOR4U]; U8 mV[LENGTHOFCOLOR4U];
U32 mAll; U32 mAll;
//LLColor4* mSources; LLColor4* mSources;
//LLColor4U* mSourcesU; LLColor4U* mSourcesU;
}; };

Some files were not shown because too many files have changed in this diff Show More