Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4cfc10609 | ||
|
|
0bdf54e6d9 | ||
|
|
10af9d64f2 | ||
|
|
a76b0cb9da | ||
|
|
0450358df3 | ||
|
|
a632010b95 | ||
|
|
a1038f3972 | ||
|
|
93f6ea1a07 | ||
|
|
d721fd65b3 | ||
|
|
e207f2e025 | ||
|
|
be37d9b5a9 | ||
|
|
e39327c017 | ||
|
|
edbc22961e | ||
|
|
3250725aae | ||
|
|
803011582c | ||
|
|
2206f68129 | ||
|
|
6388324552 | ||
|
|
89f35779b4 | ||
|
|
ad1e3f1d2d | ||
|
|
9477a5f32f | ||
|
|
8e71cf8e6e | ||
|
|
ea7396d2cf | ||
|
|
5c7debecc8 | ||
|
|
c6d6eed7d3 | ||
|
|
39f8deb498 | ||
|
|
665569dfc7 | ||
|
|
19f72cf967 | ||
|
|
a4363de59e | ||
|
|
c3aa321d51 | ||
|
|
9b268c3654 | ||
|
|
925c42294f | ||
|
|
23811df3cf | ||
|
|
f7cc9c4d33 | ||
|
|
66ccff964b | ||
|
|
0c82b8bd3a | ||
|
|
4e8b69206c | ||
|
|
0a7fc0e65e | ||
|
|
787e03e821 | ||
|
|
f381b0c2dd | ||
|
|
b8744b9e6a | ||
|
|
59ee1a1041 | ||
|
|
88d0983e37 | ||
|
|
3680e022fc | ||
|
|
a642b725e0 | ||
|
|
dbc3a8c65f | ||
|
|
86cafba0df | ||
|
|
60ded2ad14 | ||
|
|
fa560a7081 | ||
|
|
5023f23cc4 | ||
|
|
0fef1407e3 | ||
|
|
a09c505fa0 | ||
|
|
023909b8e0 | ||
|
|
60081d7ed5 | ||
|
|
0b5235d054 | ||
|
|
071554acae | ||
|
|
f05343dec1 | ||
|
|
a403cef605 | ||
|
|
9b4b234c12 | ||
|
|
42518dbfaa | ||
|
|
4e700a5808 | ||
|
|
647af6c15a | ||
|
|
b91ba258e1 | ||
|
|
fe579d6e4b | ||
|
|
6d3044b448 | ||
|
|
db8564bb3c | ||
|
|
28f73d1037 | ||
|
|
dd9196efac | ||
|
|
c542c7bdbb | ||
|
|
f8aad35775 | ||
|
|
657c8e8729 | ||
|
|
a451a8f566 | ||
|
|
0e5b1a8fa7 | ||
|
|
e922207797 | ||
|
|
e447bd98a5 | ||
|
|
a0e9e8d4b2 | ||
|
|
09817403ff | ||
|
|
8f6ef05429 | ||
|
|
4cbf259ab3 | ||
|
|
042622b5db | ||
|
|
eff72e15cd | ||
|
|
aacd33ada7 | ||
|
|
fe00eed401 | ||
|
|
000d00579b | ||
|
|
63dd39ea51 | ||
|
|
87cb88f2f7 | ||
|
|
cb3e8215d6 | ||
|
|
a3900a4830 | ||
|
|
0f9139305a | ||
|
|
0a8504fb3c | ||
|
|
d709200fc0 | ||
|
|
77df33ebf6 | ||
|
|
8064d561f4 | ||
|
|
aaf1e4d21b | ||
|
|
9f9a4cbaaf | ||
|
|
e7806f9dac | ||
|
|
d7eb4fc656 | ||
|
|
d10724ae6f | ||
|
|
0a3116422a | ||
|
|
0a59b3bb54 | ||
|
|
e0b21b08fa | ||
|
|
d4591828c8 | ||
|
|
cad0597524 | ||
|
|
e10581e319 | ||
|
|
f7b2139e81 | ||
|
|
f55452d6dd | ||
|
|
b0c675498c | ||
|
|
f16da13198 | ||
|
|
0827b3fce9 | ||
|
|
a3a7f72b7c | ||
|
|
acf378e9f2 | ||
|
|
a39209cd37 | ||
|
|
2ef5673c18 | ||
|
|
4c10ae1a4c | ||
|
|
66c3f282de | ||
|
|
fec75d9436 | ||
|
|
ef68f43687 | ||
|
|
13780fae2a | ||
|
|
c294456e2a | ||
|
|
6410fb3209 | ||
|
|
af22591cc0 | ||
|
|
b0267e4631 | ||
|
|
69a2d0bc5e | ||
|
|
108b9211ff | ||
|
|
97f2474956 | ||
|
|
b4c68d56b6 | ||
|
|
03926c2374 | ||
|
|
787f91085d | ||
|
|
1b37bd5df6 | ||
|
|
ea0a5c521f | ||
|
|
6ba69adccc | ||
|
|
481361f714 | ||
|
|
e1e34623cb | ||
|
|
78409deaa7 | ||
|
|
787a18d3a8 | ||
|
|
548f9d0a88 | ||
|
|
8450386330 | ||
|
|
8adf2965fd | ||
|
|
e0bf075f7f | ||
|
|
8a49d4ef94 | ||
|
|
d85e1afd7e | ||
|
|
c96d1c3aa5 | ||
|
|
1cbc825461 | ||
|
|
f0d6200e8e | ||
|
|
ad24fcca39 | ||
|
|
06837a2a77 | ||
|
|
47537ba764 | ||
|
|
cd51a6f795 | ||
|
|
3d40cd86ac | ||
|
|
2617df44db | ||
|
|
01a8e11ef3 | ||
|
|
5a455eac91 | ||
|
|
e18e051bfc | ||
|
|
bf44d3f98e | ||
|
|
b6fa215c4e | ||
|
|
726dc02aaa | ||
|
|
9ee0c5acf3 | ||
|
|
5e0140ba81 | ||
|
|
a690c970e6 | ||
|
|
db28c382e0 | ||
|
|
1747d529d8 | ||
|
|
27763d8390 | ||
|
|
ef79781c9a | ||
|
|
03c0509a6c | ||
|
|
0340778775 | ||
|
|
6fecfad2a2 | ||
|
|
e9fa5e6f3d | ||
|
|
b0ccec2b62 | ||
|
|
b755bcd495 | ||
|
|
315552d1e0 | ||
|
|
e7b2972fc8 | ||
|
|
e1675f6bd1 | ||
|
|
2044b88015 | ||
|
|
77a21f95e5 | ||
|
|
a4e05eea9d | ||
|
|
373cb7fb39 | ||
|
|
2e240e70d6 | ||
|
|
bdcb5dc877 | ||
|
|
e7a59c1031 | ||
|
|
8ae25e4812 | ||
|
|
3548521bef | ||
|
|
d15ed9ed34 | ||
|
|
655365247c | ||
|
|
eaecc9a4d0 | ||
|
|
dfe33963bb | ||
|
|
661f5ede04 | ||
|
|
5858f2822c | ||
|
|
379ecccec2 | ||
|
|
e69c605c4f | ||
|
|
f6711e6b4f | ||
|
|
e327e7a15e | ||
|
|
9973dfd1c7 | ||
|
|
b045d9bd96 | ||
|
|
64181e19b8 | ||
|
|
ef927134f0 | ||
|
|
f9398a3ab1 | ||
|
|
450402d83d | ||
|
|
9f7e9299bd | ||
|
|
681901cb7c | ||
|
|
a0c549534f | ||
|
|
9453c2c2e9 | ||
|
|
32c6365bf9 | ||
|
|
6c921ba340 | ||
|
|
f5818b7621 | ||
|
|
ac1a6101e1 | ||
|
|
7ff58abc17 | ||
|
|
c548aa3497 | ||
|
|
d0c782c7ae | ||
|
|
d9640ecc65 | ||
|
|
635c8d0f72 | ||
|
|
c9269c04be | ||
|
|
0c3bff3763 | ||
|
|
f06d9294a2 | ||
|
|
690338a122 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/installed.xml
|
||||
/indra/llcommon/llversionviewer.h
|
||||
/indra/build-*
|
||||
/indra/tools/vstool/obj/
|
||||
*.aps
|
||||
@@ -11,11 +12,13 @@
|
||||
/indra/viewer-*
|
||||
/indra/newview/vivox-runtime/
|
||||
/libraries/
|
||||
/lib/
|
||||
*.pyc
|
||||
*.orig
|
||||
*.rej
|
||||
*.bak
|
||||
*~
|
||||
*.DS_Store
|
||||
/LICENSES/
|
||||
/edited-files.txt
|
||||
qtcreator-build/
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
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.
|
||||
|
||||
@@ -26,7 +26,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
include(Variables)
|
||||
|
||||
# Load versions now. Install locations need them.
|
||||
include(Versions)
|
||||
include(BuildVersion)
|
||||
|
||||
include(UnixInstall)
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;ReleaseSSE2;Debug" CACHE S
|
||||
# Platform-specific compilation flags.
|
||||
|
||||
if (WINDOWS)
|
||||
# Remove default /Zm1000 flag that cmake inserts
|
||||
string (REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||
|
||||
# Don't build DLLs.
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
@@ -67,6 +70,10 @@ if (WINDOWS)
|
||||
/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)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE
|
||||
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
|
||||
@@ -113,7 +120,7 @@ if (WINDOWS)
|
||||
|
||||
endif (WINDOWS)
|
||||
|
||||
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math -frounding-math")
|
||||
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math")
|
||||
|
||||
if (LINUX)
|
||||
set(CMAKE_SKIP_RPATH TRUE)
|
||||
@@ -155,10 +162,10 @@ if (LINUX)
|
||||
add_definitions(-D_FORTIFY_SOURCE=2)
|
||||
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
|
||||
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
|
||||
|
||||
#Lets actualy get a numerical version of gxx's version
|
||||
|
||||
#Lets actually get a numerical version of gxx's 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
|
||||
if(${CXX_VERSION} GREATER 429)
|
||||
add_definitions(-Wno-parentheses)
|
||||
@@ -182,6 +189,8 @@ if (LINUX)
|
||||
-pthread
|
||||
)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
|
||||
|
||||
add_definitions(-DAPPID=secondlife)
|
||||
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.
|
||||
@@ -200,10 +209,10 @@ if (LINUX)
|
||||
if (NOT STANDALONE)
|
||||
set(MARCH_FLAG " -march=pentium4")
|
||||
endif (NOT STANDALONE)
|
||||
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 -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 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||
endif (${ARCH} STREQUAL "x86_64")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")
|
||||
|
||||
@@ -29,12 +29,12 @@ else (STANDALONE)
|
||||
)
|
||||
elseif (DARWIN)
|
||||
set(APR_LIBRARIES
|
||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.3.7.dylib
|
||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib
|
||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.dylib
|
||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.dylib
|
||||
)
|
||||
set(APRUTIL_LIBRARIES
|
||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.3.8.dylib
|
||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib
|
||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.dylib
|
||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.dylib
|
||||
)
|
||||
set(APRICONV_LIBRARIES iconv)
|
||||
else (WINDOWS)
|
||||
|
||||
@@ -10,7 +10,7 @@ if (STANDALONE)
|
||||
set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt)
|
||||
set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt)
|
||||
set(BOOST_REGEX_LIBRARY boost_regex-mt)
|
||||
set(BOOST_SYSTEM_LIBRARY boost_system-mt)
|
||||
set(BOOST_SYSTEM_LIBRARY boost_system-mt)
|
||||
else (STANDALONE)
|
||||
use_prebuilt_binary(boost)
|
||||
set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
|
||||
|
||||
@@ -1,23 +1,45 @@
|
||||
# -*- cmake -*-
|
||||
|
||||
function (build_version _target)
|
||||
# Read version components from the header file.
|
||||
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversion${_target}.h lines
|
||||
REGEX " LL_VERSION_")
|
||||
foreach(line ${lines})
|
||||
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
|
||||
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
|
||||
set(v${comp} "${value}")
|
||||
endforeach(line)
|
||||
# Read version components from the header file.
|
||||
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversionviewer.h.in lines
|
||||
REGEX " LL_VERSION_")
|
||||
foreach(line ${lines})
|
||||
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
|
||||
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
|
||||
set(v${comp} "${value}")
|
||||
endforeach(line)
|
||||
|
||||
# Compose the version.
|
||||
set(${_target}_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
|
||||
if (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
||||
message(STATUS "Version of ${_target} is ${${_target}_VERSION}")
|
||||
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]+$")
|
||||
execute_process(
|
||||
COMMAND git rev-list HEAD
|
||||
OUTPUT_VARIABLE GIT_REV_LIST_STR
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# Report version to caller.
|
||||
set(${_target}_VERSION "${${_target}_VERSION}" PARENT_SCOPE)
|
||||
endfunction (build_version)
|
||||
if(GIT_REV_LIST_STR)
|
||||
string(REPLACE "\n" ";" GIT_REV_LIST ${GIT_REV_LIST_STR})
|
||||
else()
|
||||
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)
|
||||
|
||||
@@ -19,5 +19,5 @@ else (STANDALONE)
|
||||
else (WINDOWS)
|
||||
set(CARES_LIBRARIES cares)
|
||||
endif (WINDOWS)
|
||||
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares)
|
||||
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares ${LIBS_PREBUILT_DIR}/include/ares )
|
||||
endif (STANDALONE)
|
||||
|
||||
@@ -79,7 +79,6 @@ set(cmake_SOURCE_FILES
|
||||
UI.cmake
|
||||
UnixInstall.cmake
|
||||
Variables.cmake
|
||||
Versions.cmake
|
||||
XmlRpcEpi.cmake
|
||||
ZLIB.cmake
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
if (VIEWER AND WINDOWS)
|
||||
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
|
||||
"$ENV{DXSDK_DIR}/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Include"
|
||||
@@ -25,7 +25,7 @@ if (VIEWER AND WINDOWS)
|
||||
|
||||
find_path(DIRECTX_LIBRARY_DIR dxguid.lib
|
||||
"$ENV{DXSDK_DIR}/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
|
||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"
|
||||
|
||||
@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
|
||||
use_prebuilt_binary(fmod)
|
||||
endif(INSTALL_PROPRIETARY)
|
||||
|
||||
find_library(FMOD_LIBRARY_RELEASE
|
||||
find_library(FMOD_LIBRARY
|
||||
NAMES fmod fmodvc fmod-3.75
|
||||
PATHS
|
||||
${ARCH_PREBUILT_DIRS_RELEASE}
|
||||
optimized ${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)
|
||||
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
|
||||
if (FMOD_SDK_DIR)
|
||||
|
||||
@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
|
||||
use_prebuilt_binary(fmodex)
|
||||
endif(INSTALL_PROPRIETARY)
|
||||
|
||||
find_library(FMODEX_LIBRARY_RELEASE
|
||||
find_library(FMODEX_LIBRARY
|
||||
NAMES fmodex fmodex_vc fmodexL_vc
|
||||
PATHS
|
||||
${ARCH_PREBUILT_DIRS_RELEASE}
|
||||
optimized ${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)
|
||||
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
|
||||
if (FMODEX_SDK_DIR)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# also defined, but not for general use are
|
||||
# JSONCPP_LIBRARY, where to find the jsoncpp library.
|
||||
|
||||
FIND_PATH(JSONCPP_INCLUDE_DIR jsoncpp/json.h
|
||||
FIND_PATH(JSONCPP_INCLUDE_DIR json/json.h
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# -*- cmake -*-
|
||||
include(Prebuilt)
|
||||
|
||||
if(WORD_SIZE EQUAL 64)
|
||||
set(DISABLE_TCMALLOC TRUE)
|
||||
endif()
|
||||
|
||||
if (STANDALONE)
|
||||
include(FindGooglePerfTools)
|
||||
else (STANDALONE)
|
||||
if (LINUX OR WINDOWS)
|
||||
if (LINUX OR WINDOWS AND NOT WORD_SIZE EQUAL 64)
|
||||
use_prebuilt_binary(google)
|
||||
endif (LINUX OR WINDOWS)
|
||||
if (WINDOWS)
|
||||
@@ -30,10 +34,6 @@ else ()
|
||||
set(USE_GOOGLE_PERFTOOLS OFF)
|
||||
endif ()
|
||||
|
||||
# XXX Disable temporarily, until we have compilation issues on 64-bit
|
||||
# Etch sorted.
|
||||
#set(USE_GOOGLE_PERFTOOLS OFF)
|
||||
|
||||
if (USE_GOOGLE_PERFTOOLS)
|
||||
set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
|
||||
include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})
|
||||
|
||||
@@ -14,6 +14,7 @@ endif (DARWIN)
|
||||
set(LLCOMMON_INCLUDE_DIRS
|
||||
${LIBS_OPEN_DIR}/cwdebug
|
||||
${LIBS_OPEN_DIR}/llcommon
|
||||
${APRUTIL_INCLUDE_DIR}
|
||||
${APR_INCLUDE_DIR}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ set(LLPLUGIN_INCLUDE_DIRS
|
||||
if (LINUX)
|
||||
# In order to support using ld.gold on linux, we need to explicitely
|
||||
# specify all libraries that llplugin uses.
|
||||
set(LLPLUGIN_LIBRARIES llplugin pthread)
|
||||
set(LLPLUGIN_LIBRARIES llplugin pthread)
|
||||
else (LINUX)
|
||||
set(LLPLUGIN_LIBRARIES llplugin)
|
||||
set(LLPLUGIN_LIBRARIES llplugin)
|
||||
endif (LINUX)
|
||||
|
||||
@@ -19,7 +19,6 @@ if (WINDOWS)
|
||||
[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.3\\InstallPath]
|
||||
|
||||
)
|
||||
elseif (EXISTS /etc/debian_version)
|
||||
# On Debian and Ubuntu, avoid Python 2.4 if possible.
|
||||
|
||||
@@ -38,6 +38,7 @@ else (STANDALONE)
|
||||
if (LINUX)
|
||||
set(UI_LIBRARIES
|
||||
atk-1.0
|
||||
X11
|
||||
gdk-x11-2.0
|
||||
gdk_pixbuf-2.0
|
||||
Xinerama
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
include(BuildVersion)
|
||||
|
||||
if(VIEWER)
|
||||
build_version(viewer)
|
||||
endif(VIEWER)
|
||||
|
||||
if(SERVER)
|
||||
build_version(server)
|
||||
endif(SERVER)
|
||||
@@ -67,12 +67,19 @@ elseif (LINUX)
|
||||
QtNetwork
|
||||
QtGui
|
||||
QtCore
|
||||
jscore
|
||||
crypto
|
||||
ssl
|
||||
# qgif
|
||||
# qjpeg
|
||||
jpeg
|
||||
fontconfig
|
||||
X11
|
||||
Xrender
|
||||
Xext
|
||||
GL
|
||||
)
|
||||
if (WORD_SIZE EQUAL 32)
|
||||
set(WEBKIT_PLUGIN_LIBRARIES ${WEBKIT_PLUGIN_LIBRARIES} jscore)
|
||||
endif (WORD_SIZE EQUAL 32)
|
||||
endif (STANDALONE)
|
||||
endif (WINDOWS)
|
||||
|
||||
@@ -171,7 +171,8 @@ void stop_recording_backtraces(void)
|
||||
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 backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
|
||||
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used class AIStateMachine.
|
||||
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine.
|
||||
channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities.
|
||||
|
||||
} // namespace dc
|
||||
} // namespace DEBUGCHANNELS
|
||||
|
||||
@@ -117,6 +117,7 @@ extern CWD_API channel_ct gtk;
|
||||
extern CWD_API channel_ct sdl;
|
||||
extern CWD_API channel_ct backtrace;
|
||||
extern CWD_API channel_ct statemachine;
|
||||
extern CWD_API channel_ct caps;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -47,18 +47,11 @@ target_link_libraries(linux-crash-logger
|
||||
${LLVFS_LIBRARIES}
|
||||
${LLXML_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
${LLUI_LIBRARIES}
|
||||
${LLVFS_LIBRARIES}
|
||||
${LLMATH_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
${UI_LIBRARIES}
|
||||
${DB_LIBRARIES}
|
||||
${XMLRPCEPI_LIBRARIES}
|
||||
${CURL_LIBRARIES}
|
||||
${APR_LIBRARIES}
|
||||
${APRUTIL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
rt
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
|
||||
@@ -239,12 +239,15 @@ protected:
|
||||
// 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
|
||||
// data for, or are playing back.
|
||||
public://Jay: IDGAF
|
||||
typedef std::map<LLUUID, LLAudioSource *> source_map;
|
||||
protected:
|
||||
typedef std::map<LLUUID, LLAudioData *> data_map;
|
||||
|
||||
|
||||
public://Jay: IDGAF
|
||||
source_map mAllSources;
|
||||
protected:
|
||||
data_map mAllData;
|
||||
|
||||
LLAudioChannel *mChannels[MAX_CHANNELS];
|
||||
|
||||
// Buffers needs to change into a different data structure, as the number of buffers
|
||||
@@ -351,6 +354,9 @@ public:
|
||||
protected:
|
||||
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
|
||||
public:
|
||||
const LLUUID &getOwnerID() { return mOwnerID; }
|
||||
protected:
|
||||
F32 mPriority;
|
||||
F32 mGain;
|
||||
bool mSourceMuted;
|
||||
|
||||
@@ -74,7 +74,9 @@ LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
|
||||
{
|
||||
// Number of milliseconds of audio to buffer for the audio card.
|
||||
// Must be larger than the usual Second Life frame stutter time.
|
||||
mSystem->setStreamBufferSize(200, FMOD_TIMEUNIT_MS);
|
||||
const U32 buffer_seconds = 5; //sec
|
||||
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
|
||||
// parameters. In this case we want a network buffer of 16k, we want it
|
||||
@@ -372,7 +374,7 @@ LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, con
|
||||
exinfo.cbsize = sizeof(exinfo);
|
||||
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG; //Hint to speed up loading.
|
||||
|
||||
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING, &exinfo, &mInternetStream);
|
||||
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, &exinfo, &mInternetStream);
|
||||
|
||||
if (result!= FMOD_OK)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,9 @@ include_directories(
|
||||
set(llcommon_SOURCE_FILES
|
||||
aiframetimer.cpp
|
||||
imageids.cpp
|
||||
indra_constants.cpp
|
||||
indra_constants.cpp
|
||||
llallocator.cpp
|
||||
llallocator_heap_profile.cpp
|
||||
llapp.cpp
|
||||
llapr.cpp
|
||||
llaprpool.cpp
|
||||
@@ -27,14 +29,21 @@ set(llcommon_SOURCE_FILES
|
||||
llbase64.cpp
|
||||
llcommon.cpp
|
||||
llcommonutils.cpp
|
||||
llcoros.cpp
|
||||
llcrc.cpp
|
||||
llcriticaldamp.cpp
|
||||
llcursortypes.cpp
|
||||
lldate.cpp
|
||||
lldependencies.cpp
|
||||
lldictionary.cpp
|
||||
llerror.cpp
|
||||
llerrorthread.cpp
|
||||
llevent.cpp
|
||||
lleventapi.cpp
|
||||
lleventcoro.cpp
|
||||
lleventdispatcher.cpp
|
||||
lleventfilter.cpp
|
||||
llevents.cpp
|
||||
lleventtimer.cpp
|
||||
llfasttimer_class.cpp
|
||||
llfile.cpp
|
||||
@@ -52,6 +61,7 @@ set(llcommon_SOURCE_FILES
|
||||
llmd5.cpp
|
||||
llmemory.cpp
|
||||
llmemorystream.cpp
|
||||
llmemtype.cpp
|
||||
llmetrics.cpp
|
||||
llmortician.cpp
|
||||
lloptioninterface.cpp
|
||||
@@ -81,6 +91,7 @@ set(llcommon_SOURCE_FILES
|
||||
lluri.cpp
|
||||
lluuid.cpp
|
||||
llworkerthread.cpp
|
||||
ll_template_cast.h
|
||||
metaclass.cpp
|
||||
metaproperty.cpp
|
||||
reflective.cpp
|
||||
@@ -101,6 +112,8 @@ set(llcommon_HEADER_FILES
|
||||
linden_common.h
|
||||
linked_lists.h
|
||||
llaccountingcost.h
|
||||
llallocator.h
|
||||
llallocator_heap_profile.h
|
||||
llagentconstants.h
|
||||
llavatarname.h
|
||||
llapp.h
|
||||
@@ -116,6 +129,7 @@ set(llcommon_HEADER_FILES
|
||||
llclickaction.h
|
||||
llcommon.h
|
||||
llcommonutils.h
|
||||
llcoros.h
|
||||
llcrc.h
|
||||
llcriticaldamp.h
|
||||
llcursortypes.h
|
||||
@@ -123,6 +137,7 @@ set(llcommon_HEADER_FILES
|
||||
lldarrayptr.h
|
||||
lldate.h
|
||||
lldefs.h
|
||||
lldependencies.h
|
||||
lldeleteutils.h
|
||||
lldepthstack.h
|
||||
lldictionary.h
|
||||
@@ -135,6 +150,11 @@ set(llcommon_HEADER_FILES
|
||||
llerrorlegacy.h
|
||||
llerrorthread.h
|
||||
llevent.h
|
||||
lleventapi.h
|
||||
lleventcoro.h
|
||||
lleventdispatcher.h
|
||||
lleventfilter.h
|
||||
llevents.h
|
||||
lleventemitter.h
|
||||
llextendedstatus.h
|
||||
lleventtimer.h
|
||||
@@ -208,8 +228,7 @@ set(llcommon_HEADER_FILES
|
||||
lluri.h
|
||||
lluuid.h
|
||||
lluuidhashmap.h
|
||||
llversionserver.h
|
||||
llversionviewer.h
|
||||
llversionviewer.h.in
|
||||
llworkerthread.h
|
||||
metaclass.h
|
||||
metaclasst.h
|
||||
|
||||
@@ -27,8 +27,38 @@
|
||||
* - Initial version, written by Aleric Inglewood @ SL
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
// 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.
|
||||
@@ -115,7 +145,7 @@ void AIFrameTimer::handleExpiration(F64 current_frame_time)
|
||||
// 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 call back function,
|
||||
// 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
|
||||
|
||||
@@ -61,20 +61,37 @@ class LL_COMMON_API AIFrameTimer
|
||||
// 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).
|
||||
Signal* mCallback;
|
||||
AIFrameTimer* mTimer;
|
||||
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(new Signal), mTimer(timer) { }
|
||||
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(NULL), mTimer(timer) { }
|
||||
~AIRunningFrameTimer() { delete mCallback; }
|
||||
void init(signal_type::slot_type const& slot) const { mCallback->mSignal.connect(slot); }
|
||||
|
||||
// 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;
|
||||
@@ -98,12 +115,12 @@ class LL_COMMON_API AIFrameTimer
|
||||
|
||||
// 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);
|
||||
}
|
||||
{
|
||||
// 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.
|
||||
@@ -129,6 +146,8 @@ class LL_COMMON_API AIFrameTimer
|
||||
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);
|
||||
};
|
||||
|
||||
177
indra/llcommon/ll_template_cast.h
Normal file
177
indra/llcommon/ll_template_cast.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @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) */
|
||||
134
indra/llcommon/llallocator.cpp
Normal file
134
indra/llcommon/llallocator.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
57
indra/llcommon/llallocator.h
Normal file
57
indra/llcommon/llallocator.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @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
|
||||
147
indra/llcommon/llallocator_heap_profile.cpp
Normal file
147
indra/llcommon/llallocator_heap_profile.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
|
||||
71
indra/llcommon/llallocator_heap_profile.h
Normal file
71
indra/llcommon/llallocator_heap_profile.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -401,6 +401,18 @@ bool LLApp::isExiting()
|
||||
return isQuitting() || isError();
|
||||
}
|
||||
|
||||
void LLApp::disableCrashlogger()
|
||||
{
|
||||
// Disable Breakpad exception handler.
|
||||
sDisableCrashlogger = TRUE;
|
||||
}
|
||||
|
||||
// static
|
||||
bool LLApp::isCrashloggerDisabled()
|
||||
{
|
||||
return (sDisableCrashlogger == TRUE);
|
||||
}
|
||||
|
||||
#if !LL_WINDOWS
|
||||
// static
|
||||
U32 LLApp::getSigChildCount()
|
||||
@@ -734,7 +746,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
|
||||
llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
|
||||
}
|
||||
|
||||
if(LLApp::sDisableCrashlogger) //Don't gracefully handle any signals crash and core for a gdb post mortum
|
||||
if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem
|
||||
{
|
||||
clear_signals();
|
||||
llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl;
|
||||
|
||||
@@ -191,6 +191,11 @@ public:
|
||||
//
|
||||
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
|
||||
@@ -214,9 +219,6 @@ public:
|
||||
//
|
||||
void setErrorHandler(LLAppErrorHandler handler);
|
||||
void setSyncErrorHandler(LLAppErrorHandler handler);
|
||||
|
||||
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
|
||||
|
||||
#if !LL_WINDOWS
|
||||
//
|
||||
// Child process handling (Unix only for now)
|
||||
@@ -245,6 +247,7 @@ protected:
|
||||
static void setStatus(EAppStatus status); // Use this to change the application status.
|
||||
static EAppStatus sStatus; // Reflects current application status
|
||||
static BOOL sErrorThreadRunning; // Set while the error thread is running
|
||||
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
|
||||
|
||||
#if !LL_WINDOWS
|
||||
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
|
||||
|
||||
154
indra/llcommon/llcoros.cpp
Normal file
154
indra/llcommon/llcoros.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @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
|
||||
166
indra/llcommon/llcoros.h
Normal file
166
indra/llcommon/llcoros.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @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) */
|
||||
103
indra/llcommon/lldependencies.cpp
Normal file
103
indra/llcommon/lldependencies.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
799
indra/llcommon/lldependencies.h
Normal file
799
indra/llcommon/lldependencies.h
Normal file
@@ -0,0 +1,799 @@
|
||||
/**
|
||||
* @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) */
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include "llevent.h"
|
||||
|
||||
using namespace LLOldEvents;
|
||||
|
||||
/************************************************
|
||||
Events
|
||||
************************************************/
|
||||
|
||||
@@ -35,9 +35,12 @@
|
||||
#define LL_EVENT_H
|
||||
|
||||
#include "llsd.h"
|
||||
#include "llmemory.h"
|
||||
#include "llpointer.h"
|
||||
#include "llthread.h"
|
||||
|
||||
namespace LLOldEvents
|
||||
{
|
||||
|
||||
class LLEventListener;
|
||||
class LLEvent;
|
||||
class LLEventDispatcher;
|
||||
@@ -194,4 +197,6 @@ public:
|
||||
LLSD mValue;
|
||||
};
|
||||
|
||||
} // LLOldEvents
|
||||
|
||||
#endif // LL_EVENT_H
|
||||
|
||||
77
indra/llcommon/lleventapi.cpp
Normal file
77
indra/llcommon/lleventapi.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
166
indra/llcommon/lleventapi.h
Normal file
166
indra/llcommon/lleventapi.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @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) */
|
||||
146
indra/llcommon/lleventcoro.cpp
Normal file
146
indra/llcommon/lleventcoro.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
569
indra/llcommon/lleventcoro.h
Normal file
569
indra/llcommon/lleventcoro.h
Normal file
@@ -0,0 +1,569 @@
|
||||
/**
|
||||
* @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) */
|
||||
672
indra/llcommon/lleventdispatcher.cpp
Normal file
672
indra/llcommon/lleventdispatcher.cpp
Normal file
@@ -0,0 +1,672 @@
|
||||
/**
|
||||
* @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)
|
||||
{}
|
||||
|
||||
541
indra/llcommon/lleventdispatcher.h
Normal file
541
indra/llcommon/lleventdispatcher.h
Normal file
@@ -0,0 +1,541 @@
|
||||
/**
|
||||
* @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) */
|
||||
166
indra/llcommon/lleventfilter.cpp
Normal file
166
indra/llcommon/lleventfilter.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
203
indra/llcommon/lleventfilter.h
Normal file
203
indra/llcommon/lleventfilter.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* @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) */
|
||||
628
indra/llcommon/llevents.cpp
Normal file
628
indra/llcommon/llevents.cpp
Normal file
@@ -0,0 +1,628 @@
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
1051
indra/llcommon/llevents.h
Normal file
1051
indra/llcommon/llevents.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,125 @@
|
||||
* 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"
|
||||
@@ -64,14 +183,6 @@ BOOL LLFastTimer::sMetricLog = FALSE;
|
||||
LLMutex* LLFastTimer::sLogLock = NULL;
|
||||
std::queue<LLSD> LLFastTimer::sLogQueue;
|
||||
|
||||
#define USE_RDTSC 0
|
||||
|
||||
#if LL_LINUX || LL_SOLARIS
|
||||
U64 LLFastTimer::sClockResolution = 1000000000; // Nanosecond resolution
|
||||
#else
|
||||
U64 LLFastTimer::sClockResolution = 1000000; // Microsecond resolution
|
||||
#endif
|
||||
|
||||
std::vector<LLFastTimer::FrameState>* LLFastTimer::sTimerInfos = NULL;
|
||||
U64 LLFastTimer::sTimerCycles = 0;
|
||||
U32 LLFastTimer::sTimerCalls = 0;
|
||||
@@ -130,8 +241,17 @@ public:
|
||||
mActiveTimerRoot->setCollapsed(false);
|
||||
|
||||
mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
|
||||
mRootFrameState->mParent = &mTimerRoot->getFrameState();
|
||||
mActiveTimerRoot->setParent(mTimerRoot);
|
||||
// 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);
|
||||
}
|
||||
@@ -187,7 +307,7 @@ private:
|
||||
LLFastTimer::NamedTimer* mActiveTimerRoot;
|
||||
LLFastTimer::NamedTimer* mTimerRoot;
|
||||
LLFastTimer* mAppTimer;
|
||||
LLFastTimer::FrameState* mRootFrameState;
|
||||
LLFastTimer::FrameState* mRootFrameState; // Points to memory allocated with new, so this pointer is not invalidated.
|
||||
};
|
||||
|
||||
void update_cached_pointers_if_changed()
|
||||
@@ -196,9 +316,9 @@ void update_cached_pointers_if_changed()
|
||||
static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
|
||||
if (&*(LLFastTimer::getFrameStateList().begin()) != sFirstTimerAddress)
|
||||
{
|
||||
LLFastTimer::DeclareTimer::updateCachedPointers();
|
||||
LLFastTimer::updateCachedPointers();
|
||||
sFirstTimerAddress = &*(LLFastTimer::getFrameStateList().begin());
|
||||
}
|
||||
sFirstTimerAddress = &*(LLFastTimer::getFrameStateList().begin());
|
||||
}
|
||||
|
||||
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
|
||||
@@ -217,53 +337,69 @@ LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFastTimer::DeclareTimer::updateCachedPointers()
|
||||
void LLFastTimer::updateCachedPointers()
|
||||
{
|
||||
// propagate frame state pointers to timer declarations
|
||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||
// Update DeclareTimer::mFrameState pointers.
|
||||
for (DeclareTimer::instance_iter it = DeclareTimer::beginInstances(); it != DeclareTimer::endInstances(); ++it)
|
||||
{
|
||||
// update cached pointer
|
||||
it->mFrameState = &it->mTimer.getFrameState();
|
||||
}
|
||||
|
||||
// also update frame states of timers on stack
|
||||
LLFastTimer* cur_timerp = LLFastTimer::sCurTimerData.mCurTimer;
|
||||
while(cur_timerp->mLastTimerData.mCurTimer != cur_timerp)
|
||||
// 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_timerp->mFrameState = &cur_timerp->mFrameState->mTimer->getFrameState();
|
||||
cur_timerp = cur_timerp->mLastTimerData.mCurTimer;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
#if (LL_DARWIN || LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
|
||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||
{
|
||||
return sClockResolution >> 8;
|
||||
}
|
||||
#else // windows or x86-mac or x86-linux or x86-solaris
|
||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||
{
|
||||
#if USE_RDTSC || !LL_WINDOWS
|
||||
//getCPUFrequency returns MHz and sCPUClockFrequency wants to be in Hz
|
||||
static U64 sCPUClockFrequency = U64(LLProcessorInfo().getCPUFrequency()*1000000.0);
|
||||
|
||||
// we drop the low-order byte in our timers, so report a lower frequency
|
||||
// See lltimer.cpp.
|
||||
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
|
||||
std::string LLFastTimer::sClockType = "gettimeofday";
|
||||
#elif LL_WINDOWS
|
||||
std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
|
||||
#else
|
||||
// If we're not using RDTSC, each fasttimer tick is just a performance counter tick.
|
||||
// Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency())
|
||||
// since that would change displayed MHz stats for CPUs
|
||||
#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)
|
||||
{
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency);
|
||||
sCPUClockFrequency = calc_clock_frequency();
|
||||
firstcall = false;
|
||||
}
|
||||
#endif
|
||||
return sCPUClockFrequency >> 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
|
||||
: mActiveCount(0),
|
||||
@@ -401,11 +537,12 @@ void LLFastTimer::NamedTimer::buildHierarchy()
|
||||
|
||||
// bootstrap tree construction by attaching to last timer to be on stack
|
||||
// when this timer was called
|
||||
if (timer.getFrameState().mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
|
||||
FrameState& frame_state(timer.getFrameState());
|
||||
if (frame_state.mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
|
||||
{
|
||||
timer.setParent(timer.getFrameState().mLastCaller->mTimer);
|
||||
timer.setParent(frame_state.mLastCaller);
|
||||
// no need to push up tree on first use, flag can be set spuriously
|
||||
timer.getFrameState().mMoveUpTree = false;
|
||||
frame_state.mMoveUpTree = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,15 +701,14 @@ void LLFastTimer::NamedTimer::resetFrame()
|
||||
|
||||
timerp->mFrameStateIndex = index;
|
||||
index++;
|
||||
|
||||
llassert_always(timerp->mFrameStateIndex < (S32)getFrameStateList().size());
|
||||
}
|
||||
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
|
||||
DeclareTimer::updateCachedPointers();
|
||||
updateCachedPointers();
|
||||
|
||||
// reset for next frame
|
||||
{
|
||||
@@ -644,7 +780,11 @@ LLFastTimer::info_list_t& LLFastTimer::getFrameStateList()
|
||||
{
|
||||
if (!sTimerInfos)
|
||||
{
|
||||
sTimerInfos = new info_list_t();
|
||||
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;
|
||||
}
|
||||
@@ -776,22 +916,27 @@ const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& na
|
||||
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()
|
||||
@@ -804,9 +949,3 @@ U64 LLFastTimer::getCPUClockCount64()
|
||||
return get_clock_count();
|
||||
}
|
||||
|
||||
#if LL_WINDOWS
|
||||
std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
|
||||
#else
|
||||
std::string LLFastTimer::sClockType = "gettimeofday";
|
||||
#endif
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
U32 mSelfTimeCounter;
|
||||
U32 mCalls;
|
||||
FrameState* mParent; // info for caller timer
|
||||
FrameState* mLastCaller; // used to bootstrap tree construction
|
||||
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
|
||||
@@ -144,8 +144,6 @@ public:
|
||||
DeclareTimer(const std::string& name, bool open);
|
||||
DeclareTimer(const std::string& name);
|
||||
|
||||
static void updateCachedPointers();
|
||||
|
||||
private:
|
||||
NamedTimer& mTimer;
|
||||
FrameState* mFrameState;
|
||||
@@ -172,6 +170,7 @@ public:
|
||||
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
|
||||
@@ -200,7 +199,7 @@ public:
|
||||
|
||||
// store last caller to bootstrap tree creation
|
||||
// do this in the destructor in case of recursion to get topmost caller
|
||||
frame_state->mLastCaller = mLastTimerData.mFrameState;
|
||||
frame_state->mLastCaller = mLastTimerData.mNamedTimer;
|
||||
|
||||
// we are only tracking self time, so subtract our total time delta from parents
|
||||
mLastTimerData.mChildTime += total_time;
|
||||
@@ -239,6 +238,9 @@ public:
|
||||
// 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; }
|
||||
@@ -249,6 +251,7 @@ public:
|
||||
struct CurTimerData
|
||||
{
|
||||
LLFastTimer* mCurTimer;
|
||||
NamedTimer* mNamedTimer;
|
||||
FrameState* mFrameState;
|
||||
U32 mChildTime;
|
||||
};
|
||||
@@ -258,7 +261,6 @@ public:
|
||||
public:
|
||||
static U32 getCPUClockCount32();
|
||||
static U64 getCPUClockCount64();
|
||||
static U64 sClockResolution;
|
||||
|
||||
private:
|
||||
static S32 sCurFrameIndex;
|
||||
@@ -272,6 +274,4 @@ private:
|
||||
|
||||
};
|
||||
|
||||
typedef class LLFastTimer LLFastTimer;
|
||||
|
||||
#endif // LL_LLFASTTIMER_H
|
||||
|
||||
@@ -32,9 +32,13 @@
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
|
||||
//#if MEM_TRACK_MEM
|
||||
#include "llthread.h"
|
||||
//#endif
|
||||
|
||||
#if defined(LL_WINDOWS)
|
||||
#define _WINSOCKAPI_
|
||||
# include <windows.h>
|
||||
//# include <windows.h>
|
||||
# include <psapi.h>
|
||||
#elif defined(LL_DARWIN)
|
||||
# include <sys/types.h>
|
||||
@@ -45,10 +49,9 @@
|
||||
#endif
|
||||
|
||||
#include "llmemory.h"
|
||||
#include "llmemtype.h"
|
||||
#include "llsys.h"
|
||||
#include "llthread.h"
|
||||
|
||||
#include "llsys.h"
|
||||
#include "llframetimer.h"
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//static
|
||||
@@ -162,6 +165,7 @@ void LLMemory::logMemoryInfo(BOOL update)
|
||||
if(update)
|
||||
{
|
||||
updateMemoryInfo() ;
|
||||
LLPrivateMemoryPoolManager::getInstance()->updateStatistics() ;
|
||||
}
|
||||
|
||||
llinfos << "Current allocated physical memory(KB): " << sAllocatedMemInKB << llendl ;
|
||||
@@ -242,161 +246,6 @@ U32 LLMemory::getAllocatedMemKB()
|
||||
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)
|
||||
{
|
||||
if (size == 0)
|
||||
@@ -412,38 +261,6 @@ void* ll_allocate (size_t size)
|
||||
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)
|
||||
@@ -613,6 +430,141 @@ U32 LLMemory::getWorkingSetSize()
|
||||
|
||||
#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
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#endif
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llmemtype.h"
|
||||
#if LL_DEBUG
|
||||
inline void* ll_aligned_malloc( size_t size, int align )
|
||||
{
|
||||
|
||||
232
indra/llcommon/llmemtype.cpp
Normal file
232
indra/llcommon/llmemtype.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* @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];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -36,128 +36,210 @@
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class LLMemType;
|
||||
|
||||
extern void* ll_allocate (size_t size);
|
||||
extern void ll_release (void *p);
|
||||
|
||||
#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)
|
||||
#endif // MEM_TRACK_TYPE
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "linden_common.h"
|
||||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// WARNING: Never commit with MEM_TRACK_MEM == 1
|
||||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
#define MEM_TRACK_MEM (0 && LL_WINDOWS)
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define MEM_TYPE_NEW(T)
|
||||
|
||||
class LL_COMMON_API LLMemType
|
||||
{
|
||||
public:
|
||||
// Also update sTypeDesc in llmemory.cpp
|
||||
enum EMemType
|
||||
|
||||
// class we'll initialize all instances of as
|
||||
// static members of MemType. Then use
|
||||
// to construct any new mem type.
|
||||
class LL_COMMON_API DeclareMemType
|
||||
{
|
||||
MTYPE_INIT,
|
||||
MTYPE_STARTUP,
|
||||
MTYPE_MAIN,
|
||||
|
||||
MTYPE_IMAGEBASE,
|
||||
MTYPE_IMAGERAW,
|
||||
MTYPE_IMAGEFORMATTED,
|
||||
public:
|
||||
DeclareMemType(char const * st);
|
||||
~DeclareMemType();
|
||||
|
||||
S32 mID;
|
||||
char const * mName;
|
||||
|
||||
MTYPE_APPFMTIMAGE,
|
||||
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
|
||||
// array so we can map an index ID to Name
|
||||
static std::vector<char const *> mNameList;
|
||||
};
|
||||
enum { MTYPE_MAX_DEPTH = 64 };
|
||||
|
||||
public:
|
||||
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 void reset();
|
||||
static void printMem();
|
||||
LLMemType(DeclareMemType& dt);
|
||||
~LLMemType();
|
||||
|
||||
static char const * getNameFromID(S32 id);
|
||||
|
||||
static DeclareMemType MTYPE_INIT;
|
||||
static DeclareMemType MTYPE_STARTUP;
|
||||
static DeclareMemType MTYPE_MAIN;
|
||||
static DeclareMemType MTYPE_FRAME;
|
||||
|
||||
static DeclareMemType MTYPE_GATHER_INPUT;
|
||||
static DeclareMemType MTYPE_JOY_KEY;
|
||||
|
||||
static DeclareMemType MTYPE_IDLE;
|
||||
static DeclareMemType MTYPE_IDLE_PUMP;
|
||||
static DeclareMemType MTYPE_IDLE_NETWORK;
|
||||
static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
|
||||
static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION;
|
||||
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;
|
||||
|
||||
public:
|
||||
#if MEM_TRACK_TYPE
|
||||
static S32 sCurDepth;
|
||||
static S32 sCurType;
|
||||
static S32 sType[MTYPE_MAX_DEPTH];
|
||||
static S32 sMemCount[MTYPE_NUM_TYPES];
|
||||
static S32 sMaxMemCount[MTYPE_NUM_TYPES];
|
||||
static S32 sNewCount[MTYPE_NUM_TYPES];
|
||||
static S32 sOverheadMem;
|
||||
static const char* sTypeDesc[MTYPE_NUM_TYPES];
|
||||
#endif
|
||||
static S32 sTotalMem;
|
||||
static S32 sMaxTotalMem;
|
||||
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;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -151,9 +151,10 @@ public:
|
||||
*/
|
||||
static void deleteSingleton()
|
||||
{
|
||||
delete getData().mSingletonInstance;
|
||||
getData().mSingletonInstance = NULL;
|
||||
DERIVED_TYPE* instance = getData().mSingletonInstance;
|
||||
getData().mInitState = DELETED;
|
||||
getData().mSingletonInstance = NULL;
|
||||
delete instance;
|
||||
}
|
||||
|
||||
static SingletonInstanceData& getData()
|
||||
@@ -175,25 +176,11 @@ public:
|
||||
{
|
||||
SingletonInstanceData& data = getData();
|
||||
|
||||
if (data.mInitState == CONSTRUCTING)
|
||||
if (data.mInitState != INITIALIZED)
|
||||
{
|
||||
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
|
||||
createInstance(data);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -219,7 +206,35 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static void createInstance(SingletonInstanceData& data);
|
||||
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
|
||||
|
||||
@@ -198,7 +198,7 @@ U64 get_clock_count()
|
||||
return clock_count.QuadPart - offset;
|
||||
}
|
||||
|
||||
F64 calc_clock_frequency(U32 uiMeasureMSecs)
|
||||
F64 calc_clock_frequency(void)
|
||||
{
|
||||
__int64 freq;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
|
||||
@@ -209,7 +209,7 @@ F64 calc_clock_frequency(U32 uiMeasureMSecs)
|
||||
|
||||
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
|
||||
// Both Linux and Mac use gettimeofday for accurate time
|
||||
F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
|
||||
F64 calc_clock_frequency(void)
|
||||
{
|
||||
return 1000000.0; // microseconds, so 1 Mhz.
|
||||
}
|
||||
@@ -226,7 +226,7 @@ U64 get_clock_count()
|
||||
|
||||
void update_clock_frequencies()
|
||||
{
|
||||
gClockFrequency = calc_clock_frequency(50U);
|
||||
gClockFrequency = calc_clock_frequency();
|
||||
gClockFrequencyInv = 1.0/gClockFrequency;
|
||||
gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
// 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 F64 calc_clock_frequency(U32 msecs);
|
||||
LL_COMMON_API F64 calc_clock_frequency();
|
||||
LL_COMMON_API void update_clock_frequencies();
|
||||
|
||||
// Sleep for milliseconds
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* @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
|
||||
@@ -36,12 +36,13 @@
|
||||
const S32 LL_VERSION_MAJOR = 1;
|
||||
const S32 LL_VERSION_MINOR = 6;
|
||||
const S32 LL_VERSION_PATCH = 0;
|
||||
const S32 LL_VERSION_BUILD = 3;
|
||||
const S32 LL_VERSION_BUILD = ${vBUILD};
|
||||
|
||||
const char * const LL_CHANNEL = "Singularity";
|
||||
const char * const LL_CHANNEL = "${VIEWER_CHANNEL}";
|
||||
|
||||
#if LL_DARWIN
|
||||
const char * const LL_VERSION_BUNDLE_ID = "com.secondlife.singularity.viewer";
|
||||
const char * const LL_VERSION_BUNDLE_ID = "org.singularityviewer.singularity";
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -170,7 +170,7 @@ void LLImageBase::deleteData()
|
||||
// virtual
|
||||
U8* LLImageBase::allocateData(S32 size)
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
@@ -209,7 +209,7 @@ U8* LLImageBase::reallocateData(S32 size)
|
||||
if(mData && (mDataSize == size))
|
||||
return mData;
|
||||
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
U8 *new_datap = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
|
||||
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
|
||||
void LLImageRaw::verticalFlip()
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
S32 row_bytes = getWidth() * getComponents();
|
||||
U8* line_buffer = new (std::nothrow) U8[row_bytes];
|
||||
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.
|
||||
void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
llinfos << "compositeScaled4onto3" << llendl;
|
||||
|
||||
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.
|
||||
void LLImageRaw::copyScaled( LLImageRaw* src )
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
LLImageRaw* dst = this; // Just for clarity.
|
||||
|
||||
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 )
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) );
|
||||
|
||||
S32 old_width = getWidth();
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
#include "lluuid.h"
|
||||
#include "llstring.h"
|
||||
#include "llmemory.h"
|
||||
#include "llmemtype.h"
|
||||
#include "llthread.h"
|
||||
#include "aithreadsafe.h"
|
||||
|
||||
@@ -162,7 +162,7 @@ private:
|
||||
|
||||
static LLPrivateMemoryPool* sPrivatePoolp ;
|
||||
public:
|
||||
S16 mMemType; // debug
|
||||
LLMemType::DeclareMemType& mMemType; // debug
|
||||
};
|
||||
|
||||
// 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());
|
||||
|
||||
protected:
|
||||
BOOL copyData(U8 *data, S32 size); // calls updateData()
|
||||
BOOL copyData(U8 *data, S32 size);
|
||||
|
||||
protected:
|
||||
S8 mCodec;
|
||||
|
||||
@@ -297,7 +297,7 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
|
||||
// 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 )
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
|
||||
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)
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
resetLastError();
|
||||
BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible);
|
||||
if (!mLastError.empty())
|
||||
@@ -529,7 +529,7 @@ BOOL LLImageJ2C::loadAndValidate(const std::string &filename)
|
||||
|
||||
BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
|
||||
{
|
||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
||||
LLMemType mt1(mMemType);
|
||||
|
||||
resetLastError();
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
|
||||
// dereference the array.
|
||||
if(!image || !image->numcomps)
|
||||
{
|
||||
llwarns << "ERROR -> decodeImpl: failed to decode image!" << llendl;
|
||||
llwarns << "failed to decode image!" << llendl;
|
||||
if (image)
|
||||
{
|
||||
opj_image_destroy(image);
|
||||
@@ -183,6 +183,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
|
||||
{
|
||||
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
|
||||
opj_image_destroy(image);
|
||||
base.decodeFailed();
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
/**
|
||||
* @file lleconomy.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -54,6 +48,31 @@ 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
|
||||
void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data)
|
||||
{
|
||||
@@ -94,6 +113,8 @@ void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy*
|
||||
econ_data->setTeleportPriceExponent(f);
|
||||
msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceGroupCreate, i);
|
||||
econ_data->setPriceGroupCreate(i);
|
||||
|
||||
econ_data->notifyObservers();
|
||||
}
|
||||
|
||||
S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
/**
|
||||
* @file lleconomy.h
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -37,6 +31,16 @@
|
||||
class LLMessageSystem;
|
||||
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
|
||||
{
|
||||
public:
|
||||
@@ -52,6 +56,10 @@ public:
|
||||
|
||||
virtual void print();
|
||||
|
||||
void addObserver(LLEconomyObserver* observer);
|
||||
void removeObserver(LLEconomyObserver* observer);
|
||||
void notifyObservers();
|
||||
|
||||
static void processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data);
|
||||
|
||||
S32 calculateTeleportCost(F32 distance) const;
|
||||
@@ -95,6 +103,8 @@ private:
|
||||
S32 mTeleportMinPrice;
|
||||
F32 mTeleportPriceExponent;
|
||||
S32 mPriceGroupCreate;
|
||||
|
||||
std::list<LLEconomyObserver*> mObservers;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -90,8 +90,8 @@ public:
|
||||
typedef LLOctreeTraveler<T> oct_traveler;
|
||||
typedef LLTreeTraveler<T> tree_traveler;
|
||||
typedef typename std::set<LLPointer<T> > element_list;
|
||||
typedef typename std::set<LLPointer<T> >::iterator element_iter;
|
||||
typedef typename std::set<LLPointer<T> >::const_iterator const_element_iter;
|
||||
typedef typename element_list::iterator element_iter;
|
||||
typedef typename element_list::const_iterator const_element_iter;
|
||||
typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
|
||||
typedef typename std::vector<LLOctreeNode<T>* > child_list;
|
||||
typedef LLTreeNode<T> BaseType;
|
||||
@@ -124,6 +124,8 @@ public:
|
||||
mOctant = ((oct_node*) mParent)->getOctant(mCenter);
|
||||
}
|
||||
|
||||
mElementCount = 0;
|
||||
|
||||
clearChildren();
|
||||
}
|
||||
|
||||
@@ -229,11 +231,11 @@ public:
|
||||
void accept(oct_traveler* visitor) { visitor->visit(this); }
|
||||
virtual bool isLeaf() const { return mChild.empty(); }
|
||||
|
||||
U32 getElementCount() const { return mData.size(); }
|
||||
U32 getElementCount() const { return mElementCount; }
|
||||
element_list& getData() { return mData; }
|
||||
const element_list& getData() const { return mData; }
|
||||
|
||||
U32 getChildCount() const { return mChild.size(); }
|
||||
U32 getChildCount() const { return mChildCount; }
|
||||
oct_node* getChild(U32 index) { return mChild[index]; }
|
||||
const oct_node* getChild(U32 index) const { return mChild[index]; }
|
||||
child_list& getChildren() { return mChild; }
|
||||
@@ -321,6 +323,8 @@ public:
|
||||
|
||||
mData.insert(data);
|
||||
BaseType::insert(data);
|
||||
|
||||
mElementCount = mData.size();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -356,6 +360,8 @@ public:
|
||||
{
|
||||
mData.insert(data);
|
||||
BaseType::insert(data);
|
||||
|
||||
mElementCount = mData.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -409,6 +415,7 @@ public:
|
||||
if (mData.find(data) != mData.end())
|
||||
{ //we have data
|
||||
mData.erase(data);
|
||||
mElementCount = mData.size();
|
||||
notifyRemoval(data);
|
||||
checkAlive();
|
||||
return true;
|
||||
@@ -446,6 +453,7 @@ public:
|
||||
if (mData.find(data) != mData.end())
|
||||
{
|
||||
mData.erase(data);
|
||||
mElementCount = mData.size();
|
||||
notifyRemoval(data);
|
||||
llwarns << "FOUND!" << llendl;
|
||||
checkAlive();
|
||||
@@ -462,7 +470,7 @@ public:
|
||||
void clearChildren()
|
||||
{
|
||||
mChild.clear();
|
||||
|
||||
mChildCount = 0;
|
||||
U32* foo = (U32*) mChildMap;
|
||||
foo[0] = foo[1] = 0xFFFFFFFF;
|
||||
}
|
||||
@@ -522,9 +530,10 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
mChildMap[child->getOctant()] = (U8) mChild.size();
|
||||
mChildMap[child->getOctant()] = mChildCount;
|
||||
|
||||
mChild.push_back(child);
|
||||
++mChildCount;
|
||||
child->setParent(this);
|
||||
|
||||
if (!silent)
|
||||
@@ -553,12 +562,13 @@ public:
|
||||
delete mChild[index];
|
||||
}
|
||||
mChild.erase(mChild.begin() + index);
|
||||
--mChildCount;
|
||||
|
||||
//rebuild child map
|
||||
U32* foo = (U32*) mChildMap;
|
||||
foo[0] = foo[1] = 0xFFFFFFFF;
|
||||
|
||||
for (U32 i = 0; i < mChild.size(); ++i)
|
||||
for (U32 i = 0; i < mChildCount; ++i)
|
||||
{
|
||||
mChildMap[mChild[i]->getOctant()] = i;
|
||||
}
|
||||
@@ -611,8 +621,10 @@ protected:
|
||||
|
||||
child_list mChild;
|
||||
U8 mChildMap[8];
|
||||
U32 mChildCount;
|
||||
|
||||
element_list mData;
|
||||
U32 mElementCount;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ set(llmessage_SOURCE_FILES
|
||||
llregionpresenceverifier.cpp
|
||||
llsdappservices.cpp
|
||||
llsdhttpserver.cpp
|
||||
llsdmessage.cpp
|
||||
llsdmessagebuilder.cpp
|
||||
llsdmessagereader.cpp
|
||||
llsdrpcclient.cpp
|
||||
@@ -163,6 +164,7 @@ set(llmessage_HEADER_FILES
|
||||
llregionpresenceverifier.h
|
||||
llsdappservices.h
|
||||
llsdhttpserver.h
|
||||
llsdmessage.h
|
||||
llsdmessagebuilder.h
|
||||
llsdmessagereader.h
|
||||
llsdrpcclient.h
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "llsdserialize.h"
|
||||
#include "lluuid.h"
|
||||
#include "message.h"
|
||||
#include "llmemtype.h"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
@@ -771,6 +772,7 @@ bool LLCacheName::getIfThere(const LLUUID& id, std::string& fullname, BOOL& is_g
|
||||
// </edit>
|
||||
void LLCacheName::processPending()
|
||||
{
|
||||
LLMemType mt_pp(LLMemType::MTYPE_CACHE_PROCESS_PENDING);
|
||||
const F32 SECS_BETWEEN_PROCESS = 0.1f;
|
||||
if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
|
||||
{
|
||||
@@ -873,8 +875,10 @@ std::string LLCacheName::getDefaultLastName()
|
||||
{
|
||||
return "Resident";
|
||||
}
|
||||
|
||||
void LLCacheName::Impl::processPendingAsks()
|
||||
{
|
||||
LLMemType mt_ppa(LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS);
|
||||
sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
|
||||
sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
|
||||
mAskNameQueue.clear();
|
||||
@@ -883,6 +887,7 @@ void LLCacheName::Impl::processPendingAsks()
|
||||
|
||||
void LLCacheName::Impl::processPendingReplies()
|
||||
{
|
||||
LLMemType mt_ppr(LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES);
|
||||
// First call all the callbacks, because they might send messages.
|
||||
for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
|
||||
{
|
||||
|
||||
@@ -128,6 +128,7 @@ LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp,
|
||||
: LLAssetRequest(uuid, type),
|
||||
mZInitialized(false)
|
||||
{
|
||||
memset(&mZStream, 0, sizeof(mZStream)); // we'll initialize this later, but for now zero the whole C-style struct to avoid debug/coverity noise
|
||||
mAssetStoragep = asp;
|
||||
mCurlHandle = NULL;
|
||||
mCurlMultiHandle = curl_multi;
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#define LL_HTTPCLIENTADAPTER_H
|
||||
|
||||
#include "llhttpclientinterface.h"
|
||||
#include "llmemory.h" // LLSingleton<>
|
||||
#include "llsingleton.h" // LLSingleton<>
|
||||
|
||||
class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter>
|
||||
{
|
||||
|
||||
@@ -4,31 +4,25 @@
|
||||
* @date 2005-10-05
|
||||
* @brief Implementation of the http server classes
|
||||
*
|
||||
* $LicenseInfo:firstyear=2005&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2005-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -972,7 +966,9 @@ private:
|
||||
// static
|
||||
LLHTTPNode& LLIOHTTPServer::create(LLPumpIO& pump, U16 port)
|
||||
{
|
||||
LLSocket::ptr_t socket = LLSocket::create(LLSocket::STREAM_TCP, port);
|
||||
LLSocket::ptr_t socket = LLSocket::create(
|
||||
LLSocket::STREAM_TCP,
|
||||
port);
|
||||
if(!socket)
|
||||
{
|
||||
llerrs << "Unable to initialize socket" << llendl;
|
||||
|
||||
@@ -114,11 +114,21 @@ LLSocket::ptr_t LLSocket::create(EType type, U16 port)
|
||||
|
||||
if(STREAM_TCP == type)
|
||||
{
|
||||
status = apr_socket_create(&rv->mSocket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, rv->mPool());
|
||||
status = apr_socket_create(
|
||||
&rv->mSocket,
|
||||
APR_INET,
|
||||
SOCK_STREAM,
|
||||
APR_PROTO_TCP,
|
||||
rv->mPool());
|
||||
}
|
||||
else if(DATAGRAM_UDP == type)
|
||||
{
|
||||
status = apr_socket_create(&rv->mSocket, APR_INET, SOCK_DGRAM, APR_PROTO_UDP, rv->mPool());
|
||||
status = apr_socket_create(
|
||||
&rv->mSocket,
|
||||
APR_INET,
|
||||
SOCK_DGRAM,
|
||||
APR_PROTO_UDP,
|
||||
rv->mPool());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -234,6 +244,7 @@ LLSocket::LLSocket() :
|
||||
mPool(LLThread::tldata().mRootPool),
|
||||
mPort(PORT_INVALID)
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_TCP);
|
||||
}
|
||||
|
||||
LLSocket::~LLSocket()
|
||||
|
||||
@@ -239,15 +239,23 @@ S32 LLPacketRing::receivePacket (S32 socket, char *datap)
|
||||
// no delay, pull straight from net
|
||||
if (LLSocks::isEnabled())
|
||||
{
|
||||
proxywrap_t * header;
|
||||
datap = datap-10;
|
||||
header = (proxywrap_t *)datap;
|
||||
packet_size = receive_packet(socket, datap);
|
||||
mLastSender.setAddress(header->addr);
|
||||
mLastSender.setPort(ntohs(header->port));
|
||||
if (packet_size > 10)
|
||||
const U8 SOCKS_HEADER_SIZE = 10;
|
||||
U8 buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
|
||||
packet_size = receive_packet(socket, static_cast<char*>(static_cast<void*>(buffer)));
|
||||
|
||||
if (packet_size > SOCKS_HEADER_SIZE)
|
||||
{
|
||||
packet_size -= 10;
|
||||
// *FIX We are assuming ATYP is 0x01 (IPv4), not 0x03 (hostname) or 0x04 (IPv6)
|
||||
memcpy(datap, buffer + SOCKS_HEADER_SIZE, packet_size - SOCKS_HEADER_SIZE);
|
||||
proxywrap_t * header = static_cast<proxywrap_t*>(static_cast<void*>(buffer));
|
||||
mLastSender.setAddress(header->addr);
|
||||
mLastSender.setPort(ntohs(header->port));
|
||||
|
||||
packet_size -= SOCKS_HEADER_SIZE; // The unwrapped packet size
|
||||
}
|
||||
else
|
||||
{
|
||||
packet_size = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -285,7 +293,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
||||
|
||||
if (!mUseOutThrottle)
|
||||
{
|
||||
return doSendPacket(h_socket, send_buffer, buf_size, host );
|
||||
return sendPacketImpl(h_socket, send_buffer, buf_size, host );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -306,7 +314,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
||||
mOutBufferLength -= packetp->getSize();
|
||||
packet_size = packetp->getSize();
|
||||
|
||||
status = doSendPacket(h_socket, packetp->getData(), packet_size, packetp->getHost());
|
||||
status = sendPacketImpl(h_socket, packetp->getData(), packet_size, packetp->getHost());
|
||||
|
||||
delete packetp;
|
||||
// Update the throttle
|
||||
@@ -315,7 +323,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
||||
else
|
||||
{
|
||||
// If the queue's empty, we can just send this packet right away.
|
||||
status = doSendPacket(h_socket, send_buffer, buf_size, host );
|
||||
status = sendPacketImpl(h_socket, send_buffer, buf_size, host );
|
||||
packet_size = buf_size;
|
||||
|
||||
// Update the throttle
|
||||
@@ -354,7 +362,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL LLPacketRing::doSendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host)
|
||||
BOOL LLPacketRing::sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host)
|
||||
{
|
||||
|
||||
if (!LLSocks::isEnabled())
|
||||
@@ -362,14 +370,21 @@ BOOL LLPacketRing::doSendPacket(int h_socket, const char * send_buffer, S32 buf_
|
||||
return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort());
|
||||
}
|
||||
|
||||
proxywrap_t *socks_header = (proxywrap_t *)&mProxyWrappedSendBuffer;
|
||||
const U8 SOCKS_HEADER_SIZE = 10;
|
||||
char headered_send_buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
|
||||
|
||||
proxywrap_t *socks_header = static_cast<proxywrap_t*>(static_cast<void*>(&headered_send_buffer));
|
||||
socks_header->rsv = 0;
|
||||
socks_header->addr = host.getAddress();
|
||||
socks_header->port = htons(host.getPort());
|
||||
socks_header->atype = ADDRESS_IPV4;
|
||||
socks_header->frag = 0;
|
||||
|
||||
memcpy(mProxyWrappedSendBuffer+10, send_buffer, buf_size);
|
||||
memcpy(headered_send_buffer + SOCKS_HEADER_SIZE, send_buffer, buf_size);
|
||||
|
||||
return send_packet(h_socket,(const char*) mProxyWrappedSendBuffer, buf_size+10, LLSocks::getInstance()->getUDPPproxy().getAddress(), LLSocks::getInstance()->getUDPPproxy().getPort());
|
||||
return send_packet( h_socket,
|
||||
headered_send_buffer,
|
||||
buf_size + SOCKS_HEADER_SIZE,
|
||||
LLSocks::getInstance()->getUDPPproxy().getAddress(),
|
||||
LLSocks::getInstance()->getUDPPproxy().getPort());
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ protected:
|
||||
LLHost mLastSender;
|
||||
LLHost mLastReceivingIF;
|
||||
|
||||
BOOL doSendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);
|
||||
U8 mProxyWrappedSendBuffer[NET_BUFFER_SIZE];
|
||||
private:
|
||||
BOOL sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -2,31 +2,25 @@
|
||||
* @file llregionflags.h
|
||||
* @brief Flags that are sent in the statistics message region_flags field.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -48,9 +42,6 @@ const U32 REGION_FLAGS_RESET_HOME_ON_TELEPORT = (1 << 3);
|
||||
// Does the sun move?
|
||||
const U32 REGION_FLAGS_SUN_FIXED = (1 << 4);
|
||||
|
||||
// Tax free zone (no taxes on objects, land, etc.)
|
||||
const U32 REGION_FLAGS_TAX_FREE = (1 << 5);
|
||||
|
||||
// Can't change the terrain heightfield, even on owned parcels,
|
||||
// but can plant trees and grass.
|
||||
const U32 REGION_FLAGS_BLOCK_TERRAFORM = (1 << 6);
|
||||
@@ -60,17 +51,12 @@ const U32 REGION_FLAGS_BLOCK_LAND_RESELL = (1 << 7);
|
||||
|
||||
// All content wiped once per night
|
||||
const U32 REGION_FLAGS_SANDBOX = (1 << 8);
|
||||
const U32 REGION_FLAGS_NULL_LAYER = (1 << 9);
|
||||
// const U32 REGION_FLAGS_SKIP_AGENT_ACTION = (1 << 10);
|
||||
const U32 REGION_FLAGS_HARD_ALLOW_LAND_TRANSFER = (1 << 10); // Region allows land reselling
|
||||
// const U32 REGION_FLAGS_SKIP_UPDATE_INTEREST_LIST= (1 << 11);
|
||||
const U32 REGION_FLAGS_HARD_ALLOW_POST_CLASSIFIED = (1 << 11); // Region allows posting of classified ads
|
||||
const U32 REGION_FLAGS_SKIP_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies
|
||||
const U32 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13);
|
||||
const U32 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics
|
||||
const U32 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15);
|
||||
//const U32 REGION_FLAGS_MAINLAND_VISIBLE = (1 << 16);
|
||||
const U32 REGION_FLAGS_PUBLIC_ALLOWED = (1 << 17);
|
||||
const U32 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_OBJECT = (1 << 16);
|
||||
const U32 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_ESTATE_OBJECT = (1 << 17);
|
||||
const U32 REGION_FLAGS_BLOCK_DWELL = (1 << 18);
|
||||
|
||||
// Is flight allowed?
|
||||
@@ -87,18 +73,13 @@ const U32 REGION_FLAGS_ESTATE_SKIP_SCRIPTS = (1 << 21);
|
||||
const U32 REGION_FLAGS_RESTRICT_PUSHOBJECT = (1 << 22);
|
||||
|
||||
const U32 REGION_FLAGS_DENY_ANONYMOUS = (1 << 23);
|
||||
// const U32 REGION_FLAGS_DENY_IDENTIFIED = (1 << 24);
|
||||
// const U32 REGION_FLAGS_DENY_TRANSACTED = (1 << 25);
|
||||
|
||||
const U32 REGION_FLAGS_ALLOW_PARCEL_CHANGES = (1 << 26);
|
||||
|
||||
const U32 REGION_FLAGS_ABUSE_EMAIL_TO_ESTATE_OWNER = (1 << 27);
|
||||
|
||||
const U32 REGION_FLAGS_ALLOW_VOICE = (1 << 28);
|
||||
|
||||
const U32 REGION_FLAGS_BLOCK_PARCEL_SEARCH = (1 << 29);
|
||||
const U32 REGION_FLAGS_DENY_AGEUNVERIFIED = (1 << 30);
|
||||
const U32 REGION_FLAGS_SKIP_MONO_SCRIPTS = (1 << 31);
|
||||
|
||||
const U32 REGION_FLAGS_DEFAULT = REGION_FLAGS_ALLOW_LANDMARK |
|
||||
REGION_FLAGS_ALLOW_SET_HOME |
|
||||
@@ -111,7 +92,6 @@ const U32 REGION_FLAGS_PRELUDE_UNSET = REGION_FLAGS_ALLOW_LANDMARK
|
||||
| REGION_FLAGS_ALLOW_SET_HOME;
|
||||
|
||||
const U32 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE
|
||||
| REGION_FLAGS_PUBLIC_ALLOWED
|
||||
| REGION_FLAGS_SUN_FIXED
|
||||
| REGION_FLAGS_DENY_ANONYMOUS
|
||||
| REGION_FLAGS_DENY_AGEUNVERIFIED;
|
||||
|
||||
170
indra/llmessage/llsdmessage.cpp
Normal file
170
indra/llmessage/llsdmessage.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* @file llsdmessage.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2008-10-31
|
||||
* @brief Implementation for llsdmessage.
|
||||
*
|
||||
* $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 LL_WINDOWS
|
||||
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
|
||||
#endif
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llsdmessage.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
#include "llevents.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llhttpclient.h"
|
||||
#include "llmessageconfig.h"
|
||||
#include "llhost.h"
|
||||
#include "message.h"
|
||||
#include "llsdutil.h"
|
||||
|
||||
// Declare a static LLSDMessage instance to ensure that we have a listener as
|
||||
// soon as someone tries to post on our canonical LLEventPump name.
|
||||
static LLSDMessage httpListener;
|
||||
|
||||
LLSDMessage::LLSDMessage():
|
||||
// Instantiating our own local LLEventPump with a string name the
|
||||
// constructor is NOT allowed to tweak is a way of ensuring Singleton
|
||||
// semantics: attempting to instantiate a second LLSDMessage object would
|
||||
// throw LLEventPump::DupPumpName.
|
||||
mEventPump("LLHTTPClient")
|
||||
{
|
||||
mEventPump.listen("self", boost::bind(&LLSDMessage::httpListener, this, _1));
|
||||
}
|
||||
|
||||
bool LLSDMessage::httpListener(const LLSD& request)
|
||||
{
|
||||
// Extract what we want from the request object. We do it all up front
|
||||
// partly to document what we expect.
|
||||
LLSD::String url(request["url"]);
|
||||
LLSD payload(request["payload"]);
|
||||
LLSD::String reply(request["reply"]);
|
||||
LLSD::String error(request["error"]);
|
||||
LLSD::Real timeout(request["timeout"]);
|
||||
// If the LLSD doesn't even have a "url" key, we doubt it was intended for
|
||||
// this listener.
|
||||
if (url.empty())
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "request event without 'url' key to '" << mEventPump.getName() << "'";
|
||||
throw ArgError(out.str());
|
||||
}
|
||||
// Establish default timeout. This test relies on LLSD::asReal() returning
|
||||
// exactly 0.0 for an undef value.
|
||||
if (! timeout)
|
||||
{
|
||||
timeout = HTTP_REQUEST_EXPIRY_SECS;
|
||||
}
|
||||
LLHTTPClient::post(url, payload,
|
||||
new LLSDMessage::EventResponder(LLEventPumps::instance(),
|
||||
request,
|
||||
url, "POST", reply, error),
|
||||
LLSD(), // headers
|
||||
timeout);
|
||||
return false;
|
||||
}
|
||||
|
||||
void LLSDMessage::EventResponder::result(const LLSD& data)
|
||||
{
|
||||
// If our caller passed an empty replyPump name, they're not
|
||||
// listening: this is a fire-and-forget message. Don't bother posting
|
||||
// to the pump whose name is "".
|
||||
if (! mReplyPump.empty())
|
||||
{
|
||||
LLSD response(data);
|
||||
mReqID.stamp(response);
|
||||
mPumps.obtain(mReplyPump).post(response);
|
||||
}
|
||||
else // default success handling
|
||||
{
|
||||
LL_INFOS("LLSDMessage::EventResponder")
|
||||
<< "'" << mMessage << "' to '" << mTarget << "' succeeded"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string& reason, const LLSD& content)
|
||||
{
|
||||
// If our caller passed an empty errorPump name, they're not
|
||||
// listening: "default error handling is acceptable." Only post to an
|
||||
// explicit pump name.
|
||||
if (! mErrorPump.empty())
|
||||
{
|
||||
LLSD info(mReqID.makeResponse());
|
||||
info["target"] = mTarget;
|
||||
info["message"] = mMessage;
|
||||
info["status"] = LLSD::Integer(status);
|
||||
info["reason"] = reason;
|
||||
info["content"] = content;
|
||||
mPumps.obtain(mErrorPump).post(info);
|
||||
}
|
||||
else // default error handling
|
||||
{
|
||||
// convention seems to be to use llinfos, but that seems a bit casual?
|
||||
LL_WARNS("LLSDMessage::EventResponder")
|
||||
<< "'" << mMessage << "' to '" << mTarget
|
||||
<< "' failed with code " << status << ": " << reason << '\n'
|
||||
<< ll_pretty_print_sd(content)
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderPtr responder,
|
||||
const std::string& name):
|
||||
mResponder(responder),
|
||||
mReplyPump(name + ".reply", true), // tweak name for uniqueness
|
||||
mErrorPump(name + ".error", true)
|
||||
{
|
||||
mReplyPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, true));
|
||||
mErrorPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, false));
|
||||
}
|
||||
|
||||
bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
mResponder->result(payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
mResponder->errorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
|
||||
}
|
||||
|
||||
/*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/
|
||||
delete this;
|
||||
// Destruction of mResponder will usually implicitly free its referent as well
|
||||
/*------------------------- NOTHING AFTER THIS -------------------------*/
|
||||
return false;
|
||||
}
|
||||
|
||||
void LLSDMessage::link()
|
||||
{
|
||||
}
|
||||
166
indra/llmessage/llsdmessage.h
Normal file
166
indra/llmessage/llsdmessage.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @file llsdmessage.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2008-10-30
|
||||
* @brief API intended to unify sending capability, UDP and TCP messages:
|
||||
* https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes
|
||||
*
|
||||
* $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_LLSDMESSAGE_H)
|
||||
#define LL_LLSDMESSAGE_H
|
||||
|
||||
#include "llerror.h" // LOG_CLASS()
|
||||
#include "llevents.h" // LLEventPumps
|
||||
#include "llhttpclient.h"
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
class LLSD;
|
||||
|
||||
/**
|
||||
* Class managing the messaging API described in
|
||||
* https://wiki.lindenlab.com/wiki/Viewer:Messaging/Messaging_Notes
|
||||
*/
|
||||
class LLSDMessage
|
||||
{
|
||||
LOG_CLASS(LLSDMessage);
|
||||
|
||||
public:
|
||||
LLSDMessage();
|
||||
|
||||
/// Exception if you specify arguments badly
|
||||
struct ArgError: public std::runtime_error
|
||||
{
|
||||
ArgError(const std::string& what):
|
||||
std::runtime_error(std::string("ArgError: ") + what) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* The response idiom used by LLSDMessage -- LLEventPump names on which to
|
||||
* post reply or error -- is designed for the case in which your
|
||||
* reply/error handlers are methods on the same class as the method
|
||||
* sending the message. Any state available to the sending method that
|
||||
* must be visible to the reply/error methods can conveniently be stored
|
||||
* on that class itself, if it's not already.
|
||||
*
|
||||
* The LLHTTPClient::Responder idiom requires a separate instance of a
|
||||
* separate class so that it can dispatch to the code of interest by
|
||||
* calling canonical virtual methods. Interesting state must be copied
|
||||
* into that new object.
|
||||
*
|
||||
* With some trepidation, because existing response code is packaged in
|
||||
* LLHTTPClient::Responder subclasses, we provide this adapter class
|
||||
* <i>for transitional purposes only.</i> Instantiate a new heap
|
||||
* ResponderAdapter with your new LLHTTPClient::ResponderPtr. Pass
|
||||
* ResponderAdapter::getReplyName() and/or getErrorName() in your
|
||||
* LLSDMessage (or LLViewerRegion::getCapAPI()) request event. The
|
||||
* ResponderAdapter will call the appropriate Responder method, then
|
||||
* @c delete itself.
|
||||
*/
|
||||
class ResponderAdapter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Bind the new LLHTTPClient::Responder subclass instance.
|
||||
*
|
||||
* Passing the constructor a name other than the default is only
|
||||
* interesting if you suspect some usage will lead to an exception or
|
||||
* log message.
|
||||
*/
|
||||
ResponderAdapter(LLHTTPClient::ResponderPtr responder,
|
||||
const std::string& name="ResponderAdapter");
|
||||
|
||||
/// EventPump name on which LLSDMessage should post reply event
|
||||
std::string getReplyName() const { return mReplyPump.getName(); }
|
||||
/// EventPump name on which LLSDMessage should post error event
|
||||
std::string getErrorName() const { return mErrorPump.getName(); }
|
||||
|
||||
private:
|
||||
// We have two different LLEventStreams, though we route them both to
|
||||
// the same listener, so that we can bind an extra flag identifying
|
||||
// which case (reply or error) reached that listener.
|
||||
bool listener(const LLSD&, bool success);
|
||||
|
||||
LLHTTPClient::ResponderPtr mResponder;
|
||||
LLEventStream mReplyPump, mErrorPump;
|
||||
};
|
||||
|
||||
/**
|
||||
* Force our implementation file to be linked with caller. The .cpp file
|
||||
* contains a static instance of this class, which must be linked into the
|
||||
* executable to support the canonical listener. But since the primary
|
||||
* interface to that static instance is via a named LLEventPump rather
|
||||
* than by direct reference, the linker doesn't necessarily perceive the
|
||||
* necessity to bring in the translation unit. Referencing this dummy
|
||||
* method forces the issue.
|
||||
*/
|
||||
static void link();
|
||||
|
||||
private:
|
||||
friend class LLCapabilityListener;
|
||||
/// Responder used for internal purposes by LLSDMessage and
|
||||
/// LLCapabilityListener. Others should use higher-level APIs.
|
||||
class EventResponder: public LLHTTPClient::Responder
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* LLHTTPClient::Responder that dispatches via named LLEventPump instances.
|
||||
* We bind LLEventPumps, even though it's an LLSingleton, for testability.
|
||||
* We bind the string names of the desired LLEventPump instances rather
|
||||
* than actually obtain()ing them so we only obtain() the one we're going
|
||||
* to use. If the caller doesn't bother to listen() on it, the other pump
|
||||
* may never materialize at all.
|
||||
* @a target and @a message are only to clarify error processing.
|
||||
* For a capability message, @a target should be the region description,
|
||||
* @a message should be the capability name.
|
||||
* For a service with a visible URL, pass the URL as @a target and the HTTP verb
|
||||
* (e.g. "POST") as @a message.
|
||||
*/
|
||||
EventResponder(LLEventPumps& pumps,
|
||||
const LLSD& request,
|
||||
const std::string& target, const std::string& message,
|
||||
const std::string& replyPump, const std::string& errorPump):
|
||||
mPumps(pumps),
|
||||
mReqID(request),
|
||||
mTarget(target),
|
||||
mMessage(message),
|
||||
mReplyPump(replyPump),
|
||||
mErrorPump(errorPump)
|
||||
{}
|
||||
|
||||
virtual void result(const LLSD& data);
|
||||
virtual void errorWithContent(U32 status, const std::string& reason, const LLSD& content);
|
||||
|
||||
private:
|
||||
LLEventPumps& mPumps;
|
||||
LLReqID mReqID;
|
||||
const std::string mTarget, mMessage, mReplyPump, mErrorPump;
|
||||
};
|
||||
|
||||
private:
|
||||
bool httpListener(const LLSD&);
|
||||
LLEventStream mEventPump;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLSDMESSAGE_H) */
|
||||
@@ -297,9 +297,10 @@ S32 getElementSize(const LLSD& llsd)
|
||||
case LLSD::TypeMap:
|
||||
case LLSD::TypeArray:
|
||||
case LLSD::TypeUndefined:
|
||||
default: // TypeLLSDTypeEnd, TypeLLSDNumTypes, etc.
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
//return 0;
|
||||
}
|
||||
|
||||
//virtual
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
#include "v3math.h"
|
||||
#include "v4math.h"
|
||||
#include "lltransfertargetvfile.h"
|
||||
#include "llmemtype.h"
|
||||
|
||||
// <edit>
|
||||
#include "llrand.h"
|
||||
@@ -556,11 +557,11 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count, bool faked_message, U8 fak
|
||||
S32 acks = 0;
|
||||
S32 true_rcv_size = 0;
|
||||
|
||||
U8* buffer = mTrueReceiveBuffer.buffer;
|
||||
U8* buffer = mTrueReceiveBuffer;
|
||||
|
||||
if(!faked_message)
|
||||
{
|
||||
mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer.buffer);
|
||||
mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer);
|
||||
receive_size = mTrueReceiveSize;
|
||||
mLastSender = mPacketRing.getLastSender();
|
||||
mLastReceivingIF = mPacketRing.getLastReceivingInterface();
|
||||
@@ -635,7 +636,7 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count, bool faked_message, U8 fak
|
||||
for(S32 i = 0; i < acks; ++i)
|
||||
{
|
||||
true_rcv_size -= sizeof(TPACKETID);
|
||||
memcpy(&mem_id, &buffer[true_rcv_size], /* Flawfinder: ignore*/
|
||||
memcpy(&mem_id, &mTrueReceiveBuffer[true_rcv_size], /* Flawfinder: ignore*/
|
||||
sizeof(TPACKETID));
|
||||
packet_id = ntohl(mem_id);
|
||||
//LL_INFOS("Messaging") << "got ack: " << packet_id << llendl;
|
||||
@@ -811,6 +812,7 @@ S32 LLMessageSystem::getReceiveBytes() const
|
||||
|
||||
void LLMessageSystem::processAcks()
|
||||
{
|
||||
LLMemType mt_pa(LLMemType::MTYPE_MESSAGE_PROCESS_ACKS);
|
||||
F64 mt_sec = getMessageTimeSeconds();
|
||||
{
|
||||
gTransferManager.updateTransfers();
|
||||
@@ -3405,7 +3407,7 @@ void LLMessageSystem::dumpPacketToLog()
|
||||
{
|
||||
S32 offset = cur_line_pos * 3;
|
||||
snprintf(line_buffer + offset, sizeof(line_buffer) - offset,
|
||||
"%02x ", mTrueReceiveBuffer.buffer[i]); /* Flawfinder: ignore */
|
||||
"%02x ", mTrueReceiveBuffer[i]); /* Flawfinder: ignore */
|
||||
cur_line_pos++;
|
||||
if (cur_line_pos >= 16)
|
||||
{
|
||||
@@ -4048,6 +4050,7 @@ void LLMessageSystem::setTimeDecodesSpamThreshold( F32 seconds )
|
||||
// TODO: babbage: move gServicePump in to LLMessageSystem?
|
||||
bool LLMessageSystem::checkAllMessages(S64 frame_count, LLPumpIO* http_pump)
|
||||
{
|
||||
LLMemType mt_cam(LLMemType::MTYPE_MESSAGE_CHECK_ALL);
|
||||
if(checkMessages(frame_count))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -778,18 +778,7 @@ private:
|
||||
LLMessagePollInfo *mPollInfop;
|
||||
|
||||
U8 mEncodedRecvBuffer[MAX_BUFFER_SIZE];
|
||||
|
||||
// Push current alignment to stack and set alignment to 1 byte boundary
|
||||
#pragma pack(push,1)
|
||||
|
||||
struct ReceiveBuffer_t
|
||||
{
|
||||
proxywrap_t header;
|
||||
U8 buffer[MAX_BUFFER_SIZE];
|
||||
} mTrueReceiveBuffer;
|
||||
|
||||
#pragma pack(pop) /* restore original alignment from stack */
|
||||
|
||||
U8 mTrueReceiveBuffer[MAX_BUFFER_SIZE];
|
||||
S32 mTrueReceiveSize;
|
||||
|
||||
// Must be valid during decode
|
||||
|
||||
@@ -127,6 +127,16 @@ LLMaterialTable::~LLMaterialTable()
|
||||
mMaterialInfoList.clear();
|
||||
}
|
||||
|
||||
void LLMaterialTable::initTableTransNames(std::map<std::string, std::string> namemap)
|
||||
{
|
||||
for (info_list_t::iterator iter = mMaterialInfoList.begin();
|
||||
iter != mMaterialInfoList.end(); ++iter)
|
||||
{
|
||||
LLMaterialInfo *infop = *iter;
|
||||
std::string name = infop->mName;
|
||||
infop->mName = namemap[name];
|
||||
}
|
||||
}
|
||||
void LLMaterialTable::initBasicTable()
|
||||
{
|
||||
// *TODO: Translate
|
||||
|
||||
@@ -110,6 +110,7 @@ public:
|
||||
|
||||
void initBasicTable();
|
||||
|
||||
void initTableTransNames(std::map<std::string, std::string> namemap);
|
||||
|
||||
BOOL add(U8 mcode, const std::string& name, const LLUUID &uuid);
|
||||
BOOL addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
|
||||
|
||||
@@ -2,31 +2,25 @@
|
||||
* @file llvolumexml.cpp
|
||||
* @brief LLVolumeXml base class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -40,9 +34,9 @@
|
||||
|
||||
//============================================================================
|
||||
|
||||
LLXMLNode *LLVolumeXml::exportProfileParams(const LLProfileParams* params)
|
||||
LLPointer<LLXMLNode> LLVolumeXml::exportProfileParams(const LLProfileParams* params)
|
||||
{
|
||||
LLXMLNode *ret = new LLXMLNode("profile", FALSE);
|
||||
LLPointer<LLXMLNode> ret = new LLXMLNode("profile", FALSE);
|
||||
|
||||
ret->createChild("curve_type", TRUE)->setByteValue(1, ¶ms->getCurveType());
|
||||
ret->createChild("interval", FALSE)->setFloatValue(2, ¶ms->getBegin());
|
||||
@@ -52,9 +46,9 @@ LLXMLNode *LLVolumeXml::exportProfileParams(const LLProfileParams* params)
|
||||
}
|
||||
|
||||
|
||||
LLXMLNode *LLVolumeXml::exportPathParams(const LLPathParams* params)
|
||||
LLPointer<LLXMLNode> LLVolumeXml::exportPathParams(const LLPathParams* params)
|
||||
{
|
||||
LLXMLNode *ret = new LLXMLNode("path", FALSE);
|
||||
LLPointer<LLXMLNode> ret = new LLXMLNode("path", FALSE);
|
||||
ret->createChild("curve_type", TRUE)->setByteValue(1, ¶ms->getCurveType());
|
||||
ret->createChild("interval", FALSE)->setFloatValue(2, ¶ms->getBegin());
|
||||
ret->createChild("scale", FALSE)->setFloatValue(2, params->getScale().mV);
|
||||
@@ -69,12 +63,15 @@ LLXMLNode *LLVolumeXml::exportPathParams(const LLPathParams* params)
|
||||
}
|
||||
|
||||
|
||||
LLXMLNode *LLVolumeXml::exportVolumeParams(const LLVolumeParams* params)
|
||||
LLPointer<LLXMLNode> LLVolumeXml::exportVolumeParams(const LLVolumeParams* params)
|
||||
{
|
||||
LLXMLNode *ret = new LLXMLNode("shape", FALSE);
|
||||
LLPointer<LLXMLNode> ret = new LLXMLNode("shape", FALSE);
|
||||
|
||||
exportPathParams(¶ms->getPathParams())->setParent(ret);
|
||||
exportProfileParams(¶ms->getProfileParams())->setParent(ret);
|
||||
LLPointer<LLXMLNode> node ;
|
||||
node = exportPathParams(¶ms->getPathParams()) ;
|
||||
node->setParent(ret);
|
||||
node = exportProfileParams(¶ms->getProfileParams()) ;
|
||||
node->setParent(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2,31 +2,25 @@
|
||||
* @file llvolumexml.h
|
||||
* @brief LLVolumeXml base class
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -40,11 +34,11 @@
|
||||
class LLVolumeXml
|
||||
{
|
||||
public:
|
||||
static LLXMLNode* exportProfileParams(const LLProfileParams* params);
|
||||
static LLPointer<LLXMLNode> exportProfileParams(const LLProfileParams* params);
|
||||
|
||||
static LLXMLNode* exportPathParams(const LLPathParams* params);
|
||||
static LLPointer<LLXMLNode> exportPathParams(const LLPathParams* params);
|
||||
|
||||
static LLXMLNode* exportVolumeParams(const LLVolumeParams* params);
|
||||
static LLPointer<LLXMLNode> exportVolumeParams(const LLVolumeParams* params);
|
||||
};
|
||||
|
||||
#endif // LL_LLVOLUMEXML_H
|
||||
|
||||
@@ -2,31 +2,25 @@
|
||||
* @file llcubemap.cpp
|
||||
* @brief LLCubeMap class implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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"
|
||||
@@ -266,7 +260,7 @@ void LLCubeMap::setMatrix(S32 stage)
|
||||
|
||||
if (mMatrixStage < 0) return;
|
||||
|
||||
if (stage > 0)
|
||||
//if (stage > 0)
|
||||
{
|
||||
gGL.getTexUnit(stage)->activate();
|
||||
}
|
||||
@@ -285,17 +279,17 @@ void LLCubeMap::setMatrix(S32 stage)
|
||||
gGL.loadMatrix((F32 *)trans.mMatrix);
|
||||
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
||||
|
||||
if (stage > 0)
|
||||
/*if (stage > 0)
|
||||
{
|
||||
gGL.getTexUnit(0)->activate();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void LLCubeMap::restoreMatrix()
|
||||
{
|
||||
if (mMatrixStage < 0) return;
|
||||
|
||||
if (mMatrixStage > 0)
|
||||
//if (mMatrixStage > 0)
|
||||
{
|
||||
gGL.getTexUnit(mMatrixStage)->activate();
|
||||
}
|
||||
@@ -303,10 +297,10 @@ void LLCubeMap::restoreMatrix()
|
||||
gGL.popMatrix();
|
||||
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
||||
|
||||
if (mMatrixStage > 0)
|
||||
/*if (mMatrixStage > 0)
|
||||
{
|
||||
gGL.getTexUnit(0)->activate();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void LLCubeMap::setReflection (void)
|
||||
|
||||
@@ -2,31 +2,25 @@
|
||||
* @file llcubemap.h
|
||||
* @brief LLCubeMap class definition
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
|
||||
@@ -1141,7 +1141,7 @@ LLFontGL* LLFontGL::getFontSansSerifBig()
|
||||
//static
|
||||
LLFontGL* LLFontGL::getFontSansSerifHuge()
|
||||
{
|
||||
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Large",0));
|
||||
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Huge",0));
|
||||
return fontp;
|
||||
}
|
||||
|
||||
@@ -1320,4 +1320,4 @@ void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, con
|
||||
|
||||
}
|
||||
gGL.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "llmath.h"
|
||||
#include "m4math.h"
|
||||
#include "llstring.h"
|
||||
#include "llmemtype.h"
|
||||
#include "llstacktrace.h"
|
||||
|
||||
#include "llglheaders.h"
|
||||
@@ -2203,6 +2204,7 @@ void LLGLNamePool::release(GLuint name)
|
||||
//static
|
||||
void LLGLNamePool::upkeepPools()
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_UPKEEP_POOLS);
|
||||
for (tracker_t::instance_iter iter = beginInstances(); iter != endInstances(); ++iter)
|
||||
{
|
||||
LLGLNamePool & pool = *iter;
|
||||
|
||||
@@ -151,8 +151,7 @@ BOOL LLGLSLShader::createShader(vector<string> * attributes,
|
||||
mProgramObject = glCreateProgramObjectARB();
|
||||
|
||||
static const LLCachedControl<bool> no_texture_indexing("ShyotlUseLegacyTextureBatching",false);
|
||||
//static const LLCachedControl<bool> use_legacy_path("ShyotlUseLegacyRenderPath", false); //Legacy does not jive with new batching.
|
||||
if (gGLManager.mGLVersion < 3.1f || no_texture_indexing /*|| use_legacy_path*/)
|
||||
if (gGLManager.mGLVersion < 3.1f || no_texture_indexing)
|
||||
{ //force indexed texture channels to 1 if GL version is old (performance improvement for drivers with poor branching shader model support)
|
||||
mFeatures.mIndexedTextureChannels = llmin(mFeatures.mIndexedTextureChannels, 1);
|
||||
}
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
/**
|
||||
/**
|
||||
* @file llrender.cpp
|
||||
* @brief LLRender implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -1009,7 +1003,7 @@ void LLLightState::setSpotDirection(const LLVector3& direction)
|
||||
const glh::matrix4f& mat = gGL.getModelviewMatrix();
|
||||
mat.mult_matrix_dir(dir);
|
||||
|
||||
mSpotDirection.set(direction);
|
||||
mSpotDirection.set(dir.v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1446,6 +1440,8 @@ void LLRender::loadIdentity()
|
||||
flush();
|
||||
|
||||
{
|
||||
llassert_always(mMatrixMode < NUM_MATRIX_MODES) ;
|
||||
|
||||
mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].make_identity();
|
||||
mMatHash[mMatrixMode]++;
|
||||
}
|
||||
|
||||
@@ -7,31 +7,25 @@
|
||||
* code, to define an interface for a multiple rendering API abstraction of the UI
|
||||
* rendering, and to abstract out direct rendering calls in a way that is cleaner and easier to maintain.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
|
||||
@@ -699,7 +699,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
|
||||
text[count++] = strdup("return texture2D(tex0, texcoord);\n");
|
||||
text[count++] = strdup("}\n");
|
||||
}
|
||||
else if (gGLManager.mGLVersion >= 3.f)
|
||||
else if (gGLManager.mGLVersion >= 3.f && !(gGLManager.mIsATI && gGLManager.mGLVersion < 3.3f) )
|
||||
{
|
||||
text[count++] = strdup("\tswitch (int(vary_texture_index+0.25))\n");
|
||||
text[count++] = strdup("\t{\n");
|
||||
@@ -1119,6 +1119,8 @@ void LLShaderMgr::initAttribsAndUniforms()
|
||||
mReservedUniforms.push_back("magnification");
|
||||
mReservedUniforms.push_back("max_cof");
|
||||
mReservedUniforms.push_back("res_scale");
|
||||
mReservedUniforms.push_back("dof_width");
|
||||
mReservedUniforms.push_back("dof_height");
|
||||
|
||||
mReservedUniforms.push_back("depthMap");
|
||||
mReservedUniforms.push_back("shadowMap0");
|
||||
|
||||
@@ -2,31 +2,25 @@
|
||||
* @file llshadermgr.h
|
||||
* @brief Shader Manager
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||
* 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
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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$
|
||||
*/
|
||||
|
||||
@@ -148,6 +142,8 @@ public:
|
||||
DOF_MAGNIFICATION,
|
||||
DOF_MAX_COF,
|
||||
DOF_RES_SCALE,
|
||||
DOF_WIDTH,
|
||||
DOF_HEIGHT,
|
||||
|
||||
DEFERRED_DEPTH,
|
||||
DEFERRED_SHADOW0,
|
||||
|
||||
@@ -731,7 +731,7 @@ void LLVertexBuffer::unbind()
|
||||
//static
|
||||
void LLVertexBuffer::cleanupClass()
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
|
||||
unbind();
|
||||
|
||||
sStreamIBOPool.cleanup();
|
||||
@@ -750,41 +750,25 @@ void LLVertexBuffer::cleanupClass()
|
||||
|
||||
S32 LLVertexBuffer::determineUsage(S32 usage)
|
||||
{
|
||||
S32 ret_usage = usage;
|
||||
|
||||
if (!sEnableVBOs)
|
||||
{
|
||||
ret_usage = 0;
|
||||
}
|
||||
|
||||
if (usage == GL_STREAM_DRAW_ARB && !sUseStreamDraw)
|
||||
{
|
||||
ret_usage = 0;
|
||||
}
|
||||
|
||||
if (usage == GL_DYNAMIC_DRAW_ARB && sPreferStreamDraw)
|
||||
{
|
||||
ret_usage = GL_STREAM_DRAW_ARB;
|
||||
}
|
||||
|
||||
if (usage == 0 && LLRender::sGLCoreProfile)
|
||||
if (LLRender::sGLCoreProfile)
|
||||
{ //MUST use VBOs for all rendering
|
||||
ret_usage = GL_STREAM_DRAW_ARB;
|
||||
if(!usage)
|
||||
return GL_STREAM_DRAW_ARB;
|
||||
}
|
||||
|
||||
if (usage && usage != GL_STREAM_DRAW_ARB)
|
||||
{ //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
|
||||
if (sDisableVBOMapping)
|
||||
{ //always use stream draw if VBO mapping is disabled
|
||||
ret_usage = GL_STREAM_DRAW_ARB;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_usage = GL_DYNAMIC_DRAW_ARB;
|
||||
}
|
||||
else if (!sEnableVBOs || !usage || (!sUseStreamDraw && usage == GL_STREAM_DRAW_ARB))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//Only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default.
|
||||
//Always use stream_draw VBO if mapping is disabled, or stream is preferred or expected
|
||||
if( sDisableVBOMapping || sPreferStreamDraw || (usage == GL_STREAM_DRAW_ARB))
|
||||
{
|
||||
return GL_STREAM_DRAW_ARB;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GL_DYNAMIC_DRAW_ARB;
|
||||
}
|
||||
|
||||
return ret_usage;
|
||||
}
|
||||
|
||||
LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
|
||||
@@ -812,16 +796,9 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
|
||||
mMappable(false),
|
||||
mFence(NULL)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR);
|
||||
|
||||
if (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping)
|
||||
{
|
||||
mMappable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMappable = false;
|
||||
}
|
||||
mMappable = (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping);
|
||||
|
||||
//zero out offsets
|
||||
for (U32 i = 0; i < TYPE_MAX; i++)
|
||||
@@ -880,7 +857,7 @@ S32 LLVertexBuffer::getSize() const
|
||||
//virtual
|
||||
LLVertexBuffer::~LLVertexBuffer()
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTRUCTOR);
|
||||
destroyGLBuffer();
|
||||
destroyGLIndices();
|
||||
|
||||
@@ -997,7 +974,7 @@ void LLVertexBuffer::releaseIndices()
|
||||
|
||||
void LLVertexBuffer::createGLBuffer(U32 size)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_VERTICES);
|
||||
|
||||
if (mGLBuffer)
|
||||
{
|
||||
@@ -1028,7 +1005,7 @@ void LLVertexBuffer::createGLBuffer(U32 size)
|
||||
|
||||
void LLVertexBuffer::createGLIndices(U32 size)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_INDICES);
|
||||
|
||||
if (mGLIndices)
|
||||
{
|
||||
@@ -1064,7 +1041,7 @@ void LLVertexBuffer::createGLIndices(U32 size)
|
||||
|
||||
void LLVertexBuffer::destroyGLBuffer()
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_BUFFER);
|
||||
if (mGLBuffer)
|
||||
{
|
||||
if (mMappedDataUsingVBOs)
|
||||
@@ -1085,7 +1062,7 @@ void LLVertexBuffer::destroyGLBuffer()
|
||||
|
||||
void LLVertexBuffer::destroyGLIndices()
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_INDICES);
|
||||
if (mGLIndices)
|
||||
{
|
||||
if (mMappedIndexDataUsingVBOs)
|
||||
@@ -1106,7 +1083,7 @@ void LLVertexBuffer::destroyGLIndices()
|
||||
|
||||
void LLVertexBuffer::updateNumVerts(S32 nverts)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_VERTS);
|
||||
|
||||
llassert(nverts >= 0);
|
||||
|
||||
@@ -1128,7 +1105,7 @@ void LLVertexBuffer::updateNumVerts(S32 nverts)
|
||||
|
||||
void LLVertexBuffer::updateNumIndices(S32 nindices)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_INDICES);
|
||||
|
||||
llassert(nindices >= 0);
|
||||
|
||||
@@ -1144,7 +1121,7 @@ void LLVertexBuffer::updateNumIndices(S32 nindices)
|
||||
|
||||
void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER);
|
||||
|
||||
stop_glerror();
|
||||
|
||||
@@ -1265,7 +1242,7 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
|
||||
llassert(newnverts >= 0);
|
||||
llassert(newnindices >= 0);
|
||||
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_RESIZE_BUFFER);
|
||||
|
||||
updateNumVerts(newnverts);
|
||||
updateNumIndices(newnindices);
|
||||
@@ -1313,7 +1290,7 @@ static LLFastTimer::DeclareTimer FTM_VBO_MAP_BUFFER("VBO Map");
|
||||
volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
|
||||
{
|
||||
bindGLBuffer(true);
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
|
||||
if (mFinal)
|
||||
{
|
||||
llerrs << "LLVertexBuffer::mapVeretxBuffer() called on a finalized buffer." << llendl;
|
||||
@@ -1362,6 +1339,7 @@ volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, boo
|
||||
|
||||
if (!mVertexLocked)
|
||||
{
|
||||
LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES);
|
||||
mVertexLocked = true;
|
||||
sMappedCount++;
|
||||
stop_glerror();
|
||||
@@ -1492,7 +1470,7 @@ static LLFastTimer::DeclareTimer FTM_VBO_MAP_INDEX("IBO Map");
|
||||
|
||||
volatile U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
|
||||
bindGLIndices(true);
|
||||
if (mFinal)
|
||||
{
|
||||
@@ -1539,7 +1517,7 @@ volatile U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range
|
||||
|
||||
if (!mIndexLocked)
|
||||
{
|
||||
//LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES);
|
||||
LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES);
|
||||
|
||||
mIndexLocked = true;
|
||||
sMappedCount++;
|
||||
@@ -1663,7 +1641,7 @@ static LLFastTimer::DeclareTimer FTM_IBO_FLUSH_RANGE("Flush IBO Range");
|
||||
|
||||
void LLVertexBuffer::unmapBuffer()
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER);
|
||||
if (!useVBOs())
|
||||
{
|
||||
return ; //nothing to unmap
|
||||
@@ -2007,7 +1985,7 @@ void LLVertexBuffer::setBuffer(U32 data_mask)
|
||||
{
|
||||
flush();
|
||||
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_BUFFER);
|
||||
//set up pointers if the data mask is different ...
|
||||
bool setup = (sLastMask != data_mask);
|
||||
|
||||
@@ -2149,7 +2127,7 @@ void LLVertexBuffer::setBuffer(U32 data_mask)
|
||||
// virtual (default)
|
||||
void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
|
||||
{
|
||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
||||
LLMemType mt2(LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER);
|
||||
stop_glerror();
|
||||
volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "v4math.h"
|
||||
#include "v4coloru.h"
|
||||
#include "llstrider.h"
|
||||
#include "llmemory.h"
|
||||
#include "llrender.h"
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
@@ -611,7 +611,6 @@ void LLButton::draw()
|
||||
S32 text_left = mLeftHPad;
|
||||
S32 text_right = getRect().getWidth() - mRightHPad;
|
||||
S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
|
||||
S32 text_middle = text_left + text_width/2;
|
||||
|
||||
// draw overlay image
|
||||
if (mImageOverlay.notNull() && mImageOverlay->getWidth() > 1)
|
||||
@@ -646,7 +645,6 @@ void LLButton::draw()
|
||||
case LLFontGL::LEFT:
|
||||
text_left += overlay_width + 1;
|
||||
text_width -= overlay_width + 1;
|
||||
text_middle += (overlay_width+1)/4;
|
||||
mImageOverlay->draw(
|
||||
mLeftHPad,
|
||||
center_y - (overlay_height / 2),
|
||||
@@ -665,7 +663,6 @@ void LLButton::draw()
|
||||
case LLFontGL::RIGHT:
|
||||
text_right -= overlay_width + 1;
|
||||
text_width -= overlay_width + 1;
|
||||
text_middle += (overlay_width+1)/4;
|
||||
mImageOverlay->draw(
|
||||
getRect().getWidth() - mRightHPad - overlay_width,
|
||||
center_y - (overlay_height / 2),
|
||||
@@ -691,10 +688,7 @@ void LLButton::draw()
|
||||
x = text_right;
|
||||
break;
|
||||
case LLFontGL::HCENTER:
|
||||
{
|
||||
S32 actual_width = mGLFont->getWidth(label.c_str());
|
||||
x = llmax(text_middle, text_left + actual_width/2);
|
||||
}
|
||||
x = text_left + (text_width / 2);
|
||||
break;
|
||||
case LLFontGL::LEFT:
|
||||
default:
|
||||
@@ -710,7 +704,6 @@ void LLButton::draw()
|
||||
x++;
|
||||
}
|
||||
|
||||
|
||||
mGLFont->render(label, 0,
|
||||
(F32)x,
|
||||
(F32)(LLBUTTON_V_PAD + y_offset),
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
#include "llevent.h"
|
||||
|
||||
template <class T>
|
||||
class LLMemberListener : public LLSimpleListener
|
||||
class LLMemberListener : public LLOldEvents::LLSimpleListener
|
||||
{
|
||||
public:
|
||||
LLMemberListener() : mPtr(NULL), mRegisteredName() { }
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
}
|
||||
|
||||
// This is what you have to override to handle this event
|
||||
virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0;
|
||||
virtual bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) = 0;
|
||||
|
||||
protected:
|
||||
T *mPtr; // The object that this listener manipulates
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user