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
|
/installed.xml
|
||||||
|
/indra/llcommon/llversionviewer.h
|
||||||
/indra/build-*
|
/indra/build-*
|
||||||
/indra/tools/vstool/obj/
|
/indra/tools/vstool/obj/
|
||||||
*.aps
|
*.aps
|
||||||
@@ -11,11 +12,13 @@
|
|||||||
/indra/viewer-*
|
/indra/viewer-*
|
||||||
/indra/newview/vivox-runtime/
|
/indra/newview/vivox-runtime/
|
||||||
/libraries/
|
/libraries/
|
||||||
|
/lib/
|
||||||
*.pyc
|
*.pyc
|
||||||
*.orig
|
*.orig
|
||||||
*.rej
|
*.rej
|
||||||
*.bak
|
*.bak
|
||||||
*~
|
*~
|
||||||
|
*.DS_Store
|
||||||
/LICENSES/
|
/LICENSES/
|
||||||
/edited-files.txt
|
/edited-files.txt
|
||||||
qtcreator-build/
|
qtcreator-build/
|
||||||
|
|||||||
@@ -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)
|
include(Variables)
|
||||||
|
|
||||||
# Load versions now. Install locations need them.
|
# Load versions now. Install locations need them.
|
||||||
include(Versions)
|
include(BuildVersion)
|
||||||
|
|
||||||
include(UnixInstall)
|
include(UnixInstall)
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;ReleaseSSE2;Debug" CACHE S
|
|||||||
# Platform-specific compilation flags.
|
# Platform-specific compilation flags.
|
||||||
|
|
||||||
if (WINDOWS)
|
if (WINDOWS)
|
||||||
|
# Remove default /Zm1000 flag that cmake inserts
|
||||||
|
string (REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||||
|
|
||||||
# Don't build DLLs.
|
# Don't build DLLs.
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
set(BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
@@ -67,6 +70,10 @@ if (WINDOWS)
|
|||||||
/Oy-
|
/Oy-
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# configure win32 API for windows XP+ compatibility
|
||||||
|
set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)")
|
||||||
|
add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}")
|
||||||
|
|
||||||
if(MSVC80 OR MSVC90 OR MSVC10)
|
if(MSVC80 OR MSVC90 OR MSVC10)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE
|
set(CMAKE_CXX_FLAGS_RELEASE
|
||||||
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
|
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
|
||||||
@@ -113,7 +120,7 @@ if (WINDOWS)
|
|||||||
|
|
||||||
endif (WINDOWS)
|
endif (WINDOWS)
|
||||||
|
|
||||||
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math -frounding-math")
|
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math")
|
||||||
|
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
set(CMAKE_SKIP_RPATH TRUE)
|
set(CMAKE_SKIP_RPATH TRUE)
|
||||||
@@ -156,7 +163,7 @@ if (LINUX)
|
|||||||
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
|
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
|
||||||
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
|
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
|
||||||
|
|
||||||
#Lets 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})
|
STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION})
|
||||||
|
|
||||||
#gcc 4.3 and above don't like the LL boost
|
#gcc 4.3 and above don't like the LL boost
|
||||||
@@ -182,6 +189,8 @@ if (LINUX)
|
|||||||
-pthread
|
-pthread
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
|
||||||
|
|
||||||
add_definitions(-DAPPID=secondlife)
|
add_definitions(-DAPPID=secondlife)
|
||||||
add_definitions(-fvisibility=hidden)
|
add_definitions(-fvisibility=hidden)
|
||||||
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
|
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
|
||||||
@@ -200,10 +209,10 @@ if (LINUX)
|
|||||||
if (NOT STANDALONE)
|
if (NOT STANDALONE)
|
||||||
set(MARCH_FLAG " -march=pentium4")
|
set(MARCH_FLAG " -march=pentium4")
|
||||||
endif (NOT STANDALONE)
|
endif (NOT STANDALONE)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -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 -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 -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 -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")
|
endif (${ARCH} STREQUAL "x86_64")
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")
|
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ else (STANDALONE)
|
|||||||
)
|
)
|
||||||
elseif (DARWIN)
|
elseif (DARWIN)
|
||||||
set(APR_LIBRARIES
|
set(APR_LIBRARIES
|
||||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.3.7.dylib
|
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.dylib
|
||||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib
|
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.dylib
|
||||||
)
|
)
|
||||||
set(APRUTIL_LIBRARIES
|
set(APRUTIL_LIBRARIES
|
||||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.3.8.dylib
|
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.dylib
|
||||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib
|
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.dylib
|
||||||
)
|
)
|
||||||
set(APRICONV_LIBRARIES iconv)
|
set(APRICONV_LIBRARIES iconv)
|
||||||
else (WINDOWS)
|
else (WINDOWS)
|
||||||
|
|||||||
@@ -1,23 +1,45 @@
|
|||||||
# -*- cmake -*-
|
# -*- cmake -*-
|
||||||
|
|
||||||
function (build_version _target)
|
# Read version components from the header file.
|
||||||
# Read version components from the header file.
|
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversionviewer.h.in lines
|
||||||
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversion${_target}.h lines
|
REGEX " LL_VERSION_")
|
||||||
REGEX " LL_VERSION_")
|
foreach(line ${lines})
|
||||||
foreach(line ${lines})
|
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
|
||||||
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
|
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
|
||||||
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
|
set(v${comp} "${value}")
|
||||||
set(v${comp} "${value}")
|
endforeach(line)
|
||||||
endforeach(line)
|
|
||||||
|
|
||||||
# Compose the version.
|
execute_process(
|
||||||
set(${_target}_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
|
COMMAND git rev-list HEAD
|
||||||
if (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
OUTPUT_VARIABLE GIT_REV_LIST_STR
|
||||||
message(STATUS "Version of ${_target} is ${${_target}_VERSION}")
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
else (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
message(FATAL_ERROR "Could not determine ${_target} version (${${_target}_VERSION})")
|
)
|
||||||
endif (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
|
||||||
|
|
||||||
# Report version to caller.
|
if(GIT_REV_LIST_STR)
|
||||||
set(${_target}_VERSION "${${_target}_VERSION}" PARENT_SCOPE)
|
string(REPLACE "\n" ";" GIT_REV_LIST ${GIT_REV_LIST_STR})
|
||||||
endfunction (build_version)
|
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)
|
else (WINDOWS)
|
||||||
set(CARES_LIBRARIES cares)
|
set(CARES_LIBRARIES cares)
|
||||||
endif (WINDOWS)
|
endif (WINDOWS)
|
||||||
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares)
|
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares ${LIBS_PREBUILT_DIR}/include/ares )
|
||||||
endif (STANDALONE)
|
endif (STANDALONE)
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ set(cmake_SOURCE_FILES
|
|||||||
UI.cmake
|
UI.cmake
|
||||||
UnixInstall.cmake
|
UnixInstall.cmake
|
||||||
Variables.cmake
|
Variables.cmake
|
||||||
Versions.cmake
|
|
||||||
XmlRpcEpi.cmake
|
XmlRpcEpi.cmake
|
||||||
ZLIB.cmake
|
ZLIB.cmake
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
if (VIEWER AND WINDOWS)
|
if (VIEWER AND WINDOWS)
|
||||||
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
|
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
|
||||||
"$ENV{DXSDK_DIR}/Include"
|
"$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 (March 2009)/Include"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 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
|
find_path(DIRECTX_LIBRARY_DIR dxguid.lib
|
||||||
"$ENV{DXSDK_DIR}/Lib/x86"
|
"$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 (March 2009)/Lib/x86"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"
|
||||||
|
|||||||
@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
|
|||||||
use_prebuilt_binary(fmod)
|
use_prebuilt_binary(fmod)
|
||||||
endif(INSTALL_PROPRIETARY)
|
endif(INSTALL_PROPRIETARY)
|
||||||
|
|
||||||
find_library(FMOD_LIBRARY_RELEASE
|
find_library(FMOD_LIBRARY
|
||||||
NAMES fmod fmodvc fmod-3.75
|
NAMES fmod fmodvc fmod-3.75
|
||||||
PATHS
|
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)
|
if (NOT FMOD_LIBRARY)
|
||||||
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
|
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
|
||||||
if (FMOD_SDK_DIR)
|
if (FMOD_SDK_DIR)
|
||||||
|
|||||||
@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
|
|||||||
use_prebuilt_binary(fmodex)
|
use_prebuilt_binary(fmodex)
|
||||||
endif(INSTALL_PROPRIETARY)
|
endif(INSTALL_PROPRIETARY)
|
||||||
|
|
||||||
find_library(FMODEX_LIBRARY_RELEASE
|
find_library(FMODEX_LIBRARY
|
||||||
NAMES fmodex fmodex_vc fmodexL_vc
|
NAMES fmodex fmodex_vc fmodexL_vc
|
||||||
PATHS
|
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)
|
if (NOT FMODEX_LIBRARY)
|
||||||
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
|
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
|
||||||
if (FMODEX_SDK_DIR)
|
if (FMODEX_SDK_DIR)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
# also defined, but not for general use are
|
# also defined, but not for general use are
|
||||||
# JSONCPP_LIBRARY, where to find the jsoncpp library.
|
# JSONCPP_LIBRARY, where to find the jsoncpp library.
|
||||||
|
|
||||||
FIND_PATH(JSONCPP_INCLUDE_DIR jsoncpp/json.h
|
FIND_PATH(JSONCPP_INCLUDE_DIR json/json.h
|
||||||
/usr/local/include
|
/usr/local/include
|
||||||
/usr/include
|
/usr/include
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
# -*- cmake -*-
|
# -*- cmake -*-
|
||||||
include(Prebuilt)
|
include(Prebuilt)
|
||||||
|
|
||||||
|
if(WORD_SIZE EQUAL 64)
|
||||||
|
set(DISABLE_TCMALLOC TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (STANDALONE)
|
if (STANDALONE)
|
||||||
include(FindGooglePerfTools)
|
include(FindGooglePerfTools)
|
||||||
else (STANDALONE)
|
else (STANDALONE)
|
||||||
if (LINUX OR WINDOWS)
|
if (LINUX OR WINDOWS AND NOT WORD_SIZE EQUAL 64)
|
||||||
use_prebuilt_binary(google)
|
use_prebuilt_binary(google)
|
||||||
endif (LINUX OR WINDOWS)
|
endif (LINUX OR WINDOWS)
|
||||||
if (WINDOWS)
|
if (WINDOWS)
|
||||||
@@ -30,10 +34,6 @@ else ()
|
|||||||
set(USE_GOOGLE_PERFTOOLS OFF)
|
set(USE_GOOGLE_PERFTOOLS OFF)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# XXX Disable temporarily, until we have compilation issues on 64-bit
|
|
||||||
# Etch sorted.
|
|
||||||
#set(USE_GOOGLE_PERFTOOLS OFF)
|
|
||||||
|
|
||||||
if (USE_GOOGLE_PERFTOOLS)
|
if (USE_GOOGLE_PERFTOOLS)
|
||||||
set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
|
set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
|
||||||
include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})
|
include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ endif (DARWIN)
|
|||||||
set(LLCOMMON_INCLUDE_DIRS
|
set(LLCOMMON_INCLUDE_DIRS
|
||||||
${LIBS_OPEN_DIR}/cwdebug
|
${LIBS_OPEN_DIR}/cwdebug
|
||||||
${LIBS_OPEN_DIR}/llcommon
|
${LIBS_OPEN_DIR}/llcommon
|
||||||
|
${APRUTIL_INCLUDE_DIR}
|
||||||
${APR_INCLUDE_DIR}
|
${APR_INCLUDE_DIR}
|
||||||
${Boost_INCLUDE_DIRS}
|
${Boost_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ set(LLPLUGIN_INCLUDE_DIRS
|
|||||||
if (LINUX)
|
if (LINUX)
|
||||||
# In order to support using ld.gold on linux, we need to explicitely
|
# In order to support using ld.gold on linux, we need to explicitely
|
||||||
# specify all libraries that llplugin uses.
|
# specify all libraries that llplugin uses.
|
||||||
set(LLPLUGIN_LIBRARIES llplugin pthread)
|
set(LLPLUGIN_LIBRARIES llplugin pthread)
|
||||||
else (LINUX)
|
else (LINUX)
|
||||||
set(LLPLUGIN_LIBRARIES llplugin)
|
set(LLPLUGIN_LIBRARIES llplugin)
|
||||||
endif (LINUX)
|
endif (LINUX)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ if (WINDOWS)
|
|||||||
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
|
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
|
||||||
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
|
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
|
||||||
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
|
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
|
||||||
|
|
||||||
)
|
)
|
||||||
elseif (EXISTS /etc/debian_version)
|
elseif (EXISTS /etc/debian_version)
|
||||||
# On Debian and Ubuntu, avoid Python 2.4 if possible.
|
# On Debian and Ubuntu, avoid Python 2.4 if possible.
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ else (STANDALONE)
|
|||||||
if (LINUX)
|
if (LINUX)
|
||||||
set(UI_LIBRARIES
|
set(UI_LIBRARIES
|
||||||
atk-1.0
|
atk-1.0
|
||||||
|
X11
|
||||||
gdk-x11-2.0
|
gdk-x11-2.0
|
||||||
gdk_pixbuf-2.0
|
gdk_pixbuf-2.0
|
||||||
Xinerama
|
Xinerama
|
||||||
|
|||||||
@@ -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
|
QtNetwork
|
||||||
QtGui
|
QtGui
|
||||||
QtCore
|
QtCore
|
||||||
jscore
|
crypto
|
||||||
|
ssl
|
||||||
|
# qgif
|
||||||
|
# qjpeg
|
||||||
jpeg
|
jpeg
|
||||||
fontconfig
|
fontconfig
|
||||||
X11
|
X11
|
||||||
Xrender
|
Xrender
|
||||||
|
Xext
|
||||||
GL
|
GL
|
||||||
)
|
)
|
||||||
|
if (WORD_SIZE EQUAL 32)
|
||||||
|
set(WEBKIT_PLUGIN_LIBRARIES ${WEBKIT_PLUGIN_LIBRARIES} jscore)
|
||||||
|
endif (WORD_SIZE EQUAL 32)
|
||||||
endif (STANDALONE)
|
endif (STANDALONE)
|
||||||
endif (WINDOWS)
|
endif (WINDOWS)
|
||||||
|
|||||||
@@ -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 gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk.
|
||||||
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
|
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
|
||||||
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
|
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
|
||||||
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used 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 dc
|
||||||
} // namespace DEBUGCHANNELS
|
} // namespace DEBUGCHANNELS
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ extern CWD_API channel_ct gtk;
|
|||||||
extern CWD_API channel_ct sdl;
|
extern CWD_API channel_ct sdl;
|
||||||
extern CWD_API channel_ct backtrace;
|
extern CWD_API channel_ct backtrace;
|
||||||
extern CWD_API channel_ct statemachine;
|
extern CWD_API channel_ct statemachine;
|
||||||
|
extern CWD_API channel_ct caps;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -47,18 +47,11 @@ target_link_libraries(linux-crash-logger
|
|||||||
${LLVFS_LIBRARIES}
|
${LLVFS_LIBRARIES}
|
||||||
${LLXML_LIBRARIES}
|
${LLXML_LIBRARIES}
|
||||||
${LLMESSAGE_LIBRARIES}
|
${LLMESSAGE_LIBRARIES}
|
||||||
${LLUI_LIBRARIES}
|
|
||||||
${LLVFS_LIBRARIES}
|
${LLVFS_LIBRARIES}
|
||||||
${LLMATH_LIBRARIES}
|
${LLMATH_LIBRARIES}
|
||||||
${LLCOMMON_LIBRARIES}
|
${LLCOMMON_LIBRARIES}
|
||||||
${UI_LIBRARIES}
|
${UI_LIBRARIES}
|
||||||
${DB_LIBRARIES}
|
${DB_LIBRARIES}
|
||||||
${XMLRPCEPI_LIBRARIES}
|
|
||||||
${CURL_LIBRARIES}
|
|
||||||
${APR_LIBRARIES}
|
|
||||||
${APRUTIL_LIBRARIES}
|
|
||||||
${CRYPTO_LIBRARIES}
|
|
||||||
rt
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|||||||
@@ -239,12 +239,15 @@ protected:
|
|||||||
// A list of all audio sources that are known to the viewer at this time.
|
// A list of all audio sources that are known to the viewer at this time.
|
||||||
// This is most likely a superset of the ones that we actually have audio
|
// This is most likely a superset of the ones that we actually have audio
|
||||||
// data for, or are playing back.
|
// data for, or are playing back.
|
||||||
|
public://Jay: IDGAF
|
||||||
typedef std::map<LLUUID, LLAudioSource *> source_map;
|
typedef std::map<LLUUID, LLAudioSource *> source_map;
|
||||||
|
protected:
|
||||||
typedef std::map<LLUUID, LLAudioData *> data_map;
|
typedef std::map<LLUUID, LLAudioData *> data_map;
|
||||||
|
|
||||||
|
public://Jay: IDGAF
|
||||||
source_map mAllSources;
|
source_map mAllSources;
|
||||||
|
protected:
|
||||||
data_map mAllData;
|
data_map mAllData;
|
||||||
|
|
||||||
LLAudioChannel *mChannels[MAX_CHANNELS];
|
LLAudioChannel *mChannels[MAX_CHANNELS];
|
||||||
|
|
||||||
// Buffers needs to change into a different data structure, as the number of buffers
|
// Buffers needs to change into a different data structure, as the number of buffers
|
||||||
@@ -351,6 +354,9 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
|
LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
|
||||||
LLUUID mOwnerID; // owner of the object playing the sound
|
LLUUID mOwnerID; // owner of the object playing the sound
|
||||||
|
public:
|
||||||
|
const LLUUID &getOwnerID() { return mOwnerID; }
|
||||||
|
protected:
|
||||||
F32 mPriority;
|
F32 mPriority;
|
||||||
F32 mGain;
|
F32 mGain;
|
||||||
bool mSourceMuted;
|
bool mSourceMuted;
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
|
|||||||
{
|
{
|
||||||
// Number of milliseconds of audio to buffer for the audio card.
|
// Number of milliseconds of audio to buffer for the audio card.
|
||||||
// Must be larger than the usual Second Life frame stutter time.
|
// Must be larger than the usual Second Life frame stutter time.
|
||||||
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
|
// Here's where we set the size of the network buffer and some buffering
|
||||||
// parameters. In this case we want a network buffer of 16k, we want it
|
// parameters. In this case we want a network buffer of 16k, we want it
|
||||||
@@ -372,7 +374,7 @@ LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, con
|
|||||||
exinfo.cbsize = sizeof(exinfo);
|
exinfo.cbsize = sizeof(exinfo);
|
||||||
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG; //Hint to speed up loading.
|
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG; //Hint to speed up loading.
|
||||||
|
|
||||||
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING, &exinfo, &mInternetStream);
|
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, &exinfo, &mInternetStream);
|
||||||
|
|
||||||
if (result!= FMOD_OK)
|
if (result!= FMOD_OK)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ include_directories(
|
|||||||
set(llcommon_SOURCE_FILES
|
set(llcommon_SOURCE_FILES
|
||||||
aiframetimer.cpp
|
aiframetimer.cpp
|
||||||
imageids.cpp
|
imageids.cpp
|
||||||
indra_constants.cpp
|
indra_constants.cpp
|
||||||
|
llallocator.cpp
|
||||||
|
llallocator_heap_profile.cpp
|
||||||
llapp.cpp
|
llapp.cpp
|
||||||
llapr.cpp
|
llapr.cpp
|
||||||
llaprpool.cpp
|
llaprpool.cpp
|
||||||
@@ -27,14 +29,21 @@ set(llcommon_SOURCE_FILES
|
|||||||
llbase64.cpp
|
llbase64.cpp
|
||||||
llcommon.cpp
|
llcommon.cpp
|
||||||
llcommonutils.cpp
|
llcommonutils.cpp
|
||||||
|
llcoros.cpp
|
||||||
llcrc.cpp
|
llcrc.cpp
|
||||||
llcriticaldamp.cpp
|
llcriticaldamp.cpp
|
||||||
llcursortypes.cpp
|
llcursortypes.cpp
|
||||||
lldate.cpp
|
lldate.cpp
|
||||||
|
lldependencies.cpp
|
||||||
lldictionary.cpp
|
lldictionary.cpp
|
||||||
llerror.cpp
|
llerror.cpp
|
||||||
llerrorthread.cpp
|
llerrorthread.cpp
|
||||||
llevent.cpp
|
llevent.cpp
|
||||||
|
lleventapi.cpp
|
||||||
|
lleventcoro.cpp
|
||||||
|
lleventdispatcher.cpp
|
||||||
|
lleventfilter.cpp
|
||||||
|
llevents.cpp
|
||||||
lleventtimer.cpp
|
lleventtimer.cpp
|
||||||
llfasttimer_class.cpp
|
llfasttimer_class.cpp
|
||||||
llfile.cpp
|
llfile.cpp
|
||||||
@@ -52,6 +61,7 @@ set(llcommon_SOURCE_FILES
|
|||||||
llmd5.cpp
|
llmd5.cpp
|
||||||
llmemory.cpp
|
llmemory.cpp
|
||||||
llmemorystream.cpp
|
llmemorystream.cpp
|
||||||
|
llmemtype.cpp
|
||||||
llmetrics.cpp
|
llmetrics.cpp
|
||||||
llmortician.cpp
|
llmortician.cpp
|
||||||
lloptioninterface.cpp
|
lloptioninterface.cpp
|
||||||
@@ -81,6 +91,7 @@ set(llcommon_SOURCE_FILES
|
|||||||
lluri.cpp
|
lluri.cpp
|
||||||
lluuid.cpp
|
lluuid.cpp
|
||||||
llworkerthread.cpp
|
llworkerthread.cpp
|
||||||
|
ll_template_cast.h
|
||||||
metaclass.cpp
|
metaclass.cpp
|
||||||
metaproperty.cpp
|
metaproperty.cpp
|
||||||
reflective.cpp
|
reflective.cpp
|
||||||
@@ -101,6 +112,8 @@ set(llcommon_HEADER_FILES
|
|||||||
linden_common.h
|
linden_common.h
|
||||||
linked_lists.h
|
linked_lists.h
|
||||||
llaccountingcost.h
|
llaccountingcost.h
|
||||||
|
llallocator.h
|
||||||
|
llallocator_heap_profile.h
|
||||||
llagentconstants.h
|
llagentconstants.h
|
||||||
llavatarname.h
|
llavatarname.h
|
||||||
llapp.h
|
llapp.h
|
||||||
@@ -116,6 +129,7 @@ set(llcommon_HEADER_FILES
|
|||||||
llclickaction.h
|
llclickaction.h
|
||||||
llcommon.h
|
llcommon.h
|
||||||
llcommonutils.h
|
llcommonutils.h
|
||||||
|
llcoros.h
|
||||||
llcrc.h
|
llcrc.h
|
||||||
llcriticaldamp.h
|
llcriticaldamp.h
|
||||||
llcursortypes.h
|
llcursortypes.h
|
||||||
@@ -123,6 +137,7 @@ set(llcommon_HEADER_FILES
|
|||||||
lldarrayptr.h
|
lldarrayptr.h
|
||||||
lldate.h
|
lldate.h
|
||||||
lldefs.h
|
lldefs.h
|
||||||
|
lldependencies.h
|
||||||
lldeleteutils.h
|
lldeleteutils.h
|
||||||
lldepthstack.h
|
lldepthstack.h
|
||||||
lldictionary.h
|
lldictionary.h
|
||||||
@@ -135,6 +150,11 @@ set(llcommon_HEADER_FILES
|
|||||||
llerrorlegacy.h
|
llerrorlegacy.h
|
||||||
llerrorthread.h
|
llerrorthread.h
|
||||||
llevent.h
|
llevent.h
|
||||||
|
lleventapi.h
|
||||||
|
lleventcoro.h
|
||||||
|
lleventdispatcher.h
|
||||||
|
lleventfilter.h
|
||||||
|
llevents.h
|
||||||
lleventemitter.h
|
lleventemitter.h
|
||||||
llextendedstatus.h
|
llextendedstatus.h
|
||||||
lleventtimer.h
|
lleventtimer.h
|
||||||
@@ -208,8 +228,7 @@ set(llcommon_HEADER_FILES
|
|||||||
lluri.h
|
lluri.h
|
||||||
lluuid.h
|
lluuid.h
|
||||||
lluuidhashmap.h
|
lluuidhashmap.h
|
||||||
llversionserver.h
|
llversionviewer.h.in
|
||||||
llversionviewer.h
|
|
||||||
llworkerthread.h
|
llworkerthread.h
|
||||||
metaclass.h
|
metaclass.h
|
||||||
metaclasst.h
|
metaclasst.h
|
||||||
|
|||||||
@@ -27,8 +27,38 @@
|
|||||||
* - Initial version, written by Aleric Inglewood @ SL
|
* - 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"
|
#include "aiframetimer.h"
|
||||||
|
|
||||||
static F64 const NEVER = 1e16; // 317 million years.
|
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.
|
// function here because the trylock fails.
|
||||||
//
|
//
|
||||||
// Assuming the main thread arrived here, there are two possible states
|
// 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:
|
// right after calling the cancel() function too:
|
||||||
//
|
//
|
||||||
// 1. It hasn't obtained the first lock yet, we obtain the handle.mMutex
|
// 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.
|
// See aiframetimer.cpp for more notes.
|
||||||
class AIRunningFrameTimer {
|
class AIRunningFrameTimer {
|
||||||
private:
|
private:
|
||||||
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
|
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
|
||||||
Signal* mCallback;
|
AIFrameTimer* mTimer; // The actual timer.
|
||||||
AIFrameTimer* mTimer;
|
// 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:
|
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; }
|
~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; }
|
friend bool operator<(AIRunningFrameTimer const& ft1, AIRunningFrameTimer const& ft2) { return ft1.mExpire < ft2.mExpire; }
|
||||||
|
|
||||||
void do_callback(void) const { mCallback->mSignal(); }
|
void do_callback(void) const { mCallback->mSignal(); }
|
||||||
F64 expiration(void) const { return mExpire; }
|
F64 expiration(void) const { return mExpire; }
|
||||||
AIFrameTimer* getTimer(void) const { return mTimer; }
|
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;
|
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
|
||||||
@@ -98,12 +115,12 @@ class LL_COMMON_API AIFrameTimer
|
|||||||
|
|
||||||
// Actual initialization used by AIFrameTimer::create.
|
// Actual initialization used by AIFrameTimer::create.
|
||||||
void init(timer_list_type::iterator const& running_timer, signal_type::slot_type const& slot)
|
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
|
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
|
||||||
// the object and no other thread knows of mRunningTimer at this point.
|
// the object and no other thread knows of mRunningTimer at this point.
|
||||||
mRunningTimer = running_timer;
|
mRunningTimer = running_timer;
|
||||||
mRunningTimer->init(slot);
|
mRunningTimer->init(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// LLMutex has no assignment operator.
|
// 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 create(F64 expiration, signal_type::slot_type const& slot);
|
||||||
void cancel(void);
|
void cancel(void);
|
||||||
|
|
||||||
|
bool isRunning(void) const { bool running; sMutex.lock(); running = mHandle.mRunningTimer != sTimerList.end(); sMutex.unlock(); return running; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void handleExpiration(F64 current_frame_time);
|
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();
|
return isQuitting() || isError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLApp::disableCrashlogger()
|
||||||
|
{
|
||||||
|
// Disable Breakpad exception handler.
|
||||||
|
sDisableCrashlogger = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool LLApp::isCrashloggerDisabled()
|
||||||
|
{
|
||||||
|
return (sDisableCrashlogger == TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
// static
|
// static
|
||||||
U32 LLApp::getSigChildCount()
|
U32 LLApp::getSigChildCount()
|
||||||
@@ -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;
|
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();
|
clear_signals();
|
||||||
llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl;
|
llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl;
|
||||||
|
|||||||
@@ -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.
|
virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
|
||||||
|
|
||||||
|
//
|
||||||
|
// Crash logging
|
||||||
|
//
|
||||||
|
void disableCrashlogger(); // Let the OS handle the crashes
|
||||||
|
static bool isCrashloggerDisabled(); // Get the here above set value
|
||||||
|
|
||||||
//
|
//
|
||||||
// Application status
|
// Application status
|
||||||
@@ -214,9 +219,6 @@ public:
|
|||||||
//
|
//
|
||||||
void setErrorHandler(LLAppErrorHandler handler);
|
void setErrorHandler(LLAppErrorHandler handler);
|
||||||
void setSyncErrorHandler(LLAppErrorHandler handler);
|
void setSyncErrorHandler(LLAppErrorHandler handler);
|
||||||
|
|
||||||
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
|
|
||||||
|
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
//
|
//
|
||||||
// Child process handling (Unix only for now)
|
// Child process handling (Unix only for now)
|
||||||
@@ -245,6 +247,7 @@ protected:
|
|||||||
static void setStatus(EAppStatus status); // Use this to change the application status.
|
static void setStatus(EAppStatus status); // Use this to change the application status.
|
||||||
static EAppStatus sStatus; // Reflects current application status
|
static EAppStatus sStatus; // Reflects current application status
|
||||||
static BOOL sErrorThreadRunning; // Set while the error thread is running
|
static BOOL sErrorThreadRunning; // Set while the error thread is running
|
||||||
|
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
|
||||||
|
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
|
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
|
||||||
|
|||||||
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"
|
#include "llevent.h"
|
||||||
|
|
||||||
|
using namespace LLOldEvents;
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
Events
|
Events
|
||||||
************************************************/
|
************************************************/
|
||||||
|
|||||||
@@ -35,9 +35,12 @@
|
|||||||
#define LL_EVENT_H
|
#define LL_EVENT_H
|
||||||
|
|
||||||
#include "llsd.h"
|
#include "llsd.h"
|
||||||
#include "llmemory.h"
|
#include "llpointer.h"
|
||||||
#include "llthread.h"
|
#include "llthread.h"
|
||||||
|
|
||||||
|
namespace LLOldEvents
|
||||||
|
{
|
||||||
|
|
||||||
class LLEventListener;
|
class LLEventListener;
|
||||||
class LLEvent;
|
class LLEvent;
|
||||||
class LLEventDispatcher;
|
class LLEventDispatcher;
|
||||||
@@ -194,4 +197,6 @@ public:
|
|||||||
LLSD mValue;
|
LLSD mValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // LLOldEvents
|
||||||
|
|
||||||
#endif // LL_EVENT_H
|
#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
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/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 "linden_common.h"
|
||||||
|
|
||||||
#include "llfasttimer.h"
|
#include "llfasttimer.h"
|
||||||
@@ -64,14 +183,6 @@ BOOL LLFastTimer::sMetricLog = FALSE;
|
|||||||
LLMutex* LLFastTimer::sLogLock = NULL;
|
LLMutex* LLFastTimer::sLogLock = NULL;
|
||||||
std::queue<LLSD> LLFastTimer::sLogQueue;
|
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;
|
std::vector<LLFastTimer::FrameState>* LLFastTimer::sTimerInfos = NULL;
|
||||||
U64 LLFastTimer::sTimerCycles = 0;
|
U64 LLFastTimer::sTimerCycles = 0;
|
||||||
U32 LLFastTimer::sTimerCalls = 0;
|
U32 LLFastTimer::sTimerCalls = 0;
|
||||||
@@ -130,8 +241,17 @@ public:
|
|||||||
mActiveTimerRoot->setCollapsed(false);
|
mActiveTimerRoot->setCollapsed(false);
|
||||||
|
|
||||||
mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
|
mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
|
||||||
mRootFrameState->mParent = &mTimerRoot->getFrameState();
|
// getFrameState and setParent recursively call this function,
|
||||||
mActiveTimerRoot->setParent(mTimerRoot);
|
// 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);
|
mAppTimer = new LLFastTimer(mRootFrameState);
|
||||||
}
|
}
|
||||||
@@ -187,7 +307,7 @@ private:
|
|||||||
LLFastTimer::NamedTimer* mActiveTimerRoot;
|
LLFastTimer::NamedTimer* mActiveTimerRoot;
|
||||||
LLFastTimer::NamedTimer* mTimerRoot;
|
LLFastTimer::NamedTimer* mTimerRoot;
|
||||||
LLFastTimer* mAppTimer;
|
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()
|
void update_cached_pointers_if_changed()
|
||||||
@@ -196,9 +316,9 @@ void update_cached_pointers_if_changed()
|
|||||||
static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
|
static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
|
||||||
if (&*(LLFastTimer::getFrameStateList().begin()) != sFirstTimerAddress)
|
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 )
|
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
|
||||||
@@ -217,53 +337,69 @@ LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void LLFastTimer::DeclareTimer::updateCachedPointers()
|
void LLFastTimer::updateCachedPointers()
|
||||||
{
|
{
|
||||||
// propagate frame state pointers to timer declarations
|
// Update DeclareTimer::mFrameState pointers.
|
||||||
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
for (DeclareTimer::instance_iter it = DeclareTimer::beginInstances(); it != DeclareTimer::endInstances(); ++it)
|
||||||
{
|
{
|
||||||
// update cached pointer
|
// update cached pointer
|
||||||
it->mFrameState = &it->mTimer.getFrameState();
|
it->mFrameState = &it->mTimer.getFrameState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// also update frame states of timers on stack
|
// Update CurTimerData::mFrameState and LLFastTimer::mFrameState of timers on the stack.
|
||||||
LLFastTimer* cur_timerp = LLFastTimer::sCurTimerData.mCurTimer;
|
FrameState& root_frame_state(NamedTimerFactory::instance().getRootFrameState()); // This one is not invalidated.
|
||||||
while(cur_timerp->mLastTimerData.mCurTimer != cur_timerp)
|
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_timer_data->mFrameState = cur_timer_data->mCurTimer->mFrameState = &cur_timer_data->mNamedTimer->getFrameState();
|
||||||
cur_timerp = cur_timerp->mLastTimerData.mCurTimer;
|
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
|
// See lltimer.cpp.
|
||||||
#if (LL_DARWIN || LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
|
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
|
||||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
std::string LLFastTimer::sClockType = "gettimeofday";
|
||||||
{
|
#elif LL_WINDOWS
|
||||||
return sClockResolution >> 8;
|
std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
|
||||||
}
|
|
||||||
#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
|
|
||||||
#else
|
#else
|
||||||
// If we're not using RDTSC, each fasttimer tick is just a performance counter tick.
|
#error "Platform not supported"
|
||||||
// Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency())
|
#endif
|
||||||
// since that would change displayed MHz stats for CPUs
|
|
||||||
|
//static
|
||||||
|
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||||
|
{
|
||||||
static bool firstcall = true;
|
static bool firstcall = true;
|
||||||
static U64 sCPUClockFrequency;
|
static U64 sCPUClockFrequency;
|
||||||
if (firstcall)
|
if (firstcall)
|
||||||
{
|
{
|
||||||
QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency);
|
sCPUClockFrequency = calc_clock_frequency();
|
||||||
firstcall = false;
|
firstcall = false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return sCPUClockFrequency >> 8;
|
return sCPUClockFrequency >> 8;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
|
LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
|
||||||
: mActiveCount(0),
|
: mActiveCount(0),
|
||||||
@@ -401,11 +537,12 @@ void LLFastTimer::NamedTimer::buildHierarchy()
|
|||||||
|
|
||||||
// bootstrap tree construction by attaching to last timer to be on stack
|
// bootstrap tree construction by attaching to last timer to be on stack
|
||||||
// when this timer was called
|
// 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
|
// 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;
|
timerp->mFrameStateIndex = index;
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
llassert_always(timerp->mFrameStateIndex < (S32)getFrameStateList().size());
|
|
||||||
}
|
}
|
||||||
|
llassert(index == (S32)getFrameStateList().size());
|
||||||
|
|
||||||
// sort timers by DFS traversal order to improve cache coherency
|
// sort timers by DFS traversal order to improve cache coherency
|
||||||
std::sort(getFrameStateList().begin(), getFrameStateList().end(), SortTimersDFS());
|
std::sort(getFrameStateList().begin(), getFrameStateList().end(), SortTimersDFS());
|
||||||
|
|
||||||
// update pointers into framestatelist now that we've sorted it
|
// update pointers into framestatelist now that we've sorted it
|
||||||
DeclareTimer::updateCachedPointers();
|
updateCachedPointers();
|
||||||
|
|
||||||
// reset for next frame
|
// reset for next frame
|
||||||
{
|
{
|
||||||
@@ -644,7 +780,11 @@ LLFastTimer::info_list_t& LLFastTimer::getFrameStateList()
|
|||||||
{
|
{
|
||||||
if (!sTimerInfos)
|
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;
|
return *sTimerInfos;
|
||||||
}
|
}
|
||||||
@@ -776,22 +916,27 @@ const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& na
|
|||||||
LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
|
LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
|
||||||
: mFrameState(state)
|
: mFrameState(state)
|
||||||
{
|
{
|
||||||
|
// Only called for mAppTimer with mRootFrameState, which never invalidates.
|
||||||
|
llassert(state == &NamedTimerFactory::instance().getRootFrameState());
|
||||||
|
|
||||||
U32 start_time = getCPUClockCount32();
|
U32 start_time = getCPUClockCount32();
|
||||||
mStartTime = start_time;
|
mStartTime = start_time;
|
||||||
mFrameState->mActiveCount++;
|
mFrameState->mActiveCount++;
|
||||||
LLFastTimer::sCurTimerData.mCurTimer = this;
|
LLFastTimer::sCurTimerData.mCurTimer = this;
|
||||||
|
LLFastTimer::sCurTimerData.mNamedTimer = mFrameState->mTimer;
|
||||||
LLFastTimer::sCurTimerData.mFrameState = mFrameState;
|
LLFastTimer::sCurTimerData.mFrameState = mFrameState;
|
||||||
LLFastTimer::sCurTimerData.mChildTime = 0;
|
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;
|
mLastTimerData = LLFastTimer::sCurTimerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Important note: These implementations must be FAST!
|
// Important note: These implementations must be FAST!
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
|
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
|
||||||
// These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
|
// These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
|
||||||
U32 LLFastTimer::getCPUClockCount32()
|
U32 LLFastTimer::getCPUClockCount32()
|
||||||
@@ -804,9 +949,3 @@ U64 LLFastTimer::getCPUClockCount64()
|
|||||||
return get_clock_count();
|
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 mSelfTimeCounter;
|
||||||
U32 mCalls;
|
U32 mCalls;
|
||||||
FrameState* mParent; // info for caller timer
|
FrameState* mParent; // info for caller timer
|
||||||
FrameState* mLastCaller; // used to bootstrap tree construction
|
NamedTimer* mLastCaller; // used to bootstrap tree construction
|
||||||
NamedTimer* mTimer;
|
NamedTimer* mTimer;
|
||||||
U16 mActiveCount; // number of timers with this ID active on stack
|
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
|
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, bool open);
|
||||||
DeclareTimer(const std::string& name);
|
DeclareTimer(const std::string& name);
|
||||||
|
|
||||||
static void updateCachedPointers();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NamedTimer& mTimer;
|
NamedTimer& mTimer;
|
||||||
FrameState* mFrameState;
|
FrameState* mFrameState;
|
||||||
@@ -172,6 +170,7 @@ public:
|
|||||||
LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
|
LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
|
||||||
mLastTimerData = *cur_timer_data;
|
mLastTimerData = *cur_timer_data;
|
||||||
cur_timer_data->mCurTimer = this;
|
cur_timer_data->mCurTimer = this;
|
||||||
|
cur_timer_data->mNamedTimer = &timer.mTimer;
|
||||||
cur_timer_data->mFrameState = frame_state;
|
cur_timer_data->mFrameState = frame_state;
|
||||||
cur_timer_data->mChildTime = 0;
|
cur_timer_data->mChildTime = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -200,7 +199,7 @@ public:
|
|||||||
|
|
||||||
// store last caller to bootstrap tree creation
|
// store last caller to bootstrap tree creation
|
||||||
// do this in the destructor in case of recursion to get topmost caller
|
// 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
|
// we are only tracking self time, so subtract our total time delta from parents
|
||||||
mLastTimerData.mChildTime += total_time;
|
mLastTimerData.mChildTime += total_time;
|
||||||
@@ -239,6 +238,9 @@ public:
|
|||||||
// call this to reset timer hierarchy, averages, etc.
|
// call this to reset timer hierarchy, averages, etc.
|
||||||
static void reset();
|
static void reset();
|
||||||
|
|
||||||
|
// called to update all FrameState pointers.
|
||||||
|
static void updateCachedPointers();
|
||||||
|
|
||||||
static U64 countsPerSecond();
|
static U64 countsPerSecond();
|
||||||
static S32 getLastFrameIndex() { return sLastFrameIndex; }
|
static S32 getLastFrameIndex() { return sLastFrameIndex; }
|
||||||
static S32 getCurFrameIndex() { return sCurFrameIndex; }
|
static S32 getCurFrameIndex() { return sCurFrameIndex; }
|
||||||
@@ -249,6 +251,7 @@ public:
|
|||||||
struct CurTimerData
|
struct CurTimerData
|
||||||
{
|
{
|
||||||
LLFastTimer* mCurTimer;
|
LLFastTimer* mCurTimer;
|
||||||
|
NamedTimer* mNamedTimer;
|
||||||
FrameState* mFrameState;
|
FrameState* mFrameState;
|
||||||
U32 mChildTime;
|
U32 mChildTime;
|
||||||
};
|
};
|
||||||
@@ -258,7 +261,6 @@ public:
|
|||||||
public:
|
public:
|
||||||
static U32 getCPUClockCount32();
|
static U32 getCPUClockCount32();
|
||||||
static U64 getCPUClockCount64();
|
static U64 getCPUClockCount64();
|
||||||
static U64 sClockResolution;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static S32 sCurFrameIndex;
|
static S32 sCurFrameIndex;
|
||||||
@@ -272,6 +274,4 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef class LLFastTimer LLFastTimer;
|
|
||||||
|
|
||||||
#endif // LL_LLFASTTIMER_H
|
#endif // LL_LLFASTTIMER_H
|
||||||
|
|||||||
@@ -32,9 +32,13 @@
|
|||||||
|
|
||||||
#include "linden_common.h"
|
#include "linden_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
//#if MEM_TRACK_MEM
|
||||||
|
#include "llthread.h"
|
||||||
|
//#endif
|
||||||
|
|
||||||
#if defined(LL_WINDOWS)
|
#if defined(LL_WINDOWS)
|
||||||
#define _WINSOCKAPI_
|
//# include <windows.h>
|
||||||
# include <windows.h>
|
|
||||||
# include <psapi.h>
|
# include <psapi.h>
|
||||||
#elif defined(LL_DARWIN)
|
#elif defined(LL_DARWIN)
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
@@ -45,10 +49,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "llmemory.h"
|
#include "llmemory.h"
|
||||||
#include "llmemtype.h"
|
|
||||||
#include "llsys.h"
|
|
||||||
#include "llthread.h"
|
|
||||||
|
|
||||||
|
#include "llsys.h"
|
||||||
|
#include "llframetimer.h"
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
//static
|
//static
|
||||||
@@ -162,6 +165,7 @@ void LLMemory::logMemoryInfo(BOOL update)
|
|||||||
if(update)
|
if(update)
|
||||||
{
|
{
|
||||||
updateMemoryInfo() ;
|
updateMemoryInfo() ;
|
||||||
|
LLPrivateMemoryPoolManager::getInstance()->updateStatistics() ;
|
||||||
}
|
}
|
||||||
|
|
||||||
llinfos << "Current allocated physical memory(KB): " << sAllocatedMemInKB << llendl ;
|
llinfos << "Current allocated physical memory(KB): " << sAllocatedMemInKB << llendl ;
|
||||||
@@ -242,161 +246,6 @@ U32 LLMemory::getAllocatedMemKB()
|
|||||||
return sAllocatedMemInKB ;
|
return sAllocatedMemInKB ;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
//static
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
S32 LLMemType::sCurDepth = 0;
|
|
||||||
S32 LLMemType::sCurType = LLMemType::MTYPE_INIT;
|
|
||||||
S32 LLMemType::sType[LLMemType::MTYPE_MAX_DEPTH];
|
|
||||||
S32 LLMemType::sMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
|
|
||||||
S32 LLMemType::sMaxMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
|
|
||||||
S32 LLMemType::sNewCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
|
|
||||||
S32 LLMemType::sOverheadMem = 0;
|
|
||||||
|
|
||||||
const char* LLMemType::sTypeDesc[LLMemType::MTYPE_NUM_TYPES] =
|
|
||||||
{
|
|
||||||
"INIT",
|
|
||||||
"STARTUP",
|
|
||||||
"MAIN",
|
|
||||||
|
|
||||||
"IMAGEBASE",
|
|
||||||
"IMAGERAW",
|
|
||||||
"IMAGEFORMATTED",
|
|
||||||
|
|
||||||
"APPFMTIMAGE",
|
|
||||||
"APPRAWIMAGE",
|
|
||||||
"APPAUXRAWIMAGE",
|
|
||||||
|
|
||||||
"DRAWABLE",
|
|
||||||
"OBJECT",
|
|
||||||
"PIPELINE",
|
|
||||||
"AVATAR",
|
|
||||||
"PARTICLES",
|
|
||||||
"REGIONS",
|
|
||||||
"INVENTORY",
|
|
||||||
"ANIMATION",
|
|
||||||
"NETWORK",
|
|
||||||
"PHYSICS",
|
|
||||||
"INTERESTLIST",
|
|
||||||
|
|
||||||
"SCRIPT",
|
|
||||||
"SCRIPT_RUN",
|
|
||||||
"SCRIPT_BYTECODE",
|
|
||||||
|
|
||||||
"IO_PUMP",
|
|
||||||
"IO_TCP",
|
|
||||||
"IO_BUFFER",
|
|
||||||
"IO_HTTP_SERVER"
|
|
||||||
"IO_SD_SERVER",
|
|
||||||
"IO_SD_CLIENT",
|
|
||||||
"IO_URL_REQUEST",
|
|
||||||
|
|
||||||
"TEMP1",
|
|
||||||
"TEMP2",
|
|
||||||
"TEMP3",
|
|
||||||
"TEMP4",
|
|
||||||
"TEMP5",
|
|
||||||
"TEMP6",
|
|
||||||
"TEMP7",
|
|
||||||
"TEMP8",
|
|
||||||
"TEMP9"
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
S32 LLMemType::sTotalMem = 0;
|
|
||||||
S32 LLMemType::sMaxTotalMem = 0;
|
|
||||||
|
|
||||||
//static
|
|
||||||
void LLMemType::printMem()
|
|
||||||
{
|
|
||||||
S32 misc_mem = sTotalMem;
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
for (S32 i=0; i<MTYPE_NUM_TYPES; i++)
|
|
||||||
{
|
|
||||||
if (sMemCount[i])
|
|
||||||
{
|
|
||||||
llinfos << llformat("MEM: % 20s %03d MB (%03d MB) in %06d News",sTypeDesc[i],sMemCount[i]>>20,sMaxMemCount[i]>>20, sNewCount[i]) << llendl;
|
|
||||||
}
|
|
||||||
misc_mem -= sMemCount[i];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
llinfos << llformat("MEM: % 20s %03d MB","MISC",misc_mem>>20) << llendl;
|
|
||||||
llinfos << llformat("MEM: % 20s %03d MB (Max=%d MB)","TOTAL",sTotalMem>>20,sMaxTotalMem>>20) << llendl;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MEM_TRACK_MEM
|
|
||||||
|
|
||||||
void* ll_allocate (size_t size)
|
|
||||||
{
|
|
||||||
if (size == 0)
|
|
||||||
{
|
|
||||||
llwarns << "Null allocation" << llendl;
|
|
||||||
}
|
|
||||||
|
|
||||||
size = (size+3)&~3;
|
|
||||||
S32 alloc_size = size + 4;
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
alloc_size += 4;
|
|
||||||
#endif
|
|
||||||
char* p = (char*)malloc(alloc_size);
|
|
||||||
if (p == NULL)
|
|
||||||
{
|
|
||||||
LLMemory::freeReserve();
|
|
||||||
llerrs << "Out of memory Error" << llendl;
|
|
||||||
}
|
|
||||||
LLMemType::sTotalMem += size;
|
|
||||||
LLMemType::sMaxTotalMem = llmax(LLMemType::sTotalMem, LLMemType::sMaxTotalMem);
|
|
||||||
LLMemType::sOverheadMem += 4;
|
|
||||||
*(size_t*)p = size;
|
|
||||||
p += 4;
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
if (LLMemType::sCurType < 0 || LLMemType::sCurType >= LLMemType::MTYPE_NUM_TYPES)
|
|
||||||
{
|
|
||||||
llerrs << "Memory Type Error: new" << llendl;
|
|
||||||
}
|
|
||||||
LLMemType::sOverheadMem += 4;
|
|
||||||
*(S32*)p = LLMemType::sCurType;
|
|
||||||
p += 4;
|
|
||||||
LLMemType::sMemCount[LLMemType::sCurType] += size;
|
|
||||||
if (LLMemType::sMemCount[LLMemType::sCurType] > LLMemType::sMaxMemCount[LLMemType::sCurType])
|
|
||||||
{
|
|
||||||
LLMemType::sMaxMemCount[LLMemType::sCurType] = LLMemType::sMemCount[LLMemType::sCurType];
|
|
||||||
}
|
|
||||||
LLMemType::sNewCount[LLMemType::sCurType]++;
|
|
||||||
#endif
|
|
||||||
return (void*)p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ll_release (void *pin)
|
|
||||||
{
|
|
||||||
if (!pin)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
char* p = (char*)pin;
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
p -= 4;
|
|
||||||
S32 type = *(S32*)p;
|
|
||||||
if (type < 0 || type >= LLMemType::MTYPE_NUM_TYPES)
|
|
||||||
{
|
|
||||||
llerrs << "Memory Type Error: delete" << llendl;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
p -= 4;
|
|
||||||
S32 size = *(size_t*)p;
|
|
||||||
LLMemType::sOverheadMem -= 4;
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
LLMemType::sMemCount[type] -= size;
|
|
||||||
LLMemType::sOverheadMem -= 4;
|
|
||||||
LLMemType::sNewCount[type]--;
|
|
||||||
#endif
|
|
||||||
LLMemType::sTotalMem -= size;
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
void* ll_allocate (size_t size)
|
void* ll_allocate (size_t size)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
@@ -412,38 +261,6 @@ void* ll_allocate (size_t size)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ll_release (void *p)
|
|
||||||
{
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MEM_TRACK_MEM
|
|
||||||
|
|
||||||
void* operator new (size_t size)
|
|
||||||
{
|
|
||||||
return ll_allocate(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* operator new[] (size_t size)
|
|
||||||
{
|
|
||||||
return ll_allocate(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator delete (void *p)
|
|
||||||
{
|
|
||||||
ll_release(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator delete[] (void *p)
|
|
||||||
{
|
|
||||||
ll_release(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
#if defined(LL_WINDOWS)
|
#if defined(LL_WINDOWS)
|
||||||
@@ -613,6 +430,141 @@ U32 LLMemory::getWorkingSetSize()
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
#if MEM_TRACK_MEM
|
||||||
|
#include "llframetimer.h"
|
||||||
|
|
||||||
|
//static
|
||||||
|
LLMemTracker* LLMemTracker::sInstance = NULL ;
|
||||||
|
|
||||||
|
LLMemTracker::LLMemTracker()
|
||||||
|
{
|
||||||
|
mLastAllocatedMem = LLMemory::getWorkingSetSize() ;
|
||||||
|
mCapacity = 128 ;
|
||||||
|
mCurIndex = 0 ;
|
||||||
|
mCounter = 0 ;
|
||||||
|
mDrawnIndex = 0 ;
|
||||||
|
mPaused = FALSE ;
|
||||||
|
|
||||||
|
mMutexp = new LLMutex() ;
|
||||||
|
mStringBuffer = new char*[128] ;
|
||||||
|
mStringBuffer[0] = new char[mCapacity * 128] ;
|
||||||
|
for(S32 i = 1 ; i < mCapacity ; i++)
|
||||||
|
{
|
||||||
|
mStringBuffer[i] = mStringBuffer[i-1] + 128 ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLMemTracker::~LLMemTracker()
|
||||||
|
{
|
||||||
|
delete[] mStringBuffer[0] ;
|
||||||
|
delete[] mStringBuffer;
|
||||||
|
delete mMutexp ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
LLMemTracker* LLMemTracker::getInstance()
|
||||||
|
{
|
||||||
|
if(!sInstance)
|
||||||
|
{
|
||||||
|
sInstance = new LLMemTracker() ;
|
||||||
|
}
|
||||||
|
return sInstance ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLMemTracker::release()
|
||||||
|
{
|
||||||
|
if(sInstance)
|
||||||
|
{
|
||||||
|
delete sInstance ;
|
||||||
|
sInstance = NULL ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLMemTracker::track(const char* function, const int line)
|
||||||
|
{
|
||||||
|
static const S32 MIN_ALLOCATION = 0 ; //1KB
|
||||||
|
|
||||||
|
if(mPaused)
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 allocated_mem = LLMemory::getWorkingSetSize() ;
|
||||||
|
|
||||||
|
LLMutexLock lock(mMutexp) ;
|
||||||
|
|
||||||
|
S32 delta_mem = allocated_mem - mLastAllocatedMem ;
|
||||||
|
mLastAllocatedMem = allocated_mem ;
|
||||||
|
|
||||||
|
if(delta_mem <= 0)
|
||||||
|
{
|
||||||
|
return ; //occupied memory does not grow
|
||||||
|
}
|
||||||
|
|
||||||
|
if(delta_mem < MIN_ALLOCATION)
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* buffer = mStringBuffer[mCurIndex++] ;
|
||||||
|
F32 time = (F32)LLFrameTimer::getElapsedSeconds() ;
|
||||||
|
S32 hours = (S32)(time / (60*60));
|
||||||
|
S32 mins = (S32)((time - hours*(60*60)) / 60);
|
||||||
|
S32 secs = (S32)((time - hours*(60*60) - mins*60));
|
||||||
|
strcpy(buffer, function) ;
|
||||||
|
sprintf(buffer + strlen(function), " line: %d DeltaMem: %d (bytes) Time: %d:%02d:%02d", line, delta_mem, hours,mins,secs) ;
|
||||||
|
|
||||||
|
if(mCounter < mCapacity)
|
||||||
|
{
|
||||||
|
mCounter++ ;
|
||||||
|
}
|
||||||
|
if(mCurIndex >= mCapacity)
|
||||||
|
{
|
||||||
|
mCurIndex = 0 ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLMemTracker::preDraw(BOOL pause)
|
||||||
|
{
|
||||||
|
mMutexp->lock() ;
|
||||||
|
|
||||||
|
mPaused = pause ;
|
||||||
|
mDrawnIndex = mCurIndex - 1;
|
||||||
|
mNumOfDrawn = 0 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLMemTracker::postDraw()
|
||||||
|
{
|
||||||
|
mMutexp->unlock() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
const char* LLMemTracker::getNextLine()
|
||||||
|
{
|
||||||
|
if(mNumOfDrawn >= mCounter)
|
||||||
|
{
|
||||||
|
return NULL ;
|
||||||
|
}
|
||||||
|
mNumOfDrawn++;
|
||||||
|
|
||||||
|
if(mDrawnIndex < 0)
|
||||||
|
{
|
||||||
|
mDrawnIndex = mCapacity - 1 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mStringBuffer[mDrawnIndex--] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MEM_TRACK_MEM
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------
|
||||||
//minimum slot size and minimal slot size interval
|
//minimum slot size and minimal slot size interval
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
|
#include "llmemtype.h"
|
||||||
#if LL_DEBUG
|
#if LL_DEBUG
|
||||||
inline void* ll_aligned_malloc( size_t size, int align )
|
inline void* ll_aligned_malloc( size_t size, int align )
|
||||||
{
|
{
|
||||||
|
|||||||
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
|
class LL_COMMON_API LLMemType
|
||||||
{
|
{
|
||||||
public:
|
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,
|
public:
|
||||||
MTYPE_STARTUP,
|
DeclareMemType(char const * st);
|
||||||
MTYPE_MAIN,
|
~DeclareMemType();
|
||||||
|
|
||||||
MTYPE_IMAGEBASE,
|
S32 mID;
|
||||||
MTYPE_IMAGERAW,
|
char const * mName;
|
||||||
MTYPE_IMAGEFORMATTED,
|
|
||||||
|
|
||||||
MTYPE_APPFMTIMAGE,
|
// array so we can map an index ID to Name
|
||||||
MTYPE_APPRAWIMAGE,
|
static std::vector<char const *> mNameList;
|
||||||
MTYPE_APPAUXRAWIMAGE,
|
|
||||||
|
|
||||||
MTYPE_DRAWABLE,
|
|
||||||
MTYPE_OBJECT,
|
|
||||||
MTYPE_VERTEX_DATA,
|
|
||||||
MTYPE_SPACE_PARTITION,
|
|
||||||
MTYPE_PIPELINE,
|
|
||||||
MTYPE_AVATAR,
|
|
||||||
MTYPE_AVATAR_MESH,
|
|
||||||
MTYPE_PARTICLES,
|
|
||||||
MTYPE_REGIONS,
|
|
||||||
MTYPE_INVENTORY,
|
|
||||||
MTYPE_ANIMATION,
|
|
||||||
MTYPE_VOLUME,
|
|
||||||
MTYPE_PRIMITIVE,
|
|
||||||
|
|
||||||
MTYPE_NETWORK,
|
|
||||||
MTYPE_PHYSICS,
|
|
||||||
MTYPE_INTERESTLIST,
|
|
||||||
|
|
||||||
MTYPE_SCRIPT,
|
|
||||||
MTYPE_SCRIPT_RUN,
|
|
||||||
MTYPE_SCRIPT_BYTECODE,
|
|
||||||
|
|
||||||
MTYPE_IO_PUMP,
|
|
||||||
MTYPE_IO_TCP,
|
|
||||||
MTYPE_IO_BUFFER,
|
|
||||||
MTYPE_IO_HTTP_SERVER,
|
|
||||||
MTYPE_IO_SD_SERVER,
|
|
||||||
MTYPE_IO_SD_CLIENT,
|
|
||||||
MTYPE_IO_URL_REQUEST,
|
|
||||||
|
|
||||||
MTYPE_TEMP1,
|
|
||||||
MTYPE_TEMP2,
|
|
||||||
MTYPE_TEMP3,
|
|
||||||
MTYPE_TEMP4,
|
|
||||||
MTYPE_TEMP5,
|
|
||||||
MTYPE_TEMP6,
|
|
||||||
MTYPE_TEMP7,
|
|
||||||
MTYPE_TEMP8,
|
|
||||||
MTYPE_TEMP9,
|
|
||||||
|
|
||||||
MTYPE_OTHER, // Special, used by display code
|
|
||||||
|
|
||||||
MTYPE_NUM_TYPES
|
|
||||||
};
|
};
|
||||||
enum { MTYPE_MAX_DEPTH = 64 };
|
|
||||||
|
|
||||||
public:
|
LLMemType(DeclareMemType& dt);
|
||||||
LLMemType(EMemType type)
|
~LLMemType();
|
||||||
{
|
|
||||||
#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 char const * getNameFromID(S32 id);
|
||||||
static void printMem();
|
|
||||||
|
|
||||||
public:
|
static DeclareMemType MTYPE_INIT;
|
||||||
#if MEM_TRACK_TYPE
|
static DeclareMemType MTYPE_STARTUP;
|
||||||
static S32 sCurDepth;
|
static DeclareMemType MTYPE_MAIN;
|
||||||
static S32 sCurType;
|
static DeclareMemType MTYPE_FRAME;
|
||||||
static S32 sType[MTYPE_MAX_DEPTH];
|
|
||||||
static S32 sMemCount[MTYPE_NUM_TYPES];
|
static DeclareMemType MTYPE_GATHER_INPUT;
|
||||||
static S32 sMaxMemCount[MTYPE_NUM_TYPES];
|
static DeclareMemType MTYPE_JOY_KEY;
|
||||||
static S32 sNewCount[MTYPE_NUM_TYPES];
|
|
||||||
static S32 sOverheadMem;
|
static DeclareMemType MTYPE_IDLE;
|
||||||
static const char* sTypeDesc[MTYPE_NUM_TYPES];
|
static DeclareMemType MTYPE_IDLE_PUMP;
|
||||||
#endif
|
static DeclareMemType MTYPE_IDLE_NETWORK;
|
||||||
static S32 sTotalMem;
|
static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
|
||||||
static S32 sMaxTotalMem;
|
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;
|
||||||
|
|
||||||
|
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()
|
static void deleteSingleton()
|
||||||
{
|
{
|
||||||
delete getData().mSingletonInstance;
|
DERIVED_TYPE* instance = getData().mSingletonInstance;
|
||||||
getData().mSingletonInstance = NULL;
|
|
||||||
getData().mInitState = DELETED;
|
getData().mInitState = DELETED;
|
||||||
|
getData().mSingletonInstance = NULL;
|
||||||
|
delete instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SingletonInstanceData& getData()
|
static SingletonInstanceData& getData()
|
||||||
@@ -175,23 +176,9 @@ public:
|
|||||||
{
|
{
|
||||||
SingletonInstanceData& data = getData();
|
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;
|
return data.mSingletonInstance;
|
||||||
@@ -219,7 +206,35 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void createInstance(SingletonInstanceData& data);
|
||||||
virtual void initSingleton() {}
|
virtual void initSingleton() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Moved this here cause it's too big to be inlined --Aleric.
|
||||||
|
template<typename DERIVED_TYPE>
|
||||||
|
void LLSingleton<DERIVED_TYPE>::createInstance(SingletonInstanceData& data)
|
||||||
|
{
|
||||||
|
if (data.mInitState == CONSTRUCTING)
|
||||||
|
{
|
||||||
|
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.mInitState == DELETED)
|
||||||
|
{
|
||||||
|
llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.mInitState == INITIALIZING)
|
||||||
|
{
|
||||||
|
llwarns << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from initSingleton(), using half-initialized object" << llendl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.mInitState = CONSTRUCTING;
|
||||||
|
data.mSingletonInstance = new DERIVED_TYPE();
|
||||||
|
data.mInitState = INITIALIZING;
|
||||||
|
data.mSingletonInstance->initSingleton();
|
||||||
|
data.mInitState = INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ U64 get_clock_count()
|
|||||||
return clock_count.QuadPart - offset;
|
return clock_count.QuadPart - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
F64 calc_clock_frequency(U32 uiMeasureMSecs)
|
F64 calc_clock_frequency(void)
|
||||||
{
|
{
|
||||||
__int64 freq;
|
__int64 freq;
|
||||||
QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
|
QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
|
||||||
@@ -209,7 +209,7 @@ F64 calc_clock_frequency(U32 uiMeasureMSecs)
|
|||||||
|
|
||||||
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
|
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
|
||||||
// Both Linux and Mac use gettimeofday for accurate time
|
// Both Linux and Mac use gettimeofday for accurate time
|
||||||
F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
|
F64 calc_clock_frequency(void)
|
||||||
{
|
{
|
||||||
return 1000000.0; // microseconds, so 1 Mhz.
|
return 1000000.0; // microseconds, so 1 Mhz.
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ U64 get_clock_count()
|
|||||||
|
|
||||||
void update_clock_frequencies()
|
void update_clock_frequencies()
|
||||||
{
|
{
|
||||||
gClockFrequency = calc_clock_frequency(50U);
|
gClockFrequency = calc_clock_frequency();
|
||||||
gClockFrequencyInv = 1.0/gClockFrequency;
|
gClockFrequencyInv = 1.0/gClockFrequency;
|
||||||
gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
|
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.
|
// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work.
|
||||||
//
|
//
|
||||||
LL_COMMON_API U64 get_clock_count();
|
LL_COMMON_API U64 get_clock_count();
|
||||||
LL_COMMON_API F64 calc_clock_frequency(U32 msecs);
|
LL_COMMON_API F64 calc_clock_frequency();
|
||||||
LL_COMMON_API void update_clock_frequencies();
|
LL_COMMON_API void update_clock_frequencies();
|
||||||
|
|
||||||
// Sleep for milliseconds
|
// 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_MAJOR = 1;
|
||||||
const S32 LL_VERSION_MINOR = 6;
|
const S32 LL_VERSION_MINOR = 6;
|
||||||
const S32 LL_VERSION_PATCH = 0;
|
const S32 LL_VERSION_PATCH = 0;
|
||||||
const S32 LL_VERSION_BUILD = 3;
|
const S32 LL_VERSION_BUILD = ${vBUILD};
|
||||||
|
|
||||||
const char * const LL_CHANNEL = "Singularity";
|
const char * const LL_CHANNEL = "${VIEWER_CHANNEL}";
|
||||||
|
|
||||||
#if LL_DARWIN
|
#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
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ void LLImageBase::deleteData()
|
|||||||
// virtual
|
// virtual
|
||||||
U8* LLImageBase::allocateData(S32 size)
|
U8* LLImageBase::allocateData(S32 size)
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
|
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
{
|
{
|
||||||
@@ -209,7 +209,7 @@ U8* LLImageBase::reallocateData(S32 size)
|
|||||||
if(mData && (mDataSize == size))
|
if(mData && (mDataSize == size))
|
||||||
return mData;
|
return mData;
|
||||||
|
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
U8 *new_datap = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
|
U8 *new_datap = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
|
||||||
if (!new_datap)
|
if (!new_datap)
|
||||||
{
|
{
|
||||||
@@ -457,7 +457,7 @@ void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
|
|||||||
// Reverses the order of the rows in the image
|
// Reverses the order of the rows in the image
|
||||||
void LLImageRaw::verticalFlip()
|
void LLImageRaw::verticalFlip()
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
S32 row_bytes = getWidth() * getComponents();
|
S32 row_bytes = getWidth() * getComponents();
|
||||||
U8* line_buffer = new (std::nothrow) U8[row_bytes];
|
U8* line_buffer = new (std::nothrow) U8[row_bytes];
|
||||||
if (!line_buffer )
|
if (!line_buffer )
|
||||||
@@ -595,7 +595,7 @@ void LLImageRaw::composite( LLImageRaw* src )
|
|||||||
// Src and dst can be any size. Src has 4 components. Dst has 3 components.
|
// Src and dst can be any size. Src has 4 components. Dst has 3 components.
|
||||||
void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
|
void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
llinfos << "compositeScaled4onto3" << llendl;
|
llinfos << "compositeScaled4onto3" << llendl;
|
||||||
|
|
||||||
LLImageRaw* dst = this; // Just for clarity.
|
LLImageRaw* dst = this; // Just for clarity.
|
||||||
@@ -846,7 +846,7 @@ void LLImageRaw::copyUnscaled3onto4( LLImageRaw* src )
|
|||||||
// Src and dst can be any size. Src and dst have same number of components.
|
// Src and dst can be any size. Src and dst have same number of components.
|
||||||
void LLImageRaw::copyScaled( LLImageRaw* src )
|
void LLImageRaw::copyScaled( LLImageRaw* src )
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
LLImageRaw* dst = this; // Just for clarity.
|
LLImageRaw* dst = this; // Just for clarity.
|
||||||
|
|
||||||
llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) );
|
llassert_always( (1 == src->getComponents()) || (3 == src->getComponents()) || (4 == src->getComponents()) );
|
||||||
@@ -932,7 +932,7 @@ BOOL LLImageRaw::scaleDownWithoutBlending( S32 new_width, S32 new_height)
|
|||||||
|
|
||||||
BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
|
BOOL LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) );
|
llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) );
|
||||||
|
|
||||||
S32 old_width = getWidth();
|
S32 old_width = getWidth();
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
#include "lluuid.h"
|
#include "lluuid.h"
|
||||||
#include "llstring.h"
|
#include "llstring.h"
|
||||||
#include "llmemory.h"
|
#include "llmemtype.h"
|
||||||
#include "llthread.h"
|
#include "llthread.h"
|
||||||
#include "aithreadsafe.h"
|
#include "aithreadsafe.h"
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ private:
|
|||||||
|
|
||||||
static LLPrivateMemoryPool* sPrivatePoolp ;
|
static LLPrivateMemoryPool* sPrivatePoolp ;
|
||||||
public:
|
public:
|
||||||
S16 mMemType; // debug
|
LLMemType::DeclareMemType& mMemType; // debug
|
||||||
};
|
};
|
||||||
|
|
||||||
// Raw representation of an image (used for textures, and other uncompressed formats
|
// Raw representation of an image (used for textures, and other uncompressed formats
|
||||||
@@ -330,7 +330,7 @@ public:
|
|||||||
virtual void setLastError(const std::string& message, const std::string& filename = std::string());
|
virtual void setLastError(const std::string& message, const std::string& filename = std::string());
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BOOL copyData(U8 *data, S32 size); // calls updateData()
|
BOOL copyData(U8 *data, S32 size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
S8 mCodec;
|
S8 mCodec;
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
|
|||||||
// Returns TRUE to mean done, whether successful or not.
|
// Returns TRUE to mean done, whether successful or not.
|
||||||
BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
|
BOOL LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
|
|
||||||
BOOL res = TRUE;
|
BOOL res = TRUE;
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@ BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time)
|
|||||||
|
|
||||||
BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time)
|
BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time)
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
resetLastError();
|
resetLastError();
|
||||||
BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible);
|
BOOL res = mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time, mReversible);
|
||||||
if (!mLastError.empty())
|
if (!mLastError.empty())
|
||||||
@@ -529,7 +529,7 @@ BOOL LLImageJ2C::loadAndValidate(const std::string &filename)
|
|||||||
|
|
||||||
BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
|
BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
|
||||||
{
|
{
|
||||||
LLMemType mt1((LLMemType::EMemType)mMemType);
|
LLMemType mt1(mMemType);
|
||||||
|
|
||||||
resetLastError();
|
resetLastError();
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
|
|||||||
// dereference the array.
|
// dereference the array.
|
||||||
if(!image || !image->numcomps)
|
if(!image || !image->numcomps)
|
||||||
{
|
{
|
||||||
llwarns << "ERROR -> decodeImpl: failed to decode image!" << llendl;
|
llwarns << "failed to decode image!" << llendl;
|
||||||
if (image)
|
if (image)
|
||||||
{
|
{
|
||||||
opj_image_destroy(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())
|
if (image->comps[i].factor != base.getRawDiscardLevel())
|
||||||
{
|
{
|
||||||
|
llwarns << "Expected discard level not reached!" << llendl;
|
||||||
// if we didn't get the discard level we're expecting, fail
|
// if we didn't get the discard level we're expecting, fail
|
||||||
opj_image_destroy(image);
|
opj_image_destroy(image);
|
||||||
base.decodeFailed();
|
base.decodeFailed();
|
||||||
|
|||||||
@@ -1,31 +1,25 @@
|
|||||||
/**
|
/**
|
||||||
* @file lleconomy.cpp
|
* @file lleconomy.cpp
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -54,6 +48,31 @@ LLGlobalEconomy::LLGlobalEconomy()
|
|||||||
LLGlobalEconomy::~LLGlobalEconomy()
|
LLGlobalEconomy::~LLGlobalEconomy()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
void LLGlobalEconomy::addObserver(LLEconomyObserver* observer)
|
||||||
|
{
|
||||||
|
mObservers.push_back(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLGlobalEconomy::removeObserver(LLEconomyObserver* observer)
|
||||||
|
{
|
||||||
|
std::list<LLEconomyObserver*>::iterator it =
|
||||||
|
std::find(mObservers.begin(), mObservers.end(), observer);
|
||||||
|
if (it != mObservers.end())
|
||||||
|
{
|
||||||
|
mObservers.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLGlobalEconomy::notifyObservers()
|
||||||
|
{
|
||||||
|
for (std::list<LLEconomyObserver*>::iterator it = mObservers.begin();
|
||||||
|
it != mObservers.end();
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
(*it)->onEconomyDataChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data)
|
void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data)
|
||||||
{
|
{
|
||||||
@@ -94,6 +113,8 @@ void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy*
|
|||||||
econ_data->setTeleportPriceExponent(f);
|
econ_data->setTeleportPriceExponent(f);
|
||||||
msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceGroupCreate, i);
|
msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceGroupCreate, i);
|
||||||
econ_data->setPriceGroupCreate(i);
|
econ_data->setPriceGroupCreate(i);
|
||||||
|
|
||||||
|
econ_data->notifyObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const
|
S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const
|
||||||
|
|||||||
@@ -1,31 +1,25 @@
|
|||||||
/**
|
/**
|
||||||
* @file lleconomy.h
|
* @file lleconomy.h
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -37,6 +31,16 @@
|
|||||||
class LLMessageSystem;
|
class LLMessageSystem;
|
||||||
class LLVector3;
|
class LLVector3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an observer to be notified of economy data updates coming from server.
|
||||||
|
*/
|
||||||
|
class LLEconomyObserver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~LLEconomyObserver() {}
|
||||||
|
virtual void onEconomyDataChange() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class LLGlobalEconomy
|
class LLGlobalEconomy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -52,6 +56,10 @@ public:
|
|||||||
|
|
||||||
virtual void print();
|
virtual void print();
|
||||||
|
|
||||||
|
void addObserver(LLEconomyObserver* observer);
|
||||||
|
void removeObserver(LLEconomyObserver* observer);
|
||||||
|
void notifyObservers();
|
||||||
|
|
||||||
static void processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data);
|
static void processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data);
|
||||||
|
|
||||||
S32 calculateTeleportCost(F32 distance) const;
|
S32 calculateTeleportCost(F32 distance) const;
|
||||||
@@ -95,6 +103,8 @@ private:
|
|||||||
S32 mTeleportMinPrice;
|
S32 mTeleportMinPrice;
|
||||||
F32 mTeleportPriceExponent;
|
F32 mTeleportPriceExponent;
|
||||||
S32 mPriceGroupCreate;
|
S32 mPriceGroupCreate;
|
||||||
|
|
||||||
|
std::list<LLEconomyObserver*> mObservers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ public:
|
|||||||
typedef LLOctreeTraveler<T> oct_traveler;
|
typedef LLOctreeTraveler<T> oct_traveler;
|
||||||
typedef LLTreeTraveler<T> tree_traveler;
|
typedef LLTreeTraveler<T> tree_traveler;
|
||||||
typedef typename std::set<LLPointer<T> > element_list;
|
typedef typename std::set<LLPointer<T> > element_list;
|
||||||
typedef typename std::set<LLPointer<T> >::iterator element_iter;
|
typedef typename element_list::iterator element_iter;
|
||||||
typedef typename std::set<LLPointer<T> >::const_iterator const_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<LLTreeListener<T>*>::iterator tree_listener_iter;
|
||||||
typedef typename std::vector<LLOctreeNode<T>* > child_list;
|
typedef typename std::vector<LLOctreeNode<T>* > child_list;
|
||||||
typedef LLTreeNode<T> BaseType;
|
typedef LLTreeNode<T> BaseType;
|
||||||
@@ -124,6 +124,8 @@ public:
|
|||||||
mOctant = ((oct_node*) mParent)->getOctant(mCenter);
|
mOctant = ((oct_node*) mParent)->getOctant(mCenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mElementCount = 0;
|
||||||
|
|
||||||
clearChildren();
|
clearChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,11 +231,11 @@ public:
|
|||||||
void accept(oct_traveler* visitor) { visitor->visit(this); }
|
void accept(oct_traveler* visitor) { visitor->visit(this); }
|
||||||
virtual bool isLeaf() const { return mChild.empty(); }
|
virtual bool isLeaf() const { return mChild.empty(); }
|
||||||
|
|
||||||
U32 getElementCount() const { return mData.size(); }
|
U32 getElementCount() const { return mElementCount; }
|
||||||
element_list& getData() { return mData; }
|
element_list& getData() { return mData; }
|
||||||
const element_list& getData() const { return mData; }
|
const element_list& getData() const { return mData; }
|
||||||
|
|
||||||
U32 getChildCount() const { return mChild.size(); }
|
U32 getChildCount() const { return mChildCount; }
|
||||||
oct_node* getChild(U32 index) { return mChild[index]; }
|
oct_node* getChild(U32 index) { return mChild[index]; }
|
||||||
const oct_node* getChild(U32 index) const { return mChild[index]; }
|
const oct_node* getChild(U32 index) const { return mChild[index]; }
|
||||||
child_list& getChildren() { return mChild; }
|
child_list& getChildren() { return mChild; }
|
||||||
@@ -321,6 +323,8 @@ public:
|
|||||||
|
|
||||||
mData.insert(data);
|
mData.insert(data);
|
||||||
BaseType::insert(data);
|
BaseType::insert(data);
|
||||||
|
|
||||||
|
mElementCount = mData.size();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -356,6 +360,8 @@ public:
|
|||||||
{
|
{
|
||||||
mData.insert(data);
|
mData.insert(data);
|
||||||
BaseType::insert(data);
|
BaseType::insert(data);
|
||||||
|
|
||||||
|
mElementCount = mData.size();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,6 +415,7 @@ public:
|
|||||||
if (mData.find(data) != mData.end())
|
if (mData.find(data) != mData.end())
|
||||||
{ //we have data
|
{ //we have data
|
||||||
mData.erase(data);
|
mData.erase(data);
|
||||||
|
mElementCount = mData.size();
|
||||||
notifyRemoval(data);
|
notifyRemoval(data);
|
||||||
checkAlive();
|
checkAlive();
|
||||||
return true;
|
return true;
|
||||||
@@ -446,6 +453,7 @@ public:
|
|||||||
if (mData.find(data) != mData.end())
|
if (mData.find(data) != mData.end())
|
||||||
{
|
{
|
||||||
mData.erase(data);
|
mData.erase(data);
|
||||||
|
mElementCount = mData.size();
|
||||||
notifyRemoval(data);
|
notifyRemoval(data);
|
||||||
llwarns << "FOUND!" << llendl;
|
llwarns << "FOUND!" << llendl;
|
||||||
checkAlive();
|
checkAlive();
|
||||||
@@ -462,7 +470,7 @@ public:
|
|||||||
void clearChildren()
|
void clearChildren()
|
||||||
{
|
{
|
||||||
mChild.clear();
|
mChild.clear();
|
||||||
|
mChildCount = 0;
|
||||||
U32* foo = (U32*) mChildMap;
|
U32* foo = (U32*) mChildMap;
|
||||||
foo[0] = foo[1] = 0xFFFFFFFF;
|
foo[0] = foo[1] = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
@@ -522,9 +530,10 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mChildMap[child->getOctant()] = (U8) mChild.size();
|
mChildMap[child->getOctant()] = mChildCount;
|
||||||
|
|
||||||
mChild.push_back(child);
|
mChild.push_back(child);
|
||||||
|
++mChildCount;
|
||||||
child->setParent(this);
|
child->setParent(this);
|
||||||
|
|
||||||
if (!silent)
|
if (!silent)
|
||||||
@@ -553,12 +562,13 @@ public:
|
|||||||
delete mChild[index];
|
delete mChild[index];
|
||||||
}
|
}
|
||||||
mChild.erase(mChild.begin() + index);
|
mChild.erase(mChild.begin() + index);
|
||||||
|
--mChildCount;
|
||||||
|
|
||||||
//rebuild child map
|
//rebuild child map
|
||||||
U32* foo = (U32*) mChildMap;
|
U32* foo = (U32*) mChildMap;
|
||||||
foo[0] = foo[1] = 0xFFFFFFFF;
|
foo[0] = foo[1] = 0xFFFFFFFF;
|
||||||
|
|
||||||
for (U32 i = 0; i < mChild.size(); ++i)
|
for (U32 i = 0; i < mChildCount; ++i)
|
||||||
{
|
{
|
||||||
mChildMap[mChild[i]->getOctant()] = i;
|
mChildMap[mChild[i]->getOctant()] = i;
|
||||||
}
|
}
|
||||||
@@ -611,8 +621,10 @@ protected:
|
|||||||
|
|
||||||
child_list mChild;
|
child_list mChild;
|
||||||
U8 mChildMap[8];
|
U8 mChildMap[8];
|
||||||
|
U32 mChildCount;
|
||||||
|
|
||||||
element_list mData;
|
element_list mData;
|
||||||
|
U32 mElementCount;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ set(llmessage_SOURCE_FILES
|
|||||||
llregionpresenceverifier.cpp
|
llregionpresenceverifier.cpp
|
||||||
llsdappservices.cpp
|
llsdappservices.cpp
|
||||||
llsdhttpserver.cpp
|
llsdhttpserver.cpp
|
||||||
|
llsdmessage.cpp
|
||||||
llsdmessagebuilder.cpp
|
llsdmessagebuilder.cpp
|
||||||
llsdmessagereader.cpp
|
llsdmessagereader.cpp
|
||||||
llsdrpcclient.cpp
|
llsdrpcclient.cpp
|
||||||
@@ -163,6 +164,7 @@ set(llmessage_HEADER_FILES
|
|||||||
llregionpresenceverifier.h
|
llregionpresenceverifier.h
|
||||||
llsdappservices.h
|
llsdappservices.h
|
||||||
llsdhttpserver.h
|
llsdhttpserver.h
|
||||||
|
llsdmessage.h
|
||||||
llsdmessagebuilder.h
|
llsdmessagebuilder.h
|
||||||
llsdmessagereader.h
|
llsdmessagereader.h
|
||||||
llsdrpcclient.h
|
llsdrpcclient.h
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "llsdserialize.h"
|
#include "llsdserialize.h"
|
||||||
#include "lluuid.h"
|
#include "lluuid.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
#include "llmemtype.h"
|
||||||
|
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
@@ -771,6 +772,7 @@ bool LLCacheName::getIfThere(const LLUUID& id, std::string& fullname, BOOL& is_g
|
|||||||
// </edit>
|
// </edit>
|
||||||
void LLCacheName::processPending()
|
void LLCacheName::processPending()
|
||||||
{
|
{
|
||||||
|
LLMemType mt_pp(LLMemType::MTYPE_CACHE_PROCESS_PENDING);
|
||||||
const F32 SECS_BETWEEN_PROCESS = 0.1f;
|
const F32 SECS_BETWEEN_PROCESS = 0.1f;
|
||||||
if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
|
if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
|
||||||
{
|
{
|
||||||
@@ -873,8 +875,10 @@ std::string LLCacheName::getDefaultLastName()
|
|||||||
{
|
{
|
||||||
return "Resident";
|
return "Resident";
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLCacheName::Impl::processPendingAsks()
|
void LLCacheName::Impl::processPendingAsks()
|
||||||
{
|
{
|
||||||
|
LLMemType mt_ppa(LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS);
|
||||||
sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
|
sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
|
||||||
sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
|
sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
|
||||||
mAskNameQueue.clear();
|
mAskNameQueue.clear();
|
||||||
@@ -883,6 +887,7 @@ void LLCacheName::Impl::processPendingAsks()
|
|||||||
|
|
||||||
void LLCacheName::Impl::processPendingReplies()
|
void LLCacheName::Impl::processPendingReplies()
|
||||||
{
|
{
|
||||||
|
LLMemType mt_ppr(LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES);
|
||||||
// First call all the callbacks, because they might send messages.
|
// First call all the callbacks, because they might send messages.
|
||||||
for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
|
for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp,
|
|||||||
: LLAssetRequest(uuid, type),
|
: LLAssetRequest(uuid, type),
|
||||||
mZInitialized(false)
|
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;
|
mAssetStoragep = asp;
|
||||||
mCurlHandle = NULL;
|
mCurlHandle = NULL;
|
||||||
mCurlMultiHandle = curl_multi;
|
mCurlMultiHandle = curl_multi;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
#define LL_HTTPCLIENTADAPTER_H
|
#define LL_HTTPCLIENTADAPTER_H
|
||||||
|
|
||||||
#include "llhttpclientinterface.h"
|
#include "llhttpclientinterface.h"
|
||||||
#include "llmemory.h" // LLSingleton<>
|
#include "llsingleton.h" // LLSingleton<>
|
||||||
|
|
||||||
class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter>
|
class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,31 +4,25 @@
|
|||||||
* @date 2005-10-05
|
* @date 2005-10-05
|
||||||
* @brief Implementation of the http server classes
|
* @brief Implementation of the http server classes
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2005&license=viewergpl$
|
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2005-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -972,7 +966,9 @@ private:
|
|||||||
// static
|
// static
|
||||||
LLHTTPNode& LLIOHTTPServer::create(LLPumpIO& pump, U16 port)
|
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)
|
if(!socket)
|
||||||
{
|
{
|
||||||
llerrs << "Unable to initialize socket" << llendl;
|
llerrs << "Unable to initialize socket" << llendl;
|
||||||
|
|||||||
@@ -114,11 +114,21 @@ LLSocket::ptr_t LLSocket::create(EType type, U16 port)
|
|||||||
|
|
||||||
if(STREAM_TCP == type)
|
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)
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -234,6 +244,7 @@ LLSocket::LLSocket() :
|
|||||||
mPool(LLThread::tldata().mRootPool),
|
mPool(LLThread::tldata().mRootPool),
|
||||||
mPort(PORT_INVALID)
|
mPort(PORT_INVALID)
|
||||||
{
|
{
|
||||||
|
LLMemType m1(LLMemType::MTYPE_IO_TCP);
|
||||||
}
|
}
|
||||||
|
|
||||||
LLSocket::~LLSocket()
|
LLSocket::~LLSocket()
|
||||||
|
|||||||
@@ -239,15 +239,23 @@ S32 LLPacketRing::receivePacket (S32 socket, char *datap)
|
|||||||
// no delay, pull straight from net
|
// no delay, pull straight from net
|
||||||
if (LLSocks::isEnabled())
|
if (LLSocks::isEnabled())
|
||||||
{
|
{
|
||||||
proxywrap_t * header;
|
const U8 SOCKS_HEADER_SIZE = 10;
|
||||||
datap = datap-10;
|
U8 buffer[NET_BUFFER_SIZE + SOCKS_HEADER_SIZE];
|
||||||
header = (proxywrap_t *)datap;
|
packet_size = receive_packet(socket, static_cast<char*>(static_cast<void*>(buffer)));
|
||||||
packet_size = receive_packet(socket, datap);
|
|
||||||
mLastSender.setAddress(header->addr);
|
if (packet_size > SOCKS_HEADER_SIZE)
|
||||||
mLastSender.setPort(ntohs(header->port));
|
|
||||||
if (packet_size > 10)
|
|
||||||
{
|
{
|
||||||
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
|
else
|
||||||
@@ -285,7 +293,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
|||||||
|
|
||||||
if (!mUseOutThrottle)
|
if (!mUseOutThrottle)
|
||||||
{
|
{
|
||||||
return doSendPacket(h_socket, send_buffer, buf_size, host );
|
return sendPacketImpl(h_socket, send_buffer, buf_size, host );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -306,7 +314,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
|||||||
mOutBufferLength -= packetp->getSize();
|
mOutBufferLength -= packetp->getSize();
|
||||||
packet_size = 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;
|
delete packetp;
|
||||||
// Update the throttle
|
// Update the throttle
|
||||||
@@ -315,7 +323,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the queue's empty, we can just send this packet right away.
|
// 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;
|
packet_size = buf_size;
|
||||||
|
|
||||||
// Update the throttle
|
// Update the throttle
|
||||||
@@ -354,7 +362,7 @@ BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LL
|
|||||||
return status;
|
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())
|
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());
|
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->rsv = 0;
|
||||||
socks_header->addr = host.getAddress();
|
socks_header->addr = host.getAddress();
|
||||||
socks_header->port = htons(host.getPort());
|
socks_header->port = htons(host.getPort());
|
||||||
socks_header->atype = ADDRESS_IPV4;
|
socks_header->atype = ADDRESS_IPV4;
|
||||||
socks_header->frag = 0;
|
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 mLastSender;
|
||||||
LLHost mLastReceivingIF;
|
LLHost mLastReceivingIF;
|
||||||
|
|
||||||
BOOL doSendPacket(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);
|
private:
|
||||||
U8 mProxyWrappedSendBuffer[NET_BUFFER_SIZE];
|
BOOL sendPacketImpl(int h_socket, const char * send_buffer, S32 buf_size, LLHost host);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llregionflags.h
|
* @file llregionflags.h
|
||||||
* @brief Flags that are sent in the statistics message region_flags field.
|
* @brief Flags that are sent in the statistics message region_flags field.
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -48,9 +42,6 @@ const U32 REGION_FLAGS_RESET_HOME_ON_TELEPORT = (1 << 3);
|
|||||||
// Does the sun move?
|
// Does the sun move?
|
||||||
const U32 REGION_FLAGS_SUN_FIXED = (1 << 4);
|
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,
|
// Can't change the terrain heightfield, even on owned parcels,
|
||||||
// but can plant trees and grass.
|
// but can plant trees and grass.
|
||||||
const U32 REGION_FLAGS_BLOCK_TERRAFORM = (1 << 6);
|
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
|
// All content wiped once per night
|
||||||
const U32 REGION_FLAGS_SANDBOX = (1 << 8);
|
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_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies
|
||||||
const U32 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13);
|
const U32 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13);
|
||||||
const U32 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics
|
const U32 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics
|
||||||
const U32 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15);
|
const U32 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15);
|
||||||
//const U32 REGION_FLAGS_MAINLAND_VISIBLE = (1 << 16);
|
const U32 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_OBJECT = (1 << 16);
|
||||||
const U32 REGION_FLAGS_PUBLIC_ALLOWED = (1 << 17);
|
const U32 REGION_FLAGS_ALLOW_RETURN_ENCROACHING_ESTATE_OBJECT = (1 << 17);
|
||||||
const U32 REGION_FLAGS_BLOCK_DWELL = (1 << 18);
|
const U32 REGION_FLAGS_BLOCK_DWELL = (1 << 18);
|
||||||
|
|
||||||
// Is flight allowed?
|
// 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_RESTRICT_PUSHOBJECT = (1 << 22);
|
||||||
|
|
||||||
const U32 REGION_FLAGS_DENY_ANONYMOUS = (1 << 23);
|
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_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_ALLOW_VOICE = (1 << 28);
|
||||||
|
|
||||||
const U32 REGION_FLAGS_BLOCK_PARCEL_SEARCH = (1 << 29);
|
const U32 REGION_FLAGS_BLOCK_PARCEL_SEARCH = (1 << 29);
|
||||||
const U32 REGION_FLAGS_DENY_AGEUNVERIFIED = (1 << 30);
|
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 |
|
const U32 REGION_FLAGS_DEFAULT = REGION_FLAGS_ALLOW_LANDMARK |
|
||||||
REGION_FLAGS_ALLOW_SET_HOME |
|
REGION_FLAGS_ALLOW_SET_HOME |
|
||||||
@@ -111,7 +92,6 @@ const U32 REGION_FLAGS_PRELUDE_UNSET = REGION_FLAGS_ALLOW_LANDMARK
|
|||||||
| REGION_FLAGS_ALLOW_SET_HOME;
|
| REGION_FLAGS_ALLOW_SET_HOME;
|
||||||
|
|
||||||
const U32 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE
|
const U32 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE
|
||||||
| REGION_FLAGS_PUBLIC_ALLOWED
|
|
||||||
| REGION_FLAGS_SUN_FIXED
|
| REGION_FLAGS_SUN_FIXED
|
||||||
| REGION_FLAGS_DENY_ANONYMOUS
|
| REGION_FLAGS_DENY_ANONYMOUS
|
||||||
| REGION_FLAGS_DENY_AGEUNVERIFIED;
|
| 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::TypeMap:
|
||||||
case LLSD::TypeArray:
|
case LLSD::TypeArray:
|
||||||
case LLSD::TypeUndefined:
|
case LLSD::TypeUndefined:
|
||||||
|
default: // TypeLLSDTypeEnd, TypeLLSDNumTypes, etc.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
//return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//virtual
|
//virtual
|
||||||
|
|||||||
@@ -86,6 +86,7 @@
|
|||||||
#include "v3math.h"
|
#include "v3math.h"
|
||||||
#include "v4math.h"
|
#include "v4math.h"
|
||||||
#include "lltransfertargetvfile.h"
|
#include "lltransfertargetvfile.h"
|
||||||
|
#include "llmemtype.h"
|
||||||
|
|
||||||
// <edit>
|
// <edit>
|
||||||
#include "llrand.h"
|
#include "llrand.h"
|
||||||
@@ -556,11 +557,11 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count, bool faked_message, U8 fak
|
|||||||
S32 acks = 0;
|
S32 acks = 0;
|
||||||
S32 true_rcv_size = 0;
|
S32 true_rcv_size = 0;
|
||||||
|
|
||||||
U8* buffer = mTrueReceiveBuffer.buffer;
|
U8* buffer = mTrueReceiveBuffer;
|
||||||
|
|
||||||
if(!faked_message)
|
if(!faked_message)
|
||||||
{
|
{
|
||||||
mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer.buffer);
|
mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer);
|
||||||
receive_size = mTrueReceiveSize;
|
receive_size = mTrueReceiveSize;
|
||||||
mLastSender = mPacketRing.getLastSender();
|
mLastSender = mPacketRing.getLastSender();
|
||||||
mLastReceivingIF = mPacketRing.getLastReceivingInterface();
|
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)
|
for(S32 i = 0; i < acks; ++i)
|
||||||
{
|
{
|
||||||
true_rcv_size -= sizeof(TPACKETID);
|
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));
|
sizeof(TPACKETID));
|
||||||
packet_id = ntohl(mem_id);
|
packet_id = ntohl(mem_id);
|
||||||
//LL_INFOS("Messaging") << "got ack: " << packet_id << llendl;
|
//LL_INFOS("Messaging") << "got ack: " << packet_id << llendl;
|
||||||
@@ -811,6 +812,7 @@ S32 LLMessageSystem::getReceiveBytes() const
|
|||||||
|
|
||||||
void LLMessageSystem::processAcks()
|
void LLMessageSystem::processAcks()
|
||||||
{
|
{
|
||||||
|
LLMemType mt_pa(LLMemType::MTYPE_MESSAGE_PROCESS_ACKS);
|
||||||
F64 mt_sec = getMessageTimeSeconds();
|
F64 mt_sec = getMessageTimeSeconds();
|
||||||
{
|
{
|
||||||
gTransferManager.updateTransfers();
|
gTransferManager.updateTransfers();
|
||||||
@@ -3405,7 +3407,7 @@ void LLMessageSystem::dumpPacketToLog()
|
|||||||
{
|
{
|
||||||
S32 offset = cur_line_pos * 3;
|
S32 offset = cur_line_pos * 3;
|
||||||
snprintf(line_buffer + offset, sizeof(line_buffer) - offset,
|
snprintf(line_buffer + offset, sizeof(line_buffer) - offset,
|
||||||
"%02x ", mTrueReceiveBuffer.buffer[i]); /* Flawfinder: ignore */
|
"%02x ", mTrueReceiveBuffer[i]); /* Flawfinder: ignore */
|
||||||
cur_line_pos++;
|
cur_line_pos++;
|
||||||
if (cur_line_pos >= 16)
|
if (cur_line_pos >= 16)
|
||||||
{
|
{
|
||||||
@@ -4048,6 +4050,7 @@ void LLMessageSystem::setTimeDecodesSpamThreshold( F32 seconds )
|
|||||||
// TODO: babbage: move gServicePump in to LLMessageSystem?
|
// TODO: babbage: move gServicePump in to LLMessageSystem?
|
||||||
bool LLMessageSystem::checkAllMessages(S64 frame_count, LLPumpIO* http_pump)
|
bool LLMessageSystem::checkAllMessages(S64 frame_count, LLPumpIO* http_pump)
|
||||||
{
|
{
|
||||||
|
LLMemType mt_cam(LLMemType::MTYPE_MESSAGE_CHECK_ALL);
|
||||||
if(checkMessages(frame_count))
|
if(checkMessages(frame_count))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -778,18 +778,7 @@ private:
|
|||||||
LLMessagePollInfo *mPollInfop;
|
LLMessagePollInfo *mPollInfop;
|
||||||
|
|
||||||
U8 mEncodedRecvBuffer[MAX_BUFFER_SIZE];
|
U8 mEncodedRecvBuffer[MAX_BUFFER_SIZE];
|
||||||
|
U8 mTrueReceiveBuffer[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 */
|
|
||||||
|
|
||||||
S32 mTrueReceiveSize;
|
S32 mTrueReceiveSize;
|
||||||
|
|
||||||
// Must be valid during decode
|
// Must be valid during decode
|
||||||
|
|||||||
@@ -127,6 +127,16 @@ LLMaterialTable::~LLMaterialTable()
|
|||||||
mMaterialInfoList.clear();
|
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()
|
void LLMaterialTable::initBasicTable()
|
||||||
{
|
{
|
||||||
// *TODO: Translate
|
// *TODO: Translate
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ public:
|
|||||||
|
|
||||||
void initBasicTable();
|
void initBasicTable();
|
||||||
|
|
||||||
|
void initTableTransNames(std::map<std::string, std::string> namemap);
|
||||||
|
|
||||||
BOOL add(U8 mcode, const std::string& name, const LLUUID &uuid);
|
BOOL add(U8 mcode, const std::string& name, const LLUUID &uuid);
|
||||||
BOOL addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
|
BOOL addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llvolumexml.cpp
|
* @file llvolumexml.cpp
|
||||||
* @brief LLVolumeXml base class
|
* @brief LLVolumeXml base class
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/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("curve_type", TRUE)->setByteValue(1, ¶ms->getCurveType());
|
||||||
ret->createChild("interval", FALSE)->setFloatValue(2, ¶ms->getBegin());
|
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("curve_type", TRUE)->setByteValue(1, ¶ms->getCurveType());
|
||||||
ret->createChild("interval", FALSE)->setFloatValue(2, ¶ms->getBegin());
|
ret->createChild("interval", FALSE)->setFloatValue(2, ¶ms->getBegin());
|
||||||
ret->createChild("scale", FALSE)->setFloatValue(2, params->getScale().mV);
|
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);
|
LLPointer<LLXMLNode> node ;
|
||||||
exportProfileParams(¶ms->getProfileParams())->setParent(ret);
|
node = exportPathParams(¶ms->getPathParams()) ;
|
||||||
|
node->setParent(ret);
|
||||||
|
node = exportProfileParams(¶ms->getProfileParams()) ;
|
||||||
|
node->setParent(ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llvolumexml.h
|
* @file llvolumexml.h
|
||||||
* @brief LLVolumeXml base class
|
* @brief LLVolumeXml base class
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -40,11 +34,11 @@
|
|||||||
class LLVolumeXml
|
class LLVolumeXml
|
||||||
{
|
{
|
||||||
public:
|
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
|
#endif // LL_LLVOLUMEXML_H
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llcubemap.cpp
|
* @file llcubemap.cpp
|
||||||
* @brief LLCubeMap class implementation
|
* @brief LLCubeMap class implementation
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
#include "linden_common.h"
|
#include "linden_common.h"
|
||||||
@@ -266,7 +260,7 @@ void LLCubeMap::setMatrix(S32 stage)
|
|||||||
|
|
||||||
if (mMatrixStage < 0) return;
|
if (mMatrixStage < 0) return;
|
||||||
|
|
||||||
if (stage > 0)
|
//if (stage > 0)
|
||||||
{
|
{
|
||||||
gGL.getTexUnit(stage)->activate();
|
gGL.getTexUnit(stage)->activate();
|
||||||
}
|
}
|
||||||
@@ -285,17 +279,17 @@ void LLCubeMap::setMatrix(S32 stage)
|
|||||||
gGL.loadMatrix((F32 *)trans.mMatrix);
|
gGL.loadMatrix((F32 *)trans.mMatrix);
|
||||||
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
||||||
|
|
||||||
if (stage > 0)
|
/*if (stage > 0)
|
||||||
{
|
{
|
||||||
gGL.getTexUnit(0)->activate();
|
gGL.getTexUnit(0)->activate();
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLCubeMap::restoreMatrix()
|
void LLCubeMap::restoreMatrix()
|
||||||
{
|
{
|
||||||
if (mMatrixStage < 0) return;
|
if (mMatrixStage < 0) return;
|
||||||
|
|
||||||
if (mMatrixStage > 0)
|
//if (mMatrixStage > 0)
|
||||||
{
|
{
|
||||||
gGL.getTexUnit(mMatrixStage)->activate();
|
gGL.getTexUnit(mMatrixStage)->activate();
|
||||||
}
|
}
|
||||||
@@ -303,10 +297,10 @@ void LLCubeMap::restoreMatrix()
|
|||||||
gGL.popMatrix();
|
gGL.popMatrix();
|
||||||
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
||||||
|
|
||||||
if (mMatrixStage > 0)
|
/*if (mMatrixStage > 0)
|
||||||
{
|
{
|
||||||
gGL.getTexUnit(0)->activate();
|
gGL.getTexUnit(0)->activate();
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLCubeMap::setReflection (void)
|
void LLCubeMap::setReflection (void)
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llcubemap.h
|
* @file llcubemap.h
|
||||||
* @brief LLCubeMap class definition
|
* @brief LLCubeMap class definition
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1141,7 +1141,7 @@ LLFontGL* LLFontGL::getFontSansSerifBig()
|
|||||||
//static
|
//static
|
||||||
LLFontGL* LLFontGL::getFontSansSerifHuge()
|
LLFontGL* LLFontGL::getFontSansSerifHuge()
|
||||||
{
|
{
|
||||||
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Large",0));
|
static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Huge",0));
|
||||||
return fontp;
|
return fontp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
#include "llmath.h"
|
#include "llmath.h"
|
||||||
#include "m4math.h"
|
#include "m4math.h"
|
||||||
#include "llstring.h"
|
#include "llstring.h"
|
||||||
|
#include "llmemtype.h"
|
||||||
#include "llstacktrace.h"
|
#include "llstacktrace.h"
|
||||||
|
|
||||||
#include "llglheaders.h"
|
#include "llglheaders.h"
|
||||||
@@ -2203,6 +2204,7 @@ void LLGLNamePool::release(GLuint name)
|
|||||||
//static
|
//static
|
||||||
void LLGLNamePool::upkeepPools()
|
void LLGLNamePool::upkeepPools()
|
||||||
{
|
{
|
||||||
|
LLMemType mt(LLMemType::MTYPE_UPKEEP_POOLS);
|
||||||
for (tracker_t::instance_iter iter = beginInstances(); iter != endInstances(); ++iter)
|
for (tracker_t::instance_iter iter = beginInstances(); iter != endInstances(); ++iter)
|
||||||
{
|
{
|
||||||
LLGLNamePool & pool = *iter;
|
LLGLNamePool & pool = *iter;
|
||||||
|
|||||||
@@ -151,8 +151,7 @@ BOOL LLGLSLShader::createShader(vector<string> * attributes,
|
|||||||
mProgramObject = glCreateProgramObjectARB();
|
mProgramObject = glCreateProgramObjectARB();
|
||||||
|
|
||||||
static const LLCachedControl<bool> no_texture_indexing("ShyotlUseLegacyTextureBatching",false);
|
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)
|
||||||
if (gGLManager.mGLVersion < 3.1f || no_texture_indexing /*|| use_legacy_path*/)
|
|
||||||
{ //force indexed texture channels to 1 if GL version is old (performance improvement for drivers with poor branching shader model support)
|
{ //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);
|
mFeatures.mIndexedTextureChannels = llmin(mFeatures.mIndexedTextureChannels, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,26 @@
|
|||||||
/**
|
/**
|
||||||
* @file llrender.cpp
|
* @file llrender.cpp
|
||||||
* @brief LLRender implementation
|
* @brief LLRender implementation
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -1009,7 +1003,7 @@ void LLLightState::setSpotDirection(const LLVector3& direction)
|
|||||||
const glh::matrix4f& mat = gGL.getModelviewMatrix();
|
const glh::matrix4f& mat = gGL.getModelviewMatrix();
|
||||||
mat.mult_matrix_dir(dir);
|
mat.mult_matrix_dir(dir);
|
||||||
|
|
||||||
mSpotDirection.set(direction);
|
mSpotDirection.set(dir.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1446,6 +1440,8 @@ void LLRender::loadIdentity()
|
|||||||
flush();
|
flush();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
llassert_always(mMatrixMode < NUM_MATRIX_MODES) ;
|
||||||
|
|
||||||
mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].make_identity();
|
mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].make_identity();
|
||||||
mMatHash[mMatrixMode]++;
|
mMatHash[mMatrixMode]++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,31 +7,25 @@
|
|||||||
* code, to define an interface for a multiple rendering API abstraction of the UI
|
* 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.
|
* rendering, and to abstract out direct rendering calls in a way that is cleaner and easier to maintain.
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/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("return texture2D(tex0, texcoord);\n");
|
||||||
text[count++] = strdup("}\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("\tswitch (int(vary_texture_index+0.25))\n");
|
||||||
text[count++] = strdup("\t{\n");
|
text[count++] = strdup("\t{\n");
|
||||||
@@ -1119,6 +1119,8 @@ void LLShaderMgr::initAttribsAndUniforms()
|
|||||||
mReservedUniforms.push_back("magnification");
|
mReservedUniforms.push_back("magnification");
|
||||||
mReservedUniforms.push_back("max_cof");
|
mReservedUniforms.push_back("max_cof");
|
||||||
mReservedUniforms.push_back("res_scale");
|
mReservedUniforms.push_back("res_scale");
|
||||||
|
mReservedUniforms.push_back("dof_width");
|
||||||
|
mReservedUniforms.push_back("dof_height");
|
||||||
|
|
||||||
mReservedUniforms.push_back("depthMap");
|
mReservedUniforms.push_back("depthMap");
|
||||||
mReservedUniforms.push_back("shadowMap0");
|
mReservedUniforms.push_back("shadowMap0");
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llshadermgr.h
|
* @file llshadermgr.h
|
||||||
* @brief Shader Manager
|
* @brief Shader Manager
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* 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
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -148,6 +142,8 @@ public:
|
|||||||
DOF_MAGNIFICATION,
|
DOF_MAGNIFICATION,
|
||||||
DOF_MAX_COF,
|
DOF_MAX_COF,
|
||||||
DOF_RES_SCALE,
|
DOF_RES_SCALE,
|
||||||
|
DOF_WIDTH,
|
||||||
|
DOF_HEIGHT,
|
||||||
|
|
||||||
DEFERRED_DEPTH,
|
DEFERRED_DEPTH,
|
||||||
DEFERRED_SHADOW0,
|
DEFERRED_SHADOW0,
|
||||||
|
|||||||
@@ -731,7 +731,7 @@ void LLVertexBuffer::unbind()
|
|||||||
//static
|
//static
|
||||||
void LLVertexBuffer::cleanupClass()
|
void LLVertexBuffer::cleanupClass()
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_CLEANUP_CLASS);
|
||||||
unbind();
|
unbind();
|
||||||
|
|
||||||
sStreamIBOPool.cleanup();
|
sStreamIBOPool.cleanup();
|
||||||
@@ -750,41 +750,25 @@ void LLVertexBuffer::cleanupClass()
|
|||||||
|
|
||||||
S32 LLVertexBuffer::determineUsage(S32 usage)
|
S32 LLVertexBuffer::determineUsage(S32 usage)
|
||||||
{
|
{
|
||||||
S32 ret_usage = usage;
|
if (LLRender::sGLCoreProfile)
|
||||||
|
|
||||||
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)
|
|
||||||
{ //MUST use VBOs for all rendering
|
{ //MUST use VBOs for all rendering
|
||||||
ret_usage = GL_STREAM_DRAW_ARB;
|
if(!usage)
|
||||||
|
return GL_STREAM_DRAW_ARB;
|
||||||
}
|
}
|
||||||
|
else if (!sEnableVBOs || !usage || (!sUseStreamDraw && usage == 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
|
return 0;
|
||||||
if (sDisableVBOMapping)
|
}
|
||||||
{ //always use stream draw if VBO mapping is disabled
|
//Only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default.
|
||||||
ret_usage = GL_STREAM_DRAW_ARB;
|
//Always use stream_draw VBO if mapping is disabled, or stream is preferred or expected
|
||||||
}
|
if( sDisableVBOMapping || sPreferStreamDraw || (usage == GL_STREAM_DRAW_ARB))
|
||||||
else
|
{
|
||||||
{
|
return GL_STREAM_DRAW_ARB;
|
||||||
ret_usage = GL_DYNAMIC_DRAW_ARB;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
return GL_DYNAMIC_DRAW_ARB;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret_usage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
|
LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
|
||||||
@@ -812,16 +796,9 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
|
|||||||
mMappable(false),
|
mMappable(false),
|
||||||
mFence(NULL)
|
mFence(NULL)
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_CONSTRUCTOR);
|
||||||
|
|
||||||
if (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping)
|
mMappable = (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping);
|
||||||
{
|
|
||||||
mMappable = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mMappable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//zero out offsets
|
//zero out offsets
|
||||||
for (U32 i = 0; i < TYPE_MAX; i++)
|
for (U32 i = 0; i < TYPE_MAX; i++)
|
||||||
@@ -880,7 +857,7 @@ S32 LLVertexBuffer::getSize() const
|
|||||||
//virtual
|
//virtual
|
||||||
LLVertexBuffer::~LLVertexBuffer()
|
LLVertexBuffer::~LLVertexBuffer()
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTRUCTOR);
|
||||||
destroyGLBuffer();
|
destroyGLBuffer();
|
||||||
destroyGLIndices();
|
destroyGLIndices();
|
||||||
|
|
||||||
@@ -997,7 +974,7 @@ void LLVertexBuffer::releaseIndices()
|
|||||||
|
|
||||||
void LLVertexBuffer::createGLBuffer(U32 size)
|
void LLVertexBuffer::createGLBuffer(U32 size)
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_VERTICES);
|
||||||
|
|
||||||
if (mGLBuffer)
|
if (mGLBuffer)
|
||||||
{
|
{
|
||||||
@@ -1028,7 +1005,7 @@ void LLVertexBuffer::createGLBuffer(U32 size)
|
|||||||
|
|
||||||
void LLVertexBuffer::createGLIndices(U32 size)
|
void LLVertexBuffer::createGLIndices(U32 size)
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_CREATE_INDICES);
|
||||||
|
|
||||||
if (mGLIndices)
|
if (mGLIndices)
|
||||||
{
|
{
|
||||||
@@ -1064,7 +1041,7 @@ void LLVertexBuffer::createGLIndices(U32 size)
|
|||||||
|
|
||||||
void LLVertexBuffer::destroyGLBuffer()
|
void LLVertexBuffer::destroyGLBuffer()
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_BUFFER);
|
||||||
if (mGLBuffer)
|
if (mGLBuffer)
|
||||||
{
|
{
|
||||||
if (mMappedDataUsingVBOs)
|
if (mMappedDataUsingVBOs)
|
||||||
@@ -1085,7 +1062,7 @@ void LLVertexBuffer::destroyGLBuffer()
|
|||||||
|
|
||||||
void LLVertexBuffer::destroyGLIndices()
|
void LLVertexBuffer::destroyGLIndices()
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_DESTROY_INDICES);
|
||||||
if (mGLIndices)
|
if (mGLIndices)
|
||||||
{
|
{
|
||||||
if (mMappedIndexDataUsingVBOs)
|
if (mMappedIndexDataUsingVBOs)
|
||||||
@@ -1106,7 +1083,7 @@ void LLVertexBuffer::destroyGLIndices()
|
|||||||
|
|
||||||
void LLVertexBuffer::updateNumVerts(S32 nverts)
|
void LLVertexBuffer::updateNumVerts(S32 nverts)
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_VERTS);
|
||||||
|
|
||||||
llassert(nverts >= 0);
|
llassert(nverts >= 0);
|
||||||
|
|
||||||
@@ -1128,7 +1105,7 @@ void LLVertexBuffer::updateNumVerts(S32 nverts)
|
|||||||
|
|
||||||
void LLVertexBuffer::updateNumIndices(S32 nindices)
|
void LLVertexBuffer::updateNumIndices(S32 nindices)
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_UPDATE_INDICES);
|
||||||
|
|
||||||
llassert(nindices >= 0);
|
llassert(nindices >= 0);
|
||||||
|
|
||||||
@@ -1144,7 +1121,7 @@ void LLVertexBuffer::updateNumIndices(S32 nindices)
|
|||||||
|
|
||||||
void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
|
void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER);
|
||||||
|
|
||||||
stop_glerror();
|
stop_glerror();
|
||||||
|
|
||||||
@@ -1265,7 +1242,7 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
|
|||||||
llassert(newnverts >= 0);
|
llassert(newnverts >= 0);
|
||||||
llassert(newnindices >= 0);
|
llassert(newnindices >= 0);
|
||||||
|
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_RESIZE_BUFFER);
|
||||||
|
|
||||||
updateNumVerts(newnverts);
|
updateNumVerts(newnverts);
|
||||||
updateNumIndices(newnindices);
|
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)
|
volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
|
||||||
{
|
{
|
||||||
bindGLBuffer(true);
|
bindGLBuffer(true);
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
|
||||||
if (mFinal)
|
if (mFinal)
|
||||||
{
|
{
|
||||||
llerrs << "LLVertexBuffer::mapVeretxBuffer() called on a finalized buffer." << llendl;
|
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)
|
if (!mVertexLocked)
|
||||||
{
|
{
|
||||||
|
LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES);
|
||||||
mVertexLocked = true;
|
mVertexLocked = true;
|
||||||
sMappedCount++;
|
sMappedCount++;
|
||||||
stop_glerror();
|
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)
|
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);
|
bindGLIndices(true);
|
||||||
if (mFinal)
|
if (mFinal)
|
||||||
{
|
{
|
||||||
@@ -1539,7 +1517,7 @@ volatile U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range
|
|||||||
|
|
||||||
if (!mIndexLocked)
|
if (!mIndexLocked)
|
||||||
{
|
{
|
||||||
//LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES);
|
LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES);
|
||||||
|
|
||||||
mIndexLocked = true;
|
mIndexLocked = true;
|
||||||
sMappedCount++;
|
sMappedCount++;
|
||||||
@@ -1663,7 +1641,7 @@ static LLFastTimer::DeclareTimer FTM_IBO_FLUSH_RANGE("Flush IBO Range");
|
|||||||
|
|
||||||
void LLVertexBuffer::unmapBuffer()
|
void LLVertexBuffer::unmapBuffer()
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER);
|
||||||
if (!useVBOs())
|
if (!useVBOs())
|
||||||
{
|
{
|
||||||
return ; //nothing to unmap
|
return ; //nothing to unmap
|
||||||
@@ -2007,7 +1985,7 @@ void LLVertexBuffer::setBuffer(U32 data_mask)
|
|||||||
{
|
{
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_BUFFER);
|
||||||
//set up pointers if the data mask is different ...
|
//set up pointers if the data mask is different ...
|
||||||
bool setup = (sLastMask != data_mask);
|
bool setup = (sLastMask != data_mask);
|
||||||
|
|
||||||
@@ -2149,7 +2127,7 @@ void LLVertexBuffer::setBuffer(U32 data_mask)
|
|||||||
// virtual (default)
|
// virtual (default)
|
||||||
void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
|
void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
|
||||||
{
|
{
|
||||||
LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
|
LLMemType mt2(LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER);
|
||||||
stop_glerror();
|
stop_glerror();
|
||||||
volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
|
volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
#include "v4math.h"
|
#include "v4math.h"
|
||||||
#include "v4coloru.h"
|
#include "v4coloru.h"
|
||||||
#include "llstrider.h"
|
#include "llstrider.h"
|
||||||
#include "llmemory.h"
|
|
||||||
#include "llrender.h"
|
#include "llrender.h"
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|||||||
@@ -611,7 +611,6 @@ void LLButton::draw()
|
|||||||
S32 text_left = mLeftHPad;
|
S32 text_left = mLeftHPad;
|
||||||
S32 text_right = getRect().getWidth() - mRightHPad;
|
S32 text_right = getRect().getWidth() - mRightHPad;
|
||||||
S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
|
S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
|
||||||
S32 text_middle = text_left + text_width/2;
|
|
||||||
|
|
||||||
// draw overlay image
|
// draw overlay image
|
||||||
if (mImageOverlay.notNull() && mImageOverlay->getWidth() > 1)
|
if (mImageOverlay.notNull() && mImageOverlay->getWidth() > 1)
|
||||||
@@ -646,7 +645,6 @@ void LLButton::draw()
|
|||||||
case LLFontGL::LEFT:
|
case LLFontGL::LEFT:
|
||||||
text_left += overlay_width + 1;
|
text_left += overlay_width + 1;
|
||||||
text_width -= overlay_width + 1;
|
text_width -= overlay_width + 1;
|
||||||
text_middle += (overlay_width+1)/4;
|
|
||||||
mImageOverlay->draw(
|
mImageOverlay->draw(
|
||||||
mLeftHPad,
|
mLeftHPad,
|
||||||
center_y - (overlay_height / 2),
|
center_y - (overlay_height / 2),
|
||||||
@@ -665,7 +663,6 @@ void LLButton::draw()
|
|||||||
case LLFontGL::RIGHT:
|
case LLFontGL::RIGHT:
|
||||||
text_right -= overlay_width + 1;
|
text_right -= overlay_width + 1;
|
||||||
text_width -= overlay_width + 1;
|
text_width -= overlay_width + 1;
|
||||||
text_middle += (overlay_width+1)/4;
|
|
||||||
mImageOverlay->draw(
|
mImageOverlay->draw(
|
||||||
getRect().getWidth() - mRightHPad - overlay_width,
|
getRect().getWidth() - mRightHPad - overlay_width,
|
||||||
center_y - (overlay_height / 2),
|
center_y - (overlay_height / 2),
|
||||||
@@ -691,10 +688,7 @@ void LLButton::draw()
|
|||||||
x = text_right;
|
x = text_right;
|
||||||
break;
|
break;
|
||||||
case LLFontGL::HCENTER:
|
case LLFontGL::HCENTER:
|
||||||
{
|
x = text_left + (text_width / 2);
|
||||||
S32 actual_width = mGLFont->getWidth(label.c_str());
|
|
||||||
x = llmax(text_middle, text_left + actual_width/2);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case LLFontGL::LEFT:
|
case LLFontGL::LEFT:
|
||||||
default:
|
default:
|
||||||
@@ -710,7 +704,6 @@ void LLButton::draw()
|
|||||||
x++;
|
x++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mGLFont->render(label, 0,
|
mGLFont->render(label, 0,
|
||||||
(F32)x,
|
(F32)x,
|
||||||
(F32)(LLBUTTON_V_PAD + y_offset),
|
(F32)(LLBUTTON_V_PAD + y_offset),
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
#include "llevent.h"
|
#include "llevent.h"
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class LLMemberListener : public LLSimpleListener
|
class LLMemberListener : public LLOldEvents::LLSimpleListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LLMemberListener() : mPtr(NULL), mRegisteredName() { }
|
LLMemberListener() : mPtr(NULL), mRegisteredName() { }
|
||||||
@@ -75,7 +75,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is what you have to override to handle this event
|
// 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:
|
protected:
|
||||||
T *mPtr; // The object that this listener manipulates
|
T *mPtr; // The object that this listener manipulates
|
||||||
|
|||||||
@@ -66,6 +66,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <boost/tokenizer.hpp>
|
#include <boost/tokenizer.hpp>
|
||||||
|
|
||||||
|
using namespace LLOldEvents;
|
||||||
|
|
||||||
// static
|
// static
|
||||||
LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
|
LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user