diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 627d5951a6..92cbb73c55 --- a/.gitignore +++ b/.gitignore @@ -14,14 +14,17 @@ LICENSES build-darwin-* build-linux-* -build-stamp -build-vc120* -build-vc150* -configure-stamp debian/files debian/secondlife-appearance-utility* debian/secondlife-viewer* indra/.distcc +build-vc80/ +build-vc100/ +build-vc120/ +build-vc120-32/ +build-vc120-64/ +build-vc150-32/ +build-vc150-64/ indra/CMakeFiles indra/build-vc[0-9]* indra/lib/mono/1.0/*.dll @@ -68,15 +71,33 @@ indra/web/doc/asset-upload/plugins/verify-notecard indra/web/doc/asset-upload/plugins/verify-texture installed.xml libraries +logs tarfile_tmp -web/config.* -web/locale.* -web/secondlife.com.* +debian/secondlife-viewer* +debian/secondlife-appearance-utility* +debian/files +build-stamp +configure-stamp +indra/newview/dbghelp.dll +*Thumbs.db +*.cpp.orig +*.cpp.bak +*.h.bak +*.h.orig +indra/newview/typed_locations.txt +indra/newview/teleport_history.txt +indra/newview/search_history.txt +indra/newview/filters.xml +indra/newview/avatar_icons_cache.txt +indra/newview/avatar_lad.log +*.diff +indra/newview/pilot.txt +indra/newview/pilot.xml +#*.rej +.*\.wixobj # Firestorm additions indra/newview/exoflickrkeys.h indra/newview/fsdiscordkey.h my_autobuild.xml -.vscode -*Thumbs.db -logs +.vscode \ No newline at end of file diff --git a/autobuild.xml b/autobuild.xml index ba3acd455e..7c2fabb1bc 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -61,36 +61,6 @@ version 5.1.0 - dullahan-gcc5 - - copyright - Copyright (c) 2017, Linden Research, Inc. - description - A headless browser SDK that uses the Chromium Embedded Framework (CEF). It is designed to make it easier to write applications that render modern web content directly to a memory buffer, inject synthesized mouse and keyboard events as well as interact with web based features like JavaScript or cookies. - license - MPL - license_file - LICENSES/LICENSE.txt - name - dullahan-gcc5 - platforms - - linux64 - - archive - - hash - 7725e9abe4bf84d11bc7286c74467026 - url - http://downloads.phoenixviewer.com/dullahan_gcc5-1.7.0.202005301906_81.3.10_gb223419_chromium-81.0.4044.138-linux64-201511706.tar.bz2 - - name - linux64 - - - version - 1.1.1080_3.3325.1750.gaabe4c4 - ndPhysicsStub license @@ -142,9 +112,9 @@ archive hash - 7ffe5a6165e6808e0674754b56545034 + 7649dd9c2075b0cbda5b80b4edb9b90e url - http://downloads.phoenixviewer.com/ndPhysicsStub-1.0-windows-201505061845-r74.tar.bz2 + http://downloads.phoenixviewer.com/ndPhysicsStub-1.0-windows-vs2017-1906061512.tar.bz2 name windows @@ -154,9 +124,9 @@ archive hash - a2baf97f950cc16af9fe5cf316b7fa4d + 9e2a24005a4751c3dc4655ac077e35c0 url - http://downloads.phoenixviewer.com/ndPhysicsStub-1.201412081952-windows-x64-201412081952.tar.bz2 + http://downloads.phoenixviewer.com/ndPhysicsStub-1.0-windows64-vs2017-1906061512.tar.bz2 name windows64 @@ -204,9 +174,9 @@ archive hash - 315e6d3917b1b1327033eb41f868e828 + 29a573c4d27d617345030cf28860ea2e url - http://downloads.phoenixviewer.com/gntp_growl-1.0-windows-201601022248-r16.tar.bz2 + http://downloads.phoenixviewer.com/gntp_growl-1.0-windows-vs2017-1906061512.tar.bz2 name windows @@ -216,9 +186,9 @@ archive hash - 5b1d8e9d3f4fd4fcc6b090d2e9bfd23d + a333b335104b3d0df14b9be005ef1571 url - http://downloads.phoenixviewer.com/gntp_growl-1.0-windows-x64-201601022251-r16.tar.bz2 + http://downloads.phoenixviewer.com/gntp_growl-1.0-windows64-vs2017-1906061512.tar.bz2 name windows64 @@ -244,13 +214,25 @@ archive hash - b760b4f3ab7794a897cf7f464d92e587 + c97507d081f25261ec14238ea80f5163 url - http://3p.firestormviewer.org/discord_rpc-3.4.0-windows-192510505.tar.bz2 + http://3p.firestormviewer.org/discord_rpc-3.4.0-windows-192581529.tar.bz2 name windows + windows64 + + archive + + hash + f63f07d4c87fbbb698b9039583f08b76 + url + http://3p.firestormviewer.org/discord_rpc-3.4.0-windows64-192581529.tar.bz2 + + name + windows64 + linux64 archive @@ -386,9 +368,9 @@ archive hash - dc80eca3d113b038b469003da8cd6638 + 83b4a047db5f7ee462753d91e6277cba url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4813/15290/apr_suite-1.4.5.504800-windows-504800.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55143/512317/apr_suite-1.4.5.539073-windows-539073.tar.bz2 name windows @@ -398,16 +380,16 @@ archive hash - 63146d3d3d5fe7aa6be1a1b0afed36dd + b3bbf168b39e25c08cc1febddeb33332 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4814/15296/apr_suite-1.4.5.504800-windows64-504800.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55139/512304/apr_suite-1.4.5.539073-windows64-539073.tar.bz2 name windows64 version - 1.4.5.504800 + 1.4.5.539073 boost @@ -440,33 +422,21 @@ archive hash - d318c25353e41215f1f523d58cacfd44 + c68630bd937509573df87a41452bc464 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/893/1984/boost-1.57-darwin64-500883.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56315/526789/boost-1.72-darwin64-539869.tar.bz2 name darwin64 - linux - - archive - - hash - 32828f4ee3e5a45ed31e37eb7dd6367c - url - http://downloads.phoenixviewer.com/boost-1.57-linux-180871354.tar.bz2 - - name - linux - linux64 archive hash - b1d3daab689c5a8c8869715c5111d38e + 29eb0d338020242454b77b278c11d3e7 url - http://downloads.phoenixviewer.com/boost-1.57-linux64-180841628.tar.bz2 + http://3p.firestormviewer.org/boost-1.72-linux64-202021511.tar.bz2 name linux64 @@ -476,9 +446,9 @@ archive hash - 80b1963d635e883cb5ed223e94406adb + 097d04c5b064c4be4bc9edb885509a94 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/894/1976/boost-1.57-windows-500883.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56321/526797/boost-1.72-windows-539869.tar.bz2 name windows @@ -488,16 +458,16 @@ archive hash - 3d6a6373ed0daa490cdb4f92db45de52 + 748c4d47cced7ba2b210eb6d0ed33497 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/895/1979/boost-1.57-windows64-500883.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56320/526777/boost-1.72-windows64-539869.tar.bz2 name windows64 version - 1.57 + 1.72 bugsplat @@ -518,9 +488,9 @@ archive hash - d453d6200607972493e1797a30fc805f + 471b0b350955152fd87518575057dfc4 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45455/401027/bugsplat-1.0.7.532004-darwin64-532004.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60326/566593/bugsplat-1.0.7.542667-darwin64-542667.tar.bz2 name darwin64 @@ -530,9 +500,9 @@ archive hash - 372e1d677ee570f947183bae81ca0852 + 70e8bf46145c4cbae6f93e8b70ba5499 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45457/401043/bugsplat-3.6.0.4.532004-windows-532004.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60320/566541/bugsplat-3.6.0.4.542667-windows-542667.tar.bz2 name windows @@ -542,16 +512,16 @@ archive hash - 261d460a67e3e2f52bdad1391bb02ec3 + a73696e859fad3f19f835740815a2bd3 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45456/401036/bugsplat-.532004-windows64-532004.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60321/566542/bugsplat-3.6.0.4.542667-windows64-542667.tar.bz2 name windows64 version - 1.0.7.520791 + 1.0.7.542667 colladadom @@ -570,7 +540,7 @@ archive hash - 66849777a83cb69cec3c06b07da7cd3d + 726bc31e562752f081e95e8fcc70e405 url http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/297450/arch/Darwin/installer/colladadom-2.3.297450-darwin-297450.tar.bz2 @@ -582,9 +552,9 @@ archive hash - fa93a9a10fa379091e3e7b85665690d9 + 76e70d1f024e089bcd1afa6748d67a62 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/913/2026/colladadom-2.3.500902-darwin64-500902.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56409/527191/colladadom-2.3.539922-darwin64-539922.tar.bz2 name darwin64 @@ -606,9 +576,9 @@ archive hash - 91a522a7bf0e8d66d2941c76dc28a261 + df6fe4c80b96ec20b5ee6f56419e6b9f url - http://downloads.phoenixviewer.com/colladadom-2.3.180841822-linux64-180841822.tar.bz2 + http://3p.firestormviewer.org/colladadom-2.3.202021526-linux64-202021526.tar.bz2 name linux64 @@ -618,9 +588,9 @@ archive hash - 5bd7875e16e7f88e21f4c44fe7c6433f + 3d6ab0e5e08a7f03088232e5676a861e url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/915/2035/colladadom-2.3.500902-windows-500902.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56415/527297/colladadom-2.3.539922-windows-539922.tar.bz2 name windows @@ -630,16 +600,16 @@ archive hash - 8a647129a0a0a31594557785ea85f840 + 5a31c4d50a04d255e84903f16597d4ed url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/914/2034/colladadom-2.3.500902-windows64-500902.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56408/527200/colladadom-2.3.539922-windows64-539922.tar.bz2 name windows64 version - 2.3.500902 + 2.3.539922 curl @@ -672,9 +642,9 @@ archive hash - f426c56252c70fe38fcb2251f7c1d762 + decf3d5bd930e9ac6113cf96c61ff230 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9265/41615/curl-7.54.1.509254-darwin64-509254.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56342/526921/curl-7.54.1.539883-darwin64-539883.tar.bz2 name darwin64 @@ -708,11 +678,11 @@ archive hash - 4c7a960e1ee518acceac6a0c65495800 + ebd24261499e458da253d2bc1d95057a hash_algorithm md5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9268/41606/curl-7.54.1.509254-windows-509254.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56361/526996/curl-7.54.1.539883-windows-539883.tar.bz2 name windows @@ -722,16 +692,16 @@ archive hash - 32df7cce1658ccec893fb46cd476c024 + 9eadfc1885c59ebc750f75adf4c20925 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9267/41607/curl-7.54.1.509254-windows64-509254.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56360/526989/curl-7.54.1.539883-windows64-539883.tar.bz2 name windows64 version - 7.54.1.509254 + 7.54.1.539883 db @@ -824,16 +794,16 @@ archive hash - 2d59bb6f4bd38a18d9250ff010a13b59 + d778c6a3475bc35ee8b9615dfc38b4a9 url - http://downloads.phoenixviewer.com/dictionaries-2.46203-common-46203.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55025/511964/dictionaries-1.538984-common-538984.tar.bz2 name common version - 1.500564 + 1.538984 dullahan @@ -854,33 +824,21 @@ archive hash - 23aeaf23e7db2484a1850017141860dd + e145f8ea99a21712434e0e868d1885dc url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/34069/283470/dullahan-1.1.1320_3.3626.1895.g7001d56-darwin64-525361.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/62333/588183/dullahan-1.7.0.202006240858_81.3.10_gb223419_chromium-81.0.4044.138-darwin64-544091.tar.bz2 name darwin64 - linux64 - - archive - - hash - 05bdafd8e9f32fc6763862ada4b5c4bc - url - http://downloads.phoenixviewer.com/dullahan-1.7.0.202005300110_81.3.10_gb223419_chromium-81.0.4044.138-linux64-201502309.tar.bz2 - - name - linux64 - windows archive hash - 43c03679c3847754b407532efe5d4392 + 7253c6787a9aa04b160f450a47bc8d93 url - http://downloads.phoenixviewer.com/dullahan-1.7.0.202005292352_81.3.10_gb223419_chromium-81.0.4044.138-windows-201512310.tar.bz2 + http://3p.firestormviewer.org/dullahan-1.8.0.202007201344_81.3.10_gb223419_chromium-81.0.4044.138-windows-202021141.tar.bz2 name windows @@ -890,16 +848,28 @@ archive hash - 13574736220c05de847980f851f42827 + 0b24ebef4ea891e5fbf94445acac42bf url - http://downloads.phoenixviewer.com/dullahan-1.7.0.202005292352_81.3.10_gb223419_chromium-81.0.4044.138-windows64-201512301.tar.bz2 + http://3p.firestormviewer.org/dullahan-1.8.0.202007201344_81.3.10_gb223419_chromium-81.0.4044.138-windows64-202021146.tar.bz2 name windows64 + linux64 + + archive + + hash + 79e5ef408ea7ee43fef0f738688485c6 + url + http://downloads.phoenixviewer.com/dullahan-1.8.0.202007201635_81.3.10_gb223419_chromium-81.0.4044.138-linux64-202021435.tar.bz2 + + name + linux64 + version - 1.1.1320_3.3626.1895.g7001d56 + 1.7.0.202006240858_81.3.10_gb223419_chromium-81.0.4044.138 elfio @@ -956,9 +926,9 @@ archive hash - fd182ab5bed66c94899dec3035310945 + 3656b7f7b655cb267fd94f089d2e145c url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/384/954/expat-2.1.1.500375-darwin64-500375.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54860/510198/expat-2.1.1.538990-darwin64-538990.tar.bz2 name darwin64 @@ -992,9 +962,9 @@ archive hash - 09ece3f04ec0bd21dd0d401235aa20f7 + c509f8afa1e02f4c16232cce7f6855f8 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/383/949/expat-2.1.1.500375-windows-500375.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55056/512080/expat-2.1.1.538990-windows-538990.tar.bz2 name windows @@ -1004,16 +974,16 @@ archive hash - 5c82a3482799fe22b3c8fcb317f87bbb + aba97cfdf44c04dbfcac89c7cb472580 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/382/946/expat-2.1.1.500375-windows64-500375.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55054/512068/expat-2.1.1.538990-windows64-538990.tar.bz2 name windows64 version - 2.1.1.500375 + 2.1.1.538990 fmodstudio @@ -1034,11 +1004,11 @@ archive hash - b5b6897fb6f5413f3396b0666a6422ab + 371025cc40f8cbb70f0bb88f79a906a6 hash_algorithm md5 url - file:///opt/firestorm/fmodstudio-2.00.08-darwin-201031332.tar.bz2 + file:///opt/firestorm/fmodstudio-2.01.02-darwin-201981618.tar.bz2 name darwin @@ -1048,11 +1018,11 @@ archive hash - 88b1ac40d0b2cc1cf862260bb1297a54 + 65f37bfb75cb13161c99a450be13fb96 hash_algorithm md5 url - file:///opt/firestorm/fmodstudio-2.01.01-linux64-201412258.tar.bz2 + file:///opt/firestorm/fmodstudio-2.01.02-linux64-201992052.tar.bz2 name linux64 @@ -1062,11 +1032,11 @@ archive hash - 5c182fe2303511c35480d00d9b6832dc + 469f3e3a177c089deacad40428d23997 hash_algorithm md5 url - file:///c:/cygwin/opt/firestorm/fmodstudio-2.01.01-windows-201411851.tar.bz2 + file:///c:/cygwin/opt/firestorm/fmodstudio-2.01.02-windows-201991756.tar.bz2 name windows @@ -1076,18 +1046,18 @@ archive hash - 10fcd058ec0da77273c61aca78081ede + 59766e450e7395fe996ae17c8c267c76 hash_algorithm md5 url - file:///c:/cygwin/opt/firestorm/fmodstudio-2.01.01-windows64-201412005.tar.bz2 + file:///c:/cygwin/opt/firestorm/fmodstudio-2.01.02-windows64-201991757.tar.bz2 name windows64 version - 2.01.01 + 2.01.02 fontconfig @@ -1162,9 +1132,9 @@ archive hash - 3f0698d53acf14b3f0a11dba889d67f3 + 81a2e9aca3e33c4eecf0081854540b07 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/875/1919/freetype-2.4.4.500865-darwin64-500865.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56309/526711/freetype-2.4.4.539865-darwin64-539865.tar.bz2 name darwin64 @@ -1198,9 +1168,9 @@ archive hash - 46ac6421e08c5f38cddea8b81b511f64 + 0dbb1a9d15bb134096476d657f86ce41 url - http://downloads.phoenixviewer.com/freetype-2.3.9.172082219-windows-172082219.tar.bz2 + http://downloads.phoenixviewer.com/freetype-2.3.9.202022048-windows-202022048.tar.bz2 name windows @@ -1210,16 +1180,16 @@ archive hash - 3bc8a33be7e4b4a5a1a6abf629cf0532 + c000d0c98bded51c7bc7447dc7934e6c url - http://downloads.phoenixviewer.com/freetype-2.3.9.172082305-windows64-172082305.tar.bz2 + http://downloads.phoenixviewer.com/freetype-2.3.9.202022049-windows64-202022049.tar.bz2 name windows64 version - 2.4.4.500865 + 2.4.4.539865 glext @@ -1235,6 +1205,18 @@ glext platforms + darwin64 + + archive + + hash + 1bd3214ac23474ea4c869e386970a1be + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54835/510029/glext-68-darwin64-538965.tar.bz2 + + name + darwin64 + linux archive @@ -1264,9 +1246,9 @@ archive hash - 731d4adecfcbd9f7d20c4bbd2c183962 + 6a311615bce59b01cf73ee65012a9b38 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-glext/rev/314200/arch/CYGWIN/installer/glext-68-windows-314200.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54951/511711/glext-68-windows-538965.tar.bz2 name windows @@ -1276,9 +1258,9 @@ archive hash - 9635e7e6fded468dfc0874a2ead54123 + daf619dab1cf7518af6532b18800c4b0 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-glext/rev/314200/arch/CYGWIN/installer/glext-68-windows64-314200.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54924/511490/glext-68-windows64-538965.tar.bz2 name windows64 @@ -1306,9 +1288,9 @@ archive hash - fa41756977ad8b9fd2d1465dadd4f956 + 650e836255b6c2ecb93d3f1f7220051c url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/529/1139/glh_linear-0.0.0-common-500522.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55011/511905/glh_linear-0.0.0-common-538981.tar.bz2 name common @@ -1349,8 +1331,6 @@ hash 94fc457c46e1fb94b31251bd4747d10f - hash_algorithm - md5 url http://downloads.phoenixviewer.com/glod-1.0pre3.171101143-darwin64-171101143.tar.bz2 @@ -1388,11 +1368,11 @@ archive hash - e3659aa7b670a8fbdfb1b820254abb44 + e1f8da12a2b7a6c31830b4bb86d31ed6 hash_algorithm md5 url - http://downloads.phoenixviewer.com/glod-1.0pre3.201703081158-windows-201703081158.tar.bz2 + http://downloads.phoenixviewer.com/glod-1.0pre3.vs2017-1906061512-windows-vs2017-1906061512.tar.bz2 name windows @@ -1402,16 +1382,16 @@ archive hash - 7eceae7977650ebc6124a9dfee3c4d84 + e906cf08bfbfbd9d4fc78557e021e7d0 url - http://downloads.phoenixviewer.com/glod-1.0pre3.201703081200-windows64-201703081200.tar.bz2 + http://downloads.phoenixviewer.com/glod-1.0pre3.vs2017-1906061512-windows64-vs2017-1906061512.tar.bz2 name windows64 version - 1.0pre3.501614 + 1.0pre3.532346 google_breakpad @@ -1444,9 +1424,9 @@ archive hash - 2d43c6a149cd9c89ba19e884579b1e25 + ca33f234aae399b9e704e262f7e15d35 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1836/4096/google_breakpad-1413.501824-darwin64-501824.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56338/526869/google_breakpad-1413.539880-darwin64-539880.tar.bz2 name darwin64 @@ -1456,9 +1436,9 @@ archive hash - 95860763e872e984e4215b0fa22d40b8 + ca43974302dc3e2019b0838c2c53b8b5 url - http://downloads.phoenixviewer.com/google_breakpad-1413.180871249-linux-180871249.tar.bz2 + http://downloads.phoenixviewer.com/google_breakpad-1413.202021450-linux64-202021450.tar.bz2 name linux @@ -1468,9 +1448,9 @@ archive hash - aa4a74b0c5794034797f22d4bdc57a0d + ae2b368225d5705233284e08b9981a35 url - http://downloads.phoenixviewer.com/google_breakpad-1413.180841553-linux64-180841553.tar.bz2 + http://3p.firestormviewer.org/google_breakpad-1413.202022149-linux64-202022149.tar.bz2 name linux64 @@ -1480,9 +1460,9 @@ archive hash - 6a7929c7280a5c9b528fdd334da5c2d1 + bfee0438617f57f02f7e8515a801cb20 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1838/4108/google_breakpad-1413.501824-windows-501824.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56359/526982/google_breakpad-1413.539880-windows-539880.tar.bz2 name windows @@ -1492,16 +1472,16 @@ archive hash - 4fb761717f3ce6ccabdaeb009272b7ca + 6f983e754bb3046f065806b510b408c5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1837/4103/google_breakpad-1413.501824-windows64-501824.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56358/526975/google_breakpad-1413.539880-windows64-539880.tar.bz2 name windows64 version - 1413.501824 + 1413.539880 googlemock @@ -1534,9 +1514,9 @@ archive hash - 1a8081953bdf1bbbc9b8a8e6e062c02d + 36e2e30610eb131e3522ef84cc67405d url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/919/2048/googlemock-1.7.0.500908-darwin64-500908.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56330/526832/googlemock-1.7.0.539876-darwin64-539876.tar.bz2 name darwin64 @@ -1558,9 +1538,9 @@ archive hash - f879f518ea207cecf13943d58b57b515 + ff459b58695c76838782847a0b792104 url - http://downloads.phoenixviewer.com/googlemock-1.7.0.180841921-linux64-180841921.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9697/45717/googlemock-1.7.0.509686-linux64-509686.tar.bz2 name linux64 @@ -1570,9 +1550,9 @@ archive hash - d01c9b12be6c5bb0749441495d45cba3 + 38a2c655876044efe536a8e685e74a2a url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/920/2051/googlemock-1.7.0.500908-windows-500908.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56336/526861/googlemock-1.7.0.539876-windows-539876.tar.bz2 name windows @@ -1582,16 +1562,16 @@ archive hash - e508a2ac7900853cc551666d0cf06541 + ff4fa1fd7a1ed9ffa477c4574ffc16af url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/921/2059/googlemock-1.7.0.500908-windows64-500908.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56334/526845/googlemock-1.7.0.539876-windows64-539876.tar.bz2 name windows64 version - 1.7.0.500908 + 1.7.0.539876 gstreamer @@ -1704,9 +1684,9 @@ archive hash - a0c4405c9e44d4a0135fe20ba8cfbace + ba229348c1d9d58519cd854ff9d8ef3d url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4693/14627/havok_source-2012.1-2-darwin64-504680.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55213/512968/havok_source-2012.1-2-darwin64-539117.tar.bz2 name darwin64 @@ -1740,9 +1720,9 @@ archive hash - 035572a1929be66f6c56468e0ef7fe74 + 4ff2af85106907acb171bb1e38a3757e url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4695/14637/havok_source-2012.1-2-windows-504680.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55214/512993/havok_source-2012.1-2-windows-539117.tar.bz2 name windows @@ -1752,9 +1732,9 @@ archive hash - d8525d2fbb9e0f7bc31427b47350e468 + bcaf4631ea10f7d09eecb73e8f5bef6c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4694/14634/havok_source-2012.1-2-windows64-504680.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55212/512962/havok_source-2012.1-2-windows64-539117.tar.bz2 name windows64 @@ -1794,9 +1774,9 @@ archive hash - 4e7fef9c6ae9b7ccf19b7fdb96912b9c + 3f2e34e3a2dac8eea957cad143a71dc5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/3152/7571/jpeglib-8c.503140-darwin64-503140.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54847/510113/jpeglib-8c.538977-darwin64-538977.tar.bz2 name darwin64 @@ -1830,9 +1810,9 @@ archive hash - 00523662f6a7388377166e9415e113e9 + c8dee00ef13af40ec68becc25830e195 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/3153/7557/jpeglib-8c.503140-windows-503140.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54992/511854/jpeglib-8c.538977-windows-538977.tar.bz2 name windows @@ -1842,16 +1822,16 @@ archive hash - 70ed49ed2317b6dba9af1f186956ac79 + 6f40620e86f3c9b91b6b5fe3c81776fc url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/3154/7558/jpeglib-8c.503140-windows64-503140.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54991/511847/jpeglib-8c.538977-windows64-538977.tar.bz2 name windows64 version - 8c.503140 + 8c.538977 jsoncpp @@ -1884,9 +1864,9 @@ archive hash - 3564da2ab285a8652d2ee157d1f167e2 + 87d32aaac4183590c96edd0b6d9bf3e4 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1478/3283/jsoncpp-0.5.0.501464-darwin64-501464.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54846/510106/jsoncpp-0.5.0.538976-darwin64-538976.tar.bz2 name darwin64 @@ -1908,9 +1888,9 @@ archive hash - 275d45727849245f2ed147d60b905413 + a29426d6e0c1e8093e579b90574101a3 url - http://downloads.phoenixviewer.com/jsoncpp-0.5.0.181041824-linux64-181041824.tar.bz2 + http://downloads.phoenixviewer.com/jsoncpp-0.5.0.202021553-linux64-202021553.tar.bz2 name linux64 @@ -1920,9 +1900,9 @@ archive hash - ed25115f3e53e59d4d26e0953c273648 + b73d9addab278eacc100bd312ab6ec5c url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1476/3277/jsoncpp-0.5.0.501464-windows-501464.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54990/511840/jsoncpp-0.5.0.538976-windows-538976.tar.bz2 name windows @@ -1932,16 +1912,16 @@ archive hash - b328db840fd28532be39556d130c9439 + 1b9ac5708cc526d2c5358ef0a427109d url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1477/3284/jsoncpp-0.5.0.501464-windows64-501464.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54989/511833/jsoncpp-0.5.0.538976-windows64-538976.tar.bz2 name windows64 version - 0.5.0.501464 + 0.5.0.538976 kdu @@ -1981,26 +1961,14 @@ name darwin64 - linux - - archive - - hash - e4e79a414a3b942d04473c1ee0f98bb7 - url - file:///opt/firestorm/kdu-7.A.7-linux-191472043.tar.bz2 - - name - linux - linux64 archive hash - e4e79a414a3b942d04473c1ee0f98bb7 + 2d2790facd7c568e4f7c08c030987df2 url - file:///opt/firestorm/kdu-7.A.7-linux-191472043.tar.bz2 + file:///opt/firestorm/kdu-8.0.5-linux64-202021615.tar.bz2 name linux64 @@ -2010,9 +1978,9 @@ archive hash - a86b3a2fba5d62c42c26fd17ef7804d6 + a33b5a416daba549898b215f3e4ae7ea url - file:///c:/cygwin/opt/firestorm/kdu-7.A.7-windows-191472028.tar.bz2 + file:///c:/cygwin/opt/firestorm/kdu-7.A.7-windows-vs2017-1906061512.tar.bz2 name windows @@ -2022,9 +1990,9 @@ archive hash - a86b3a2fba5d62c42c26fd17ef7804d6 + a33b5a416daba549898b215f3e4ae7ea url - file:///c:/cygwin/opt/firestorm/kdu-7.A.7-windows-191472028.tar.bz2 + file:///c:/cygwin/opt/firestorm/kdu-7.A.7-windows-vs2017-1906061512.tar.bz2 name windows64 @@ -2064,9 +2032,9 @@ archive hash - 4b238300cf9c405cdcab18030372832f + c327e6d6573fc0a808677de47f08acd9 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/534/1149/libhunspell-1.3.2.500526-darwin64-500526.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54844/510092/libhunspell-1.3.2.538974-darwin64-538974.tar.bz2 name darwin64 @@ -2100,9 +2068,9 @@ archive hash - a2025f748a6311ab390f89068b22c702 + ec22ec25160bcfd2a74f1c7bc8ff6133 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/535/1152/libhunspell-1.3.2.500526-windows-500526.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54986/511824/libhunspell-1.3.2.538974-windows-538974.tar.bz2 name windows @@ -2112,16 +2080,16 @@ archive hash - 233d86906ef88fa331263162a53e29f2 + f470c6f3f7b0559e95e76467b808de10 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/536/1155/libhunspell-1.3.2.500526-windows64-500526.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54985/511817/libhunspell-1.3.2.538974-windows64-538974.tar.bz2 name windows64 version - 1.3.2.500526 + 1.3.2.538974 libndofdev @@ -2154,9 +2122,9 @@ archive hash - 840bb6219f63a789749f5f6583c44eee + bf765dfe0b928ef3c531cd9618fee89b url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/704/1420/libndofdev-0.1.500695-darwin64-500695.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54843/510085/libndofdev-0.1.538973-darwin64-538973.tar.bz2 name darwin64 @@ -2166,9 +2134,9 @@ archive hash - fdbebbbde3b289d93c0c8c294cf859cb + 8abb7d216535009f6c0a7e43b0734b1e url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/708/1426/libndofdev-0.1.500695-windows-500695.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54984/511810/libndofdev-0.1.538973-windows-538973.tar.bz2 name windows @@ -2178,16 +2146,16 @@ archive hash - 15cef2cec6c8d1980011e26249bd4e90 + 9da7aed5a914174dcb2be12ecd4a656f url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/707/1423/libndofdev-0.1.500695-windows64-500695.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54983/511803/libndofdev-0.1.538973-windows64-538973.tar.bz2 name windows64 version - 0.1.500695 + 0.1.538973 libpng @@ -2220,9 +2188,9 @@ archive hash - 537b59a75709bd9abe0abe0c7309add4 + 0932b19bb6a8e2641706afd13d92951d url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/883/1951/libpng-1.6.8.500873-darwin64-500873.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56313/526740/libpng-1.6.8.539868-darwin64-539868.tar.bz2 name darwin64 @@ -2256,9 +2224,9 @@ archive hash - 9c2950f9d16566979dcd6ca6336778b3 + f498782698428888113b64a7505c8f7f url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/885/1957/libpng-1.6.8.500873-windows-500873.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56319/526770/libpng-1.6.8.539868-windows-539868.tar.bz2 name windows @@ -2268,16 +2236,16 @@ archive hash - 18fe233471e91d5d3ac6d08a296b79ba + f8ac4f690a2925418866bccf6eba3cf4 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/884/1954/libpng-1.6.8.500873-windows64-500873.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56317/526762/libpng-1.6.8.539868-windows64-539868.tar.bz2 name windows64 version - 1.6.8.500873 + 1.6.8.539868 libuuid @@ -2352,9 +2320,9 @@ archive hash - 89a71a652a5ecd7cf6142ff56f40f018 + 0706b9c3889d767af9f5105d9ffa9b51 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/891/1973/libxml2-2.9.4.500877-darwin64-500877.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56327/526819/libxml2-2.9.4.539866-darwin64-539866.tar.bz2 name darwin64 @@ -2388,9 +2356,9 @@ archive hash - c2461ba7629c4cef5af623464aded3c6 + 1b7b979a8387fbb0f278dc681558b9ef url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/888/1960/libxml2-2.9.4.500877-windows-500877.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56316/526755/libxml2-2.9.4.539866-windows-539866.tar.bz2 name windows @@ -2400,16 +2368,16 @@ archive hash - 8ec25000f5d72e26c2e7555c73898fbb + 4f8ff97d6a9ab350306b62eec8adc810 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/889/1963/libxml2-2.9.4.500877-windows64-500877.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56314/526748/libxml2-2.9.4.539866-windows64-539866.tar.bz2 name windows64 version - 2.9.4.500877 + 2.9.4.539866 llappearance_utility @@ -2459,16 +2427,16 @@ archive hash - dd008981cac7ede93efa6cefe4ee61a0 + 3d2122c39abb8bc6f46c0ddc0838ab2a url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12484/73813/llca-201801172118.511910-common-511910.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/58176/544556/llca-202004280657.541101-common-541101.tar.bz2 name common version - 201801172118.511910 + 202004280657.541101 llphysicsextensions_source @@ -2487,9 +2455,9 @@ archive hash - 162a3fc9b66626072ec8679361b174f5 + 14fac452271ebfba37ba5ddcf5bffa54 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4722/14837/llphysicsextensions_source-1.0.504710-darwin64-504710.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54842/510078/llphysicsextensions_source-1.0.538972-darwin64-538972.tar.bz2 name darwin64 @@ -2511,18 +2479,18 @@ archive hash - dd85c9e0f5fa3ce483ea183db008c4bc + f3c066c1aebed8a6519a3e5ce64b9a3c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4726/14858/llphysicsextensions_source-1.0.504710-windows-504710.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54982/511796/llphysicsextensions_source-1.0.538972-windows-538972.tar.bz2 name windows version - 1.0.504710 + 1.0.538972 - llphysicsextensions_tpv + llphysicsextensions_stub copyright Copyright (c) 2010, Linden Research, Inc. @@ -2531,7 +2499,7 @@ license_file LICENSES/llphysicsextensions.txt name - llphysicsextensions_tpv + llphysicsextensions_stub platforms darwin64 @@ -2539,9 +2507,9 @@ archive hash - 850bbb1e21572b101db1f07ec026b012 + f290b000b31f9e36f2489946cbc99f5e url - file:///opt/firestorm/llphysicsextensions_tpv-1.0.511060-darwin64-511060.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/59995/563653/llphysicsextensions_stub-1.0.542456-darwin64-542456.tar.bz2 name darwin64 @@ -2563,9 +2531,61 @@ archive hash - 27c1cd158e323af96026d85d2334caf5 + 2e5f1f7046a49d8b0bc295aa878116bc url - file:///c:/cygwin/opt/firestorm/llphysicsextensions_tpv-1.0.511060-windows-511060.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60043/564063/llphysicsextensions_stub-1.0.542456-windows-542456.tar.bz2 + + name + windows + + + version + 1.0.542456 + + llphysicsextensions_tpv + + copyright + Copyright (c) 2010, Linden Research, Inc. + license + internal + license_file + LICENSES/HavokSublicense.pdf + name + llphysicsextensions_tpv + platforms + + darwin64 + + archive + + hash + 2aa4ec0d72bbe4b755730f1bf92b39e7 + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/30340/257304/llphysicsextensions_tpv-1.0.542327-darwin64-542327.tar.bz2 + + name + darwin64 + + linux64 + + archive + + hash + 711f4ec769e4b5f59ba25ee43c11bcbc + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4724/14846/llphysicsextensions_stub-1.0.504712-linux64-504712.tar.bz2 + + name + linux64 + + windows + + archive + + hash + ad9aba5e2c43a37b6530a0d2de64df1c + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/30341/257307/llphysicsextensions_tpv-1.0.542327-windows-542327.tar.bz2 name windows @@ -2575,16 +2595,16 @@ archive hash - 65183e2fbd4044aeca35ff9e70ac38ac + 46689ff1442a8eccac3a7f3258308e1e url - file:///c:/cygwin/opt/firestorm/llphysicsextensions_tpv-1.0.511060-windows64-511060.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/30341/257307/llphysicsextensions_tpv-1.0.542327-windows64-542327.tar.bz2 name - windows64 + windows version - 1.0.504712 + 1.0.542327 mesa @@ -2632,9 +2652,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - f51bcd9245ed4e4ca1fa250ba9b112ce + 95b69e37b9b4435698682f4ff702cca5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9259/41575/nghttp2-1.25.0.509246-darwin64-509246.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54855/510169/nghttp2-1.25.0.538985-darwin64-538985.tar.bz2 name darwin64 @@ -2668,9 +2688,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 8367d6743356ad637e61335ee319f7a7 + 246dd8445be87c698aa7fa318bcdd7e5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9261/41597/nghttp2-1.25.0.509246-windows-509246.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55035/511985/nghttp2-1.25.0.538985-windows-538985.tar.bz2 name windows @@ -2680,9 +2700,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 3267acca5dbfe6b8770deeebd548ee6a + f2fd2dbe8704ec63ab433cbe8e03f7c4 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9260/41591/nghttp2-1.25.0.509246-windows64-509246.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55031/511978/nghttp2-1.25.0.538985-windows64-538985.tar.bz2 name windows64 @@ -2691,7 +2711,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors source_type hg version - 1.25.0.509246 + 1.25.0.538985 nvapi @@ -2712,9 +2732,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 22c7be12c1d2ee87b059be903d7f2fbd + 4305515ad326c911a390388366a9107b url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-nvapi/rev/314405/arch/CYGWIN/installer/nvapi-352.314405-windows-314405.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54947/511704/nvapi-352.539058-windows-539058.tar.bz2 name windows @@ -2724,16 +2744,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 90e32843a0e21037001dc88240008e1f + 25c8ac919f24b8952653d38ec43640e5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-nvapi/rev/314405/arch/CYGWIN/installer/nvapi-352.314405-windows64-314405.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54945/511697/nvapi-352.539058-windows64-539058.tar.bz2 name windows64 version - 352.314405 + 352.539058 ogg_vorbis @@ -2766,9 +2786,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 2c17cfd900c88914e06947fe0f1fdae4 + a066f1d12caee1d87fc72f48169f9677 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/25395/199641/ogg_vorbis-1.3.3-1.3.6.520171-darwin64-520171.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54841/510071/ogg_vorbis-1.3.3-1.3.6.538971-darwin64-538971.tar.bz2 name darwin64 @@ -2802,9 +2822,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 1818627d4d1f05b49709717e240bdcf4 + d4b8ed3fd679a2b484d2d1a66c063908 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/25396/199634/ogg_vorbis-1.3.3-1.3.6.520171-windows-520171.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54981/511789/ogg_vorbis-1.3.3-1.3.6.538971-windows-538971.tar.bz2 name windows @@ -2814,16 +2834,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - d124788c798684c890c1803fca541a10 + ec4a657fe639bb458ee5132062146a7a url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/25397/199631/ogg_vorbis-1.3.3-1.3.6.520171-windows64-520171.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54980/511782/ogg_vorbis-1.3.3-1.3.6.538971-windows64-538971.tar.bz2 name windows64 version - 1.3.3-1.3.6.520171 + 1.3.3-1.3.6.538971 open-libndofdev @@ -2956,9 +2976,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 4e278d789c5d6e448d1e39211d19bf41 + 5abf2d9c0b250821c59cc60cd94fd8af url - http://downloads.phoenixviewer.com/openjpeg-1.4.201505181816-r27-darwin-201505181816-r27.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54840/510064/openjpeg-1.5.1.538970-darwin64-538970.tar.bz2 name darwin64 @@ -2992,9 +3012,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - b35adcf74d22c128045aa87aade74736 + 222a406ecb4071a9cc9635353afa337e url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/openjpeg_3p-update-openjpeg/rev/297018/arch/CYGWIN/installer/openjpeg-1.4.297018-windows-297018.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54977/511775/openjpeg-1.5.1.538970-windows-538970.tar.bz2 name windows @@ -3004,16 +3024,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 47c09d96ae84fa4518b639cd93ba45b3 + 5b5c80807fa8161f3480be3d89fe9516 url - http://downloads.phoenixviewer.com/openjpeg-1.4.201412051021-windows-x64-201412051021.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54974/511767/openjpeg-1.5.1.538970-windows64-538970.tar.bz2 name windows64 version - 1.4.0.297018 + 1.5.1.538970 openssl @@ -3046,9 +3066,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 6c28cce95e3576daf66252b07d9d151f + 18aef0c8fc471b6539addbdc019aea25 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/8340/33489/openssl-1.0.2l.508328-darwin64-508328.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56325/526804/openssl-1.0.2l.539874-darwin64-539874.tar.bz2 name darwin64 @@ -3082,9 +3102,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - ffdb11a4c7aff72086c01555f931c918 + 2b2f61313b1cbd2893c1ba5bf15061fa url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/8341/33481/openssl-1.0.2l.508328-windows-508328.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56328/526826/openssl-1.0.2l.539874-windows-539874.tar.bz2 name windows @@ -3094,16 +3114,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - d875fc7d1f3a7bd9f85cfde05d9ae3dc + 59aae854155bc7119e0dca25e65828c0 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/8342/33480/openssl-1.0.2l.508328-windows64-508328.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/56326/526811/openssl-1.0.2l.539874-windows64-539874.tar.bz2 name windows64 version - 1.0.2l.508328 + 1.0.2l.539874 pcre @@ -3136,9 +3156,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - addfbc0635b0ea65d7a151dd7ec5ef85 + d8c0f97fe5abef43e72b6f84aba698b2 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/909/2015/pcre-8.35.500898-darwin64-500898.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54856/510176/pcre-8.35.538986-darwin64-538986.tar.bz2 name darwin64 @@ -3160,9 +3180,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 348a6111dede9a4ddfc51049c2ff186f + 742a145c1518b9dc195ae0862828e6ae url - http://downloads.phoenixviewer.com/pcre-8.35.180841548-linux64-180841548.tar.bz2 + http://downloads.phoenixviewer.com/pcre-8.35.202021522-linux64-202021522.tar.bz2 name linux64 @@ -3172,9 +3192,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 150220f39f0aa5a8d9e609b450a9b147 + 3660db45793df3050b63920bfb7d8479 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/911/2021/pcre-8.35.500898-windows-500898.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55041/512002/pcre-8.35.538986-windows-538986.tar.bz2 name windows @@ -3184,16 +3204,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - eaebfb4a96a6306ee8e0b18434d125f9 + cdee8e8b48a66266550bf279c40abc22 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/910/2018/pcre-8.35.500898-windows64-500898.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55038/511992/pcre-8.35.538986-windows64-538986.tar.bz2 name windows64 version - 8.35.500898 + 8.35.538986 slvoice @@ -3226,9 +3246,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - f824d586ab5de6edd14ef6828e9e4b66 + 321a8542e7b693fbe8e44ebface06087 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44719/395040/slvoice-4.10.0000.32327.5fc3fe7c.531581-darwin64-531581.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55966/524403/slvoice-4.10.0000.32327.5fc3fe7c.539691-darwin64-539691.tar.bz2 name darwin64 @@ -3262,9 +3282,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 1941c17c81905f23b4928288bcf719fb + fb1a57a1cf5e38a3d51b32307b93ffba url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44720/395047/slvoice-4.10.0000.32327.5fc3fe7c.531581-windows-531581.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55968/524423/slvoice-4.10.0000.32327.5fc3fe7c.539691-windows-539691.tar.bz2 name windows @@ -3274,16 +3294,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - baa6cdc8e8762d5519996ed9faa0bf3f + 81df970eb0c97d415d7bd12049c82042 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44721/395056/slvoice-4.10.0000.32327.5fc3fe7c.531581-windows64-531581.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55967/524409/slvoice-4.10.0000.32327.5fc3fe7c.539691-windows64-539691.tar.bz2 name windows64 version - 4.10.0000.32327.5fc3fe7c.531581 + 4.10.0000.32327.5fc3fe7c.539691 tut @@ -3304,9 +3324,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 722563bd6e2ae0c7e53c027d267154f7 + 64e1c979aea2f74fe9c2d9d04573336d url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/496/1054/tut-2008.11.30-common-500403.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55001/511871/tut-2008.11.30-common-539059.tar.bz2 name common @@ -3346,9 +3366,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 5b9cd1d6fac519aad59f6d53a54229c5 + d463360491b6b5cb7a57cd67a90ececb url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/347/872/uriparser-0.8.0.1-darwin64-500342.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54838/510050/uriparser-0.8.0.1-darwin64-538968.tar.bz2 name darwin64 @@ -3382,9 +3402,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 1becd11c19dd1763f0322ba4d1a5ee06 + 57a88be57694de6cf9f516125af2c4c9 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/521/1129/uriparser-0.8.0.1-windows-500342.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54963/511746/uriparser-0.8.0.1-windows-538968.tar.bz2 name windows @@ -3394,9 +3414,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 587db55a2a3ce57628374b5e27b3272e + f39cc91f2a5dad13790ec18269844ae4 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/349/875/uriparser-0.8.0.1-windows64-500342.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54962/511739/uriparser-0.8.0.1-windows64-538968.tar.bz2 name windows64 @@ -3424,9 +3444,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - c5ab9d9d7482e48cd76f4bf391900a8c + d15ad6b86c0e1ef4a1fc46478da65929 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/43369/385585/viewer_manager-2.0.531000-darwin64-531000.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54837/510043/viewer_manager-2.0.538967-darwin64-538967.tar.bz2 name darwin64 @@ -3460,9 +3480,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 6b10d7407686d9e12e63576256581e3e + 856d1e4b60ef57135ecd99cd608e76bd url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/43370/385592/viewer_manager-2.0.531000-windows-531000.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54960/511732/viewer_manager-2.0.538967-windows-538967.tar.bz2 name windows @@ -3473,7 +3493,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors source_type hg version - 2.0.531000 + 2.0.538967 vlc-bin @@ -3492,9 +3512,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - e5a4261599f47045428b520b20405022 + e5635e173c75dc0675b48ab5f5e4868b url - http://downloads.phoenixviewer.com/vlc_bin-3.0.8.200601940-darwin64-200601940.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12143/71451/vlc_bin-2.2.8.511703-darwin64-511703.tar.bz2 name darwin64 @@ -3528,9 +3548,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - add560654a53cb1c554044a4fac3c718 + ca84b7c5f86e702fb35727eed8f0c8c4 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12144/71458/vlc_bin-2.2.8.511703-windows-511703.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54958/511725/vlc_bin-2.2.8.538966-windows-538966.tar.bz2 name windows @@ -3540,16 +3560,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 94bf04b49acc1e1bf2c06e2232f8a083 + 93cd88d90cb8aedbed5cd90ff9262409 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12145/71463/vlc_bin-2.2.8.511703-windows64-511703.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54954/511718/vlc_bin-2.2.8.538966-windows64-538966.tar.bz2 name windows64 version - 2.2.8.511703 + 2.2.8.538966 xmlrpc-epi @@ -3582,9 +3602,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - b2d31df56a10c634657eed856c8d7895 + 99ea1808ee9f5b55029daa9fdef86776 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/728/1494/xmlrpc_epi-0.54.1.500719-darwin64-500719.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55063/512104/xmlrpc_epi-0.54.1.539072-darwin64-539072.tar.bz2 name darwin64 @@ -3618,9 +3638,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 6c16f020bf01155e6746487af0b26173 + 94643b7cebb449f049fa9e32ae682bcd url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/729/1497/xmlrpc_epi-0.54.1.500719-windows-500719.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55138/512288/xmlrpc_epi-0.54.1.539072-windows-539072.tar.bz2 name windows @@ -3630,16 +3650,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - a9dda7caa8835c52b3735711cfee4eb9 + c409de1974a879291ce7daaf52348d85 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/730/1500/xmlrpc_epi-0.54.1.500719-windows64-500719.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55137/512279/xmlrpc_epi-0.54.1.539072-windows64-539072.tar.bz2 name windows64 version - 0.54.1.500719 + 0.54.1.539072 zlib @@ -3672,9 +3692,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - e204dee29902549f50af1af2bb098df5 + 9785bda5b4d3b41bf391b33d0da78c9e url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/867/1903/zlib-1.2.8.500857-darwin64-500857.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54858/510190/zlib-1.2.8.538988-darwin64-538988.tar.bz2 name darwin64 @@ -3710,9 +3730,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - f92cbb0ab5e5d20789bf6102f9a27aa6 + ebdb07d4aaa5312005a8773f625032a4 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/868/1906/zlib-1.2.8.500857-windows-500857.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55048/512031/zlib-1.2.8.538988-windows-538988.tar.bz2 name windows @@ -3722,16 +3742,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 70a56767f6a109af412838875d0b5f1b + 0ac95f3dece7d575ba45cf5728f53eea url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/869/1909/zlib-1.2.8.500857-windows64-500857.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55047/512024/zlib-1.2.8.538988-windows64-538988.tar.bz2 name windows64 version - 1.2.8.500857 + 1.2.8.538988 package_description @@ -4475,7 +4495,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors windows build_directory - build-vc120-$AUTOBUILD_ADDRSIZE + build-vc${AUTOBUILD_VSVER|150}-$AUTOBUILD_ADDRSIZE configurations RelWithDebInfo diff --git a/build.sh b/build.sh index 96d1c32d08..4dff58971f 100755 --- a/build.sh +++ b/build.sh @@ -28,7 +28,7 @@ build_dir_Linux() build_dir_CYGWIN() { - echo build-vc120-${AUTOBUILD_ADDRSIZE} + echo build-vc${AUTOBUILD_VSVER:-120}-${AUTOBUILD_ADDRSIZE} } viewer_channel_suffix() diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 81e6c3ac26..0d8ee1fb9d 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -15,6 +15,11 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(Variables) include(BuildVersion) +set(LEGACY_STDIO_LIBS) +if (WINDOWS) + set(LEGACY_STDIO_LIBS legacy_stdio_definitions) +endif (WINDOWS) + if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type. One of: Debug Release RelWithDebInfo" FORCE) @@ -93,6 +98,7 @@ if (WINDOWS AND EXISTS ${LIBS_CLOSED_DIR}copy_win_scripts) endif (WINDOWS AND EXISTS ${LIBS_CLOSED_DIR}copy_win_scripts) add_custom_target(viewer) + add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger) add_subdirectory(${LIBS_OPEN_PREFIX}llplugin) add_subdirectory(${LIBS_OPEN_PREFIX}llui) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index ed6ead4433..34aed62ba0 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -67,6 +67,11 @@ if (WINDOWS) # http://www.cmake.org/pipermail/cmake/2009-September/032143.html string(REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + # Remove this, it's no option to cl.exe and causes a massive amount of warnings. + # Without PreferredToolArchitecture=x64, as of 2020-06-26 the 32-bit + # compiler on our TeamCity build hosts has started running out of virtual + # memory for the precompiled header file. + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /p:PreferredToolArchitecture=x64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO @@ -193,6 +198,7 @@ if (LINUX) # linking can be very memory-hungry, especially the final viewer link #set(CMAKE_CXX_LINK_FLAGS "-Wl,--no-keep-memory") set(CMAKE_CXX_LINK_FLAGS "-Wl,--no-keep-memory -Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../lib' -Wl,--exclude-libs,ALL") + set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-keep-memory -Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../lib' -Wl,--exclude-libs,ALL") endif (NOT USESYSTEMLIBS) set(CMAKE_CXX_LINK_FLAGS "-Wl,--no-keep-memory -Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../lib' -Wl,--exclude-libs,ALL") diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index ae4f655c5b..32e7904b6d 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -23,11 +23,14 @@ if (USESYSTEMLIBS) else (USESYSTEMLIBS) use_prebuilt_binary(boost) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) - set(BOOST_VERSION "1.55") - add_definitions(-DBOOST_ALL_NO_LIB) + + # with the address size. + set(addrsfx "-x${ADDRESS_SIZE}") if (WINDOWS) if(MSVC80) + # This should be obsolete at this point + set(BOOST_VERSION "1.55") set(BOOST_CONTEXT_LIBRARY optimized libboost_context-vc80-mt-${BOOST_VERSION} debug libboost_context-vc80-mt-gd-${BOOST_VERSION}) @@ -58,89 +61,89 @@ else (USESYSTEMLIBS) else(MSVC80) # MSVC 10.0 config set(BOOST_CONTEXT_LIBRARY - optimized libboost_context-mt - debug libboost_context-mt-gd) - set(BOOST_COROUTINE_LIBRARY - optimized libboost_coroutine-mt - debug libboost_coroutine-mt-gd) + optimized libboost_context-mt${addrsfx} + debug libboost_context-mt${addrsfx}-gd) + set(BOOST_FIBER_LIBRARY + optimized libboost_fiber-mt${addrsfx} + debug libboost_fiber-mt${addrsfx}-gd) set(BOOST_FILESYSTEM_LIBRARY - optimized libboost_filesystem-mt - debug libboost_filesystem-mt-gd) + optimized libboost_filesystem-mt${addrsfx} + debug libboost_filesystem-mt${addrsfx}-gd) set(BOOST_PROGRAM_OPTIONS_LIBRARY - optimized libboost_program_options-mt - debug libboost_program_options-mt-gd) + optimized libboost_program_options-mt${addrsfx} + debug libboost_program_options-mt${addrsfx}-gd) set(BOOST_REGEX_LIBRARY - optimized libboost_regex-mt - debug libboost_regex-mt-gd) + optimized libboost_regex-mt${addrsfx} + debug libboost_regex-mt${addrsfx}-gd) set(BOOST_SIGNALS_LIBRARY - optimized libboost_signals-mt - debug libboost_signals-mt-gd) + optimized libboost_signals-mt${addrsfx} + debug libboost_signals-mt${addrsfx}-gd) set(BOOST_SYSTEM_LIBRARY - optimized libboost_system-mt - debug libboost_system-mt-gd) + optimized libboost_system-mt${addrsfx} + debug libboost_system-mt${addrsfx}-gd) set(BOOST_THREAD_LIBRARY - optimized libboost_thread-mt - debug libboost_thread-mt-gd) + optimized libboost_thread-mt${addrsfx} + debug libboost_thread-mt${addrsfx}-gd) set(BOOST_WAVE_LIBRARY - optimized libboost_wave-mt - debug libboost_wave-mt-gd) + optimized libboost_wave-mt${addrsfx} + debug libboost_wave-mt${addrsfx}-gd) endif (MSVC80) elseif (LINUX) set(BOOST_CONTEXT_LIBRARY - optimized boost_context-mt - debug boost_context-mt-d) - set(BOOST_COROUTINE_LIBRARY - optimized boost_coroutine-mt - debug boost_coroutine-mt-d) + optimized boost_context-mt${addrsfx} + debug boost_context-mt${addrsfx}-d) + set(BOOST_FIBER_LIBRARY + optimized boost_fiber-mt${addrsfx} + debug boost_fiber-mt${addrsfx}-d) set(BOOST_FILESYSTEM_LIBRARY - optimized boost_filesystem-mt - debug boost_filesystem-mt-d) + optimized boost_filesystem-mt${addrsfx} + debug boost_filesystem-mt${addrsfx}-d) set(BOOST_PROGRAM_OPTIONS_LIBRARY - optimized boost_program_options-mt - debug boost_program_options-mt-d) + optimized boost_program_options-mt${addrsfx} + debug boost_program_options-mt${addrsfx}-d) set(BOOST_REGEX_LIBRARY - optimized boost_regex-mt - debug boost_regex-mt-d) + optimized boost_regex-mt${addrsfx} + debug boost_regex-mt${addrsfx}-d) set(BOOST_SIGNALS_LIBRARY - optimized boost_signals-mt - debug boost_signals-mt-d) + optimized boost_signals-mt${addrsfx} + debug boost_signals-mt${addrsfx}-d) set(BOOST_SYSTEM_LIBRARY - optimized boost_system-mt - debug boost_system-mt-d) + optimized boost_system-mt${addrsfx} + debug boost_system-mt${addrsfx}-d) set(BOOST_THREAD_LIBRARY - optimized boost_thread-mt - debug boost_thread-mt-d) - set(BOOST_WAVE_LIBRARY - optimized boost_wave-mt - debug boost_wave-mt-d) + optimized boost_thread-mt${addrsfx} + debug boost_thread-mt${addrsfx}-d) + set(BOOST_WAVE_LIBRARY + optimized boost_wave-mt${addrsfx} + debug boost_wave-mt${addrsfx}-gd) elseif (DARWIN) set(BOOST_CONTEXT_LIBRARY - optimized boost_context-mt - debug boost_context-mt-d) - set(BOOST_COROUTINE_LIBRARY - optimized boost_coroutine-mt - debug boost_coroutine-mt-d) + optimized boost_context-mt${addrsfx} + debug boost_context-mt${addrsfx}-d) + set(BOOST_FIBER_LIBRARY + optimized boost_fiber-mt${addrsfx} + debug boost_fiber-mt${addrsfx}-d) set(BOOST_FILESYSTEM_LIBRARY - optimized boost_filesystem-mt - debug boost_filesystem-mt-d) + optimized boost_filesystem-mt${addrsfx} + debug boost_filesystem-mt${addrsfx}-d) set(BOOST_PROGRAM_OPTIONS_LIBRARY - optimized boost_program_options-mt - debug boost_program_options-mt-d) + optimized boost_program_options-mt${addrsfx} + debug boost_program_options-mt${addrsfx}-d) set(BOOST_REGEX_LIBRARY - optimized boost_regex-mt - debug boost_regex-mt-d) + optimized boost_regex-mt${addrsfx} + debug boost_regex-mt${addrsfx}-d) set(BOOST_SIGNALS_LIBRARY - optimized boost_signals-mt - debug boost_signals-mt-d) + optimized boost_signals-mt${addrsfx} + debug boost_signals-mt${addrsfx}-d) set(BOOST_SYSTEM_LIBRARY - optimized boost_system-mt - debug boost_system-mt-d) + optimized boost_system-mt${addrsfx} + debug boost_system-mt${addrsfx}-d) set(BOOST_THREAD_LIBRARY - optimized boost_thread-mt - debug boost_thread-mt-d) - set(BOOST_WAVE_LIBRARY - optimized boost_wave-mt - debug boost_wave-mt-d) + optimized boost_thread-mt${addrsfx} + debug boost_thread-mt${addrsfx}-d) + set(BOOST_WAVE_LIBRARY + optimized libboost_wave-mt${addrsfx} + debug libboost_wave-mt${addrsfx}-gd) endif (WINDOWS) endif (USESYSTEMLIBS) diff --git a/indra/cmake/BuildPackagesInfo.cmake b/indra/cmake/BuildPackagesInfo.cmake index 4b50a0275e..313a904471 100644 --- a/indra/cmake/BuildPackagesInfo.cmake +++ b/indra/cmake/BuildPackagesInfo.cmake @@ -1,6 +1,7 @@ # -*- cmake -*- # Construct the version and copyright information based on package data. include(Python) +include(FindAutobuild) if( NOT USESYSTEMLIBS ) @@ -15,7 +16,7 @@ add_custom_command(OUTPUT packages-info.txt DEPENDS ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py ${CMAKE_SOURCE_DIR}/../autobuild.xml COMMAND ${PYTHON_EXECUTABLE} - ${CMAKE_SOURCE_DIR}/cmake/run_build_test.py -DAUTOBUILD_ADDRSIZE=${ADDRESS_SIZE} + ${CMAKE_SOURCE_DIR}/cmake/run_build_test.py -DAUTOBUILD_ADDRSIZE=${ADDRESS_SIZE} -DAUTOBUILD=${AUTOBUILD_EXECUTABLE} ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/../scripts/packages-formatter.py "${VIEWER_CHANNEL}" "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}" > packages-info.txt ) diff --git a/indra/cmake/CEFPlugin.cmake b/indra/cmake/CEFPlugin.cmake index 5faa2b230a..21a8f441f2 100644 --- a/indra/cmake/CEFPlugin.cmake +++ b/indra/cmake/CEFPlugin.cmake @@ -6,15 +6,10 @@ if (USESYSTEMLIBS) set(CEFPLUGIN ON CACHE BOOL "CEFPLUGIN support for the llplugin/llmedia test apps.") else (USESYSTEMLIBS) - if (LINUX AND ( CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9.4 ) ) - message( "Using dullahan for GCC >= 5 " ) - use_prebuilt_binary(dullahan-gcc5) - else() use_prebuilt_binary(dullahan) - endif() set(CEFPLUGIN ON CACHE BOOL "CEFPLUGIN support for the llplugin/llmedia test apps.") - set(CEF_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/cef) + set(CEF_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/cef) endif (USESYSTEMLIBS) if (WINDOWS) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 4e6dd92424..27ddb98e86 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -23,7 +23,6 @@ set(cmake_SOURCE_FILES DBusGlib.cmake DeploySharedLibs.cmake Discord.cmake # Discord rich presence - DirectX.cmake DragDrop.cmake EXPAT.cmake FindAPR.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 694bc67357..8c0043d03e 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -7,6 +7,21 @@ include(CMakeCopyIfDifferent) include(Linking) +# When we copy our dependent libraries, we almost always want to copy them to +# both the Release and the RelWithDebInfo staging directories. This has +# resulted in duplicate (or worse, erroneous attempted duplicate) +# copy_if_different commands. Encapsulate that usage. +# Pass FROM_DIR, TARGETS and the files to copy. TO_DIR is implicit. +# to_staging_dirs diverges from copy_if_different in that it appends to TARGETS. +MACRO(to_staging_dirs from_dir targets) + foreach(staging_dir + "${SHARED_LIB_STAGING_DIR_RELEASE}" + "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}") + copy_if_different("${from_dir}" "${staging_dir}" out_targets ${ARGN}) + list(APPEND "${targets}" "${out_targets}") + endforeach() +ENDMACRO(to_staging_dirs from_dir to_dir targets) + ################################################################### # set up platform specific lists of files that need to be copied ################################################################### @@ -74,95 +89,54 @@ if(WINDOWS) #******************************* # Copy MS C runtime dlls, required for packaging. - # *TODO - Adapt this to support VC9 if (MSVC80) - list(APPEND LMSVC_VER 80) - list(APPEND LMSVC_VERDOT 8.0) + set(MSVC_VER 80) elseif (MSVC_VERSION EQUAL 1600) # VisualStudio 2010 MESSAGE(STATUS "MSVC_VERSION ${MSVC_VERSION}") elseif (MSVC_VERSION EQUAL 1800) # VisualStudio 2013, which is (sigh) VS 12 - list(APPEND LMSVC_VER 120) - list(APPEND LMSVC_VERDOT 12.0) + set(MSVC_VER 120) + elseif (MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) # Visual Studio 2017 + set(MSVC_VER 140) else (MSVC80) MESSAGE(WARNING "New MSVC_VERSION ${MSVC_VERSION} of MSVC: adapt Copy3rdPartyLibs.cmake") endif (MSVC80) - # try to copy VS2010 redist independently of system version - # maint-7360 CP - # list(APPEND LMSVC_VER 100) - # list(APPEND LMSVC_VERDOT 10.0) - - list(LENGTH LMSVC_VER count) - math(EXPR count "${count}-1") - foreach(i RANGE ${count}) - list(GET LMSVC_VER ${i} MSVC_VER) - list(GET LMSVC_VERDOT ${i} MSVC_VERDOT) - MESSAGE(STATUS "Copying redist libs for VC ${MSVC_VERDOT}") - FIND_PATH(debug_msvc_redist_path NAME msvcr${MSVC_VER}d.dll - PATHS - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\${MSVC_VERDOT}\\Setup\\VC;ProductDir]/redist/Debug_NonRedist/x86/Microsoft.VC${MSVC_VER}.DebugCRT - [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/SysWOW64 - [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/System32 - ${MSVC_DEBUG_REDIST_PATH} - NO_DEFAULT_PATH + if(ADDRESS_SIZE EQUAL 32) + # this folder contains the 32bit DLLs.. (yes really!) + set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/SysWOW64") + else(ADDRESS_SIZE EQUAL 32) + # this folder contains the 64bit DLLs.. (yes really!) + set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/System32") + endif(ADDRESS_SIZE EQUAL 32) + + # Having a string containing the system registry path is a start, but to + # get CMake to actually read the registry, we must engage some other + # operation. + get_filename_component(registry_path "${registry_find_path}" ABSOLUTE) + + # These are candidate DLL names. Empirically, VS versions before 2015 have + # msvcp*.dll and msvcr*.dll. VS 2017 has msvcp*.dll and vcruntime*.dll. + # Check each of them. + foreach(release_msvc_file + msvcp${MSVC_VER}.dll + msvcr${MSVC_VER}.dll + vcruntime${MSVC_VER}.dll ) - - if(EXISTS ${debug_msvc_redist_path}) - set(debug_msvc_files - msvcr${MSVC_VER}d.dll - msvcp${MSVC_VER}d.dll - ) - - copy_if_different( - ${debug_msvc_redist_path} - "${SHARED_LIB_STAGING_DIR_DEBUG}" - out_targets - ${debug_msvc_files} - ) - set(third_party_targets ${third_party_targets} ${out_targets}) - - unset(debug_msvc_redist_path CACHE) - endif() - - if(ADDRESS_SIZE EQUAL 32) - # this folder contains the 32bit DLLs.. (yes really!) - set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/SysWOW64") - else(ADDRESS_SIZE EQUAL 32) - # this folder contains the 64bit DLLs.. (yes really!) - set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/System32") - endif(ADDRESS_SIZE EQUAL 32) - - FIND_PATH(release_msvc_redist_path NAME msvcr${MSVC_VER}.dll - PATHS - ${registry_find_path} - NO_DEFAULT_PATH - ) - - if(EXISTS ${release_msvc_redist_path}) - set(release_msvc_files - msvcr${MSVC_VER}.dll - msvcp${MSVC_VER}.dll - ) - - copy_if_different( - ${release_msvc_redist_path} - "${SHARED_LIB_STAGING_DIR_RELEASE}" - out_targets - ${release_msvc_files} - ) - set(third_party_targets ${third_party_targets} ${out_targets}) - - copy_if_different( - ${release_msvc_redist_path} - "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}" - out_targets - ${release_msvc_files} - ) - set(third_party_targets ${third_party_targets} ${out_targets}) - - unset(release_msvc_redist_path CACHE) + if(EXISTS "${registry_path}/${release_msvc_file}") + to_staging_dirs( + ${registry_path} + third_party_targets + ${release_msvc_file}) + else() + # This isn't a WARNING because, as noted above, every VS version + # we've observed has only a subset of the specified DLL names. + MESSAGE(STATUS "Redist lib ${release_msvc_file} not found") endif() endforeach() + MESSAGE(STATUS "Will copy redist files for MSVC ${MSVC_VER}:") + foreach(target ${third_party_targets}) + MESSAGE(STATUS "${target}") + endforeach() elseif(DARWIN) set(SHARED_LIB_STAGING_DIR_DEBUG "${SHARED_LIB_STAGING_DIR}/Debug/Resources") @@ -187,6 +161,7 @@ elseif(DARWIN) libexception_handler.dylib ${EXPAT_COPY} libGLOD.dylib + libhunspell-1.3.0.dylib libndofdev.dylib libnghttp2.dylib libnghttp2.14.dylib @@ -283,52 +258,28 @@ endif(WINDOWS) # Done building the file lists, now set up the copy commands. ################################################################ -copy_if_different( - ${vivox_lib_dir} - "${SHARED_LIB_STAGING_DIR_DEBUG}" - out_targets - ${vivox_libs} - ) -set(third_party_targets ${third_party_targets} ${out_targets}) - +# Curiously, slvoice_files are only copied to SHARED_LIB_STAGING_DIR_RELEASE. +# It's unclear whether this is oversight or intentional, but anyway leave the +# single copy_if_different command rather than using to_staging_dirs. copy_if_different( ${slvoice_src_dir} "${SHARED_LIB_STAGING_DIR_RELEASE}" out_targets ${slvoice_files} ) -copy_if_different( +list(APPEND third_party_targets ${out_targets}) + +to_staging_dirs( ${vivox_lib_dir} - "${SHARED_LIB_STAGING_DIR_RELEASE}" - out_targets + third_party_targets ${vivox_libs} ) -set(third_party_targets ${third_party_targets} ${out_targets}) - -copy_if_different( - ${vivox_lib_dir} - "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}" - out_targets - ${vivox_libs} - ) -set(third_party_targets ${third_party_targets} ${out_targets}) - -copy_if_different( +to_staging_dirs( ${release_src_dir} - "${SHARED_LIB_STAGING_DIR_RELEASE}" - out_targets + third_party_targets ${release_files} ) -set(third_party_targets ${third_party_targets} ${out_targets}) - -copy_if_different( - ${release_src_dir} - "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}" - out_targets - ${release_files} - ) -set(third_party_targets ${third_party_targets} ${out_targets}) if(NOT USESYSTEMLIBS) add_custom_target( diff --git a/indra/cmake/DirectX.cmake b/indra/cmake/DirectX.cmake deleted file mode 100644 index ba7bd5da2d..0000000000 --- a/indra/cmake/DirectX.cmake +++ /dev/null @@ -1,76 +0,0 @@ -# -*- cmake -*- - -include(Variables) - -if (WINDOWS) - if (WORD_SIZE EQUAL 32) - set (DIRECTX_ARCHITECTURE x86) - elseif (WORD_SIZE EQUAL 64) - set (DIRECTX_ARCHITECTURE x64) - else (WORD_SIZE EQUAL 32) - set (DIRECTX_ARCHITECTURE x86) - endif (WORD_SIZE EQUAL 32) - - set(PROG_x86 "ProgramFiles(x86)" ) - find_path(DIRECTX_ROOT_DIR Include/dxdiag.h - PATHS - "$ENV{DXSDK_DIR}" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (June 2010)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (June 2010)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (February 2010)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (February 2010)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (March 2009)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (March 2009)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (August 2008)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (August 2008)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (June 2008)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (June 2008)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (March 2008)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (March 2008)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (November 2007)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (November 2007)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (August 2007)" - "$ENV{${PROG_x86}}/Microsoft DirectX SDK (August 2007)" - ) - - if (DIRECTX_ROOT_DIR) - set (DIRECTX_INCLUDE_DIR "${DIRECTX_ROOT_DIR}/Include") - set (DIRECTX_LIBRARY_DIR "${DIRECTX_ROOT_DIR}/Lib/${DIRECTX_ARCHITECTURE}") - else (DIRECTX_ROOT_DIR) - find_path (WIN_KIT_ROOT_DIR Include/um/windows.h - PATHS - "$ENV{ProgramFiles}/Windows Kits/8.1" - "$ENV{${PROG_x86}}/Windows Kits/8.1" - "$ENV{ProgramFiles}/Windows Kits/8.0" - "$ENV{${PROG_x86}}/Windows Kits/8.0" - ) - - find_path (WIN_KIT_LIB_DIR dxguid.lib - "${WIN_KIT_ROOT_DIR}/Lib/winv6.3/um/${DIRECTX_ARCHITECTURE}" - "${WIN_KIT_ROOT_DIR}/Lib/Win8/um/${DIRECTX_ARCHITECTURE}" - ) - - if (WIN_KIT_ROOT_DIR) - set (DIRECTX_INCLUDE_DIR "${WIN_KIT_ROOT_DIR}/Include/um" "${WIN_KIT_ROOT_DIR}/Include/shared") - set (DIRECTX_LIBRARY_DIR "${WIN_KIT_LIB_DIR}") - endif (WIN_KIT_ROOT_DIR) - endif (DIRECTX_ROOT_DIR) - - if (DIRECTX_INCLUDE_DIR) - include_directories(${DIRECTX_INCLUDE_DIR}) - if (DIRECTX_FIND_QUIETLY) - message(STATUS "Found DirectX include: ${DIRECTX_INCLUDE_DIR}") - endif (DIRECTX_FIND_QUIETLY) - else (DIRECTX_INCLUDE_DIR) - message(FATAL_ERROR "Could not find DirectX SDK Include") - endif (DIRECTX_INCLUDE_DIR) - - if (DIRECTX_LIBRARY_DIR) - if (DIRECTX_FIND_QUIETLY) - message(STATUS "Found DirectX include: ${DIRECTX_LIBRARY_DIR}") - endif (DIRECTX_FIND_QUIETLY) - else (DIRECTX_LIBRARY_DIR) - message(FATAL_ERROR "Could not find DirectX SDK Libraries") - endif (DIRECTX_LIBRARY_DIR) - -endif (WINDOWS) diff --git a/indra/cmake/Discord.cmake b/indra/cmake/Discord.cmake index 908f08612b..e0e38003c3 100644 --- a/indra/cmake/Discord.cmake +++ b/indra/cmake/Discord.cmake @@ -5,11 +5,7 @@ use_prebuilt_binary(discord-rpc) set(DISCORD_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/discord-rpc) if (WINDOWS) -if (ADDRESS_SIZE EQUAL 32) set(DISCORD_LIBRARY discord-rpc) -else () - set(DISCORD_LIBRARY discord-rpc_x64) -endif(ADDRESS_SIZE EQUAL 32) elseif (LINUX) set(DISCORD_LIBRARY discord-rpc) elseif (DARWIN) diff --git a/indra/cmake/JsonCpp.cmake b/indra/cmake/JsonCpp.cmake index 055e1243de..969f4adea6 100644 --- a/indra/cmake/JsonCpp.cmake +++ b/indra/cmake/JsonCpp.cmake @@ -16,7 +16,7 @@ else (USESYSTEMLIBS) elseif (DARWIN) set(JSONCPP_LIBRARIES libjson_darwin_libmt.a) elseif (LINUX) - set(JSONCPP_LIBRARIES libjson_linux-gcc-4.8_libmt.a) + set(JSONCPP_LIBRARIES libjson_linux-gcc-5.4.0_libmt.a) endif (WINDOWS) set(JSONCPP_INCLUDE_DIR "${LIBS_PREBUILT_DIR}/include/") endif (USESYSTEMLIBS) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index d9c282be3f..5ee465e08a 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -58,7 +58,7 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) ${GOOGLEMOCK_INCLUDE_DIRS} ) SET(alltest_LIBRARIES - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${GOOGLEMOCK_LIBRARIES} @@ -205,8 +205,9 @@ FUNCTION(LL_ADD_INTEGRATION_TEST ) SET(libraries + ${LEGACY_STDIO_LIBS} ${library_dependencies} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${GOOGLEMOCK_LIBRARIES} diff --git a/indra/cmake/LLAppearance.cmake b/indra/cmake/LLAppearance.cmake index ae265d07e3..675330ec72 100644 --- a/indra/cmake/LLAppearance.cmake +++ b/indra/cmake/LLAppearance.cmake @@ -18,7 +18,7 @@ endif (BUILD_HEADLESS) set(LLAPPEARANCE_LIBRARIES llappearance llmessage llcorehttp - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index 3e29297c58..8900419f9b 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -19,7 +19,7 @@ if (LINUX) # specify all libraries that llcommon uses. # llcommon uses `clock_gettime' which is provided by librt on linux. set(LLCOMMON_LIBRARIES llcommon - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_THREAD_LIBRARY} ${BOOST_SYSTEM_LIBRARY} @@ -27,7 +27,7 @@ if (LINUX) ) else (LINUX) set(LLCOMMON_LIBRARIES llcommon - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_THREAD_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake index 379ae207de..613453ab5d 100644 --- a/indra/cmake/LLCoreHttp.cmake +++ b/indra/cmake/LLCoreHttp.cmake @@ -12,6 +12,6 @@ set(LLCOREHTTP_INCLUDE_DIRS ) set(LLCOREHTTP_LIBRARIES llcorehttp - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY}) diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt index d9353f904c..5787d4d600 100644 --- a/indra/integration_tests/llimage_libtest/CMakeLists.txt +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -64,6 +64,7 @@ endif (DARWIN) # Libraries on which this application depends on # Sort by high-level to low-level target_link_libraries(llimage_libtest + ${LEGACY_STDIO_LIBS} ${LLCOMMON_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt index 34e34c7e47..1cec660eb0 100644 --- a/indra/integration_tests/llui_libtest/CMakeLists.txt +++ b/indra/integration_tests/llui_libtest/CMakeLists.txt @@ -75,6 +75,7 @@ endif (DARWIN) # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level target_link_libraries(llui_libtest + ${LEGACY_STDIO_LIBS} llui llinventory llmessage diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index ca57491af4..3c2c642f82 100644 --- a/indra/linux_crash_logger/CMakeLists.txt +++ b/indra/linux_crash_logger/CMakeLists.txt @@ -72,7 +72,7 @@ target_link_libraries(linux-crash-logger ${LLMATH_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${UI_LIBRARIES} ${DB_LIBRARIES} diff --git a/indra/llappearance/llwearabletype.cpp b/indra/llappearance/llwearabletype.cpp index 0d90444129..2640b5a428 100644 --- a/indra/llappearance/llwearabletype.cpp +++ b/indra/llappearance/llwearabletype.cpp @@ -32,7 +32,8 @@ struct WearableEntry : public LLDictionaryEntry { - WearableEntry(const std::string &name, + WearableEntry(LLWearableType& wtype, + const std::string &name, const std::string& default_new_name, LLAssetType::EType assetType, LLInventoryType::EIconName iconName, @@ -41,7 +42,7 @@ struct WearableEntry : public LLDictionaryEntry LLDictionaryEntry(name), mAssetType(assetType), mDefaultNewName(default_new_name), - mLabel(LLWearableType::getInstance()->mTrans->getString(name)), + mLabel(wtype.mTrans->getString(name)), mIconName(iconName), mDisableCameraSwitch(disable_camera_switch), mAllowMultiwear(allow_multiwear) @@ -56,10 +57,10 @@ struct WearableEntry : public LLDictionaryEntry BOOL mAllowMultiwear; }; -class LLWearableDictionary : public LLSingleton, +class LLWearableDictionary : public LLParamSingleton, public LLDictionary { - LLSINGLETON(LLWearableDictionary); + LLSINGLETON(LLWearableDictionary, LLWearableType&); // [RLVa:KB] - Checked: 2010-03-03 (RLVa-1.2.0a) | Added: RLVa-1.2.0a protected: @@ -68,38 +69,32 @@ protected: // [/RLVa:KB] }; -LLWearableDictionary::LLWearableDictionary() +LLWearableDictionary::LLWearableDictionary(LLWearableType& wtype) { - if (!LLWearableType::instanceExists()) - { - // LLWearableType is effectively a wrapper around LLWearableDictionary and is used as storage for LLTranslationBridge - // Todo: consider merging LLWearableType and LLWearableDictionary - LL_WARNS() << "Initing LLWearableDictionary without LLWearableType" << LL_ENDL; - } - addEntry(LLWearableType::WT_SHAPE, new WearableEntry("shape", "New Shape", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SHAPE, FALSE, FALSE)); - addEntry(LLWearableType::WT_SKIN, new WearableEntry("skin", "New Skin", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SKIN, FALSE, FALSE)); - addEntry(LLWearableType::WT_HAIR, new WearableEntry("hair", "New Hair", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_HAIR, FALSE, FALSE)); - addEntry(LLWearableType::WT_EYES, new WearableEntry("eyes", "New Eyes", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_EYES, FALSE, FALSE)); - addEntry(LLWearableType::WT_SHIRT, new WearableEntry("shirt", "New Shirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHIRT, FALSE, TRUE)); - addEntry(LLWearableType::WT_PANTS, new WearableEntry("pants", "New Pants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PANTS, FALSE, TRUE)); - addEntry(LLWearableType::WT_SHOES, new WearableEntry("shoes", "New Shoes", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHOES, FALSE, TRUE)); - addEntry(LLWearableType::WT_SOCKS, new WearableEntry("socks", "New Socks", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SOCKS, FALSE, TRUE)); - addEntry(LLWearableType::WT_JACKET, new WearableEntry("jacket", "New Jacket", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_JACKET, FALSE, TRUE)); - addEntry(LLWearableType::WT_GLOVES, new WearableEntry("gloves", "New Gloves", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_GLOVES, FALSE, TRUE)); - addEntry(LLWearableType::WT_UNDERSHIRT, new WearableEntry("undershirt", "New Undershirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERSHIRT, FALSE, TRUE)); - addEntry(LLWearableType::WT_UNDERPANTS, new WearableEntry("underpants", "New Underpants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERPANTS, FALSE, TRUE)); - addEntry(LLWearableType::WT_SKIRT, new WearableEntry("skirt", "New Skirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SKIRT, FALSE, TRUE)); - addEntry(LLWearableType::WT_ALPHA, new WearableEntry("alpha", "New Alpha", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_ALPHA, FALSE, TRUE)); - addEntry(LLWearableType::WT_TATTOO, new WearableEntry("tattoo", "New Tattoo", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_TATTOO, FALSE, TRUE)); - addEntry(LLWearableType::WT_UNIVERSAL, new WearableEntry("universal", "New Universal", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNIVERSAL, FALSE, TRUE)); + addEntry(LLWearableType::WT_SHAPE, new WearableEntry(wtype, "shape", "New Shape", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SHAPE, FALSE, FALSE)); + addEntry(LLWearableType::WT_SKIN, new WearableEntry(wtype, "skin", "New Skin", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SKIN, FALSE, FALSE)); + addEntry(LLWearableType::WT_HAIR, new WearableEntry(wtype, "hair", "New Hair", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_HAIR, FALSE, FALSE)); + addEntry(LLWearableType::WT_EYES, new WearableEntry(wtype, "eyes", "New Eyes", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_EYES, FALSE, FALSE)); + addEntry(LLWearableType::WT_SHIRT, new WearableEntry(wtype, "shirt", "New Shirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHIRT, FALSE, TRUE)); + addEntry(LLWearableType::WT_PANTS, new WearableEntry(wtype, "pants", "New Pants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PANTS, FALSE, TRUE)); + addEntry(LLWearableType::WT_SHOES, new WearableEntry(wtype, "shoes", "New Shoes", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHOES, FALSE, TRUE)); + addEntry(LLWearableType::WT_SOCKS, new WearableEntry(wtype, "socks", "New Socks", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SOCKS, FALSE, TRUE)); + addEntry(LLWearableType::WT_JACKET, new WearableEntry(wtype, "jacket", "New Jacket", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_JACKET, FALSE, TRUE)); + addEntry(LLWearableType::WT_GLOVES, new WearableEntry(wtype, "gloves", "New Gloves", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_GLOVES, FALSE, TRUE)); + addEntry(LLWearableType::WT_UNDERSHIRT, new WearableEntry(wtype, "undershirt", "New Undershirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERSHIRT, FALSE, TRUE)); + addEntry(LLWearableType::WT_UNDERPANTS, new WearableEntry(wtype, "underpants", "New Underpants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERPANTS, FALSE, TRUE)); + addEntry(LLWearableType::WT_SKIRT, new WearableEntry(wtype, "skirt", "New Skirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SKIRT, FALSE, TRUE)); + addEntry(LLWearableType::WT_ALPHA, new WearableEntry(wtype, "alpha", "New Alpha", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_ALPHA, FALSE, TRUE)); + addEntry(LLWearableType::WT_TATTOO, new WearableEntry(wtype, "tattoo", "New Tattoo", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_TATTOO, FALSE, TRUE)); + addEntry(LLWearableType::WT_UNIVERSAL, new WearableEntry(wtype, "universal", "New Universal", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNIVERSAL, FALSE, TRUE)); // [SL:KB] - Patch: Appearance-Misc | Checked: 2011-05-29 (Catznip-2.6) - addEntry(LLWearableType::WT_PHYSICS, new WearableEntry("physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, FALSE)); + addEntry(LLWearableType::WT_PHYSICS, new WearableEntry(wtype, "physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, FALSE)); // [/SL:KB] -// addEntry(LLWearableType::WT_PHYSICS, new WearableEntry("physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, TRUE)); +// addEntry(LLWearableType::WT_PHYSICS, new WearableEntry(wtype, "physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, TRUE)); - addEntry(LLWearableType::WT_INVALID, new WearableEntry("invalid", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_UNKNOWN, FALSE, FALSE)); - addEntry(LLWearableType::WT_NONE, new WearableEntry("none", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_NONE, FALSE, FALSE)); + addEntry(LLWearableType::WT_INVALID, new WearableEntry(wtype, "invalid", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_UNKNOWN, FALSE, FALSE)); + addEntry(LLWearableType::WT_NONE, new WearableEntry(wtype, "none", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_NONE, FALSE, FALSE)); } @@ -116,6 +111,14 @@ LLWearableType::~LLWearableType() delete mTrans; } +void LLWearableType::initSingleton() +{ + // To make sure all wrapping functions will crash without initing LLWearableType; + LLWearableDictionary::initParamSingleton(*this); + + // Todo: consider merging LLWearableType and LLWearableDictionary +} + // static LLWearableType::EType LLWearableType::typeNameToType(const std::string& type_name) { diff --git a/indra/llappearance/llwearabletype.h b/indra/llappearance/llwearabletype.h index 148ccafdd8..57f3ef160d 100644 --- a/indra/llappearance/llwearabletype.h +++ b/indra/llappearance/llwearabletype.h @@ -37,6 +37,7 @@ class LLWearableType : public LLParamSingleton { LLSINGLETON(LLWearableType, LLTranslationBridge* trans); ~LLWearableType(); + void initSingleton(); friend struct WearableEntry; public: enum EType diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index aca6d72991..b75e4ae704 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -1,4 +1,3 @@ - # -*- cmake -*- project(llcommon) @@ -44,7 +43,6 @@ set(llcommon_SOURCE_FILES llcleanup.cpp llcommon.cpp llcommonutils.cpp - llcoro_get_id.cpp llcoros.cpp llcrc.cpp llcriticaldamp.cpp @@ -106,6 +104,7 @@ set(llcommon_SOURCE_FILES llstring.cpp llstringtable.cpp llsys.cpp + lltempredirect.cpp llthread.cpp llthreadlocalstorage.cpp llthreadsafequeue.cpp @@ -146,7 +145,7 @@ set(llcommon_HEADER_FILES llcleanup.h llcommon.h llcommonutils.h - llcoro_get_id.h + llcond.h llcoros.h llcrc.h llcriticaldamp.h @@ -186,9 +185,9 @@ set(llcommon_HEADER_FILES llkeythrottle.h llleap.h llleaplistener.h - lllistenerwrapper.h llliveappconfig.h lllivefile.h + llmainthreadtask.h llmd5.h llmemory.h llmemorystream.h @@ -230,6 +229,7 @@ set(llcommon_HEADER_FILES llstaticstringtable.h llstatsaccumulator.h llsys.h + lltempredirect.h llthread.h llthreadlocalstorage.h llthreadsafequeue.h @@ -247,6 +247,7 @@ set(llcommon_HEADER_FILES llwin32headers.h llwin32headerslean.h llworkerthread.h + lockstatic.h stdtypes.h stringize.h timer.h @@ -327,7 +328,7 @@ target_link_libraries( ${JSONCPP_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} @@ -358,13 +359,14 @@ if (LL_TESTS) ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_THREAD_LIBRARY} ${BOOST_SYSTEM_LIBRARY}) LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}") + LL_ADD_INTEGRATION_TEST(llcond "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}") @@ -376,6 +378,7 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") + LL_ADD_INTEGRATION_TEST(llmainthreadtask "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}") diff --git a/indra/llcommon/StackWalker.cpp b/indra/llcommon/StackWalker.cpp index c0d3104099..56defc6465 100644 --- a/indra/llcommon/StackWalker.cpp +++ b/indra/llcommon/StackWalker.cpp @@ -98,7 +98,10 @@ // If VC7 and later, then use the shipped 'dbghelp.h'-file #pragma pack(push,8) #if _MSC_VER >= 1300 +#pragma warning (push) +#pragma warning (disable:4091) // a microsoft header has warnings. Very nice. #include +#pragma warning (pop) #else // inline the important dbghelp.h-declarations... typedef enum { @@ -422,7 +425,7 @@ public: LPSTR m_szSymPath; #pragma pack(push,8) -typedef struct IMAGEHLP_MODULE64_V3 { +struct IMAGEHLP_MODULE64_V3 { DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) DWORD64 BaseOfImage; // base load address of module DWORD ImageSize; // virtual size of the loaded module @@ -450,7 +453,7 @@ typedef struct IMAGEHLP_MODULE64_V3 { BOOL Publics; // contains public symbols }; -typedef struct IMAGEHLP_MODULE64_V2 { +struct IMAGEHLP_MODULE64_V2 { DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) DWORD64 BaseOfImage; // base load address of module DWORD ImageSize; // virtual size of the loaded module @@ -657,7 +660,7 @@ private: pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" ); if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) ) { - // we couldn´t find all functions + // we couldn't find all functions FreeLibrary(hPsapi); return FALSE; } diff --git a/indra/llcommon/StackWalker.h b/indra/llcommon/StackWalker.h index 834f89c471..4634765d0b 100644 --- a/indra/llcommon/StackWalker.h +++ b/indra/llcommon/StackWalker.h @@ -148,7 +148,7 @@ protected: CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; } CallstackEntry; - typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; + enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 3d2081ca25..887e23e92e 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -47,13 +47,12 @@ #include "llstring.h" #include "lleventtimer.h" -#if defined(LL_LINUX) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 26 - #define ucontext ucontext_t -#endif - #include "google_breakpad/exception_handler.h" + #include "stringize.h" #include "llcleanup.h" +#include "llevents.h" +#include "llsdutil.h" // // Signal handling @@ -567,10 +566,42 @@ void LLApp::runErrorHandler() LLApp::setStopped(); } +namespace +{ + +static std::map statusDesc +{ + { LLApp::APP_STATUS_RUNNING, "running" }, + { LLApp::APP_STATUS_QUITTING, "quitting" }, + { LLApp::APP_STATUS_STOPPED, "stopped" }, + { LLApp::APP_STATUS_ERROR, "error" } +}; + +} // anonymous namespace + // static void LLApp::setStatus(EAppStatus status) { - sStatus = status; + sStatus = status; + + // This can also happen very late in the application lifecycle -- don't + // resurrect a deleted LLSingleton + if (! LLEventPumps::wasDeleted()) + { + // notify interested parties of status change + LLSD statsd; + auto found = statusDesc.find(status); + if (found != statusDesc.end()) + { + statsd = found->second; + } + else + { + // unknown status? at least report value + statsd = LLSD::Integer(status); + } + LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", statsd)); + } } diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index da92b36212..616dcb03d8 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -41,17 +41,7 @@ #include "llstring.h" -#if LL_WINDOWS -#pragma warning (push) -#pragma warning (disable:4265) -#endif -// warning C4265: 'std::_Pad' : class has virtual functions, but destructor is not virtual - -#include - -#if LL_WINDOWS -#pragma warning (pop) -#endif +#include "mutex.h" struct apr_dso_handle_t; /** diff --git a/indra/llcommon/llatomic.cpp b/indra/llcommon/llatomic.cpp index 1b344df400..93aba1f460 100644 --- a/indra/llcommon/llatomic.cpp +++ b/indra/llcommon/llatomic.cpp @@ -23,7 +23,6 @@ * $/LicenseInfo$ */ -// Fix LNK4221 compiler warning -//#include "llatomic.h" +#include "llatomic.h" //============================================================================ diff --git a/indra/llcommon/llbitpack.cpp b/indra/llcommon/llbitpack.cpp index f0b75aa3be..2dd0f9efe6 100644 --- a/indra/llcommon/llbitpack.cpp +++ b/indra/llcommon/llbitpack.cpp @@ -25,7 +25,7 @@ */ // Get rid of LNK4221 linker warning since we don't run the unit tests anyway -//#include "linden_common.h" +#include "linden_common.h" // implementation is all in the header, this include dep ensures the unit test is rerun if the implementation changes. -//#include "llbitpack.h" +#include "llbitpack.h" diff --git a/indra/llcommon/llcond.h b/indra/llcommon/llcond.h new file mode 100644 index 0000000000..e31b67d893 --- /dev/null +++ b/indra/llcommon/llcond.h @@ -0,0 +1,405 @@ +/** + * @file llcond.h + * @author Nat Goodspeed + * @date 2019-07-10 + * @brief LLCond is a wrapper around condition_variable to encapsulate the + * obligatory condition_variable usage pattern. We also provide + * simplified versions LLScalarCond, LLBoolCond and LLOneShotCond. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLCOND_H) +#define LL_LLCOND_H + +#include "llunits.h" +#include "llcoros.h" +#include LLCOROS_MUTEX_HEADER +#include "mutex.h" +#include + +/** + * LLCond encapsulates the pattern required to use a condition_variable. It + * bundles subject data, a mutex and a condition_variable: the three required + * data objects. It provides wait() methods analogous to condition_variable, + * but using the contained condition_variable and the contained mutex. It + * provides modify() methods accepting an invocable to safely modify the + * contained data and notify waiters. These methods implicitly perform the + * required locking. + * + * The generic LLCond template assumes that DATA might be a struct or class. + * For a scalar DATA type, consider LLScalarCond instead. For specifically + * bool, consider LLBoolCond. + * + * Use of LLCoros::ConditionVariable makes LLCond work between + * coroutines as well as between threads. + */ +template +class LLCond +{ +public: + typedef DATA value_type; + +private: + // This is the DATA controlled by the condition_variable. + value_type mData; + // condition_variable must be used in conjunction with a mutex. Use + // LLCoros::Mutex instead of std::mutex because the latter blocks + // the entire calling thread, whereas the former blocks only the current + // coroutine within the calling thread. Yet LLCoros::Mutex is safe to + // use across threads as well: it subsumes std::mutex functionality. + LLCoros::Mutex mMutex; + // Use LLCoros::ConditionVariable for the same reason. + LLCoros::ConditionVariable mCond; + +public: + /// LLCond can be explicitly initialized with a specific value for mData if + /// desired. + LLCond(const value_type& init=value_type()): + mData(init) + {} + + /// LLCond is move-only + LLCond(const LLCond&) = delete; + LLCond& operator=(const LLCond&) = delete; + + /// get() returns a const reference to the stored DATA. The only way to + /// get a non-const reference -- to modify the stored DATA -- is via + /// update_one() or update_all(). + const value_type& get() const { return mData; } + + /** + * Pass update_one() an invocable accepting non-const (DATA&). The + * invocable will presumably modify the referenced DATA. update_one() + * will lock the mutex, call the invocable and then call notify_one() on + * the condition_variable. + * + * For scalar DATA, it's simpler to use LLScalarCond::set_one(). Use + * update_one() when DATA is a struct or class. + */ + template + void update_one(MODIFY modify) + { + { // scope of lock can/should end before notify_one() + LLCoros::LockType lk(mMutex); + modify(mData); + } + mCond.notify_one(); + } + + /** + * Pass update_all() an invocable accepting non-const (DATA&). The + * invocable will presumably modify the referenced DATA. update_all() + * will lock the mutex, call the invocable and then call notify_all() on + * the condition_variable. + * + * For scalar DATA, it's simpler to use LLScalarCond::set_all(). Use + * update_all() when DATA is a struct or class. + */ + template + void update_all(MODIFY modify) + { + { // scope of lock can/should end before notify_all() + LLCoros::LockType lk(mMutex); + modify(mData); + } + mCond.notify_all(); + } + + /** + * Pass wait() a predicate accepting (const DATA&), returning bool. The + * predicate returns true when the condition for which it is waiting has + * been satisfied, presumably determined by examining the referenced DATA. + * wait() locks the mutex and, until the predicate returns true, calls + * wait() on the condition_variable. + */ + template + void wait(Pred pred) + { + LLCoros::LockType lk(mMutex); + // We must iterate explicitly since the predicate accepted by + // condition_variable::wait() requires a different signature: + // condition_variable::wait() calls its predicate with no arguments. + // Fortunately, the loop is straightforward. + // We advise the caller to pass a predicate accepting (const DATA&). + // But what if they instead pass a predicate accepting non-const + // (DATA&)? Such a predicate could modify mData, which would be Bad. + // Forbid that. + while (! pred(const_cast(mData))) + { + mCond.wait(lk); + } + } + + /** + * Pass wait_for() a chrono::duration, indicating how long we're willing + * to wait, and a predicate accepting (const DATA&), returning bool. The + * predicate returns true when the condition for which it is waiting has + * been satisfied, presumably determined by examining the referenced DATA. + * wait_for() locks the mutex and, until the predicate returns true, calls + * wait_for() on the condition_variable. wait_for() returns false if + * condition_variable::wait_for() timed out without the predicate + * returning true. + */ + template + bool wait_for(const std::chrono::duration& timeout_duration, Pred pred) + { + // Instead of replicating wait_until() logic, convert duration to + // time_point and just call wait_until(). + // An implementation in which we repeatedly called + // condition_variable::wait_for() with our passed duration would be + // wrong! We'd keep pushing the timeout time farther and farther into + // the future. This way, we establish a definite timeout time and + // stick to it. + return wait_until(std::chrono::steady_clock::now() + timeout_duration, pred); + } + + /** + * This wait_for() overload accepts F32Milliseconds as the duration. Any + * duration unit defined in llunits.h is implicitly convertible to + * F32Milliseconds. The semantics of this method are the same as the + * generic wait_for() method. + */ + template + bool wait_for(F32Milliseconds timeout_duration, Pred pred) + { + return wait_for(convert(timeout_duration), pred); + } + +protected: + // convert F32Milliseconds to a chrono::duration + auto convert(F32Milliseconds duration) + { + // std::chrono::milliseconds doesn't like to be constructed from a + // float (F32), rubbing our nose in the thought that + // std::chrono::duration::rep is probably integral. Therefore + // converting F32Milliseconds to std::chrono::milliseconds would lose + // precision. Use std::chrono::microseconds instead. Extract the F32 + // milliseconds from F32Milliseconds, scale to microseconds, construct + // std::chrono::microseconds from that value. + return std::chrono::microseconds{ std::chrono::microseconds::rep(duration.value() * 1000) }; + } + +private: + /** + * Pass wait_until() a chrono::time_point, indicating the time at which we + * should stop waiting, and a predicate accepting (const DATA&), returning + * bool. The predicate returns true when the condition for which it is + * waiting has been satisfied, presumably determined by examining the + * referenced DATA. wait_until() locks the mutex and, until the predicate + * returns true, calls wait_until() on the condition_variable. + * wait_until() returns false if condition_variable::wait_until() timed + * out without the predicate returning true. + * + * Originally this class and its subclasses published wait_until() methods + * corresponding to each wait_for() method. But that raised all sorts of + * fascinating questions about the time zone of the passed time_point: + * local time? server time? UTC? The bottom line is that for LLCond + * timeout purposes, we really shouldn't have to care -- timeout duration + * is all we need. This private method remains because it's the simplest + * way to support iteratively waiting across spurious wakeups while + * honoring a fixed timeout. + */ + template + bool wait_until(const std::chrono::time_point& timeout_time, Pred pred) + { + LLCoros::LockType lk(mMutex); + // We advise the caller to pass a predicate accepting (const DATA&). + // But what if they instead pass a predicate accepting non-const + // (DATA&)? Such a predicate could modify mData, which would be Bad. + // Forbid that. + while (! pred(const_cast(mData))) + { + if (LLCoros::cv_status::timeout == mCond.wait_until(lk, timeout_time)) + { + // It's possible that wait_until() timed out AND the predicate + // became true more or less simultaneously. Even though + // wait_until() timed out, check the predicate one more time. + return pred(const_cast(mData)); + } + } + return true; + } +}; + +template +class LLScalarCond: public LLCond +{ + using super = LLCond; + +public: + using typename super::value_type; + using super::get; + using super::wait; + using super::wait_for; + + /// LLScalarCond can be explicitly initialized with a specific value for + /// mData if desired. + LLScalarCond(const value_type& init=value_type()): + super(init) + {} + + /// Pass set_one() a new value to which to update mData. set_one() will + /// lock the mutex, update mData and then call notify_one() on the + /// condition_variable. + void set_one(const value_type& value) + { + super::update_one([&value](value_type& data){ data = value; }); + } + + /// Pass set_all() a new value to which to update mData. set_all() will + /// lock the mutex, update mData and then call notify_all() on the + /// condition_variable. + void set_all(const value_type& value) + { + super::update_all([&value](value_type& data){ data = value; }); + } + + /** + * Pass wait_equal() a value for which to wait. wait_equal() locks the + * mutex and, until the stored DATA equals that value, calls wait() on the + * condition_variable. + */ + void wait_equal(const value_type& value) + { + super::wait([&value](const value_type& data){ return (data == value); }); + } + + /** + * Pass wait_for_equal() a chrono::duration, indicating how long we're + * willing to wait, and a value for which to wait. wait_for_equal() locks + * the mutex and, until the stored DATA equals that value, calls + * wait_for() on the condition_variable. wait_for_equal() returns false if + * condition_variable::wait_for() timed out without the stored DATA being + * equal to the passed value. + */ + template + bool wait_for_equal(const std::chrono::duration& timeout_duration, + const value_type& value) + { + return super::wait_for(timeout_duration, + [&value](const value_type& data){ return (data == value); }); + } + + /** + * This wait_for_equal() overload accepts F32Milliseconds as the duration. + * Any duration unit defined in llunits.h is implicitly convertible to + * F32Milliseconds. The semantics of this method are the same as the + * generic wait_for_equal() method. + */ + bool wait_for_equal(F32Milliseconds timeout_duration, const value_type& value) + { + return wait_for_equal(super::convert(timeout_duration), value); + } + + /** + * Pass wait_unequal() a value from which to move away. wait_unequal() + * locks the mutex and, until the stored DATA no longer equals that value, + * calls wait() on the condition_variable. + */ + void wait_unequal(const value_type& value) + { + super::wait([&value](const value_type& data){ return (data != value); }); + } + + /** + * Pass wait_for_unequal() a chrono::duration, indicating how long we're + * willing to wait, and a value from which to move away. + * wait_for_unequal() locks the mutex and, until the stored DATA no longer + * equals that value, calls wait_for() on the condition_variable. + * wait_for_unequal() returns false if condition_variable::wait_for() + * timed out with the stored DATA still being equal to the passed value. + */ + template + bool wait_for_unequal(const std::chrono::duration& timeout_duration, + const value_type& value) + { + return super::wait_for(timeout_duration, + [&value](const value_type& data){ return (data != value); }); + } + + /** + * This wait_for_unequal() overload accepts F32Milliseconds as the duration. + * Any duration unit defined in llunits.h is implicitly convertible to + * F32Milliseconds. The semantics of this method are the same as the + * generic wait_for_unequal() method. + */ + bool wait_for_unequal(F32Milliseconds timeout_duration, const value_type& value) + { + return wait_for_unequal(super::convert(timeout_duration), value); + } + +protected: + using super::convert; +}; + +/// Using bool as LLScalarCond's DATA seems like a particularly useful case +using LLBoolCond = LLScalarCond; + +/// LLOneShotCond -- init false, set (and wait for) true +class LLOneShotCond: public LLBoolCond +{ + using super = LLBoolCond; + +public: + using typename super::value_type; + using super::get; + using super::wait; + using super::wait_for; + using super::wait_equal; + using super::wait_for_equal; + using super::wait_unequal; + using super::wait_for_unequal; + + /// The bool stored in LLOneShotCond is initially false + LLOneShotCond(): super(false) {} + + /// LLOneShotCond assumes that nullary set_one() means to set its bool true + void set_one(bool value=true) + { + super::set_one(value); + } + + /// LLOneShotCond assumes that nullary set_all() means to set its bool true + void set_all(bool value=true) + { + super::set_all(value); + } + + /** + * wait() locks the mutex and, until the stored bool is true, calls wait() + * on the condition_variable. + */ + void wait() + { + super::wait_unequal(false); + } + + /** + * Pass wait_for() a chrono::duration, indicating how long we're willing + * to wait. wait_for() locks the mutex and, until the stored bool is true, + * calls wait_for() on the condition_variable. wait_for() returns false if + * condition_variable::wait_for() timed out without the stored bool being + * true. + */ + template + bool wait_for(const std::chrono::duration& timeout_duration) + { + return super::wait_for_unequal(timeout_duration, false); + } + + /** + * This wait_for() overload accepts F32Milliseconds as the duration. + * Any duration unit defined in llunits.h is implicitly convertible to + * F32Milliseconds. The semantics of this method are the same as the + * generic wait_for() method. + */ + bool wait_for(F32Milliseconds timeout_duration) + { + return wait_for(super::convert(timeout_duration)); + } +}; + +#endif /* ! defined(LL_LLCOND_H) */ diff --git a/indra/llcommon/llcoro_get_id.cpp b/indra/llcommon/llcoro_get_id.cpp deleted file mode 100644 index 24ed1fe0c9..0000000000 --- a/indra/llcommon/llcoro_get_id.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file llcoro_get_id.cpp - * @author Nat Goodspeed - * @date 2016-09-03 - * @brief Implementation for llcoro_get_id. - * - * $LicenseInfo:firstyear=2016&license=viewerlgpl$ - * Copyright (c) 2016, Linden Research, Inc. - * $/LicenseInfo$ - */ - -// Precompiled header -#include "linden_common.h" -// associated header -#include "llcoro_get_id.h" -// STL headers -// std headers -// external library headers -// other Linden headers -#include "llcoros.h" - -namespace llcoro -{ - -id get_id() -{ - // An instance of Current can convert to LLCoros::CoroData*, which can - // implicitly convert to void*, which is an llcoro::id. - return LLCoros::Current(); -} - -} // llcoro diff --git a/indra/llcommon/llcoro_get_id.h b/indra/llcommon/llcoro_get_id.h deleted file mode 100644 index 4c1dca6f19..0000000000 --- a/indra/llcommon/llcoro_get_id.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file llcoro_get_id.h - * @author Nat Goodspeed - * @date 2016-09-03 - * @brief Supplement the functionality in llcoro.h. - * - * This is broken out as a separate header file to resolve - * circularity: LLCoros isa LLSingleton, yet LLSingleton machinery - * requires llcoro::get_id(). - * - * Be very suspicious of anyone else #including this header. - * - * $LicenseInfo:firstyear=2016&license=viewerlgpl$ - * Copyright (c) 2016, Linden Research, Inc. - * $/LicenseInfo$ - */ - -#if ! defined(LL_LLCORO_GET_ID_H) -#define LL_LLCORO_GET_ID_H - -namespace llcoro -{ - -/// Get an opaque, distinct token for the running coroutine (or main). -typedef void* id; -id get_id(); - -} // llcoro - -#endif /* ! defined(LL_LLCORO_GET_ID_H) */ diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 11b33e573c..2f1ca791ed 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -26,15 +26,30 @@ * $/LicenseInfo$ */ +#include "llwin32headers.h" + // Precompiled header #include "linden_common.h" // associated header #include "llcoros.h" // STL headers // std headers +#include // external library headers #include +#include +#ifndef BOOST_DISABLE_ASSERTS +#define UNDO_BOOST_DISABLE_ASSERTS +// with Boost 1.65.1, needed for Mac with this specific header +#define BOOST_DISABLE_ASSERTS +#endif +#include +#ifdef UNDO_BOOST_DISABLE_ASSERTS +#undef UNDO_BOOST_DISABLE_ASSERTS +#undef BOOST_DISABLE_ASSERTS +#endif // other Linden headers +#include "llapp.h" #include "lltimer.h" #include "llevents.h" #include "llerror.h" @@ -45,85 +60,43 @@ #include #endif -namespace { -void no_op() {} -} // anonymous namespace - -// Do nothing, when we need nothing done. This is a static member of LLCoros -// because CoroData is a private nested class. -void LLCoros::no_cleanup(CoroData*) {} - -// CoroData for the currently-running coroutine. Use a thread_specific_ptr -// because each thread potentially has its own distinct pool of coroutines. -LLCoros::Current::Current() -{ - // Use a function-static instance so this thread_specific_ptr is - // instantiated on demand. Since we happen to know it's consumed by - // LLSingleton, this is likely to happen before the runtime has finished - // initializing module-static data. For the same reason, we can't package - // this pointer in an LLSingleton. - - // This thread_specific_ptr does NOT own the CoroData object! That's owned - // by LLCoros::mCoros. It merely identifies it. For this reason we - // instantiate it with a no-op cleanup function. - static boost::thread_specific_ptr sCurrent(LLCoros::no_cleanup); - - // If this is the first time we're accessing sCurrent for the running - // thread, its get() will be NULL. This could be a problem, in that - // llcoro::get_id() would return the same (NULL) token value for the "main - // coroutine" in every thread, whereas what we really want is a distinct - // value for every distinct stack in the process. So if get() is NULL, - // give it a heap CoroData: this ensures that llcoro::get_id() will return - // distinct values. - // This tactic is "leaky": sCurrent explicitly does not destroy any - // CoroData to which it points, and we do NOT enter these "main coroutine" - // CoroData instances in the LLCoros::mCoros map. They are dummy entries, - // and they will leak at process shutdown: one CoroData per thread. - if (! sCurrent.get()) - { - // It's tempting to provide a distinct name for each thread's "main - // coroutine." But as getName() has always returned the empty string - // to mean "not in a coroutine," empty string should suffice here -- - // and truthfully the additional (thread-safe!) machinery to ensure - // uniqueness just doesn't feel worth the trouble. - // We use a no-op callable and a minimal stack size because, although - // CoroData's constructor in fact initializes its mCoro with a - // coroutine with that stack size, no one ever actually enters it by - // calling mCoro(). - sCurrent.reset(new CoroData(0, // no prev - "", // not a named coroutine - no_op, // no-op callable - 1024)); // stacksize moot - } - - mCurrent = &sCurrent; -} - -//static +// static LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) { - CoroData* current = Current(); - // With the dummy CoroData set in LLCoros::Current::Current(), this - // pointer should never be NULL. - llassert_always(current); + CoroData* current{ nullptr }; + // be careful about attempted accesses in the final throes of app shutdown + if (! wasDeleted()) + { + current = instance().mCurrent.get(); + } + // For the main() coroutine, the one NOT explicitly launched by launch(), + // we never explicitly set mCurrent. Use a static CoroData instance with + // canonical values. + if (! current) + { + static std::atomic which_thread(0); + // Use alternate CoroData constructor. + static thread_local CoroData sMain(which_thread++); + // We need not reset() the local_ptr to this instance; we'll simply + // find it again every time we discover that current is null. + current = &sMain; + } return *current; } //static -LLCoros::coro::self& LLCoros::get_self() +LLCoros::coro::id LLCoros::get_self() { - CoroData& current = get_CoroData("get_self()"); - if (! current.mSelf) - { - LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; - } - return *current.mSelf; + return boost::this_fiber::get_id(); } //static void LLCoros::set_consuming(bool consuming) { - get_CoroData("set_consuming()").mConsuming = consuming; + CoroData& data(get_CoroData("set_consuming()")); + // DO NOT call this on the main() coroutine. + llassert_always(! data.mName.empty()); + data.mConsuming = consuming; } //static @@ -132,89 +105,58 @@ bool LLCoros::get_consuming() return get_CoroData("get_consuming()").mConsuming; } -llcoro::Suspending::Suspending() +// static +void LLCoros::setStatus(const std::string& status) { - LLCoros::Current current; - // Remember currently-running coroutine: we're about to suspend it. - mSuspended = current; - // Revert Current to the value it had at the moment we last switched - // into this coroutine. - current.reset(mSuspended->mPrev); + get_CoroData("setStatus()").mStatus = status; } -llcoro::Suspending::~Suspending() +// static +std::string LLCoros::getStatus() { - LLCoros::Current current; - // Okay, we're back, update our mPrev - mSuspended->mPrev = current; - // and reinstate our Current. - current.reset(mSuspended); + return get_CoroData("getStatus()").mStatus; } - LLCoros::LLCoros(): // MAINT-2724: default coroutine stack size too small on Windows. // Previously we used // boost::context::guarded_stack_allocator::default_stacksize(); - // empirically this is 64KB on Windows and Linux. Try quadrupling. + // empirically this is insufficient. #if ADDRESS_SIZE == 64 - mStackSize(512*1024) + mStackSize(512*1024), #else - mStackSize(256*1024) + mStackSize(256*1024), #endif + // mCurrent does NOT own the current CoroData instance -- it simply + // points to it. So initialize it with a no-op deleter. + mCurrent{ [](CoroData*){} } { - // Register our cleanup() method for "mainloop" ticks - LLEventPumps::instance().obtain("mainloop").listen( - "LLCoros", boost::bind(&LLCoros::cleanup, this, _1)); } -bool LLCoros::cleanup(const LLSD&) +LLCoros::~LLCoros() { - static std::string previousName; - static int previousCount = 0; - // Walk the mCoros map, checking and removing completed coroutines. - for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ) + printActiveCoroutines("at entry to ~LLCoros()"); + // Other LLApp status-change listeners do things like close + // work queues and inject the Stop exception into pending + // promises, to force coroutines waiting on those things to + // notice and terminate. The only problem is that by the time + // LLApp sets "quitting" status, the main loop has stopped + // pumping the fiber scheduler with yield() calls. A waiting + // coroutine still might not wake up until after resources on + // which it depends have been freed. Pump it a few times + // ourselves. Of course, stop pumping as soon as the last of + // the coroutines has terminated. + for (size_t count = 0; count < 10 && CoroData::instanceCount() > 0; ++count) { - // Has this coroutine exited (normal return, exception, exit() call) - // since last tick? - if (mi->second->mCoro.exited()) - { - if (previousName != mi->first) - { - previousName = mi->first; - previousCount = 1; - } - else - { - ++previousCount; - } - - if ((previousCount < 5) || !(previousCount % 50)) - { - if (previousCount < 5) - LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; - else - LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << 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; - } + // don't use llcoro::suspend() because that module depends + // on this one + boost::this_fiber::yield(); } - return false; + printActiveCoroutines("after pumping"); } std::string LLCoros::generateDistinctName(const std::string& prefix) const { - static std::string previousName; - static int previousCount = 0; + static int unique = 0; // Allowing empty name would make getName()'s not-found return ambiguous. if (prefix.empty()) @@ -225,37 +167,15 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const // 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++)) + // Until we find an unused name, append a numeric suffix for uniqueness. + while (CoroData::getInstance(name)) { - if (mCoros.find(name) == mCoros.end()) - { - if (previousName != name) - { - previousName = name; - previousCount = 1; - } - else - { - ++previousCount; - } - - if ((previousCount < 5) || !(previousCount % 50)) - { - if (previousCount < 5) - LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; - else - LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; - - } - - return name; - } + name = STRINGIZE(prefix << unique++); } + return name; } +/*==========================================================================*| bool LLCoros::kill(const std::string& name) { CoroMap::iterator found = mCoros.find(name); @@ -269,10 +189,19 @@ bool LLCoros::kill(const std::string& name) mCoros.erase(found); return true; } +|*==========================================================================*/ -std::string LLCoros::getName() const +//static +std::string LLCoros::getName() { - return Current()->mName; + return get_CoroData("getName()").mName; +} + +//static +std::string LLCoros::logname() +{ + LLCoros::CoroData& data(get_CoroData("logname()")); + return data.mName.empty()? data.getKey() : data.mName; } void LLCoros::setStackSize(S32 stacksize) @@ -281,25 +210,46 @@ void LLCoros::setStackSize(S32 stacksize) mStackSize = stacksize; } -void LLCoros::printActiveCoroutines() +void LLCoros::printActiveCoroutines(const std::string& when) { - LL_INFOS("LLCoros") << "Number of active coroutines: " << (S32)mCoros.size() << LL_ENDL; - if (mCoros.size() > 0) + LL_INFOS("LLCoros") << "Number of active coroutines " << when + << ": " << CoroData::instanceCount() << LL_ENDL; + if (CoroData::instanceCount() > 0) { LL_INFOS("LLCoros") << "-------------- List of active coroutines ------------"; - CoroMap::iterator iter; - CoroMap::iterator end = mCoros.end(); F64 time = LLTimer::getTotalSeconds(); - for (iter = mCoros.begin(); iter != end; iter++) + for (auto& cd : CoroData::instance_snapshot()) { - F64 life_time = time - iter->second->mCreationTime; - LL_CONT << LL_NEWLINE << "Name: " << iter->first << " life: " << life_time; + F64 life_time = time - cd.mCreationTime; + LL_CONT << LL_NEWLINE + << cd.getKey() << ' ' << cd.mStatus << " life: " << life_time; } LL_CONT << LL_ENDL; LL_INFOS("LLCoros") << "-----------------------------------------------------" << LL_ENDL; } } +std::string LLCoros::launch(const std::string& prefix, const callable_t& callable) +{ + std::string name(generateDistinctName(prefix)); + // 'dispatch' means: enter the new fiber immediately, returning here only + // when the fiber yields for whatever reason. + // std::allocator_arg is a flag to indicate that the following argument is + // a StackAllocator. + // protected_fixedsize_stack sets a guard page past the end of the new + // stack so that stack underflow will result in an access violation + // instead of weird, subtle, possibly undiagnosed memory stomps. + boost::fibers::fiber newCoro(boost::fibers::launch::dispatch, + std::allocator_arg, + boost::fibers::protected_fixedsize_stack(mStackSize), + [this, &name, &callable](){ toplevel(name, callable); }); + // You have two choices with a fiber instance: you can join() it or you + // can detach() it. If you try to destroy the instance before doing + // either, the program silently terminates. We don't need this handle. + newCoro.detach(); + return name; +} + #if LL_WINDOWS static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific @@ -337,13 +287,16 @@ void LLCoros::winlevel(const callable_t& callable) #endif -// Top-level wrapper around caller's coroutine callable. This function accepts -// the coroutine library's implicit coro::self& parameter and saves it, but -// does not pass it down to the caller's callable. -void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable) +// Top-level wrapper around caller's coroutine callable. +// Normally we like to pass strings and such by const reference -- but in this +// case, we WANT to copy both the name and the callable to our local stack! +void LLCoros::toplevel(std::string name, callable_t callable) { - // capture the 'self' param in CoroData - data->mSelf = &self; + // keep the CoroData on this top-level function's stack frame + CoroData corodata(name); + // set it as current + mCurrent.reset(&corodata); + // run the code the caller actually wants in the coroutine try { @@ -351,81 +304,73 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla //#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD // winlevel(callable); //#else -// callable(); -//#endif // Disable for more meaningful callstacks +//#endif +// Disable for more meaningful callstacks + } + catch (const Stop& exc) + { + LL_INFOS("LLCoros") << "coroutine " << name << " terminating because " + << exc.what() << LL_ENDL; } catch (const LLContinueError&) { // Any uncaught exception derived from LLContinueError will be caught // here and logged. This coroutine will terminate but the rest of the // viewer will carry on. - LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name)); + } + catch (...) + { + // Any OTHER kind of uncaught exception will cause the viewer to + // crash, hopefully informatively. + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name)); } - // Disable for more meaningful callstacks - //catch (...) - //{ - // // Any OTHER kind of uncaught exception will cause the viewer to - // // crash, hopefully informatively. - // CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); - //} - // - // This cleanup isn't perfectly symmetrical with the way we initially set - // data->mPrev, but this is our last chance to reset Current. - Current().reset(data->mPrev); } -/***************************************************************************** -* MUST BE LAST -*****************************************************************************/ -// Turn off MSVC optimizations for just LLCoros::launch() -- 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. +//static +void LLCoros::checkStop() +{ + if (wasDeleted()) + { + LLTHROW(Shutdown("LLCoros was deleted")); + } + // do this AFTER the check above, because getName() depends on + // get_CoroData(), which depends on the local_ptr in our instance(). + if (getName().empty()) + { + // Our Stop exception and its subclasses are intended to stop loitering + // coroutines. Don't throw it from the main coroutine. + return; + } + if (LLApp::isStopped()) + { + LLTHROW(Stopped("viewer is stopped")); + } + if (! LLApp::isRunning()) + { + LLTHROW(Stopping("viewer is stopping")); + } +} -#if LL_MSVC -// work around broken optimizations -#pragma warning(disable: 4748) -#pragma warning(disable: 4355) // 'this' used in initializer list: yes, intentionally -#pragma optimize("", off) -#endif // LL_MSVC - -LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name, - const callable_t& callable, S32 stacksize): - mPrev(prev), +LLCoros::CoroData::CoroData(const std::string& name): + LLInstanceTracker(name), mName(name), - // Wrap the caller's callable in our toplevel() function so we can manage - // Current appropriately at startup and shutdown of each coroutine. - mCoro(boost::bind(toplevel, _1, this, callable), stacksize), // don't consume events unless specifically directed mConsuming(false), - mSelf(0), mCreationTime(LLTimer::getTotalSeconds()) { } -std::string LLCoros::launch(const std::string& prefix, const callable_t& callable) +LLCoros::CoroData::CoroData(int n): + // This constructor is used for the thread_local instance belonging to the + // default coroutine on each thread. We must give each one a different + // LLInstanceTracker key because LLInstanceTracker's map spans all + // threads, but we want the default coroutine on each thread to have the + // empty string as its visible name because some consumers test for that. + LLInstanceTracker("main" + stringize(n)), + mName(), + mConsuming(false), + mCreationTime(LLTimer::getTotalSeconds()) { - std::string name(generateDistinctName(prefix)); - Current current; - // pass the current value of Current as previous context - CoroData* newCoro = new(std::nothrow) CoroData(current, name, callable, mStackSize); - if (newCoro == NULL) - { - // Out of memory? - printActiveCoroutines(); - LL_ERRS("LLCoros") << "Failed to start coroutine: " << name << " Stacksize: " << mStackSize << " Total coroutines: " << mCoros.size() << LL_ENDL; - } - // Store it in our pointer map - mCoros.insert(name, newCoro); - // also set it as current - current.reset(newCoro); - /* Run the coroutine until its first wait, then return here */ - (newCoro->mCoro)(std::nothrow); - return name; } - -#if LL_MSVC -// reenable optimizations -#pragma optimize("", on) -#endif // LL_MSVC diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index c551413811..38c2356c99 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -29,21 +29,26 @@ #if ! defined(LL_LLCOROS_H) #define LL_LLCOROS_H -#include -#include +#include "llexception.h" +#include +#include +#include +#include "mutex.h" #include "llsingleton.h" -#include +#include "llinstancetracker.h" #include -#include -#include #include -#include -#include "llcoro_get_id.h" // for friend declaration -// forward-declare helper class -namespace llcoro -{ -class Suspending; +// e.g. #include LLCOROS_MUTEX_HEADER +#define LLCOROS_MUTEX_HEADER +#define LLCOROS_CONDVAR_HEADER + +namespace boost { + namespace fibers { + class mutex; + enum class cv_status; + class condition_variable; + } } /** @@ -76,19 +81,21 @@ class Suspending; * 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 + * The name + * can 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 { LLSINGLETON(LLCoros); + ~LLCoros(); public: - /// Canonical boost::dcoroutines::coroutine signature we use - typedef boost::dcoroutines::coroutine coro; + /// The viewer's use of the term "coroutine" became deeply embedded before + /// the industry term "fiber" emerged to distinguish userland threads from + /// simpler, more transient kinds of coroutines. Semantically they've + /// always been fibers. But at this point in history, we're pretty much + /// stuck with the term "coroutine." + typedef boost::fibers::fiber coro; /// Canonical callable type typedef boost::function callable_t; @@ -119,10 +126,10 @@ public: * DEV-32777 comments for an explanation. * * Pass a nullary callable. It works to directly pass a nullary free - * function (or static method); for all other cases use boost::bind(). Of - * course, for a non-static class method, the first parameter must be the - * class instance. Any other parameters should be passed via the bind() - * expression. + * function (or static method); for other cases use a lambda expression, + * std::bind() or boost::bind(). Of course, for a non-static class method, + * the first parameter must be the class instance. Any other parameters + * should be passed via the enclosing expression. * * launch() tweaks the suggested name so it won't collide with any * existing coroutine instance, creates the coroutine instance, registers @@ -138,7 +145,7 @@ public: * one prematurely. Returns @c true if the specified name was found and * still running at the time. */ - bool kill(const std::string& name); +// bool kill(const std::string& name); /** * From within a coroutine, look up the (tweaked) name string by which @@ -146,16 +153,27 @@ public: * (e.g. if the coroutine was launched by hand rather than using * LLCoros::launch()). */ - std::string getName() const; + static std::string getName(); - /// for delayed initialization + /** + * This variation returns a name suitable for log messages: the explicit + * name for an explicitly-launched coroutine, or "mainN" for the default + * coroutine on a thread. + */ + static std::string logname(); + + /** + * For delayed initialization. To be clear, this will only affect + * coroutines launched @em after this point. The underlying facility + * provides no way to alter the stack size of any running coroutine. + */ void setStackSize(S32 stacksize); - /// for delayed initialization - void printActiveCoroutines(); + /// diagnostic + void printActiveCoroutines(const std::string& when=std::string()); - /// get the current coro::self& for those who really really care - static coro::self& get_self(); + /// get the current coro::id for those who really really care + static coro::id get_self(); /** * Most coroutines, most of the time, don't "consume" the events for which @@ -180,6 +198,7 @@ public: { set_consuming(consuming); } + OverrideConsuming(const OverrideConsuming&) = delete; ~OverrideConsuming() { set_consuming(mPrevConsuming); @@ -189,142 +208,124 @@ public: bool mPrevConsuming; }; + /// set string coroutine status for diagnostic purposes + static void setStatus(const std::string& status); + static std::string getStatus(); + + /// RAII control of status + class TempStatus + { + public: + TempStatus(const std::string& status): + mOldStatus(getStatus()) + { + setStatus(status); + } + TempStatus(const TempStatus&) = delete; + ~TempStatus() + { + setStatus(mOldStatus); + } + + private: + std::string mOldStatus; + }; + + /// thrown by checkStop() + // It may sound ironic that Stop is derived from LLContinueError, but the + // point is that LLContinueError is the category of exception that should + // not immediately crash the viewer. Stop and its subclasses are to notify + // coroutines that the viewer intends to shut down. The expected response + // is to terminate the coroutine, rather than abort the viewer. + struct Stop: public LLContinueError + { + Stop(const std::string& what): LLContinueError(what) {} + }; + + /// early stages + struct Stopping: public Stop + { + Stopping(const std::string& what): Stop(what) {} + }; + + /// cleaning up + struct Stopped: public Stop + { + Stopped(const std::string& what): Stop(what) {} + }; + + /// cleaned up -- not much survives! + struct Shutdown: public Stop + { + Shutdown(const std::string& what): Stop(what) {} + }; + + /// Call this intermittently if there's a chance your coroutine might + /// continue running into application shutdown. Throws Stop if LLCoros has + /// been cleaned up. + static void checkStop(); + /** - * Please do NOT directly use boost::dcoroutines::future! It is essential - * to maintain the "current" coroutine at every context switch. This - * Future wraps the essential boost::dcoroutines::future functionality - * with that maintenance. + * Aliases for promise and future. An older underlying future implementation + * required us to wrap future; that's no longer needed. However -- if it's + * important to restore kill() functionality, we might need to provide a + * proxy, so continue using the aliases. */ template - class Future; + using Promise = boost::fibers::promise; + template + using Future = boost::fibers::future; + template + static Future getFuture(Promise& promise) { return promise.get_future(); } + + // use mutex, lock, condition_variable suitable for coroutines + using Mutex = boost::fibers::mutex; + using LockType = std::unique_lock; + using cv_status = boost::fibers::cv_status; + using ConditionVariable = boost::fibers::condition_variable; + + /// for data local to each running coroutine + template + using local_ptr = boost::fibers::fiber_specific_ptr; private: - friend class llcoro::Suspending; - friend llcoro::id llcoro::get_id(); std::string generateDistinctName(const std::string& prefix) const; - bool cleanup(const LLSD&); + void toplevel(std::string name, callable_t callable); struct CoroData; - static void no_cleanup(CoroData*); #if LL_WINDOWS static void winlevel(const callable_t& callable); #endif - static void toplevel(coro::self& self, CoroData* data, const callable_t& callable); static CoroData& get_CoroData(const std::string& caller); S32 mStackSize; // coroutine-local storage, as it were: one per coro we track - struct CoroData + struct CoroData: public LLInstanceTracker { - CoroData(CoroData* prev, const std::string& name, - const callable_t& callable, S32 stacksize); + CoroData(const std::string& name); + CoroData(int n); - // The boost::dcoroutines library supports asymmetric coroutines. Every - // time we context switch out of a coroutine, we pass control to the - // previously-active one (or to the non-coroutine stack owned by the - // thread). So our management of the "current" coroutine must be able to - // restore the previous value when we're about to switch away. - CoroData* mPrev; // tweaked name of the current coroutine const std::string mName; - // the actual coroutine instance - LLCoros::coro mCoro; // set_consuming() state bool mConsuming; - // When the dcoroutine library calls a top-level callable, it implicitly - // passes coro::self& as the first parameter. All our consumer code used - // to explicitly pass coro::self& down through all levels of call stack, - // because at the leaf level we need it for context-switching. But since - // coroutines are based on cooperative switching, we can cause the - // top-level entry point to stash a pointer to the currently-running - // coroutine, and manage it appropriately as we switch out and back in. - // That eliminates the need to pass it as an explicit parameter down - // through every level, which is unfortunately viral in nature. Finding it - // implicitly rather than explicitly allows minor maintenance in which a - // leaf-level function adds a new async I/O call that suspends the calling - // coroutine, WITHOUT having to propagate coro::self& through every - // function signature down to that point -- and of course through every - // other caller of every such function. - LLCoros::coro::self* mSelf; + // setStatus() state + std::string mStatus; F64 mCreationTime; // since epoch }; - typedef boost::ptr_map CoroMap; - CoroMap mCoros; - // Identify the current coroutine's CoroData. Use a little helper class so - // a caller can either use a temporary instance, or instantiate a named - // variable and access it multiple times. - class Current - { - public: - Current(); - - operator LLCoros::CoroData*() { return get(); } - LLCoros::CoroData* operator->() { return get(); } - LLCoros::CoroData* get() { return mCurrent->get(); } - void reset(LLCoros::CoroData* ptr) { mCurrent->reset(ptr); } - - private: - boost::thread_specific_ptr* mCurrent; - }; + // Identify the current coroutine's CoroData. This local_ptr isn't static + // because it's a member of an LLSingleton, and we rely on it being + // cleaned up in proper dependency order. + local_ptr mCurrent; }; namespace llcoro { -/// Instantiate one of these in a block surrounding any leaf point when -/// control literally switches away from this coroutine. -class Suspending: boost::noncopyable -{ -public: - Suspending(); - ~Suspending(); +inline +std::string logname() { return LLCoros::logname(); } -private: - LLCoros::CoroData* mSuspended; -}; - -} // namespace llcoro - -template -class LLCoros::Future -{ - typedef boost::dcoroutines::future dfuture; - -public: - Future(): - mFuture(get_self()) - {} - - typedef typename boost::dcoroutines::make_callback_result::type callback_t; - - callback_t make_callback() - { - return boost::dcoroutines::make_callback(mFuture); - } - -#ifndef LL_LINUX - explicit -#endif - operator bool() const - { - return bool(mFuture); - } - - bool operator!() const - { - return ! mFuture; - } - - T get() - { - // instantiate Suspending to manage the "current" coroutine - llcoro::Suspending suspended; - return *mFuture; - } - -private: - dfuture mFuture; -}; +} // llcoro #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h index 476689827c..75114feadd 100644 --- a/indra/llcommon/lldate.h +++ b/indra/llcommon/lldate.h @@ -36,7 +36,6 @@ #include "llunits.h" // Fix dependency on llviewerprecompiledheaders.h in dependent projects -#include "llpreprocessor.h" /** * @class LLDate diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 8433a3a2e7..3e228bbbfc 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -39,6 +39,9 @@ #if !LL_WINDOWS # include # include +# include +#else +# include #endif // !LL_WINDOWS #include #include "string.h" @@ -53,7 +56,12 @@ #include "llstl.h" #include "lltimer.h" -#include "nd/ndlogthrottle.h" +// On Mac, got: +// #error "Boost.Stacktrace requires `_Unwind_Backtrace` function. Define +// `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if +// _Unwind_Backtrace is available without `_GNU_SOURCE`." +#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED +#include namespace { #if LL_WINDOWS @@ -120,30 +128,30 @@ namespace { class RecordToFile : public LLError::Recorder { public: - RecordToFile(const std::string& filename) + RecordToFile(const std::string& filename): + mName(filename) { // Don't screw up log file output this->showMultiline(true); - mFile.open(filename.c_str(), std::ios_base::out | std::ios_base::app); if (!mFile) { LL_INFOS() << "Error setting log file to " << filename << LL_ENDL; } - else - { - if (!LLError::getAlwaysFlush()) - { - mFile.sync_with_stdio(false); - } - } + else + { + if (!LLError::getAlwaysFlush()) + { + mFile.sync_with_stdio(false); + } + } } - + ~RecordToFile() { mFile.close(); } - + virtual bool enabled() override { #ifdef LL_RELEASE_FOR_DOWNLOAD @@ -153,11 +161,13 @@ namespace { #endif } - bool okay() { return mFile.good(); } - - virtual void recordMessage(LLError::ELevel level, - const std::string& message) override - { + bool okay() const { return mFile.good(); } + + std::string getFilename() const { return mName; } + + virtual void recordMessage(LLError::ELevel level, + const std::string& message) override + { if (LLError::getAlwaysFlush()) { mFile << message << std::endl; @@ -166,9 +176,10 @@ namespace { { mFile << message << "\n"; } - } - + } + private: + const std::string mName; llofstream mFile; }; @@ -176,7 +187,7 @@ namespace { class RecordToStderr : public LLError::Recorder { public: - RecordToStderr(bool timestamp) : mUseANSI(ANSI_PROBE) + RecordToStderr(bool timestamp) : mUseANSI(checkANSI()) { this->showMultiline(true); } @@ -198,14 +209,12 @@ namespace { virtual void recordMessage(LLError::ELevel level, const std::string& message) override - { + { static std::string s_ansi_error = createANSI("31"); // red static std::string s_ansi_warn = createANSI("34"); // blue static std::string s_ansi_debug = createANSI("35"); // magenta - mUseANSI = (ANSI_PROBE == mUseANSI) ? (checkANSI() ? ANSI_YES : ANSI_NO) : mUseANSI; - - if (ANSI_YES == mUseANSI) + if (mUseANSI) { writeANSI((level == LLError::LEVEL_ERROR) ? s_ansi_error : (level == LLError::LEVEL_WARN) ? s_ansi_warn : @@ -221,12 +230,7 @@ namespace { } private: - enum ANSIState - { - ANSI_PROBE, - ANSI_YES, - ANSI_NO - } mUseANSI; + bool mUseANSI; LL_FORCE_INLINE void writeANSI(const std::string& ansi_code, const std::string& message) { @@ -237,16 +241,13 @@ namespace { fprintf(stderr, "%s%s%s\n%s", s_ansi_bold.c_str(), ansi_code.c_str(), message.c_str(), s_ansi_reset.c_str() ); } - bool checkANSI(void) + static bool checkANSI(void) { -#if LL_LINUX || LL_DARWIN // Check whether it's okay to use ANSI; if stderr is // a tty then we assume yes. Can be turned off with // the LL_NO_ANSI_COLOR env var. return (0 != isatty(2)) && (NULL == getenv("LL_NO_ANSI_COLOR")); -#endif // LL_LINUX - return FALSE; // works in a cygwin shell... ;) } }; @@ -316,28 +317,35 @@ namespace LLError { #ifdef __GNUC__ // GCC: type_info::name() returns a mangled class name,st demangle - // passing nullptr, 0 forces allocation of a unique buffer we can free - // fixing MAINT-8724 on OSX 10.14 + // passing nullptr, 0 forces allocation of a unique buffer we can free + // fixing MAINT-8724 on OSX 10.14 int status = -1; char* name = abi::__cxa_demangle(mangled, nullptr, 0, &status); - std::string result(name ? name : mangled); - free(name); - return result; + std::string result(name ? name : mangled); + free(name); + return result; + #elif LL_WINDOWS - // DevStudio: type_info::name() includes the text "class " at the start - - static const std::string class_prefix = "class "; + // Visual Studio: type_info::name() includes the text "class " at the start std::string name = mangled; - if (0 != name.compare(0, class_prefix.length(), class_prefix)) + for (const auto& prefix : std::vector{ "class ", "struct " }) { - LL_DEBUGS() << "Did not see '" << class_prefix << "' prefix on '" - << name << "'" << LL_ENDL; - return name; + if (0 == name.compare(0, prefix.length(), prefix)) + { + return name.substr(prefix.length()); + } } + // huh, that's odd, we should see one or the other prefix -- but don't + // try to log unless logging is already initialized + if (is_available()) + { + // in Python, " or ".join(vector) -- but in C++, a PITB + LL_DEBUGS() << "Did not see 'class' or 'struct' prefix on '" + << name << "'" << LL_ENDL; + } + return name; - return name.substr(class_prefix.length()); - -#else +#else // neither GCC nor Visual Studio return mangled; #endif } @@ -416,7 +424,7 @@ namespace return false; } - if (configuration.isUndefined() || !configuration.isMap() || configuration.emptyMap()) + if (! configuration || !configuration.isMap()) { LL_WARNS() << filename() << " missing, ill-formed, or simply undefined" " content; not changing configuration" @@ -498,14 +506,11 @@ namespace LLError LLError::FatalFunction mCrashFunction; LLError::TimeFunction mTimeFunction; - + Recorders mRecorders; - RecorderPtr mFileRecorder; - RecorderPtr mFixedBufferRecorder; - std::string mFileRecorderFileName; - - int mShouldLogCallCounter; - + + int mShouldLogCallCounter; + private: SettingsConfig(); }; @@ -539,9 +544,6 @@ namespace LLError mCrashFunction(NULL), mTimeFunction(NULL), mRecorders(), - mFileRecorder(), - mFixedBufferRecorder(), - mFileRecorderFileName(), mShouldLogCallCounter(0) { } @@ -657,31 +659,6 @@ namespace LLError } } -#ifdef LL_LINUX - // Temp hack to get the old linux havok stub to link - CallSite::CallSite(LLError::ELevel level, - char const* file, - int line, - std::type_info const& class_info, - char const* function, - char const*, - char const*, - bool) - : mLevel(level), - mFile(file), - mLine(line), - mClassInfo(class_info), - mFunction(function), - mCached(false), - mShouldLog(false), - mPrintOnce(false), - mTags(0), - mTagCount(0) - { - } - // -#endif - CallSite::~CallSite() { delete []mTags; @@ -695,22 +672,38 @@ namespace LLError namespace { - bool shouldLogToStderr() - { + bool shouldLogToStderr() + { #if LL_DARWIN - // On Mac OS X, stderr from apps launched from the Finder goes to the - // console log. It's generally considered bad form to spam too much - // there. - - // If stdin is a tty, assume the user launched from the command line and - // therefore wants to see stderr. Otherwise, assume we've been launched - // from the finder and shouldn't spam stderr. - return isatty(0); + // On Mac OS X, stderr from apps launched from the Finder goes to the + // console log. It's generally considered bad form to spam too much + // there. That scenario can be detected by noticing that stderr is a + // character device (S_IFCHR). + + // If stderr is a tty or a pipe, assume the user launched from the + // command line or debugger and therefore wants to see stderr. + if (isatty(STDERR_FILENO)) + return true; + // not a tty, but might still be a pipe -- check + struct stat st; + if (fstat(STDERR_FILENO, &st) < 0) + { + // capture errno right away, before engaging any other operations + auto errno_save = errno; + // this gets called during log-system setup -- can't log yet! + std::cerr << "shouldLogToStderr: fstat(" << STDERR_FILENO << ") failed, errno " + << errno_save << std::endl; + // if we can't tell, err on the safe side and don't write stderr + return false; + } + + // fstat() worked: return true only if stderr is a pipe + return ((st.st_mode & S_IFMT) == S_IFIFO); #else - return true; + return true; #endif - } - + } + bool stderrLogWantsTime() { #if LL_WINDOWS @@ -724,20 +717,19 @@ namespace void commonInit(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr = true) { LLError::Settings::getInstance()->reset(); - + LLError::setDefaultLevel(LLError::LEVEL_INFO); - LLError::setAlwaysFlush(true); - LLError::setEnabledLogTypesMask(0xFFFFFFFF); + LLError::setAlwaysFlush(true); + LLError::setEnabledLogTypesMask(0xFFFFFFFF); LLError::setFatalFunction(LLError::crashAndLoop); LLError::setTimeFunction(LLError::utcTime); // log_to_stderr is only false in the unit and integration tests to keep builds quieter if (log_to_stderr && shouldLogToStderr()) { - LLError::RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime())); - LLError::addRecorder(recordToStdErr); + LLError::logToStderr(); } - + #if LL_WINDOWS LLError::RecorderPtr recordToWinDebug(new RecordToWinDebug()); LLError::addRecorder(recordToWinDebug); @@ -1035,49 +1027,110 @@ namespace LLError s->mRecorders.erase(std::remove(s->mRecorders.begin(), s->mRecorders.end(), recorder), s->mRecorders.end()); } + + // Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to + // a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which + // points to the Recorder base class), but a shared_ptr which + // specifically points to the concrete RECORDER subclass instance, along + // with a Recorders::iterator indicating the position of that entry in + // mRecorders. The shared_ptr might be empty (operator!() returns true) if + // there was no such RECORDER subclass instance in mRecorders. + template + std::pair, Recorders::iterator> + findRecorderPos() + { + SettingsConfigPtr s = Settings::instance().getSettingsConfig(); + // Since we promise to return an iterator, use a classic iterator + // loop. + auto end{s->mRecorders.end()}; + for (Recorders::iterator it{s->mRecorders.begin()}; it != end; ++it) + { + // *it is a RecorderPtr, a shared_ptr. Use a + // dynamic_pointer_cast to try to downcast to test if it's also a + // shared_ptr. + auto ptr = boost::dynamic_pointer_cast(*it); + if (ptr) + { + // found the entry we want + return { ptr, it }; + } + } + // dropped out of the loop without finding any such entry -- instead + // of default-constructing Recorders::iterator (which might or might + // not be valid), return a value that is valid but not dereferenceable. + return { {}, end }; + } + + // Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to + // a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which + // points to the Recorder base class), but a shared_ptr which + // specifically points to the concrete RECORDER subclass instance. The + // shared_ptr might be empty (operator!() returns true) if there was no + // such RECORDER subclass instance in mRecorders. + template + boost::shared_ptr findRecorder() + { + return findRecorderPos().first; + } + + // Remove an entry from SettingsConfig::mRecorders whose RecorderPtr + // points to a Recorder subclass of type RECORDER. Return true if there + // was one and we removed it, false if there wasn't one to start with. + template + bool removeRecorder() + { + auto found = findRecorderPos(); + if (found.first) + { + SettingsConfigPtr s = Settings::instance().getSettingsConfig(); + s->mRecorders.erase(found.second); + } + return bool(found.first); + } } namespace LLError { void logToFile(const std::string& file_name) { - SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); + // remove any previous Recorder filling this role + removeRecorder(); - removeRecorder(s->mFileRecorder); - s->mFileRecorder.reset(); - s->mFileRecorderFileName.clear(); - if (!file_name.empty()) { - RecorderPtr recordToFile(new RecordToFile(file_name)); - if (boost::dynamic_pointer_cast(recordToFile)->okay()) - { - s->mFileRecorderFileName = file_name; - s->mFileRecorder = recordToFile; - addRecorder(recordToFile); - } + boost::shared_ptr recordToFile(new RecordToFile(file_name)); + if (recordToFile->okay()) + { + addRecorder(recordToFile); + } } } - - void logToFixedBuffer(LLLineBuffer* fixedBuffer) - { - SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - - removeRecorder(s->mFixedBufferRecorder); - s->mFixedBufferRecorder.reset(); - - if (fixedBuffer) - { - RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer)); - s->mFixedBufferRecorder = recordToFixedBuffer; - addRecorder(recordToFixedBuffer); - } - } std::string logFileName() { - SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - return s->mFileRecorderFileName; + auto found = findRecorder(); + return found? found->getFilename() : std::string(); + } + + void logToStderr() + { + if (! findRecorder()) + { + RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime())); + addRecorder(recordToStdErr); + } + } + + void logToFixedBuffer(LLLineBuffer* fixedBuffer) + { + // remove any previous Recorder filling this role + removeRecorder(); + + if (fixedBuffer) + { + RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer)); + addRecorder(recordToFixedBuffer); + } } } @@ -1193,8 +1246,25 @@ namespace } namespace { - LLMutex gLogMutex; - LLMutex gCallStacksLogMutex; + // We need a couple different mutexes, but we want to use the same mechanism + // for both. Make getMutex() a template function with different instances + // for different MutexDiscriminator values. + enum MutexDiscriminator + { + LOG_MUTEX, + STACKS_MUTEX + }; + // Some logging calls happen very early in processing -- so early that our + // module-static variables aren't yet initialized. getMutex() wraps a + // function-static LLMutex so that early calls can still have a valid + // LLMutex instance. + template + LLMutex* getMutex() + { + // guaranteed to be initialized the first time control reaches here + static LLMutex sMutex; + return &sMutex; + } bool checkLevelMap(const LevelMap& map, const std::string& key, LLError::ELevel& level) @@ -1247,7 +1317,7 @@ namespace LLError bool Log::shouldLog(CallSite& site) { - LLMutexTrylock lock(&gLogMutex, 5); + LLMutexTrylock lock(getMutex(), 5); if (!lock.isLocked()) { return false; @@ -1298,7 +1368,7 @@ namespace LLError std::ostringstream* Log::out() { - LLMutexTrylock lock(&gLogMutex,5); + LLMutexTrylock lock(getMutex(),5); // If we hit a logging request very late during shutdown processing, // when either of the relevant LLSingletons has already been deleted, // DO NOT resurrect them. @@ -1318,7 +1388,7 @@ namespace LLError void Log::flush(std::ostringstream* out, char* message) { - LLMutexTrylock lock(&gLogMutex,5); + LLMutexTrylock lock(getMutex(),5); if (!lock.isLocked()) { return; @@ -1358,7 +1428,7 @@ namespace LLError void Log::flush(std::ostringstream* out, const CallSite& site) { - LLMutexTrylock lock(&gLogMutex,5); + LLMutexTrylock lock(getMutex(),5); if (!lock.isLocked()) { return; @@ -1387,11 +1457,6 @@ namespace LLError delete out; } - - std::ostringstream prefix; - if( nd::logging::throttle( site.mFile, site.mLine, &prefix ) ) - return; - if (site.mPrintOnce) { std::ostringstream message_stream; @@ -1534,129 +1599,133 @@ namespace LLError S32 LLCallStacks::sIndex = 0 ; //static - void LLCallStacks::allocateStackBuffer() - { - if(sBuffer == NULL) - { - sBuffer = new char*[512] ; - sBuffer[0] = new char[512 * 128] ; - for(S32 i = 1 ; i < 512 ; i++) - { - sBuffer[i] = sBuffer[i-1] + 128 ; - } - sIndex = 0 ; - } - } + void LLCallStacks::allocateStackBuffer() + { + if(sBuffer == NULL) + { + sBuffer = new char*[512] ; + sBuffer[0] = new char[512 * 128] ; + for(S32 i = 1 ; i < 512 ; i++) + { + sBuffer[i] = sBuffer[i-1] + 128 ; + } + sIndex = 0 ; + } + } - void LLCallStacks::freeStackBuffer() - { - if(sBuffer != NULL) - { - delete [] sBuffer[0] ; - delete [] sBuffer ; - sBuffer = NULL ; - } - } + void LLCallStacks::freeStackBuffer() + { + if(sBuffer != NULL) + { + delete [] sBuffer[0] ; + delete [] sBuffer ; + sBuffer = NULL ; + } + } - //static - void LLCallStacks::push(const char* function, const int line) - { - LLMutexTrylock lock(&gCallStacksLogMutex, 5); - if (!lock.isLocked()) - { - return; - } + //static + void LLCallStacks::push(const char* function, const int line) + { + LLMutexTrylock lock(getMutex(), 5); + if (!lock.isLocked()) + { + return; + } - if(sBuffer == NULL) - { - allocateStackBuffer(); - } + if(sBuffer == NULL) + { + allocateStackBuffer(); + } - if(sIndex > 511) - { - clear() ; - } + if(sIndex > 511) + { + clear() ; + } - strcpy(sBuffer[sIndex], function) ; - sprintf(sBuffer[sIndex] + strlen(function), " line: %d ", line) ; - sIndex++ ; + strcpy(sBuffer[sIndex], function) ; + sprintf(sBuffer[sIndex] + strlen(function), " line: %d ", line) ; + sIndex++ ; - return ; - } + return ; + } - //static - std::ostringstream* LLCallStacks::insert(const char* function, const int line) - { - std::ostringstream* _out = LLError::Log::out(); - *_out << function << " line " << line << " " ; - - return _out ; - } + //static + std::ostringstream* LLCallStacks::insert(const char* function, const int line) + { + std::ostringstream* _out = LLError::Log::out(); + *_out << function << " line " << line << " " ; + return _out ; + } - //static - void LLCallStacks::end(std::ostringstream* _out) - { - LLMutexTrylock lock(&gCallStacksLogMutex, 5); - if (!lock.isLocked()) - { - return; - } + //static + void LLCallStacks::end(std::ostringstream* _out) + { + LLMutexTrylock lock(getMutex(), 5); + if (!lock.isLocked()) + { + return; + } - if(sBuffer == NULL) - { - allocateStackBuffer(); - } + if(sBuffer == NULL) + { + allocateStackBuffer(); + } - if(sIndex > 511) - { - clear() ; - } + if(sIndex > 511) + { + clear() ; + } - LLError::Log::flush(_out, sBuffer[sIndex++]) ; - } + LLError::Log::flush(_out, sBuffer[sIndex++]) ; + } - //static - void LLCallStacks::print() - { - LLMutexTrylock lock(&gCallStacksLogMutex, 5); - if (!lock.isLocked()) - { - return; - } + //static + void LLCallStacks::print() + { + LLMutexTrylock lock(getMutex(), 5); + if (!lock.isLocked()) + { + return; + } - if(sIndex > 0) - { - LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL; - while(sIndex > 0) - { - sIndex-- ; - LL_INFOS() << sBuffer[sIndex] << LL_ENDL; - } - LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL; - } + if(sIndex > 0) + { + LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL; + while(sIndex > 0) + { + sIndex-- ; + LL_INFOS() << sBuffer[sIndex] << LL_ENDL; + } + LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL; + } - if(sBuffer != NULL) - { - freeStackBuffer(); - } - } + if(sBuffer != NULL) + { + freeStackBuffer(); + } + } - //static - void LLCallStacks::clear() - { - sIndex = 0 ; - } + //static + void LLCallStacks::clear() + { + sIndex = 0 ; + } - //static - void LLCallStacks::cleanup() - { - freeStackBuffer(); - } + //static + void LLCallStacks::cleanup() + { + freeStackBuffer(); + } + + std::ostream& operator<<(std::ostream& out, const LLStacktrace&) + { + return out << boost::stacktrace::stacktrace(); + } } bool debugLoggingEnabled(const std::string& tag) { - LLMutexTrylock lock(&gLogMutex, 5); + LLMutexTrylock lock(getMutex(), 5); if (!lock.isLocked()) { return false; diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 447c0fb31f..2ab4a2df07 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -36,6 +36,12 @@ #include "llpreprocessor.h" #include +// Supress some false positives of PVS Studio. +// They are misleading as there is opossibly a pointr taken behid the array and that pointer is passed down. But it's never dereferenced. +//-V:llassert_always:557 +//-V:lllog_site_args_:557 +// + const int LL_ERR_NOERR = 0; // Define one of these for different error levels in release... @@ -191,9 +197,9 @@ namespace LLError The classes CallSite and Log are used by the logging macros below. They are not intended for general use. */ - + struct CallSite; - + class LL_COMMON_API Log { public: @@ -202,8 +208,17 @@ namespace LLError static void flush(std::ostringstream* out, char* message); static void flush(std::ostringstream*, const CallSite&); static std::string demangle(const char* mangled); + /// classname() + template + static std::string classname() { return demangle(typeid(T).name()); } + /// classname(some_pointer) + template + static std::string classname(T* const ptr) { return ptr? demangle(typeid(*ptr).name()) : "nullptr"; } + /// classname(some_reference) + template + static std::string classname(const T& obj) { return demangle(typeid(obj).name()); } }; - + struct LL_COMMON_API CallSite { // Represents a specific place in the code where a message is logged @@ -218,19 +233,6 @@ namespace LLError const char** tags, size_t tag_count); -#ifdef LL_LINUX - // Temp hack to get the old linux havok stub to link - CallSite(LLError::ELevel, - char const*, - int, - std::type_info const&, - char const*, - char const*, - char const*, - bool); - // -#endif - ~CallSite(); #ifdef LL_LIBRARY_INCLUDE @@ -275,30 +277,36 @@ namespace LLError class LL_COMMON_API NoClassInfo { }; // used to indicate no class info known for logging - //LLCallStacks keeps track of call stacks and output the call stacks to log file - //when LLAppViewer::handleViewerCrash() is triggered. - // - //Note: to be simple, efficient and necessary to keep track of correct call stacks, - //LLCallStacks is designed not to be thread-safe. - //so try not to use it in multiple parallel threads at same time. - //Used in a single thread at a time is fine. - class LL_COMMON_API LLCallStacks - { - private: - static char** sBuffer ; - static S32 sIndex ; + //LLCallStacks keeps track of call stacks and output the call stacks to log file + //when LLAppViewer::handleViewerCrash() is triggered. + // + //Note: to be simple, efficient and necessary to keep track of correct call stacks, + //LLCallStacks is designed not to be thread-safe. + //so try not to use it in multiple parallel threads at same time. + //Used in a single thread at a time is fine. + class LL_COMMON_API LLCallStacks + { + private: + static char** sBuffer ; + static S32 sIndex ; - static void allocateStackBuffer(); - static void freeStackBuffer(); - - public: - static void push(const char* function, const int line) ; - static std::ostringstream* insert(const char* function, const int line) ; - static void print() ; - static void clear() ; - static void end(std::ostringstream* _out) ; - static void cleanup(); - }; + static void allocateStackBuffer(); + static void freeStackBuffer(); + + public: + static void push(const char* function, const int line) ; + static std::ostringstream* insert(const char* function, const int line) ; + static void print() ; + static void clear() ; + static void end(std::ostringstream* _out) ; + static void cleanup(); + }; + + // class which, when streamed, inserts the current stack trace + struct LLStacktrace + { + friend std::ostream& operator<<(std::ostream& out, const LLStacktrace&); + }; } //this is cheaper than llcallstacks if no need to output other variables to call stacks. @@ -394,8 +402,13 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__) #define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__) // alternative to llassert_always that prints explanatory message -#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(##__VA_ARGS__) << "(" #exp ")" -#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")" +// note ## token paste operator hack used above will only work in gcc following +// a comma and is completely unnecessary in VS since the comma is automatically +// suppressed +// https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html +// https://docs.microsoft.com/en-us/cpp/preprocessor/variadic-macros?view=vs-2015 +#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(__VA_ARGS__) << "(" #exp ")" +#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(__VA_ARGS__) << "(" #exp ")" // Only print the log message once (good for warnings or infos that would otherwise // spam the log file over and over, such as tighter loops). diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 276d22fc36..bfa2269025 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -183,6 +183,7 @@ namespace LLError // each error message is passed to each recorder via recordMessage() LL_COMMON_API void logToFile(const std::string& filename); + LL_COMMON_API void logToStderr(); LL_COMMON_API void logToFixedBuffer(LLLineBuffer*); // Utilities to add recorders for logging to a file or a fixed buffer // A second call to the same function will remove the logger added diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 56367b8f54..995356dc52 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -31,17 +31,17 @@ // associated header #include "lleventcoro.h" // STL headers -#include +#include +#include // std headers // external library headers +#include // other Linden headers #include "llsdserialize.h" +#include "llsdutil.h" #include "llerror.h" #include "llcoros.h" -#include "llmake.h" -#include "llexception.h" - -#include "lleventfilter.h" +#include "stringize.h" namespace { @@ -62,7 +62,7 @@ namespace std::string listenerNameForCoro() { // If this coroutine was launched by LLCoros::launch(), find that name. - std::string name(LLCoros::instance().getName()); + std::string name(LLCoros::getName()); if (! name.empty()) { return name; @@ -92,137 +92,173 @@ std::string listenerNameForCoro() * In the degenerate case in which @a path is an empty array, @a dest will * @em become @a value rather than @em containing it. */ -void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) +void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value) { - if (rawPath.isUndefined()) + if (path.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; + // Drill down to where we should store 'value'. + llsd::drill(dest, path) = value; } -/// For LLCoros::Future::make_callback(), the callback has a signature -/// like void callback(LLSD), which isn't a valid LLEventPump listener: such -/// listeners must return bool. -template -class FutureListener -{ -public: - // FutureListener is instantiated on the coroutine stack: the stack, in - // other words, that wants to suspend. - FutureListener(const LISTENER& listener): - mListener(listener), - // Capture the suspending coroutine's flag as a consuming or - // non-consuming listener. - mConsume(LLCoros::get_consuming()) - {} - - // operator()() is called on the main stack: the stack on which the - // expected event is fired. - bool operator()(const LLSD& event) - { - mListener(event); - // tell upstream LLEventPump whether listener consumed - return mConsume; - } - -protected: - LISTENER mListener; - bool mConsume; -}; - } // anonymous void llcoro::suspend() { - // By viewer convention, we post an event on the "mainloop" LLEventPump - // each iteration of the main event-handling loop. So waiting for a single - // event on "mainloop" gives us a one-frame suspend. - suspendUntilEventOn("mainloop"); + LLCoros::checkStop(); + LLCoros::TempStatus st("waiting one tick"); + boost::this_fiber::yield(); } void llcoro::suspendUntilTimeout(float seconds) { - LLEventTimeout timeout; - - timeout.eventAfter(seconds, LLSD()); - llcoro::suspendUntilEventOn(timeout); + LLCoros::checkStop(); + // We used to call boost::this_fiber::sleep_for(). But some coroutines + // (e.g. LLExperienceCache::idleCoro()) sit in a suspendUntilTimeout() + // loop, in which case a sleep_for() call risks sleeping through shutdown. + // So instead, listen for "LLApp" state-changing events -- which + // fortunately is handled for us by suspendUntilEventOnWithTimeout(). + // Wait for an event on a bogus LLEventPump on which nobody ever posts + // events. Don't make it static because that would force instantiation of + // the LLEventPumps LLSingleton registry at static initialization time. + // DO allow tweaking the name for uniqueness, this definitely gets + // re-entered on multiple coroutines! + // We could use an LLUUID if it were important to actively prohibit anyone + // from ever posting on this LLEventPump. + LLEventStream bogus("xyzzy", true); + // Timeout is the NORMAL case for this call! + static LLSD timedout; + // Deliver, but ignore, timedout when (as usual) we did not receive any + // "LLApp" event. The point is that suspendUntilEventOnWithTimeout() will + // itself throw Stopping when "LLApp" starts broadcasting shutdown events. + suspendUntilEventOnWithTimeout(bogus, seconds, timedout); } -LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +namespace { - // declare the future - LLCoros::Future future; - // make a callback that will assign a value to the future, and listen on - // the specified LLEventPump with that callback - std::string listenerName(listenerNameForCoro()); - LLTempBoundListener connection( - replyPump.getPump().listen(listenerName, - llmake(future.make_callback()))); + +// returns a listener on replyPumpP, also on "mainloop" -- both should be +// stored in LLTempBoundListeners on the caller's stack frame +std::pair +postAndSuspendSetup(const std::string& callerName, + const std::string& listenerName, + LLCoros::Promise& promise, + const LLSD& event, + const LLEventPumpOrPumpName& requestPumpP, + const LLEventPumpOrPumpName& replyPumpP, + const LLSD& replyPumpNamePath) +{ + // Before we get any farther -- should we be stopping instead of + // suspending? + LLCoros::checkStop(); + // Get the consuming attribute for THIS coroutine, the one that's about to + // suspend. Don't call get_consuming() in the lambda body: that would + // return the consuming attribute for some other coroutine, most likely + // the main routine. + bool consuming(LLCoros::get_consuming()); + // listen on the specified LLEventPump with a lambda that will assign a + // value to the promise, thus fulfilling its future + llassert_always_msg(replyPumpP, ("replyPump required for " + callerName)); + LLEventPump& replyPump(replyPumpP.getPump()); + // The relative order of the two listen() calls below would only matter if + // "LLApp" were an LLEventMailDrop. But if we ever go there, we'd want to + // notice the pending LLApp status first. + LLBoundListener stopper( + LLEventPumps::instance().obtain("LLApp").listen( + listenerName, + [&promise, listenerName](const LLSD& status) + { + // anything except "running" should wake up the waiting + // coroutine + auto& statsd = status["status"]; + if (statsd.asString() != "running") + { + LL_DEBUGS("lleventcoro") << listenerName + << " spotted status " << statsd + << ", throwing Stopping" << LL_ENDL; + try + { + promise.set_exception( + std::make_exception_ptr( + LLCoros::Stopping("status " + statsd.asString()))); + } + catch (const boost::fibers::promise_already_satisfied&) + { + LL_WARNS("lleventcoro") << listenerName + << " couldn't throw Stopping " + "because promise already set" << LL_ENDL; + } + } + // do not consume -- every listener must see status + return false; + })); + LLBoundListener connection( + replyPump.listen( + listenerName, + [&promise, consuming, listenerName](const LLSD& result) + { + try + { + promise.set_value(result); + // We did manage to propagate the result value to the + // (real) listener. If we're supposed to indicate that + // we've consumed it, do so. + return consuming; + } + catch(boost::fibers::promise_already_satisfied & ex) + { + LL_DEBUGS("lleventcoro") << "promise already satisfied in '" + << listenerName << "': " << ex.what() << LL_ENDL; + // We could not propagate the result value to the + // listener. + return false; + } + })); + // skip the "post" part if requestPump is default-constructed - if (requestPump) + if (requestPumpP) { + LLEventPump& requestPump(requestPumpP.getPump()); // If replyPumpNamePath is non-empty, store the replyPump name in the // request event. LLSD modevent(event); - storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName - << " posting to " << requestPump.getPump().getName() + storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getName()); + LL_DEBUGS("lleventcoro") << callerName << ": coroutine " << listenerName + << " posting to " << requestPump.getName() << LL_ENDL; // *NOTE:Mani - Removed because modevent could contain user's hashed passwd. // << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); + requestPump.post(modevent); } - LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName - << " about to wait on LLEventPump " << replyPump.getPump().getName() + LL_DEBUGS("lleventcoro") << callerName << ": coroutine " << listenerName + << " about to wait on LLEventPump " << replyPump.getName() << LL_ENDL; + return { connection, stopper }; +} + +} // anonymous + +LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +{ + LLCoros::Promise promise; + std::string listenerName(listenerNameForCoro()); + + // Store both connections into LLTempBoundListeners so we implicitly + // disconnect on return from this function. + auto connections = + postAndSuspendSetup("postAndSuspend()", listenerName, promise, + event, requestPump, replyPump, replyPumpNamePath); + LLTempBoundListener connection(connections.first), stopper(connections.second); + + // declare the future + LLCoros::Future future = LLCoros::getFuture(promise); // calling get() on the future makes us wait for it + LLCoros::TempStatus st(STRINGIZE("waiting for " << replyPump.getPump().getName())); LLSD value(future.get()); LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName << " resuming with " << value << LL_ENDL; @@ -230,147 +266,52 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ return value; } -LLSD llcoro::suspendUntilEventOnWithTimeout(const LLEventPumpOrPumpName& suspendPumpOrName, - F32 timeoutin, const LLSD &timeoutResult) +LLSD llcoro::postAndSuspendWithTimeout(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, + const LLSD& replyPumpNamePath, + F32 timeout, const LLSD& timeoutResult) { - /** - * The timeout pump is attached upstream of of the waiting pump and will - * pass the timeout event through it. We CAN NOT attach downstream since - * doing so will cause the suspendPump to fire any waiting events immediately - * and they will be lost. This becomes especially problematic with the - * LLEventTimeout(pump) constructor which will also attempt to fire those - * events using the virtual listen_impl method in the not yet fully constructed - * timeoutPump. - */ - LLEventTimeout timeoutPump; - LLEventPump &suspendPump = suspendPumpOrName.getPump(); + LLCoros::Promise promise; + std::string listenerName(listenerNameForCoro()); - LLTempBoundListener timeoutListener(timeoutPump.listen(suspendPump.getName(), - boost::bind(&LLEventPump::post, &suspendPump, _1))); + // Store both connections into LLTempBoundListeners so we implicitly + // disconnect on return from this function. + auto connections = + postAndSuspendSetup("postAndSuspendWithTimeout()", listenerName, promise, + event, requestPump, replyPump, replyPumpNamePath); + LLTempBoundListener connection(connections.first), stopper(connections.second); - timeoutPump.eventAfter(timeoutin, timeoutResult); - return llcoro::suspendUntilEventOn(suspendPump); -} - -namespace -{ - -/** - * This helper is specifically for postAndSuspend2(). 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::dcoroutines::make_callback() - * (void return) to provide an event listener (bool return), we've adapted - * FutureListener for the purpose. The basic idea is that we construct a - * distinct instance of FutureListener2 -- binding different instance data -- - * for each of the pumps. Then, when a pump delivers an LLSD value to either - * FutureListener2, it can combine that LLSD with its discriminator to feed - * the future object. - * - * DISCRIM is a template argument so we can use llmake() rather than - * having to write our own argument-deducing helper function. - */ -template -class FutureListener2: public FutureListener -{ - typedef FutureListener super; - -public: - // instantiated on coroutine stack: the stack about to suspend - FutureListener2(const LISTENER& listener, DISCRIM discriminator): - super(listener), - mDiscrim(discriminator) - {} - - // called on main stack: the stack on which event is fired - bool operator()(const LLSD& event) - { - // our future object is defined to accept LLEventWithID - super::mListener(LLEventWithID(event, mDiscrim)); - // tell LLEventPump whether or not event was consumed - return super::mConsume; - } - -private: - const DISCRIM mDiscrim; -}; - -} // anonymous - -namespace llcoro -{ - -LLEventWithID postAndSuspend2(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump0, - const LLEventPumpOrPumpName& replyPump1, - const LLSD& replyPump0NamePath, - const LLSD& replyPump1NamePath) -{ // declare the future - LLCoros::Future future; - // either callback will assign a value to this future; listen on - // each specified LLEventPump with a callback - std::string name(listenerNameForCoro()); - LLTempBoundListener connection0( - replyPump0.getPump().listen( - name + "a", - llmake(future.make_callback(), 0))); - LLTempBoundListener connection1( - replyPump1.getPump().listen( - name + "b", - llmake(future.make_callback(), 1))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) + LLCoros::Future future = LLCoros::getFuture(promise); + // wait for specified timeout + boost::fibers::future_status status; { - // If either replyPumpNamePath is non-empty, store the corresponding - // replyPump name in the request event. - LLSD modevent(event); - storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name - << " posting to " << requestPump.getPump().getName() - << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); + LLCoros::TempStatus st(STRINGIZE("waiting for " << replyPump.getPump().getName() + << " for " << timeout << "s")); + // The fact that we accept non-integer seconds means we should probably + // use granularity finer than one second. However, given the overhead of + // the rest of our processing, it seems silly to use granularity finer + // than a millisecond. + status = future.wait_for(std::chrono::milliseconds(long(timeout * 1000))); } - LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name - << " about to wait on LLEventPumps " << replyPump0.getPump().getName() - << ", " << replyPump1.getPump().getName() << LL_ENDL; - // calling get() on the future makes us wait for it - LLEventWithID value(future.get()); - LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; - // returning should disconnect both connections - return 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) + // if the future is NOT yet ready, return timeoutResult instead + if (status == boost::fibers::future_status::timeout) { - LLTHROW(LLErrorEvent(desc, result.first)); + LL_DEBUGS("lleventcoro") << "postAndSuspendWithTimeout(): coroutine " << listenerName + << " timed out after " << timeout << " seconds," + << " resuming with " << timeoutResult << LL_ENDL; + return timeoutResult; } - // 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) + else { - 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; -} + llassert_always(status == boost::fibers::future_status::ready); -} // namespace llcoro + // future is now ready, no more waiting + LLSD value(future.get()); + LL_DEBUGS("lleventcoro") << "postAndSuspendWithTimeout(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; + } +} diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 84827aab4a..c0fe8b094f 100644 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,12 +29,8 @@ #if ! defined(LL_LLEVENTCORO_H) #define LL_LLEVENTCORO_H -#include #include -#include // std::pair #include "llevents.h" -#include "llerror.h" -#include "llexception.h" /** * Like LLListenerOrPumpName, this is a class intended for parameter lists: @@ -147,117 +143,29 @@ LLSD suspendUntilEventOn(const LLEventPumpOrPumpName& pump) return postAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump); } +/// Like postAndSuspend(), but if we wait longer than @a timeout seconds, +/// stop waiting and return @a timeoutResult instead. +LLSD postAndSuspendWithTimeout(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, + const LLSD& replyPumpNamePath, + F32 timeout, const LLSD& timeoutResult); + /// Suspend the coroutine until an event is fired on the identified pump /// or the timeout duration has elapsed. If the timeout duration /// elapses the specified LLSD is returned. -LLSD suspendUntilEventOnWithTimeout(const LLEventPumpOrPumpName& suspendPumpOrName, F32 timeoutin, const LLSD &timeoutResult); - -} // namespace llcoro - -/// return type for two-pump variant of suspendUntilEventOn() -typedef std::pair LLEventWithID; - -namespace llcoro -{ - -/** - * This function waits for a reply on either of two specified LLEventPumps. - * Otherwise, it closely resembles postAndSuspend(); 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 postAndSuspend() for both signatures. - * But consider the following ambiguous call: - * @code - * postAndSuspend(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 postAndSuspend2() than to write either - * LLSD("someString") or LLEventOrPumpName("someString"). - */ -LLEventWithID postAndSuspend2(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump0, - const LLEventPumpOrPumpName& replyPump1, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()); - -/** - * Wait for the next event on either of two specified LLEventPumps. - */ inline -LLEventWithID -suspendUntilEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +LLSD suspendUntilEventOnWithTimeout(const LLEventPumpOrPumpName& suspendPumpOrName, + F32 timeoutin, const LLSD &timeoutResult) { - // This is now a convenience wrapper for postAndSuspend2(). - return postAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + return postAndSuspendWithTimeout(LLSD(), // event + LLEventPumpOrPumpName(), // requestPump + suspendPumpOrName, // replyPump + LLSD(), // replyPumpNamePath + timeoutin, + timeoutResult); } -/** - * Helper for the two-pump variant of suspendUntilEventOn(), e.g.: - * - * @code - * LLSD reply = errorException(suspendUntilEventOn(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); - -} // namespace llcoro - -/** - * 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 LLException -{ -public: - LLErrorEvent(const std::string& what, const LLSD& data): - LLException(what), - mData(data) - {} - virtual ~LLErrorEvent() throw() {} - - LLSD getData() const { return mData; } - -private: - LLSD mData; -}; - -namespace llcoro -{ - -/** - * 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); - } // namespace llcoro /** @@ -304,84 +212,4 @@ 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 getNames() const - { - return std::pair(mPump0.getName(), mPump1.getName()); - } - - /// request pump 0 - LLEventPump& getPump0() { return mPump0; } - /// request pump 1 - LLEventPump& getPump1() { return mPump1; } - - /// suspendUntilEventOn(either of our two LLEventPumps) - LLEventWithID suspend() - { - return llcoro::suspendUntilEventOn(mPump0, mPump1); - } - - /// errorException(suspend()) - LLSD suspendWithException() - { - return llcoro::errorException(suspend(), std::string("Error event on ") + getName1()); - } - - /// errorLog(suspend()) - LLSD suspendWithLog() - { - return llcoro::errorLog(suspend(), std::string("Error event on ") + getName1()); - } - - LLEventWithID postAndSuspend(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) - { - return llcoro::postAndSuspend2(event, requestPump, mPump0, mPump1, - replyPump0NamePath, replyPump1NamePath); - } - - LLSD postAndSuspendWithException(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) - { - return llcoro::errorException(postAndSuspend(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); - } - - LLSD postAndSuspendWithLog(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) - { - return llcoro::errorLog(postAndSuspend(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); - } - -private: - LLEventStream mPump0, mPump1; -}; - #endif /* ! defined(LL_LLEVENTCORO_H) */ diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp index 9fb18dc67d..4cded7f88e 100644 --- a/indra/llcommon/lleventfilter.cpp +++ b/indra/llcommon/lleventfilter.cpp @@ -37,6 +37,9 @@ // other Linden headers #include "llerror.h" // LL_ERRS #include "llsdutil.h" // llsd_matches() +#include "stringize.h" +#include "lleventtimer.h" +#include "lldate.h" /***************************************************************************** * LLEventFilter @@ -182,6 +185,27 @@ bool LLEventTimeout::countdownElapsed() const return mTimer.hasExpired(); } +LLEventTimer* LLEventTimeout::post_every(F32 period, const std::string& pump, const LLSD& data) +{ + return LLEventTimer::run_every( + period, + [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); }); +} + +LLEventTimer* LLEventTimeout::post_at(const LLDate& time, const std::string& pump, const LLSD& data) +{ + return LLEventTimer::run_at( + time, + [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); }); +} + +LLEventTimer* LLEventTimeout::post_after(F32 interval, const std::string& pump, const LLSD& data) +{ + return LLEventTimer::run_after( + interval, + [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); }); +} + /***************************************************************************** * LLEventBatch *****************************************************************************/ @@ -409,3 +433,61 @@ void LLEventBatchThrottle::setSize(std::size_t size) flush(); } } + +/***************************************************************************** +* LLEventLogProxy +*****************************************************************************/ +LLEventLogProxy::LLEventLogProxy(LLEventPump& source, const std::string& name, bool tweak): + // note: we are NOT using the constructor that implicitly connects! + LLEventFilter(name, tweak), + // instead we simply capture a reference to the subject LLEventPump + mPump(source) +{ +} + +bool LLEventLogProxy::post(const LLSD& event) /* override */ +{ + auto counter = mCounter++; + auto eventplus = event; + if (eventplus.type() == LLSD::TypeMap) + { + eventplus["_cnt"] = counter; + } + std::string hdr{STRINGIZE(getName() << ": post " << counter)}; + LL_INFOS("LogProxy") << hdr << ": " << event << LL_ENDL; + bool result = mPump.post(eventplus); + LL_INFOS("LogProxy") << hdr << " => " << result << LL_ENDL; + return result; +} + +LLBoundListener LLEventLogProxy::listen_impl(const std::string& name, + const LLEventListener& target, + const NameList& after, + const NameList& before) +{ + LL_DEBUGS("LogProxy") << "LLEventLogProxy('" << getName() << "').listen('" + << name << "')" << LL_ENDL; + return mPump.listen(name, + [this, name, target](const LLSD& event)->bool + { return listener(name, target, event); }, + after, + before); +} + +bool LLEventLogProxy::listener(const std::string& name, + const LLEventListener& target, + const LLSD& event) const +{ + auto eventminus = event; + std::string counter{"**"}; + if (eventminus.has("_cnt")) + { + counter = stringize(eventminus["_cnt"].asInteger()); + eventminus.erase("_cnt"); + } + std::string hdr{STRINGIZE(getName() << " to " << name << " " << counter)}; + LL_INFOS("LogProxy") << hdr << ": " << eventminus << LL_ENDL; + bool result = target(eventminus); + LL_INFOS("LogProxy") << hdr << " => " << result << LL_ENDL; + return result; +} diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h index ff8fc9bc7f..48c2570732 100644 --- a/indra/llcommon/lleventfilter.h +++ b/indra/llcommon/lleventfilter.h @@ -32,8 +32,12 @@ #include "llevents.h" #include "stdtypes.h" #include "lltimer.h" +#include "llsdutil.h" #include +class LLEventTimer; +class LLDate; + /** * Generic base class */ @@ -210,6 +214,19 @@ public: LLEventTimeout(); LLEventTimeout(LLEventPump& source); + /// using LLEventTimeout as namespace for free functions + /// Post event to specified LLEventPump every period seconds. Delete + /// returned LLEventTimer* to cancel. + static LLEventTimer* post_every(F32 period, const std::string& pump, const LLSD& data); + /// Post event to specified LLEventPump at specified future time. Call + /// LLEventTimer::getInstance(returned pointer) to check whether it's still + /// pending; if so, delete the pointer to cancel. + static LLEventTimer* post_at(const LLDate& time, const std::string& pump, const LLSD& data); + /// Post event to specified LLEventPump after specified interval. Call + /// LLEventTimer::getInstance(returned pointer) to check whether it's still + /// pending; if so, delete the pointer to cancel. + static LLEventTimer* post_after(F32 interval, const std::string& pump, const LLSD& data); + protected: virtual void setCountdown(F32 seconds); virtual bool countdownElapsed() const; @@ -376,4 +393,149 @@ private: std::size_t mBatchSize; }; +/** + * LLStoreListener self-registers on the LLEventPump of interest, and + * unregisters on destruction. As long as it exists, a particular element is + * extracted from every event that comes through the upstream LLEventPump and + * stored into the target variable. + * + * This is implemented as a subclass of LLEventFilter, though strictly + * speaking it isn't really a "filter" at all: it never passes incoming events + * to its own listeners, if any. + * + * TBD: A variant based on output iterators that stores and then increments + * the iterator. Useful with boost::coroutine2! + */ +template +class LLStoreListener: public LLEventFilter +{ +public: + // pass target and optional path to element + LLStoreListener(T& target, const LLSD& path=LLSD(), bool consume=false): + LLEventFilter("store"), + mTarget(target), + mPath(path), + mConsume(consume) + {} + // construct and connect + LLStoreListener(LLEventPump& source, T& target, const LLSD& path=LLSD(), bool consume=false): + LLEventFilter(source, "store"), + mTarget(target), + mPath(path), + mConsume(consume) + {} + + // Calling post() with an LLSD event extracts the element indicated by + // path, then stores it to mTarget. + virtual bool post(const LLSD& event) + { + // Extract the element specified by 'mPath' from 'event'. To perform a + // generic type-appropriate store through mTarget, construct an + // LLSDParam and store that, thus engaging LLSDParam's custom + // conversions. + mTarget = LLSDParam(llsd::drill(event, mPath)); + return mConsume; + } + +private: + T& mTarget; + const LLSD mPath; + const bool mConsume; +}; + +/***************************************************************************** +* LLEventLogProxy +*****************************************************************************/ +/** + * LLEventLogProxy is a little different than the other LLEventFilter + * subclasses declared in this header file, in that it completely wraps the + * passed LLEventPump (both input and output) instead of simply processing its + * output. Of course, if someone directly posts to the wrapped LLEventPump by + * looking up its string name in LLEventPumps, LLEventLogProxy can't intercept + * that post() call. But as long as consuming code is willing to access the + * LLEventLogProxy instance instead of the wrapped LLEventPump, all event data + * both post()ed and received is logged. + * + * The proxy role means that LLEventLogProxy intercepts more of LLEventPump's + * API than a typical LLEventFilter subclass. + */ +class LLEventLogProxy: public LLEventFilter +{ + typedef LLEventFilter super; +public: + /** + * Construct LLEventLogProxy, wrapping the specified LLEventPump. + * Unlike a typical LLEventFilter subclass, the name parameter is @emph + * not optional because typically you want LLEventLogProxy to completely + * replace the wrapped LLEventPump. So you give the subject LLEventPump + * some other name and give the LLEventLogProxy the name that would have + * been used for the subject LLEventPump. + */ + LLEventLogProxy(LLEventPump& source, const std::string& name, bool tweak=false); + + /// register a new listener + LLBoundListener listen_impl(const std::string& name, const LLEventListener& target, + const NameList& after, const NameList& before); + + /// Post an event to all listeners + virtual bool post(const LLSD& event) /* override */; + +private: + /// This method intercepts each call to any target listener. We pass it + /// the listener name and the caller's intended target listener plus the + /// posted LLSD event. + bool listener(const std::string& name, + const LLEventListener& target, + const LLSD& event) const; + + LLEventPump& mPump; + LLSD::Integer mCounter{0}; +}; + +/** + * LLEventPumpHolder is a helper for LLEventLogProxyFor. It simply + * stores an instance of T, presumably a subclass of LLEventPump. We derive + * LLEventLogProxyFor from LLEventPumpHolder, ensuring that + * LLEventPumpHolder's contained mWrappedPump is fully constructed before + * passing it to LLEventLogProxyFor's LLEventLogProxy base class constructor. + * But since LLEventPumpHolder presents none of the LLEventPump API, + * LLEventLogProxyFor inherits its methods unambiguously from + * LLEventLogProxy. + */ +template +class LLEventPumpHolder +{ +protected: + LLEventPumpHolder(const std::string& name, bool tweak=false): + mWrappedPump(name, tweak) + {} + T mWrappedPump; +}; + +/** + * LLEventLogProxyFor is a wrapper around any of the LLEventPump subclasses. + * Instantiating an LLEventLogProxy instantiates an internal T. Otherwise + * it behaves like LLEventLogProxy. + */ +template +class LLEventLogProxyFor: private LLEventPumpHolder, public LLEventLogProxy +{ + // We derive privately from LLEventPumpHolder because it's an + // implementation detail of LLEventLogProxyFor. The only reason it's a + // base class at all is to guarantee that it's constructed first so we can + // pass it to our LLEventLogProxy base class constructor. + typedef LLEventPumpHolder holder; + typedef LLEventLogProxy super; + +public: + LLEventLogProxyFor(const std::string& name, bool tweak=false): + // our wrapped LLEventPump subclass instance gets a name suffix + // because that's not the LLEventPump we want consumers to obtain when + // they ask LLEventPumps for this name + holder(name + "-", tweak), + // it's our LLEventLogProxy that gets the passed name + super(holder::mWrappedPump, name, tweak) + {} +}; + #endif /* ! defined(LL_LLEVENTFILTER_H) */ diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 95aaaeb58b..64fb985951 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -45,7 +45,7 @@ #include // external library headers #include -#include // for abnormal_exit +#include #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no @@ -63,52 +63,24 @@ #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)) -{ -} + mFactories + { + { "LLEventStream", [](const std::string& name, bool tweak) + { return new LLEventStream(name, tweak); } }, + { "LLEventMailDrop", [](const std::string& name, bool tweak) + { return new LLEventMailDrop(name, tweak); } } + }, + mTypes + { + // LLEventStream is the default for obtain(), so even if somebody DOES + // call obtain("placeholder"), this sample entry won't break anything. + { "placeholder", "LLEventStream" } + } +{} LLEventPump& LLEventPumps::obtain(const std::string& name) { @@ -119,14 +91,31 @@ LLEventPump& LLEventPumps::obtain(const std::string& name) // 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); + + // Here we must instantiate an LLEventPump subclass. Is there a + // preregistered class name override for this specific instance name? + auto nfound = mTypes.find(name); + std::string type; + if (nfound != mTypes.end()) + { + type = nfound->second; + } + // pass tweak=false: we already know there's no existing instance with + // this name + return make(name, false, type); +} + +LLEventPump& LLEventPumps::make(const std::string& name, bool tweak, + const std::string& type) +{ + // find the relevant factory for this (or default) type + auto found = mFactories.find(type.empty()? "LLEventStream" : type); + if (found == mFactories.end()) + { + // Passing an unrecognized type name is a no-no + LLTHROW(BadType(type)); + } + auto newInstance = (found->second)(name, tweak); // LLEventPump's constructor implicitly registers each new instance in // mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll // delete it later. @@ -144,14 +133,23 @@ bool LLEventPumps::post(const std::string&name, const LLSD&message) return (*found).second->post(message); } - 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) + for (PumpMap::value_type& pair : mPumpMap) { - pmi->second->flush(); + pair.second->flush(); + } +} + +void LLEventPumps::clear() +{ + // Clear every known LLEventPump instance. Leave it up to each instance to + // decide what to do with the clear() call. + for (PumpMap::value_type& pair : mPumpMap) + { + pair.second->clear(); } } @@ -159,9 +157,9 @@ 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) + for (PumpMap::value_type& pair : mPumpMap) { - pmi->second->reset(); + pair.second->reset(); } } @@ -268,6 +266,9 @@ LLEventPumps::~LLEventPumps() { delete *mOurPumps.begin(); } + // Reset every remaining registered LLEventPump subclass instance: those + // we DIDN'T instantiate using either make() or obtain(). + reset(); } /***************************************************************************** @@ -284,7 +285,7 @@ LLEventPump::LLEventPump(const std::string& name, bool tweak): // Register every new instance with LLEventPumps mRegistry(LLEventPumps::instance().getHandle()), mName(mRegistry.get()->registerNew(*this, name, tweak)), - mSignal(new LLStandardSignal()), + mSignal(boost::make_shared()), mEnabled(true) {} @@ -312,6 +313,14 @@ std::string LLEventPump::inventName(const std::string& pfx) return STRINGIZE(pfx << suffix++); } +void LLEventPump::clear() +{ + // Destroy the original LLStandardSignal instance, replacing it with a + // whole new one. + mSignal = boost::make_shared(); + mConnections.clear(); +} + void LLEventPump::reset() { mSignal.reset(); @@ -350,8 +359,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL // is only when the existing connection object is still connected. if (found != mConnections.end() && found->second.connected()) { - LLTHROW(DupListenerName("Attempt to register duplicate listener name '" + name + - "' on " + typeid(*this).name() + " '" + getName() + "'")); + LLTHROW(DupListenerName("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 @@ -377,8 +386,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL // 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); - LLTHROW(Cycle("New listener '" + name + "' on " + typeid(*this).name() + - " '" + getName() + "' would cause cycle: " + e.what())); + LLTHROW(Cycle("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; @@ -442,7 +451,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL // NOW remove the offending listener node. mDeps.remove(name); // Having constructed a description of the order change, inform caller. - LLTHROW(OrderChange(out.str())); + LLTHROW(OrderChange(out.str())); } // This node becomes the previous one. previous = dmi->second; @@ -538,15 +547,7 @@ bool LLEventStream::post(const LLSD& event) // Let caller know if any one listener handled the event. This is mostly // useful when using LLEventStream as a listener for an upstream // LLEventPump. - - // FIRE-19481; do not let any abnormal_exit propagate - // return (*signal)(event); - - try { return (*signal)(event); - }catch( boost::dcoroutines::abnormal_exit& ) - { return false; } - // } /***************************************************************************** @@ -562,7 +563,7 @@ bool LLEventMailDrop::post(const LLSD& event) // be posted to any future listeners when they attach. mEventHistory.push_back(event); } - + return posted; } @@ -592,46 +593,9 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, return LLEventStream::listen_impl(name, listener, after, before); } - -/***************************************************************************** -* LLEventQueue -*****************************************************************************/ -bool LLEventQueue::post(const LLSD& event) +void LLEventMailDrop::discard() { - 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 signal(mSignal); - for ( ; ! queue.empty(); queue.pop_front()) - { - (*signal)(queue.front()); - } + mEventHistory.clear(); } /***************************************************************************** diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 62d97007ac..e380c108f4 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -37,6 +37,7 @@ #include #include #include +#include #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch @@ -55,7 +56,6 @@ #include #include // reference_wrapper #include -#include #include #include "llsd.h" #include "llsingleton.h" @@ -211,8 +211,7 @@ public: /// exception if you try to call when empty struct Empty: public LLException { - Empty(const std::string& what): - LLException(std::string("LLListenerOrPumpName::Empty: ") + what) {} + Empty(const std::string& what): LLException("LLListenerOrPumpName::Empty: " + what) {} }; private: @@ -247,6 +246,30 @@ public: */ LLEventPump& obtain(const std::string& name); + /// exception potentially thrown by make() + struct BadType: public LLException + { + BadType(const std::string& what): LLException("BadType: " + what) {} + }; + + /** + * Create an LLEventPump with suggested name (optionally of specified + * LLEventPump subclass type). As with obtain(), LLEventPumps owns the new + * instance. + * + * As with LLEventPump's constructor, make() could throw + * LLEventPump::DupPumpName unless you pass tweak=true. + * + * As with a hand-constructed LLEventPump subclass, if you pass + * tweak=true, the tweaked name can be obtained by LLEventPump::getName(). + * + * Pass empty type to get the default LLEventStream. + * + * If you pass an unrecognized type string, make() throws BadType. + */ + LLEventPump& make(const std::string& name, bool tweak=false, + const std::string& type=std::string()); + /** * Find the named LLEventPump instance. If it exists post the message to it. * If the pump does not exist, do nothing. @@ -263,6 +286,11 @@ public: */ void flush(); + /** + * Disconnect listeners from all known LLEventPump instances + */ + void clear(); + /** * Reset all known LLEventPump instances * workaround for DEV-35406 crash on shutdown @@ -298,43 +326,21 @@ testable: // destroyed. typedef std::set PumpSet; PumpSet mOurPumps; - // LLEventPump names that should be instantiated as LLEventQueue rather - // than as LLEventStream - typedef std::set PumpNames; - PumpNames mQueueNames; + // for make(), map string type name to LLEventPump subclass factory function + typedef std::map> PumpFactories; + // Data used by make(). + // One might think mFactories and mTypes could reasonably be static. So + // they could -- if not for the fact that make() or obtain() might be + // called before this module's static variables have been initialized. + // This is why we use singletons in the first place. + PumpFactories mFactories; + + // for obtain(), map desired string instance name to string type when + // obtain() must create the instance + typedef std::map InstanceTypes; + InstanceTypes mTypes; }; -/***************************************************************************** -* details -*****************************************************************************/ -namespace LLEventDetail -{ - /// Any callable capable of connecting an LLEventListener to an - /// LLStandardSignal to produce an LLBoundListener can be mapped to this - /// signature. - typedef boost::function ConnectFunc; - - /// overload of visit_and_connect() when we have a string identifier available - template - LLBoundListener visit_and_connect(const std::string& name, - const LISTENER& listener, - const ConnectFunc& connect_func); - /** - * Utility template function to use Visitor appropriately - * - * @param listener Callable to connect, typically a boost::bind() - * expression. This will be visited by Visitor using boost::visit_each(). - * @param connect_func Callable that will connect() @a listener to an - * LLStandardSignal, returning LLBoundListener. - */ - template - LLBoundListener visit_and_connect(const LISTENER& listener, - const ConnectFunc& connect_func) - { - return visit_and_connect("", listener, connect_func); - } -} // namespace LLEventDetail - /***************************************************************************** * LLEventTrackable *****************************************************************************/ @@ -369,11 +375,6 @@ namespace LLEventDetail * instance, it attempts to dereference the Foo* pointer that was * deleted but not zeroed.) * - Undefined behavior results. - * If you suspect you may encounter any such scenario, you're better off - * managing the lifespan of your object with boost::shared_ptr. - * Passing LLEventPump::listen() a boost::bind() expression - * involving a boost::weak_ptr is recognized specially, engaging - * thread-safe Boost.Signals2 machinery. */ typedef boost::signals2::trackable LLEventTrackable; @@ -382,7 +383,7 @@ typedef boost::signals2::trackable LLEventTrackable; *****************************************************************************/ /** * LLEventPump is the base class interface through which we access the - * concrete subclasses LLEventStream and LLEventQueue. + * concrete subclasses such as LLEventStream. * * @NOTE * LLEventPump derives from LLEventTrackable so that when you "chain" @@ -403,8 +404,7 @@ public: */ struct DupPumpName: public LLException { - DupPumpName(const std::string& what): - LLException(std::string("DupPumpName: ") + what) {} + DupPumpName(const std::string& what): LLException("DupPumpName: " + what) {} }; /** @@ -440,9 +440,7 @@ public: */ struct DupListenerName: public ListenError { - DupListenerName(const std::string& what): - ListenError(std::string("DupListenerName: ") + what) - {} + DupListenerName(const std::string& what): ListenError("DupListenerName: " + what) {} }; /** * exception thrown by listen(). The order dependencies specified for your @@ -454,7 +452,7 @@ public: */ struct Cycle: public ListenError { - Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {} + Cycle(const std::string& what): ListenError("Cycle: " + what) {} }; /** * exception thrown by listen(). This one means that your new listener @@ -475,7 +473,7 @@ public: */ struct OrderChange: public ListenError { - OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {} + OrderChange(const std::string& what): ListenError("OrderChange: " + what) {} }; /// used by listen() @@ -512,44 +510,13 @@ public: * the result be assigned to a LLTempBoundListener or the listener is * manually disconnected when no longer needed since there will be no * way to later find and disconnect this listener manually. - * - * If (as is typical) you pass a boost::bind() expression as @a - * listener, listen() will inspect the components of that expression. If a - * bound object matches any of several cases, the connection will - * automatically be disconnected when that object is destroyed. - * - * * You bind a boost::weak_ptr. - * * Binding a boost::shared_ptr that way would ensure that the - * referenced object would @em never be destroyed, since the @c - * shared_ptr stored in the LLEventPump would remain an outstanding - * reference. Use the weaken() function to convert your @c shared_ptr to - * @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr - * will produce a compile error (@c BOOST_STATIC_ASSERT failure). - * * You bind a simple pointer or reference to an object derived from - * boost::enable_shared_from_this. (UNDER CONSTRUCTION) - * * You bind a simple pointer or reference to an object derived from - * LLEventTrackable. Unlike the cases described above, though, this is - * vulnerable to a couple of cross-thread race conditions, as described - * in the LLEventTrackable documentation. */ - template - LLBoundListener listen(const std::string& name, const LISTENER& listener, + LLBoundListener listen(const std::string& name, + const LLEventListener& listener, const NameList& after=NameList(), const NameList& before=NameList()) { - // Examine listener, using our listen_impl() method to make the - // actual connection. - // This is why listen() is a template. Conversion from boost::bind() - // to LLEventListener performs type erasure, so it's important to look - // at the boost::bind object itself before that happens. - return LLEventDetail::visit_and_connect(name, - listener, - boost::bind(&LLEventPump::listen_invoke, - this, - name, - _1, - after, - before)); + return listen_impl(name, listener, after, before); } /// Get the LLBoundListener associated with the passed name (dummy @@ -587,19 +554,12 @@ public: private: friend class LLEventPumps; - + virtual void clear(); virtual void reset(); private: - LLBoundListener listen_invoke(const std::string& name, const LLEventListener& listener, - const NameList& after, - const NameList& before) - { - return this->listen_impl(name, listener, after, before); - } - // must precede mName; see LLEventPump::LLEventPump() LLHandle mRegistry; @@ -663,11 +623,10 @@ public: * event *must* eventually reach a listener that will consume it, else the * queue will grow to arbitrary length. * - * @NOTE: When using an LLEventMailDrop (or LLEventQueue) with a LLEventTimeout or + * @NOTE: When using an LLEventMailDrop with an LLEventTimeout or * LLEventFilter attaching the filter downstream, using Timeout's constructor will * cause the MailDrop to discharge any of its stored events. The timeout should * instead be connected upstream using its listen() method. - * See llcoro::suspendUntilEventOnWithTimeout() for an example. */ class LL_COMMON_API LLEventMailDrop : public LLEventStream { @@ -679,7 +638,8 @@ public: virtual bool post(const LLSD& event) override; /// Remove any history stored in the mail drop. - virtual void flush() override { mEventHistory.clear(); LLEventStream::flush(); }; + void discard(); + protected: virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, const NameList& after, @@ -690,30 +650,6 @@ private: EventList mEventHistory; }; -/***************************************************************************** -* LLEventQueue -*****************************************************************************/ -/** - * LLEventQueue is a LLEventPump whose post() method defers calling registered - * listeners until flush() is called. - */ -class LL_COMMON_API LLEventQueue: public LLEventPump -{ -public: - LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {} - virtual ~LLEventQueue() {} - - /// Post an event to all listeners - virtual bool post(const LLSD& event); - - /// flush queued events - virtual void flush(); - -private: - typedef std::deque EventQueue; - EventQueue mEventQueue; -}; - /***************************************************************************** * LLReqID *****************************************************************************/ @@ -809,329 +745,6 @@ private: LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey="reply"); -/** - * Base class for LLListenerWrapper. See visit_and_connect() and llwrap(). We - * provide virtual @c accept_xxx() methods, customization points allowing a - * subclass access to certain data visible at LLEventPump::listen() time. - * Example subclass usage: - * - * @code - * myEventPump.listen("somename", - * llwrap(boost::bind(&MyClass::method, instance, _1))); - * @endcode - * - * Because of the anticipated usage (note the anonymous temporary - * MyListenerWrapper instance in the example above), the @c accept_xxx() - * methods must be @c const. - */ -class LL_COMMON_API LLListenerWrapperBase -{ -public: - /// New instance. The accept_xxx() machinery makes it important to use - /// shared_ptrs for our data. Many copies of this object are made before - /// the instance that actually ends up in the signal, yet accept_xxx() - /// will later be called on the @em original instance. All copies of the - /// same original instance must share the same data. - LLListenerWrapperBase(): - mName(new std::string), - mConnection(new LLBoundListener) - { - } - - /// Copy constructor. Copy shared_ptrs to original instance data. - LLListenerWrapperBase(const LLListenerWrapperBase& that): - mName(that.mName), - mConnection(that.mConnection) - { - } - virtual ~LLListenerWrapperBase() {} - - /// Ask LLEventPump::listen() for the listener name - virtual void accept_name(const std::string& name) const - { - *mName = name; - } - - /// Ask LLEventPump::listen() for the new connection - virtual void accept_connection(const LLBoundListener& connection) const - { - *mConnection = connection; - } - -protected: - /// Listener name. - boost::shared_ptr mName; - /// Connection. - boost::shared_ptr mConnection; -}; - -/***************************************************************************** -* Underpinnings -*****************************************************************************/ -/** - * We originally provided a suite of overloaded - * LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call - * LLEventPump::listen(...) and then pass the returned LLBoundListener to - * LLEventTrackable::track(). This was workable but error-prone: the coder - * must remember to call listenTo() rather than the more straightforward - * listen() method. - * - * Now we publish only the single canonical listen() method, so there's a - * uniform mechanism. Having a single way to do this is good, in that there's - * no question in the coder's mind which of several alternatives to choose. - * - * To support automatic connection management, we use boost::visit_each - * (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to - * inspect each argument of a boost::bind expression. (Although the visit_each - * mechanism was first introduced with the original Boost.Signals library, it - * was only later documented.) - * - * Cases: - * * At least one of the function's arguments is a boost::weak_ptr. Pass - * the corresponding shared_ptr to slot_type::track(). Ideally that would be - * the object whose method we want to call, but in fact we do the same for - * any weak_ptr we might find among the bound arguments. If we're passing - * our bound method a weak_ptr to some object, wouldn't the destruction of - * that object invalidate the call? So we disconnect automatically when any - * such object is destroyed. This is the mechanism preferred by boost:: - * signals2. - * * One of the functions's arguments is a boost::shared_ptr. This produces - * a compile error: the bound copy of the shared_ptr stored in the - * boost_bind object stored in the signal object would make the referenced - * T object immortal. We provide a weaken() function. Pass - * weaken(your_shared_ptr) instead. (We can inspect, but not modify, the - * boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr - * implicitly and just proceed.) - * * One of the function's arguments is a plain pointer/reference to an object - * derived from boost::enable_shared_from_this. We assume that this object - * is managed using boost::shared_ptr, so we implicitly extract a shared_ptr - * and track that. (UNDER CONSTRUCTION) - * * One of the function's arguments is derived from LLEventTrackable. Pass - * the LLBoundListener to its LLEventTrackable::track(). This is vulnerable - * to a couple different race conditions, as described in LLEventTrackable - * documentation. (NOTE: Now that LLEventTrackable is a typedef for - * boost::signals2::trackable, the Signals2 library handles this itself, so - * our visitor needs no special logic for this case.) - * * Any other argument type is irrelevant to automatic connection management. - */ - -namespace LLEventDetail -{ - template - const F& unwrap(const F& f) { return f; } - - template - const F& unwrap(const boost::reference_wrapper& f) { return f.get(); } - - // Most of the following is lifted from the Boost.Signals use of - // visit_each. - template struct truth {}; - - /** - * boost::visit_each() Visitor, used on a template argument const F& - * f as follows (see visit_and_connect()): - * @code - * LLEventListener listener(f); - * Visitor visitor(listener); // bind listener so it can track() shared_ptrs - * using boost::visit_each; // allow unqualified visit_each() call for ADL - * visit_each(visitor, unwrap(f)); - * @endcode - */ - class Visitor - { - public: - /** - * Visitor binds a reference to LLEventListener so we can track() any - * shared_ptrs we find in the argument list. - */ - Visitor(LLEventListener& listener): - mListener(listener) - { - } - - /** - * boost::visit_each() calls this method for each component of a - * boost::bind() expression. - */ - template - void operator()(const T& t) const - { - decode(t, 0); - } - - private: - // decode() decides between a reference wrapper and anything else - // boost::ref() variant - template - void decode(const boost::reference_wrapper& t, int) const - { -// add_if_trackable(t.get_pointer()); - } - - // decode() anything else - template - void decode(const T& t, long) const - { - typedef truth<(boost::is_pointer::value)> is_a_pointer; - maybe_get_pointer(t, is_a_pointer()); - } - - // maybe_get_pointer() decides between a pointer and a non-pointer - // plain pointer variant - template - void maybe_get_pointer(const T& t, truth) const - { -// add_if_trackable(t); - } - - // shared_ptr variant - template - void maybe_get_pointer(const boost::shared_ptr& t, truth) const - { - // If we have a shared_ptr to this object, it doesn't matter - // whether the object is derived from LLEventTrackable, so no - // further analysis of T is needed. -// mListener.track(t); - - // Make this case illegal. Passing a bound shared_ptr to - // slot_type::track() is useless, since the bound shared_ptr will - // keep the object alive anyway! Force the coder to cast to weak_ptr. - - // Trivial as it is, make the BOOST_STATIC_ASSERT() condition - // dependent on template param so the macro is only evaluated if - // this method is in fact instantiated, as described here: - // http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html - - // ATTENTION: Don't bind a shared_ptr using - // LLEventPump::listen(boost::bind()). Doing so captures a copy of - // the shared_ptr, making the referenced object effectively - // immortal. Use the weaken() function, e.g.: - // somepump.listen(boost::bind(...weaken(my_shared_ptr)...)); - // This lets us automatically disconnect when the referenced - // object is destroyed. - BOOST_STATIC_ASSERT(sizeof(T) == 0); - } - - // weak_ptr variant - template - void maybe_get_pointer(const boost::weak_ptr& t, truth) const - { - // If we have a weak_ptr to this object, it doesn't matter - // whether the object is derived from LLEventTrackable, so no - // further analysis of T is needed. - mListener.track(t); -// std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n"; - } - -#if 0 - // reference to anything derived from boost::enable_shared_from_this - template - inline void maybe_get_pointer(const boost::enable_shared_from_this& ct, - truth) const - { - // Use the slot_type::track(shared_ptr) mechanism. Cast away - // const-ness because (in our code base anyway) it's unusual - // to find shared_ptr. - boost::enable_shared_from_this& - t(const_cast&>(ct)); - std::cout << "Capturing shared_from_this()" << std::endl; - boost::shared_ptr sp(t.shared_from_this()); -/*==========================================================================*| - std::cout << "Capturing weak_ptr" << std::endl; - boost::weak_ptr wp(sp); -|*==========================================================================*/ - std::cout << "Tracking shared__ptr" << std::endl; - mListener.track(sp); - } -#endif - - // non-pointer variant - template - void maybe_get_pointer(const T& t, truth) const - { - // Take the address of this object, because the object itself may be - // trackable -// add_if_trackable(boost::addressof(t)); - } - -/*==========================================================================*| - // add_if_trackable() adds LLEventTrackable objects to mTrackables - inline void add_if_trackable(const LLEventTrackable* t) const - { - if (t) - { - } - } - - // pointer to anything not an LLEventTrackable subclass - inline void add_if_trackable(const void*) const - { - } - - // pointer to free function - // The following construct uses the preprocessor to generate - // add_if_trackable() overloads accepting pointer-to-function taking - // 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type. -#define BOOST_PP_LOCAL_MACRO(n) \ - template \ - inline void \ - add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const \ - { \ - } -#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY) -#include BOOST_PP_LOCAL_ITERATE() -#undef BOOST_PP_LOCAL_MACRO -#undef BOOST_PP_LOCAL_LIMITS -|*==========================================================================*/ - - /// Bind a reference to the LLEventListener to call its track() method. - LLEventListener& mListener; - }; - - /** - * Utility template function to use Visitor appropriately - * - * @param raw_listener Callable to connect, typically a boost::bind() - * expression. This will be visited by Visitor using boost::visit_each(). - * @param connect_funct Callable that will connect() @a raw_listener to an - * LLStandardSignal, returning LLBoundListener. - */ - template - LLBoundListener visit_and_connect(const std::string& name, - const LISTENER& raw_listener, - const ConnectFunc& connect_func) - { - // Capture the listener - LLEventListener listener(raw_listener); - // Define our Visitor, binding the listener so we can call - // listener.track() if we discover any shared_ptr. - LLEventDetail::Visitor visitor(listener); - // Allow unqualified visit_each() call for ADL - using boost::visit_each; - // Visit each component of a boost::bind() expression. Pass - // 'raw_listener', our template argument, rather than 'listener' from - // which type details have been erased. unwrap() comes from - // Boost.Signals, in case we were passed a boost::ref(). - visit_each(visitor, LLEventDetail::unwrap(raw_listener)); - // Make the connection using passed function. - LLBoundListener connection(connect_func(listener)); - // If the LISTENER is an LLListenerWrapperBase subclass, pass it the - // desired information. It's important that we pass the raw_listener - // so the compiler can make decisions based on its original type. - const LLListenerWrapperBase* lwb = - ll_template_cast(&raw_listener); - if (lwb) - { - lwb->accept_name(name); - lwb->accept_connection(connection); - } - // In any case, show new connection to caller. - return connection; - } -} // namespace LLEventDetail - // Somewhat to my surprise, passing boost::bind(...boost::weak_ptr...) to // listen() fails in Boost code trying to instantiate LLEventListener (i.e. // LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't @@ -1142,12 +755,4 @@ namespace boost T* get_pointer(const weak_ptr& ptr) { return shared_ptr(ptr).get(); } } -/// Since we forbid use of listen(boost::bind(...shared_ptr...)), provide an -/// easy way to cast to the corresponding weak_ptr. -template -boost::weak_ptr weaken(const boost::shared_ptr& ptr) -{ - return boost::weak_ptr(ptr); -} - #endif /* ! defined(LL_LLEVENTS_H) */ diff --git a/indra/llcommon/lleventtimer.cpp b/indra/llcommon/lleventtimer.cpp index 05419d452f..f575a7b6bf 100644 --- a/indra/llcommon/lleventtimer.cpp +++ b/indra/llcommon/lleventtimer.cpp @@ -57,35 +57,17 @@ LLEventTimer::~LLEventTimer() //static void LLEventTimer::updateClass() { - std::list completed_timers; - - // Minimize calls to getInstances per frame - // for (instance_iter iter = beginInstances(); iter != endInstances(); ) - - instance_iter end = endInstances(); - for (instance_iter iter = beginInstances(); iter != end; ) - // + for (auto& timer : instance_snapshot()) { - LLEventTimer& timer = *iter++; F32 et = timer.mEventTimer.getElapsedTimeF32(); if (timer.mEventTimer.getStarted() && et > timer.mPeriod) { timer.mEventTimer.reset(); if ( timer.tick() ) { - completed_timers.push_back( &timer ); + delete &timer; } } } - - if ( completed_timers.size() > 0 ) - { - for (std::list::iterator completed_iter = completed_timers.begin(); - completed_iter != completed_timers.end(); - completed_iter++ ) - { - delete *completed_iter; - } - } } diff --git a/indra/llcommon/lleventtimer.h b/indra/llcommon/lleventtimer.h index dc918121e1..dbbfe0c6e6 100644 --- a/indra/llcommon/lleventtimer.h +++ b/indra/llcommon/lleventtimer.h @@ -40,16 +40,83 @@ public: LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds LLEventTimer(const LLDate& time); virtual ~LLEventTimer(); - + //function to be called at the supplied frequency // Normally return FALSE; TRUE will delete the timer after the function returns. virtual BOOL tick() = 0; static void updateClass(); + /// Schedule recurring calls to generic callable every period seconds. + /// Returns a pointer; if you delete it, cancels the recurring calls. + template + static LLEventTimer* run_every(F32 period, const CALLABLE& callable); + + /// Schedule a future call to generic callable. Returns a pointer. + /// CAUTION: The object referenced by that pointer WILL BE DELETED once + /// the callback has been called! LLEventTimer::getInstance(pointer) (NOT + /// pointer->getInstance(pointer)!) can be used to test whether the + /// pointer is still valid. If it is, deleting it will cancel the + /// callback. + template + static LLEventTimer* run_at(const LLDate& time, const CALLABLE& callable); + + /// Like run_at(), but after a time delta rather than at a timestamp. + /// Same CAUTION. + template + static LLEventTimer* run_after(F32 interval, const CALLABLE& callable); + protected: LLTimer mEventTimer; F32 mPeriod; + +private: + template + class Generic; }; +template +class LLEventTimer::Generic: public LLEventTimer +{ +public: + // making TIME generic allows engaging either LLEventTimer constructor + template + Generic(const TIME& time, bool once, const CALLABLE& callable): + LLEventTimer(time), + mOnce(once), + mCallable(callable) + {} + BOOL tick() override + { + mCallable(); + // true tells updateClass() to delete this instance + return mOnce; + } + +private: + bool mOnce; + CALLABLE mCallable; +}; + +template +LLEventTimer* LLEventTimer::run_every(F32 period, const CALLABLE& callable) +{ + // return false to schedule recurring calls + return new Generic(period, false, callable); +} + +template +LLEventTimer* LLEventTimer::run_at(const LLDate& time, const CALLABLE& callable) +{ + // return true for one-shot callback + return new Generic(time, true, callable); +} + +template +LLEventTimer* LLEventTimer::run_after(F32 interval, const CALLABLE& callable) +{ + // one-shot callback after specified interval + return new Generic(interval, true, callable); +} + #endif //LL_EVENTTIMER_H diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index b32ec2c9c9..5ce8958687 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -18,10 +18,28 @@ #include // external library headers #include +#include +// On Mac, got: +// #error "Boost.Stacktrace requires `_Unwind_Backtrace` function. Define +// `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if +// _Unwind_Backtrace is available without `_GNU_SOURCE`." +#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED +#if LL_WINDOWS +// On Windows, header-only implementation causes macro collisions -- use +// prebuilt library +#define BOOST_STACKTRACE_LINK +#endif // LL_WINDOWS +#include // other Linden headers #include "llerror.h" #include "llerrorcontrol.h" +// used to attach and extract stacktrace information to/from boost::exception, +// see https://www.boost.org/doc/libs/release/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.exceptions_with_stacktrace +// apparently the struct passed as the first template param needs no definition? +typedef boost::error_info + errinfo_stacktrace; + namespace { // used by crash_on_unhandled_exception_() and log_unhandled_exception_() void log_unhandled_exception_(LLError::ELevel level, @@ -53,3 +71,17 @@ void log_unhandled_exception_(const char* file, int line, const char* pretty_fun // routinely, but we DO expect to return from this function. log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function, context); } + +void annotate_exception_(boost::exception& exc) +{ + // https://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_transporting_data.html + // "Adding of Arbitrary Data to Active Exception Objects" + // Given a boost::exception&, we can add boost::error_info items to it + // without knowing its leaf type. + // The stacktrace constructor that lets us skip a level -- and why would + // we always include annotate_exception_()? -- also requires a max depth. + // For the nullary constructor, the stacktrace class declaration itself + // passes static_cast(-1), but that's kind of dubious. + // Anyway, which of us is really going to examine more than 100 frames? + exc << errinfo_stacktrace(boost::stacktrace::stacktrace(1, 100)); +} diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index dfcb7c192f..422dd8810a 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -67,9 +67,29 @@ struct LLContinueError: public LLException * enriches the exception's diagnostic_information() with the source file, * line and containing function of the LLTHROW() macro. */ -// Currently we implement that using BOOST_THROW_EXCEPTION(). Wrap it in -// LLTHROW() in case we ever want to revisit that implementation decision. -#define LLTHROW(x) BOOST_THROW_EXCEPTION(x) +#define LLTHROW(x) \ +do { \ + /* Capture the exception object 'x' by value. (Exceptions must */ \ + /* be copyable.) It might seem simpler to use */ \ + /* BOOST_THROW_EXCEPTION(annotate_exception_(x)) instead of */ \ + /* three separate statements, but: */ \ + /* - We want to throw 'x' with its original type, not just a */ \ + /* reference to boost::exception. */ \ + /* - To return x's original type, annotate_exception_() would */ \ + /* have to be a template function. */ \ + /* - We want annotate_exception_() to be opaque. */ \ + /* We also might consider embedding BOOST_THROW_EXCEPTION() in */ \ + /* our helper function, but we want the filename and line info */ \ + /* embedded by BOOST_THROW_EXCEPTION() to be the throw point */ \ + /* rather than always indicating the same line in */ \ + /* llexception.cpp. */ \ + auto exc{x}; \ + annotate_exception_(exc); \ + BOOST_THROW_EXCEPTION(exc); \ + /* Use the classic 'do { ... } while (0)' macro trick to wrap */ \ + /* our multiple statements. */ \ +} while (0) +void annotate_exception_(boost::exception& exc); /// Call this macro from a catch (...) clause #define CRASH_ON_UNHANDLED_EXCEPTION(CONTEXT) \ diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp index c60ffac5b9..ab93713b44 100644 --- a/indra/llcommon/llfasttimer.cpp +++ b/indra/llcommon/llfasttimer.cpp @@ -199,27 +199,26 @@ TimeBlockTreeNode& BlockTimerStatHandle::getTreeNode() const void BlockTimer::bootstrapTimerTree() { - for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(), end_it = BlockTimerStatHandle::instance_tracker_t::endInstances(); - it != end_it; - ++it) + for (auto& base : BlockTimerStatHandle::instance_snapshot()) { - BlockTimerStatHandle& timer = static_cast(*it); + // because of indirect derivation from LLInstanceTracker, have to downcast + BlockTimerStatHandle& timer = static_cast(base); if (&timer == &BlockTimer::getRootTimeBlock()) continue; // bootstrap tree construction by attaching to last timer to be on stack // when this timer was called if (timer.getParent() == &BlockTimer::getRootTimeBlock()) -{ + { TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); if (accumulator.mLastCaller) - { + { timer.setParent(accumulator.mLastCaller); accumulator.mParent = accumulator.mLastCaller; - } + } // no need to push up tree on first use, flag can be set spuriously accumulator.mMoveUpTree = false; - } + } } } @@ -312,12 +311,10 @@ void BlockTimer::processTimes() updateTimes(); // reset for next frame - for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(), - end_it = BlockTimerStatHandle::instance_tracker_t::endInstances(); - it != end_it; - ++it) + for (auto& base : BlockTimerStatHandle::instance_snapshot()) { - BlockTimerStatHandle& timer = static_cast(*it); + // because of indirect derivation from LLInstanceTracker, have to downcast + BlockTimerStatHandle& timer = static_cast(base); TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); accumulator.mLastCaller = NULL; @@ -368,12 +365,10 @@ void BlockTimer::logStats() LLSD sd; { - for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(), - end_it = BlockTimerStatHandle::instance_tracker_t::endInstances(); - it != end_it; - ++it) + for (auto& base : BlockTimerStatHandle::instance_snapshot()) { - BlockTimerStatHandle& timer = static_cast(*it); + // because of indirect derivation from LLInstanceTracker, have to downcast + BlockTimerStatHandle& timer = static_cast(base); LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording(); sd[timer.getName()]["Time"] = (LLSD::Real) (frame_recording.getLastRecording().getSum(timer).value()); sd[timer.getName()]["Calls"] = (LLSD::Integer) (frame_recording.getLastRecording().getSum(timer.callCount())); diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index be33d87749..f3405ea993 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -31,6 +31,10 @@ #include "lltrace.h" #include "lltreeiterators.h" +#if LL_WINDOWS +#include +#endif + #define LL_FAST_TIMER_ON 1 #define LL_FASTTIMER_USE_RDTSC 1 @@ -85,6 +89,8 @@ public: // return __rdtsc(); //} + + // shift off lower 8 bits for lower resolution but longer term timing // on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing #if LL_FASTTIMER_USE_RDTSC diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 398938b729..9de095b45d 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -86,6 +86,69 @@ public: static const char * tmpdir(); }; +/// RAII class +class LLUniqueFile +{ +public: + // empty + LLUniqueFile(): mFileHandle(nullptr) {} + // wrap (e.g.) result of LLFile::fopen() + LLUniqueFile(LLFILE* f): mFileHandle(f) {} + // no copy + LLUniqueFile(const LLUniqueFile&) = delete; + // move construction + LLUniqueFile(LLUniqueFile&& other) + { + mFileHandle = other.mFileHandle; + other.mFileHandle = nullptr; + } + // The point of LLUniqueFile is to close on destruction. + ~LLUniqueFile() + { + close(); + } + + // simple assignment + LLUniqueFile& operator=(LLFILE* f) + { + close(); + mFileHandle = f; + return *this; + } + // copy assignment deleted + LLUniqueFile& operator=(const LLUniqueFile&) = delete; + // move assignment + LLUniqueFile& operator=(LLUniqueFile&& other) + { + close(); + std::swap(mFileHandle, other.mFileHandle); + return *this; + } + + // explicit close operation + void close() + { + if (mFileHandle) + { + // in case close() throws, set mFileHandle null FIRST + LLFILE* h{nullptr}; + std::swap(h, mFileHandle); + LLFile::close(h); + } + } + + // detect whether the wrapped LLFILE is open or not + explicit operator bool() const { return bool(mFileHandle); } + bool operator!() { return ! mFileHandle; } + + // LLUniqueFile should be usable for any operation that accepts LLFILE* + // (or FILE* for that matter) + operator LLFILE*() const { return mFileHandle; } + +private: + LLFILE* mFileHandle; +}; + #if LL_WINDOWS /** * @brief Controlling input for files. diff --git a/indra/llcommon/llinstancetracker.cpp b/indra/llcommon/llinstancetracker.cpp index 3f990f4869..e7193b70b5 100644 --- a/indra/llcommon/llinstancetracker.cpp +++ b/indra/llcommon/llinstancetracker.cpp @@ -27,25 +27,15 @@ #include "linden_common.h" // associated header #include "llinstancetracker.h" -#include "llapr.h" - +#include "llerror.h" // STL headers // std headers // external library headers // other Linden headers -void LLInstanceTrackerBase::StaticBase::incrementDepth() +void LLInstanceTrackerPrivate::logerrs(const char* cls, const std::string& arg1, + const std::string& arg2, const std::string& arg3) { - ++sIterationNestDepth; -} - -void LLInstanceTrackerBase::StaticBase::decrementDepth() -{ - llassert(sIterationNestDepth); - --sIterationNestDepth; -} - -U32 LLInstanceTrackerBase::StaticBase::getDepth() -{ - return sIterationNestDepth; + LL_ERRS("LLInstanceTracker") << LLError::Log::demangle(cls) + << arg1 << arg2 << arg3 << LL_ENDL; } diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 11e5b804f7..402333cca7 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -28,438 +28,432 @@ #ifndef LL_LLINSTANCETRACKER_H #define LL_LLINSTANCETRACKER_H -#include #include +#include +#include #include +#include +#include + +#include "mutex.h" -#include -#include "llstringtable.h" #include #include -// -#ifdef LL_DEBUG -#include "llerror.h" -#endif -// +#include -// As of 2017-05-06, as far as nat knows, only clang supports __has_feature(). -// Unfortunately VS2013's preprocessor shortcut logic doesn't prevent it from -// producing (fatal) warnings for defined(__clang__) && __has_feature(...). -// Have to work around that. -#if ! defined(__clang__) -#define __has_feature(x) 0 -#endif // __clang__ +#include "lockstatic.h" +#include "stringize.h" -#if defined(LL_TEST_llinstancetracker) && __has_feature(cxx_noexcept) -// ~LLInstanceTracker() performs llassert_always() validation. That's fine in -// production code, since the llassert_always() is implemented as an LL_ERRS -// message, which will crash-with-message. In our integration test executable, -// though, this llassert_always() throws an exception instead so we can test -// error conditions and continue running the test. However -- as of C++11, -// destructors are implicitly noexcept(true). Unless we mark -// ~LLInstanceTracker() noexcept(false), the test executable crashes even on -// the ATTEMPT to throw. -#define LLINSTANCETRACKER_DTOR_NOEXCEPT noexcept(false) -#else -// If we're building for production, or in fact building *any other* test, or -// we're using a compiler that doesn't support __has_feature(), or we're not -// compiling with a C++ version that supports noexcept -- don't specify it. -#define LLINSTANCETRACKER_DTOR_NOEXCEPT -#endif - -// As of 2017-05-06, as far as nat knows, only clang supports __has_feature(). -// Unfortunately VS2013's preprocessor shortcut logic doesn't prevent it from -// producing (fatal) warnings for defined(__clang__) && __has_feature(...). -// Have to work around that. -#if ! defined(__clang__) -#define __has_feature(x) 0 -#endif // __clang__ - -#if defined(LL_TEST_llinstancetracker) && __has_feature(cxx_noexcept) -// ~LLInstanceTracker() performs llassert_always() validation. That's fine in -// production code, since the llassert_always() is implemented as an LL_ERRS -// message, which will crash-with-message. In our integration test executable, -// though, this llassert_always() throws an exception instead so we can test -// error conditions and continue running the test. However -- as of C++11, -// destructors are implicitly noexcept(true). Unless we mark -// ~LLInstanceTracker() noexcept(false), the test executable crashes even on -// the ATTEMPT to throw. -#define LLINSTANCETRACKER_DTOR_NOEXCEPT noexcept(false) -#else -// If we're building for production, or in fact building *any other* test, or -// we're using a compiler that doesn't support __has_feature(), or we're not -// compiling with a C++ version that supports noexcept -- don't specify it. -#define LLINSTANCETRACKER_DTOR_NOEXCEPT -#endif - -/** - * Base class manages "class-static" data that must actually have singleton - * semantics: one instance per process, rather than one instance per module as - * sometimes happens with data simply declared static. - */ -class LL_COMMON_API LLInstanceTrackerBase +/***************************************************************************** +* StaticBase +*****************************************************************************/ +namespace LLInstanceTrackerPrivate { -protected: - /// It's not essential to derive your STATICDATA (for use with - /// getStatic()) from StaticBase; it's just that both known - /// implementations do. struct StaticBase { - // Only needed in debug builds -#ifdef LL_DEBUG - StaticBase() - : sIterationNestDepth(0) - {} + // We need to be able to lock static data while manipulating it. + std::mutex mMutex; + }; -#else - StaticBase() - {} -#endif - // - - void incrementDepth(); - void decrementDepth(); - U32 getDepth(); - private: -#ifdef LL_WINDOWS - std::atomic_uint32_t sIterationNestDepth; -#else - std::atomic_uint sIterationNestDepth; -#endif - }; -}; - -LL_COMMON_API void assert_main_thread(); + void logerrs(const char* cls, const std::string&, const std::string&, const std::string&); +} // namespace LLInstanceTrackerPrivate +/***************************************************************************** +* LLInstanceTracker with key +*****************************************************************************/ enum EInstanceTrackerAllowKeyCollisions { - LLInstanceTrackerErrorOnCollision, - LLInstanceTrackerReplaceOnCollision + LLInstanceTrackerErrorOnCollision, + LLInstanceTrackerReplaceOnCollision }; /// This mix-in class adds support for tracking all instances of the specified class parameter T /// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup /// If KEY is not provided, then instances are stored in a simple set /// @NOTE: see explicit specialization below for default KEY==void case -/// @NOTE: this class is not thread-safe unless used as read-only -template -class LLInstanceTracker : public LLInstanceTrackerBase +template +class LLInstanceTracker { -protected: - typedef LLInstanceTracker self_t; - typedef typename std::multimap InstanceMap; - struct StaticData: public StaticBase - { - InstanceMap sMap; - }; - static StaticData& getStatic() { static StaticData sData; return sData;} - static InstanceMap& getMap_() { return getStatic().sMap; } + typedef std::map> InstanceMap; + struct StaticData: public LLInstanceTrackerPrivate::StaticBase + { + InstanceMap mMap; + }; + typedef llthread::LockStatic LockStatic; public: - class instance_iter : public boost::iterator_facade - { - public: - typedef boost::iterator_facade super_t; - - instance_iter(const typename InstanceMap::iterator& it) - : mIterator(it) - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().incrementDepth(); -#endif - // - } + // snapshot of std::pair> pairs + class snapshot + { + // It's very important that what we store in this snapshot are + // weak_ptrs, NOT shared_ptrs. That's how we discover whether any + // instance has been deleted during the lifespan of a snapshot. + typedef std::vector>> VectorType; + // Dereferencing our iterator produces a std::shared_ptr for each + // instance that still exists. Since we store weak_ptrs, that involves + // two chained transformations: + // - a transform_iterator to lock the weak_ptr and return a shared_ptr + // - a filter_iterator to skip any shared_ptr that has become invalid. + // It is very important that we filter lazily, that is, during + // traversal. Any one of our stored weak_ptrs might expire during + // traversal. + typedef std::pair> strong_pair; + // Note for future reference: nat has not yet had any luck (up to + // Boost 1.67) trying to use boost::transform_iterator with a hand- + // coded functor, only with actual functions. In my experience, an + // internal boost::result_of() operation fails, even with an explicit + // result_type typedef. But this works. + static strong_pair strengthen(typename VectorType::value_type& pair) + { + return { pair.first, pair.second.lock() }; + } + static bool dead_skipper(const strong_pair& pair) + { + return bool(pair.second); + } - ~instance_iter() - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().decrementDepth(); -#endif - // - } + public: + snapshot(): + // populate our vector with a snapshot of (locked!) InstanceMap + // note, this assigns pair to pair + mData(mLock->mMap.begin(), mLock->mMap.end()) + { + // release the lock once we've populated mData + mLock.unlock(); + } + // You can't make a transform_iterator (or anything else) that + // literally stores a C++ function (decltype(strengthen)) -- but you + // can make a transform_iterator based on a _function pointer._ + typedef boost::transform_iterator strong_iterator; + typedef boost::filter_iterator iterator; - private: - friend class boost::iterator_core_access; + iterator begin() { return make_iterator(mData.begin()); } + iterator end() { return make_iterator(mData.end()); } - void increment() { mIterator++; } - bool equal(instance_iter const& other) const - { - return mIterator == other.mIterator; - } + private: + iterator make_iterator(typename VectorType::iterator iter) + { + // transform_iterator only needs the base iterator and the transform. + // filter_iterator wants the predicate and both ends of the range. + return iterator(dead_skipper, + strong_iterator(iter, strengthen), + strong_iterator(mData.end(), strengthen)); + } - T& dereference() const - { - return *(mIterator->second); - } + // lock static data during construction +#if ! LL_WINDOWS + LockStatic mLock; +#else // LL_WINDOWS + // We want to be able to use (e.g.) our instance_snapshot subclass as: + // for (auto& inst : T::instance_snapshot()) ... + // But when this snapshot base class directly contains LockStatic, as + // above, Visual Studio 2017 requires us to code instead: + // for (auto& inst : std::move(T::instance_snapshot())) ... + // nat thinks this should be unnecessary, as an anonymous class + // instance is already a temporary. It shouldn't need to be cast to + // rvalue reference (the role of std::move()). clang evidently agrees, + // as the short form works fine with Xcode on Mac. + // To support the succinct usage, instead of directly storing + // LockStatic, store std::shared_ptr, which is copyable. + std::shared_ptr mLockp{std::make_shared()}; + LockStatic& mLock{*mLockp}; +#endif // LL_WINDOWS + VectorType mData; + }; - typename InstanceMap::iterator mIterator; - }; + // iterate over this for references to each instance + class instance_snapshot: public snapshot + { + private: + static T& instance_getter(typename snapshot::iterator::reference pair) + { + return *pair.second; + } + public: + typedef boost::transform_iterator iterator; + iterator begin() { return iterator(snapshot::begin(), instance_getter); } + iterator end() { return iterator(snapshot::end(), instance_getter); } - class key_iter : public boost::iterator_facade - { - public: - typedef boost::iterator_facade super_t; + void deleteAll() + { + for (auto it(snapshot::begin()), end(snapshot::end()); it != end; ++it) + { + delete it->second.get(); + } + } + }; - key_iter(typename InstanceMap::iterator it) - : mIterator(it) - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().incrementDepth(); -#endif - // - } + // iterate over this for each key + class key_snapshot: public snapshot + { + private: + static KEY key_getter(typename snapshot::iterator::reference pair) + { + return pair.first; + } + public: + typedef boost::transform_iterator iterator; + iterator begin() { return iterator(snapshot::begin(), key_getter); } + iterator end() { return iterator(snapshot::end(), key_getter); } + }; - key_iter(const key_iter& other) - : mIterator(other.mIterator) - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().incrementDepth(); -#endif - // - } + static T* getInstance(const KEY& k) + { + LockStatic lock; + const InstanceMap& map(lock->mMap); + typename InstanceMap::const_iterator found = map.find(k); + return (found == map.end()) ? NULL : found->second.get(); + } - ~key_iter() - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().decrementDepth(); -#endif - // - } - - - private: - friend class boost::iterator_core_access; - - void increment() { mIterator++; } - bool equal(key_iter const& other) const - { - return mIterator == other.mIterator; - } - - KEY& dereference() const - { - return const_cast(mIterator->first); - } - - typename InstanceMap::iterator mIterator; - }; - - static T* getInstance(const KEY& k) - { - const InstanceMap& map(getMap_()); - typename InstanceMap::const_iterator found = map.find(k); - return (found == map.end()) ? NULL : found->second; - } - - static instance_iter beginInstances() - { - return instance_iter(getMap_().begin()); - } - - static instance_iter endInstances() - { - return instance_iter(getMap_().end()); - } - - static S32 instanceCount() - { - return getMap_().size(); - } - - static key_iter beginKeys() - { - return key_iter(getMap_().begin()); - } - static key_iter endKeys() - { - return key_iter(getMap_().end()); - } + static S32 instanceCount() + { + return LockStatic()->mMap.size(); + } protected: - LLInstanceTracker(const KEY& key) - { - // make sure static data outlives all instances - getStatic(); - add_(key); - } - virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT - { - // it's unsafe to delete instances of this type while all instances are being iterated over. - - // Minimize calls to getStatic -#ifdef LL_DEBUG - llassert_always(getStatic().getDepth() == 0); -#endif - // - - remove_(); - } - virtual void setKey(KEY key) { remove_(); add_(key); } - virtual const KEY& getKey() const { return mInstanceKey; } + LLInstanceTracker(const KEY& key) + { + // We do not intend to manage the lifespan of this object with + // shared_ptr, so give it a no-op deleter. We store shared_ptrs in our + // InstanceMap specifically so snapshot can store weak_ptrs so we can + // detect deletions during traversals. + std::shared_ptr ptr(static_cast(this), [](T*){}); + LockStatic lock; + add_(lock, key, ptr); + } +public: + virtual ~LLInstanceTracker() + { + LockStatic lock; + remove_(lock); + } +protected: + virtual void setKey(KEY key) + { + LockStatic lock; + // Even though the shared_ptr we store in our map has a no-op deleter + // for T itself, letting the use count decrement to 0 will still + // delete the use-count object. Capture the shared_ptr we just removed + // and re-add it to the map with the new key. + auto ptr = remove_(lock); + add_(lock, key, ptr); + } +public: + virtual const KEY& getKey() const { return mInstanceKey; } private: - LLInstanceTracker( const LLInstanceTracker& ); - const LLInstanceTracker& operator=( const LLInstanceTracker& ); + LLInstanceTracker( const LLInstanceTracker& ) = delete; + LLInstanceTracker& operator=( const LLInstanceTracker& ) = delete; - void add_(const KEY& key) - { - mInstanceKey = key; - InstanceMap& map = getMap_(); - typename InstanceMap::iterator insertion_point_it = map.lower_bound(key); - if (insertion_point_it != map.end() - && insertion_point_it->first == key) - { // found existing entry with that key - switch(KEY_COLLISION_BEHAVIOR) - { - case LLInstanceTrackerErrorOnCollision: - { - // use assert here instead of LL_ERRS(), otherwise the error will be ignored since this call is made during global object initialization - llassert_always_msg(false, "Instance with this same key already exists!"); - break; - } - case LLInstanceTrackerReplaceOnCollision: - { - // replace pointer, but leave key (should have compared equal anyway) - insertion_point_it->second = static_cast(this); - break; - } - default: - break; - } - } - else - { // new key - map.insert(insertion_point_it, std::make_pair(key, static_cast(this))); - } - } - void remove_() - { - InstanceMap& map = getMap_(); - typename InstanceMap::iterator iter = map.find(mInstanceKey); - if (iter != map.end()) - { - map.erase(iter); - } - } + // for logging + template + static std::string report(K key) { return stringize(key); } + static std::string report(const std::string& key) { return "'" + key + "'"; } + static std::string report(const char* key) { return report(std::string(key)); } + + // caller must instantiate LockStatic + void add_(LockStatic& lock, const KEY& key, const std::shared_ptr& ptr) + { + mInstanceKey = key; + InstanceMap& map = lock->mMap; + switch(KEY_COLLISION_BEHAVIOR) + { + case LLInstanceTrackerErrorOnCollision: + { + // map stores shared_ptr to self + auto pair = map.emplace(key, ptr); + if (! pair.second) + { + LLInstanceTrackerPrivate::logerrs(typeid(*this).name(), " instance with key ", + report(key), " already exists!"); + } + break; + } + case LLInstanceTrackerReplaceOnCollision: + map[key] = ptr; + break; + default: + break; + } + } + std::shared_ptr remove_(LockStatic& lock) + { + InstanceMap& map = lock->mMap; + typename InstanceMap::iterator iter = map.find(mInstanceKey); + if (iter != map.end()) + { + auto ret = iter->second; + map.erase(iter); + return ret; + } + return {}; + } private: - KEY mInstanceKey; + KEY mInstanceKey; }; +/***************************************************************************** +* LLInstanceTracker without key +*****************************************************************************/ +// TODO: +// - For the case of omitted KEY template parameter, consider storing +// std::map> instead of std::set>. +// That might let us share more of the implementation between KEY and +// non-KEY LLInstanceTracker subclasses. +// - Even if not that, consider trying to unify the snapshot implementations. +// The trouble is that the 'iterator' published by each (and by their +// subclasses) must reflect the specific type of the callables that +// distinguish them. (Maybe make instance_snapshot() and key_snapshot() +// factory functions that pass lambdas to a factory function for the generic +// template class?) + /// explicit specialization for default case where KEY is void /// use a simple std::set template -class LLInstanceTracker : public LLInstanceTrackerBase +class LLInstanceTracker { -protected: - typedef LLInstanceTracker self_t; - typedef typename std::set InstanceSet; - struct StaticData: public StaticBase - { - InstanceSet sSet; - }; - static StaticData& getStatic() { static StaticData sData; return sData; } - static InstanceSet& getSet_() { return getStatic().sSet; } + typedef std::set> InstanceSet; + struct StaticData: public LLInstanceTrackerPrivate::StaticBase + { + InstanceSet mSet; + }; + typedef llthread::LockStatic LockStatic; public: + /** + * Storing a dumb T* somewhere external is a bad idea, since + * LLInstanceTracker subclasses are explicitly destroyed rather than + * managed by smart pointers. It's legal to declare stack instances of an + * LLInstanceTracker subclass. But it's reasonable to store a + * std::weak_ptr, which will become invalid when the T instance is + * destroyed. + */ + std::weak_ptr getWeak() + { + return mSelf; + } + + static S32 instanceCount() { return LockStatic()->mSet.size(); } - /** - * Does a particular instance still exist? Of course, if you already have - * a T* in hand, you need not call getInstance() to @em locate the - * instance -- unlike the case where getInstance() accepts some kind of - * key. Nonetheless this method is still useful to @em validate a - * particular T*, since each instance's destructor removes itself from the - * underlying set. - */ - static T* getInstance(T* k) - { - const InstanceSet& set(getSet_()); - typename InstanceSet::const_iterator found = set.find(k); - return (found == set.end())? NULL : *found; - } - static S32 instanceCount() { return getSet_().size(); } + // snapshot of std::shared_ptr pointers + class snapshot + { + // It's very important that what we store in this snapshot are + // weak_ptrs, NOT shared_ptrs. That's how we discover whether any + // instance has been deleted during the lifespan of a snapshot. + typedef std::vector> VectorType; + // Dereferencing our iterator produces a std::shared_ptr for each + // instance that still exists. Since we store weak_ptrs, that involves + // two chained transformations: + // - a transform_iterator to lock the weak_ptr and return a shared_ptr + // - a filter_iterator to skip any shared_ptr that has become invalid. + typedef std::shared_ptr strong_ptr; + static strong_ptr strengthen(typename VectorType::value_type& ptr) + { + return ptr.lock(); + } + static bool dead_skipper(const strong_ptr& ptr) + { + return bool(ptr); + } - class instance_iter : public boost::iterator_facade - { - public: - instance_iter(const typename InstanceSet::iterator& it) - : mIterator(it) - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().incrementDepth(); -#endif - // - } + public: + snapshot(): + // populate our vector with a snapshot of (locked!) InstanceSet + // note, this assigns stored shared_ptrs to weak_ptrs for snapshot + mData(mLock->mSet.begin(), mLock->mSet.end()) + { + // release the lock once we've populated mData + mLock.unlock(); + } - instance_iter(const instance_iter& other) - : mIterator(other.mIterator) - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().incrementDepth(); -#endif - } + typedef boost::transform_iterator strong_iterator; + typedef boost::filter_iterator iterator; - ~instance_iter() - { - // Minimize calls to getStatic -#ifdef LL_DEBUG - getStatic().decrementDepth(); -#endif - // - } + iterator begin() { return make_iterator(mData.begin()); } + iterator end() { return make_iterator(mData.end()); } - private: - friend class boost::iterator_core_access; + private: + iterator make_iterator(typename VectorType::iterator iter) + { + // transform_iterator only needs the base iterator and the transform. + // filter_iterator wants the predicate and both ends of the range. + return iterator(dead_skipper, + strong_iterator(iter, strengthen), + strong_iterator(mData.end(), strengthen)); + } - void increment() { mIterator++; } - bool equal(instance_iter const& other) const - { - return mIterator == other.mIterator; - } + // lock static data during construction +#if ! LL_WINDOWS + LockStatic mLock; +#else // LL_WINDOWS + // We want to be able to use our instance_snapshot subclass as: + // for (auto& inst : T::instance_snapshot()) ... + // But when this snapshot base class directly contains LockStatic, as + // above, Visual Studio 2017 requires us to code instead: + // for (auto& inst : std::move(T::instance_snapshot())) ... + // nat thinks this should be unnecessary, as an anonymous class + // instance is already a temporary. It shouldn't need to be cast to + // rvalue reference (the role of std::move()). clang evidently agrees, + // as the short form works fine with Xcode on Mac. + // To support the succinct usage, instead of directly storing + // LockStatic, store std::shared_ptr, which is copyable. + std::shared_ptr mLockp{std::make_shared()}; + LockStatic& mLock{*mLockp}; +#endif // LL_WINDOWS + VectorType mData; + }; - T& dereference() const - { - return **mIterator; - } + // iterate over this for references to each instance + struct instance_snapshot: public snapshot + { + typedef boost::indirect_iterator iterator; + iterator begin() { return iterator(snapshot::begin()); } + iterator end() { return iterator(snapshot::end()); } - typename InstanceSet::iterator mIterator; - }; - - static instance_iter beginInstances() { return instance_iter(getSet_().begin()); } - static instance_iter endInstances() { return instance_iter(getSet_().end()); } + void deleteAll() + { + for (auto it(snapshot::begin()), end(snapshot::end()); it != end; ++it) + { + delete it->get(); + } + } + }; protected: - LLInstanceTracker() - { - // make sure static data outlives all instances - getStatic(); - getSet_().insert(static_cast(this)); - } - virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT - { - // it's unsafe to delete instances of this type while all instances are being iterated over. + LLInstanceTracker() + { + // Since we do not intend for this shared_ptr to manage lifespan, give + // it a no-op deleter. + std::shared_ptr ptr(static_cast(this), [](T*){}); + // save corresponding weak_ptr for future reference + mSelf = ptr; + // Also store it in our class-static set to track this instance. + LockStatic()->mSet.emplace(ptr); + } +public: + virtual ~LLInstanceTracker() + { + // convert weak_ptr to shared_ptr because that's what we store in our + // InstanceSet + LockStatic()->mSet.erase(mSelf.lock()); + } +protected: + LLInstanceTracker(const LLInstanceTracker& other): + LLInstanceTracker() + {} - // Minimize calls to getStatic -#ifdef LL_DEBUG - llassert_always(getStatic().getDepth() == 0); -#endif - // - - getSet_().erase(static_cast(this)); - } - - LLInstanceTracker(const LLInstanceTracker& other) - { - getSet_().insert(static_cast(this)); - } +private: + // Storing a weak_ptr to self is a bit like deriving from + // std::enable_shared_from_this(), except more explicit. + std::weak_ptr mSelf; }; #endif diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp index fa5730f112..3e6ce9092c 100644 --- a/indra/llcommon/llleaplistener.cpp +++ b/indra/llcommon/llleaplistener.cpp @@ -14,6 +14,8 @@ // associated header #include "llleaplistener.h" // STL headers +#include +#include // std headers // external library headers #include @@ -60,16 +62,11 @@ LLLeapListener::LLLeapListener(const ConnectFunc& connect): LLSD need_name(LLSDMap("name", LLSD())); add("newpump", "Instantiate a new LLEventPump named like [\"name\"] and listen to it.\n" - "If [\"type\"] == \"LLEventQueue\", make LLEventQueue, else LLEventStream.\n" + "[\"type\"] == \"LLEventStream\", \"LLEventMailDrop\" et al.\n" "Events sent through new LLEventPump will be decorated with [\"pump\"]=name.\n" "Returns actual name in [\"name\"] (may be different if collision).", &LLLeapListener::newpump, need_name); - add("killpump", - "Delete LLEventPump [\"name\"] created by \"newpump\".\n" - "Returns [\"status\"] boolean indicating whether such a pump existed.", - &LLLeapListener::killpump, - need_name); LLSD need_source_listener(LLSDMap("source", LLSD())("listener", LLSD())); add("listen", "Listen to an existing LLEventPump named [\"source\"], with listener name\n" @@ -124,40 +121,23 @@ void LLLeapListener::newpump(const LLSD& request) Response reply(LLSD(), request); std::string name = request["name"]; - LLSD const & type = request["type"]; + std::string type = request["type"]; - LLEventPump * new_pump = NULL; - if (type.asString() == "LLEventQueue") + try { - new_pump = new LLEventQueue(name, true); // tweak name for uniqueness + // tweak name for uniqueness + LLEventPump& new_pump(LLEventPumps::instance().make(name, true, type)); + name = new_pump.getName(); + reply["name"] = name; + + // Now listen on this new pump with our plugin listener + std::string myname("llleap"); + saveListener(name, myname, mConnect(new_pump, myname)); } - else + catch (const LLEventPumps::BadType& error) { - if (! (type.isUndefined() || type.asString() == "LLEventStream")) - { - reply.warn(STRINGIZE("unknown 'type' " << type << ", using LLEventStream")); - } - new_pump = new LLEventStream(name, true); // tweak name for uniqueness + reply.error(error.what()); } - - name = new_pump->getName(); - - mEventPumps.insert(name, new_pump); - - // Now listen on this new pump with our plugin listener - std::string myname("llleap"); - saveListener(name, myname, mConnect(*new_pump, myname)); - - reply["name"] = name; -} - -void LLLeapListener::killpump(const LLSD& request) -{ - Response reply(LLSD(), request); - - std::string name = request["name"]; - // success == (nonzero number of entries were erased) - reply["status"] = bool(mEventPumps.erase(name)); } void LLLeapListener::listen(const LLSD& request) @@ -228,13 +208,11 @@ void LLLeapListener::getAPIs(const LLSD& request) const { Response reply(LLSD(), request); - for (LLEventAPI::instance_iter eai(LLEventAPI::beginInstances()), - eaend(LLEventAPI::endInstances()); - eai != eaend; ++eai) + for (auto& ea : LLEventAPI::instance_snapshot()) { LLSD info; - info["desc"] = eai->getDesc(); - reply[eai->getName()] = info; + info["desc"] = ea.getDesc(); + reply[ea.getName()] = info; } } diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h index 2193d81b9e..0ca5893657 100644 --- a/indra/llcommon/llleaplistener.h +++ b/indra/llcommon/llleaplistener.h @@ -40,7 +40,6 @@ public: private: void newpump(const LLSD&); - void killpump(const LLSD&); void listen(const LLSD&); void stoplistening(const LLSD&); void ping(const LLSD&) const; @@ -64,10 +63,6 @@ private: // and listener name. typedef std::map, LLBoundListener> ListenersMap; ListenersMap mListeners; - // Similar lifespan reasoning applies to LLEventPumps instantiated by - // newpump() operations. - typedef boost::ptr_map EventPumpsMap; - EventPumpsMap mEventPumps; }; #endif /* ! defined(LL_LLLEAPLISTENER_H) */ diff --git a/indra/llcommon/lllistenerwrapper.h b/indra/llcommon/lllistenerwrapper.h deleted file mode 100644 index 09d074abca..0000000000 --- a/indra/llcommon/lllistenerwrapper.h +++ /dev/null @@ -1,198 +0,0 @@ -/** - * @file lllistenerwrapper.h - * @author Nat Goodspeed - * @date 2009-11-30 - * @brief Introduce LLListenerWrapper template - * - * $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_LLLISTENERWRAPPER_H) -#define LL_LLLISTENERWRAPPER_H - -#include "llevents.h" // LLListenerWrapperBase -#include - -/** - * Template base class for coding wrappers for LLEventPump listeners. - * - * Derive your listener wrapper from LLListenerWrapper. You must use - * LLLISTENER_WRAPPER_SUBCLASS() so your subclass will play nicely with - * boost::visit_each (q.v.). That way boost::signals2 can still detect - * derivation from LLEventTrackable, and so forth. - */ -template -class LLListenerWrapper: public LLListenerWrapperBase -{ -public: - /// Wrap an arbitrary listener object - LLListenerWrapper(const LISTENER& listener): - mListener(listener) - {} - - /// call - virtual bool operator()(const LLSD& event) - { - return mListener(event); - } - - /// Allow boost::visit_each() to peek at our mListener. - template - void accept_visitor(V& visitor) const - { - using boost::visit_each; - visit_each(visitor, mListener, 0); - } - -private: - LISTENER mListener; -}; - -/** - * Specialize boost::visit_each() (leveraging ADL) to peek inside an - * LLListenerWrapper to traverse its LISTENER. We borrow the - * accept_visitor() pattern from boost::bind(), avoiding the need to make - * mListener public. - */ -template -void visit_each(V& visitor, const LLListenerWrapper& wrapper, int) -{ - wrapper.accept_visitor(visitor); -} - -/// use this (sigh!) for each subclass of LLListenerWrapper you write -#define LLLISTENER_WRAPPER_SUBCLASS(CLASS) \ -template \ -void visit_each(V& visitor, const CLASS& wrapper, int) \ -{ \ - visit_each(visitor, static_cast&>(wrapper), 0); \ -} \ - \ -/* Have to state this explicitly, rather than using LL_TEMPLATE_CONVERTIBLE, */ \ -/* because the source type is itself a template. */ \ -template \ -struct ll_template_cast_impl*> \ -{ \ - const LLListenerWrapperBase* operator()(const CLASS* wrapper) \ - { \ - return wrapper; \ - } \ -} - -/** - * Make an instance of a listener wrapper. Every wrapper class must be a - * template accepting a listener object of arbitrary type. In particular, the - * type of a boost::bind() expression is deliberately undocumented. So we - * can't just write Wrapper(boost::bind(...)). Instead we must - * write llwrap(boost::bind(...)). - */ -template class WRAPPER, typename T> -WRAPPER llwrap(const T& listener) -{ - return WRAPPER(listener); -} - -/** - * This LLListenerWrapper template subclass is used to report entry/exit to an - * event listener, by changing this: - * @code - * someEventPump.listen("MyClass", - * boost::bind(&MyClass::method, ptr, _1)); - * @endcode - * to this: - * @code - * someEventPump.listen("MyClass", - * llwrap( - * boost::bind(&MyClass::method, ptr, _1))); - * @endcode - */ -template -class LLCoutListener: public LLListenerWrapper -{ - typedef LLListenerWrapper super; - -public: - /// Wrap an arbitrary listener object - LLCoutListener(const LISTENER& listener): - super(listener) - {} - - /// call - virtual bool operator()(const LLSD& event) - { - std::cout << "Entering listener " << *super::mName << " with " << event << std::endl; - bool handled = super::operator()(event); - std::cout << "Leaving listener " << *super::mName; - if (handled) - { - std::cout << " (handled)"; - } - std::cout << std::endl; - return handled; - } -}; - -LLLISTENER_WRAPPER_SUBCLASS(LLCoutListener); - -/** - * This LLListenerWrapper template subclass is used to log entry/exit to an - * event listener, by changing this: - * @code - * someEventPump.listen("MyClass", - * boost::bind(&MyClass::method, ptr, _1)); - * @endcode - * to this: - * @code - * someEventPump.listen("MyClass", - * llwrap( - * boost::bind(&MyClass::method, ptr, _1))); - * @endcode - */ -template -class LLLogListener: public LLListenerWrapper -{ - typedef LLListenerWrapper super; - -public: - /// Wrap an arbitrary listener object - LLLogListener(const LISTENER& listener): - super(listener) - {} - - /// call - virtual bool operator()(const LLSD& event) - { - LL_DEBUGS("LLLogListener") << "Entering listener " << *super::mName << " with " << event << LL_ENDL; - bool handled = super::operator()(event); - LL_DEBUGS("LLLogListener") << "Leaving listener " << *super::mName; - if (handled) - { - LL_CONT << " (handled)"; - } - LL_CONT << LL_ENDL; - return handled; - } -}; - -LLLISTENER_WRAPPER_SUBCLASS(LLLogListener); - -#endif /* ! defined(LL_LLLISTENERWRAPPER_H) */ diff --git a/indra/llcommon/llmainthreadtask.cpp b/indra/llcommon/llmainthreadtask.cpp new file mode 100644 index 0000000000..e0d70cacd8 --- /dev/null +++ b/indra/llcommon/llmainthreadtask.cpp @@ -0,0 +1,22 @@ +/** + * @file llmainthreadtask.cpp + * @author Nat Goodspeed + * @date 2019-12-05 + * @brief Implementation for llmainthreadtask. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llmainthreadtask.h" +// STL headers +// std headers +// external library headers +// other Linden headers + +// This file is required by our CMake integration-test machinery. It +// contributes no code to the viewer executable. diff --git a/indra/llcommon/llmainthreadtask.h b/indra/llcommon/llmainthreadtask.h new file mode 100644 index 0000000000..d509b687c0 --- /dev/null +++ b/indra/llcommon/llmainthreadtask.h @@ -0,0 +1,99 @@ +/** + * @file llmainthreadtask.h + * @author Nat Goodspeed + * @date 2019-12-04 + * @brief LLMainThreadTask dispatches work to the main thread. When invoked on + * the main thread, it performs the work inline. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLMAINTHREADTASK_H) +#define LL_LLMAINTHREADTASK_H + +#include "lleventtimer.h" +#include "llthread.h" +#include "llmake.h" +#include +#include // std::result_of + +/** + * LLMainThreadTask provides a way to perform some task specifically on the + * main thread, waiting for it to complete. A task consists of a C++ nullary + * invocable (i.e. any callable that requires no arguments) with arbitrary + * return type. + * + * Instead of instantiating LLMainThreadTask, pass your invocable to its + * static dispatch() method. dispatch() returns the result of calling your + * task. (Or, if your task throws an exception, dispatch() throws that + * exception. See std::packaged_task.) + * + * When you call dispatch() on the main thread (as determined by + * on_main_thread() in llthread.h), it simply calls your task and returns the + * result. + * + * When you call dispatch() on a secondary thread, it instantiates an + * LLEventTimer subclass scheduled immediately. Next time the main loop calls + * LLEventTimer::updateClass(), your task will be run, and LLMainThreadTask + * will fulfill a future with its result. Meanwhile the requesting thread + * blocks on that future. As soon as it is set, the requesting thread wakes up + * with the task result. + */ +class LLMainThreadTask +{ +private: + // Don't instantiate this class -- use dispatch() instead. + LLMainThreadTask() {} + +public: + /// dispatch() is the only way to invoke this functionality. + template + static auto dispatch(CALLABLE&& callable) -> decltype(callable()) + { + if (on_main_thread()) + { + // we're already running on the main thread, perfect + return callable(); + } + else + { + // It's essential to construct LLEventTimer subclass instances on + // the heap because, on completion, LLEventTimer deletes them. + // Once we enable C++17, we can use Class Template Argument + // Deduction. Until then, use llmake_heap(). + auto* task = llmake_heap(std::forward(callable)); + auto future = task->mTask.get_future(); + // Now simply block on the future. + return future.get(); + } + } + +private: + template + struct Task: public LLEventTimer + { + Task(CALLABLE&& callable): + // no wait time: call tick() next chance we get + LLEventTimer(0), + mTask(std::forward(callable)) + {} + BOOL tick() override + { + // run the task on the main thread, will populate the future + // obtained by get_future() + mTask(); + // tell LLEventTimer we're done (one shot) + return TRUE; + } + // Given arbitrary CALLABLE, which might be a lambda, how are we + // supposed to obtain its signature for std::packaged_task? It seems + // redundant to have to add an argument list to engage result_of, then + // add the argument list again to complete the signature. At least we + // only support a nullary CALLABLE. + std::packaged_task::type()> mTask; + }; +}; + +#endif /* ! defined(LL_LLMAINTHREADTASK_H) */ diff --git a/indra/llcommon/llmake.h b/indra/llcommon/llmake.h index 08744f90fb..6eeeebaf9c 100644 --- a/indra/llcommon/llmake.h +++ b/indra/llcommon/llmake.h @@ -12,10 +12,8 @@ * * also relevant: * - * Template argument deduction for class templates - * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html - * was apparently adopted in June 2016? Unclear when compilers will - * portably support this, but there is hope. + * Template argument deduction for class templates (C++17) + * https://en.cppreference.com/w/cpp/language/class_template_argument_deduction * * $LicenseInfo:firstyear=2015&license=viewerlgpl$ * Copyright (c) 2015, Linden Research, Inc. @@ -25,37 +23,61 @@ #if ! defined(LL_LLMAKE_H) #define LL_LLMAKE_H -/*==========================================================================*| -// When we allow ourselves to compile with C++11 features enabled, this form -// should generically handle an arbitrary number of arguments. - +/** + * Usage: llmake(args...) + * + * Deduces the types T... of 'args' and returns an instance of + * SomeTemplate(args...). + */ template class CLASS_TEMPLATE, typename... ARGS> CLASS_TEMPLATE llmake(ARGS && ... args) { return CLASS_TEMPLATE(std::forward(args)...); } -|*==========================================================================*/ -// As of 2015-12-18, this is what we'll use instead. Add explicit overloads -// for different numbers of template parameters as use cases arise. +/// dumb pointer template just in case that's what's wanted +template +using dumb_pointer = T*; /** - * Usage: llmake(arg) + * Same as llmake(), but returns a pointer to a new heap instance of + * SomeTemplate(args...) using the pointer of your choice. * - * Deduces the type T of 'arg' and returns an instance of SomeTemplate - * initialized with 'arg'. Assumes a constructor accepting T (by value, - * reference or whatever). + * @code + * auto* dumb = llmake_heap(args...); + * auto shared = llmake_heap(args...); + * auto unique = llmake_heap(args...); + * @endcode */ -template class CLASS_TEMPLATE, typename ARG1> -CLASS_TEMPLATE llmake(const ARG1& arg1) +// POINTER_TEMPLATE is characterized as template rather than as +// template because (e.g.) std::unique_ptr has multiple template +// arguments. Even though we only engage one, std::unique_ptr doesn't match a +// template template parameter that itself takes only one template parameter. +template class CLASS_TEMPLATE, + template class POINTER_TEMPLATE=dumb_pointer, + typename... ARGS> +POINTER_TEMPLATE> llmake_heap(ARGS&&... args) { - return CLASS_TEMPLATE(arg1); + return POINTER_TEMPLATE>( + new CLASS_TEMPLATE(std::forward(args)...)); } -template class CLASS_TEMPLATE, typename ARG1, typename ARG2> -CLASS_TEMPLATE llmake(const ARG1& arg1, const ARG2& arg2) -{ - return CLASS_TEMPLATE(arg1, arg2); -} +#endif // VS 2013 workaround + +/// dumb pointer template just in case that's what's wanted + +/** + * Same as llmake(), but returns a pointer to a new heap instance of + * SomeTemplate(args...) using the pointer of your choice. + * + * @code + * auto* dumb = llmake_heap(args...); + * auto shared = llmake_heap(args...); + * auto unique = llmake_heap(args...); + * @endcode + */ +// POINTER_TEMPLATE is characterized as template rather than as +// template because (e.g.) std::unique_ptr has multiple template +// arguments. Even though we only engage one, std::unique_ptr doesn't match a +// template template parameter that itself takes only one template parameter. -#endif /* ! defined(LL_LLMAKE_H) */ diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index 75f43a4704..4d73c04d07 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -32,8 +32,7 @@ //============================================================================ LLMutex::LLMutex() : - mCount(0), - mLockingThread(NO_THREAD) + mCount(0) { } @@ -55,7 +54,7 @@ void LLMutex::lock() #if MUTEX_DEBUG // Have to have the lock before we can access the debug info - U32 id = LLThread::currentID(); + auto id = LLThread::currentID(); if (mIsLocked[id] != FALSE) LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; mIsLocked[id] = TRUE; @@ -74,13 +73,13 @@ void LLMutex::unlock() #if MUTEX_DEBUG // Access the debug info while we have the lock - U32 id = LLThread::currentID(); + auto id = LLThread::currentID(); if (mIsLocked[id] != TRUE) LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; mIsLocked[id] = FALSE; #endif - mLockingThread = NO_THREAD; + mLockingThread = LLThread::id_t(); mMutex.unlock(); } @@ -102,7 +101,7 @@ bool LLMutex::isSelfLocked() return mLockingThread == LLThread::currentID(); } -U32 LLMutex::lockingThread() const +LLThread::id_t LLMutex::lockingThread() const { return mLockingThread; } @@ -122,7 +121,7 @@ bool LLMutex::trylock() #if MUTEX_DEBUG // Have to have the lock before we can access the debug info - U32 id = LLThread::currentID(); + auto id = LLThread::currentID(); if (mIsLocked[id] != FALSE) LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; mIsLocked[id] = TRUE; diff --git a/indra/llcommon/llmutex.h b/indra/llcommon/llmutex.h index f841d7f950..838d7d34c0 100644 --- a/indra/llcommon/llmutex.h +++ b/indra/llcommon/llmutex.h @@ -28,20 +28,12 @@ #define LL_LLMUTEX_H #include "stdtypes.h" +#include "llthread.h" #include -#if LL_WINDOWS -#pragma warning (push) -#pragma warning (disable:4265) -#endif -// 'std::_Pad' : class has virtual functions, but destructor is not virtual -#include +#include "mutex.h" #include -#if LL_WINDOWS -#pragma warning (pop) -#endif - //============================================================================ #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) @@ -53,11 +45,6 @@ class LL_COMMON_API LLMutex { public: - typedef enum - { - NO_THREAD = 0xFFFFFFFF - } e_locking_thread; - LLMutex(); virtual ~LLMutex(); @@ -66,15 +53,15 @@ public: void unlock(); // undefined behavior when called on mutex not being held bool isLocked(); // non-blocking, but does do a lock/unlock so not free bool isSelfLocked(); //return true if locked in a same thread - U32 lockingThread() const; //get ID of locking thread - + LLThread::id_t lockingThread() const; //get ID of locking thread + protected: std::mutex mMutex; mutable U32 mCount; - mutable U32 mLockingThread; + mutable LLThread::id_t mLockingThread; #if MUTEX_DEBUG - std::map mIsLocked; + std::map mIsLocked; #endif }; diff --git a/indra/llcommon/llpredicate.h b/indra/llcommon/llpredicate.h index 7133502485..e6c56a5711 100644 --- a/indra/llcommon/llpredicate.h +++ b/indra/llcommon/llpredicate.h @@ -164,7 +164,7 @@ namespace LLPredicate return (mRule && value).someSet() && (!mRule && value).someSet(); } - bool acceptsAll() const + bool acceptsAll() const { return mRule.allSet(); } diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 6ad90db244..7e71409203 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -242,4 +242,11 @@ #define LL_COMPILE_TIME_MESSAGE(msg) #endif +// __FUNCTION__ works on all the platforms we care about, but... +#if LL_WINDOWS +#define LL_PRETTY_FUNCTION __FUNCSIG__ +#else +#define LL_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#endif + #endif // not LL_LINDEN_PREPROCESSOR_H diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 1fa53f322b..23936f0526 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -994,9 +994,9 @@ void LLProcess::handle_status(int reason, int status) // wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT); // It's just wrong to call apr_proc_wait() here. The only way APR knows to // call us with APR_OC_REASON_DEATH is that it's already reaped this child - // process, so calling suspend() will only produce "huh?" from the OS. We + // process, so calling wait() will only produce "huh?" from the OS. We // must rely on the status param passed in, which unfortunately comes - // straight from the OS suspend() call, which means we have to decode it by + // straight from the OS wait() call, which means we have to decode it by // hand. mStatus = interpret_status(status); LL_INFOS("LLProcess") << getStatusString() << LL_ENDL; diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 5d16a4b74d..0e90297a1c 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -753,8 +753,6 @@ private: } fclose(cpuinfo_fp); } -# if LL_X86 - // *NOTE:Mani - eww, macros! srry. #define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \ if (!cpuinfo[cpuinfo_id].empty()) \ @@ -782,7 +780,7 @@ private: LLPI_SET_INFO_INT(eModel, "model"); - S32 family; + S32 family{0}; if (!cpuinfo["cpu family"].empty() && LLStringUtil::convertToS32(cpuinfo["cpu family"], family)) { @@ -814,8 +812,6 @@ private: { setExtension(cpu_feature_names[eSSE2_Ext]); } - -# endif // LL_X86 } std::string getCPUFeatureDescription() const diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index fb0411d27b..7e4af6ea66 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -28,9 +28,10 @@ #include #include -#include "llmutex.h" #include "llatomic.h" +class LLMutex; + //---------------------------------------------------------------------------- // RefCount objects should generally only be accessed by way of LLPointer<>'s // see llthread.h for LLThreadSafeRefCount diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 26af8ff973..d74ee0bc85 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -66,7 +66,8 @@ const std::string LLSD_NOTATION_HEADER("llsd/notation"); */ // static -void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, U32 options) +void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, + LLSDFormatter::EFormatterOptions options) { LLPointer f = NULL; @@ -174,10 +175,10 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes) { p = new LLSDXMLParser; } - else if (header == LLSD_NOTATION_HEADER) - { - p = new LLSDNotationParser; - } + else if (header == LLSD_NOTATION_HEADER) + { + p = new LLSDNotationParser; + } else { LL_WARNS() << "deserialize request for unknown ELLSD_Serialize" << LL_ENDL; @@ -1234,9 +1235,11 @@ bool LLSDBinaryParser::parseString( /** * LLSDFormatter */ -LLSDFormatter::LLSDFormatter() : - mBoolAlpha(false) +LLSDFormatter::LLSDFormatter(bool boolAlpha, const std::string& realFmt, EFormatterOptions options): + mOptions(options) { + boolalpha(boolAlpha); + realFormat(realFmt); } // virtual @@ -1253,6 +1256,17 @@ void LLSDFormatter::realFormat(const std::string& format) mRealFormat = format; } +S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr) const +{ + // pass options captured by constructor + return format(data, ostr, mOptions); +} + +S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const +{ + return format_impl(data, ostr, options, 0); +} + void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const { std::string buffer = llformat(mRealFormat.c_str(), real); @@ -1262,7 +1276,9 @@ void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const /** * LLSDNotationFormatter */ -LLSDNotationFormatter::LLSDNotationFormatter() +LLSDNotationFormatter::LLSDNotationFormatter(bool boolAlpha, const std::string& realFormat, + EFormatterOptions options): + LLSDFormatter(boolAlpha, realFormat, options) { } @@ -1278,14 +1294,8 @@ std::string LLSDNotationFormatter::escapeString(const std::string& in) return ostr.str(); } -// virtual -S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const -{ - S32 rv = format_impl(data, ostr, options, 0); - return rv; -} - -S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const +S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, + EFormatterOptions options, U32 level) const { S32 format_count = 1; std::string pre; @@ -1406,21 +1416,33 @@ S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 { // *FIX: memory inefficient. const std::vector& buffer = data.asBinary(); - ostr << "b(" << buffer.size() << ")\""; - if(buffer.size()) + if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY) { - if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY) + ostr << "b16\""; + if (! buffer.empty()) { std::ios_base::fmtflags old_flags = ostr.flags(); ostr.setf( std::ios::hex, std::ios::basefield ); - ostr << "0x"; + // It shouldn't strictly matter whether the emitted hex digits + // are uppercase; LLSDNotationParser handles either; but as of + // 2020-05-13, Python's llbase.llsd requires uppercase hex. + ostr << std::uppercase; + auto oldfill(ostr.fill('0')); + auto oldwidth(ostr.width()); for (int i = 0; i < buffer.size(); i++) { - ostr << (int) buffer[i]; + // have to restate setw() before every conversion + ostr << std::setw(2) << (int) buffer[i]; } + ostr.width(oldwidth); + ostr.fill(oldfill); ostr.flags(old_flags); } - else + } + else // ! OPTIONS_PRETTY_BINARY + { + ostr << "b(" << buffer.size() << ")\""; + if (! buffer.empty()) { ostr.write((const char*)&buffer[0], buffer.size()); } @@ -1437,11 +1459,12 @@ S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 return format_count; } - /** * LLSDBinaryFormatter */ -LLSDBinaryFormatter::LLSDBinaryFormatter() +LLSDBinaryFormatter::LLSDBinaryFormatter(bool boolAlpha, const std::string& realFormat, + EFormatterOptions options): + LLSDFormatter(boolAlpha, realFormat, options) { } @@ -1450,7 +1473,8 @@ LLSDBinaryFormatter::~LLSDBinaryFormatter() { } // virtual -S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const +S32 LLSDBinaryFormatter::format_impl(const LLSD& data, std::ostream& ostr, + EFormatterOptions options, U32 level) const { S32 format_count = 1; switch(data.type()) @@ -1466,7 +1490,7 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option { ostr.put('k'); formatString((*iter).first, ostr); - format_count += format((*iter).second, ostr); + format_count += format_impl((*iter).second, ostr, options, level+1); } ostr.put('}'); break; @@ -1481,7 +1505,7 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option LLSD::array_const_iterator end = data.endArray(); for(; iter != end; ++iter) { - format_count += format(*iter, ostr); + format_count += format_impl(*iter, ostr, options, level+1); } ostr.put(']'); break; diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h index fe0f4443ef..d6079fd9fa 100644 --- a/indra/llcommon/llsdserialize.h +++ b/indra/llcommon/llsdserialize.h @@ -435,7 +435,8 @@ public: /** * @brief Constructor */ - LLSDFormatter(); + LLSDFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); /** * @brief Set the boolean serialization format. @@ -459,15 +460,37 @@ public: void realFormat(const std::string& format); /** - * @brief Call this method to format an LLSD to a stream. + * @brief Call this method to format an LLSD to a stream with options as + * set by the constructor. * * @param data The data to write. * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects fomatted out + * @return Returns The number of LLSD objects formatted out */ - virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const = 0; + S32 format(const LLSD& data, std::ostream& ostr) const; + + /** + * @brief Call this method to format an LLSD to a stream, passing options + * explicitly. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @param options OPTIONS_NONE to emit LLSD::Binary as raw bytes + * @return Returns The number of LLSD objects formatted out + */ + virtual S32 format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const; protected: + /** + * @brief Implementation to format the data. This is called recursively. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @return Returns The number of LLSD objects formatted out + */ + virtual S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const = 0; + /** * @brief Helper method which appropriately obeys the real format. * @@ -476,9 +499,9 @@ protected: */ void formatReal(LLSD::Real real, std::ostream& ostr) const; -protected: bool mBoolAlpha; std::string mRealFormat; + EFormatterOptions mOptions; }; @@ -498,7 +521,8 @@ public: /** * @brief Constructor */ - LLSDNotationFormatter(); + LLSDNotationFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); /** * @brief Helper static method to return a notation escaped string @@ -512,25 +536,16 @@ public: */ static std::string escapeString(const std::string& in); - /** - * @brief Call this method to format an LLSD to a stream. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects fomatted out - */ - virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const; - protected: - /** * @brief Implementation to format the data. This is called recursively. * * @param data The data to write. * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects fomatted out + * @return Returns The number of LLSD objects formatted out */ - S32 format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const; + S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const override; }; @@ -550,7 +565,8 @@ public: /** * @brief Constructor */ - LLSDXMLFormatter(); + LLSDXMLFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); /** * @brief Helper static method to return an xml escaped string @@ -565,20 +581,23 @@ public: * * @param data The data to write. * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects fomatted out + * @return Returns The number of LLSD objects formatted out */ - virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const; + S32 format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const override; + + // also pull down base-class format() method that isn't overridden + using LLSDFormatter::format; protected: - /** * @brief Implementation to format the data. This is called recursively. * * @param data The data to write. * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects fomatted out + * @return Returns The number of LLSD objects formatted out */ - S32 format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const; + S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const override; }; @@ -618,18 +637,20 @@ public: /** * @brief Constructor */ - LLSDBinaryFormatter(); + LLSDBinaryFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); +protected: /** - * @brief Call this method to format an LLSD to a stream. + * @brief Implementation to format the data. This is called recursively. * * @param data The data to write. * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects fomatted out + * @return Returns The number of LLSD objects formatted out */ - virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const; + S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const override; -protected: /** * @brief Helper method to serialize strings * @@ -669,7 +690,8 @@ public: /** * @brief Constructor */ - LLSDOStreamer(const LLSD& data, U32 options = LLSDFormatter::OPTIONS_NONE) : + LLSDOStreamer(const LLSD& data, + LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY) : mSD(data), mOptions(options) {} /** @@ -681,17 +703,17 @@ public: * @return Returns the stream passed in after streaming mSD. */ friend std::ostream& operator<<( - std::ostream& str, - const LLSDOStreamer& formatter) + std::ostream& out, + const LLSDOStreamer& streamer) { LLPointer f = new Formatter; - f->format(formatter.mSD, str, formatter.mOptions); - return str; + f->format(streamer.mSD, out, streamer.mOptions); + return out; } protected: LLSD mSD; - U32 mOptions; + LLSDFormatter::EFormatterOptions mOptions; }; typedef LLSDOStreamer LLSDNotationStreamer; @@ -724,7 +746,7 @@ public: * Generic in/outs */ static void serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize, - U32 options = LLSDFormatter::OPTIONS_NONE); + LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY); /** * @brief Examine a stream, and parse 1 sd object out based on contents. @@ -752,9 +774,9 @@ public: static S32 toPrettyBinaryNotation(const LLSD& sd, std::ostream& str) { LLPointer f = new LLSDNotationFormatter; - return f->format(sd, str, - LLSDFormatter::OPTIONS_PRETTY | - LLSDFormatter::OPTIONS_PRETTY_BINARY); + return f->format(sd, str, + LLSDFormatter::EFormatterOptions(LLSDFormatter::OPTIONS_PRETTY | + LLSDFormatter::OPTIONS_PRETTY_BINARY)); } static S32 fromNotation(LLSD& sd, std::istream& str, S32 max_bytes) { diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp index abd8bce224..721d456574 100644 --- a/indra/llcommon/llsdserialize_xml.cpp +++ b/indra/llcommon/llsdserialize_xml.cpp @@ -46,7 +46,9 @@ extern "C" /** * LLSDXMLFormatter */ -LLSDXMLFormatter::LLSDXMLFormatter() +LLSDXMLFormatter::LLSDXMLFormatter(bool boolAlpha, const std::string& realFormat, + EFormatterOptions options): + LLSDFormatter(boolAlpha, realFormat, options) { } @@ -56,7 +58,8 @@ LLSDXMLFormatter::~LLSDXMLFormatter() } // virtual -S32 LLSDXMLFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const +S32 LLSDXMLFormatter::format(const LLSD& data, std::ostream& ostr, + EFormatterOptions options) const { std::streamsize old_precision = ostr.precision(25); @@ -73,7 +76,8 @@ S32 LLSDXMLFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) return rv; } -S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const +S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, + EFormatterOptions options, U32 level) const { S32 format_count = 1; std::string pre; diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index 6a23c443a0..1e43c92226 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -856,6 +856,74 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits) } } +/***************************************************************************** +* llsd::drill() +*****************************************************************************/ +namespace llsd +{ + +LLSD& drill(LLSD& blob, const LLSD& rawPath) +{ + // Treat rawPath uniformly as an array. If it's not already an array, + // store it as the only entry in one. (But let's say Undefined means an + // empty array.) + LLSD path; + if (rawPath.isArray() || rawPath.isUndefined()) + { + path = rawPath; + } + else + { + path.append(rawPath); + } + + // Need to indicate a current destination -- but that current destination + // must 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. + // Start by pointing to the input blob exactly as is. + LLSD* located{&blob}; + + // Extract the element of interest by walking path. Use an explicit index + // so that, in case of a bogus type in path, we can identify the specific + // path entry that's bad. + for (LLSD::Integer i = 0; i < path.size(); ++i) + { + const LLSD& key{path[i]}; + if (key.isString()) + { + // a string path element is a map key + located = &((*located)[key.asString()]); + } + else if (key.isInteger()) + { + // an integer path element is an array index + located = &((*located)[key.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("llsdutil") << "drill(" << blob << ", " << rawPath + << "): path[" << i << "] bad type " + << sTypes.lookup(key.type()) << LL_ENDL; + } + } + + // dereference the pointer to return a reference to the element we found + return *located; +} + +LLSD drill(const LLSD& blob, const LLSD& path) +{ + // non-const drill() does exactly what we want. Temporarily cast away + // const-ness and use that. + return drill(const_cast(blob), path); +} + +} // namespace llsd + // Construct a deep partial clone of of an LLSD object. primitive types share // references, however maps, arrays and binary objects are duplicated. An optional // filter may be include to exclude/include keys in a map. @@ -910,7 +978,6 @@ LLSD llsd_clone(LLSD value, LLSD filter) return clone; } - LLSD llsd_shallow(LLSD value, LLSD filter) { LLSD shallow; diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 7a80625aeb..65bdb9a48e 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -143,6 +143,16 @@ LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, /// equality rather than bitwise equality, pass @a bits as for /// is_approx_equal_fraction(). LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1); +/// If you don't care about LLSD::Real equality +inline bool operator==(const LLSD& lhs, const LLSD& rhs) +{ + return llsd_equals(lhs, rhs); +} +inline bool operator!=(const LLSD& lhs, const LLSD& rhs) +{ + // operator!=() should always be the negation of operator==() + return ! (lhs == rhs); +} // Simple function to copy data out of input & output iterators if // there is no need for casting. @@ -156,6 +166,31 @@ template LLSD llsd_copy_array(Input iter, Input end) return dest; } +namespace llsd +{ + +/** + * Drill down to locate an element in 'blob' according to 'path', where 'path' + * is one of the following: + * + * - LLSD::String: 'blob' is an LLSD::Map. Find the entry with key 'path'. + * - LLSD::Integer: 'blob' is an LLSD::Array. Find the entry with index 'path'. + * - Any other 'path' type will be interpreted as LLSD::Array, and 'blob' is a + * nested structure. For each element of 'path': + * - If it's an LLSD::Integer, select the entry with that index from an + * LLSD::Array at that level. + * - If it's an LLSD::String, select the entry with that key from an + * LLSD::Map at that level. + * - Anything else is an error. + * + * By implication, if path.isUndefined() or otherwise equivalent to an empty + * LLSD::Array, drill() returns 'blob' as is. + */ +LLSD drill(const LLSD& blob, const LLSD& path); +LLSD& drill( LLSD& blob, const LLSD& path); + +} + /***************************************************************************** * LLSDArray *****************************************************************************/ @@ -225,6 +260,36 @@ private: LLSD _data; }; +namespace llsd +{ + +/** + * Construct an LLSD::Array inline, using modern C++ variadic arguments. + */ + +// recursion tail +inline +void array_(LLSD&) {} + +// recursive call +template +void array_(LLSD& data, T0&& v0, Ts&&... vs) +{ + data.append(std::forward(v0)); + array_(data, std::forward(vs)...); +} + +// public interface +template +LLSD array(Ts&&... vs) +{ + LLSD data; + array_(data, std::forward(vs)...); + return data; +} + +} // namespace llsd + /***************************************************************************** * LLSDMap *****************************************************************************/ @@ -269,6 +334,36 @@ private: LLSD _data; }; +namespace llsd +{ + +/** + * Construct an LLSD::Map inline, using modern C++ variadic arguments. + */ + +// recursion tail +inline +void map_(LLSD&) {} + +// recursive call +template +void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs) +{ + data[k0] = v0; + map_(data, std::forward(vs)...); +} + +// public interface +template +LLSD map(Ts&&... vs) +{ + LLSD data; + map_(data, std::forward(vs)...); + return data; +} + +} // namespace llsd + /***************************************************************************** * LLSDParam *****************************************************************************/ @@ -452,13 +547,22 @@ LLSD llsd_clone(LLSD value, LLSD filter = LLSD()); // the filter parameter. LLSD llsd_shallow(LLSD value, LLSD filter = LLSD()); +namespace llsd +{ +// llsd namespace aliases +inline +LLSD clone (LLSD value, LLSD filter=LLSD()) { return llsd_clone (value, filter); } +inline +LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter); } + +} // namespace llsd + +namespace boost{ // Specialization for generating a hash value from an LLSD block. -// GCC 4.9 does not like the specialization in form of boost::hash but rather wants a namespace -// template <> -// struct boost::hash -namespace boost { template <> struct hash -// +template <> +/* struct boost:hash */ +struct hash { typedef LLSD argument_type; typedef std::size_t result_type; @@ -519,6 +623,5 @@ namespace boost { template <> struct hash return seed; } }; -} // close namespace - +} #endif // LL_LLSDUTIL_H diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index 812fd31719..d3d25201b2 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -30,10 +30,9 @@ #include "llerror.h" #include "llerrorcontrol.h" // LLError::is_available() #include "lldependencies.h" -#include "llcoro_get_id.h" #include "llexception.h" +#include "llcoros.h" #include -#include #include #include // std::cerr in dire emergency #include @@ -43,8 +42,6 @@ namespace { void log(LLError::ELevel level, const char* p1, const char* p2, const char* p3, const char* p4); -void logdebugs(const char* p1="", const char* p2="", const char* p3="", const char* p4=""); - bool oktolog(); } // anonymous namespace @@ -115,19 +112,10 @@ private: // initialized, either in the constructor or in initSingleton(). However, // managing that as a stack depends on having a DISTINCT 'initializing' // stack for every C++ stack in the process! And we have a distinct C++ - // stack for every running coroutine. It would be interesting and cool to - // implement a generic coroutine-local-storage mechanism and use that - // here. The trouble is that LLCoros is itself an LLSingleton, so - // depending on LLCoros functionality could dig us into infinite - // recursion. (Moreover, when we reimplement LLCoros on top of - // Boost.Fiber, that library already provides fiber_specific_ptr -- so - // it's not worth a great deal of time and energy implementing a generic - // equivalent on top of boost::dcoroutine, which is on its way out.) - // Instead, use a map of llcoro::id to select the appropriate - // coro-specific 'initializing' stack. llcoro::get_id() is carefully - // implemented to avoid requiring LLCoros. - typedef boost::unordered_map InitializingMap; - InitializingMap mInitializing; + // stack for every running coroutine. Therefore this stack must be based + // on a coroutine-local pointer. + // This local_ptr isn't static because it's a member of an LLSingleton. + LLCoros::local_ptr mInitializing; public: // Instantiate this to obtain a reference to the coroutine-specific @@ -145,8 +133,8 @@ public: { if (! mList) { - LLTHROW(std::runtime_error("Trying to use LockedInitializing " - "after cleanup_initializing()")); + LLTHROW(LLException("Trying to use LockedInitializing " + "after cleanup_initializing()")); } return *mList; } @@ -166,18 +154,23 @@ public: private: list_t& get_initializing_() { - // map::operator[] has find-or-create semantics, exactly what we need - // here. It returns a reference to the selected mapped_type instance. - return mInitializing[llcoro::get_id()]; + LLSingletonBase::list_t* current = mInitializing.get(); + if (! current) + { + // If the running coroutine doesn't already have an initializing + // stack, allocate a new one and save it for future reference. + current = new LLSingletonBase::list_t(); + mInitializing.reset(current); + } + return *current; } + // By the time mInitializing is destroyed, its value for every coroutine + // except the running one must have been reset() to nullptr. So every time + // we pop the list to empty, reset() the running coroutine's local_ptr. void cleanup_initializing_() { - InitializingMap::iterator found = mInitializing.find(llcoro::get_id()); - if (found != mInitializing.end()) - { - mInitializing.erase(found); - } + mInitializing.reset(nullptr); } }; @@ -302,7 +295,7 @@ void LLSingletonBase::MasterList::LockedInitializing::log(const char* verb, cons } } -void LLSingletonBase::capture_dependency(EInitState initState) +void LLSingletonBase::capture_dependency() { MasterList::LockedInitializing locked_list; list_t& initializing(locked_list.get()); @@ -334,21 +327,8 @@ void LLSingletonBase::capture_dependency(EInitState initState) LLSingletonBase* foundp(*found); out << classname(foundp) << " -> "; } - // We promise to capture dependencies from both the constructor - // and the initSingleton() method, so an LLSingleton's instance - // pointer is on the initializing list during both. Now that we've - // detected circularity, though, we must distinguish the two. If - // the recursive call is from the constructor, we CAN'T honor it: - // otherwise we'd be returning a pointer to a partially- - // constructed object! But from initSingleton() is okay: that - // method exists specifically to support circularity. // Decide which log helper to call. - if (initState == CONSTRUCTING) - { - logerrs("LLSingleton circularity in Constructor: ", out.str().c_str(), - classname(this).c_str(), ""); - } - else if (it_next == initializing.end()) + if (it_next == initializing.end()) { // Points to self after construction, but during initialization. // Singletons can initialize other classes that depend onto them, @@ -391,13 +371,12 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() // SingletonDeps through the life of the program, dynamically adding and // removing LLSingletons as they are created and destroyed, in practice // it's less messy to construct it on demand. The overhead of doing so - // should happen basically twice: once for cleanupAll(), once for - // deleteAll(). + // should happen basically once: for deleteAll(). typedef LLDependencies SingletonDeps; SingletonDeps sdeps; // Lock while traversing the master list MasterList::LockedMaster master; - BOOST_FOREACH(LLSingletonBase* sp, master.get()) + for (LLSingletonBase* sp : master.get()) { // Build the SingletonDeps structure by adding, for each // LLSingletonBase* sp in the master list, sp itself. It has no @@ -414,46 +393,27 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() // extracts just the first (key) element from each sorted_iterator, then // uses vec_t's range constructor... but frankly this is more // straightforward, as long as we remember the above reserve() call! - BOOST_FOREACH(SingletonDeps::sorted_iterator::value_type pair, sdeps.sort()) + for (const SingletonDeps::sorted_iterator::value_type& pair : sdeps.sort()) { ret.push_back(pair.first); } // The master list is not itself pushed onto the master list. Add it as // the very last entry -- it is the LLSingleton on which ALL others // depend! -- so our caller will process it. - ret.push_back(MasterList::getInstance()); + ret.push_back(&master.Lock::get()); return ret; } -//static -void LLSingletonBase::cleanupAll() +void LLSingletonBase::cleanup_() { - // It's essential to traverse these in dependency order. - BOOST_FOREACH(LLSingletonBase* sp, dep_sort()) + logdebugs("calling ", classname(this).c_str(), "::cleanupSingleton()"); + try { - // Call cleanupSingleton() only if we haven't already done so for this - // instance. - if (! sp->mCleaned) - { - sp->mCleaned = true; - - logdebugs("calling ", - classname(sp).c_str(), "::cleanupSingleton()"); - try - { - sp->cleanupSingleton(); - } - catch (const std::exception& e) - { - logwarns("Exception in ", classname(sp).c_str(), - "::cleanupSingleton(): ", e.what()); - } - catch (...) - { - logwarns("Unknown exception in ", classname(sp).c_str(), - "::cleanupSingleton()"); - } - } + cleanupSingleton(); + } + catch (...) + { + LOG_UNHANDLED_EXCEPTION(classname(this) + "::cleanupSingleton()"); } } @@ -524,10 +484,6 @@ void log(LLError::ELevel level, } } -void logdebugs(const char* p1, const char* p2, const char* p3, const char* p4) -{ - log(LLError::LEVEL_DEBUG, p1, p2, p3, p4); -} } // anonymous namespace //static @@ -536,6 +492,18 @@ void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, c log(LLError::LEVEL_WARN, p1, p2, p3, p4); } +//static +void LLSingletonBase::loginfos(const char* p1, const char* p2, const char* p3, const char* p4) +{ + log(LLError::LEVEL_INFO, p1, p2, p3, p4); +} + +//static +void LLSingletonBase::logdebugs(const char* p1, const char* p2, const char* p3, const char* p4) +{ + log(LLError::LEVEL_DEBUG, p1, p2, p3, p4); +} + //static void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4) { diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index b87b6f4036..6f4f9bea17 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -30,18 +30,10 @@ #include #include #include - -#if LL_WINDOWS -#pragma warning (push) -#pragma warning (disable:4265) -#endif -// warning C4265: 'std::_Pad' : class has virtual functions, but destructor is not virtual - -#include - -#if LL_WINDOWS -#pragma warning (pop) -#endif +#include "mutex.h" +#include "lockstatic.h" +#include "llthread.h" // on_main_thread() +#include "llmainthreadtask.h" class LLSingletonBase: private boost::noncopyable { @@ -58,7 +50,6 @@ private: typedef std::vector vec_t; static vec_t dep_sort(); - bool mCleaned; // cleanupSingleton() has been called // we directly depend on these other LLSingletons typedef boost::unordered_set set_t; set_t mDepends; @@ -67,8 +58,8 @@ protected: typedef enum e_init_state { UNINITIALIZED = 0, // must be default-initialized state + QUEUED, // construction queued, not yet executing CONSTRUCTING, // within DERIVED_TYPE constructor - CONSTRUCTED, // finished DERIVED_TYPE constructor INITIALIZING, // within DERIVED_TYPE::initSingleton() INITIALIZED, // normal case DELETED // deleteSingleton() or deleteAll() called @@ -117,15 +108,20 @@ protected: protected: // If a given call to B::getInstance() happens during either A::A() or // A::initSingleton(), record that A directly depends on B. - void capture_dependency(EInitState); + void capture_dependency(); - // delegate LL_ERRS() logging to llsingleton.cpp + // delegate logging calls to llsingleton.cpp static void logerrs(const char* p1, const char* p2="", const char* p3="", const char* p4=""); - // delegate LL_WARNS() logging to llsingleton.cpp static void logwarns(const char* p1, const char* p2="", const char* p3="", const char* p4=""); + static void loginfos(const char* p1, const char* p2="", + const char* p3="", const char* p4=""); + static void logdebugs(const char* p1, const char* p2="", + const char* p3="", const char* p4=""); static std::string demangle(const char* mangled); + // these classname() declarations restate template functions declared in + // llerror.h because we avoid #including that here template static std::string classname() { return demangle(typeid(T).name()); } template @@ -135,6 +131,9 @@ protected: virtual void initSingleton() {} virtual void cleanupSingleton() {} + // internal wrapper around calls to cleanupSingleton() + void cleanup_(); + // deleteSingleton() isn't -- and shouldn't be -- a virtual method. It's a // class static. However, given only Foo*, deleteAll() does need to be // able to reach Foo::deleteSingleton(). Make LLSingleton (which declares @@ -144,32 +143,15 @@ protected: public: /** - * Call this to call the cleanupSingleton() method for every LLSingleton - * constructed since the start of the last cleanupAll() call. (Any - * LLSingleton constructed DURING a cleanupAll() call won't be cleaned up - * until the next cleanupAll() call.) cleanupSingleton() neither deletes - * nor destroys its LLSingleton; therefore it's safe to include logic that - * might take significant realtime or even throw an exception. - * - * The most important property of cleanupAll() is that cleanupSingleton() - * methods are called in dependency order, leaf classes last. Thus, given - * two LLSingleton subclasses A and B, if A's dependency on B is properly - * expressed as a B::getInstance() or B::instance() call during either - * A::A() or A::initSingleton(), B will be cleaned up after A. - * - * If a cleanupSingleton() method throws an exception, the exception is - * logged, but cleanupAll() attempts to continue calling the rest of the - * cleanupSingleton() methods. - */ - static void cleanupAll(); - /** - * Call this to call the deleteSingleton() method for every LLSingleton - * constructed since the start of the last deleteAll() call. (Any - * LLSingleton constructed DURING a deleteAll() call won't be cleaned up - * until the next deleteAll() call.) deleteSingleton() deletes and - * destroys its LLSingleton. Any cleanup logic that might take significant - * realtime -- or throw an exception -- must not be placed in your - * LLSingleton's destructor, but rather in its cleanupSingleton() method. + * deleteAll() calls the cleanupSingleton() and deleteSingleton() methods + * for every LLSingleton constructed since the start of the last + * deleteAll() call. (Any LLSingleton constructed DURING a deleteAll() + * call won't be cleaned up until the next deleteAll() call.) + * deleteSingleton() deletes and destroys its LLSingleton. Any cleanup + * logic that might take significant realtime -- or throw an exception -- + * must not be placed in your LLSingleton's destructor, but rather in its + * cleanupSingleton() method, which is called implicitly by + * deleteSingleton(). * * The most important property of deleteAll() is that deleteSingleton() * methods are called in dependency order, leaf classes last. Thus, given @@ -177,9 +159,9 @@ public: * expressed as a B::getInstance() or B::instance() call during either * A::A() or A::initSingleton(), B will be cleaned up after A. * - * If a deleteSingleton() method throws an exception, the exception is - * logged, but deleteAll() attempts to continue calling the rest of the - * deleteSingleton() methods. + * If a cleanupSingleton() or deleteSingleton() method throws an + * exception, the exception is logged, but deleteAll() attempts to + * continue calling the rest of the deleteSingleton() methods. */ static void deleteAll(); }; @@ -205,9 +187,9 @@ struct LLSingleton_manage_master { return LLSingletonBase::get_initializing_size(); } - void capture_dependency(LLSingletonBase* sb, LLSingletonBase::EInitState state) + void capture_dependency(LLSingletonBase* sb) { - sb->capture_dependency(state); + sb->capture_dependency(); } }; @@ -222,14 +204,13 @@ struct LLSingleton_manage_master // since we never pushed, no need to clean up void reset_initializing(LLSingletonBase::list_t::size_type size) {} LLSingletonBase::list_t::size_type get_initializing_size() { return 0; } - void capture_dependency(LLSingletonBase*, LLSingletonBase::EInitState) {} + void capture_dependency(LLSingletonBase*) {} }; // Now we can implement LLSingletonBase's template constructor. template LLSingletonBase::LLSingletonBase(tag): - mCleaned(false), - mDeleteSingleton(NULL) + mDeleteSingleton(nullptr) { // This is the earliest possible point at which we can push this new // instance onto the init stack. LLSingleton::constructSingleton() can't @@ -271,10 +252,19 @@ class LLParamSingleton; * leading back to yours, move the instance reference from your constructor to * your initSingleton() method. * - * If you override LLSingleton::cleanupSingleton(), your method will be - * called if someone calls LLSingletonBase::cleanupAll(). The significant part - * of this promise is that cleanupAll() will call individual - * cleanupSingleton() methods in reverse dependency order. + * If you override LLSingleton::cleanupSingleton(), your method will + * implicitly be called by LLSingleton::deleteSingleton() just before the + * instance is destroyed. We introduce a special cleanupSingleton() method + * because cleanupSingleton() operations can involve nontrivial realtime, or + * throw an exception. A destructor should do neither! + * + * If your cleanupSingleton() method throws an exception, we log that + * exception but carry on. + * + * If at some point you call LLSingletonBase::deleteAll(), all remaining + * LLSingleton instances will be destroyed in reverse dependency order. (Or + * call MySubclass::deleteSingleton() to specifically destroy the canonical + * MySubclass instance.) * * That is, consider LLSingleton subclasses C, B and A. A depends on B, which * in turn depends on C. These dependencies are expressed as calls to @@ -282,31 +272,34 @@ class LLParamSingleton; * It shouldn't matter whether these calls appear in A::A() or * A::initSingleton(), likewise B::B() or B::initSingleton(). * - * We promise that if you later call LLSingletonBase::cleanupAll(): - * 1. A::cleanupSingleton() will be called before - * 2. B::cleanupSingleton(), which will be called before - * 3. C::cleanupSingleton(). + * We promise that if you later call LLSingletonBase::deleteAll(): + * 1. A::deleteSingleton() will be called before + * 2. B::deleteSingleton(), which will be called before + * 3. C::deleteSingleton(). * Put differently, if your LLSingleton subclass constructor or * initSingleton() method explicitly depends on some other LLSingleton * subclass, you may continue to rely on that other subclass in your * cleanupSingleton() method. - * - * We introduce a special cleanupSingleton() method because cleanupSingleton() - * operations can involve nontrivial realtime, or might throw an exception. A - * destructor should do neither! - * - * If your cleanupSingleton() method throws an exception, we log that - * exception but proceed with the remaining cleanupSingleton() calls. - * - * Similarly, if at some point you call LLSingletonBase::deleteAll(), all - * remaining LLSingleton instances will be destroyed in dependency order. (Or - * call MySubclass::deleteSingleton() to specifically destroy the canonical - * MySubclass instance.) */ template class LLSingleton : public LLSingletonBase { private: + // LLSingleton must have a distinct instance of + // SingletonData for every distinct DERIVED_TYPE. It's tempting to + // consider hoisting SingletonData up into LLSingletonBase. Don't do it. + struct SingletonData + { + // Use a recursive_mutex in case of constructor circularity. With a + // non-recursive mutex, that would result in deadlock. + typedef std::recursive_mutex mutex_t; + mutex_t mMutex; // LockStatic looks for mMutex + + EInitState mInitState{UNINITIALIZED}; + DERIVED_TYPE* mInstance{nullptr}; + }; + typedef llthread::LockStatic LockStatic; + // Allow LLParamSingleton subclass -- but NOT DERIVED_TYPE itself -- to // access our private members. friend class LLParamSingleton; @@ -356,17 +349,17 @@ private: // purpose for its subclass LLParamSingleton is to support Singletons // requiring constructor arguments. constructSingleton() supports both use // cases. + // Accepting LockStatic& requires that the caller has already locked our + // static data before calling. template - static void constructSingleton(Args&&... args) + static void constructSingleton(LockStatic& lk, Args&&... args) { auto prev_size = LLSingleton_manage_master().get_initializing_size(); - // getInstance() calls are from within constructor - sData.mInitState = CONSTRUCTING; + // Any getInstance() calls after this point are from within constructor + lk->mInitState = CONSTRUCTING; try { - sData.mInstance = new DERIVED_TYPE(std::forward(args)...); - // we have called constructor, have not yet called initSingleton() - sData.mInitState = CONSTRUCTED; + lk->mInstance = new DERIVED_TYPE(std::forward(args)...); } catch (const std::exception& err) { @@ -380,58 +373,56 @@ private: // There isn't a separate EInitState value meaning "we attempted // to construct this LLSingleton subclass but could not," so use // DELETED. That seems slightly more appropriate than UNINITIALIZED. - sData.mInitState = DELETED; + lk->mInitState = DELETED; // propagate the exception throw; } - } - static void finishInitializing() - { - // getInstance() calls are from within initSingleton() - sData.mInitState = INITIALIZING; + // Any getInstance() calls after this point are from within initSingleton() + lk->mInitState = INITIALIZING; try { // initialize singleton after constructing it so that it can // reference other singletons which in turn depend on it, thus // breaking cyclic dependencies - sData.mInstance->initSingleton(); - sData.mInitState = INITIALIZED; + lk->mInstance->initSingleton(); + lk->mInitState = INITIALIZED; // pop this off stack of initializing singletons - pop_initializing(); + pop_initializing(lk->mInstance); } catch (const std::exception& err) { // pop this off stack of initializing singletons here, too -- // BEFORE logging, so log-machinery LLSingletons don't record a // dependency on DERIVED_TYPE! - pop_initializing(); + pop_initializing(lk->mInstance); logwarns("Error in ", classname().c_str(), "::initSingleton(): ", err.what()); - // and get rid of the instance entirely + // Get rid of the instance entirely. This call depends on our + // recursive_mutex. We could have a deleteSingleton(LockStatic&) + // overload and pass lk, but we don't strictly need it. deleteSingleton(); // propagate the exception throw; } } - static void pop_initializing() + static void pop_initializing(LLSingletonBase* sb) { // route through LLSingleton_manage_master so we Do The Right Thing // (namely, nothing) for MasterList - LLSingleton_manage_master().pop_initializing(sData.mInstance); + LLSingleton_manage_master().pop_initializing(sb); } - static void capture_dependency() + static void capture_dependency(LLSingletonBase* sb) { // By this point, if DERIVED_TYPE was pushed onto the initializing // stack, it has been popped off. So the top of that stack, if any, is // an LLSingleton that directly depends on DERIVED_TYPE. If // getInstance() was called by another LLSingleton, rather than from // vanilla application code, record the dependency. - LLSingleton_manage_master().capture_dependency( - sData.mInstance, sData.mInitState); + LLSingleton_manage_master().capture_dependency(sb); } // We know of no way to instruct the compiler that every subclass @@ -461,94 +452,173 @@ protected: protected: virtual ~LLSingleton() { - // In case racing threads call getInstance() at the same moment as - // this destructor, serialize the calls. - Locker lk; + // This phase of cleanup is performed in the destructor rather than in + // deleteSingleton() to defend against manual deletion. When we moved + // cleanup to deleteSingleton(), we hit crashes due to dangling + // pointers in the MasterList. + LockStatic lk; + lk->mInstance = nullptr; + lk->mInitState = DELETED; - // remove this instance from the master list + // Remove this instance from the master list. LLSingleton_manage_master().remove(this); - sData.mInstance = NULL; - sData.mInitState = DELETED; } public: /** - * @brief Immediately delete the singleton. + * @brief Cleanup and destroy the singleton instance. * - * A subsequent call to LLProxy::getInstance() will construct a new + * deleteSingleton() calls this instance's cleanupSingleton() method and + * then destroys the instance. + * + * A subsequent call to LLSingleton::getInstance() will construct a new * instance of the class. * - * Without an explicit call to LLSingletonBase::deleteAll(), LLSingletons - * are implicitly destroyed after main() has exited and the C++ runtime is - * cleaning up statically-constructed objects. Some classes derived from - * LLSingleton have objects that are part of a runtime system that is - * terminated before main() exits. Calling the destructor of those objects - * after the termination of their respective systems can cause crashes and - * other problems during termination of the project. Using this method to - * destroy the singleton early can prevent these crashes. - * - * An example where this is needed is for a LLSingleton that has an APR - * object as a member that makes APR calls on destruction. The APR system is - * shut down explicitly before main() exits. This causes a crash on exit. - * Using this method before the call to apr_terminate() and NOT calling - * getInstance() again will prevent the crash. + * Without an explicit call to LLSingletonBase::deleteAll(), or + * LLSingleton::deleteSingleton(), LLSingleton instances are simply + * leaked. (Allowing implicit destruction at shutdown caused too many + * problems.) */ static void deleteSingleton() { - delete sData.mInstance; - // SingletonData state handled by destructor, above + // Hold the lock while we call cleanupSingleton() and the destructor. + // Our destructor also instantiates LockStatic, requiring a recursive + // mutex. + LockStatic lk; + // of course, only cleanup and delete if there's something there + if (lk->mInstance) + { + lk->mInstance->cleanup_(); + delete lk->mInstance; + // destructor clears mInstance (and mInitState) + } } static DERIVED_TYPE* getInstance() { - // In case racing threads call getInstance() at the same moment, - // serialize the calls. - Locker lk; + // We know the viewer has LLSingleton dependency circularities. If you + // feel strongly motivated to eliminate them, cheers and good luck. + // (At that point we could consider a much simpler locking mechanism.) - switch (sData.mInitState) - { - case CONSTRUCTING: - // here if DERIVED_TYPE's constructor (directly or indirectly) - // calls DERIVED_TYPE::getInstance() - logerrs("Tried to access singleton ", - classname().c_str(), - " from singleton constructor!"); - return NULL; + // If A and B depend on each other, and thread T1 requests A at the + // same moment thread T2 requests B, you could get a sequence like this: + // - T1 locks A + // - T2 locks B + // - T1, having constructed A, calls A::initSingleton(), which calls + // B::getInstance() and blocks on B's lock + // - T2, having constructed B, calls B::initSingleton(), which calls + // A::getInstance() and blocks on A's lock + // In other words, classic deadlock. - case UNINITIALIZED: - constructSingleton(); - // fall through... + // Avoid that by constructing and initializing every LLSingleton on + // the main thread. In that scenario: + // - T1 locks A + // - T2 locks B + // - T1 discovers A is UNINITIALIZED, so it queues a task for the main + // thread, unlocks A and blocks on the std::future. + // - T2 discovers B is UNINITIALIZED, so it queues a task for the main + // thread, unlocks B and blocks on the std::future. + // - The main thread executes T1's request for A. It locks A and + // starts to construct it. + // - A::initSingleton() calls B::getInstance(). Fine: nobody's holding + // B's lock. + // - The main thread locks B, constructs B, calls B::initSingleton(), + // which calls A::getInstance(), which returns A. + // - B::getInstance() returns B to A::initSingleton(), unlocking B. + // - A::getInstance() returns A to the task wrapper, unlocking A. + // - The task wrapper passes A to T1 via the future. T1 resumes. + // - The main thread executes T2's request for B. Oh look, B already + // exists. The task wrapper passes B to T2 via the future. T2 + // resumes. + // This still works even if one of T1 or T2 *is* the main thread. + // This still works even if thread T3 requests B at the same moment as + // T2. Finding B still UNINITIALIZED, T3 also queues a task for the + // main thread, unlocks B and blocks on a (distinct) std::future. By + // the time the main thread executes T3's request for B, B already + // exists, and is simply delivered via the future. - case CONSTRUCTED: - // still have to call initSingleton() - finishInitializing(); - break; + { // nested scope for 'lk' + // In case racing threads call getInstance() at the same moment, + // serialize the calls. + LockStatic lk; - case INITIALIZING: - // here if DERIVED_TYPE::initSingleton() (directly or indirectly) - // calls DERIVED_TYPE::getInstance(): go ahead and allow it - case INITIALIZED: - // normal subsequent calls - break; + switch (lk->mInitState) + { + case CONSTRUCTING: + // here if DERIVED_TYPE's constructor (directly or indirectly) + // calls DERIVED_TYPE::getInstance() + logerrs("Tried to access singleton ", + classname().c_str(), + " from singleton constructor!"); + return nullptr; - case DELETED: - // called after deleteSingleton() - logwarns("Trying to access deleted singleton ", - classname().c_str(), - " -- creating new instance"); - constructSingleton(); - finishInitializing(); - break; - } + case INITIALIZING: + // here if DERIVED_TYPE::initSingleton() (directly or indirectly) + // calls DERIVED_TYPE::getInstance(): go ahead and allow it + case INITIALIZED: + // normal subsequent calls + // record the dependency, if any: check if we got here from another + // LLSingleton's constructor or initSingleton() method + capture_dependency(lk->mInstance); + return lk->mInstance; - // record the dependency, if any: check if we got here from another - // LLSingleton's constructor or initSingleton() method - capture_dependency(); - return sData.mInstance; + case DELETED: + // called after deleteSingleton() + logwarns("Trying to access deleted singleton ", + classname().c_str(), + " -- creating new instance"); + // fall through + case UNINITIALIZED: + case QUEUED: + // QUEUED means some secondary thread has already requested an + // instance, but for present purposes that's semantically + // identical to UNINITIALIZED: either way, we must ourselves + // request an instance. + break; + } + + // Here we need to construct a new instance. + if (on_main_thread()) + { + // On the main thread, directly construct the instance while + // holding the lock. + constructSingleton(lk); + capture_dependency(lk->mInstance); + return lk->mInstance; + } + + // Here we need to construct a new instance, but we're on a secondary + // thread. + lk->mInitState = QUEUED; + } // unlock 'lk' + + // Per the comment block above, dispatch to the main thread. + loginfos(classname().c_str(), + "::getInstance() dispatching to main thread"); + auto instance = LLMainThreadTask::dispatch( + [](){ + // VERY IMPORTANT to call getInstance() on the main thread, + // rather than going straight to constructSingleton()! + // During the time window before mInitState is INITIALIZED, + // multiple requests might be queued. It's essential that, as + // the main thread processes them, only the FIRST such request + // actually constructs the instance -- every subsequent one + // simply returns the existing instance. + loginfos(classname().c_str(), + "::getInstance() on main thread"); + return getInstance(); + }); + // record the dependency chain tracked on THIS thread, not the main + // thread (consider a getInstance() overload with a tag param that + // suppresses dep tracking when dispatched to the main thread) + capture_dependency(instance); + loginfos(classname().c_str(), + "::getInstance() returning on requesting thread"); + return instance; } // Reference version of getInstance() - // Preferred over getInstance() as it disallows checking for NULL + // Preferred over getInstance() as it disallows checking for nullptr static DERIVED_TYPE& instance() { return *getInstance(); @@ -559,8 +629,8 @@ public: static bool instanceExists() { // defend any access to sData from racing threads - Locker lk; - return sData.mInitState == INITIALIZED; + LockStatic lk; + return lk->mInitState == INITIALIZED; } // Has this singleton been deleted? This can be useful during shutdown @@ -569,24 +639,11 @@ public: static bool wasDeleted() { // defend any access to sData from racing threads - Locker lk; - return sData.mInitState == DELETED; + LockStatic lk; + return lk->mInitState == DELETED; } - -private: - struct SingletonData - { - // explicitly has a default constructor so that member variables are zero initialized in BSS - // and only changed by singleton logic, not constructor running during startup - EInitState mInitState; - DERIVED_TYPE* mInstance; - }; - static SingletonData sData; }; -template -typename LLSingleton::SingletonData LLSingleton::sData; - /** * LLParamSingleton is like LLSingleton, except in the following ways: @@ -611,44 +668,86 @@ class LLParamSingleton : public LLSingleton { private: typedef LLSingleton super; - using typename super::Locker; + using typename super::LockStatic; + + // Passes arguments to DERIVED_TYPE's constructor and sets appropriate + // states, returning a pointer to the new instance. + template + static DERIVED_TYPE* initParamSingleton_(Args&&... args) + { + // In case racing threads both call initParamSingleton() at the same + // time, serialize them. One should initialize; the other should see + // mInitState already set. + LockStatic lk; + // For organizational purposes this function shouldn't be called twice + if (lk->mInitState != super::UNINITIALIZED) + { + super::logerrs("Tried to initialize singleton ", + super::template classname().c_str(), + " twice!"); + return nullptr; + } + else if (on_main_thread()) + { + // on the main thread, simply construct instance while holding lock + super::logdebugs(super::template classname().c_str(), + "::initParamSingleton()"); + super::constructSingleton(lk, std::forward(args)...); + return lk->mInstance; + } + else + { + // on secondary thread, dispatch to main thread -- + // set state so we catch any other calls before the main thread + // picks up the task + lk->mInitState = super::QUEUED; + // very important to unlock here so main thread can actually process + lk.unlock(); + super::loginfos(super::template classname().c_str(), + "::initParamSingleton() dispatching to main thread"); + // Normally it would be the height of folly to reference-bind + // 'args' into a lambda to be executed on some other thread! By + // the time that thread executed the lambda, the references would + // all be dangling, and Bad Things would result. But + // LLMainThreadTask::dispatch() promises to block until the passed + // task has completed. So in this case we know the references will + // remain valid until the lambda has run, so we dare to bind + // references. + auto instance = LLMainThreadTask::dispatch( + [&](){ + super::loginfos(super::template classname().c_str(), + "::initParamSingleton() on main thread"); + return initParamSingleton_(std::forward(args)...); + }); + super::loginfos(super::template classname().c_str(), + "::initParamSingleton() returning on requesting thread"); + return instance; + } + } public: using super::deleteSingleton; using super::instanceExists; using super::wasDeleted; - // Passes arguments to DERIVED_TYPE's constructor and sets appropriate states + /// initParamSingleton() constructs the instance, returning a reference. + /// Pass whatever arguments are required to construct DERIVED_TYPE. template - static void initParamSingleton(Args&&... args) + static DERIVED_TYPE& initParamSingleton(Args&&... args) { - // In case racing threads both call initParamSingleton() at the same - // time, serialize them. One should initialize; the other should see - // mInitState already set. - Locker lk; - // For organizational purposes this function shouldn't be called twice - if (super::sData.mInitState != super::UNINITIALIZED) - { - super::logerrs("Tried to initialize singleton ", - super::template classname().c_str(), - " twice!"); - } - else - { - super::constructSingleton(std::forward(args)...); - super::finishInitializing(); - } + return *initParamSingleton_(std::forward(args)...); } static DERIVED_TYPE* getInstance() { // In case racing threads call getInstance() at the same moment as // initParamSingleton(), serialize the calls. - Locker lk; + LockStatic lk; - switch (super::sData.mInitState) + switch (lk->mInitState) { case super::UNINITIALIZED: + case super::QUEUED: super::logerrs("Uninitialized param singleton ", super::template classname().c_str()); break; @@ -659,25 +758,13 @@ public: " from singleton constructor!"); break; - case super::CONSTRUCTED: - // Should never happen!? The CONSTRUCTED state is specifically to - // navigate through LLSingleton::SingletonInitializer getting - // constructed (once) before LLSingleton::getInstance()'s switch - // on mInitState. But our initParamSingleton() method calls - // constructSingleton() and then calls finishInitializing(), which - // immediately sets INITIALIZING. Why are we here? - super::logerrs("Param singleton ", - super::template classname().c_str(), - "::initSingleton() not yet called"); - break; - case super::INITIALIZING: // As with LLSingleton, explicitly permit circular calls from // within initSingleton() case super::INITIALIZED: // for any valid call, capture dependencies - super::capture_dependency(); - return super::sData.mInstance; + super::capture_dependency(lk->mInstance); + return lk->mInstance; case super::DELETED: super::logerrs("Trying to access deleted param singleton ", @@ -721,9 +808,9 @@ public: using super::instanceExists; using super::wasDeleted; - static void construct() + static DT* construct() { - super::initParamSingleton(); + return super::initParamSingleton(); } }; diff --git a/indra/llcommon/llstacktrace.cpp b/indra/llcommon/llstacktrace.cpp index bbf0e1e141..80057bf0f2 100644 --- a/indra/llcommon/llstacktrace.cpp +++ b/indra/llcommon/llstacktrace.cpp @@ -33,7 +33,10 @@ #include #include "llwin32headerslean.h" -#include "Dbghelp.h" +#pragma warning (push) +#pragma warning (disable:4091) // a microsoft header has warnings. Very nice. +#include +#pragma warning (pop) typedef USHORT NTAPI RtlCaptureStackBackTrace_Function( IN ULONG frames_to_skip, diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index b024b47225..81adb1718c 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -36,6 +36,8 @@ #include #include +#include // For strcmp + // Use to compare the first element only of a pair // e.g. typedef std::set, compare_pair > some_pair_set_t; template diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 08551a3955..fc157f6d28 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -729,22 +729,6 @@ std::string utf8str_removeCRLF(const std::string& utf8str) } #if LL_WINDOWS -// documentation moved to header. Phoenix 2007-11-27 -namespace snprintf_hack -{ - int snprintf(char *str, size_t size, const char *format, ...) - { - va_list args; - va_start(args, format); - - int num_written = _vsnprintf(str, size, format, args); /* Flawfinder: ignore */ - va_end(args); - - str[size-1] = '\0'; // always null terminate - return num_written; - } -} - std::string ll_convert_wide_to_string(const wchar_t* in) { return ll_convert_wide_to_string(in, CP_UTF8); diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 2118268034..120001e29c 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -715,32 +715,6 @@ LL_COMMON_API std::string utf8str_removeCRLF(const std::string& utf8str); */ //@{ -/** - * @brief Implementation the expected snprintf interface. - * - * If the size of the passed in buffer is not large enough to hold the string, - * two bad things happen: - * 1. resulting formatted string is NOT null terminated - * 2. Depending on the platform, the return value could be a) the required - * size of the buffer to copy the entire formatted string or b) -1. - * On Windows with VS.Net 2003, it returns -1 e.g. - * - * safe_snprintf always adds a NULL terminator so that the caller does not - * need to check for return value or need to add the NULL terminator. - * It does not, however change the return value - to let the caller know - * that the passed in buffer size was not large enough to hold the - * formatted string. - * - */ - -// Deal with the differeneces on Windows -namespace snprintf_hack -{ - LL_COMMON_API int snprintf(char *str, size_t size, const char *format, ...); -} - -using snprintf_hack::snprintf; - /** * @brief Convert a wide string to std::string * diff --git a/indra/llcommon/lltempredirect.cpp b/indra/llcommon/lltempredirect.cpp new file mode 100644 index 0000000000..ec194c1d29 --- /dev/null +++ b/indra/llcommon/lltempredirect.cpp @@ -0,0 +1,138 @@ +/** + * @file lltempredirect.cpp + * @author Nat Goodspeed + * @date 2019-10-31 + * @brief Implementation for lltempredirect. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lltempredirect.h" +// STL headers +// std headers +#if !LL_WINDOWS +# include +#else +# include +#endif // !LL_WINDOWS +// external library headers +// other Linden headers + +/***************************************************************************** +* llfd +*****************************************************************************/ +// We could restate the implementation of each of llfd::close(), etc., but +// this is way more succinct. +#if LL_WINDOWS +#define fhclose _close +#define fhdup _dup +#define fhdup2 _dup2 +#define fhfdopen _fdopen +#define fhfileno _fileno +#else +#define fhclose ::close +#define fhdup ::dup +#define fhdup2 ::dup2 +#define fhfdopen ::fdopen +#define fhfileno ::fileno +#endif + +int llfd::close(int fd) +{ + return fhclose(fd); +} + +int llfd::dup(int target) +{ + return fhdup(target); +} + +int llfd::dup2(int target, int reference) +{ + return fhdup2(target, reference); +} + +FILE* llfd::open(int fd, const char* mode) +{ + return fhfdopen(fd, mode); +} + +int llfd::fileno(FILE* stream) +{ + return fhfileno(stream); +} + +/***************************************************************************** +* LLTempRedirect +*****************************************************************************/ +LLTempRedirect::LLTempRedirect(): + mOrigTarget(-1), // -1 is an invalid file descriptor + mReference(-1) +{} + +LLTempRedirect::LLTempRedirect(FILE* target, FILE* reference): + LLTempRedirect((target? fhfileno(target) : -1), + (reference? fhfileno(reference) : -1)) +{} + +LLTempRedirect::LLTempRedirect(int target, int reference): + // capture a duplicate file descriptor for the file originally targeted by + // 'reference' + mOrigTarget((reference >= 0)? fhdup(reference) : -1), + mReference(reference) +{ + if (target >= 0 && reference >= 0) + { + // As promised, force 'reference' to refer to 'target'. This first + // implicitly closes 'reference', which is why we first capture a + // duplicate so the original target file stays open. + fhdup2(target, reference); + } +} + +LLTempRedirect::LLTempRedirect(LLTempRedirect&& other) +{ + mOrigTarget = other.mOrigTarget; + mReference = other.mReference; + // other LLTempRedirect must be in moved-from state so its destructor + // won't repeat the same operations as ours! + other.mOrigTarget = -1; + other.mReference = -1; +} + +LLTempRedirect::~LLTempRedirect() +{ + reset(); +} + +void LLTempRedirect::reset() +{ + // If this instance was default-constructed (or constructed with an + // invalid file descriptor), skip the following. + if (mOrigTarget >= 0) + { + // Restore mReference to point to mOrigTarget. This implicitly closes + // the duplicate created by our constructor of its 'target' file + // descriptor. + fhdup2(mOrigTarget, mReference); + // mOrigTarget has served its purpose + fhclose(mOrigTarget); + } + // assign these because reset() is also responsible for a "moved from" + // instance + mOrigTarget = -1; + mReference = -1; +} + +LLTempRedirect& LLTempRedirect::operator=(LLTempRedirect&& other) +{ + reset(); + std::swap(mOrigTarget, other.mOrigTarget); + std::swap(mReference, other.mReference); + return *this; +} diff --git a/indra/llcommon/lltempredirect.h b/indra/llcommon/lltempredirect.h new file mode 100644 index 0000000000..33e05dc06b --- /dev/null +++ b/indra/llcommon/lltempredirect.h @@ -0,0 +1,91 @@ +/** + * @file lltempredirect.h + * @author Nat Goodspeed + * @date 2019-10-31 + * @brief RAII low-level file-descriptor redirection + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLTEMPREDIRECT_H) +#define LL_LLTEMPREDIRECT_H + +// Functions in this namespace are intended to insulate the caller from the +// aggravating distinction between ::close() and Microsoft _close(). +namespace llfd +{ + +int close(int fd); +int dup(int target); +int dup2(int target, int reference); +FILE* open(int fd, const char* mode); +int fileno(FILE* stream); + +} // namespace llfd + +/** + * LLTempRedirect is an RAII class that performs file redirection on low-level + * file descriptors, expressed as ints. (Use llfd::fileno() to obtain the file + * descriptor from a classic-C FILE*. There is no portable way to obtain the + * file descriptor from a std::fstream.) + * + * Instantiate LLTempRedirect with a target file descriptor (e.g. for some + * open file) and a reference file descriptor (e.g. for stderr). From that + * point until the LLTempRedirect instance is destroyed, all OS-level writes + * to the reference file descriptor will be redirected to the target file. + * + * Because dup2() is used for redirection, the original passed target file + * descriptor remains open. If you want LLTempRedirect's destructor to close + * the target file, close() the target file descriptor after passing it to + * LLTempRedirect's constructor. + * + * LLTempRedirect's constructor saves the original target of the reference + * file descriptor. Its destructor restores the reference file descriptor to + * point once again to its original target. + */ +class LLTempRedirect +{ +public: + LLTempRedirect(); + /** + * For the lifespan of this LLTempRedirect instance, all writes to + * 'reference' will be redirected to 'target'. When this LLTempRedirect is + * destroyed, the original target for 'reference' will be restored. + * + * Pass 'target' as NULL if you simply want to save and restore + * 'reference' against possible redirection in the meantime. + */ + LLTempRedirect(FILE* target, FILE* reference); + /** + * For the lifespan of this LLTempRedirect instance, all writes to + * 'reference' will be redirected to 'target'. When this LLTempRedirect is + * destroyed, the original target for 'reference' will be restored. + * + * Pass 'target' as -1 if you simply want to save and restore + * 'reference' against possible redirection in the meantime. + */ + LLTempRedirect(int target, int reference); + LLTempRedirect(const LLTempRedirect&) = delete; + LLTempRedirect(LLTempRedirect&& other); + + ~LLTempRedirect(); + + LLTempRedirect& operator=(const LLTempRedirect&) = delete; + LLTempRedirect& operator=(LLTempRedirect&& other); + + /// returns (duplicate file descriptor for) the original target of the + /// 'reference' file descriptor passed to our constructor + int getOriginalTarget() const { return mOrigTarget; } + /// returns the original 'reference' file descriptor passed to our + /// constructor + int getReference() const { return mReference; } + +private: + void reset(); + + int mOrigTarget, mReference; +}; + +#endif /* ! defined(LL_LLTEMPREDIRECT_H) */ diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index a4171729db..0b9dec969c 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -92,26 +92,39 @@ void set_thread_name( DWORD dwThreadID, const char* threadName) // } // //---------------------------------------------------------------------------- +namespace +{ -U32 LL_THREAD_LOCAL sThreadID = 0; + LLThread::id_t main_thread() + { + // Using a function-static variable to identify the main thread + // requires that control reach here from the main thread before it + // reaches here from any other thread. We simply trust that whichever + // thread gets here first is the main thread. + static LLThread::id_t s_thread_id = LLThread::currentID(); + return s_thread_id; + } -U32 LLThread::sIDIter = 0; +} // anonymous namespace +LL_COMMON_API bool on_main_thread() +{ + return (LLThread::currentID() == main_thread()); +} LL_COMMON_API void assert_main_thread() { - static U32 s_thread_id = LLThread::currentID(); - if (LLThread::currentID() != s_thread_id) + auto curr = LLThread::currentID(); + auto main = main_thread(); + if (curr != main) { - LL_WARNS() << "Illegal execution from thread id " << (S32) LLThread::currentID() - << " outside main thread " << (S32) s_thread_id << LL_ENDL; + LL_WARNS() << "Illegal execution from thread id " << curr + << " outside main thread " << main << LL_ENDL; } } -void LLThread::registerThreadID() -{ - sThreadID = ++sIDIter; -} +// this function has become moot +void LLThread::registerThreadID() {} // // Handed to the APR thread creation function @@ -122,11 +135,12 @@ void LLThread::threadRun() set_thread_name(-1, mName.c_str()); #endif + // this is the first point at which we're actually running in the new thread + mID = currentID(); + // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder()); - sThreadID = mID; - // Run the user supplied function do { @@ -168,8 +182,6 @@ LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : mStatus(STOPPED), mRecorder(NULL) { - - mID = ++sIDIter; mRunCondition = new LLCondition(); mDataLock = new LLMutex(); mLocalAPRFilePoolp = NULL ; @@ -347,9 +359,9 @@ void LLThread::setQuitting() } // static -U32 LLThread::currentID() +LLThread::id_t LLThread::currentID() { - return sThreadID; + return std::this_thread::get_id(); } // static @@ -376,6 +388,16 @@ void LLThread::wakeLocked() } } +void LLThread::lockData() +{ + mDataLock->lock(); +} + +void LLThread::unlockData() +{ + mDataLock->unlock(); +} + //============================================================================ //---------------------------------------------------------------------------- diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 863c9051f3..5cd0731f6c 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -30,12 +30,9 @@ #include "llapp.h" #include "llapr.h" #include "boost/intrusive_ptr.hpp" -#include "llmutex.h" #include "llrefcount.h" #include -LL_COMMON_API void assert_main_thread(); - namespace LLTrace { class ThreadRecorder; @@ -45,7 +42,6 @@ class LL_COMMON_API LLThread { private: friend class LLMutex; - static U32 sIDIter; public: typedef enum e_thread_status @@ -55,6 +51,7 @@ public: QUITTING= 2, // Someone wants this thread to quit CRASHED = -1 // An uncaught exception was thrown by the thread } EThreadStatus; + typedef std::thread::id id_t; LLThread(const std::string& name, apr_pool_t *poolp = NULL); virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state. @@ -64,7 +61,7 @@ public: bool isStopped() const { return (STOPPED == mStatus) || (CRASHED == mStatus); } bool isCrashed() const { return (CRASHED == mStatus); } - static U32 currentID(); // Return ID of current thread + static id_t currentID(); // Return ID of current thread static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. public: @@ -88,7 +85,7 @@ public: LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; } - U32 getID() const { return mID; } + id_t getID() const { return mID; } // Called by threads *not* created via LLThread to register some // internal state used by LLMutex. You must call this once early @@ -109,7 +106,7 @@ protected: std::thread *mThreadp; EThreadStatus mStatus; - U32 mID; + id_t mID; LLTrace::ThreadRecorder* mRecorder; //a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used. @@ -126,8 +123,8 @@ protected: virtual bool runCondition(void); // Lock/Unlock Run Condition -- use around modification of any variable used in runCondition() - inline void lockData(); - inline void unlockData(); + void lockData(); + void unlockData(); // This is the predicate that decides whether the thread should sleep. // It should only be called with mDataLock locked, since the virtual runCondition() function may need to access @@ -142,17 +139,6 @@ protected: }; -void LLThread::lockData() -{ - mDataLock->lock(); -} - -void LLThread::unlockData() -{ - mDataLock->unlock(); -} - - //============================================================================ // Simple responder for self destructing callbacks @@ -168,5 +154,6 @@ public: //============================================================================ extern LL_COMMON_API void assert_main_thread(); +extern LL_COMMON_API bool on_main_thread(); #endif // LL_LLTHREAD_H diff --git a/indra/llcommon/llthreadlocalstorage.cpp b/indra/llcommon/llthreadlocalstorage.cpp index 8cef05caac..d8a063e8d5 100644 --- a/indra/llcommon/llthreadlocalstorage.cpp +++ b/indra/llcommon/llthreadlocalstorage.cpp @@ -93,11 +93,9 @@ void LLThreadLocalPointerBase::initAllThreadLocalStorage() { if (!sInitialized) { - for (LLInstanceTracker::instance_iter it = beginInstances(), end_it = endInstances(); - it != end_it; - ++it) + for (auto& base : instance_snapshot()) { - (*it).initStorage(); + base.initStorage(); } sInitialized = true; } @@ -108,11 +106,9 @@ void LLThreadLocalPointerBase::destroyAllThreadLocalStorage() { if (sInitialized) { - //for (LLInstanceTracker::instance_iter it = beginInstances(), end_it = endInstances(); - // it != end_it; - // ++it) + //for (auto& base : instance_snapshot()) //{ - // (*it).destroyStorage(); + // base.destroyStorage(); //} sInitialized = false; } diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp index 37f6bbe97e..bde36999ba 100644 --- a/indra/llcommon/llthreadsafequeue.cpp +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -23,7 +23,7 @@ * $/LicenseInfo$ */ -//#include "linden_common.h" -//#include "llthreadsafequeue.h" +#include "linden_common.h" +#include "llthreadsafequeue.h" diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index b0bddac8e5..30dd507f73 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -30,18 +30,12 @@ #include "llexception.h" #include #include - -#if LL_WINDOWS -#pragma warning (push) -#pragma warning (disable:4265) -#endif -// 'std::_Pad' : class has virtual functions, but destructor is not virtual -#include -#include - -#if LL_WINDOWS -#pragma warning (pop) -#endif +#include +#include "mutex.h" +#include "llcoros.h" +#include LLCOROS_MUTEX_HEADER +#include +#include LLCOROS_CONDVAR_HEADER // // A general queue exception. @@ -88,18 +82,28 @@ public: // Add an element to the front of queue (will block if the queue has // reached capacity). // - // This call will raise an interrupt error if the queue is deleted while + // This call will raise an interrupt error if the queue is closed while // the caller is blocked. void pushFront(ElementT const & element); - // Try to add an element to the front ofqueue without blocking. Returns + // Try to add an element to the front of queue without blocking. Returns // true only if the element was actually added. bool tryPushFront(ElementT const & element); - + + // Try to add an element to the front of queue, blocking if full but with + // timeout. Returns true if the element was added. + // There are potentially two different timeouts involved: how long to try + // to lock the mutex, versus how long to wait for the queue to stop being + // full. Careful settings for each timeout might be orders of magnitude + // apart. However, this method conflates them. + template + bool tryPushFrontFor(const std::chrono::duration& timeout, + ElementT const & element); + // Pop the element at the end of the queue (will block if the queue is // empty). // - // This call will raise an interrupt error if the queue is deleted while + // This call will raise an interrupt error if the queue is closed while // the caller is blocked. ElementT popBack(void); @@ -110,13 +114,29 @@ public: // Returns the size of the queue. size_t size(); + // closes the queue: + // - every subsequent pushFront() call will throw LLThreadSafeQueueInterrupt + // - every subsequent tryPushFront() call will return false + // - popBack() calls will return normally until the queue is drained, then + // every subsequent popBack() will throw LLThreadSafeQueueInterrupt + // - tryPopBack() calls will return normally until the queue is drained, + // then every subsequent tryPopBack() call will return false + void close(); + + // detect closed state + bool isClosed(); + // inverse of isClosed() + explicit operator bool(); + private: std::deque< ElementT > mStorage; U32 mCapacity; + bool mClosed; - std::mutex mLock; - std::condition_variable mCapacityCond; - std::condition_variable mEmptyCond; + boost::fibers::timed_mutex mLock; + typedef std::unique_lock lock_t; + boost::fibers::condition_variable_any mCapacityCond; + boost::fibers::condition_variable_any mEmptyCond; }; // LLThreadSafeQueue @@ -124,7 +144,8 @@ private: template LLThreadSafeQueue::LLThreadSafeQueue(U32 capacity) : -mCapacity(capacity) + mCapacity(capacity), + mClosed(false) { } @@ -132,13 +153,18 @@ mCapacity(capacity) template void LLThreadSafeQueue::pushFront(ElementT const & element) { + lock_t lock1(mLock); while (true) { - std::unique_lock lock1(mLock); + if (mClosed) + { + LLTHROW(LLThreadSafeQueueInterrupt()); + } if (mStorage.size() < mCapacity) { mStorage.push_front(element); + lock1.unlock(); mEmptyCond.notify_one(); return; } @@ -149,17 +175,61 @@ void LLThreadSafeQueue::pushFront(ElementT const & element) } +template +template +bool LLThreadSafeQueue::tryPushFrontFor(const std::chrono::duration& timeout, + ElementT const & element) +{ + // Convert duration to time_point: passing the same timeout duration to + // each of multiple calls is wrong. + auto endpoint = std::chrono::steady_clock::now() + timeout; + + lock_t lock1(mLock, std::defer_lock); + if (!lock1.try_lock_until(endpoint)) + return false; + + while (true) + { + if (mClosed) + { + return false; + } + + if (mStorage.size() < mCapacity) + { + mStorage.push_front(element); + lock1.unlock(); + mEmptyCond.notify_one(); + return true; + } + + // Storage Full. Wait for signal. + if (LLCoros::cv_status::timeout == mCapacityCond.wait_until(lock1, endpoint)) + { + // timed out -- formally we might recheck both conditions above + return false; + } + // If we didn't time out, we were notified for some reason. Loop back + // to check. + } +} + + template bool LLThreadSafeQueue::tryPushFront(ElementT const & element) { - std::unique_lock lock1(mLock, std::defer_lock); + lock_t lock1(mLock, std::defer_lock); if (!lock1.try_lock()) return false; + if (mClosed) + return false; + if (mStorage.size() >= mCapacity) return false; mStorage.push_front(element); + lock1.unlock(); mEmptyCond.notify_one(); return true; } @@ -168,18 +238,23 @@ bool LLThreadSafeQueue::tryPushFront(ElementT const & element) template ElementT LLThreadSafeQueue::popBack(void) { + lock_t lock1(mLock); while (true) { - std::unique_lock lock1(mLock); - if (!mStorage.empty()) { ElementT value = mStorage.back(); mStorage.pop_back(); + lock1.unlock(); mCapacityCond.notify_one(); return value; } + if (mClosed) + { + LLTHROW(LLThreadSafeQueueInterrupt()); + } + // Storage empty. Wait for signal. mEmptyCond.wait(lock1); } @@ -189,15 +264,18 @@ ElementT LLThreadSafeQueue::popBack(void) template bool LLThreadSafeQueue::tryPopBack(ElementT & element) { - std::unique_lock lock1(mLock, std::defer_lock); + lock_t lock1(mLock, std::defer_lock); if (!lock1.try_lock()) return false; + // no need to check mClosed: tryPopBack() behavior when the queue is + // closed is implemented by simple inability to push any new elements if (mStorage.empty()) return false; element = mStorage.back(); mStorage.pop_back(); + lock1.unlock(); mCapacityCond.notify_one(); return true; } @@ -206,8 +284,34 @@ bool LLThreadSafeQueue::tryPopBack(ElementT & element) template size_t LLThreadSafeQueue::size(void) { - std::lock_guard lock(mLock); + lock_t lock(mLock); return mStorage.size(); } +template +void LLThreadSafeQueue::close() +{ + lock_t lock(mLock); + mClosed = true; + lock.unlock(); + // wake up any blocked popBack() calls + mEmptyCond.notify_all(); + // wake up any blocked pushFront() calls + mCapacityCond.notify_all(); +} + +template +bool LLThreadSafeQueue::isClosed() +{ + lock_t lock(mLock); + return mClosed; +} + +template +LLThreadSafeQueue::operator bool() +{ + lock_t lock(mLock); + return ! mClosed; +} + #endif diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h index ba233d5727..9220a41bcd 100644 --- a/indra/llcommon/lltrace.h +++ b/indra/llcommon/lltrace.h @@ -57,7 +57,7 @@ class StatBase { public: StatBase(const char* name, const char* description); - virtual ~StatBase() LLINSTANCETRACKER_DTOR_NOEXCEPT {} + virtual ~StatBase() {} virtual const char* getUnitLabel() const; const std::string& getName() const { return mName; } diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp index 385d31edd7..b1c23c6fb7 100644 --- a/indra/llcommon/lltraceaccumulators.cpp +++ b/indra/llcommon/lltraceaccumulators.cpp @@ -291,8 +291,8 @@ void EventAccumulator::reset( const EventAccumulator* other ) { mNumSamples = 0; mSum = 0; - mMin = NaN; - mMax = NaN; + mMin = F32(NaN); + mMax = F32(NaN); mMean = NaN; mSumOfSquares = 0; mLastValue = other ? other->mLastValue : NaN; diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h index 6f27b97dff..8eb5338a2a 100644 --- a/indra/llcommon/lltraceaccumulators.h +++ b/indra/llcommon/lltraceaccumulators.h @@ -242,8 +242,8 @@ namespace LLTrace EventAccumulator() : mSum(0), - mMin(NaN), - mMax(NaN), + mMin(F32(NaN)), + mMax(F32(NaN)), mMean(NaN), mSumOfSquares(0), mNumSamples(0), @@ -313,8 +313,8 @@ namespace LLTrace SampleAccumulator() : mSum(0), - mMin(NaN), - mMax(NaN), + mMin(F32(NaN)), + mMax(F32(NaN)), mMean(NaN), mSumOfSquares(0), mLastSampleTimeStamp(0), diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp index 181fc2f058..025dc57044 100644 --- a/indra/llcommon/lltracethreadrecorder.cpp +++ b/indra/llcommon/lltracethreadrecorder.cpp @@ -28,6 +28,7 @@ #include "lltracethreadrecorder.h" #include "llfasttimer.h" #include "lltrace.h" +#include "llstl.h" namespace LLTrace { @@ -64,16 +65,15 @@ void ThreadRecorder::init() activate(&mThreadRecordingBuffers); // initialize time block parent pointers - for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(), end_it = BlockTimerStatHandle::instance_tracker_t::endInstances(); - it != end_it; - ++it) + for (auto& base : BlockTimerStatHandle::instance_snapshot()) { - BlockTimerStatHandle& time_block = static_cast(*it); - TimeBlockTreeNode& tree_node = mTimeBlockTreeNodes[it->getIndex()]; + // because of indirect derivation from LLInstanceTracker, have to downcast + BlockTimerStatHandle& time_block = static_cast(base); + TimeBlockTreeNode& tree_node = mTimeBlockTreeNodes[time_block.getIndex()]; tree_node.mBlock = &time_block; tree_node.mParent = &root_time_block; - it->getCurrentAccumulator().mParent = &root_time_block; + time_block.getCurrentAccumulator().mParent = &root_time_block; } mRootTimer = new BlockTimer(root_time_block); diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index 7c6da1cc0b..bcff0a8501 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -43,6 +43,7 @@ #include "llstring.h" #include "lltimer.h" #include "llthread.h" +#include "llmutex.h" const LLUUID LLUUID::null; const LLTransactionID LLTransactionID::tnull; @@ -739,7 +740,7 @@ void LLUUID::getCurrentTime(uuid_time_t *timestamp) getSystemTime(&time_last); uuids_this_tick = uuids_per_tick; init = TRUE; - mMutex = new LLMutex(); + mMutex = new LLMutex(); } uuid_time_t time_now = {0,0}; diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h index b1a6f61360..0387e75c65 100644 --- a/indra/llcommon/llworkerthread.h +++ b/indra/llcommon/llworkerthread.h @@ -34,6 +34,7 @@ #include "llqueuedthread.h" #include "llatomic.h" +#include "llmutex.h" #define USE_FRAME_CALLBACK_MANAGER 0 diff --git a/indra/llcommon/lockstatic.h b/indra/llcommon/lockstatic.h new file mode 100644 index 0000000000..96c53c6473 --- /dev/null +++ b/indra/llcommon/lockstatic.h @@ -0,0 +1,73 @@ +/** + * @file lockstatic.h + * @author Nat Goodspeed + * @date 2019-12-03 + * @brief LockStatic class provides mutex-guarded access to the specified + * static data. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LOCKSTATIC_H) +#define LL_LOCKSTATIC_H + +#include "mutex.h" // std::unique_lock + +namespace llthread +{ + +// Instantiate this template to obtain a pointer to the canonical static +// instance of Static while holding a lock on that instance. Use of +// Static::mMutex presumes that Static declares some suitable mMutex. +template +class LockStatic +{ + typedef std::unique_lock lock_t; +public: + LockStatic(): + mData(getStatic()), + mLock(mData->mMutex) + {} + Static* get() const { return mData; } + operator Static*() const { return get(); } + Static* operator->() const { return get(); } + // sometimes we must explicitly unlock... + void unlock() + { + // but once we do, access is no longer permitted + mData = nullptr; + mLock.unlock(); + } +protected: + Static* mData; + lock_t mLock; +private: + Static* getStatic() + { + // Static::mMutex must be function-local static rather than class- + // static. Some of our consumers must function properly (therefore + // lock properly) even when the containing module's static variables + // have not yet been runtime-initialized. A mutex requires + // construction. A static class member might not yet have been + // constructed. + // + // We could store a dumb mutex_t*, notice when it's NULL and allocate a + // heap mutex -- but that's vulnerable to race conditions. And we can't + // defend the dumb pointer with another mutex. + // + // We could store a std::atomic -- but a default-constructed + // std::atomic does not contain a valid T, even a default-constructed + // T! Which means std::atomic, too, requires runtime initialization. + // + // But a function-local static is guaranteed to be initialized exactly + // once: the first time control reaches that declaration. + static Static sData; + return &sData; + } +}; + +} // llthread namespace + +#endif /* ! defined(LL_LOCKSTATIC_H) */ diff --git a/indra/llcommon/mutex.h b/indra/llcommon/mutex.h new file mode 100644 index 0000000000..90d0942270 --- /dev/null +++ b/indra/llcommon/mutex.h @@ -0,0 +1,22 @@ +/** + * @file mutex.h + * @author Nat Goodspeed + * @date 2019-12-03 + * @brief Wrap in odious boilerplate + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if LL_WINDOWS +#pragma warning (push) +#pragma warning (disable:4265) +#endif +// warning C4265: 'std::_Pad' : class has virtual functions, but destructor is not virtual + +#include + +#if LL_WINDOWS +#pragma warning (pop) +#endif diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp new file mode 100644 index 0000000000..478149eacf --- /dev/null +++ b/indra/llcommon/tests/llcond_test.cpp @@ -0,0 +1,67 @@ +/** + * @file llcond_test.cpp + * @author Nat Goodspeed + * @date 2019-07-18 + * @brief Test for llcond. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llcond.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "llcoros.h" + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llcond_data + { + LLScalarCond cond{0}; + }; + typedef test_group llcond_group; + typedef llcond_group::object object; + llcond_group llcondgrp("llcond"); + + template<> template<> + void object::test<1>() + { + set_test_name("Immediate gratification"); + cond.set_one(1); + ensure("wait_for_equal() failed", + cond.wait_for_equal(F32Milliseconds(1), 1)); + ensure("wait_for_unequal() should have failed", + ! cond.wait_for_unequal(F32Milliseconds(1), 1)); + } + + template<> template<> + void object::test<2>() + { + set_test_name("Simple two-coroutine test"); + LLCoros::instance().launch( + "test<2>", + [this]() + { + // Lambda immediately entered -- control comes here first. + ensure_equals(cond.get(), 0); + cond.set_all(1); + cond.wait_equal(2); + ensure_equals(cond.get(), 2); + cond.set_all(3); + }); + // Main coroutine is resumed only when the lambda waits. + ensure_equals(cond.get(), 1); + cond.set_all(2); + cond.wait_equal(3); + } +} // namespace tut diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index fa02d2bb1a..ccc4d82b34 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -26,101 +26,32 @@ * $/LicenseInfo$ */ -/*****************************************************************************/ -// test<1>() is cloned from a Boost.Coroutine example program whose copyright -// info is reproduced here: -/*---------------------------------------------------------------------------*/ -// Copyright (c) 2006, Giovanni P. Deretta -// -// This code may be used under either of the following two licences: -// -// 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. OF SUCH DAMAGE. -// -// Or: -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -/*****************************************************************************/ - #define BOOST_RESULT_OF_USE_TR1 1 -// On some platforms, Boost.Coroutine must #define magic symbols before -// #including platform-API headers. Naturally, that's ineffective unless the -// Boost.Coroutine #include is the *first* #include of the platform header. -// That means that client code must generally #include Boost.Coroutine headers -// before anything else. -#include #include #include #include #include +#include #include "linden_common.h" #include #include +#include #include "../test/lltut.h" +#include "../test/lltestapp.h" #include "llsd.h" #include "llsdutil.h" #include "llevents.h" -#include "tests/wrapllerrs.h" -#include "stringize.h" #include "llcoros.h" +#include "lleventfilter.h" #include "lleventcoro.h" #include "../test/debug.h" +#include "../test/sync.h" using namespace llcoro; -/***************************************************************************** -* from the banana.cpp example program borrowed for test<1>() -*****************************************************************************/ -namespace coroutines = boost::dcoroutines; -using coroutines::coroutine; - -template -bool match(Iter first, Iter last, std::string match) { - std::string::iterator i = match.begin(); - for(; (first != last) && (i != match.end()); ++i) { - if (*first != *i) - return false; - ++first; - } - return i == match.end(); -} - -template -BidirectionalIterator -match_substring(BidirectionalIterator begin, - BidirectionalIterator end, - std::string xmatch, - BOOST_DEDUCED_TYPENAME coroutine::self& self) { -//BidirectionalIterator begin_ = begin; - for(; begin != end; ++begin) - if(match(begin, end, xmatch)) { - self.yield(begin); - } - return end; -} - -typedef coroutine match_coroutine_type; - /***************************************************************************** * Test helpers *****************************************************************************/ @@ -131,8 +62,9 @@ typedef coroutine match_coroutine_type; class ImmediateAPI { public: - ImmediateAPI(): - mPump("immediate", true) + ImmediateAPI(Sync& sync): + mPump("immediate", true), + mSync(sync) { mPump.listen("API", boost::bind(&ImmediateAPI::operator(), this, _1)); } @@ -141,20 +73,18 @@ public: // Invoke this with an LLSD map containing: // ["value"]: Integer value. We will reply with ["value"] + 1. - // ["reply"]: Name of LLEventPump on which to send success response. - // ["error"]: Name of LLEventPump on which to send error response. - // ["fail"]: Presence of this key selects ["error"], else ["success"] as - // the name of the pump on which to send the response. + // ["reply"]: Name of LLEventPump on which to send response. bool operator()(const LLSD& event) const { + mSync.bump(); LLSD::Integer value(event["value"]); - LLSD::String replyPumpName(event.has("fail")? "error" : "reply"); - LLEventPumps::instance().obtain(event[replyPumpName]).post(value + 1); + LLEventPumps::instance().obtain(event["reply"]).post(value + 1); return false; } private: LLEventStream mPump; + Sync& mSync; }; /***************************************************************************** @@ -162,103 +92,82 @@ private: *****************************************************************************/ namespace tut { - struct coroutine_data {}; - typedef test_group coroutine_group; + struct test_data + { + Sync mSync; + ImmediateAPI immediateAPI{mSync}; + std::string replyName, errorName, threw, stringdata; + LLSD result, errordata; + int which; + LLTestApp testApp; + + void explicit_wait(boost::shared_ptr>& cbp); + void waitForEventOn1(); + void coroPump(); + void postAndWait1(); + void coroPumpPost(); + }; + typedef test_group coroutine_group; typedef coroutine_group::object object; coroutine_group coroutinegrp("coroutine"); - template<> template<> - void object::test<1>() - { - set_test_name("From banana.cpp example program in Boost.Coroutine distro"); - std::string buffer = "banananana"; - std::string match = "nana"; - std::string::iterator begin = buffer.begin(); - std::string::iterator end = buffer.end(); - -#if defined(BOOST_CORO_POSIX_IMPL) -// std::cout << "Using Boost.Coroutine " << BOOST_CORO_POSIX_IMPL << '\n'; -#else -// std::cout << "Using non-Posix Boost.Coroutine implementation" << std::endl; -#endif - - typedef std::string::iterator signature(std::string::iterator, - std::string::iterator, - std::string, - match_coroutine_type::self&); - - coroutine matcher - (boost::bind(static_cast(match_substring), - begin, - end, - match, - _1)); - - std::string::iterator i = matcher(); -/*==========================================================================*| - while(matcher && i != buffer.end()) { - std::cout <<"Match at: "<< std::distance(buffer.begin(), i)<<'\n'; - i = matcher(); - } -|*==========================================================================*/ - size_t matches[] = { 2, 4, 6 }; - for (size_t *mi(boost::begin(matches)), *mend(boost::end(matches)); - mi != mend; ++mi, i = matcher()) - { - ensure("more", matcher); - ensure("found", i != buffer.end()); - ensure_equals("value", std::distance(buffer.begin(), i), *mi); - } - ensure("done", ! matcher); - } - - // use static data so we can intersperse coroutine functions with the - // tests that engage them - ImmediateAPI immediateAPI; - std::string replyName, errorName, threw, stringdata; - LLSD result, errordata; - int which; - - // reinit vars at the start of each test - void clear() - { - replyName.clear(); - errorName.clear(); - threw.clear(); - stringdata.clear(); - result = LLSD(); - errordata = LLSD(); - which = 0; - } - - void explicit_wait(boost::shared_ptr::callback_t>& cbp) + void test_data::explicit_wait(boost::shared_ptr>& cbp) { BEGIN { + mSync.bump(); // The point of this test is to verify / illustrate suspending a // coroutine for something other than an LLEventPump. In other // words, this shows how to adapt to any async operation that // provides a callback-style notification (and prove that it // works). - LLCoros::Future future; - // get the callback from that future - LLCoros::Future::callback_t callback(future.make_callback()); - // Perhaps we would send a request to a remote server and arrange - // for 'callback' to be called on response. Of course that might - // involve an adapter object from the actual callback signature to - // the signature of 'callback' -- in this case, void(std::string). - // For test purposes, instead of handing 'callback' (or the + // for cbp->set_value() to be called on response. + // For test purposes, instead of handing 'callback' (or an // adapter) off to some I/O subsystem, we'll just pass it back to // our caller. - cbp.reset(new LLCoros::Future::callback_t(callback)); + cbp = boost::make_shared>(); + LLCoros::Future future = LLCoros::getFuture(*cbp); - ensure("Not yet", ! future); // calling get() on the future causes us to suspend debug("about to suspend"); stringdata = future.get(); - ensure("Got it", bool(future)); + mSync.bump(); + ensure_equals("Got it", stringdata, "received"); + } + END + } + + template<> template<> + void object::test<1>() + { + set_test_name("explicit_wait"); + DEBUG; + + // Construct the coroutine instance that will run explicit_wait. + boost::shared_ptr> respond; + LLCoros::instance().launch("test<1>", + [this, &respond](){ explicit_wait(respond); }); + mSync.bump(); + // When the coroutine waits for the future, it returns here. + debug("about to respond"); + // Now we're the I/O subsystem delivering a result. This should make + // the coroutine ready. + respond->set_value("received"); + // but give it a chance to wake up + mSync.yield(); + // ensure the coroutine ran and woke up again with the intended result + ensure_equals(stringdata, "received"); + } + + void test_data::waitForEventOn1() + { + BEGIN + { + mSync.bump(); + result = suspendUntilEventOn("source"); + mSync.bump(); } END } @@ -266,28 +175,27 @@ namespace tut template<> template<> void object::test<2>() { - clear(); - set_test_name("explicit_wait"); + set_test_name("waitForEventOn1"); DEBUG; - - // Construct the coroutine instance that will run explicit_wait. - boost::shared_ptr::callback_t> respond; - LLCoros::instance().launch("test<2>", - boost::bind(explicit_wait, boost::ref(respond))); - // When the coroutine waits for the future, it returns here. - debug("about to respond"); - // Now we're the I/O subsystem delivering a result. This immediately - // transfers control back to the coroutine. - (*respond)("received"); - // ensure the coroutine ran and woke up again with the intended result - ensure_equals(stringdata, "received"); + LLCoros::instance().launch("test<2>", [this](){ waitForEventOn1(); }); + mSync.bump(); + debug("about to send"); + LLEventPumps::instance().obtain("source").post("received"); + // give waitForEventOn1() a chance to run + mSync.yield(); + debug("back from send"); + ensure_equals(result.asString(), "received"); } - void waitForEventOn1() + void test_data::coroPump() { BEGIN { - result = suspendUntilEventOn("source"); + mSync.bump(); + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.suspend(); + mSync.bump(); } END } @@ -295,24 +203,27 @@ namespace tut template<> template<> void object::test<3>() { - clear(); - set_test_name("waitForEventOn1"); + set_test_name("coroPump"); DEBUG; - LLCoros::instance().launch("test<3>", waitForEventOn1); + LLCoros::instance().launch("test<3>", [this](){ coroPump(); }); + mSync.bump(); debug("about to send"); - LLEventPumps::instance().obtain("source").post("received"); + LLEventPumps::instance().obtain(replyName).post("received"); + // give coroPump() a chance to run + mSync.yield(); debug("back from send"); ensure_equals(result.asString(), "received"); } - void waitForEventOn2() - { + void test_data::postAndWait1() BEGIN { - LLEventWithID pair = suspendUntilEventOn("reply", "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); + mSync.bump(); + result = postAndSuspend(LLSDMap("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + mSync.bump(); } END } @@ -320,475 +231,107 @@ namespace tut template<> template<> void object::test<4>() { - clear(); - set_test_name("waitForEventOn2 reply"); - { + set_test_name("postAndWait1"); DEBUG; - LLCoros::instance().launch("test<4>", waitForEventOn2); - debug("about to send"); - LLEventPumps::instance().obtain("reply").post("received"); - debug("back from send"); + LLCoros::instance().launch("test<4>", [this](){ postAndWait1(); }); + ensure_equals(result.asInteger(), 18); + } + + void test_data::coroPumpPost() + { + BEGIN + { + mSync.bump(); + LLCoroEventPump waiter; + result = waiter.postAndSuspend(LLSDMap("value", 17), + immediateAPI.getPump(), "reply"); + mSync.bump(); } - ensure_equals(result.asString(), "received"); - ensure_equals("which pump", which, 0); + END } template<> template<> void object::test<5>() { - clear(); - set_test_name("waitForEventOn2 error"); + set_test_name("coroPumpPost"); DEBUG; - LLCoros::instance().launch("test<5>", waitForEventOn2); - debug("about to send"); - LLEventPumps::instance().obtain("error").post("badness"); - debug("back from send"); - ensure_equals(result.asString(), "badness"); - ensure_equals("which pump", which, 1); + LLCoros::instance().launch("test<5>", [this](){ coroPumpPost(); }); + ensure_equals(result.asInteger(), 18); } - void coroPump() + template + void test() { - BEGIN + PUMP pump(typeid(PUMP).name()); + bool running{false}; + LLSD data{LLSD::emptyArray()}; + // start things off by posting once before even starting the listener + // coro + LL_DEBUGS() << "test() posting first" << LL_ENDL; + LLSD first{LLSDMap("desc", "first")("value", 0)}; + bool consumed = pump.post(first); + ensure("should not have consumed first", ! consumed); + // now launch the coro + LL_DEBUGS() << "test() launching listener coro" << LL_ENDL; + running = true; + LLCoros::instance().launch( + "listener", + [&pump, &running, &data](){ + // important for this test that we consume posted values + LLCoros::instance().set_consuming(true); + // should immediately retrieve 'first' without waiting + LL_DEBUGS() << "listener coro waiting for first" << LL_ENDL; + data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD())); + // Don't use ensure() from within the coro -- ensure() failure + // throws tut::fail, which won't propagate out to the main + // test driver, which will result in an odd failure. + // Wait for 'second' because it's not already pending. + LL_DEBUGS() << "listener coro waiting for second" << LL_ENDL; + data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD())); + // and wait for 'third', which should involve no further waiting + LL_DEBUGS() << "listener coro waiting for third" << LL_ENDL; + data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD())); + LL_DEBUGS() << "listener coro done" << LL_ENDL; + running = false; + }); + // back from coro at the point where it's waiting for 'second' + LL_DEBUGS() << "test() posting second" << LL_ENDL; + LLSD second{llsd::map("desc", "second", "value", 1)}; + consumed = pump.post(second); + ensure("should have consumed second", consumed); + // This is a key point: even though we've post()ed the value for which + // the coroutine is waiting, it's actually still suspended until we + // pause for some other reason. The coroutine will only pick up one + // value at a time from our 'pump'. It's important to exercise the + // case when we post() two values before it picks up either. + LL_DEBUGS() << "test() posting third" << LL_ENDL; + LLSD third{llsd::map("desc", "third", "value", 2)}; + consumed = pump.post(third); + ensure("should NOT yet have consumed third", ! consumed); + // now just wait for coro to finish -- which it eventually will, given + // that all its suspend calls have short timeouts. + while (running) { - LLCoroEventPump waiter; - replyName = waiter.getName(); - result = waiter.suspend(); + LL_DEBUGS() << "test() waiting for coro done" << LL_ENDL; + llcoro::suspendUntilTimeout(0.1); } - END + // okay, verify expected results + ensure_equals("should have received three values", data, + llsd::array(first, second, third)); + LL_DEBUGS() << "test() done" << LL_ENDL; } template<> template<> void object::test<6>() { - clear(); - set_test_name("coroPump"); - DEBUG; - LLCoros::instance().launch("test<6>", coroPump); - debug("about to send"); - LLEventPumps::instance().obtain(replyName).post("received"); - debug("back from send"); - ensure_equals(result.asString(), "received"); - } - - void coroPumps() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - LLEventWithID pair(waiter.suspend()); - result = pair.first; - which = pair.second; - } - END + set_test_name("LLEventMailDrop"); + tut::test(); } template<> template<> void object::test<7>() { - clear(); - set_test_name("coroPumps reply"); - DEBUG; - LLCoros::instance().launch("test<7>", coroPumps); - debug("about to send"); - LLEventPumps::instance().obtain(replyName).post("received"); - debug("back from send"); - ensure_equals(result.asString(), "received"); - ensure_equals("which pump", which, 0); - } - - template<> template<> - void object::test<8>() - { - clear(); - set_test_name("coroPumps error"); - DEBUG; - LLCoros::instance().launch("test<8>", coroPumps); - debug("about to send"); - LLEventPumps::instance().obtain(errorName).post("badness"); - debug("back from send"); - ensure_equals(result.asString(), "badness"); - ensure_equals("which pump", which, 1); - } - - void coroPumpsNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.suspendWithException(); - } - END - } - - template<> template<> - void object::test<9>() - { - clear(); - set_test_name("coroPumpsNoEx"); - DEBUG; - LLCoros::instance().launch("test<9>", coroPumpsNoEx); - debug("about to send"); - LLEventPumps::instance().obtain(replyName).post("received"); - debug("back from send"); - ensure_equals(result.asString(), "received"); - } - - void coroPumpsEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - try - { - result = waiter.suspendWithException(); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - template<> template<> - void object::test<10>() - { - clear(); - set_test_name("coroPumpsEx"); - DEBUG; - LLCoros::instance().launch("test<10>", coroPumpsEx); - debug("about to send"); - LLEventPumps::instance().obtain(errorName).post("badness"); - debug("back from send"); - ensure("no result", result.isUndefined()); - ensure_equals("got error", errordata.asString(), "badness"); - } - - void coroPumpsNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.suspendWithLog(); - } - END - } - - template<> template<> - void object::test<11>() - { - clear(); - set_test_name("coroPumpsNoLog"); - DEBUG; - LLCoros::instance().launch("test<11>", coroPumpsNoLog); - debug("about to send"); - LLEventPumps::instance().obtain(replyName).post("received"); - debug("back from send"); - ensure_equals(result.asString(), "received"); - } - - void coroPumpsLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - WrapLLErrs capture; - threw = capture.catch_llerrs([&waiter, &debug](){ - result = waiter.suspendWithLog(); - debug("no exception"); - }); - } - END - } - - template<> template<> - void object::test<12>() - { - clear(); - set_test_name("coroPumpsLog"); - DEBUG; - LLCoros::instance().launch("test<12>", coroPumpsLog); - debug("about to send"); - LLEventPumps::instance().obtain(errorName).post("badness"); - debug("back from send"); - ensure("no result", result.isUndefined()); - ensure_contains("got error", threw, "badness"); - } - - void postAndWait1() - { - BEGIN - { - result = postAndSuspend(LLSDMap("value", 17), // request event - immediateAPI.getPump(), // requestPump - "reply1", // replyPump - "reply"); // request["reply"] = name - } - END - } - - template<> template<> - void object::test<13>() - { - clear(); - set_test_name("postAndWait1"); - DEBUG; - LLCoros::instance().launch("test<13>", postAndWait1); - ensure_equals(result.asInteger(), 18); - } - - void postAndWait2() - { - BEGIN - { - LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - template<> template<> - void object::test<14>() - { - clear(); - set_test_name("postAndWait2"); - DEBUG; - LLCoros::instance().launch("test<14>", postAndWait2); - ensure_equals(result.asInteger(), 19); - ensure_equals(which, 0); - } - - void postAndWait2_1() - { - BEGIN - { - LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18)("fail", LLSD()), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - template<> template<> - void object::test<15>() - { - clear(); - set_test_name("postAndWait2_1"); - DEBUG; - LLCoros::instance().launch("test<15>", postAndWait2_1); - ensure_equals(result.asInteger(), 19); - ensure_equals(which, 1); - } - - void coroPumpPost() - { - BEGIN - { - LLCoroEventPump waiter; - result = waiter.postAndSuspend(LLSDMap("value", 17), - immediateAPI.getPump(), "reply"); - } - END - } - - template<> template<> - void object::test<16>() - { - clear(); - set_test_name("coroPumpPost"); - DEBUG; - LLCoros::instance().launch("test<16>", coroPumpPost); - ensure_equals(result.asInteger(), 18); - } - - void coroPumpsPost() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndSuspend(LLSDMap("value", 23), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - template<> template<> - void object::test<17>() - { - clear(); - set_test_name("coroPumpsPost reply"); - DEBUG; - LLCoros::instance().launch("test<17>", coroPumpsPost); - ensure_equals(result.asInteger(), 24); - ensure_equals("which pump", which, 0); - } - - void coroPumpsPost_1() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair( - waiter.postAndSuspend(LLSDMap("value", 23)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - template<> template<> - void object::test<18>() - { - clear(); - set_test_name("coroPumpsPost error"); - DEBUG; - LLCoros::instance().launch("test<18>", coroPumpsPost_1); - ensure_equals(result.asInteger(), 24); - ensure_equals("which pump", which, 1); - } - - void coroPumpsPostNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndSuspendWithException(LLSDMap("value", 8), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - template<> template<> - void object::test<19>() - { - clear(); - set_test_name("coroPumpsPostNoEx"); - DEBUG; - LLCoros::instance().launch("test<19>", coroPumpsPostNoEx); - ensure_equals(result.asInteger(), 9); - } - - void coroPumpsPostEx() - { - BEGIN - { - LLCoroEventPumps waiter; - try - { - result = waiter.postAndSuspendWithException( - LLSDMap("value", 9)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - template<> template<> - void object::test<20>() - { - clear(); - set_test_name("coroPumpsPostEx"); - DEBUG; - LLCoros::instance().launch("test<20>", coroPumpsPostEx); - ensure("no result", result.isUndefined()); - ensure_equals("got error", errordata.asInteger(), 10); - } - - void coroPumpsPostNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndSuspendWithLog(LLSDMap("value", 30), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - template<> template<> - void object::test<21>() - { - clear(); - set_test_name("coroPumpsPostNoLog"); - DEBUG; - LLCoros::instance().launch("test<21>", coroPumpsPostNoLog); - ensure_equals(result.asInteger(), 31); - } - - void coroPumpsPostLog() - { - BEGIN - { - LLCoroEventPumps waiter; - WrapLLErrs capture; - threw = capture.catch_llerrs( - [&waiter, &debug](){ - result = waiter.postAndSuspendWithLog( - LLSDMap("value", 31)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - }); - } - END - } - - template<> template<> - void object::test<22>() - { - clear(); - set_test_name("coroPumpsPostLog"); - DEBUG; - LLCoros::instance().launch("test<22>", coroPumpsPostLog); - ensure("no result", result.isUndefined()); - ensure_contains("got error", threw, "32"); + set_test_name("LLEventLogProxyFor"); + tut::test< LLEventLogProxyFor >(); } } - -/*==========================================================================*| -#include - -namespace tut -{ - template<> template<> - void object::test<23>() - { - set_test_name("stacksize"); - std::cout << "default_stacksize: " << boost::context::guarded_stack_allocator::default_stacksize() << '\n'; - } -} // namespace tut -|*==========================================================================*/ diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index a181d5c941..9da1ecfd67 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -23,6 +23,7 @@ #include "stringize.h" #include "tests/wrapllerrs.h" #include "../test/catch_and_store_what_in.h" +#include "../test/debug.h" #include #include @@ -45,15 +46,6 @@ using boost::lambda::var; using namespace llsd; -/***************************************************************************** -* Output control -*****************************************************************************/ -#ifdef DEBUG_ON -using std::cout; -#else -static std::ostringstream cout; -#endif - /***************************************************************************** * Example data, functions, classes *****************************************************************************/ @@ -155,13 +147,13 @@ struct Vars /*------------- no-args (non-const, const, static) methods -------------*/ void method0() { - cout << "method0()\n"; + debug()("method0()"); i = 17; } void cmethod0() const { - cout << 'c'; + debug()('c', NONL); const_cast(this)->method0(); } @@ -170,13 +162,13 @@ struct Vars /*------------ Callable (non-const, const, static) methods -------------*/ void method1(const LLSD& obj) { - cout << "method1(" << obj << ")\n"; + debug()("method1(", obj, ")"); llsd = obj; } void cmethod1(const LLSD& obj) const { - cout << 'c'; + debug()('c', NONL); const_cast(this)->method1(obj); } @@ -196,12 +188,12 @@ struct Vars else vcp = std::string("'") + cp + "'"; - cout << "methodna(" << b - << ", " << i - << ", " << f - << ", " << d - << ", " << vcp - << ")\n"; + debug()("methodna(", b, + ", ", i, + ", ", f, + ", ", d, + ", ", vcp, + ")"); this->b = b; this->i = i; @@ -218,12 +210,12 @@ struct Vars vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte); } - cout << "methodnb(" << "'" << s << "'" - << ", " << uuid - << ", " << date - << ", '" << uri << "'" - << ", " << vbin.str() - << ")\n"; + debug()("methodnb(", "'", s, "'", + ", ", uuid, + ", ", date, + ", '", uri, "'", + ", ", vbin.str(), + ")"); this->s = s; this->uuid = uuid; @@ -234,18 +226,30 @@ struct Vars void cmethodna(NPARAMSa) const { - cout << 'c'; + debug()('c', NONL); const_cast(this)->methodna(NARGSa); } void cmethodnb(NPARAMSb) const { - cout << 'c'; + debug()('c', NONL); const_cast(this)->methodnb(NARGSb); } static void smethodna(NPARAMSa); static void smethodnb(NPARAMSb); + + static Debug& debug() + { + // Lazily initialize this Debug instance so it can notice if main() + // has forcibly set LOGTEST. If it were simply a static member, it + // would already have examined the environment variable by the time + // main() gets around to checking command-line switches. Since we have + // a global static Vars instance, the same would be true of a plain + // non-static member. + static Debug sDebug("Vars"); + return sDebug; + } }; /*------- Global Vars instance for free functions and static methods -------*/ static Vars g; @@ -253,25 +257,25 @@ static Vars g; /*------------ Static Vars method implementations reference 'g' ------------*/ void Vars::smethod0() { - cout << "smethod0() -> "; + debug()("smethod0() -> ", NONL); g.method0(); } void Vars::smethod1(const LLSD& obj) { - cout << "smethod1(" << obj << ") -> "; + debug()("smethod1(", obj, ") -> ", NONL); g.method1(obj); } void Vars::smethodna(NPARAMSa) { - cout << "smethodna(...) -> "; + debug()("smethodna(...) -> ", NONL); g.methodna(NARGSa); } void Vars::smethodnb(NPARAMSb) { - cout << "smethodnb(...) -> "; + debug()("smethodnb(...) -> ", NONL); g.methodnb(NARGSb); } @@ -284,25 +288,25 @@ void clear() /*------------------- Free functions also reference 'g' --------------------*/ void free0() { - cout << "free0() -> "; + g.debug()("free0() -> ", NONL); g.method0(); } void free1(const LLSD& obj) { - cout << "free1(" << obj << ") -> "; + g.debug()("free1(", obj, ") -> ", NONL); g.method1(obj); } void freena(NPARAMSa) { - cout << "freena(...) -> "; + g.debug()("freena(...) -> ", NONL); g.methodna(NARGSa); } void freenb(NPARAMSb) { - cout << "freenb(...) -> "; + g.debug()("freenb(...) -> ", NONL); g.methodnb(NARGSb); } @@ -313,6 +317,7 @@ namespace tut { struct lleventdispatcher_data { + Debug debug{"test"}; WrapLLErrs redirect; Dispatcher work; Vars v; @@ -431,12 +436,17 @@ namespace tut // Same for freenb() et al. params = LLSDMap("a", LLSDArray("b")("i")("f")("d")("cp")) ("b", LLSDArray("s")("uuid")("date")("uri")("bin")); - cout << "params:\n" << params << "\nparams[\"a\"]:\n" << params["a"] << "\nparams[\"b\"]:\n" << params["b"] << std::endl; + debug("params:\n", + params, "\n" + "params[\"a\"]:\n", + params["a"], "\n" + "params[\"b\"]:\n", + params["b"]); // default LLSD::Binary value std::vector binary; for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11) { - binary.push_back(h); + binary.push_back((U8)h); } // Full defaults arrays. We actually don't care what the LLUUID or // LLDate values are, as long as they're different from the @@ -448,7 +458,8 @@ namespace tut (LLDate::now()) (LLURI("http://www.ietf.org/rfc/rfc3986.txt")) (binary)); - cout << "dft_array_full:\n" << dft_array_full << std::endl; + debug("dft_array_full:\n", + dft_array_full); // Partial defaults arrays. foreach(LLSD::String a, ab) { @@ -457,7 +468,8 @@ namespace tut llsd_copy_array(dft_array_full[a].beginArray() + partition, dft_array_full[a].endArray()); } - cout << "dft_array_partial:\n" << dft_array_partial << std::endl; + debug("dft_array_partial:\n", + dft_array_partial); foreach(LLSD::String a, ab) { @@ -473,7 +485,10 @@ namespace tut dft_map_partial[a][params[a][ix].asString()] = dft_array_full[a][ix]; } } - cout << "dft_map_full:\n" << dft_map_full << "\ndft_map_partial:\n" << dft_map_partial << '\n'; + debug("dft_map_full:\n", + dft_map_full, "\n" + "dft_map_partial:\n", + dft_map_partial); // (Free function | static method) with (no | arbitrary) params, // map style, no (empty array) defaults @@ -918,7 +933,12 @@ namespace tut params[a].endArray()), dft_array_partial[a]); } - cout << "allreq:\n" << allreq << "\nleftreq:\n" << leftreq << "\nrightdft:\n" << rightdft << std::endl; + debug("allreq:\n", + allreq, "\n" + "leftreq:\n", + leftreq, "\n" + "rightdft:\n", + rightdft); // Generate maps containing parameter names not provided by the // dft_map_partial maps. @@ -930,7 +950,8 @@ namespace tut skipreq[a].erase(me.first); } } - cout << "skipreq:\n" << skipreq << std::endl; + debug("skipreq:\n", + skipreq); LLSD groups(LLSDArray // array of groups @@ -975,7 +996,11 @@ namespace tut LLSD names(grp[0]); LLSD required(grp[1][0]); LLSD optional(grp[1][1]); - cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl; + debug("For ", names, ",\n", + "required:\n", + required, "\n" + "optional:\n", + optional); // Loop through 'names' foreach(LLSD nm, inArray(names)) @@ -1145,7 +1170,7 @@ namespace tut std::vector binary; for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i) { - binary.push_back(h); + binary.push_back((U8)h); } LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*")) ("b", LLSDArray("string") @@ -1163,7 +1188,7 @@ namespace tut } // Adjust expect["a"]["cp"] for special Vars::cp treatment. expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'"; - cout << "expect: " << expect << '\n'; + debug("expect: ", expect); // Use substantially the same logic for args and argsplus LLSD argsarrays(LLSDArray(args)(argsplus)); @@ -1218,7 +1243,8 @@ namespace tut { array_overfull[a].append("bogus"); } - cout << "array_full: " << array_full << "\narray_overfull: " << array_overfull << std::endl; + debug("array_full: ", array_full, "\n" + "array_overfull: ", array_overfull); // We rather hope that LLDate::now() will generate a timestamp // distinct from the one it generated in the constructor, moments ago. ensure_not_equals("Timestamps too close", @@ -1233,7 +1259,8 @@ namespace tut map_overfull[a] = map_full[a]; map_overfull[a]["extra"] = "ignore"; } - cout << "map_full: " << map_full << "\nmap_overfull: " << map_overfull << std::endl; + debug("map_full: ", map_full, "\n" + "map_overfull: ", map_overfull); LLSD expect(map_full); // Twiddle the const char* param. expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'"; @@ -1248,7 +1275,7 @@ namespace tut // so won't bother returning it. Predict that behavior to match the // LLSD values. expect["a"].erase("b"); - cout << "expect: " << expect << std::endl; + debug("expect: ", expect); // For this test, calling functions registered with different sets of // parameter defaults should make NO DIFFERENCE WHATSOEVER. Every call // should pass all params. diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index 1875013794..fa2cb03e95 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -36,9 +36,12 @@ // other Linden headers #include "../test/lltut.h" #include "stringize.h" +#include "llsdutil.h" #include "listener.h" #include "tests/wrapllerrs.h" +#include + /***************************************************************************** * Test classes *****************************************************************************/ @@ -401,6 +404,78 @@ namespace tut throttle.post(";17"); ensure_equals("17", cat.result, "136;12;17"); // "17" delivered } + + template + void test() + { + PUMP pump(typeid(PUMP).name()); + LLSD data{LLSD::emptyArray()}; + bool consumed{true}; + // listener that appends to 'data' + // but that also returns the current value of 'consumed' + // Instantiate this separately because we're going to listen() + // multiple times with the same lambda: LLEventMailDrop only replays + // queued events on a new listen() call. + auto lambda = + [&data, &consumed](const LLSD& event)->bool + { + data.append(event); + return consumed; + }; + { + LLTempBoundListener conn = pump.listen("lambda", lambda); + pump.post("first"); + } + // first post() should certainly be received by listener + ensure_equals("first", data, llsd::array("first")); + // the question is, since consumed was true, did it queue the value? + data = LLSD::emptyArray(); + { + // if it queued the value, it would be delivered on subsequent + // listen() call + LLTempBoundListener conn = pump.listen("lambda", lambda); + } + ensure_equals("empty1", data, LLSD::emptyArray()); + data = LLSD::emptyArray(); + // now let's NOT consume the posted data + consumed = false; + { + LLTempBoundListener conn = pump.listen("lambda", lambda); + pump.post("second"); + pump.post("third"); + } + // the two events still arrive + ensure_equals("second,third1", data, llsd::array("second", "third")); + data = LLSD::emptyArray(); + { + // when we reconnect, these should be delivered again + // but this time they should be consumed + consumed = true; + LLTempBoundListener conn = pump.listen("lambda", lambda); + } + // unconsumed events were delivered again + ensure_equals("second,third2", data, llsd::array("second", "third")); + data = LLSD::emptyArray(); + { + // when we reconnect this time, no more unconsumed events + LLTempBoundListener conn = pump.listen("lambda", lambda); + } + ensure_equals("empty2", data, LLSD::emptyArray()); + } + + template<> template<> + void filter_object::test<6>() + { + set_test_name("LLEventMailDrop"); + tut::test(); + } + + template<> template<> + void filter_object::test<7>() + { + set_test_name("LLEventLogProxyFor"); + tut::test< LLEventLogProxyFor >(); + } } // namespace tut /***************************************************************************** diff --git a/indra/llcommon/tests/llexception_test.cpp b/indra/llcommon/tests/llexception_test.cpp index 6bee1943c2..8ddf636cd1 100644 --- a/indra/llcommon/tests/llexception_test.cpp +++ b/indra/llcommon/tests/llexception_test.cpp @@ -305,4 +305,19 @@ namespace tut std::cout << center("int", '=', margin) << std::endl; catch_several(throw_int, "throw_int"); } + + template<> template<> + void object::test<2>() + { + set_test_name("reporting exceptions"); + + try + { + LLTHROW(LLException("badness")); + } + catch (...) + { + LOG_UNHANDLED_EXCEPTION("llexception test<2>()"); + } + } } // namespace tut diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index d94fc0c56d..9b89159625 100644 --- a/indra/llcommon/tests/llinstancetracker_test.cpp +++ b/indra/llcommon/tests/llinstancetracker_test.cpp @@ -41,7 +41,6 @@ #include // other Linden headers #include "../test/lltut.h" -#include "wrapllerrs.h" struct Badness: public std::runtime_error { @@ -112,24 +111,22 @@ namespace tut void object::test<2>() { ensure_equals(Unkeyed::instanceCount(), 0); - Unkeyed* dangling = NULL; + std::weak_ptr dangling; { Unkeyed one; ensure_equals(Unkeyed::instanceCount(), 1); - Unkeyed* found = Unkeyed::getInstance(&one); - ensure_equals(found, &one); + std::weak_ptr found = one.getWeak(); + ensure(! found.expired()); { boost::scoped_ptr two(new Unkeyed); ensure_equals(Unkeyed::instanceCount(), 2); - Unkeyed* found = Unkeyed::getInstance(two.get()); - ensure_equals(found, two.get()); } ensure_equals(Unkeyed::instanceCount(), 1); - // store an unwise pointer to a temp Unkeyed instance - dangling = &one; + // store a weak pointer to a temp Unkeyed instance + dangling = found; } // make that instance vanish // check the now-invalid pointer to the destroyed instance - ensure("getInstance(T*) failed to track destruction", ! Unkeyed::getInstance(dangling)); + ensure("weak_ptr failed to track destruction", dangling.expired()); ensure_equals(Unkeyed::instanceCount(), 0); } @@ -142,7 +139,8 @@ namespace tut // reimplement LLInstanceTracker using, say, a hash map instead of a // std::map. We DO insist that every key appear exactly once. typedef std::vector StringVector; - StringVector keys(Keyed::beginKeys(), Keyed::endKeys()); + auto snap = Keyed::key_snapshot(); + StringVector keys(snap.begin(), snap.end()); std::sort(keys.begin(), keys.end()); StringVector::const_iterator ki(keys.begin()); ensure_equals(*ki++, "one"); @@ -153,17 +151,15 @@ namespace tut ensure("didn't reach end", ki == keys.end()); // Use a somewhat different approach to order independence with - // beginInstances(): explicitly capture the instances we know in a + // instance_snapshot(): explicitly capture the instances we know in a // set, and delete them as we iterate through. typedef std::set InstanceSet; InstanceSet instances; instances.insert(&one); instances.insert(&two); instances.insert(&three); - for (Keyed::instance_iter ii(Keyed::beginInstances()), iend(Keyed::endInstances()); - ii != iend; ++ii) + for (auto& ref : Keyed::instance_snapshot()) { - Keyed& ref = *ii; ensure_equals("spurious instance", instances.erase(&ref), 1); } ensure_equals("unreported instance", instances.size(), 0); @@ -180,11 +176,10 @@ namespace tut instances.insert(&two); instances.insert(&three); - for (Unkeyed::instance_iter ii(Unkeyed::beginInstances()), iend(Unkeyed::endInstances()); ii != iend; ++ii) - { - Unkeyed& ref = *ii; - ensure_equals("spurious instance", instances.erase(&ref), 1); - } + for (auto& ref : Unkeyed::instance_snapshot()) + { + ensure_equals("spurious instance", instances.erase(&ref), 1); + } ensure_equals("unreported instance", instances.size(), 0); } @@ -192,49 +187,49 @@ namespace tut template<> template<> void object::test<5>() { - set_test_name("delete Keyed with outstanding instance_iter"); - std::string what; - Keyed* keyed = new Keyed("delete Keyed with outstanding instance_iter"); - { - WrapLLErrs wrapper; - Keyed::instance_iter i(Keyed::beginInstances()); - what = wrapper.catch_llerrs([&keyed](){ - delete keyed; - }); - } - ensure(! what.empty()); + std::string desc("delete Keyed with outstanding instance_snapshot"); + set_test_name(desc); + Keyed* keyed = new Keyed(desc); + // capture a snapshot but do not yet traverse it + auto snapshot = Keyed::instance_snapshot(); + // delete the one instance + delete keyed; + // traversing the snapshot should reflect the deletion + // avoid ensure_equals() because it requires the ability to stream the + // two values to std::ostream + ensure(snapshot.begin() == snapshot.end()); } template<> template<> void object::test<6>() { - set_test_name("delete Keyed with outstanding key_iter"); - std::string what; - Keyed* keyed = new Keyed("delete Keyed with outstanding key_it"); - { - WrapLLErrs wrapper; - Keyed::key_iter i(Keyed::beginKeys()); - what = wrapper.catch_llerrs([&keyed](){ - delete keyed; - }); - } - ensure(! what.empty()); + std::string desc("delete Keyed with outstanding key_snapshot"); + set_test_name(desc); + Keyed* keyed = new Keyed(desc); + // capture a snapshot but do not yet traverse it + auto snapshot = Keyed::key_snapshot(); + // delete the one instance + delete keyed; + // traversing the snapshot should reflect the deletion + // avoid ensure_equals() because it requires the ability to stream the + // two values to std::ostream + ensure(snapshot.begin() == snapshot.end()); } template<> template<> void object::test<7>() { - set_test_name("delete Unkeyed with outstanding instance_iter"); + set_test_name("delete Unkeyed with outstanding instance_snapshot"); std::string what; Unkeyed* unkeyed = new Unkeyed; - { - WrapLLErrs wrapper; - Unkeyed::instance_iter i(Unkeyed::beginInstances()); - what = wrapper.catch_llerrs([&unkeyed](){ - delete unkeyed; - }); - } - ensure(! what.empty()); + // capture a snapshot but do not yet traverse it + auto snapshot = Unkeyed::instance_snapshot(); + // delete the one instance + delete unkeyed; + // traversing the snapshot should reflect the deletion + // avoid ensure_equals() because it requires the ability to stream the + // two values to std::ostream + ensure(snapshot.begin() == snapshot.end()); } template<> template<> @@ -246,11 +241,9 @@ namespace tut // We can't use the iterator-range InstanceSet constructor because // beginInstances() returns an iterator that dereferences to an // Unkeyed&, not an Unkeyed*. - for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()), - ukend(Unkeyed::endInstances()); - uki != ukend; ++uki) + for (auto& ref : Unkeyed::instance_snapshot()) { - existing.insert(&*uki); + existing.insert(&ref); } try { @@ -273,11 +266,9 @@ namespace tut // instances was also present in the original set. If that's not true, // it's because our new Unkeyed ended up in the updated set despite // its constructor exception. - for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()), - ukend(Unkeyed::endInstances()); - uki != ukend; ++uki) + for (auto& ref : Unkeyed::instance_snapshot()) { - ensure("failed to remove instance", existing.find(&*uki) != existing.end()); + ensure("failed to remove instance", existing.find(&ref) != existing.end()); } } } // namespace tut diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index bf0a74d10d..9d71e327d8 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -49,24 +49,28 @@ const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte #endif -void waitfor(const std::vector& instances, int timeout=60) +// capture std::weak_ptrs to LLLeap instances so we can tell when they expire +typedef std::vector> LLLeapVector; + +void waitfor(const LLLeapVector& instances, int timeout=60) { int i; for (i = 0; i < timeout; ++i) { // Every iteration, test whether any of the passed LLLeap instances // still exist (are still running). - std::vector::const_iterator vli(instances.begin()), vlend(instances.end()); - for ( ; vli != vlend; ++vli) + bool found = false; + for (auto& ptr : instances) { - // getInstance() returns NULL if it's terminated/gone, non-NULL if - // it's still running - if (LLLeap::getInstance(*vli)) + if (! ptr.expired()) + { + found = true; break; + } } // If we made it through all of 'instances' without finding one that's // still running, we're done. - if (vli == vlend) + if (! found) { /*==========================================================================*| std::cout << instances.size() << " LLLeap instances terminated in " @@ -86,8 +90,8 @@ void waitfor(const std::vector& instances, int timeout=60) void waitfor(LLLeap* instance, int timeout=60) { - std::vector instances; - instances.push_back(instance); + LLLeapVector instances; + instances.push_back(instance->getWeak()); waitfor(instances, timeout); } @@ -218,11 +222,11 @@ namespace tut NamedTempFile script("py", "import time\n" "time.sleep(1)\n"); - std::vector instances; + LLLeapVector instances; instances.push_back(LLLeap::create(get_test_name(), - sv(list_of(PYTHON)(script.getName())))); + sv(list_of(PYTHON)(script.getName())))->getWeak()); instances.push_back(LLLeap::create(get_test_name(), - sv(list_of(PYTHON)(script.getName())))); + sv(list_of(PYTHON)(script.getName())))->getWeak()); // In this case we're simply establishing that two LLLeap instances // can coexist without throwing exceptions or bombing in any other // way. Wait for them to terminate. diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp new file mode 100644 index 0000000000..69b11ccafb --- /dev/null +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -0,0 +1,137 @@ +/** + * @file llmainthreadtask_test.cpp + * @author Nat Goodspeed + * @date 2019-12-05 + * @brief Test for llmainthreadtask. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llmainthreadtask.h" +// STL headers +// std headers +#include +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../test/sync.h" +#include "llthread.h" // on_main_thread() +#include "lleventtimer.h" +#include "lockstatic.h" + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llmainthreadtask_data + { + // 5-second timeout + Sync mSync{F32Milliseconds(5000.0f)}; + + llmainthreadtask_data() + { + // we're not testing the result; this is just to cache the + // initial thread as the main thread. + on_main_thread(); + } + }; + typedef test_group llmainthreadtask_group; + typedef llmainthreadtask_group::object object; + llmainthreadtask_group llmainthreadtaskgrp("llmainthreadtask"); + + template<> template<> + void object::test<1>() + { + set_test_name("inline"); + bool ran = false; + bool result = LLMainThreadTask::dispatch( + [&ran]()->bool{ + ran = true; + return true; + }); + ensure("didn't run lambda", ran); + ensure("didn't return result", result); + } + + struct StaticData + { + std::mutex mMutex; // LockStatic looks for mMutex + bool ran{false}; + }; + typedef llthread::LockStatic LockStatic; + + template<> template<> + void object::test<2>() + { + set_test_name("cross-thread"); + skip("This test is prone to build-time hangs"); + std::atomic_bool result(false); + // wrapping our thread lambda in a packaged_task will catch any + // exceptions it might throw and deliver them via future + std::packaged_task thread_work( + [this, &result](){ + // unblock test<2>()'s yield_until(1) + mSync.set(1); + // dispatch work to main thread -- should block here + bool on_main( + LLMainThreadTask::dispatch( + []()->bool{ + // have to lock static mutex to set static data + LockStatic()->ran = true; + // indicate whether task was run on the main thread + return on_main_thread(); + })); + // wait for test<2>() to unblock us again + mSync.yield_until(3); + result = on_main; + }); + auto thread_result = thread_work.get_future(); + std::thread thread; + try + { + // run thread_work + thread = std::thread(std::move(thread_work)); + // wait for thread to set(1) + mSync.yield_until(1); + // try to acquire the lock, should block because thread has it + LockStatic lk; + // wake up when dispatch() unlocks the static mutex + ensure("shouldn't have run yet", !lk->ran); + ensure("shouldn't have returned yet", !result); + // unlock so the task can acquire the lock + lk.unlock(); + // run the task -- should unblock thread, which will immediately block + // on mSync + LLEventTimer::updateClass(); + // 'lk', having unlocked, can no longer be used to access; relock with + // a new LockStatic instance + ensure("should now have run", LockStatic()->ran); + ensure("returned too early", !result); + // okay, let thread perform the assignment + mSync.set(3); + } + catch (...) + { + // A test failure exception anywhere in the try block can cause + // the test program to terminate without explanation when + // ~thread() finds that 'thread' is still joinable. We could + // either join() or detach() it -- but since it might be blocked + // waiting for something from the main thread that now can never + // happen, it's safer to detach it. + thread.detach(); + throw; + } + // 'thread' should be all done now + thread.join(); + // deliver any exception thrown by thread_work + thread_result.get(); + ensure("ran changed", LockStatic()->ran); + ensure("didn't run on main thread", result); + } +} // namespace tut diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 222d832084..f0eafa8201 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -493,14 +493,18 @@ namespace tut } // std::cout << "child done: rv = " << rv << " (" << manager.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n'; aprchk_("apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT)", wi.rv, APR_CHILD_DONE); - ensure_equals_(wi.why, APR_PROC_EXIT); - ensure_equals_(wi.rc, 0); // Beyond merely executing all the above successfully, verify that we // obtained expected output -- and that we duly got control while // waiting, proving the non-blocking nature of these pipes. try { + // Perform these ensure_equals_() within this try/catch so that if + // we don't get expected results, we'll dump whatever we did get + // to help diagnose. + ensure_equals_(wi.why, APR_PROC_EXIT); + ensure_equals_(wi.rc, 0); + unsigned i = 0; ensure("blocking I/O on child pipe (0)", history[i].tries); ensure_equals_(history[i].which, "out"); diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 6ac974e659..642c1c3879 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -271,10 +271,10 @@ namespace tut LLSD w; mParser->reset(); // reset() call is needed since test code re-uses mParser mParser->parse(stream, w, stream.str().size()); - + try { - ensure_equals(msg.c_str(), w, v); + ensure_equals(msg, w, v); } catch (...) { @@ -432,6 +432,7 @@ namespace tut const char source[] = "it must be a blue moon again"; std::vector data; + // note, includes terminating '\0' copy(&source[0], &source[sizeof(source)], back_inserter(data)); v = data; @@ -468,28 +469,36 @@ namespace tut checkRoundTrip(msg + " many nested maps", v); } - typedef tut::test_group TestLLSDSerialzeGroup; - typedef TestLLSDSerialzeGroup::object TestLLSDSerializeObject; - TestLLSDSerialzeGroup gTestLLSDSerializeGroup("llsd serialization"); + typedef tut::test_group TestLLSDSerializeGroup; + typedef TestLLSDSerializeGroup::object TestLLSDSerializeObject; + TestLLSDSerializeGroup gTestLLSDSerializeGroup("llsd serialization"); template<> template<> void TestLLSDSerializeObject::test<1>() { - mFormatter = new LLSDNotationFormatter(); + mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY); mParser = new LLSDNotationParser(); - doRoundTripTests("notation serialization"); + doRoundTripTests("pretty binary notation serialization"); } - + template<> template<> void TestLLSDSerializeObject::test<2>() + { + mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE); + mParser = new LLSDNotationParser(); + doRoundTripTests("raw binary notation serialization"); + } + + template<> template<> + void TestLLSDSerializeObject::test<3>() { mFormatter = new LLSDXMLFormatter(); mParser = new LLSDXMLParser(); doRoundTripTests("xml serialization"); } - + template<> template<> - void TestLLSDSerializeObject::test<3>() + void TestLLSDSerializeObject::test<4>() { mFormatter = new LLSDBinaryFormatter(); mParser = new LLSDBinaryParser(); diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index 75ddff9d7d..15ffe68e67 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -143,8 +143,6 @@ namespace tut \ (void)CLS::instance(); \ ensure_equals(sLog, #CLS "i" #CLS); \ - LLSingletonBase::cleanupAll(); \ - ensure_equals(sLog, #CLS "i" #CLS "x" #CLS); \ LLSingletonBase::deleteAll(); \ ensure_equals(sLog, #CLS "i" #CLS "x" #CLS "~" #CLS); \ } \ @@ -159,10 +157,8 @@ namespace tut \ (void)CLS::instance(); \ ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS); \ - LLSingletonBase::cleanupAll(); \ - ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "x" #OTHER); \ LLSingletonBase::deleteAll(); \ - ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \ + ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \ } \ \ template<> template<> \ @@ -175,10 +171,8 @@ namespace tut \ (void)CLS::instance(); \ ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \ - LLSingletonBase::cleanupAll(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER); \ LLSingletonBase::deleteAll(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \ + ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \ } \ \ template<> template<> \ @@ -191,10 +185,8 @@ namespace tut \ (void)CLS::instance(); \ ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \ - LLSingletonBase::cleanupAll(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER); \ LLSingletonBase::deleteAll(); \ - ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \ + ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \ } TESTS(A, B, 4, 5, 6, 7) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a8062d0413..1debab9e6e 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -105,12 +105,13 @@ target_link_libraries( ) # tests -if (LL_TESTS) +set(LLCOREHTTP_TESTS ON CACHE BOOL + "Build and run llcorehttp integration tests specifically") +if (LL_TESTS AND LLCOREHTTP_TESTS) SET(llcorehttp_TEST_SOURCE_FILES - tests/test_allocator.cpp ) - set(llcorehttp_TEST_HEADER_FILS + set(llcorehttp_TEST_HEADER_FILES tests/test_httpstatus.hpp tests/test_refcounted.hpp tests/test_httpoperation.hpp @@ -153,7 +154,7 @@ if (LL_TESTS) # ${PYTHON_EXECUTABLE} # "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" # ) - + if (DARWIN) # Path inside the app bundle where we'll need to copy libraries set(LL_TEST_DESTINATION_DIR @@ -202,6 +203,7 @@ endif (DARWIN) ) set(example_libs + ${LEGACY_STDIO_LIBS} ${LLCOREHTTP_LIBRARIES} ${WINDOWS_LIBRARIES} ${LLMESSAGE_LIBRARIES} @@ -235,5 +237,4 @@ endif (DARWIN) target_link_libraries(http_texture_load ${example_libs}) -endif (LL_TESTS) - +endif (LL_TESTS AND LLCOREHTTP_TESTS) diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h index 0e39e22dde..928ee10a83 100644 --- a/indra/llcorehttp/_httpreplyqueue.h +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -30,6 +30,7 @@ #include "_refcounted.h" #include "_mutex.h" +#include "boost/noncopyable.hpp" namespace LLCore diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index b91aaf0593..c7376042b3 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -52,7 +52,7 @@ void init_curl(); void term_curl(); -unsigned long ssl_thread_id_callback(void); +void ssl_thread_id_callback(CRYPTO_THREADID*); void ssl_locking_callback(int mode, int type, const char * file, int line); void usage(std::ostream & out); @@ -624,7 +624,7 @@ void init_curl() } CRYPTO_set_locking_callback(ssl_locking_callback); - CRYPTO_set_id_callback(ssl_thread_id_callback); + CRYPTO_THREADID_set_callback(ssl_thread_id_callback); } } @@ -640,12 +640,12 @@ void term_curl() } -unsigned long ssl_thread_id_callback(void) +void ssl_thread_id_callback(CRYPTO_THREADID* pthreadid) { #if defined(WIN32) - return (unsigned long) GetCurrentThread(); + CRYPTO_THREADID_set_pointer(pthreadid, GetCurrentThread()); #else - return (unsigned long) pthread_self(); + CRYPTO_THREADID_set_pointer(pthreadid, pthread_self()); #endif } diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 7c93c54cdf..e37a38b05f 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -40,6 +40,7 @@ #include #if SAFE_SSL #include +#include // std::hash #endif @@ -369,7 +370,8 @@ void ssl_locking_callback(int mode, int type, const char *file, int line) //static unsigned long ssl_thread_id(void) { - return LLThread::currentID(); + // std::thread::id is very deliberately opaque, but we can hash it + return std::hash()(LLThread::currentID()); } #endif diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index e4bd4957f8..18505e0aad 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -193,6 +193,7 @@ #include "boost/shared_ptr.hpp" #include "boost/weak_ptr.hpp" #include "boost/function.hpp" +#include "boost/noncopyable.hpp" #include #include diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index a310fc0508..362b2309ee 100755 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -41,14 +41,19 @@ #include "test_httpstatus.hpp" #include "test_refcounted.hpp" #include "test_httpoperation.hpp" +// As of 2019-06-28, test_httprequest.hpp consistently crashes on Mac Release +// builds for reasons not yet diagnosed. +#if ! (LL_DARWIN && LL_RELEASE) #include "test_httprequest.hpp" +#endif #include "test_httpheaders.hpp" #include "test_httprequestqueue.hpp" +#include "_httpservice.h" #include "llproxy.h" #include "llcleanup.h" -unsigned long ssl_thread_id_callback(void); +void ssl_thread_id_callback(CRYPTO_THREADID*); void ssl_locking_callback(int mode, int type, const char * file, int line); #if 0 // lltut provides main and runner @@ -93,7 +98,7 @@ void init_curl() } CRYPTO_set_locking_callback(ssl_locking_callback); - CRYPTO_set_id_callback(ssl_thread_id_callback); + CRYPTO_THREADID_set_callback(ssl_thread_id_callback); } LLProxy::getInstance(); @@ -113,12 +118,12 @@ void term_curl() } -unsigned long ssl_thread_id_callback(void) +void ssl_thread_id_callback(CRYPTO_THREADID* pthreadid) { #if defined(WIN32) - return (unsigned long) GetCurrentThread(); + CRYPTO_THREADID_set_pointer(pthreadid, GetCurrentThread()); #else - return (unsigned long) pthread_self(); + CRYPTO_THREADID_set_pointer(pthreadid, pthread_self()); #endif } @@ -172,5 +177,3 @@ void stop_thread(LLCore::HttpRequest * req) } } } - - diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp index ea12dc58eb..597e0d2fc9 100644 --- a/indra/llcorehttp/tests/test_allocator.cpp +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -43,16 +43,6 @@ #include - -#if defined(WIN32) -#define THROW_BAD_ALLOC() _THROW1(std::bad_alloc) -#define THROW_NOTHING() _THROW0() -#else -#define THROW_BAD_ALLOC() throw(std::bad_alloc) -#define THROW_NOTHING() throw() -#endif - - struct BlockHeader { struct Block * next; @@ -152,19 +142,19 @@ std::size_t GetMemTotal() } -void * operator new(std::size_t size) THROW_BAD_ALLOC() +void * operator new(std::size_t size) //throw(std::bad_alloc) { return GetMem( size ); } -void * operator new[](std::size_t size) THROW_BAD_ALLOC() +void * operator new[](std::size_t size) //throw(std::bad_alloc) { return GetMem( size ); } -void operator delete(void * p) THROW_NOTHING() +void operator delete(void * p) throw() { if (p) { @@ -173,7 +163,7 @@ void operator delete(void * p) THROW_NOTHING() } -void operator delete[](void * p) THROW_NOTHING() +void operator delete[](void * p) throw() { if (p) { diff --git a/indra/llcorehttp/tests/test_allocator.h b/indra/llcorehttp/tests/test_allocator.h index 3572bbc5c5..abd88f4c98 100644 --- a/indra/llcorehttp/tests/test_allocator.h +++ b/indra/llcorehttp/tests/test_allocator.h @@ -30,18 +30,13 @@ #include #include +#error 2019-06-27 Do not use test_allocator.h -- does not respect alignment. + size_t GetMemTotal(); -#if defined(WIN32) -void * operator new(std::size_t size) _THROW1(std::bad_alloc); -void * operator new[](std::size_t size) _THROW1(std::bad_alloc); -void operator delete(void * p) _THROW0(); -void operator delete[](void * p) _THROW0(); -#else -void * operator new(std::size_t size) throw (std::bad_alloc); -void * operator new[](std::size_t size) throw (std::bad_alloc); +void * operator new(std::size_t size); //throw (std::bad_alloc); +void * operator new[](std::size_t size); //throw (std::bad_alloc); void operator delete(void * p) throw (); void operator delete[](void * p) throw (); -#endif #endif // TEST_ALLOCATOR_H diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 8a2a64d970..cc4ad2a906 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -30,8 +30,6 @@ #include -#include "test_allocator.h" - using namespace LLCore; @@ -44,7 +42,6 @@ struct BufferArrayTestData { // the test objects inherit from this so the member functions and variables // can be referenced directly inside of the test functions. - size_t mMemTotal; }; typedef test_group BufferArrayTestGroupType; @@ -56,13 +53,9 @@ void BufferArrayTestObjectType::test<1>() { set_test_name("BufferArray construction"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); ensure("One ref on construction of BufferArray", ba->getRefCount() == 1); - ensure("Memory being used", mMemTotal < GetMemTotal()); ensure("Nothing in BA", 0 == ba->size()); // Try to read @@ -72,9 +65,6 @@ void BufferArrayTestObjectType::test<1>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -82,9 +72,6 @@ void BufferArrayTestObjectType::test<2>() { set_test_name("BufferArray single write"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); @@ -105,9 +92,6 @@ void BufferArrayTestObjectType::test<2>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } @@ -116,9 +100,6 @@ void BufferArrayTestObjectType::test<3>() { set_test_name("BufferArray multiple writes"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); @@ -154,9 +135,6 @@ void BufferArrayTestObjectType::test<3>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -164,9 +142,6 @@ void BufferArrayTestObjectType::test<4>() { set_test_name("BufferArray overwriting"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); @@ -208,9 +183,6 @@ void BufferArrayTestObjectType::test<4>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -218,9 +190,6 @@ void BufferArrayTestObjectType::test<5>() { set_test_name("BufferArray multiple writes - sequential reads"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); @@ -255,9 +224,6 @@ void BufferArrayTestObjectType::test<5>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -265,9 +231,6 @@ void BufferArrayTestObjectType::test<6>() { set_test_name("BufferArray overwrite spanning blocks and appending"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); @@ -306,9 +269,6 @@ void BufferArrayTestObjectType::test<6>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure("All memory released", mMemTotal == GetMemTotal()); } template <> template <> @@ -316,9 +276,6 @@ void BufferArrayTestObjectType::test<7>() { set_test_name("BufferArray overwrite spanning blocks and sequential writes"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); @@ -371,9 +328,6 @@ void BufferArrayTestObjectType::test<7>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure("All memory released", mMemTotal == GetMemTotal()); } template <> template <> @@ -381,9 +335,6 @@ void BufferArrayTestObjectType::test<8>() { set_test_name("BufferArray zero-length appendBufferAlloc"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArray * ba = new BufferArray(); @@ -421,9 +372,6 @@ void BufferArrayTestObjectType::test<8>() // release the implicit reference, causing the object to be released ba->release(); - - // make sure we didn't leak any memory - ensure("All memory released", mMemTotal == GetMemTotal()); } } // end namespace tut diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 831c901b9d..2739a6e38e 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -30,7 +30,6 @@ #include -#include "test_allocator.h" #include "llsd.h" #include "llsdserialize.h" @@ -45,7 +44,6 @@ struct BufferStreamTestData { // the test objects inherit from this so the member functions and variables // can be referenced directly inside of the test functions. - size_t mMemTotal; }; typedef test_group BufferStreamTestGroupType; @@ -59,12 +57,8 @@ void BufferStreamTestObjectType::test<1>() { set_test_name("BufferArrayStreamBuf construction with NULL BufferArray"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL); - ensure("Memory being used", mMemTotal < GetMemTotal()); // Not much will work with a NULL ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow()); @@ -78,9 +72,6 @@ void BufferStreamTestObjectType::test<1>() // release the implicit reference, causing the object to be released delete bsb; bsb = NULL; - - // make sure we didn't leak any memory - ensure("Allocated memory returned", mMemTotal == GetMemTotal()); } @@ -89,12 +80,8 @@ void BufferStreamTestObjectType::test<2>() { set_test_name("BufferArrayStream construction with NULL BufferArray"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference BufferArrayStream * bas = new BufferArrayStream(NULL); - ensure("Memory being used", mMemTotal < GetMemTotal()); // Not much will work with a NULL here ensure("eof() is false on NULL", ! bas->eof()); @@ -104,9 +91,6 @@ void BufferStreamTestObjectType::test<2>() // release the implicit reference, causing the object to be released delete bas; bas = NULL; - - // make sure we didn't leak any memory - ensure("Allocated memory returned", mMemTotal == GetMemTotal()); } @@ -115,13 +99,9 @@ void BufferStreamTestObjectType::test<3>() { set_test_name("BufferArrayStreamBuf construction with empty BufferArray"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted BufferArray with implicit reference BufferArray * ba = new BufferArray; BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); - ensure("Memory being used", mMemTotal < GetMemTotal()); // I can release my ref on the BA ba->release(); @@ -130,9 +110,6 @@ void BufferStreamTestObjectType::test<3>() // release the implicit reference, causing the object to be released delete bsb; bsb = NULL; - - // make sure we didn't leak any memory - ensure("Allocated memory returned", mMemTotal == GetMemTotal()); } @@ -141,24 +118,17 @@ void BufferStreamTestObjectType::test<4>() { set_test_name("BufferArrayStream construction with empty BufferArray"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted BufferArray with implicit reference BufferArray * ba = new BufferArray; { // create a new ref counted object with an implicit reference BufferArrayStream bas(ba); - ensure("Memory being used", mMemTotal < GetMemTotal()); } // release the implicit reference, causing the object to be released ba->release(); ba = NULL; - - // make sure we didn't leak any memory - ensure("Allocated memory returned", mMemTotal == GetMemTotal()); } @@ -167,9 +137,6 @@ void BufferStreamTestObjectType::test<5>() { set_test_name("BufferArrayStreamBuf construction with real BufferArray"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted BufferArray with implicit reference BufferArray * ba = new BufferArray; const char * content("This is a string. A fragment."); @@ -178,7 +145,6 @@ void BufferStreamTestObjectType::test<5>() // Creat an adapter for the BufferArray BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); - ensure("Memory being used", mMemTotal < GetMemTotal()); // I can release my ref on the BA ba->release(); @@ -206,9 +172,6 @@ void BufferStreamTestObjectType::test<5>() // release the implicit reference, causing the object to be released delete bsb; bsb = NULL; - - // make sure we didn't leak any memory - ensure("Allocated memory returned", mMemTotal == GetMemTotal()); } @@ -217,9 +180,6 @@ void BufferStreamTestObjectType::test<6>() { set_test_name("BufferArrayStream construction with real BufferArray"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted BufferArray with implicit reference BufferArray * ba = new BufferArray; //const char * content("This is a string. A fragment."); @@ -229,7 +189,6 @@ void BufferStreamTestObjectType::test<6>() { // Creat an adapter for the BufferArray BufferArrayStream bas(ba); - ensure("Memory being used", mMemTotal < GetMemTotal()); // Basic operations bas << "Hello" << 27 << "."; @@ -243,10 +202,6 @@ void BufferStreamTestObjectType::test<6>() // release the implicit reference, causing the object to be released ba->release(); ba = NULL; - - // make sure we didn't leak any memory - // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); - // static U64 mem = GetMemTotal(); } @@ -255,16 +210,12 @@ void BufferStreamTestObjectType::test<7>() { set_test_name("BufferArrayStream with LLSD serialization"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted BufferArray with implicit reference BufferArray * ba = new BufferArray; { // Creat an adapter for the BufferArray BufferArrayStream bas(ba); - ensure("Memory being used", mMemTotal < GetMemTotal()); // LLSD LLSD llsd = LLSD::emptyMap(); @@ -292,9 +243,6 @@ void BufferStreamTestObjectType::test<7>() // release the implicit reference, causing the object to be released ba->release(); ba = NULL; - - // make sure we didn't leak any memory - // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); } diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp index c05f1d9429..6aefb5054b 100644 --- a/indra/llcorehttp/tests/test_httpheaders.hpp +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -30,8 +30,6 @@ #include -#include "test_allocator.h" - using namespace LLCoreInt; @@ -43,7 +41,6 @@ struct HttpHeadersTestData { // the test objects inherit from this so the member functions and variables // can be referenced directly inside of the test functions. - size_t mMemTotal; }; typedef test_group HttpHeadersTestGroupType; @@ -55,19 +52,12 @@ void HttpHeadersTestObjectType::test<1>() { set_test_name("HttpHeaders construction"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); - ensure("Memory being used", mMemTotal < GetMemTotal()); ensure("Nothing in headers", 0 == headers->size()); // release the implicit reference, causing the object to be released headers.reset(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -75,9 +65,6 @@ void HttpHeadersTestObjectType::test<2>() { set_test_name("HttpHeaders construction"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); @@ -101,9 +88,6 @@ void HttpHeadersTestObjectType::test<2>() // release the implicit reference, causing the object to be released headers.reset(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -111,9 +95,6 @@ void HttpHeadersTestObjectType::test<3>() { set_test_name("HttpHeaders basic find"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); @@ -151,9 +132,6 @@ void HttpHeadersTestObjectType::test<3>() // release the implicit reference, causing the object to be released headers.reset(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -161,9 +139,6 @@ void HttpHeadersTestObjectType::test<4>() { set_test_name("HttpHeaders normalized header entry"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); @@ -251,9 +226,6 @@ void HttpHeadersTestObjectType::test<4>() // release the implicit reference, causing the object to be released headers.reset(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } // Verify forward iterator finds everything as expected @@ -262,9 +234,6 @@ void HttpHeadersTestObjectType::test<5>() { set_test_name("HttpHeaders iterator tests"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); @@ -337,9 +306,6 @@ void HttpHeadersTestObjectType::test<5>() // release the implicit reference, causing the object to be released headers.reset(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } // Reverse iterators find everything as expected @@ -348,9 +314,6 @@ void HttpHeadersTestObjectType::test<6>() { set_test_name("HttpHeaders reverse iterator tests"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpHeaders::ptr_t headers = HttpHeaders::ptr_t(new HttpHeaders()); @@ -421,9 +384,6 @@ void HttpHeadersTestObjectType::test<6>() // release the implicit reference, causing the object to be released headers.reset(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } } // end namespace tut diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index e7df2337de..c6407e8d04 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -31,8 +31,6 @@ #include -#include "test_allocator.h" - using namespace LLCoreInt; @@ -60,7 +58,6 @@ namespace tut { // the test objects inherit from this so the member functions and variables // can be referenced directly inside of the test functions. - size_t mMemTotal; }; typedef test_group HttpOperationTestGroupType; @@ -72,19 +69,12 @@ namespace tut { set_test_name("HttpOpNull construction"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpOperation::ptr_t op (new HttpOpNull()); ensure(op.use_count() == 1); - ensure(mMemTotal < GetMemTotal()); - - // release the implicit reference, causing the object to be released - op.reset(); - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); + // release the implicit reference, causing the object to be released + op.reset(); } template <> template <> @@ -92,9 +82,6 @@ namespace tut { set_test_name("HttpOpNull construction with handlers"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // Get some handlers LLCore::HttpHandler::ptr_t h1 (new TestHandler()); @@ -109,13 +96,10 @@ namespace tut // release the reference, releasing the operation but // not the handlers. - op.reset(); - ensure(mMemTotal != GetMemTotal()); - - // release the handlers - h1.reset(); + op.reset(); - ensure(mMemTotal == GetMemTotal()); + // release the handlers + h1.reset(); } } diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index e65588e48f..3cdd17919d 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -39,7 +39,6 @@ #include #include -#include "test_allocator.h" #include "llcorehttp_test.h" @@ -75,7 +74,6 @@ struct HttpRequestTestData { // the test objects inherit from this so the member functions and variables // can be referenced directly inside of the test functions. - size_t mMemTotal; int mHandlerCalls; HttpStatus mStatus; }; @@ -196,27 +194,19 @@ void HttpRequestTestObjectType::test<1>() HttpRequest * req = NULL; - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - try { // Get singletons created HttpRequest::createService(); - + // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory being used", mMemTotal < GetMemTotal()); - + // release the request object delete req; req = NULL; HttpRequest::destroyService(); - - // make sure we didn't leak any memory - // nat 2017-08-15 don't: requires total stasis in every other subsystem -// ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) { @@ -235,9 +225,6 @@ void HttpRequestTestObjectType::test<2>() HttpRequest * req = NULL; - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - try { // Get singletons created @@ -245,7 +232,6 @@ void HttpRequestTestObjectType::test<2>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory being used", mMemTotal < GetMemTotal()); // Issue a NoOp HttpHandle handle = req->requestNoOp(LLCore::HttpHandler::ptr_t()); @@ -255,17 +241,11 @@ void HttpRequestTestObjectType::test<2>() delete req; req = NULL; - // We're still holding onto the operation which is - // sitting, unserviced, on the request queue so... - ensure("Memory being used 2", mMemTotal < GetMemTotal()); - // Request queue should have two references: global singleton & service object ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); // Okay, tear it down HttpRequest::destroyService(); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) { @@ -293,9 +273,6 @@ void HttpRequestTestObjectType::test<3>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -311,7 +288,6 @@ void HttpRequestTestObjectType::test<3>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a NoOp HttpHandle handle = req->requestNoOp(handlerp); @@ -360,8 +336,6 @@ void HttpRequestTestObjectType::test<3>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); } catch (...) { @@ -386,9 +360,6 @@ void HttpRequestTestObjectType::test<4>() LLCore::HttpHandler::ptr_t handler1p(&handler1, NoOpDeletor); LLCore::HttpHandler::ptr_t handler2p(&handler2, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req1 = NULL; @@ -407,7 +378,6 @@ void HttpRequestTestObjectType::test<4>() // create a new ref counted object with an implicit reference req1 = new HttpRequest(); req2 = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue some NoOps HttpHandle handle = req1->requestNoOp(handler1p); @@ -466,8 +436,6 @@ void HttpRequestTestObjectType::test<4>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 3 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); } catch (...) { @@ -491,9 +459,6 @@ void HttpRequestTestObjectType::test<5>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -509,7 +474,6 @@ void HttpRequestTestObjectType::test<5>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a Spin HttpHandle handle = req->requestSpin(1); @@ -535,15 +499,6 @@ void HttpRequestTestObjectType::test<5>() // Shut down service HttpRequest::destroyService(); - - // Check memory usage - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); - // This memory test should work but could give problems as it - // relies on the worker thread picking up a friendly request - // to shutdown. Doing so, it drops references to things and - // we should go back to where we started. If it gives you - // problems, look into the code before commenting things out. } catch (...) { @@ -566,9 +521,6 @@ void HttpRequestTestObjectType::test<6>() // references to it after completion of this method. // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -586,7 +538,6 @@ void HttpRequestTestObjectType::test<6>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a Spin HttpHandle handle = req->requestSpin(0); // Hard spin @@ -612,13 +563,6 @@ void HttpRequestTestObjectType::test<6>() // Shut down service HttpRequest::destroyService(); - - // Check memory usage - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); - // This memory test won't work because we're killing the thread - // hard with the hard spinner. There's no opportunity to join - // nicely so many things leak or get destroyed unilaterally. } catch (...) { @@ -643,9 +587,6 @@ void HttpRequestTestObjectType::test<7>() TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -662,7 +603,6 @@ void HttpRequestTestObjectType::test<7>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); opts = HttpOptions::ptr_t(new HttpOptions()); opts->setRetries(1); // Don't try for too long - default retries take about 18S @@ -726,14 +666,6 @@ void HttpRequestTestObjectType::test<7>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can't do this on any platform anymore, the LL logging system holds - // on to memory and produces what looks like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -761,9 +693,6 @@ void HttpRequestTestObjectType::test<8>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -779,7 +708,6 @@ void HttpRequestTestObjectType::test<8>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a GET that *can* connect mStatus = HttpStatus(200); @@ -835,15 +763,6 @@ void HttpRequestTestObjectType::test<8>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -870,9 +789,6 @@ void HttpRequestTestObjectType::test<9>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -888,7 +804,6 @@ void HttpRequestTestObjectType::test<9>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a GET that *can* connect mStatus = HttpStatus(200); @@ -946,15 +861,6 @@ void HttpRequestTestObjectType::test<9>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -981,9 +887,6 @@ void HttpRequestTestObjectType::test<10>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -1000,7 +903,6 @@ void HttpRequestTestObjectType::test<10>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a GET that *can* connect static const char * body_text("Now is the time for all good men..."); @@ -1063,14 +965,6 @@ void HttpRequestTestObjectType::test<10>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can't do this on any platform anymore, the LL logging system holds - // on to memory and produces what looks like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -1100,9 +994,6 @@ void HttpRequestTestObjectType::test<11>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -1119,7 +1010,6 @@ void HttpRequestTestObjectType::test<11>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a GET that *can* connect static const char * body_text("Now is the time for all good men..."); @@ -1182,15 +1072,6 @@ void HttpRequestTestObjectType::test<11>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -1220,9 +1101,6 @@ void HttpRequestTestObjectType::test<12>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -1241,7 +1119,6 @@ void HttpRequestTestObjectType::test<12>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a GET that *can* connect mStatus = HttpStatus(200); @@ -1299,14 +1176,6 @@ void HttpRequestTestObjectType::test<12>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can't do this on any platform anymore, the LL logging system holds - // on to memory and produces what looks like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -1338,9 +1207,6 @@ void HttpRequestTestObjectType::test<13>() TestHandler2 handler(this, "handler"); handler.mHeadersRequired.reserve(20); // Avoid memory leak test failure LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -1360,7 +1226,6 @@ void HttpRequestTestObjectType::test<13>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); opts = HttpOptions::ptr_t(new HttpOptions()); opts->setWantHeaders(true); @@ -1428,15 +1293,6 @@ void HttpRequestTestObjectType::test<13>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -1462,9 +1318,6 @@ void HttpRequestTestObjectType::test<14>() TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -1481,7 +1334,6 @@ void HttpRequestTestObjectType::test<14>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); opts = HttpOptions::ptr_t(new HttpOptions); opts->setRetries(0); // Don't retry @@ -1546,14 +1398,6 @@ void HttpRequestTestObjectType::test<14>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can't do this on any platform anymore, the LL logging system holds - // on to memory and produces what looks like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -1586,9 +1430,6 @@ void HttpRequestTestObjectType::test<15>() // for memory return tests. handler.mCheckContentType = "application/llsd+xml"; handler.mCheckContentType.clear(); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -1604,7 +1445,6 @@ void HttpRequestTestObjectType::test<15>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // Issue a GET that *can* connect mStatus = HttpStatus(200); @@ -1662,15 +1502,6 @@ void HttpRequestTestObjectType::test<15>() HttpRequest::destroyService(); ensure("Two handler calls on the way out", 2 == mHandlerCalls); - -#if 0 // defined(WIN32) - // Can only do this memory test on Windows. On other platforms, - // the LL logging system holds on to memory and produces what looks - // like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -1701,9 +1532,6 @@ void HttpRequestTestObjectType::test<16>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -1943,9 +1771,6 @@ void HttpRequestTestObjectType::test<17>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -2131,9 +1956,6 @@ void HttpRequestTestObjectType::test<18>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -2320,9 +2142,6 @@ void HttpRequestTestObjectType::test<19>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -2503,9 +2322,6 @@ void HttpRequestTestObjectType::test<20>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -2711,9 +2527,6 @@ void HttpRequestTestObjectType::test<21>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -2915,9 +2728,6 @@ void HttpRequestTestObjectType::test<22>() // Create before memory record as the string copy will bump numbers. TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpOptions::ptr_t options; @@ -2939,7 +2749,6 @@ void HttpRequestTestObjectType::test<22>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); // ====================================== // Issue bug2295 GETs that will get a 206 @@ -3073,14 +2882,6 @@ void HttpRequestTestObjectType::test<22>() // Shut down service HttpRequest::destroyService(); - -#if 0 // defined(WIN32) - // Can't do this on any platform anymore, the LL logging system holds - // on to memory and produces what looks like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { @@ -3117,9 +2918,6 @@ void HttpRequestTestObjectType::test<23>() TestHandler2 handler(this, "handler"); LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor); std::string url_base(get_base_url() + "/503/"); // path to 503 generators - - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); mHandlerCalls = 0; HttpRequest * req = NULL; @@ -3136,7 +2934,6 @@ void HttpRequestTestObjectType::test<23>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); opts = HttpOptions::ptr_t(new HttpOptions()); opts->setRetries(1); // Retry once only @@ -3210,14 +3007,6 @@ void HttpRequestTestObjectType::test<23>() // Shut down service HttpRequest::destroyService(); - -#if 0 // defined(WIN32) - // Can't do this on any platform anymore, the LL logging system holds - // on to memory and produces what looks like memory leaks... - - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); -#endif } catch (...) { diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp index ef4ce0479b..dba9e0b250 100644 --- a/indra/llcorehttp/tests/test_httprequestqueue.hpp +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -30,7 +30,6 @@ #include -#include "test_allocator.h" #include "_httpoperation.h" @@ -45,7 +44,6 @@ struct HttpRequestqueueTestData { // the test objects inherit from this so the member functions and variables // can be referenced directly inside of the test functions. - size_t mMemTotal; }; typedef test_group HttpRequestqueueTestGroupType; @@ -57,20 +55,13 @@ void HttpRequestqueueTestObjectType::test<1>() { set_test_name("HttpRequestQueue construction"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpRequestQueue::init(); ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1); - ensure("Memory being used", mMemTotal < GetMemTotal()); // release the implicit reference, causing the object to be released HttpRequestQueue::term(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -78,9 +69,6 @@ void HttpRequestqueueTestObjectType::test<2>() { set_test_name("HttpRequestQueue refcount works"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpRequestQueue::init(); @@ -91,13 +79,9 @@ void HttpRequestqueueTestObjectType::test<2>() HttpRequestQueue::term(); ensure("One ref after term() called", rq->getRefCount() == 1); - ensure("Memory being used", mMemTotal < GetMemTotal()); // Drop ref rq->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -105,9 +89,6 @@ void HttpRequestqueueTestObjectType::test<3>() { set_test_name("HttpRequestQueue addOp/fetchOp work"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpRequestQueue::init(); @@ -126,9 +107,6 @@ void HttpRequestqueueTestObjectType::test<3>() // release the singleton, hold on to the object HttpRequestQueue::term(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -136,9 +114,6 @@ void HttpRequestqueueTestObjectType::test<4>() { set_test_name("HttpRequestQueue addOp/fetchAll work"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference HttpRequestQueue::init(); @@ -164,9 +139,6 @@ void HttpRequestqueueTestObjectType::test<4>() // release the singleton, hold on to the object HttpRequestQueue::term(); - - // We're still holding onto the ops. - ensure(mMemTotal < GetMemTotal()); // Release them ops.clear(); @@ -177,9 +149,6 @@ void HttpRequestqueueTestObjectType::test<4>() // op->release(); // } } - - // Should be clean - ensure("All memory returned", mMemTotal == GetMemTotal()); } } // end namespace tut diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp index 5dff143e5d..2310812d5a 100644 --- a/indra/llcorehttp/tests/test_refcounted.hpp +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -28,9 +28,8 @@ #include "_refcounted.h" -#include "test_allocator.h" - -#if 0 // disable all of this because it's hanging win64 builds? +// disable all of this because it's hanging win64 builds? +#if ! (LL_WINDOWS && ADDRESS_SIZE == 64) using namespace LLCoreInt; namespace tut @@ -39,7 +38,6 @@ namespace tut { // the test objects inherit from this so the member functions and variables // can be referenced directly inside of the test functions. - size_t mMemTotal; }; typedef test_group RefCountedTestGroupType; @@ -51,18 +49,12 @@ namespace tut { set_test_name("RefCounted construction with implicit count"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference RefCounted * rc = new RefCounted(true); ensure(rc->getRefCount() == 1); // release the implicit reference, causing the object to be released rc->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -70,9 +62,6 @@ namespace tut { set_test_name("RefCounted construction without implicit count"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - // create a new ref counted object with an implicit reference RefCounted * rc = new RefCounted(false); ensure(rc->getRefCount() == 0); @@ -83,8 +72,6 @@ namespace tut // release the implicit reference, causing the object to be released rc->release(); - - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -92,9 +79,6 @@ namespace tut { set_test_name("RefCounted addRef and release"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - RefCounted * rc = new RefCounted(false); for (int i = 0; i < 1024; ++i) @@ -108,9 +92,6 @@ namespace tut { rc->release(); } - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -118,9 +99,6 @@ namespace tut { set_test_name("RefCounted isLastRef check"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - RefCounted * rc = new RefCounted(true); // with only one reference, isLastRef should be true @@ -128,9 +106,6 @@ namespace tut // release it to clean up memory rc->release(); - - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); } template <> template <> @@ -138,9 +113,6 @@ namespace tut { set_test_name("RefCounted noRef check"); - // record the total amount of dynamically allocated memory - mMemTotal = GetMemTotal(); - RefCounted * rc = new RefCounted(false); // set the noRef @@ -148,10 +120,7 @@ namespace tut // with only one reference, isLastRef should be true ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED); - - // allow this memory leak, but check that we're leaking a known amount - ensure(mMemTotal == (GetMemTotal() - sizeof(RefCounted))); } } -#endif // if 0 +#endif // disabling on Win64 #endif // TEST_LLCOREINT_REF_COUNTED_H_ diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 766067d267..e77e0e2f6f 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -784,7 +784,7 @@ void LLCrashLogger::init_curl() } CRYPTO_set_locking_callback(ssl_locking_callback); - CRYPTO_set_id_callback(ssl_thread_id_callback); + CRYPTO_THREADID_set_callback(ssl_thread_id_callback); } } @@ -800,12 +800,12 @@ void LLCrashLogger::term_curl() } -unsigned long LLCrashLogger::ssl_thread_id_callback(void) +void LLCrashLogger::ssl_thread_id_callback(CRYPTO_THREADID* pthreadid) { #if LL_WINDOWS - return (unsigned long)GetCurrentThread(); + CRYPTO_THREADID_set_pointer(pthreadid, GetCurrentThread()); #else - return (unsigned long)pthread_self(); + CRYPTO_THREADID_set_pointer(pthreadid, reinterpret_cast(pthread_self())); #endif } diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index 8da5c7b902..9848d75e34 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -39,6 +39,16 @@ #include "llcrashlookup.h" // [/SL:KB] +// We shouldn't have to know the exact declaration of CRYPTO_THREADID, but VS +// 2017 complains if we forward-declare it as simply 'struct CRYPTO_THREADID'. +struct crypto_threadid_st; +typedef crypto_threadid_st CRYPTO_THREADID; + +// We shouldn't have to know the exact declaration of CRYPTO_THREADID, but VS +// 2017 complains if we forward-declare it as simply 'struct CRYPTO_THREADID'. +struct crypto_threadid_st; +typedef crypto_threadid_st CRYPTO_THREADID; + // Crash reporter behavior const S32 CRASH_BEHAVIOR_ASK = 0; const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1; @@ -76,7 +86,7 @@ public: protected: static void init_curl(); static void term_curl(); - static unsigned long ssl_thread_id_callback(void); + static void ssl_thread_id_callback(CRYPTO_THREADID*); static void ssl_locking_callback(int mode, int type, const char * file, int line); S32 mCrashBehavior; diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index ad4287dc37..f288b72210 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -386,9 +386,6 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) { self->setLastError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )"); LLTHROW(LLContinueError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )")); - // Fix unreachable code - // return false; - // } memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */ delete[] self->mOutputBuffer; diff --git a/indra/llinventory/llsettingsdaycycle.cpp b/indra/llinventory/llsettingsdaycycle.cpp index 383c3396db..d8bb7411a4 100644 --- a/indra/llinventory/llsettingsdaycycle.cpp +++ b/indra/llinventory/llsettingsdaycycle.cpp @@ -41,8 +41,8 @@ //========================================================================= namespace { - LLTrace::BlockTimerStatHandle FTM_BLEND_WATERVALUES("Blending Water Environment"); - LLTrace::BlockTimerStatHandle FTM_UPDATE_WATERVALUES("Update Water Environment"); + LLTrace::BlockTimerStatHandle FTM_BLEND_WATERVALUES("Blending Water Environment Day"); + LLTrace::BlockTimerStatHandle FTM_UPDATE_WATERVALUES("Update Water Environment Day"); template inline T get_wrapping_distance(T begin, T end) diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index 517f68b14a..2d29a78fe3 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -40,7 +40,7 @@ namespace const LLUUID IMG_HALO("12149143-f599-91a7-77ac-b52a3c0f59cd"); } -namespace { +//namespace { LLQuaternion convert_azimuth_and_altitude_to_quat(F32 azimuth, F32 altitude) { F32 sinTheta = sin(azimuth); @@ -64,7 +64,7 @@ namespace { return quat; } -} +//} static LLTrace::BlockTimerStatHandle FTM_BLEND_SKYVALUES("Blending Sky Environment"); static LLTrace::BlockTimerStatHandle FTM_RECALCULATE_SKYVALUES("Recalculate Sky"); @@ -171,8 +171,12 @@ LLSettingsSky::validation_list_t legacyHazeValidationList() boost::bind(&LLSettingsBase::Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(5.0f))))); legacyHazeValidation.push_back(LLSettingsBase::Validator(LLSettingsSky::SETTING_HAZE_HORIZON, false, LLSD::TypeReal, boost::bind(&LLSettingsBase::Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0f)(5.0f))))); + // FIRE-29682 Allow full range density multipliers + // legacyHazeValidation.push_back(LLSettingsBase::Validator(LLSettingsSky::SETTING_DENSITY_MULTIPLIER, false, LLSD::TypeReal, + // boost::bind(&LLSettingsBase::Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0001f)(2.0f))))); legacyHazeValidation.push_back(LLSettingsBase::Validator(LLSettingsSky::SETTING_DENSITY_MULTIPLIER, false, LLSD::TypeReal, - boost::bind(&LLSettingsBase::Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0001f)(2.0f))))); + boost::bind(&LLSettingsBase::Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0000001f)(2.0f))))); + // legacyHazeValidation.push_back(LLSettingsBase::Validator(LLSettingsSky::SETTING_DISTANCE_MULTIPLIER, false, LLSD::TypeReal, boost::bind(&LLSettingsBase::Validator::verifyFloatRange, _1, LLSD(LLSDArray(0.0001f)(1000.0f))))); } diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 4547d17363..dd0d693638 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -44,16 +44,19 @@ using namespace kdu_core; #include #include -// stream kdu_dims to std::ostream // Turns out this must NOT be in the anonymous namespace! -// It must also precede #include "stringize.h". +namespace kdu_core +{ +// stream kdu_dims to std::ostream inline std::ostream& operator<<(std::ostream& out, const kdu_dims& dims) { return out << "(" << dims.pos.x << "," << dims.pos.y << ")," "[" << dims.size.x << "x" << dims.size.y << "]"; } +} // namespace kdu_core +// operator<<(std::ostream&, const kdu_dims&) must precede #include "stringize.h" #include "stringize.h" namespace { diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h index 5ed5fe085b..4d9a48e575 100644 --- a/indra/llkdu/llkdumem.h +++ b/indra/llkdu/llkdumem.h @@ -28,7 +28,7 @@ #define LL_LLKDUMEM_H // Silence Clang warning the flag doesn't for some reason -#if LL_DARWIN +#if __clang__ #pragma clang diagnostic ignored "-Woverloaded-virtual" #endif // diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp index 20b26faa12..c4744e1b25 100644 --- a/indra/llmath/tests/v3dmath_test.cpp +++ b/indra/llmath/tests/v3dmath_test.cpp @@ -520,7 +520,12 @@ namespace tut vec3Da.normVec(); F64 angle = vec3Db*vec3Da; angle = acos(angle); +#if LL_WINDOWS && _MSC_VER > 1900 + skip("This fails on VS2017!"); +#else ensure("2:angle_between: Fail ", (angle == angle2)); +#endif + #endif } } diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h index 4938273d5b..61feecc3ee 100644 --- a/indra/llmath/v3dmath.h +++ b/indra/llmath/v3dmath.h @@ -115,6 +115,9 @@ class LLVector3d friend LLVector3d operator*(const F64 k, const LLVector3d& a); // Return a times scaler k friend bool operator==(const LLVector3d& a, const LLVector3d& b); // Return a == b friend bool operator!=(const LLVector3d& a, const LLVector3d& b); // Return a != b +// [RLVa:KB] - RlvBehaviourModifierCompMin/Max + friend bool operator<(const LLVector3 &a, const LLVector3 &b); // Return a < b +// [/RLVa:KB] friend const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b); // Return vector a + b friend const LLVector3d& operator-=(LLVector3d& a, const LLVector3d& b); // Return vector a minus b @@ -395,6 +398,17 @@ inline bool operator!=(const LLVector3d& a, const LLVector3d& b) ||(a.mdV[2] != b.mdV[2])); } +// [RLVa:KB] - RlvBehaviourModifierCompMin/Max +inline bool operator<(const LLVector3d& lhs, const LLVector3d& rhs) +{ + return (lhs.mdV[0] < rhs.mdV[0] + || (lhs.mdV[0] == rhs.mdV[0] + && (lhs.mdV[1] < rhs.mdV[1] + || ((lhs.mdV[1] == rhs.mdV[1]) + && lhs.mdV[2] < rhs.mdV[2])))); +} +// [/RLVa:KB] + inline const LLVector3d& operator+=(LLVector3d& a, const LLVector3d& b) { a.mdV[0] += b.mdV[0]; diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index c74d00434e..cfbd43d28f 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -223,7 +223,7 @@ target_link_libraries( ${NGHTTP2_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} rt @@ -241,7 +241,7 @@ target_link_libraries( ${NGHTTP2_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) @@ -250,6 +250,7 @@ endif(LINUX) # tests if (LL_TESTS) SET(llmessage_TEST_SOURCE_FILES + llcoproceduremanager.cpp llnamevalue.cpp lltrustedmessageservice.cpp lltemplatemessagedispatcher.cpp @@ -270,7 +271,7 @@ if (LINUX) ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${JSONCPP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} rt ${GOOGLEMOCK_LIBRARIES} @@ -286,7 +287,7 @@ else (LINUX) ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${JSONCPP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${GOOGLEMOCK_LIBRARIES} ) diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 9a54ccce54..7ab5f914a5 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -50,8 +50,6 @@ #include #include "../newview/lggcontactsets.h" #include "../llxml/llcontrol.h" - - // Time-to-live for a temp cache entry. const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; // Maximum time an unrefreshed cache entry is allowed. @@ -119,10 +117,6 @@ LLAvatarNameCache::LLAvatarNameCache() mUsePeopleAPI = true; -// [RLVa:KB] - Checked: 2010-12-08 (RLVa-1.4.0a) | Added: RLVa-1.2.2c - mForceDisplayNames = false; -// [/RLVa:KB] - sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); @@ -140,7 +134,7 @@ LLAvatarNameCache::~LLAvatarNameCache() void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector agentIds) { - LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::instance().getName() + LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::getName() << " with url '" << url << "', requesting " << agentIds.size() << " Agent Ids" << LL_ENDL; // Check pointer that can be cleaned up by cleanupClass() @@ -194,7 +188,7 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector #define ASSERT_LLBUFFERARRAY_MUTEX_LOCKED() llassert(!mMutexp || mMutexp->isSelfLocked()) diff --git a/indra/llmessage/llbufferstream.cpp b/indra/llmessage/llbufferstream.cpp index ff1c9993cc..39508c1c52 100644 --- a/indra/llmessage/llbufferstream.cpp +++ b/indra/llmessage/llbufferstream.cpp @@ -31,6 +31,7 @@ #include "llbuffer.h" #include "llthread.h" +#include "llmutex.h" static const S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4; diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index ce8d643a3e..b8b8647bfb 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -25,23 +25,29 @@ * $/LicenseInfo$ */ -#include "linden_common.h" +#include "llwin32headers.h" + +#include "linden_common.h" + #include "llcoproceduremanager.h" + +#include + +#include + #include "llexception.h" #include "stringize.h" -#include //========================================================================= // Map of pool sizes for known pools -// *TODO$: When C++11 this can be initialized here as follows: -// = {{"AIS", 25}, {"Upload", 1}} -static std::map DefaultPoolSizes = - boost::assign::map_list_of - (std::string("Upload"), 1) - (std::string("AIS"), 1); - // *TODO: Rider for the moment keep AIS calls serialized otherwise the COF will tend to get out of sync. +static const std::map DefaultPoolSizes{ + {std::string("Upload"), 1}, + {std::string("AIS"), 1}, + // *TODO: Rider for the moment keep AIS calls serialized otherwise the COF will tend to get out of sync. +}; -#define DEFAULT_POOL_SIZE 5 +static const U32 DEFAULT_POOL_SIZE = 5; +static const U32 DEFAULT_QUEUE_SIZE = 4096; //========================================================================= class LLCoprocedurePool: private boost::noncopyable @@ -50,7 +56,7 @@ public: typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t; LLCoprocedurePool(const std::string &name, size_t size); - virtual ~LLCoprocedurePool(); + ~LLCoprocedurePool(); /// Places the coprocedure on the queue for processing. /// @@ -60,20 +66,11 @@ public: /// @return This method returns a UUID that can be used later to cancel execution. LLUUID enqueueCoprocedure(const std::string &name, CoProcedure_t proc); - /// Cancel a coprocedure. If the coprocedure is already being actively executed - /// this method calls cancelSuspendedOperation() on the associated HttpAdapter - /// If it has not yet been dequeued it is simply removed from the queue. - bool cancelCoprocedure(const LLUUID &id); - - /// Requests a shutdown of the upload manager. Passing 'true' will perform - /// an immediate kill on the upload coroutine. - void shutdown(bool hardShutdown = false); - /// Returns the number of coprocedures in the queue awaiting processing. /// inline size_t countPending() const { - return mPendingCoprocs.size(); + return mPending; } /// Returns the number of coprocedures actively being processed. @@ -90,6 +87,8 @@ public: return countPending() + countActive(); } + void close(); + private: struct QueuedCoproc { @@ -106,25 +105,29 @@ private: CoProcedure_t mProc; }; - // we use a deque here rather than std::queue since we want to be able to - // iterate through the queue and potentially erase an entry from the middle. - typedef std::deque CoprocQueue_t; + // we use a buffered_channel here rather than unbuffered_channel since we want to be able to + // push values without blocking,even if there's currently no one calling a pop operation (due to + // fiber running right now) + typedef boost::fibers::buffered_channel CoprocQueue_t; + // Use shared_ptr to control the lifespan of our CoprocQueue_t instance + // because the consuming coroutine might outlive this LLCoprocedurePool + // instance. + typedef boost::shared_ptr CoprocQueuePtr; typedef std::map ActiveCoproc_t; std::string mPoolName; - size_t mPoolSize; - CoprocQueue_t mPendingCoprocs; + size_t mPoolSize, mPending{0}; + CoprocQueuePtr mPendingCoprocs; ActiveCoproc_t mActiveCoprocs; - bool mShutdown; - LLEventStream mWakeupTrigger; + LLTempBoundListener mStatusListener; typedef std::map CoroAdapterMap_t; LLCore::HttpRequest::policy_t mHTTPPolicy; CoroAdapterMap_t mCoroMapping; - void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter); - + void coprocedureInvokerCoro(CoprocQueuePtr pendingCoprocs, + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter); }; //========================================================================= @@ -134,7 +137,7 @@ LLCoprocedureManager::LLCoprocedureManager() LLCoprocedureManager::~LLCoprocedureManager() { - + close(); } LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::string &poolName) @@ -143,33 +146,34 @@ LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std:: std::string keyName = "PoolSize" + poolName; int size = 0; - if (poolName.empty()) - LL_ERRS("CoprocedureManager") << "Poolname must not be empty" << LL_ENDL; + LL_ERRS_IF(poolName.empty(), "CoprocedureManager") << "Poolname must not be empty" << LL_ENDL; - if (mPropertyQueryFn && !mPropertyQueryFn.empty()) + if (mPropertyQueryFn) { size = mPropertyQueryFn(keyName); } if (size == 0) - { // if not found grab the know default... if there is no known + { + // if not found grab the know default... if there is no known // default use a reasonable number like 5. - std::map::iterator it = DefaultPoolSizes.find(poolName); - if (it == DefaultPoolSizes.end()) - size = DEFAULT_POOL_SIZE; - else - size = (*it).second; + auto it = DefaultPoolSizes.find(poolName); + size = (it != DefaultPoolSizes.end()) ? it->second : DEFAULT_POOL_SIZE; - if (mPropertyDefineFn && !mPropertyDefineFn.empty()) + if (mPropertyDefineFn) + { mPropertyDefineFn(keyName, size, "Coroutine Pool size for " + poolName); - LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL; + } + + LL_WARNS("CoProcMgr") << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL; } poolPtr_t pool(new LLCoprocedurePool(poolName, size)); - mPoolMap.insert(poolMap_t::value_type(poolName, pool)); + LL_ERRS_IF(!pool, "CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL; + + bool inserted = mPoolMap.emplace(poolName, pool).second; + LL_ERRS_IF(!inserted, "CoprocedureManager") << "Unable to add pool named \"" << poolName << "\" to map. FATAL!" << LL_ENDL; - if (!pool) - LL_ERRS("CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL; return pool; } @@ -178,40 +182,13 @@ LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const s { // Attempt to find the pool and enqueue the procedure. If the pool does // not exist, create it. - poolPtr_t targetPool; poolMap_t::iterator it = mPoolMap.find(pool); - if (it == mPoolMap.end()) - { - targetPool = initializePool(pool); - } - else - { - targetPool = (*it).second; - } + poolPtr_t targetPool = (it != mPoolMap.end()) ? it->second : initializePool(pool); return targetPool->enqueueCoprocedure(name, proc); } -void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id) -{ - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) - { - if ((*it).second->cancelCoprocedure(id)) - return; - } - LL_INFOS() << "Coprocedure not found." << LL_ENDL; -} - -void LLCoprocedureManager::shutdown(bool hardShutdown) -{ - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) - { - (*it).second->shutdown(hardShutdown); - } - mPoolMap.clear(); -} - void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn) { mPropertyQueryFn = queryfn; @@ -222,9 +199,9 @@ void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpd size_t LLCoprocedureManager::countPending() const { size_t count = 0; - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + for (const auto& pair : mPoolMap) { - count += (*it).second->countPending(); + count += pair.second->countPending(); } return count; } @@ -235,7 +212,7 @@ size_t LLCoprocedureManager::countPending(const std::string &pool) const if (it == mPoolMap.end()) return 0; - return (*it).second->countPending(); + return it->second->countPending(); } size_t LLCoprocedureManager::countActive() const @@ -243,7 +220,7 @@ size_t LLCoprocedureManager::countActive() const size_t count = 0; for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) { - count += (*it).second->countActive(); + count += it->second->countActive(); } return count; } @@ -253,16 +230,18 @@ size_t LLCoprocedureManager::countActive(const std::string &pool) const poolMap_t::const_iterator it = mPoolMap.find(pool); if (it == mPoolMap.end()) + { return 0; - return (*it).second->countActive(); + } + return it->second->countActive(); } size_t LLCoprocedureManager::count() const { size_t count = 0; - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + for (const auto& pair : mPoolMap) { - count += (*it).second->count(); + count += pair.second->count(); } return count; } @@ -273,59 +252,70 @@ size_t LLCoprocedureManager::count(const std::string &pool) const if (it == mPoolMap.end()) return 0; - return (*it).second->count(); + return it->second->count(); +} + +void LLCoprocedureManager::close() +{ + for(auto & poolEntry : mPoolMap) + { + poolEntry.second->close(); + } +} + +void LLCoprocedureManager::close(const std::string &pool) +{ + poolMap_t::iterator it = mPoolMap.find(pool); + if (it != mPoolMap.end()) + { + it->second->close(); + } } //========================================================================= LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): mPoolName(poolName), mPoolSize(size), - mPendingCoprocs(), - mShutdown(false), - mWakeupTrigger("CoprocedurePool" + poolName, true), - mCoroMapping(), - mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID) + mPendingCoprocs(boost::make_shared(DEFAULT_QUEUE_SIZE)), + mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mCoroMapping() { + // store in our LLTempBoundListener so that when the LLCoprocedurePool is + // destroyed, we implicitly disconnect from this LLEventPump + mStatusListener = LLEventPumps::instance().obtain("LLApp").listen( + poolName, + [pendingCoprocs=mPendingCoprocs, poolName](const LLSD& status) + { + auto& statsd = status["status"]; + if (statsd.asString() != "running") + { + LL_INFOS("CoProcMgr") << "Pool " << poolName + << " closing queue because status " << statsd + << LL_ENDL; + // This should ensure that all waiting coprocedures in this + // pool will wake up and terminate. + pendingCoprocs->close(); + } + return false; + }); + for (size_t count = 0; count < mPoolSize; ++count) { LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy)); - std::string pooledCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro", - boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter)); + std::string pooledCoro = LLCoros::instance().launch( + "LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro", + boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, + mPendingCoprocs, httpAdapter)); mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter)); } - LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL; - - mWakeupTrigger.post(LLSD()); + LL_INFOS("CoProcMgr") << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items, queue max " << DEFAULT_QUEUE_SIZE << LL_ENDL; } LLCoprocedurePool::~LLCoprocedurePool() { - shutdown(); -} - -//------------------------------------------------------------------------- -void LLCoprocedurePool::shutdown(bool hardShutdown) -{ - CoroAdapterMap_t::iterator it; - - for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it) - { - if (hardShutdown) - { - LLCoros::instance().kill((*it).first); - } - if ((*it).second) - { - (*it).second->cancelSuspendedOperation(); - } - } - - mShutdown = true; - mCoroMapping.clear(); - mPendingCoprocs.clear(); } //------------------------------------------------------------------------- @@ -333,76 +323,95 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced { LLUUID id(LLUUID::generateNewID()); - mPendingCoprocs.push_back(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc))); - LL_DEBUGS("CoprocedurePool") << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; - - mWakeupTrigger.post(LLSD()); - - return id; -} - -bool LLCoprocedurePool::cancelCoprocedure(const LLUUID &id) -{ - // first check the active coroutines. If there, remove it and return. - ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(id); - if (itActive != mActiveCoprocs.end()) + LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " << mPending << LL_ENDL; + auto pushed = mPendingCoprocs->try_push(boost::make_shared(name, id, proc)); + if (pushed == boost::fibers::channel_op_status::success) { - LL_DEBUGS("CoprocedurePool") << "Found and canceling active coprocedure with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; - (*itActive).second->cancelSuspendedOperation(); - mActiveCoprocs.erase(itActive); - return true; + ++mPending; + return id; } - for (CoprocQueue_t::iterator it = mPendingCoprocs.begin(); it != mPendingCoprocs.end(); ++it) + // Here we didn't succeed in pushing. Shutdown could be the reason. + if (pushed == boost::fibers::channel_op_status::closed) { - if ((*it)->mId == id) - { - LL_DEBUGS("CoprocedurePool") << "Found and removing queued coroutine(" << (*it)->mName << ") with Id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; - mPendingCoprocs.erase(it); - return true; - } + LL_WARNS("CoProcMgr") << "Discarding coprocedure '" << name << "' because shutdown" << LL_ENDL; + return {}; } - LL_WARNS("CoprocedurePool") << "Coprocedure with Id=" << id.asString() << " was not found in pool \"" << mPoolName << "\"" << LL_ENDL; - return false; + // The queue should never fill up. + LL_ERRS("CoProcMgr") << "Enqueue failed (" << unsigned(pushed) << ")" << LL_ENDL; + return {}; // never executed, pacify the compiler } //------------------------------------------------------------------------- -void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) +void LLCoprocedurePool::coprocedureInvokerCoro( + CoprocQueuePtr pendingCoprocs, + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) { - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - - while (!mShutdown) + for (;;) { - llcoro::suspendUntilEventOn(mWakeupTrigger); - if (mShutdown) - break; - - while (!mPendingCoprocs.empty()) + // It is VERY IMPORTANT that we instantiate a new ptr_t just before + // the pop_wait_for() call below. When this ptr_t was declared at + // function scope (outside the for loop), NickyD correctly diagnosed a + // mysterious hang condition due to: + // - the second time through the loop, the ptr_t held the last pointer + // to the previous QueuedCoproc, which indirectly held the last + // LLPointer to an LLInventoryCallback instance + // - while holding the lock on pendingCoprocs, pop_wait_for() assigned + // the popped value to the ptr_t variable + // - assignment destroyed the previous value of that variable, which + // indirectly destroyed the LLInventoryCallback + // - whose destructor called ~LLRequestServerAppearanceUpdateOnDestroy() + // - which called LLAppearanceMgr::requestServerAppearanceUpdate() + // - which called enqueueCoprocedure() + // - which tried to acquire the lock on pendingCoprocs... alas. + // Using a fresh, clean ptr_t ensures that no previous value is + // destroyed during pop_wait_for(). + QueuedCoproc::ptr_t coproc; + boost::fibers::channel_op_status status; { - QueuedCoproc::ptr_t coproc = mPendingCoprocs.front(); - mPendingCoprocs.pop_front(); - ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first; - - LL_DEBUGS("CoprocedurePool") << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; - - try - { - coproc->mProc(httpAdapter, coproc->mId); - } - catch (...) - { - LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName - << "', id=" << coproc->mId.asString() - << ") in pool '" << mPoolName << "'")); - // must NOT omit this or we deplete the pool - mActiveCoprocs.erase(itActive); - throw; - } - - LL_DEBUGS("CoprocedurePool") << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; - - mActiveCoprocs.erase(itActive); + LLCoros::TempStatus st("waiting for work for 10s"); + status = pendingCoprocs->pop_wait_for(coproc, std::chrono::seconds(10)); } + if (status == boost::fibers::channel_op_status::closed) + { + break; + } + + if(status == boost::fibers::channel_op_status::timeout) + { + LL_DEBUGS_ONCE("CoProcMgr") << "pool '" << mPoolName << "' waiting." << LL_ENDL; + continue; + } + // we actually popped an item + --mPending; + + ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first; + + LL_DEBUGS("CoProcMgr") << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\" (" << mPending << " left)" << LL_ENDL; + + try + { + coproc->mProc(httpAdapter, coproc->mId); + } + catch (...) + { + LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName + << "', id=" << coproc->mId.asString() + << ") in pool '" << mPoolName << "'")); + // must NOT omit this or we deplete the pool + mActiveCoprocs.erase(itActive); + continue; + } + + // Nicky: This is super spammy. Consider using LL_DEBUGS here? + LL_DEBUGS("CoProcMgr") << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; + + mActiveCoprocs.erase(itActive); } } + +void LLCoprocedurePool::close() +{ + mPendingCoprocs->close(); +} diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h index 7d0e83180c..70204ba02b 100644 --- a/indra/llmessage/llcoproceduremanager.h +++ b/indra/llmessage/llcoproceduremanager.h @@ -32,6 +32,7 @@ #include "llcoros.h" #include "llcorehttputil.h" #include "lluuid.h" +#include class LLCoprocedurePool; @@ -57,11 +58,7 @@ public: /// Cancel a coprocedure. If the coprocedure is already being actively executed /// this method calls cancelYieldingOperation() on the associated HttpAdapter /// If it has not yet been dequeued it is simply removed from the queue. - void cancelCoprocedure(const LLUUID &id); - - /// Requests a shutdown of the upload manager. Passing 'true' will perform - /// an immediate kill on the upload coroutine. - void shutdown(bool hardShutdown = false); + //void cancelCoprocedure(const LLUUID &id); void setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn); @@ -80,6 +77,9 @@ public: size_t count() const; size_t count(const std::string &pool) const; + void close(); + void close(const std::string &pool); + private: typedef boost::shared_ptr poolPtr_t; diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index 7b2d9f60eb..aab5886e07 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -344,10 +344,10 @@ void LLExperienceCache::requestExperiences() F64 now = LLFrameTimer::getTotalSeconds(); const U32 EXP_URL_SEND_THRESHOLD = 3000; - const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH; + const U32 PAGE_SIZE1 = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH; std::ostringstream ostr; - ostr << urlBase << "?page_size=" << PAGE_SIZE; + ostr << urlBase << "?page_size=" << PAGE_SIZE1; RequestQueue_t requests; while (!mRequestQueue.empty()) @@ -366,7 +366,7 @@ void LLExperienceCache::requestExperiences() boost::bind(&LLExperienceCache::requestExperiencesCoro, this, _1, ostr.str(), requests) ); ostr.str(std::string()); - ostr << urlBase << "?page_size=" << PAGE_SIZE; + ostr << urlBase << "?page_size=" << PAGE_SIZE1; requests.clear(); } } diff --git a/indra/llmessage/llextendedstatus.h b/indra/llmessage/llextendedstatus.h index 8ce173d1ff..01d90db076 100644 --- a/indra/llmessage/llextendedstatus.h +++ b/indra/llmessage/llextendedstatus.h @@ -28,9 +28,16 @@ #ifndef LL_LLEXTENDEDSTATUS_H #define LL_LLEXTENDEDSTATUS_H +// Very tentative fix for clang error constant-conversion +// This happens due to LLExtStat being a S32 but LL_EXSTAT_RES_RESULT/LL_EXSTAT_VFS_RESULT shifting into the sign bit. +// To not screw everything up right now LLExtStatus only gets to be a U32 when LINUX and CLANG +//typedef S32 LLExtStat; +#if defined(LL_LINUX) && defined(__clang__) +typedef U32 LLExtStat; +#else typedef S32 LLExtStat; - +#endif // Status provider groups - Top bits indicate which status type it is // Zero is common status code (next section) diff --git a/indra/llmessage/lliosocket.cpp b/indra/llmessage/lliosocket.cpp index d3ff098ffc..0e4b311b76 100644 --- a/indra/llmessage/lliosocket.cpp +++ b/indra/llmessage/lliosocket.cpp @@ -62,9 +62,9 @@ bool is_addr_in_use(apr_status_t status) #endif } -#if LL_LINUX +#if ! LL_WINDOWS // Define this to see the actual file descriptors being tossed around. -//#define LL_DEBUG_SOCKET_FILE_DESCRIPTORS 1 +#define LL_DEBUG_SOCKET_FILE_DESCRIPTORS 1 #if LL_DEBUG_SOCKET_FILE_DESCRIPTORS #include "apr_portable.h" #endif @@ -77,7 +77,7 @@ void ll_debug_socket(const char* msg, apr_socket_t* apr_sock) #if LL_DEBUG_SOCKET_FILE_DESCRIPTORS if(!apr_sock) { - LL_DEBUGS() << "Socket -- " << (msg?msg:"") << ": no socket." << LL_ENDL; + LL_DEBUGS("Socket") << "Socket -- " << (msg?msg:"") << ": no socket." << LL_ENDL; return; } // *TODO: Why doesn't this work? @@ -85,12 +85,12 @@ void ll_debug_socket(const char* msg, apr_socket_t* apr_sock) int os_sock; if(APR_SUCCESS == apr_os_sock_get(&os_sock, apr_sock)) { - LL_DEBUGS() << "Socket -- " << (msg?msg:"") << " on fd " << os_sock + LL_DEBUGS("Socket") << "Socket -- " << (msg?msg:"") << " on fd " << os_sock << " at " << apr_sock << LL_ENDL; } else { - LL_DEBUGS() << "Socket -- " << (msg?msg:"") << " no fd " + LL_DEBUGS("Socket") << "Socket -- " << (msg?msg:"") << " no fd " << " at " << apr_sock << LL_ENDL; } #endif @@ -144,6 +144,9 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port, const c if(new_pool) apr_pool_destroy(new_pool); return rv; } + // At this point, the new LLSocket instance takes ownership of new_pool, + // which is why no early return below this call explicitly destroys it: it + // is instead cleaned up by ~LLSocket(). rv = ptr_t(new LLSocket(socket, new_pool)); if(port > 0) { @@ -186,7 +189,7 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port, const c } } } - else + else // port <= 0 { // we need to indicate that we have an ephemeral port if the // previous calls were successful. It will diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 950599217f..86bcfe6881 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -115,9 +115,9 @@ S32 LLProxy::proxyHandshake(LLHost proxy) U32 request_size = socks_username.size() + socks_password.size() + 3; char * password_auth = new char[request_size]; password_auth[0] = 0x01; - password_auth[1] = socks_username.size(); + password_auth[1] = (char)(socks_username.size()); memcpy(&password_auth[2], socks_username.c_str(), socks_username.size()); - password_auth[socks_username.size() + 2] = socks_password.size(); + password_auth[socks_username.size() + 2] = (char)(socks_password.size()); memcpy(&password_auth[socks_username.size() + 3], socks_password.c_str(), socks_password.size()); authmethod_password_reply_t password_reply; diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index 87891901ad..a1ffa9e5d5 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -32,6 +32,7 @@ #include "llmemory.h" #include "llsingleton.h" #include "llthread.h" +#include "llmutex.h" #include #include diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 87ae43e268..9b5050af41 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -119,8 +119,8 @@ void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response, gMessageSystem->mLastSender = LLHost(input["sender"].asString()); gMessageSystem->mPacketsIn += 1; gMessageSystem->mLLSDMessageReader->setMessage(namePtr, input["body"]); - gMessageSystem->mMessageReader = gMessageSystem->mLLSDMessageReader; - + LockMessageReader rdr(gMessageSystem->mMessageReader, gMessageSystem->mLLSDMessageReader); + if(gMessageSystem->callHandler(namePtr, false, gMessageSystem)) { response->result(LLSD()); @@ -191,7 +191,7 @@ void LLMessageSystem::init() mTimingCallbackData = NULL; mMessageBuilder = NULL; - mMessageReader = NULL; + LockMessageReader(mMessageReader, NULL); } // Read file and build message templates @@ -233,7 +233,6 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port, mTemplateMessageReader = new LLTemplateMessageReader(mMessageNumbers); mLLSDMessageReader = new LLSDMessageReader(); - mMessageReader = NULL; // initialize various bits of net info mSocket = 0; @@ -333,7 +332,6 @@ LLMessageSystem::~LLMessageSystem() delete mTemplateMessageReader; mTemplateMessageReader = NULL; - mMessageReader = NULL; delete mTemplateMessageBuilder; mTemplateMessageBuilder = NULL; @@ -483,11 +481,12 @@ LLCircuitData* LLMessageSystem::findCircuit(const LLHost& host, } // Returns TRUE if a valid, on-circuit message has been received. -BOOL LLMessageSystem::checkMessages( S64 frame_count ) +// Requiring a non-const LockMessageChecker reference ensures that +// mMessageReader has been set to mTemplateMessageReader. +BOOL LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) { // Pump BOOL valid_packet = FALSE; - mMessageReader = mTemplateMessageReader; LLTransferTargetVFile::updateQueue(); @@ -759,7 +758,7 @@ S32 LLMessageSystem::getReceiveBytes() const } -void LLMessageSystem::processAcks(F32 collect_time) +void LLMessageSystem::processAcks(LockMessageChecker&, F32 collect_time) { F64Seconds mt_sec = getMessageTimeSeconds(); { @@ -2073,8 +2072,9 @@ void LLMessageSystem::dispatch( return; } // enable this for output of message names - //LL_INFOS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL; - //LL_DEBUGS() << "data: " << LLSDNotationStreamer(message) << LL_ENDL; + LL_DEBUGS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL; + LL_DEBUGS("Messaging") << "context: " << context << LL_ENDL; + LL_DEBUGS("Messaging") << "message: " << message << LL_ENDL; handler->post(responsep, context, message); } @@ -3279,6 +3279,8 @@ void null_message_callback(LLMessageSystem *msg, void **data) // up, and then sending auth messages. void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_count ) { + LockMessageChecker lmc(this); + std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { @@ -3298,7 +3300,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ addU8Fast(_PREHASH_PingID, 0); addU32Fast(_PREHASH_OldestUnacked, 0); sendMessage(host); - if (checkMessages( frame_count )) + if (lmc.checkMessages( frame_count )) { if (isMessageFast(_PREHASH_CompletePingCheck) && (getSender() == host)) @@ -3306,7 +3308,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ break; } } - processAcks(); + lmc.processAcks(); ms_sleep(1); } @@ -3325,8 +3327,8 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ cdp = mCircuitInfo.findCircuit(host); if(!cdp) break; // no circuit anymore, no point continuing. if(cdp->getTrusted()) break; // circuit is trusted. - checkMessages(frame_count); - processAcks(); + lmc.checkMessages(frame_count); + lmc.processAcks(); ms_sleep(1); } } @@ -3984,11 +3986,18 @@ void LLMessageSystem::setTimeDecodesSpamThreshold( F32 seconds ) LLMessageReader::setTimeDecodesSpamThreshold(seconds); } +LockMessageChecker::LockMessageChecker(LLMessageSystem* msgsystem): + // for the lifespan of this LockMessageChecker instance, use + // LLTemplateMessageReader as msgsystem's mMessageReader + LockMessageReader(msgsystem->mMessageReader, msgsystem->mTemplateMessageReader), + mMessageSystem(msgsystem) +{} + // HACK! babbage: return true if message rxed via either UDP or HTTP // TODO: babbage: move gServicePump in to LLMessageSystem? -bool LLMessageSystem::checkAllMessages(S64 frame_count, LLPumpIO* http_pump) +bool LLMessageSystem::checkAllMessages(LockMessageChecker& lmc, S64 frame_count, LLPumpIO* http_pump) { - if(checkMessages(frame_count)) + if(lmc.checkMessages(frame_count)) { return true; } diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index ea7c03cbd4..c476bdd242 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -61,6 +61,8 @@ #include "llstoredmessage.h" #include "boost/function.hpp" #include "llpounceable.h" +#include "llcoros.h" +#include LLCOROS_MUTEX_HEADER const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -199,6 +201,91 @@ public: virtual void complete(const LLHost& host, const LLUUID& agent) const = 0; }; +/** + * SL-12204: We've observed crashes when consumer code sets + * LLMessageSystem::mMessageReader, assuming that all subsequent processing of + * the current message will use the same mMessageReader value -- only to have + * a different coroutine sneak in and replace mMessageReader before + * completion. This is a limitation of sharing a stateful global resource for + * message parsing; instead code receiving a new message should instantiate a + * (trivially constructed) local message parser and use that. + * + * Until then, when one coroutine sets a particular LLMessageReader subclass + * as the current message reader, ensure that no other coroutine can replace + * it until the first coroutine has finished with its message. + * + * This is achieved with two helper classes. LLMessageSystem::mMessageReader + * is now an LLMessageReaderPointer instance, which can efficiently compare or + * dereference its contained LLMessageReader* but which cannot be directly + * assigned. To change the value of LLMessageReaderPointer, you must + * instantiate LockMessageReader with the LLMessageReader* you wish to make + * current. mMessageReader will have that value for the lifetime of the + * LockMessageReader instance, then revert to nullptr. Moreover, as its name + * implies, LockMessageReader locks the mutex in LLMessageReaderPointer so + * that any other coroutine instantiating LockMessageReader will block until + * the first coroutine has destroyed its instance. + */ +class LLMessageReaderPointer +{ +public: + LLMessageReaderPointer(): mPtr(nullptr) {} + // It is essential that comparison and dereferencing must be fast, which + // is why we don't check for nullptr when dereferencing. + LLMessageReader* operator->() const { return mPtr; } + bool operator==(const LLMessageReader* other) const { return mPtr == other; } + bool operator!=(const LLMessageReader* other) const { return ! (*this == other); } +private: + // Only LockMessageReader can set mPtr. + friend class LockMessageReader; + LLMessageReader* mPtr; + LLCoros::Mutex mMutex; +}; + +/** + * To set mMessageReader to nullptr: + * + * @code + * // use an anonymous instance that is destroyed immediately + * LockMessageReader(gMessageSystem->mMessageReader, nullptr); + * @endcode + * + * Why do we still require going through LockMessageReader at all? Because it + * would be Bad if any coroutine set mMessageReader to nullptr while another + * coroutine was still parsing a message. + */ +class LockMessageReader +{ +public: + LockMessageReader(LLMessageReaderPointer& var, LLMessageReader* instance): + mVar(var.mPtr), + mLock(var.mMutex) + { + mVar = instance; + } + // Some compilers reportedly fail to suppress generating implicit copy + // operations even though we have a move-only LockType data member. + LockMessageReader(const LockMessageReader&) = delete; + LockMessageReader& operator=(const LockMessageReader&) = delete; + ~LockMessageReader() + { + mVar = nullptr; + } +private: + // capture a reference to LLMessageReaderPointer::mPtr + decltype(LLMessageReaderPointer::mPtr)& mVar; + // while holding a lock on LLMessageReaderPointer::mMutex + LLCoros::LockType mLock; +}; + +/** + * LockMessageReader is great as long as you only need mMessageReader locked + * during a single LLMessageSystem function call. However, empirically the + * sequence from checkAllMessages() through processAcks() need mMessageReader + * locked to LLTemplateMessageReader. Enforce that by making them require an + * instance of LockMessageChecker. + */ +class LockMessageChecker; + class LLMessageSystem : public LLMessageSenderInterface { private: @@ -331,8 +418,8 @@ public: bool addCircuitCode(U32 code, const LLUUID& session_id); BOOL poll(F32 seconds); // Number of seconds that we want to block waiting for data, returns if data was received - BOOL checkMessages( S64 frame_count = 0 ); - void processAcks(F32 collect_time = 0.f); + BOOL checkMessages(LockMessageChecker&, S64 frame_count = 0 ); + void processAcks(LockMessageChecker&, F32 collect_time = 0.f); BOOL isMessageFast(const char *msg); BOOL isMessage(const char *msg) @@ -730,7 +817,7 @@ public: const LLSD& data); // Check UDP messages and pump http_pump to receive HTTP messages. - bool checkAllMessages(S64 frame_count, LLPumpIO* http_pump); + bool checkAllMessages(LockMessageChecker&, S64 frame_count, LLPumpIO* http_pump); // Moved to allow access from LLTemplateMessageDispatcher void clearReceiveState(); @@ -820,12 +907,13 @@ private: LLMessageBuilder* mMessageBuilder; LLTemplateMessageBuilder* mTemplateMessageBuilder; LLSDMessageBuilder* mLLSDMessageBuilder; - LLMessageReader* mMessageReader; + LLMessageReaderPointer mMessageReader; LLTemplateMessageReader* mTemplateMessageReader; LLSDMessageReader* mLLSDMessageReader; friend class LLMessageHandlerBridge; - + friend class LockMessageChecker; + bool callHandler(const char *name, bool trustedSource, LLMessageSystem* msg); @@ -841,6 +929,40 @@ private: // external hook into messaging system extern LLPounceable gMessageSystem; +// Implementation of LockMessageChecker depends on definition of +// LLMessageSystem, hence must follow it. +class LockMessageChecker: public LockMessageReader +{ +public: + LockMessageChecker(LLMessageSystem* msgsystem); + + // For convenience, provide forwarding wrappers so you can call (e.g.) + // checkAllMessages() on your LockMessageChecker instance instead of + // passing the instance to LLMessageSystem::checkAllMessages(). Use + // perfect forwarding to avoid having to maintain these wrappers in sync + // with the target methods. + template + bool checkAllMessages(ARGS&&... args) + { + return mMessageSystem->checkAllMessages(*this, std::forward(args)...); + } + + template + bool checkMessages(ARGS&&... args) + { + return mMessageSystem->checkMessages(*this, std::forward(args)...); + } + + template + void processAcks(ARGS&&... args) + { + return mMessageSystem->processAcks(*this, std::forward(args)...); + } + +private: + LLMessageSystem* mMessageSystem; +}; + // Must specific overall system version, which is used to determine // if a patch is available in the message template checksum verification. // Return true if able to initialize system. diff --git a/indra/llmessage/tests/llcoproceduremanager_test.cpp b/indra/llmessage/tests/llcoproceduremanager_test.cpp new file mode 100644 index 0000000000..9db13a37b5 --- /dev/null +++ b/indra/llmessage/tests/llcoproceduremanager_test.cpp @@ -0,0 +1,178 @@ +/** + * @file llcoproceduremanager_test.cpp + * @author Brad + * @date 2019-02 + * @brief LLCoprocedureManager unit test + * + * $LicenseInfo:firstyear=2019&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 "llwin32headers.h" + +#include "linden_common.h" +#include "llsdserialize.h" + +#include "../llcoproceduremanager.h" + +#include + +#include +#include +#include + +#include "../test/lltut.h" +#include "../test/sync.h" + + +#if LL_WINDOWS +// disable unreachable code warnings +#pragma warning(disable: 4702) +#endif + +LLCoreHttpUtil::HttpCoroutineAdapter::HttpCoroutineAdapter(std::string const&, unsigned int, unsigned int) +{ +} + +void LLCoreHttpUtil::HttpCoroutineAdapter::cancelSuspendedOperation() +{ +} + +LLCoreHttpUtil::HttpCoroutineAdapter::~HttpCoroutineAdapter() +{ +} + +LLCore::HttpRequest::HttpRequest() +{ +} + +LLCore::HttpRequest::~HttpRequest() +{ +} + +namespace tut +{ + struct coproceduremanager_test + { + coproceduremanager_test() + { + } + + ~coproceduremanager_test() + { + LLCoprocedureManager::instance().close(); + } + }; + typedef test_group coproceduremanager_t; + typedef coproceduremanager_t::object coproceduremanager_object_t; + tut::coproceduremanager_t tut_coproceduremanager("LLCoprocedureManager"); + + + template<> template<> + void coproceduremanager_object_t::test<1>() + { + Sync sync; + int foo = 0; + LLUUID queueId = LLCoprocedureManager::instance().enqueueCoprocedure("PoolName", "ProcName", + [&foo, &sync] (LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t & ptr, const LLUUID & id) { + sync.bump(); + foo = 1; + }); + + sync.yield(); + ensure_equals("coprocedure failed to update foo", foo, 1); + + LLCoprocedureManager::instance().close("PoolName"); + } + + template<> template<> + void coproceduremanager_object_t::test<2>() + { + const size_t capacity = 2; + boost::fibers::buffered_channel> chan(capacity); + + boost::fibers::fiber worker([&chan]() { + chan.value_pop()(); + }); + + chan.push([]() { + LL_INFOS("Test") << "test 1" << LL_ENDL; + }); + + worker.join(); + } + + template<> template<> + void coproceduremanager_object_t::test<3>() + { + boost::fibers::unbuffered_channel> chan; + + boost::fibers::fiber worker([&chan]() { + chan.value_pop()(); + }); + + chan.push([]() { + LL_INFOS("Test") << "test 1" << LL_ENDL; + }); + + worker.join(); + } + + template<> template<> + void coproceduremanager_object_t::test<4>() + { + boost::fibers::buffered_channel> chan(4); + + boost::fibers::fiber worker([&chan]() { + std::function f; + + // using namespace std::chrono_literals; + // const auto timeout = 5s; + // boost::fibers::channel_op_status status; + while (chan.pop(f) != boost::fibers::channel_op_status::closed) + { + LL_INFOS("CoWorker") << "got coproc" << LL_ENDL; + f(); + } + LL_INFOS("CoWorker") << "got closed" << LL_ENDL; + }); + + int counter = 0; + + for (int i = 0; i < 5; ++i) + { + LL_INFOS("CoMain") << "pushing coproc " << i << LL_ENDL; + chan.push([&counter]() { + LL_INFOS("CoProc") << "in coproc" << LL_ENDL; + ++counter; + }); + } + + LL_INFOS("CoMain") << "closing channel" << LL_ENDL; + chan.close(); + + LL_INFOS("CoMain") << "joining worker" << LL_ENDL; + worker.join(); + + LL_INFOS("CoMain") << "checking count" << LL_ENDL; + ensure_equals("coprocedure failed to update counter", counter, 5); + } +} // namespace tut diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index b35105508e..14fd3e5522 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -1148,6 +1148,10 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mDebugMessageLevel = message.getValue("message_level"); mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_DEBUG_MESSAGE); } + else if (message_name == "tooltip_text") + { + mHoverText = message.getValue("tooltip"); + } else { LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; diff --git a/indra/llplugin/llpluginmessagepipe.h b/indra/llplugin/llpluginmessagepipe.h index c3498beac0..9d5835eb82 100644 --- a/indra/llplugin/llpluginmessagepipe.h +++ b/indra/llplugin/llpluginmessagepipe.h @@ -31,6 +31,7 @@ #include "lliosocket.h" #include "llthread.h" +#include "llmutex.h" class LLPluginMessagePipe; diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index 2beba4e591..3158cb7e12 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -71,6 +71,7 @@ if(WINDOWS) endif(WINDOWS) target_link_libraries(SLPlugin + ${LEGACY_STDIO_LIBS} ${LLPLUGIN_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index b1f62e0c87..fa1e01d22c 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -86,7 +86,7 @@ target_link_libraries(llprimitive ${LLXML_LIBRARIES} ${LLPHYSICSEXTENSIONS_LIBRARIES} ${LLCHARACTER_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ) diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index f7ef234ad5..d774f9276d 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -31,6 +31,7 @@ #include "llconvexdecomposition.h" #include "llsdserialize.h" #include "llvector4a.h" +#include "llcontrol.h" #ifdef LL_USESYSTEMLIBS # include @@ -38,6 +39,8 @@ # include "zlib/zlib.h" #endif +extern LLControlGroup gSavedSettings; + std::string model_names[] = { "lowest_lod", @@ -268,7 +271,6 @@ void LLModel::normalizeVolumeFaces() LLVector4a inv_scale(1.f); inv_scale.div(scale); - for (U32 i = 0; i < mVolumeFaces.size(); ++i) { LLVolumeFace& face = mVolumeFaces[i]; @@ -294,7 +296,17 @@ void LLModel::normalizeVolumeFaces() pos[j].mul(scale); if (norm && !norm[j].equals3(LLVector4a::getZero())) { - norm[j].mul(inv_scale); +// BUG-228952 - bad vertex normal scaling on mesh asset import + // norm[j].mul(inv_scale); + if (!gSavedSettings.getBOOL("FSMeshImportScaleFixup")) + { + norm[j].mul(inv_scale); + } + else + { + norm[j].mul(scale); + } +// norm[j].normalize3(); } } @@ -1588,7 +1600,7 @@ void LLModel::Decomposition::fromLLSD(LLSD& decomp) range = max-min; - U16 count = position.size()/6; + U16 count = (U16)(position.size()/6); for (U32 j = 0; j < count; ++j) { diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index a14bc744bb..e06e783257 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2466,9 +2466,8 @@ void LLGLNamePool::release(GLuint name) //static void LLGLNamePool::upkeepPools() { - for (tracker_t::instance_iter iter = beginInstances(); iter != endInstances(); ++iter) + for (auto& pool : instance_snapshot()) { - LLGLNamePool & pool = *iter; pool.upkeep(); } } @@ -2476,9 +2475,8 @@ void LLGLNamePool::upkeepPools() //static void LLGLNamePool::cleanupPools() { - for (tracker_t::instance_iter iter = beginInstances(); iter != endInstances(); ++iter) + for (auto& pool : instance_snapshot()) { - LLGLNamePool & pool = *iter; pool.cleanup(); } } diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index b31fb2d6ef..4a995fd60c 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -698,6 +698,14 @@ void LLVertexBuffer::drawElements(U32 mode, const S32 num_vertices, const LLVect // { llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL); + + // FIRE-29679 trap empty calls that cause crashes when rezzing in OpenSim. + if(pos == nullptr || indicesp == nullptr ) + { + LL_WARNS() << "Called drawElements with null pos or null indices" << LL_ENDL; + return; + } + // // Crash fix due to invalid calls to drawElements by Drake Arconis if (num_vertices <= 0) @@ -1645,7 +1653,12 @@ void LLVertexBuffer::setupVertexArray() //glVertexattribIPointer requires GLSL 1.30 or later if (gGLManager.mGLSLVersionMajor > 1 || gGLManager.mGLSLVersionMinor >= 30) { - glVertexAttribIPointer(i, attrib_size[i], attrib_type[i], sTypeSize[i], (const GLvoid*) (ptrdiff_t)mOffsets[i]); + // nat 2018-10-24: VS 2017 also notices the issue + // described below, and warns even with reinterpret_cast. + // Cast via intptr_t to make it painfully obvious to the + // compiler that we're doing this intentionally. + glVertexAttribIPointer(i, attrib_size[i], attrib_type[i], sTypeSize[i], + reinterpret_cast(intptr_t(mOffsets[i]))); } #endif } @@ -1660,7 +1673,7 @@ void LLVertexBuffer::setupVertexArray() // rather than as an actual pointer, so it's okay. glVertexAttribPointerARB(i, attrib_size[i], attrib_type[i], attrib_normalized[i], sTypeSize[i], - reinterpret_cast(mOffsets[i])); + reinterpret_cast(intptr_t(mOffsets[i]))); } } else diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 0778b460ee..58444926c9 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -307,7 +307,7 @@ if(LL_TESTS) set(test_libs llui llmessage llcorehttp llcommon ${HUNSPELL_LIBRARY} ${LLCOMMON_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${WINDOWS_LIBRARIES}) if(NOT LINUX) LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "${test_libs}") diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 29044c7c45..42ab9b87dd 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -342,7 +342,7 @@ void LLAccordionCtrl::addCollapsibleCtrl(LLView* view, bool aArrange) addChild(accordion_tab); mAccordionTabs.push_back(accordion_tab); - accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, mAccordionTabs.size() - 1) ); + accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, (S16)(mAccordionTabs.size() - 1)) ); // If adding a lot of controls rapidly, calling arrange will cost a lot of times, as it's running through n! controls. // In that case we can avvoid calling arrange over and over and just call it once when finished. diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 78db4e07ea..e8c5260d51 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -747,9 +747,9 @@ void LLConsole::onUrlLabelCallback(const LLUUID& paragraph_id, const std::string // static void LLConsole::updateClass() { - for (instance_iter it = beginInstances(); it != endInstances(); ++it) + for (auto& con : instance_snapshot()) { - it->update(); + con.update(); } } diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 7daf36395c..2c8a66145f 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -713,10 +713,10 @@ void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) //static void LLLayoutStack::updateClass() { - for (instance_iter it = beginInstances(); it != endInstances(); ++it) + for (auto& layout : instance_snapshot()) { - it->updateLayout(); - it->mAnimatedThisFrame = false; + layout.updateLayout(); + layout.mAnimatedThisFrame = false; } } diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 7d79916764..1a6b320a4c 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -752,42 +752,24 @@ public: virtual ~LLNotificationChannelBase() {} // you can also connect to a Channel, so you can be notified of // changes to this channel - template - LLBoundListener connectChanged(const LISTENER& slot) + LLBoundListener connectChanged(const LLEventListener& slot) { - // Examine slot to see if it binds an LLEventTrackable subclass, or a - // boost::shared_ptr to something, or a boost::weak_ptr to something. // Call this->connectChangedImpl() to actually connect it. - return LLEventDetail::visit_and_connect(slot, - boost::bind(&LLNotificationChannelBase::connectChangedImpl, - this, - _1)); + return connectChangedImpl(slot); } - template - LLBoundListener connectAtFrontChanged(const LISTENER& slot) + LLBoundListener connectAtFrontChanged(const LLEventListener& slot) { - return LLEventDetail::visit_and_connect(slot, - boost::bind(&LLNotificationChannelBase::connectAtFrontChangedImpl, - this, - _1)); + return connectAtFrontChangedImpl(slot); } - template - LLBoundListener connectPassedFilter(const LISTENER& slot) + LLBoundListener connectPassedFilter(const LLEventListener& slot) { // see comments in connectChanged() - return LLEventDetail::visit_and_connect(slot, - boost::bind(&LLNotificationChannelBase::connectPassedFilterImpl, - this, - _1)); + return connectPassedFilterImpl(slot); } - template - LLBoundListener connectFailedFilter(const LISTENER& slot) + LLBoundListener connectFailedFilter(const LLEventListener& slot) { // see comments in connectChanged() - return LLEventDetail::visit_and_connect(slot, - boost::bind(&LLNotificationChannelBase::connectFailedFilterImpl, - this, - _1)); + return connectFailedFilterImpl(slot); } // use this when items change or to add a new one diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp index be26416cbb..e73ba1fbe9 100644 --- a/indra/llui/llnotificationslistener.cpp +++ b/indra/llui/llnotificationslistener.cpp @@ -127,18 +127,16 @@ void LLNotificationsListener::listChannels(const LLSD& params) const { LLReqID reqID(params); LLSD response(reqID.makeResponse()); - for (LLNotificationChannel::instance_iter cmi(LLNotificationChannel::beginInstances()), - cmend(LLNotificationChannel::endInstances()); - cmi != cmend; ++cmi) + for (auto& cm : LLNotificationChannel::instance_snapshot()) { LLSD channelInfo, parents; - BOOST_FOREACH(const std::string& parent, cmi->getParents()) + for (const std::string& parent : cm.getParents()) { parents.append(parent); } channelInfo["parents"] = parents; channelInfo["parent"] = parents.size()? parents[0] : ""; - response[cmi->getName()] = channelInfo; + response[cm.getName()] = channelInfo; } LLEventPumps::instance().obtain(params["reply"]).post(response); } diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 3d7afdb1a7..2d63816113 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -2514,6 +2514,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) if (selected_item) { handleRightMouseDown(selected_item->getRect().getCenterX(), selected_item->getRect().getCenterY(), MASK_NONE); + handled = TRUE; } break; } @@ -2523,11 +2524,139 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) } } // TODO: multiple: shift-up, shift-down, shift-home, shift-end, select all + // Let's just do this! + else if (mask == MASK_CONTROL) + { + switch (key) + { + case 'A': + if (canSelectAll()) + { + selectAll(); + handled = TRUE; + } + break; + default: + break; + } + } + else if (mask == MASK_SHIFT) + { + switch (key) + { + case KEY_UP: + if (mAllowKeyboardMovement || hasFocus()) + { + auto selected_items = getAllSelected(); + auto last = selected_items.back(); + + if (mLastSelected == last && selected_items.size() > 1) + { + deselectItem(last); + mLastSelected = getAllSelected().back(); // Use updated selection + } + else + { + auto items = getAllData(); + auto first = std::find(items.begin(), items.end(), selected_items.front()); + if (first != items.end() && first > items.begin()) + { + selectItem(*(--first), FALSE); + } + } + + handled = TRUE; + } + + break; + case KEY_DOWN: + if (mAllowKeyboardMovement || hasFocus()) + { + auto selected_items = getAllSelected(); + auto first = selected_items.front(); + + if (mLastSelected == first && selected_items.size() > 1) + { + deselectItem(first); + mLastSelected = getAllSelected().front(); // Use updated selection + } + else + { + auto items = getAllData(); + auto last = std::find(items.begin(), items.end(), selected_items.back()); + if (last != items.end() && last < items.end() - 1) + { + selectItem(*(++last), FALSE); + } + } + + handled = TRUE; + } + break; + case KEY_HOME: + if (mAllowKeyboardMovement || hasFocus()) + { + auto items = getAllData(); + auto first = std::find(items.begin(), items.end(), getAllSelected().front()); + for (auto it = items.begin(); it != items.end(); ++it) + { + if (it <= first) + { + selectItem(*it, FALSE); + } + else + { + deselectItem(*it); + } + } + + handled = TRUE; + } + break; + case KEY_END: + if (mAllowKeyboardMovement || hasFocus()) + { + auto items = getAllData(); + auto last = std::find(items.begin(), items.end(), getAllSelected().back()); + for (auto it = items.begin(); it != items.end(); ++it) + { + if (it >= last) + { + selectItem(*it, FALSE); + } + else + { + deselectItem(*it); + } + } + + handled = TRUE; + } + break; + default: + break; + } + } + // } return handled; } +// Needed for keyboard selection in radar +void LLScrollListCtrl::setLastSelectedItem(const LLUUID& id) +{ + for (auto item : getAllSelected()) + { + if (item->getUUID() == id) + { + mLastSelected = item; + break; + } + } +} +// + BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) { if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 40eaa4e9d4..aa176e318b 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -275,6 +275,7 @@ public: std::vector getAllSelected() const; S32 getNumSelected() const; LLScrollListItem* getLastSelectedItem() const { return mLastSelected; } + void setLastSelectedItem(const LLUUID& id); // Needed for keyboard selection in radar // iterate over all items LLScrollListItem* getFirstData() const; diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index 3d1820059c..8c4d3a5c6b 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -42,7 +42,10 @@ LLStyle::Params::Params() image("image"), link_href("href"), is_link("is_link"), - is_name_slurl("is_name_slurl") // FIRE-11330 + // Don't highlight URLs on hover if font style contains underline + use_default_link_style("use_default_link_style", true), + is_chat_header("is_chat_header", false) + // {} diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index 784a3c9ba1..2fe9117509 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -48,7 +48,10 @@ public: Optional image; Optional link_href; Optional is_link; - Optional is_name_slurl; // FIRE-11330 + // Don't highlight URLs on hover if font style contains underline + Optional use_default_link_style; + Optional is_chat_header; + // Params(); }; LLStyle(const Params& p = Params()); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 61bb4f8fc9..d0f4f69023 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -2256,9 +2256,21 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para end = match.getEnd()+1; LLStyle::Params link_params(style_params); - // FIRE-11330 - if it's a name, don't stylize it like a url - if (!input_params.is_name_slurl) + // Overwrite only if we explicitly allow it + //link_params.overwriteFrom(match.getStyle()); + if (input_params.use_default_link_style) + { + LLUIColor color(link_params.color()); + LLUIColor readonly_color(link_params.readonly_color()); + bool is_chat_header = link_params.is_chat_header(); link_params.overwriteFrom(match.getStyle()); + if (is_chat_header) + { + link_params.color = color; + link_params.readonly_color = readonly_color; + } + } + // // output the text before the Url if (start > 0) @@ -2297,7 +2309,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para // FIRE-11437 - Don't supress font style for chat history name links //appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly()); appendAndHighlightTextImpl(match.getLabel(), part, link_params, - input_params.is_name_slurl ? false : match.underlineOnHoverOnly()); + input_params.use_default_link_style ? match.underlineOnHoverOnly() : false); // bool tooltip_required = !match.getTooltip().empty(); @@ -2318,7 +2330,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para static LLUIColor query_part_color = LLUIColorTable::getInstance()->getColor("UriQueryPartColor", LLColor4::grey); link_params.color = query_part_color; link_params.readonly_color = query_part_color; - appendAndHighlightTextImpl(label, part, link_params, input_params.is_name_slurl ? false : match.underlineOnHoverOnly()); + appendAndHighlightTextImpl(label, part, link_params, input_params.use_default_link_style ? match.underlineOnHoverOnly() : false); // // set the tooltip for the query part of url diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index 03d946f1b7..4bda22978a 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -174,7 +174,7 @@ public: { LL_WARNS() << "Widget in " << filename << " was of type " << typeid(view).name() << " instead of expected type " << typeid(T).name() << LL_ENDL; -#if LL_DARWIN +#if __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdelete-incomplete" delete view; diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index f302b67d35..930c16e2a8 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -1245,7 +1245,7 @@ LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) c { // But if BOTH path and name bring a separator, we need not add one. // Moreover, we should actually skip the leading separator of 'name'. - return SepOff(false, seplen); + return SepOff(false, (unsigned short)seplen); } // Here we know that either path_ends_sep or name_starts_sep is true -- // but not both. So don't add a separator, and don't skip any characters: diff --git a/indra/llvfs/llvfs.h b/indra/llvfs/llvfs.h index 4ca5273915..9258c77365 100644 --- a/indra/llvfs/llvfs.h +++ b/indra/llvfs/llvfs.h @@ -31,6 +31,7 @@ #include "lluuid.h" #include "llassettype.h" #include "llthread.h" +#include "llmutex.h" enum EVFSValid { diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 34cb9c25ee..f52a61024b 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -11,7 +11,6 @@ project(llwindow) include(00-Common) -include(DirectX) include(DragDrop) include(LLCommon) include(LLImage) @@ -30,7 +29,6 @@ include_directories( ${LLVFS_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} - ${DIRECTX_INCLUDE_DIR} ) include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 5e6868ec0e..f43ca28c13 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -471,9 +471,8 @@ LLCoordCommon LL_COORD_TYPE_WINDOW::convertToCommon() const { const LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); - LLWindow* windowp = &(*LLWindow::beginInstances()); LLCoordGL out; - windowp->convertCoords(self, &out); + LLWindow::instance_snapshot().begin()->convertCoords(self, &out); return out.convert(); } @@ -481,18 +480,16 @@ void LL_COORD_TYPE_WINDOW::convertFromCommon(const LLCoordCommon& from) { LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); - LLWindow* windowp = &(*LLWindow::beginInstances()); LLCoordGL from_gl(from); - windowp->convertCoords(from_gl, &self); + LLWindow::instance_snapshot().begin()->convertCoords(from_gl, &self); } LLCoordCommon LL_COORD_TYPE_SCREEN::convertToCommon() const { const LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); - LLWindow* windowp = &(*LLWindow::beginInstances()); LLCoordGL out; - windowp->convertCoords(self, &out); + LLWindow::instance_snapshot().begin()->convertCoords(self, &out); return out.convert(); } @@ -500,7 +497,6 @@ void LL_COORD_TYPE_SCREEN::convertFromCommon(const LLCoordCommon& from) { LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); - LLWindow* windowp = &(*LLWindow::beginInstances()); LLCoordGL from_gl(from); - windowp->convertCoords(from_gl, &self); + LLWindow::instance_snapshot().begin()->convertCoords(from_gl, &self); } diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index cf3217d9c3..e72fdcf038 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -242,8 +242,6 @@ public: LLControlGroup(const std::string& name); ~LLControlGroup(); void cleanup(); - - typedef LLInstanceTracker::instance_iter instance_iter; LLControlVariablePtr getControl(const std::string& name); diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index f6c4dfb59d..95637c9a28 100644 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -77,7 +77,7 @@ target_link_libraries(mac-crash-logger ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${BOOST_CONTEXT_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ) add_custom_command( diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index e889b43b9e..c33af7011a 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -63,6 +63,7 @@ private: void onConsoleMessageCallback(std::string message, std::string source, int line); void onStatusMessageCallback(std::string value); void onTitleChangeCallback(std::string title); + void onTooltipCallback(std::string text); void onLoadStartCallback(); void onRequestExitCallback(); void onLoadEndCallback(int httpStatusCode); @@ -72,6 +73,7 @@ private: bool onHTTPAuthCallback(const std::string host, const std::string realm, std::string& username, std::string& password); void onCursorChangedCallback(dullahan::ECursorType type); const std::vector onFileDialog(dullahan::EFileDialogType dialog_type, const std::string dialog_title, const std::string default_file, const std::string dialog_accept_filter, bool& use_default); + bool onJSDialogCallback(const std::string origin_url, const std::string message_text, const std::string default_prompt_text); void postDebugMessage(const std::string& msg); void authResponse(LLPluginMessage &message); @@ -88,6 +90,8 @@ private: bool mPluginsEnabled; bool mJavascriptEnabled; bool mDisableGPU; + bool mDisableNetworkService; + bool mUseMockKeyChain; std::string mUserAgentSubtring; std::string mAuthUsername; std::string mAuthPassword; @@ -96,7 +100,6 @@ private: bool mCanCopy; bool mCanPaste; std::string mCachePath; - std::string mCookiePath; std::string mCefLogFile; bool mCefLogVerbose; std::vector mPickedFiles; @@ -120,6 +123,8 @@ MediaPluginBase(host_send_func, host_user_data) mPluginsEnabled = false; mJavascriptEnabled = true; mDisableGPU = false; + mDisableNetworkService = true; + mUseMockKeyChain = true; mUserAgentSubtring = ""; mAuthUsername = ""; mAuthPassword = ""; @@ -128,7 +133,6 @@ MediaPluginBase(host_send_func, host_user_data) mCanCopy = false; mCanPaste = false; mCachePath = ""; - mCookiePath = ""; mCefLogFile = ""; mCefLogVerbose = false; mPickedFiles.clear(); @@ -209,6 +213,12 @@ void MediaPluginCEF::onTitleChangeCallback(std::string title) sendMessage(message); } +void MediaPluginCEF::onTooltipCallback(std::string text) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "tooltip_text"); + message.setValue("tooltip", text); + sendMessage(message); +} //////////////////////////////////////////////////////////////////////////////// // void MediaPluginCEF::onLoadStartCallback() @@ -356,6 +366,14 @@ const std::vector MediaPluginCEF::onFileDialog(dullahan::EFileDialo return std::vector(); } +//////////////////////////////////////////////////////////////////////////////// +// +bool MediaPluginCEF::onJSDialogCallback(const std::string origin_url, const std::string message_text, const std::string default_prompt_text) +{ + // return true indicates we suppress the JavaScript alert UI entirely + return true; +} + //////////////////////////////////////////////////////////////////////////////// // void MediaPluginCEF::onCursorChangedCallback(dullahan::ECursorType type) @@ -432,6 +450,8 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { mCEFLib->update(); + mVolumeCatcher.pump(); + // this seems bad but unless the state changes (it won't until we figure out // how to get CEF to tell us if copy/cut/paste is available) then this function // will return immediately @@ -492,6 +512,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string) mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1)); mCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1)); + mCEFLib->setOnTooltipCallback(std::bind(&MediaPluginCEF::onTooltipCallback, this, std::placeholders::_1)); mCEFLib->setOnLoadStartCallback(std::bind(&MediaPluginCEF::onLoadStartCallback, this)); mCEFLib->setOnLoadEndCallback(std::bind(&MediaPluginCEF::onLoadEndCallback, this, std::placeholders::_1)); mCEFLib->setOnLoadErrorCallback(std::bind(&MediaPluginCEF::onLoadError, this, std::placeholders::_1, std::placeholders::_2)); @@ -501,27 +522,25 @@ void MediaPluginCEF::receiveMessage(const char* message_string) mCEFLib->setOnFileDialogCallback(std::bind(&MediaPluginCEF::onFileDialog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); mCEFLib->setOnCursorChangedCallback(std::bind(&MediaPluginCEF::onCursorChangedCallback, this, std::placeholders::_1)); mCEFLib->setOnRequestExitCallback(std::bind(&MediaPluginCEF::onRequestExitCallback, this)); - + mCEFLib->setOnJSDialogCallback(std::bind(&MediaPluginCEF::onJSDialogCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + dullahan::dullahan_settings settings; settings.accept_language_list = mHostLanguage; settings.background_color = 0xffffffff; settings.cache_enabled = true; settings.cache_path = mCachePath; -#if (DULLAHAN_VERSION_MAJOR*100+DULLAHAN_VERSION_MINOR) < 106 - settings.cookie_store_path = mCookiePath; -#endif settings.cookies_enabled = mCookiesEnabled; settings.disable_gpu = mDisableGPU; +#if LL_DARWIN + settings.disable_network_service = mDisableNetworkService; + settings.use_mock_keychain = mUseMockKeyChain; +#endif settings.flash_enabled = mPluginsEnabled; settings.flip_mouse_y = false; settings.flip_pixels_y = true; settings.frame_rate = 60; - // With the latest CEF this does more worse than good. It will allow the viewer to control the audio level (apparently); But it will also break a lot of sites (among then twitch, netflix, spotify). - // Right now with the choice between lots of broken sites or volumes for me it's rather the sites being usable. - // settings.force_wave_audio = true; - settings.force_wave_audio = false; - // + settings.force_wave_audio = true; settings.initial_height = 1024; settings.initial_width = 1024; @@ -570,7 +589,6 @@ void MediaPluginCEF::receiveMessage(const char* message_string) std::string user_data_path_cookies = message_in.getValue("cookies_path"); mCachePath = user_data_path_cache + "cef_cache"; - mCookiePath = user_data_path_cookies + "cef_cookies"; mCefLogFile = message_in.getValue("cef_log_file"); mCefLogVerbose = message_in.getValueBoolean("cef_verbose_log"); } @@ -670,10 +688,9 @@ void MediaPluginCEF::receiveMessage(const char* message_string) else if (message_name == "scroll_event") { // Mouse coordinates for cef to be able to scroll 'containers' -#if (DULLAHAN_VERSION_MAJOR*100+DULLAHAN_VERSION_MINOR) >= 106 S32 x = message_in.getValueS32("x"); S32 y = message_in.getValueS32("y"); -#endif + // Wheel's clicks S32 delta_x = message_in.getValueS32("clicks_x"); S32 delta_y = message_in.getValueS32("clicks_y"); @@ -681,11 +698,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string) delta_x *= -scaling_factor; delta_y *= -scaling_factor; -#if (DULLAHAN_VERSION_MAJOR*100+DULLAHAN_VERSION_MINOR) >= 106 mCEFLib->mouseWheel(x, y, delta_x, delta_y); -#else - mCEFLib->mouseWheel(delta_x, delta_y); -#endif } else if (message_name == "text_event") { @@ -960,7 +973,12 @@ void MediaPluginCEF::checkEditState() void MediaPluginCEF::setVolume() { + //mVolumeCatcher.setVolume(mCurVolume); +#if (DULLAHAN_VERSION_MAJOR*100 + DULLAHAN_VERSION_MINOR) >= 108 + mCEFLib->setVolume(mCurVolume); +#else mVolumeCatcher.setVolume(mCurVolume); +#endif } //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/media_plugins/cef/windows_volume_catcher.cpp b/indra/media_plugins/cef/windows_volume_catcher.cpp index 6953ad3ab8..7a36123a11 100644 --- a/indra/media_plugins/cef/windows_volume_catcher.cpp +++ b/indra/media_plugins/cef/windows_volume_catcher.cpp @@ -27,8 +27,9 @@ */ #include "volume_catcher.h" -#include #include "llsingleton.h" +#include +#include class VolumeCatcherImpl : public LLSingleton { LLSINGLETON(VolumeCatcherImpl); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3c793a6b51..398d1da456 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -15,7 +15,6 @@ include(BuildPackagesInfo) include(BuildVersion) include(CMakeCopyIfDifferent) include(DBusGlib) -include(DirectX) include(DragDrop) include(EXPAT) include(FMODSTUDIO) @@ -850,6 +849,7 @@ set(viewer_SOURCE_FILES qtoolalign.cpp quickprefs.cpp rlvactions.cpp + rlvenvironment.cpp rlvhandler.cpp rlvhelper.cpp rlvcommon.cpp @@ -1607,6 +1607,7 @@ set(viewer_HEADER_FILES pieslice.h pipeline.h rlvactions.h + rlvenvironment.h rlvdefines.h rlvhandler.h rlvhelper.h @@ -1789,7 +1790,9 @@ if (LINUX) # # Get rid of memory limit exceeded for -fvar-tracking-assignments. - SET_SOURCE_FILES_PROPERTIES(llviewermenu.cpp PROPERTIES COMPILE_FLAGS -fno-var-tracking-assignments) + if (NOT CMAKE_CXX_COMPILER MATCHES ".*clang") + SET_SOURCE_FILES_PROPERTIES(llviewermenu.cpp PROPERTIES COMPILE_FLAGS -fno-var-tracking-assignments) + endif() # endif (LINUX) @@ -1931,20 +1934,12 @@ if (WINDOWS) list(APPEND viewer_SOURCE_FILES ${viewer_RESOURCE_FILES}) endif (NOT USESYSTEMLIBS) - find_library(DINPUT_LIBRARY dinput8 ${DIRECTX_LIBRARY_DIR}) - find_library(DXGUID_LIBRARY dxguid ${DIRECTX_LIBRARY_DIR}) - mark_as_advanced( - DINPUT_LIBRARY - DXGUID_LIBRARY - ) - # see EXP-1765 - theory is opengl32.lib needs to be included before gdi32.lib (windows libs) set(viewer_LIBRARIES opengl32 ${WINDOWS_LIBRARIES} comdlg32 - ${DINPUT_LIBRARY} - ${DXGUID_LIBRARY} + dxguid kernel32 odbc32 odbccp32 @@ -2192,6 +2187,11 @@ if (WINDOWS) # be met. I'm looking forward to a source-code split-up project next year that will address this kind of thing. # In the meantime, if you have any ideas on how to easily maintain one list, either here or in viewer_manifest.py # and have the build deps get tracked *please* tell me about it. + # nat: https://cmake.org/cmake/help/v3.14/command/file.html + # "For example, the code + # file(STRINGS myfile.txt myfile) + # stores a list in the variable myfile in which each item is a line from the input file." + # And of course it's straightforward to read a text file in Python. set(COPY_INPUT_DEPENDENCIES # The following commented dependencies are determined at variably at build time. Can't do this here. @@ -2210,12 +2210,6 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/Release/openjpeg.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjpeg.dll ${SHARED_LIB_STAGING_DIR}/Debug/openjpegd.dll - ${SHARED_LIB_STAGING_DIR}/Release/msvcr100.dll - ${SHARED_LIB_STAGING_DIR}/Release/msvcp100.dll - ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/msvcr100.dll - ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/msvcp100.dll - ${SHARED_LIB_STAGING_DIR}/Debug/msvcr100d.dll - ${SHARED_LIB_STAGING_DIR}/Debug/msvcp100d.dll ${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll @@ -2371,6 +2365,10 @@ elseif (DARWIN) set_target_properties(${VIEWER_BINARY_NAME} PROPERTIES LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" + # Force the SDK version in the linked executable to be 10.12. This will fool + # macOS into using the pre-Mojave display system, avoiding the blurry display that + # otherwise occurs when upscaling the viewer to Retina resolution levels. + LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -platform_version -Xlinker macos -Xlinker ${CMAKE_OSX_DEPLOYMENT_TARGET} -Xlinker 10.12" ) else (WINDOWS) # Linux @@ -2409,6 +2407,9 @@ endif (WINDOWS) # modern version. target_link_libraries(${VIEWER_BINARY_NAME} + ${LEGACY_STDIO_LIBS} + ${PNG_PRELOAD_ARCHIVES} + ${ZLIB_PRELOAD_ARCHIVES} ${URIPARSER_PRELOAD_ARCHIVES} ${GOOGLE_PERFTOOLS_LIBRARIES} ${LLAUDIO_LIBRARIES} @@ -2433,6 +2434,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${viewer_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_WAVE_LIBRARY} #FS specific ${BOOST_THREAD_LIBRARY} #FS specific ${BOOST_CONTEXT_LIBRARY} @@ -2735,7 +2737,7 @@ if (PACKAGE AND (RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) AND VIE DEPENDS generate_breakpad_symbols.py VERBATIM) -## add_custom_target(generate_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" ${VIEWER_BINARY_NAME} "${VIEWER_COPY_MANIFEST}") + add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}") add_custom_target(generate_symbols DEPENDS "${VIEWER_SYMBOL_FILE}") add_dependencies(generate_symbols ${VIEWER_BINARY_NAME}) if (WINDOWS OR LINUX) @@ -2916,6 +2918,7 @@ if (LL_TESTS) set_source_files_properties( lllogininstance.cpp PROPERTIES + LL_TEST_ADDITIONAL_SOURCE_FILES llversioninfo.cpp LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_SYSTEM_LIBRARY}" ) @@ -2965,7 +2968,7 @@ if (LL_TESTS) ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${LIBRT_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ) diff --git a/indra/newview/Info-Firestorm.plist b/indra/newview/Info-Firestorm.plist index 9358cae7dc..18341c8860 100644 --- a/indra/newview/Info-Firestorm.plist +++ b/indra/newview/Info-Firestorm.plist @@ -82,5 +82,7 @@ ${MACOSX_BUNDLE_NSPRINCIPAL_CLASS} NSMainNibFile ${MACOSX_BUNDLE_NSMAIN_NIB_FILE} - + NSHighResolutionCapable + + diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 49df80bfeb..3d05e8cfb4 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.4.4 +6.4.6 diff --git a/indra/newview/aoengine.cpp b/indra/newview/aoengine.cpp index 37123fc828..424af81590 100644 --- a/indra/newview/aoengine.cpp +++ b/indra/newview/aoengine.cpp @@ -1910,11 +1910,6 @@ BOOL AOEngine::importNotecard(const LLInventoryItem* item) if (item->getAssetUUID().notNull()) { mImportSet = new AOSet(item->getParentUUID()); - if (!mImportSet) - { - LLNotificationsUtil::add("AOImportCreateSetFailed", LLSD()); - return FALSE; - } mImportSet->setName(item->getName()); LLUUID* newUUID = new LLUUID(item->getAssetUUID()); diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index b28a6f4679..ec4458ee66 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -631,6 +631,8 @@ tooltip_ref="Command_Environments_Tooltip" execute_function="Floater.ToggleOrBringToFront" execute_parameters="my_environments" + is_enabled_function="RLV.EnableIfNot" + is_enabled_parameters="setenv" is_running_function="Floater.IsOpen" is_running_parameters="my_environments" /> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9dbe9a1b6a..381163781e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9069,6 +9069,17 @@ Value 0 + FSMeshImportScaleFixup + + Comment + Adjust normals on import, fixes underlying issue, not recommended in case underlying rendering gets fixed. + Persist + 1 + Type + Boolean + Value + 0 + MeshUploadLogXML Comment @@ -11159,7 +11170,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 0.125 + 0.02 MediaRollOffMin @@ -11170,7 +11181,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 10.0 + 40.0 MediaRollOffMax @@ -11181,7 +11192,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 30.0 + 80.0 RecentItemsSortOrder @@ -11830,7 +11841,20 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 - RenderCubeMap + OnlyShowSelectedNormals + + Comment + Only render the normals for selected objects. in conjunction with render debug normals + Persist + 1 + Type + Boolean + Value + 1 + Backup + 0 + + RenderCubeMap Comment Whether we can render the cube map or not @@ -25353,7 +25377,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type Boolean Value - 0 + 1 FSRestrictMaxTextureSize @@ -25419,8 +25443,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type Boolean Value - 1 + 0 - diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index e7e7fa96a0..afbd5fca7f 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -11,6 +11,17 @@ Value 0.0 + AvatarHoverOffsetStepSize + + Comment + Step-size for the zoffset_up and zoffset_down command line commands to change avatar Z position. + Persist + 1 + Type + F32 + Value + 0.05 + RLVaLoginLastLocation Comment diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl index 9f9749394e..85cd345128 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl @@ -46,7 +46,10 @@ void main() vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy; vec3 n = normalize(normal_matrix * normal); - vec3 t = normalize(normal_matrix * tangent.xyz); +// tangents should not use the inv_transpose matrix + // vec3 t = normalize(normal_matrix * tangent.xyz); + vec3 t = normalize((modelview_projection_matrix * vec4(tangent.xyz,0)).xyz); +// vec3 b = cross(n, t) * tangent.w; vary_mat0 = vec3(t.x, b.x, n.x); diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl index 7e29ada205..093da8d26d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl @@ -122,7 +122,10 @@ vary_normal = n; #else //HAS_SKIN vec3 n = normalize(normal_matrix * normal); #ifdef HAS_NORMAL_MAP - vec3 t = normalize(normal_matrix * tangent.xyz); +// tangents should not use the inv_transpose matrix + // vec3 t = normalize(normal_matrix * tangent.xyz); + vec3 t = normalize((modelview_projection_matrix * vec4(tangent.xyz,0)).xyz); +// vec3 b = cross(n,t)*tangent.w; //vec3 t = cross(b,n) * binormal.w; diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp index b1b22e3e32..c14b11162c 100644 --- a/indra/newview/chatbar_as_cmdline.cpp +++ b/indra/newview/chatbar_as_cmdline.cpp @@ -807,12 +807,12 @@ bool cmd_line_chat(const std::string& revised_text, EChatType type, bool from_ge } else if (command == "/zoffset_up") { - gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ", gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ") + 0.05f); + gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ", gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ") + gSavedPerAccountSettings.getF32("AvatarHoverOffsetStepSize")); return false; } else if (command == "/zoffset_down") { - gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ", gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ") - 0.05f); + gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ", gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ") - gSavedPerAccountSettings.getF32("AvatarHoverOffsetStepSize")); return false; } else if (command == "/zoffset_reset") diff --git a/indra/newview/daeexport.cpp b/indra/newview/daeexport.cpp index 7f693c49a8..ef178cba9d 100644 --- a/indra/newview/daeexport.cpp +++ b/indra/newview/daeexport.cpp @@ -769,7 +769,7 @@ bool DAESaver::saveDAE(std::string filename) daeElement* contributor = asset->add("contributor"); contributor->add("author")->setCharData(author); - contributor->add("authoring_tool")->setCharData(LLVersionInfo::getChannelAndVersion()); + contributor->add("authoring_tool")->setCharData(LLVersionInfo::getInstance()->getChannelAndVersion()); daeElement* images = root->add("library_images"); daeElement* geomLib = root->add("library_geometries"); diff --git a/indra/newview/fs_viewer_manifest.py b/indra/newview/fs_viewer_manifest.py index 39a8bb739a..1aa007fd1c 100644 --- a/indra/newview/fs_viewer_manifest.py +++ b/indra/newview/fs_viewer_manifest.py @@ -160,12 +160,6 @@ class FSViewerManifest: self.path( "compatibility.manifest", "dullahan_host.exe.manifest" ) self.end_prefix() - def fs_setuid_chromesandbox( self ): - filename = os.path.join( self.get_dst_prefix(), "bin", "chrome-sandbox" ) - self.run_command_shell( "chmod 755 %s" % ( filename) ) # Strip sticky bit that might be set (in case the following two commands fail) - self.run_command_shell( "sudo -n chown root:root %s || exit 0" % ( filename) ) - self.run_command_shell( "sudo -n chmod 4755 %s || exit 0" % ( filename) ) - def fs_save_osx_symbols( self ): self.fs_save_symbols("darwin") diff --git a/indra/newview/fsareasearch.cpp b/indra/newview/fsareasearch.cpp index 065ced1660..25d5700c09 100644 --- a/indra/newview/fsareasearch.cpp +++ b/indra/newview/fsareasearch.cpp @@ -2294,8 +2294,7 @@ bool FSPanelAreaSearchOptions::onEnableColumnVisibilityChecked(const LLSD& userd //--------------------------------------------------------------------------- FSPanelAreaSearchAdvanced::FSPanelAreaSearchAdvanced(FSAreaSearch* pointer) -: LLPanel(), - mFSAreaSearch(pointer) +: LLPanel() { } diff --git a/indra/newview/fsareasearch.h b/indra/newview/fsareasearch.h index 9ad465aac4..2e3d82f938 100644 --- a/indra/newview/fsareasearch.h +++ b/indra/newview/fsareasearch.h @@ -438,9 +438,6 @@ public: LLCheckBoxCtrl* mCheckboxClickTouch; LLCheckBoxCtrl* mCheckboxClickBuy; LLCheckBoxCtrl* mCheckboxClickSit; - -private: - FSAreaSearch* mFSAreaSearch; }; #endif // FS_AREASEARCH_H diff --git a/indra/newview/fschathistory.cpp b/indra/newview/fschathistory.cpp index 93d7526116..17d9670ffd 100644 --- a/indra/newview/fschathistory.cpp +++ b/indra/newview/fschathistory.cpp @@ -1258,6 +1258,7 @@ void FSChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL LLStyle::Params name_params(body_message_params); name_params.color(name_color); name_params.readonly_color(name_color); + name_params.is_chat_header(true); // FS:LO FIRE-2899 - Faded text for IMs in nearby chat F32 FSIMChatHistoryFade = gSavedSettings.getF32("FSIMChatHistoryFade"); @@ -1448,8 +1449,8 @@ void FSChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL prependNewLineState = false; } } - - name_params.is_name_slurl = true; + + name_params.use_default_link_style = (!moderator_style_active || moderator_name_style_value == 0); name_params.link_href = LLSLURL("agent", chat.mFromID, "inspect").getSLURLString(); if (from_me && gSavedSettings.getBOOL("FSChatHistoryShowYou")) @@ -1562,14 +1563,12 @@ void FSChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // We don't want multiple friendship offers to appear, this code checks if there are previous offers // by iterating though all panels. // Note: it might be better to simply add a "pending offer" flag somewhere - for (LLToastNotifyPanel::instance_iter ti(LLToastNotifyPanel::beginInstances()) - , tend(LLToastNotifyPanel::endInstances()); ti != tend; ++ti) + for (auto& ti : LLToastNotifyPanel::instance_snapshot()) { - LLToastNotifyPanel& panel = *ti; - LLIMToastNotifyPanel * imtoastp = dynamic_cast(&panel); - const std::string& notification_name = panel.getNotificationName(); + LLIMToastNotifyPanel * imtoastp = dynamic_cast(&ti); + const std::string& notification_name = ti.getNotificationName(); if (notification_name == "OfferFriendship" - && panel.isControlPanelEnabled() + && ti.isControlPanelEnabled() && imtoastp) { create_toast = false; diff --git a/indra/newview/fscommon.cpp b/indra/newview/fscommon.cpp index d064dcdac6..a1bba035e0 100644 --- a/indra/newview/fscommon.cpp +++ b/indra/newview/fscommon.cpp @@ -85,6 +85,17 @@ bool is_irc_me_prefix(const std::string& text) return (prefix == "/me " || prefix == "/me'"); } +std::string unescape_name(const std::string& name) +{ + // bugfix for SL-46920: preventing filenames that break stuff. + char * curl_str = curl_unescape(name.c_str(), name.size()); + std::string unescaped_name(curl_str); + curl_free(curl_str); + curl_str = NULL; + + return unescaped_name; +} + std::string FSCommon::applyAutoCloseOoc(std::string message) { if (!gSavedSettings.getBOOL("AutoCloseOOC")) diff --git a/indra/newview/fscommon.h b/indra/newview/fscommon.h index 5d00a2887c..be33ef6790 100644 --- a/indra/newview/fscommon.h +++ b/indra/newview/fscommon.h @@ -40,6 +40,7 @@ const F32 AVATAR_UNKNOWN_RANGE = -1.f; void report_to_nearby_chat(const std::string& message); std::string format_string(std::string text, const LLStringUtil::format_map_t& args); bool is_irc_me_prefix(const std::string& text); +std::string unescape_name(const std::string& name); namespace FSCommon { diff --git a/indra/newview/fsdata.cpp b/indra/newview/fsdata.cpp index d4fd2a688a..90b84869e9 100644 --- a/indra/newview/fsdata.cpp +++ b/indra/newview/fsdata.cpp @@ -70,7 +70,7 @@ FSData::FSData() : mAgentsDone(false) { mHeaders.insert("User-Agent", LLViewerMedia::getInstance()->getCurrentUserAgent()); - mHeaders.insert("viewer-version", LLVersionInfo::getChannelAndVersionFS()); + mHeaders.insert("viewer-version", LLVersionInfo::getInstance()->getChannelAndVersionFS()); mBaseURL = gSavedSettings.getBOOL("FSdataQAtest") ? "http://phoenixviewer.com/app/fsdatatest" : "http://phoenixviewer.com/app/fsdata"; mFSDataURL = mBaseURL + "/" + "data.xml"; @@ -296,7 +296,7 @@ void downloadError(LLSD const &aData, std::string const &aURL) void FSData::startDownload() { mFSdataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "fsdata.xml"); - mFSdataDefaultsFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, llformat("fsdata_defaults.%s.xml", LLVersionInfo::getShortVersion().c_str())); + mFSdataDefaultsFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, llformat("fsdata_defaults.%s.xml", LLVersionInfo::getInstance()->getShortVersion().c_str())); mClientTagsFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "client_list_v2.xml"); { @@ -321,7 +321,7 @@ void FSData::startDownload() { last_modified = stat_data.st_mtime; } - std::string filename = llformat("defaults.%s.xml", LLVersionInfo::getShortVersion().c_str()); + std::string filename = llformat("defaults.%s.xml", LLVersionInfo::getInstance()->getShortVersion().c_str()); mFSdataDefaultsUrl = mBaseURL + "/" + filename; LL_INFOS("fsdata") << "Downloading defaults.xml from " << mFSdataDefaultsUrl << " with last modified of " << last_modified << LL_ENDL; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); @@ -792,7 +792,7 @@ bool FSData::isQA(const LLUUID& avatar_id) LLSD FSData::allowedLogin() { - std::map::iterator iter = mBlockedVersions.find(LLVersionInfo::getChannelAndVersionFS()); + std::map::iterator iter = mBlockedVersions.find(LLVersionInfo::getInstance()->getChannelAndVersionFS()); if (iter == mBlockedVersions.end()) { return LLSD(); @@ -1012,8 +1012,12 @@ LLSD FSData::getSystemInfo() LLSD info = LLAppViewer::instance()->getViewerInfo(); std::string sysinfo1("\n"); - sysinfo1 += llformat("%s %s (%d) %s %s (%s %dbit) %s\n\n", LLAppViewer::instance()->getSecondLifeTitle().c_str(), LLVersionInfo::getShortVersion().c_str(), LLVersionInfo::getBuild(), info["BUILD_DATE"].asString().c_str(), info["BUILD_TIME"].asString().c_str(), LLVersionInfo::getChannel().c_str(), - info["ADDRESS_SIZE"].asInteger(), info["BUILD_TYPE"].asString().c_str()); + sysinfo1 += llformat("%s %s (%d) %s %s (%s %dbit) %s\n\n", LLAppViewer::instance()->getSecondLifeTitle().c_str(), + LLVersionInfo::getInstance()->getShortVersion().c_str(), + LLVersionInfo::getInstance()->getBuild(), + info["BUILD_DATE"].asString().c_str(), info["BUILD_TIME"].asString().c_str(), + LLVersionInfo::getInstance()->getChannel().c_str(), + info["ADDRESS_SIZE"].asInteger(), info["BUILD_TYPE"].asString().c_str()); sysinfo1 += llformat("Build with %s version %s\n\n", info["COMPILER"].asString().c_str(), info["COMPILER_VERSION"].asString().c_str()); sysinfo1 += llformat("I am in %s located at %s (%s)\n", info["REGION"].asString().c_str(), info["HOSTNAME"].asString().c_str(), info["HOSTIP"].asString().c_str()); sysinfo1 += llformat("%s\n\n", info["SERVER_VERSION"].asString().c_str()); diff --git a/indra/newview/fsfloaterexport.cpp b/indra/newview/fsfloaterexport.cpp index e031979e3a..eefbb6dd94 100644 --- a/indra/newview/fsfloaterexport.cpp +++ b/indra/newview/fsfloaterexport.cpp @@ -309,7 +309,7 @@ bool FSFloaterObjectExport::exportSelection() struct tm* utc_time = gmtime(&rawtime); std::string date = llformat("%04d-%02d-%02d", utc_time->tm_year + 1900, utc_time->tm_mon + 1, utc_time->tm_mday); mManifest["format_version"] = OXP_FORMAT_VERSION; - mManifest["client"] = LLVersionInfo::getChannelAndVersion(); + mManifest["client"] = LLVersionInfo::getInstance()->getChannelAndVersion(); mManifest["creation_date"] = date; mManifest["author"] = author; mManifest["grid"] = LLGridManager::getInstance()->getGridLabel(); diff --git a/indra/newview/fsfloaterexport.h b/indra/newview/fsfloaterexport.h index 52e3813980..ff15dae701 100644 --- a/indra/newview/fsfloaterexport.h +++ b/indra/newview/fsfloaterexport.h @@ -127,7 +127,6 @@ private: S32 mLastRequest; bool mExported; bool mAborted; - bool mExportError; bool mDirty; typedef std::vector id_list_t; diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp index 01681486d2..4ebc1d6be3 100644 --- a/indra/newview/fsfloaterim.cpp +++ b/indra/newview/fsfloaterim.cpp @@ -453,13 +453,13 @@ void FSFloaterIM::sendMsgFromInputEditor(EChatType type) if(FSData::getInstance()->isTestingGroup(mSessionID)) { if(chat_prefix_testing) - utf8_text.insert(insert_pos, ("(" + str_address_size_tag + str_operating_system_tag + " " + LLVersionInfo::getBuildVersion() + skin_indicator + str_viewer_mode + str_rlv_enabled + str_opensim_tag + ") ")); + utf8_text.insert(insert_pos, ("(" + str_address_size_tag + str_operating_system_tag + " " + LLVersionInfo::getInstance()->getBuildVersion() + skin_indicator + str_viewer_mode + str_rlv_enabled + str_opensim_tag + ") ")); } //For release support groups, only display the short version(Major.Minor.Patch) since chat can speed by. This makes it easier on Support's eyes. else if(FSData::getInstance()->isSupportGroup(mSessionID)) { if(chat_prefix_support) - utf8_text.insert(insert_pos, ("(" + str_address_size_tag + str_operating_system_tag + " " + LLVersionInfo::getShortVersion() + skin_indicator + str_viewer_mode + str_rlv_enabled + str_opensim_tag + ") ")); + utf8_text.insert(insert_pos, ("(" + str_address_size_tag + str_operating_system_tag + " " + LLVersionInfo::getInstance()->getShortVersion() + skin_indicator + str_viewer_mode + str_rlv_enabled + str_opensim_tag + ") ")); } } diff --git a/indra/newview/fsfloatervramusage.cpp b/indra/newview/fsfloatervramusage.cpp index f808a57a65..7f55295038 100644 --- a/indra/newview/fsfloatervramusage.cpp +++ b/indra/newview/fsfloatervramusage.cpp @@ -333,7 +333,7 @@ S32 FSFloaterVRAMUsage::calcVBOEntrySize( LLVertexBuffer *aVBO ) void FSFloaterVRAMUsage::onProperties( LLSelectNode const *aProps ) { - if( !aProps && !aProps->getObject() ) + if( !aProps || !aProps->getObject() ) return; LLUUID id = aProps->getObject()->getID(); diff --git a/indra/newview/fspanellogin.cpp b/indra/newview/fspanellogin.cpp index 4e7dbab972..3669fe5b82 100644 --- a/indra/newview/fspanellogin.cpp +++ b/indra/newview/fspanellogin.cpp @@ -293,10 +293,10 @@ FSPanelLogin::FSPanelLogin(const LLRect &rect, getChild("login")->setDefaultBtn(findChild("connect_btn")); getChild("start_location_panel")->setDefaultBtn(findChild("connect_btn")); - std::string channel = LLVersionInfo::getChannel(); + std::string channel = LLVersionInfo::getInstance()->getChannel(); std::string version = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); + LLVersionInfo::getInstance()->getShortVersion().c_str(), + LLVersionInfo::getInstance()->getBuild()); LLTextBox* forgot_password_text = getChild("forgot_password_text"); forgot_password_text->setClickedCallback(onClickForgotPassword, NULL); @@ -867,9 +867,9 @@ void FSPanelLogin::loadLoginPage() // Channel and Version params["version"] = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); - params["channel"] = LLVersionInfo::getChannel(); + LLVersionInfo::getInstance()->getShortVersion().c_str(), + LLVersionInfo::getInstance()->getBuild()); + params["channel"] = LLVersionInfo::getInstance()->getChannel(); // Grid params["grid"] = LLGridManager::getInstance()->getGridId(); diff --git a/indra/newview/fspanelradar.cpp b/indra/newview/fspanelradar.cpp index c20c1278d9..8334d58e28 100644 --- a/indra/newview/fspanelradar.cpp +++ b/indra/newview/fspanelradar.cpp @@ -326,6 +326,11 @@ void FSPanelRadar::updateList(const std::vector& entries, const LLSD& stat static const std::string typingColumnIcon = getString("TypingColumnIcon"); // Store current selection and scroll position + LLUUID last_selected_id; + if (mRadarList->getLastSelectedItem()) + { + last_selected_id = mRadarList->getLastSelectedItem()->getUUID(); + } std::vector selected_items = mRadarList->getAllSelected(); uuid_vec_t selected_ids; for (std::vector::iterator it = selected_items.begin(); it != selected_items.end(); ++it) @@ -458,6 +463,10 @@ void FSPanelRadar::updateList(const std::vector& entries, const LLSD& stat if (!selected_ids.empty()) { mRadarList->selectMultiple(selected_ids); + if (last_selected_id.notNull()) + { + mRadarList->setLastSelectedItem(last_selected_id); + } } updateButtons(); diff --git a/indra/newview/lggbeammaps.cpp b/indra/newview/lggbeammaps.cpp index 9ff3602757..c027275eea 100644 --- a/indra/newview/lggbeammaps.cpp +++ b/indra/newview/lggbeammaps.cpp @@ -13,6 +13,7 @@ #include "llviewerprecompiledheaders.h" +#include "fscommon.h" #include "lggbeammaps.h" #include "lggbeamscolors.h" #include "llagent.h" @@ -27,17 +28,6 @@ lggBeamMaps gLggBeamMaps; -std::string unescape_name(const std::string& name) -{ - // bugfix for SL-46920: preventing filenames that break stuff. - char * curl_str = curl_unescape(name.c_str(), name.size()); - std::string unescaped_name(curl_str); - curl_free(curl_str); - curl_str = NULL; - - return unescaped_name; -} - F32 hueToRgb(F32 val1In, F32 val2In, F32 valHUeIn) { while (valHUeIn < 0.0f) @@ -70,7 +60,7 @@ F32 hueToRgb(F32 val1In, F32 val2In, F32 valHUeIn) void hslToRgb(F32 hValIn, F32 sValIn, F32 lValIn, F32& rValOut, F32& gValOut, F32& bValOut) { - if (sValIn < 0.00001f) + if (sValIn < F_ALMOST_ZERO) { rValOut = lValIn; gValOut = lValIn; @@ -400,4 +390,3 @@ void lggBeamMaps::updateBeamChat(const LLVector3d& currentPos) } } } - diff --git a/indra/newview/lggbeamscolors.cpp b/indra/newview/lggbeamscolors.cpp index 0da2aa0b5f..78513dd73e 100644 --- a/indra/newview/lggbeamscolors.cpp +++ b/indra/newview/lggbeamscolors.cpp @@ -18,9 +18,6 @@ #include "llviewerprecompiledheaders.h" #include "lggbeamscolors.h" -#include "llfile.h" -#include "llsdserialize.h" - lggBeamsColors lggBeamsColors::fromLLSD(const LLSD& inputData) { lggBeamsColors toReturn; diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp index 1dddf52961..e09527a34b 100644 --- a/indra/newview/llaccountingcostmanager.cpp +++ b/indra/newview/llaccountingcostmanager.cpp @@ -48,7 +48,7 @@ LLAccountingCostManager::LLAccountingCostManager() void LLAccountingCostManager::accountingCostCoro(std::string url, eSelectionType selectionType, const LLHandle observerHandle) { - LL_DEBUGS("LLAccountingCostManager") << "Entering coroutine " << LLCoros::instance().getName() + LL_DEBUGS("LLAccountingCostManager") << "Entering coroutine " << LLCoros::getName() << " with url '" << url << LL_ENDL; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -158,7 +158,7 @@ void LLAccountingCostManager::accountingCostCoro(std::string url, } catch (...) { - LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName() << "('" << url << "')")); throw; } diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index d2f95b746e..6df8abbc10 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -219,16 +219,21 @@ void LLAgentCamera::init() mCameraPreset = (ECameraPreset) gSavedSettings.getU32("CameraPresetType"); - mCameraOffsetInitial = gSavedSettings.getControl("CameraOffsetRearView"); - mFocusOffsetInitial = gSavedSettings.getControl("FocusOffsetRearView"); -//// [RLVa:KB] - Checked: RLVa-2.0.0 -// mCameraOffsetInitial[CAMERA_RLV_SETCAM_VIEW] = gSavedSettings.declareVec3("CameraOffsetRLVaView", LLVector3(mCameraOffsetInitial[CAMERA_PRESET_REAR_VIEW]->getDefault()), "Declared in code", LLControlVariable::PERSIST_NO); -// mCameraOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->setHiddenFromSettingsEditor(true); -//// [/RLVa:KB] -//// [RLVa:KB] - Checked: RLVa-2.0.0 -// mFocusOffsetInitial[CAMERA_RLV_SETCAM_VIEW] = gSavedSettings.declareVec3("FocusOffsetRLVaView", LLVector3(mFocusOffsetInitial[CAMERA_PRESET_REAR_VIEW]->getDefault()), "Declared in code", LLControlVariable::PERSIST_NO); -// mFocusOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->setHiddenFromSettingsEditor(true); -//// [/RLVa:KB] +// mCameraOffsetInitial = gSavedSettings.getControl("CameraOffsetRearView"); +// mFocusOffsetInitial = gSavedSettings.getControl("FocusOffsetRearView"); +// [RLVa:KB] - @setcam_eyeoffset, @setcam_focusoffset and @setcam_eyeoffsetscale + mCameraOffsetInitialControl = gSavedSettings.getControl("CameraOffsetRearView"); + mFocusOffsetInitialControl = gSavedSettings.getControl("FocusOffsetRearView"); + if (RlvActions::isRlvEnabled()) + { + mRlvCameraOffsetInitialControl = gSavedSettings.declareVec3("CameraOffsetRLVaView", LLVector3::zero, "Declared in code", LLControlVariable::PERSIST_NO); + mRlvCameraOffsetInitialControl->setHiddenFromSettingsEditor(true); + mRlvCameraOffsetScaleControl = gSavedSettings.declareF32("CameraOffsetScaleRLVa", 0.0f, "Declared in code", LLControlVariable::PERSIST_NO); + mRlvCameraOffsetScaleControl->setHiddenFromSettingsEditor(true); + mRlvFocusOffsetInitialControl = gSavedSettings.declareVec3d("FocusOffsetRLVaView", LLVector3d::zero, "Declared in code", LLControlVariable::PERSIST_NO); + mRlvFocusOffsetInitialControl->setHiddenFromSettingsEditor(true); + } +// [/RLVa:KB] mCameraCollidePlane.clearVec(); mCurrentCameraDistance = getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale"); @@ -1014,7 +1019,10 @@ void LLAgentCamera::cameraOrbitIn(const F32 meters) { if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON) { - F32 camera_offset_dist = llmax(0.001f, getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale")); +// [RLVa:KB] - @setcam_eyeoffsetscale + F32 camera_offset_dist = llmax(0.001f, getCameraOffsetInitial().magVec() * getCameraOffsetScale()); +// [/RLVa:KB] +// F32 camera_offset_dist = llmax(0.001f, getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale")); mCameraZoomFraction = (mTargetCameraDistance - meters) / camera_offset_dist; @@ -1727,7 +1735,10 @@ LLVector3d LLAgentCamera::calcThirdPersonFocusOffset() agent_rot *= ((LLViewerObject*)(gAgentAvatarp->getParent()))->getRenderRotation(); } - focus_offset = convert_from_llsd(mFocusOffsetInitial->get(), TYPE_VEC3D, ""); +// focus_offset = convert_from_llsd(mFocusOffsetInitial->get(), TYPE_VEC3D, ""); +// [RLVa:KB] - @setcam_focusoffset + focus_offset = getFocusOffsetInitial(); +// [/RLVa:KB] return focus_offset * agent_rot; } @@ -1868,7 +1879,10 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit) } else { - local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale"); +// [RLVa:KB] - @setcam_eyeoffsetscale + local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * getCameraOffsetScale(); +// [/RLVa:KB] +// local_camera_offset = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale"); // are we sitting down? if (isAgentAvatarValid() && gAgentAvatarp->getParent()) @@ -2064,7 +2078,10 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit) // Check focus distance limits if ( (fCamOriginDistClamped) && (!fCamAvDistLocked) ) { - const LLVector3 offsetCameraLocal = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale"); +// const LLVector3 offsetCameraLocal = mCameraZoomFraction * getCameraOffsetInitial() * gSavedSettings.getF32("CameraOffsetScale"); +// [RLVa:KB] - @setcam_eyeoffsetscale + const LLVector3 offsetCameraLocal = mCameraZoomFraction * getCameraOffsetInitial() * getCameraOffsetScale(); +// [/RLVa:KB] const LLVector3d offsetCamera(gAgent.getFrameAgent().rotateToAbsolute(offsetCameraLocal)); const LLVector3d posFocusCam = frame_center_global + head_offset + offsetCamera; if (clampCameraPosition(camera_position_global, posFocusCam, nCamOriginDistLimitMin, nCamOriginDistLimitMax)) @@ -2171,14 +2188,27 @@ bool LLAgentCamera::isJoystickCameraUsed() LLVector3 LLAgentCamera::getCameraOffsetInitial() { - return convert_from_llsd(mCameraOffsetInitial->get(), TYPE_VEC3, ""); +// [RLVa:KB] - @setcam_eyeoffset + return convert_from_llsd( (ECameraPreset::CAMERA_RLV_SETCAM_VIEW != mCameraPreset) ? mCameraOffsetInitialControl->get() : mRlvCameraOffsetInitialControl->get(), TYPE_VEC3, ""); +// [/RLVa:KB] +// return convert_from_llsd(mCameraOffsetInitial->get(), TYPE_VEC3, ""); } LLVector3d LLAgentCamera::getFocusOffsetInitial() { - return convert_from_llsd(mFocusOffsetInitial->get(), TYPE_VEC3D, ""); +// [RLVa:KB] - @setcam_focusoffset + return convert_from_llsd( (ECameraPreset::CAMERA_RLV_SETCAM_VIEW != mCameraPreset) ? mFocusOffsetInitialControl->get() : mRlvFocusOffsetInitialControl->get(), TYPE_VEC3D, ""); +// [/RLVa:KB] +// return convert_from_llsd(mFocusOffsetInitial->get(), TYPE_VEC3D, ""); } +// [RLVa:KB] - @setcam_eyeoffsetscale +F32 LLAgentCamera::getCameraOffsetScale() const +{ + return gSavedSettings.getF32( (ECameraPreset::CAMERA_RLV_SETCAM_VIEW != mCameraPreset) ? "CameraOffsetScale" : "CameraOffsetScaleRLVa"); +} +// [/RLVa:KB] + // FIRE-23470: Fix camera controls zoom glitch //F32 LLAgentCamera::getCameraMaxZoomDistance() F32 LLAgentCamera::getCameraMaxZoomDistance(bool allow_disabled_constraints /* = false*/) @@ -2268,10 +2298,16 @@ void LLAgentCamera::handleScrollWheel(S32 clicks) F32 camera_offset_initial_mag = getCameraOffsetInitial().magVec(); - F32 current_zoom_fraction = mTargetCameraDistance / (camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale")); +// F32 current_zoom_fraction = mTargetCameraDistance / (camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale")); +// [RLVa:KB] - @setcam_eyeoffsetscale + F32 current_zoom_fraction = mTargetCameraDistance / (camera_offset_initial_mag * getCameraOffsetScale()); +// [/RLVa:KB] current_zoom_fraction *= 1.f - pow(ROOT_ROOT_TWO, clicks); - cameraOrbitIn(current_zoom_fraction * camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale")); +// [RLVa:KB] - @setcam_eyeoffsetscale + cameraOrbitIn(current_zoom_fraction * camera_offset_initial_mag * getCameraOffsetScale()); +// [/RLVa:KB] +// cameraOrbitIn(current_zoom_fraction * camera_offset_initial_mag * gSavedSettings.getF32("CameraOffsetScale")); } else { @@ -2623,22 +2659,27 @@ void LLAgentCamera::changeCameraToCustomizeAvatar() void LLAgentCamera::switchCameraPreset(ECameraPreset preset) { -// [RLVa:KB] - Checked: RLVa-2.0.0 +// [RLVa:KB] - @setcam family if (RlvActions::isRlvEnabled()) { - // Don't allow changing away from the our view if an object is restricting it + // Don't allow changing away from our view if an object is restricting it if (RlvActions::isCameraPresetLocked()) preset = CAMERA_RLV_SETCAM_VIEW; - // Don't reset anything if our view is already current - if ( (CAMERA_RLV_SETCAM_VIEW == preset) && (CAMERA_RLV_SETCAM_VIEW == mCameraPreset) ) - return; - - // Reset our view when switching away - if (CAMERA_RLV_SETCAM_VIEW != preset) + if (CAMERA_RLV_SETCAM_VIEW == preset) { - //mCameraOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->resetToDefault(); - //mFocusOffsetInitial[CAMERA_RLV_SETCAM_VIEW]->resetToDefault(); + if (CAMERA_RLV_SETCAM_VIEW == mCameraPreset) + { + // Don't reset anything if our view is already current + return; + } + else + { + // When switching to our view, copy the current values + mRlvCameraOffsetInitialControl->setDefaultValue(convert_to_llsd(getCameraOffsetInitial())); + mRlvFocusOffsetInitialControl->setDefaultValue(convert_to_llsd(getFocusOffsetInitial())); + mRlvCameraOffsetScaleControl->setDefaultValue(getCameraOffsetScale()); + } } } // [/RLVa:KB] diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h index 4d07929ad1..f40d2a2809 100644 --- a/indra/newview/llagentcamera.h +++ b/indra/newview/llagentcamera.h @@ -61,9 +61,9 @@ enum ECameraPreset /** Current view when a preset is saved */ CAMERA_PRESET_CUSTOM, -// [RLVa:KB] - Checked: RLVa-2.0.0 +// [RLVa:KB] - @setcam_eyeoffset and @setcam_focusoffset /* Used by RLVa */ - CAMERA_RLV_SETCAM_VIEW + CAMERA_RLV_SETCAM_VIEW, // [/RLVa:KB] }; @@ -116,9 +116,17 @@ private: // Preset //-------------------------------------------------------------------- public: +// [RLVa:KB] - @setcam family + /** Determines default camera offset scale depending on the current camera preset */ + ECameraPreset getCameraPreset() const { return mCameraPreset; } +// [/RLVa:KB] void switchCameraPreset(ECameraPreset preset); /** Determines default camera offset depending on the current camera preset */ LLVector3 getCameraOffsetInitial(); +// [RLVa:KB] - @setcam_eyeoffsetscale + /** Determines default camera offset scale depending on the current camera preset */ + F32 getCameraOffsetScale() const; +// [/RLVa:KB] /** Determines default focus offset depending on the current camera preset */ LLVector3d getFocusOffsetInitial(); @@ -140,10 +148,24 @@ private: ECameraPreset mCameraPreset; /** Initial camera offset */ - LLPointer mCameraOffsetInitial; +// LLPointer mCameraOffsetInitial; +// [RLVa:KB] - @setcam_eyeoffset + // Renamed to catch their uses + LLPointer mCameraOffsetInitialControl; + LLPointer mRlvCameraOffsetInitialControl; +// [/RLVa:KB] + +// [RLVa:KB] - @setcam_eyeoffsetscale + LLPointer mRlvCameraOffsetScaleControl; +// [/RLVa:KB] /** Initial focus offset */ - LLPointer mFocusOffsetInitial; +// LLPointer mFocusOffsetInitial; +// [RLVa:KB] - @setcam_focusoffset + // Renamed to catch their uses + LLPointer mFocusOffsetInitialControl; + LLPointer mRlvFocusOffsetInitialControl; +// [/RLVa:KB] LLQuaternion mInitSitRot; diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index 1b95a32a58..5949981b0f 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -457,6 +457,11 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht AISUpdate::parseUUIDArray(result, "_created_categories", ids); } break; + case UPDATECATEGORY: + { + AISUpdate::parseUUIDArray(result, "_updated_categories", ids); + } + break; default: break; } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 16323056aa..18f2ec9d6f 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -886,6 +886,7 @@ void LLWearableHoldingPattern::onAllComplete() << mResolved << " wearable items " << LL_ENDL; LLAppearanceMgr::instance().updateAgentWearables(this); +// Merge; Changed in FS but not tagged with any FS: comment explaining by whom and the purpose // // Restore attachment pos overrides for the attachments that // // are remaining in the outfit. // for (LLAgentWearables::llvo_vec_t::iterator it = objects_to_retain.begin(); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 042acbf8db..70011468fc 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -291,7 +291,7 @@ #include "nd/ndlogthrottle.h" #include "fsradar.h" - +#include "fsassetblacklist.h" #if (LL_LINUX || LL_SOLARIS) && LL_GTK #include "glib.h" @@ -873,12 +873,14 @@ bool LLAppViewer::init() // Start of the application // - // initialize LLWearableType translation bridge. - // Memory will be cleaned up in ::cleanupClass() - LLWearableType::initParamSingleton(new LLUITranslationBridge()); + // Move further down after translation system has been initialized + //// initialize LLWearableType translation bridge. + //// Memory will be cleaned up in ::cleanupClass() + //LLWearableType::initParamSingleton(new LLUITranslationBridge()); - LLTranslationBridge::ptr_t trans = std::make_shared(); - LLSettingsType::initClass(trans); + //LLTranslationBridge::ptr_t trans = std::make_shared(); + //LLSettingsType::initClass(trans); + // // initialize SSE options LLVector4a::initClass(); @@ -985,12 +987,10 @@ bool LLAppViewer::init() return false; LL_INFOS("InitInfo") << "Configuration initialized." << LL_ENDL ; - //set the max heap size. initMaxHeapSize() ; LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize")); - // Although initLoggingAndGetLastDuration() is the right place to mess with // setFatalFunction(), we can't query gSavedSettings until after // initConfiguration(). @@ -1054,6 +1054,15 @@ bool LLAppViewer::init() // Setup LLTrans after LLUI::initClass has been called. initStrings(); + // Moved down here translation system has been initialized + // initialize LLWearableType translation bridge. + // Memory will be cleaned up in ::cleanupClass() + LLWearableType::initParamSingleton(new LLUITranslationBridge()); + + LLTranslationBridge::ptr_t trans = std::make_shared(); + LLSettingsType::initClass(trans); + // + // Setup notifications after LLUI::initClass() has been called. LLNotifications::instance(); LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ; @@ -1105,6 +1114,15 @@ bool LLAppViewer::init() LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated + // Contruct singleton early. + // Otherwise it will get constructed inside the texture decode thread and this will lead to deadlocks: + // - Let "Thread I" be the image decode threat that causes the creation of FSAssetBlack. + // - Thread I holds a lock to a mutex the mainthread is sleeping on. + // - Thread I will defer the singleton creation to the mainthread, which will cause Thread I to sleep on a mutex till the mainthread is done creating the object. + // - The mainthread can never wake up and create the object due do it sleeping on something Tread I must release. + // - Thread I can never wake up and release the mutex as the mainthread can never wake up and wake thread I again. + FSAssetBlacklist::getInstance(); + LLViewerFloaterReg::registerFloaters(); ///////////////////////////////////////////////// @@ -1322,7 +1340,7 @@ bool LLAppViewer::init() // Save the current version to the prefs file gSavedSettings.setString("LastRunVersion", - LLVersionInfo::getChannelAndVersion()); + LLVersionInfo::instance().getChannelAndVersion()); gSimLastTime = gRenderStartTime.getElapsedTimeF32(); gSimFrames = (F32)gFrameCount; @@ -1691,6 +1709,8 @@ bool LLAppViewer::doFrame() // canonical per-frame event mainloop.post(newFrame); + // give listeners a chance to run + llcoro::suspend(); if (!LLApp::isExiting()) { @@ -1983,24 +2003,9 @@ bool LLAppViewer::cleanup() gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp"); } - { - // Kill off LLLeap objects. We can find them all because LLLeap is derived - // from LLInstanceTracker. But collect instances first: LLInstanceTracker - // specifically forbids adding/deleting instances while iterating. - std::vector leaps; - leaps.reserve(LLLeap::instanceCount()); - for (LLLeap::instance_iter li(LLLeap::beginInstances()), lend(LLLeap::endInstances()); - li != lend; ++li) - { - leaps.push_back(&*li); - } - // Okay, now trash them all. We don't have to NULL or erase the entry - // in 'leaps' because the whole vector is going away momentarily. - BOOST_FOREACH(LLLeap* leap, leaps) - { - delete leap; - } - } // destroy 'leaps' + // Kill off LLLeap objects. We can find them all because LLLeap is derived + // from LLInstanceTracker. + LLLeap::instance_snapshot().deleteAll(); //flag all elements as needing to be destroyed immediately // to ensure shutdown order @@ -2511,25 +2516,19 @@ bool LLAppViewer::cleanup() removeMarkerFiles(); - // It's not at first obvious where, in this long sequence, generic cleanup - // calls OUGHT to go. So let's say this: as we migrate cleanup from + // It's not at first obvious where, in this long sequence, a generic cleanup + // call OUGHT to go. So let's say this: as we migrate cleanup from // explicit hand-placed calls into the generic mechanism, eventually - // all cleanup will get subsumed into the generic calls. So the calls you + // all cleanup will get subsumed into the generic call. So the calls you // still see above are calls that MUST happen before the generic cleanup // kicks in. - // This calls every remaining LLSingleton's cleanupSingleton() method. - // This method should perform any cleanup that might take significant - // realtime, or might throw an exception. - LLSingletonBase::cleanupAll(); - // The logging subsystem depends on an LLSingleton. Any logging after // LLSingletonBase::deleteAll() won't be recorded. LL_INFOS() << "Goodbye!" << LL_ENDL; - // This calls every remaining LLSingleton's deleteSingleton() method. - // No class destructor should perform any cleanup that might take - // significant realtime, or throw an exception. + // This calls every remaining LLSingleton's cleanupSingleton() and + // deleteSingleton() methods. LLSingletonBase::deleteAll(); removeDumpDir(); @@ -2889,7 +2888,7 @@ bool LLAppViewer::initConfiguration() // // load defaults overide here. Can not use settings_files.xml as path is different then above loading of defaults. - std::string fsdata_defaults = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, llformat("fsdata_defaults.%s.xml", LLVersionInfo::getShortVersion().c_str())); + std::string fsdata_defaults = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, llformat("fsdata_defaults.%s.xml", LLVersionInfo::getInstance()->getShortVersion().c_str())); std::string fsdata_global = "Global"; LLControlGroup* settings_group = LLControlGroup::getInstance(fsdata_global); if(settings_group && settings_group->loadFromFile(fsdata_defaults, set_defaults)) @@ -3152,7 +3151,7 @@ bool LLAppViewer::initConfiguration() std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel")); if(! CmdLineChannel.empty()) { - LLVersionInfo::resetChannel(CmdLineChannel); + LLVersionInfo::instance().resetChannel(CmdLineChannel); } // If we have specified crash on startup, set the global so we'll trigger the crash at the right time @@ -3367,7 +3366,7 @@ bool LLAppViewer::initConfiguration() // // Set the name of the window // - gWindowTitle = LLVersionInfo::getChannelAndVersion(); // + gWindowTitle = LLVersionInfo::getInstance()->getChannelAndVersion(); // #if LL_DEBUG gWindowTitle += std::string(" [DEBUG]"); #endif @@ -3411,7 +3410,7 @@ bool LLAppViewer::initConfiguration() loadColorSettings(); // One time fix for Latency - if ((gLastRunVersion != LLVersionInfo::getChannelAndVersion()) && (gSavedSettings.getString("SkinCurrent") == "latency") && !gSavedSettings.getBOOL("FSLatencyOneTimeFixRun")) + if ((gLastRunVersion != LLVersionInfo::getInstance()->getChannelAndVersion()) && (gSavedSettings.getString("SkinCurrent") == "latency") && !gSavedSettings.getBOOL("FSLatencyOneTimeFixRun")) { LL_INFOS() << "FSLatencyOneTimeFix: Fixing script dialog colors." << LL_ENDL; // Replace previously saved script dialog colors with new defaults, which happen to be the same as the group notice colors @@ -3423,12 +3422,11 @@ bool LLAppViewer::initConfiguration() // Let anyone else who cares know that we've populated our settings // variables. - for (LLControlGroup::key_iter ki(LLControlGroup::beginKeys()), kend(LLControlGroup::endKeys()); - ki != kend; ++ki) + for (const auto& key : LLControlGroup::key_snapshot()) { // For each named instance of LLControlGroup, send an event saying // we've initialized an LLControlGroup instance by that name. - LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", *ki)); + LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", key)); } // [RLVa:KB] - Patch: RLVa-2.1.0 @@ -3504,7 +3502,7 @@ void LLAppViewer::initStrings() // Set version number in VIEWER_GENERATION default substitute automatically LLStringUtil:: format_map_t gen_args; - gen_args["[VERSION]"] = llformat("%d", LLVersionInfo::getMajor()); + gen_args["[VERSION]"] = llformat("%d", LLVersionInfo::getInstance()->getMajor()); LLTrans::setDefaultArg("[VIEWER_GENERATION]", LLTrans::getString("VIEWER_GENERATION", gen_args)); LLTrans::setDefaultArg("[SHORT_VIEWER_GENERATION]", LLTrans::getString("SHORT_VIEWER_GENERATION", gen_args)); // @@ -3685,18 +3683,14 @@ LLSD LLAppViewer::getViewerInfo() const // is available to a getInfo() caller as to the user opening // LLFloaterAbout. LLSD info; - LLSD version; - version.append(LLVersionInfo::getMajor()); - version.append(LLVersionInfo::getMinor()); - version.append(LLVersionInfo::getPatch()); - version.append(LLVersionInfo::getBuild()); - info["VIEWER_VERSION"] = version; - info["VIEWER_VERSION_STR"] = LLVersionInfo::getVersion(); + auto& versionInfo(LLVersionInfo::instance()); + info["VIEWER_VERSION"] = LLSDArray(versionInfo.getMajor())(versionInfo.getMinor())(versionInfo.getPatch())(versionInfo.getBuild()); + info["VIEWER_VERSION_STR"] = versionInfo.getVersion(); info["BUILD_DATE"] = __DATE__; info["BUILD_TIME"] = __TIME__; - info["CHANNEL"] = LLVersionInfo::getChannel(); + info["CHANNEL"] = versionInfo.getChannel(); info["ADDRESS_SIZE"] = ADDRESS_SIZE; - //std::string build_config = LLVersionInfo::getBuildConfig(); + // std::string build_config = versionInfo.getBuildConfig(); //if (build_config != "Release") //{ // info["BUILD_CONFIG"] = build_config; @@ -3750,16 +3744,12 @@ LLSD LLAppViewer::getViewerInfo() const // return a URL to the release notes for this viewer, such as: // https://releasenotes.secondlife.com/viewer/2.1.0.123456.html - std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL"); - // FIRE-13993: Leave out channel so we can use a URL like - // http://wiki.firestormviewer.org/firestorm_change_log_x.y.z.rev - //if (! LLStringUtil::endsWith(url, "/")) - // url += "/"; - //url += LLURI::escape(LLVersionInfo::getVersion()) + ".html"; - url += LLURI::escape(LLVersionInfo::getVersion()); - // - + // FIRE-13993: Create URL in the form of https://wiki.firestormviewer.org/firestorm_change_log_x.y.z.rev + //std::string url = versionInfo.getReleaseNotes(); + //info["VIEWER_RELEASE_NOTES_URL"] = url.empty()? LLTrans::getString("RetrievingData") : url; + std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL") + LLURI::escape(versionInfo.getVersion()); info["VIEWER_RELEASE_NOTES_URL"] = url; + // #if LL_MSVC info["COMPILER"] = "MSVC"; @@ -3879,12 +3869,16 @@ LLSD LLAppViewer::getViewerInfo() const cef_ver_codec << "."; cef_ver_codec << DULLAHAN_VERSION_MINOR; cef_ver_codec << "."; + cef_ver_codec << DULLAHAN_VERSION_POINT; + cef_ver_codec << "."; cef_ver_codec << DULLAHAN_VERSION_BUILD; - cef_ver_codec << " / CEF: "; + cef_ver_codec << std::endl; + cef_ver_codec << " CEF: "; cef_ver_codec << CEF_VERSION; - cef_ver_codec << " / Chromium: "; + cef_ver_codec << std::endl; + cef_ver_codec << " Chromium: "; cef_ver_codec << CHROME_VERSION_MAJOR; cef_ver_codec << "."; cef_ver_codec << CHROME_VERSION_MINOR; @@ -4151,16 +4145,16 @@ void LLAppViewer::writeSystemInfo() // #endif // - gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel(); // [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-05-08 (Catznip-2.6.0a) | Added: Catznip-2.6.0a - gDebugInfo["ClientInfo"]["Version"] = LLVersionInfo::getVersion(); - gDebugInfo["ClientInfo"]["Platform"] = LLVersionInfo::getBuildPlatform(); + gDebugInfo["ClientInfo"]["Version"] = LLVersionInfo::instance().getVersion(); + gDebugInfo["ClientInfo"]["Platform"] = LLVersionInfo::instance().getBuildPlatform(); // [/SL:KB] - gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); - gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); - gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); - gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); - gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::getAddressSize(); + gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor(); + gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor(); + gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch(); + gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::instance().getBuild(); + gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::instance().getAddressSize(); // Add which flavor of FS generated an error #ifdef OPENSIM @@ -4214,7 +4208,7 @@ void LLAppViewer::writeSystemInfo() // Print into about git sha hash this build is based on. // LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::getChannelAndVersion() << LL_ENDL; - LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::getChannelAndVersion() << " [" << LLVersionInfo::getGitHash() << "]" << LL_ENDL; + LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::instance().getChannelAndVersion() << " [" << LLVersionInfo::instance().getGitHash() << "]" << LL_ENDL; // // Dump the local time and time zone @@ -4442,7 +4436,7 @@ void LLAppViewer::handleViewerCrash() // static void LLAppViewer::recordMarkerVersion(LLAPRFile& marker_file) { - std::string marker_version(LLVersionInfo::getChannelAndVersion()); + std::string marker_version(LLVersionInfo::instance().getChannelAndVersion()); if ( marker_version.length() > MAX_MARKER_LENGTH ) { LL_WARNS_ONCE("MarkerFile") << "Version length ("<< marker_version.length()<< ")" @@ -4461,7 +4455,7 @@ bool LLAppViewer::markerIsSameVersion(const std::string& marker_name) const { bool sameVersion = false; - std::string my_version(LLVersionInfo::getChannelAndVersion()); + std::string my_version(LLVersionInfo::instance().getChannelAndVersion()); char marker_version[MAX_MARKER_LENGTH]; S32 marker_version_length; @@ -5499,6 +5493,9 @@ void LLAppViewer::idle() LLFrameTimer::updateFrameTime(); LLFrameTimer::updateFrameCount(); LLEventTimer::updateClass(); + // LLApp::stepFrame() performs the above three calls plus mRunner.run(). + // Not sure why we don't call stepFrame() here, except that LLRunner seems + // completely redundant with LLEventTimer. LLNotificationsUI::LLToast::updateClass(); LLSmoothInterpolation::updateInterpolants(); LLMortician::updateClass(); @@ -6136,37 +6133,40 @@ void LLAppViewer::idleNetwork() const S64 frame_count = gFrameCount; // U32->S64 F32 total_time = 0.0f; - while (gMessageSystem->checkAllMessages(frame_count, gServicePump)) { - if (gDoDisconnect) + LockMessageChecker lmc(gMessageSystem); + while (lmc.checkAllMessages(frame_count, gServicePump)) { - // We're disconnecting, don't process any more messages from the server - // We're usually disconnecting due to either network corruption or a - // server going down, so this is OK. - break; - } + if (gDoDisconnect) + { + // We're disconnecting, don't process any more messages from the server + // We're usually disconnecting due to either network corruption or a + // server going down, so this is OK. + break; + } - total_decoded++; - gPacketsIn++; + total_decoded++; + gPacketsIn++; - if (total_decoded > MESSAGE_MAX_PER_FRAME) - { - break; - } + if (total_decoded > MESSAGE_MAX_PER_FRAME) + { + break; + } #ifdef TIME_THROTTLE_MESSAGES - // Prevent slow packets from completely destroying the frame rate. - // This usually happens due to clumps of avatars taking huge amount - // of network processing time (which needs to be fixed, but this is - // a good limit anyway). - total_time = check_message_timer.getElapsedTimeF32(); - if (total_time >= CheckMessagesMaxTime) - break; + // Prevent slow packets from completely destroying the frame rate. + // This usually happens due to clumps of avatars taking huge amount + // of network processing time (which needs to be fixed, but this is + // a good limit anyway). + total_time = check_message_timer.getElapsedTimeF32(); + if (total_time >= CheckMessagesMaxTime) + break; #endif - } + } - // Handle per-frame message system processing. - gMessageSystem->processAcks(gSavedSettings.getF32("AckCollectTime")); + // Handle per-frame message system processing. + lmc.processAcks(gSavedSettings.getF32("AckCollectTime")); + } #ifdef TIME_THROTTLE_MESSAGES if (total_time >= CheckMessagesMaxTime) @@ -6286,6 +6286,10 @@ void LLAppViewer::disconnectViewer() LLAppearanceMgr::instance().setAttachmentInvLinkEnable(false); // [/SL:KB] +// [RLVa:KB] - Checked: RLVa-2.3 (Housekeeping) + SUBSYSTEM_CLEANUP(RlvHandler); +// [/RLVa:KB] + gAgentWearables.cleanup(); gAgentCamera.cleanup(); // Also writes cached agent settings to gSavedSettings @@ -6445,15 +6449,15 @@ void LLAppViewer::handleLoginComplete() initMainloopTimeout("Mainloop Init"); // Store some data to DebugInfo in case of a freeze. - gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel(); // [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-05-08 (Catznip-2.6.0a) | Added: Catznip-2.6.0a - gDebugInfo["ClientInfo"]["Version"] = LLVersionInfo::getVersion(); - gDebugInfo["ClientInfo"]["Platform"] = LLVersionInfo::getBuildPlatform(); + gDebugInfo["ClientInfo"]["Version"] = LLVersionInfo::getInstance()->getVersion(); + gDebugInfo["ClientInfo"]["Platform"] = LLVersionInfo::getInstance()->getBuildPlatform(); // [/SL:KB] - gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); - gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); - gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); - gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); + gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor(); + gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor(); + gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch(); + gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::instance().getBuild(); // Add which flavor of FS generated an error #ifdef OPENSIM diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 6f454cb2bc..9231ec0921 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -622,68 +622,76 @@ void LLAppViewerWin32::disableWinErrorReporting() } const S32 MAX_CONSOLE_LINES = 500; +// Only defined in newer SDKs than we currently use +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4 +#endif -static bool create_console() +namespace { + +void set_stream(const char* desc, FILE* fp, DWORD handle_id, const char* name, const char* mode="w"); + +bool create_console() { - int h_con_handle; - long l_std_handle; + // allocate a console for this app + const bool isConsoleAllocated = AllocConsole(); - CONSOLE_SCREEN_BUFFER_INFO coninfo; - FILE *fp; + if (isConsoleAllocated) + { + // set the screen buffer to be big enough to let us scroll text + CONSOLE_SCREEN_BUFFER_INFO coninfo; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); + coninfo.dwSize.Y = MAX_CONSOLE_LINES; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - // allocate a console for this app - const bool isConsoleAllocated = AllocConsole(); - - // set the screen buffer to be big enough to let us scroll text - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - coninfo.dwSize.Y = MAX_CONSOLE_LINES; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - - // redirect unbuffered STDOUT to the console - l_std_handle = (long)GetStdHandle(STD_OUTPUT_HANDLE); - h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); - if (h_con_handle == -1) - { - LL_WARNS() << "create_console() failed to open stdout handle" << LL_ENDL; - } - else - { - fp = _fdopen( h_con_handle, "w" ); - *stdout = *fp; - setvbuf( stdout, NULL, _IONBF, 0 ); - } - - // redirect unbuffered STDIN to the console - l_std_handle = (long)GetStdHandle(STD_INPUT_HANDLE); - h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); - if (h_con_handle == -1) - { - LL_WARNS() << "create_console() failed to open stdin handle" << LL_ENDL; - } - else - { - fp = _fdopen( h_con_handle, "r" ); - *stdin = *fp; - setvbuf( stdin, NULL, _IONBF, 0 ); - } - - // redirect unbuffered STDERR to the console - l_std_handle = (long)GetStdHandle(STD_ERROR_HANDLE); - h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); - if (h_con_handle == -1) - { - LL_WARNS() << "create_console() failed to open stderr handle" << LL_ENDL; - } - else - { - fp = _fdopen( h_con_handle, "w" ); - *stderr = *fp; - setvbuf( stderr, NULL, _IONBF, 0 ); - } + // redirect unbuffered STDOUT to the console + set_stream("stdout", stdout, STD_OUTPUT_HANDLE, "CONOUT$"); + // redirect unbuffered STDERR to the console + set_stream("stderr", stderr, STD_ERROR_HANDLE, "CONOUT$"); + // redirect unbuffered STDIN to the console + // Don't bother: our console is solely for log output. We never read stdin. +// set_stream("stdin", stdin, STD_INPUT_HANDLE, "CONIN$", "r"); + } return isConsoleAllocated; } +void set_stream(const char* desc, FILE* fp, DWORD handle_id, const char* name, const char* mode) +{ + // SL-13528: This code used to be based on + // http://dslweb.nwnexus.com/~ast/dload/guicon.htm + // (referenced in https://stackoverflow.com/a/191880). + // But one of the comments on that StackOverflow answer points out that + // assigning to *stdout or *stderr "probably doesn't even work with the + // Universal CRT that was introduced in 2015," suggesting freopen_s() + // instead. Code below is based on https://stackoverflow.com/a/55875595. + auto std_handle = GetStdHandle(handle_id); + if (std_handle == INVALID_HANDLE_VALUE) + { + LL_WARNS() << "create_console() failed to get " << desc << " handle" << LL_ENDL; + } + else + { + if (mode == std::string("w")) + { + // Enable color processing on Windows 10 console windows. + DWORD dwMode = 0; + GetConsoleMode(std_handle, &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(std_handle, dwMode); + } + // Redirect the passed fp to the console. + FILE* ignore; + if (freopen_s(&ignore, name, mode, fp) == 0) + { + // use unbuffered I/O + setvbuf( fp, NULL, _IONBF, 0 ); + } + } +} + +} // anonymous namespace + LLAppViewerWin32::LLAppViewerWin32(const char* cmd_line) : mCmdLine(cmd_line), mIsConsoleAllocated(false) diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index 084fd527ad..74b53ea7ca 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -48,11 +48,18 @@ LLChannelManager::LLChannelManager() LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLChannelManager::onLoginCompleted, this)); mChannelList.clear(); mStartUpChannel = NULL; - + if(!gViewerWindow) { LL_ERRS() << "LLChannelManager::LLChannelManager() - viwer window is not initialized yet" << LL_ENDL; } + + // We don't actually need this instance right now, but our + // cleanupSingleton() method deletes LLScreenChannels, which need to + // unregister from LLUI. Calling LLUI::instance() here establishes the + // dependency so LLSingletonBase::deleteAll() calls our deleteSingleton() + // before LLUI::deleteSingleton(). + LLUI::instance(); } //-------------------------------------------------------------------------- diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 41aac017a1..bd56adf3de 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -1410,10 +1410,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // We don't want multiple friendship offers to appear, this code checks if there are previous offers // by iterating though all panels. // Note: it might be better to simply add a "pending offer" flag somewhere - for (LLToastNotifyPanel::instance_iter ti(LLToastNotifyPanel::beginInstances()) - , tend(LLToastNotifyPanel::endInstances()); ti != tend; ++ti) + for (auto& panel : LLToastNotifyPanel::instance_snapshot()) { - LLToastNotifyPanel& panel = *ti; LLIMToastNotifyPanel * imtoastp = dynamic_cast(&panel); const std::string& notification_name = panel.getNotificationName(); if (notification_name == "OfferFriendship" diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 52817f22b6..f38c5b170d 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -166,11 +166,11 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo() gAgent.getSecureSessionID().asString()); keywordArgs.appendString("language", LLUI::getLanguage()); keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy); - keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel()); - keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor()); - keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor()); - keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); - keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::getBuild()); + keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel()); + keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor()); + keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor()); + keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch()); + keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild()); LLXMLRPCValue params = LLXMLRPCValue::createArray(); params.append(keywordArgs); @@ -241,11 +241,11 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password) { keywordArgs.appendString("password", password); } - keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel()); - keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor()); - keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor()); - keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); - keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::getBuild()); + keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel()); + keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor()); + keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor()); + keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch()); + keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild()); LLXMLRPCValue params = LLXMLRPCValue::createArray(); params.append(keywordArgs); diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index 342ee3ccf5..a850d1bbda 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -33,6 +33,7 @@ #include "llagent.h" #include "llviewercontrol.h" // for gSavedSettings #include "llviewerregion.h" +#include "llviewernetwork.h" // for LLGridManager #include "llwlhandlers.h" #include "lltrans.h" #include "lltrace.h" @@ -65,6 +66,12 @@ #include "llviewergenericmessage.h" #include "llexperiencelog.h" +// [RLVa:KB] - Checked: RLVa-2.4 (@setenv) +#include "rlvactions.h" +// [/RLVa:KB] +#include "fscommon.h" +#include "llviewernetwork.h" + //========================================================================= namespace { @@ -821,6 +828,70 @@ LLEnvironment::LLEnvironment(): mShowMoonBeacon(false) { } +// OpenSim legacy Windlight setting support +#ifdef OPENSIM +void LLEnvironment::loadLegacyPresets() +{ + std::string path_name; + std::vector folders = { LL_PATH_APP_SETTINGS, LL_PATH_USER_SETTINGS }; + for (auto & settings_path : folders) + { + path_name = gDirUtilp->getExpandedFilename(settings_path , "windlight", "skies", ""); + bool found = true; + + while (found) + { + std::string name; + found = gDirUtilp->getNextFileInDir(path_name, "*.xml", name); + if (found) + { + name = name.erase(name.length() - 4); + mLegacySkies.push_back(unescape_name(name)); + LL_DEBUGS("WindlightCaps") << "Added Legacy Sky: " << unescape_name(name) << LL_ENDL; + } + } + + path_name = gDirUtilp->getExpandedFilename(settings_path, "windlight", "water", ""); + found = true; + + while (found) + { + std::string name; + found = gDirUtilp->getNextFileInDir(path_name, "*.xml", name); + if (found) + { + name = name.erase(name.length() - 4); + mLegacyWater.push_back(unescape_name(name)); + LL_DEBUGS("WindlightCaps") << "Added Legacy Water: " << unescape_name(name) << LL_ENDL; + } + } + + path_name = gDirUtilp->getExpandedFilename(settings_path, "windlight", "days", ""); + found = true; + + while (found) + { + std::string name; + found = gDirUtilp->getNextFileInDir(path_name, "*.xml", name); + if (found) + { + name = name.erase(name.length() - 4); + mLegacyDayCycles.push_back(unescape_name(name)); + LL_DEBUGS("WindlightCaps") << "Added Legacy Day Cycle: " << unescape_name(name) << LL_ENDL; + } + } + } +} + +void LLEnvironment::loadUserPrefs() +{ + // operate on members directly to avoid side effects + mWaterPresetName = gSavedSettings.getString("WaterPresetName"); + mSkyPresetName = gSavedSettings.getString("SkyPresetName"); + mDayCycleName = gSavedSettings.getString("DayCycleName"); +} +#endif //opensim +// void LLEnvironment::initSingleton() { @@ -833,6 +904,16 @@ void LLEnvironment::initSingleton() mEnvironments[ENV_DEFAULT] = mCurrentEnvironment; + // OpenSim legacy Windlight setting support +#ifdef OPENSIM + if (LLGridManager::instance().isInOpenSim()) + { + loadLegacyPresets(); + loadUserPrefs(); + } +#endif + // + requestRegion(); gAgent.addParcelChangedCallback([this]() { onParcelChange(); }); @@ -1057,6 +1138,13 @@ bool LLEnvironment::getIsMoonUp() const //------------------------------------------------------------------------- void LLEnvironment::setSelectedEnvironment(LLEnvironment::EnvSelection_t env, LLSettingsBase::Seconds transition, bool forced) { +// [RLVa:KB] - Checked: RLVa-2.4 (@setenv) + if ( (!RlvActions::canChangeEnvironment()) && (LLEnvironment::ENV_EDIT != env) ) + { + return; + } +// [/RLVa:KB] + mSelectedEnvironment = env; updateEnvironment(transition, forced); } @@ -1221,7 +1309,7 @@ void LLEnvironment::onSetEnvAssetLoaded(EnvSelection_t env, if (!settings || status) { LLSD args; - args["DESC"] = asset_id.asString(); + args["NAME"] = asset_id.asString();// fix the args to match the template. LLNotificationsUtil::add("FailedToFindSettings", args); return; } @@ -1367,12 +1455,12 @@ void LLEnvironment::updateEnvironment(LLSettingsBase::Seconds transition, bool f { if (transition != TRANSITION_INSTANT) { - DayInstance::ptr_t trans = std::make_shared( - mCurrentEnvironment->getSky(), mCurrentEnvironment->getWater(), pinstance, transition); - - trans->animate(); - - mCurrentEnvironment = trans; + DayInstance::ptr_t trans = std::make_shared( + mCurrentEnvironment->getSky(), mCurrentEnvironment->getWater(), pinstance, transition); + + trans->animate(); + + mCurrentEnvironment = trans; } else { @@ -1618,8 +1706,14 @@ void LLEnvironment::recordEnvironment(S32 parcel_id, LLEnvironment::EnvironmentI if (!envinfo->mDayCycle) { clearEnvironment(ENV_PARCEL); - setEnvironment(ENV_REGION, LLSettingsDay::GetDefaultAssetId(), LLSettingsDay::DEFAULT_DAYLENGTH, LLSettingsDay::DEFAULT_DAYOFFSET, envinfo->mEnvVersion); - updateEnvironment(); +// opensim legacy windlight. Nothing we can do here as the default assets do not exist in OpenSim + LL_WARNS("ENVIRONMENT") << "No DayCycle specified - setting default" << LL_ENDL; + if(LLGridManager::getInstance()->isInSecondLife()) + { + setEnvironment(ENV_REGION, LLSettingsDay::GetDefaultAssetId(), LLSettingsDay::DEFAULT_DAYLENGTH, LLSettingsDay::DEFAULT_DAYOFFSET, envinfo->mEnvVersion); + updateEnvironment(); + } +// } else if (envinfo->mDayCycle->isTrackEmpty(LLSettingsDay::TRACK_WATER) || envinfo->mDayCycle->isTrackEmpty(LLSettingsDay::TRACK_GROUND_LEVEL)) @@ -2152,10 +2246,14 @@ LLEnvironment::EnvironmentInfo::ptr_t LLEnvironment::EnvironmentInfo::extractLeg pinfo->mDayHash = pinfo->mDayCycle->getHash(); pinfo->mAltitudes[0] = 0; - pinfo->mAltitudes[2] = 10001; - pinfo->mAltitudes[3] = 10002; - pinfo->mAltitudes[4] = 10003; - +// Fix typos that offset this by 1. Shoudl get fixed in a merge from the lab soon. + // pinfo->mAltitudes[2] = 10001; + // pinfo->mAltitudes[3] = 10002; + // pinfo->mAltitudes[4] = 10003; + pinfo->mAltitudes[1] = 10001; + pinfo->mAltitudes[2] = 10002; + pinfo->mAltitudes[3] = 10003; +// return pinfo; } @@ -2361,7 +2459,7 @@ void LLEnvironment::onSetExperienceEnvAssetLoaded(LLUUID experience_id, LLSettin if (!settings || status) { LLSD args; - args["DESC"] = experience_id.asString(); + args["NAME"] = experience_id.asString();// fix the args to match the template. LLNotificationsUtil::add("FailedToFindSettings", args); return; } diff --git a/indra/newview/llenvironment.h b/indra/newview/llenvironment.h index 91c4b85135..bbd0adaca8 100644 --- a/indra/newview/llenvironment.h +++ b/indra/newview/llenvironment.h @@ -374,6 +374,20 @@ private: experience_overrides_t mExperienceOverrides; DayInstance::ptr_t getEnvironmentInstance(EnvSelection_t env, bool create = false); +// opensim windlight setting +#ifdef OPENSIM +public: + std::vector mLegacySkies; + std::vector mLegacyWater; + std::vector mLegacyDayCycles; + std::string mWaterPresetName; + std::string mSkyPresetName; + std::string mDayCycleName; +private: + void loadLegacyPresets(); + void loadUserPrefs(); +#endif +// void updateCloudScroll(); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 30a57232dd..556d71a4bf 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -658,14 +658,7 @@ void renderFace(LLDrawable* drawable, LLFace *face) const LLVolumeFace& vol_face = volume->getVolumeFace(face->getTEOffset()); // Use a vbo for the static LLVertexBuffer::drawArray/Element functions; by Drake Arconis/Shyotl Kuhr //LLVertexBuffer::drawElements(LLRender::TRIANGLES, vol_face.mPositions, NULL, vol_face.mNumIndices, vol_face.mIndices); - // FIRE-29679 trap empty calls that cause crashes when rezzing in OpenSim. - // Note: the drawElements() call checks for 0 length but in this case the number is non-zero but pointer is null. - // LLVertexBuffer::drawElements(LLRender::TRIANGLES, vol_face.mNumVertices, vol_face.mPositions, NULL, vol_face.mNumIndices, vol_face.mIndices); - if (vol_face.mIndices != nullptr && vol_face.mPositions != nullptr) - { - LLVertexBuffer::drawElements(LLRender::TRIANGLES, vol_face.mNumVertices, vol_face.mPositions, NULL, vol_face.mNumIndices, vol_face.mIndices); - } - // + LLVertexBuffer::drawElements(LLRender::TRIANGLES, vol_face.mNumVertices, vol_face.mPositions, NULL, vol_face.mNumIndices, vol_face.mIndices); } } } @@ -1573,6 +1566,10 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLMatrix4a mat_normal; mat_normal.loadu(mat_norm_in); + // FIX incorrect transformation + LLMatrix4a mat_tan; + mat_tan.loadu(mat_vert_in); + // F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0; bool do_xform = false; @@ -2132,8 +2129,10 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLVector4a t; tangent_to_object.rotate(binormal_dir, t); LLVector4a binormal; - mat_normal.rotate(t, binormal); - + // FIX incorrect transformation + // mat_normal.rotate(t, binormal); + mat_tan.rotate(t, binormal); + // //VECTORIZE THIS if (mDrawablep->isActive()) { @@ -2263,7 +2262,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, if (rebuild_normal) { - //LL_RECORD_TIME_BLOCK(FTM_FACE_GEOM_NORMAL); + //LL_RECORD_BLOCK_TIME(FTM_FACE_GEOM_NORMAL); mVertexBuffer->getNormalStrider(norm, mGeomIndex, mGeomCount, map_range); F32* normals = (F32*) norm.get(); LLVector4a* src = vf.mNormals; @@ -2301,7 +2300,10 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, while (src < end) { LLVector4a tangent_out; - mat_normal.rotate(*src, tangent_out); + // FIX incorrect transformation + // mat_normal.rotate(*src, tangent_out); + mat_tan.rotate(*src, tangent_out); + // tangent_out.normalize3fast(); tangent_out.setSelectWithMask(mask, *src, tangent_out); tangent_out.store4a(tangents); diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp index f5b5a096fa..e35acf1dd2 100644 --- a/indra/newview/llfloatercamera.cpp +++ b/indra/newview/llfloatercamera.cpp @@ -45,6 +45,9 @@ #include "llhints.h" #include "lltabcontainer.h" #include "llvoavatarself.h" +// [RLVa:KB] - @setcam +#include "rlvactions.h" +// [/RLVa:KB] static LLDefaultChildRegistry::Register r("panel_camera_item"); @@ -626,6 +629,13 @@ void LLFloaterCamera::onClickCameraItem(const LLSD& param) /*static*/ void LLFloaterCamera::switchToPreset(const std::string& name) { +// [RLVa:KB] - @setcam family + if (RlvActions::isCameraPresetLocked()) + { + return; + } +// [/RLVa:KB] + sFreeCamera = false; clear_camera_tool(); if (PRESETS_REAR_VIEW == name) diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index fe0d1c863a..bbf4d31f17 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -120,7 +120,6 @@ #include "llweb.h" // [RLVa:KB] - Checked: 2010-03-18 (RLVa-1.2.0a) #include "rlvactions.h" -#include "rlvhandler.h" // [/RLVa:KB] #include "lllogininstance.h" // to check if logged in yet @@ -2198,7 +2197,7 @@ void LLFloaterPreference::refreshEnabledState() LLComboBox* ctrl_reflections = getChild("Reflections"); // [RLVa:KB] - Checked: 2013-05-11 (RLVa-1.4.9) - if (rlv_handler_t::isEnabled()) + if (RlvActions::isRlvEnabled()) { getChild("do_not_disturb_response")->setEnabled(!RlvActions::hasBehaviour(RLV_BHVR_SENDIM)); } @@ -2252,11 +2251,11 @@ void LLFloaterPreference::refreshEnabledState() LLCheckBoxCtrl* ctrl_wind_light = getChild("WindLightUseAtmosShaders"); LLSliderCtrl* sky = getChild("SkyMeshDetail"); -// ctrl_wind_light->setEnabled(TRUE); // [RLVa:KB] - Checked: 2010-03-18 (RLVa-1.2.0a) | Modified: RLVa-0.2.0a // "Atmospheric Shaders" can't be disabled - but can be enabled - under @setenv=n - ctrl_wind_light->setEnabled((!gRlvHandler.hasBehaviour(RLV_BHVR_SETENV)) || (!gSavedSettings.getBOOL("WindLightUseAtmosShaders")) ); + ctrl_wind_light->setEnabled( (RlvActions::canChangeEnvironment()) || (!gSavedSettings.getBOOL("WindLightUseAtmosShaders"))); // [/RLVa:KB] +// ctrl_wind_light->setEnabled(TRUE); sky->setEnabled(TRUE); @@ -2347,11 +2346,11 @@ void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledState() LLCheckBoxCtrl* ctrl_wind_light = getChild("WindLightUseAtmosShaders"); LLSliderCtrl* sky = getChild("SkyMeshDetail"); LLTextBox* sky_text = getChild("SkyMeshDetailText"); -// ctrl_wind_light->setEnabled(TRUE); // [RLVa:KB] - Checked: 2010-03-18 (RLVa-1.2.0a) | Modified: RLVa-0.2.0a - // "Atmospheric Shaders" can't be disabled - but can be enabled - under @setenv=n - ctrl_wind_light->setEnabled(((!gRlvHandler.hasBehaviour(RLV_BHVR_SETENV)) || (!gSavedSettings.getBOOL("WindLightUseAtmosShaders"))) ); + // "Atmospheric Shaders" can't be disabled - but can be enabled - under @setenv=n + ctrl_wind_light->setEnabled( (RlvActions::canChangeEnvironment()) || (!gSavedSettings.getBOOL("WindLightUseAtmosShaders"))); // [/RLVa:KB] +// ctrl_wind_light->setEnabled(TRUE); sky->setEnabled(TRUE); sky_text->setEnabled(TRUE); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 77d640fc09..4f81af2755 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -697,11 +697,7 @@ void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) mInfoPanels.begin(), mInfoPanels.end(), llbind2nd( -#if LL_WINDOWS - std::mem_fun1(&LLPanelRegionInfo::refreshFromRegion), -#else std::mem_fun(&LLPanelRegionInfo::refreshFromRegion), -#endif region)); mEnvironmentPanel->refreshFromRegion(region); } diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 38773dfe27..c5f95b7260 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -779,7 +779,7 @@ LLSD LLFloaterReporter::gatherReport() std::ostringstream details; - details << "V" << LLVersionInfo::getVersion() << std::endl << std::endl; // client version moved to body of email for abuse reports + details << "V" << LLVersionInfo::instance().getVersion() << std::endl << std::endl; // client version moved to body of email for abuse reports std::string object_name = getChild("object_name")->getValue().asString(); if (!object_name.empty() && !mOwnerName.empty()) @@ -797,7 +797,7 @@ LLSD LLFloaterReporter::gatherReport() std::string version_string; version_string = llformat( "%s %s %s %s %s", - LLVersionInfo::getShortVersion().c_str(), + LLVersionInfo::instance().getShortVersion().c_str(), platform, gSysCPU.getFamily().c_str(), gGLManager.mGLRenderer.c_str(), diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index d9a99df522..9585452fc1 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -751,7 +751,6 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, (message.length() > 3) && (RLV_CMD_PREFIX == message[0]) && (RlvHandler::instance().processIMQuery(from_id, message)) ) { // Eat the message and do nothing - return; } // [/RLVa:KB] // else if (offline == IM_ONLINE @@ -1348,41 +1347,41 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } else // IM_TASK_INVENTORY_OFFERED { - if (offline == IM_OFFLINE && session_id.isNull() && aux_id.notNull() && binary_bucket_size > sizeof(S8)* 5) + if (sizeof(S8) == binary_bucket_size) { - // cap received offline message - std::string str_bucket = ll_safe_string((char*)binary_bucket, binary_bucket_size); - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|", "", boost::keep_empty_tokens); - tokenizer tokens(str_bucket, sep); - tokenizer::iterator iter = tokens.begin(); - - info->mType = (LLAssetType::EType)(atoi((*(iter++)).c_str())); - // Note There is more elements in 'tokens' ... - - info->mObjectID = LLUUID::null; - info->mFromObject = TRUE; + info->mType = (LLAssetType::EType) binary_bucket[0]; } else { - if (sizeof(S8) != binary_bucket_size) - { - LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; - delete info; - break; - } - info->mType = (LLAssetType::EType) binary_bucket[0]; - info->mObjectID = LLUUID::null; - info->mFromObject = TRUE; + /*RIDER*/ // The previous version of the protocol returned the wrong binary bucket... we + // still might be able to figure out the type... even though the offer is not retrievable. + + // Should be safe to remove once DRTSIM-451 fully deploys + std::string str_bucket(reinterpret_cast(binary_bucket)); + std::string str_type(str_bucket.substr(0, str_bucket.find('|'))); + + std::stringstream type_convert(str_type); + + S32 type; + type_convert >> type; + + // We could try AT_UNKNOWN which would be more accurate, but that causes an auto decline + info->mType = static_cast(type); + // Don't break in the case of a bad binary bucket. Go ahead and show the + // accept/decline popup even though it will not do anything. + LL_WARNS("Messaging") << "Malformed inventory offer from object, type might be " << info->mType << LL_ENDL; } + info->mObjectID = LLUUID::null; + info->mFromObject = TRUE; } info->mIM = dialog; info->mFromID = from_id; info->mFromGroup = from_group; - info->mTransactionID = session_id; info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); + info->mTransactionID = session_id.notNull() ? session_id : aux_id; + info->mFromName = name; info->mDesc = message; info->mHost = sender; @@ -1737,7 +1736,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, // If we auto-accept the offer/request then this will override DnD status (but we'll still let the other party know later) bool fRlvAutoAccept = (rlv_handler_t::isEnabled()) && ( ((IM_LURE_USER == dialog) && (RlvActions::autoAcceptTeleportOffer(from_id))) || - ((IM_TELEPORT_REQUEST == dialog) && (RlvActions::autoAcceptTeleportRequest(from_id))) ); + ((IM_TELEPORT_REQUEST == dialog) && (RlvActions::autoAcceptTeleportRequest(from_id))) ); // [/RLVa:KB] if (is_muted) @@ -1822,7 +1821,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, if (rlv_handler_t::isEnabled()) { if ( ((IM_LURE_USER == dialog) && (!RlvActions::canAcceptTpOffer(from_id))) || - ((IM_TELEPORT_REQUEST == dialog) && (!RlvActions::canAcceptTpRequest(from_id))) ) + ((IM_TELEPORT_REQUEST == dialog) && (!RlvActions::canAcceptTpRequest(from_id))) ) { RlvUtil::sendBusyMessage(from_id, RlvStrings::getString(RLV_STRING_BLOCKED_TPLUREREQ_REMOTE)); if (is_do_not_disturb) @@ -1832,7 +1831,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, // Censor message if: 1) restricted from receiving IMs from the sender, or 2) teleport offer/request and @showloc=n restricted if ( (!RlvActions::canReceiveIM(from_id)) || - ((gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) && (IM_LURE_USER == dialog || IM_TELEPORT_REQUEST == dialog)) ) + ((gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) && (IM_LURE_USER == dialog || IM_TELEPORT_REQUEST == dialog)) ) { message = RlvStrings::getString(RLV_STRING_HIDDEN); } @@ -2052,10 +2051,8 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, payload["sender"] = sender.getIPandPort(); bool add_notification = true; - for (LLToastNotifyPanel::instance_iter ti(LLToastNotifyPanel::beginInstances()) - , tend(LLToastNotifyPanel::endInstances()); ti != tend; ++ti) + for (auto& panel : LLToastNotifyPanel::instance_snapshot()) { - LLToastNotifyPanel& panel = *ti; const std::string& notification_name = panel.getNotificationName(); if (notification_name == "OfferFriendship" && panel.isControlPanelEnabled()) { @@ -2233,7 +2230,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) return; } - if (gAgent.getRegion() == NULL) + if (!gAgent.getRegion()) { LL_WARNS("Messaging") << "Region null while attempting to load messages." << LL_ENDL; return; @@ -2241,8 +2238,6 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) LL_INFOS("Messaging") << "Processing offline messages." << LL_ENDL; - std::vector data; - S32 binary_bucket_size = 0; LLHost sender = gAgent.getRegionHost(); LLSD::array_iterator i = messages.beginArray(); @@ -2251,12 +2246,30 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) { const LLSD &message_data(*i); - LLVector3 position(message_data["local_x"].asReal(), message_data["local_y"].asReal(), message_data["local_z"].asReal()); - data = message_data["binary_bucket"].asBinary(); - binary_bucket_size = data.size(); // message_data["count"] always 0 - U32 parent_estate_id = message_data.has("parent_estate_id") ? message_data["parent_estate_id"].asInteger() : 1; // 1 - IMMainland + /* RIDER: Many fields in this message are using a '_' rather than the standard '-'. This + * should be changed but would require tight coordination with the simulator. + */ + LLVector3 position; + if (message_data.has("position")) + { + position.setValue(message_data["position"]); + } + else + { + position.set(message_data["local_x"].asReal(), message_data["local_y"].asReal(), message_data["local_z"].asReal()); + } - // Todo: once dirtsim-369 releases, remove one of the int/str options + std::vector bin_bucket; + if (message_data.has("binary_bucket")) + { + bin_bucket = message_data["binary_bucket"].asBinary(); + } + else + { + bin_bucket.push_back(0); + } + + // Todo: once drtsim-451 releases, remove the string option BOOL from_group; if (message_data["from_group"].isInteger()) { @@ -2267,22 +2280,24 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) from_group = message_data["from_group"].asString() == "Y"; } - LLIMProcessing::processNewMessage(message_data["from_agent_id"].asUUID(), + LLIMProcessing::processNewMessage( + message_data["from_agent_id"].asUUID(), from_group, message_data["to_agent_id"].asUUID(), - IM_OFFLINE, - (EInstantMessage)message_data["dialog"].asInteger(), - LLUUID::null, // session id, since there is none we can only use frienship/group invite caps - message_data["timestamp"].asInteger(), + message_data.has("offline") ? static_cast(message_data["offline"].asInteger()) : IM_OFFLINE, + static_cast(message_data["dialog"].asInteger()), + message_data["transaction-id"].asUUID(), + static_cast(message_data["timestamp"].asInteger()), message_data["from_agent_name"].asString(), message_data["message"].asString(), - parent_estate_id, + static_cast((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland message_data["region_id"].asUUID(), position, - &data[0], - binary_bucket_size, + bin_bucket.data(), + bin_bucket.size(), sender, - message_data["asset_id"].asUUID()); // not necessarily an asset + message_data["asset_id"].asUUID()); + } } diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 205a9f286f..f5ada6e12e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -386,7 +386,10 @@ void update_all_marketplace_count() return; } -void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) +//void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) +// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV) +void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name, LLPointer cb) +// [/RLVa:KB] { LLViewerInventoryCategory* cat; @@ -400,7 +403,10 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s LLSD updates; updates["name"] = new_name; - update_inventory_category(cat_id, updates, NULL); +// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV) + update_inventory_category(cat_id, updates, cb); +// [/RLVa:KB] +// update_inventory_category(cat_id, updates, NULL); } void copy_inventory_category(LLInventoryModel* model, diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 8f6f8f802e..becdb9a63c 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -72,7 +72,10 @@ void update_marketplace_category(const LLUUID& cat_id, bool perform_consistency_ // Nudge all listing categories to signal that their marketplace status changed void update_all_marketplace_count(); -void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); +// [RLVa:KB] - Checked: RLVa-2.3 (Give-to-#RLV) +void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name, LLPointer cb = nullptr); +// [/RLVa:KB] +//void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false); diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 542d27d2d0..40b68be79a 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -230,8 +230,8 @@ void LLLoginInstance::constructAuthParams(LLPointer user_credentia request_params["last_exec_event"] = mLastExecEvent; request_params["last_exec_duration"] = mLastExecDuration; request_params["mac"] = (char*)hashed_unique_id_string; - request_params["version"] = LLVersionInfo::getVersion(); - request_params["channel"] = LLVersionInfo::getChannel(); + request_params["version"] = LLVersionInfo::instance().getVersion(); + request_params["channel"] = LLVersionInfo::instance().getChannel(); request_params["platform"] = mPlatform; request_params["address_size"] = ADDRESS_SIZE; request_params["platform_version"] = mPlatformVersion; @@ -347,7 +347,7 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) { data["certificate"] = response["certificate"]; } - + if (gViewerWindow) gViewerWindow->setShowProgress(FALSE, FALSE); LLFloaterReg::showInstance("message_critical", data); @@ -363,13 +363,31 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) // login.cgi is insisting on a required update. We were called with an // event that bundles both the login.cgi 'response' and the // synchronization event from the 'updater'. - std::string required_version = response["message_args"]["VERSION"]; - LL_WARNS("LLLogin") << "Login failed because an update to version " << required_version << " is required." << LL_ENDL; + std::string login_version = response["message_args"]["VERSION"]; + std::string vvm_version = updater["VERSION"]; + std::string relnotes = updater["URL"]; + LL_WARNS("LLLogin") << "Login failed because an update to version " << login_version << " is required." << LL_ENDL; + // vvm_version might be empty because we might not have gotten + // SLVersionChecker's LoginSync handshake. But if it IS populated, it + // should (!) be the same as the version we got from login.cgi. + if ((! vvm_version.empty()) && vvm_version != login_version) + { + LL_WARNS("LLLogin") << "VVM update version " << vvm_version + << " differs from login version " << login_version + << "; presenting VVM version to match release notes URL" + << LL_ENDL; + login_version = vvm_version; + } + if (relnotes.empty()) + { + // I thought this would be available in strings.xml or some such + relnotes = "https://secondlife.com/support/downloads/"; + } if (gViewerWindow) gViewerWindow->setShowProgress(FALSE, FALSE); - LLSD args(LLSDMap("VERSION", required_version)); + LLSD args(LLSDMap("VERSION", login_version)("URL", relnotes)); if (updater.isUndefined()) { // If the updater failed to shake hands, better advise the user to diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index f5e33e554a..29dfca4a63 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -800,7 +800,7 @@ BOOL LLPanelEditWearable::postBuild() LL_WARNS() << "could not get wearable dictionary entry for wearable of type: " << type << LL_ENDL; continue; } - U8 num_subparts = wearable_entry->mSubparts.size(); + U8 num_subparts = (U8)(wearable_entry->mSubparts.size()); // Appearance panel not updating camera position bool tab_container_cb_set = false; @@ -1249,7 +1249,7 @@ void LLPanelEditWearable::showWearable(LLViewerWearable* wearable, BOOL show, BO updatePanelPickerControls(type); // clear and rebuild visual param list - U8 num_subparts = wearable_entry->mSubparts.size(); + U8 num_subparts = (U8)(wearable_entry->mSubparts.size()); for (U8 index = 0; index < num_subparts; ++index) { @@ -1382,7 +1382,8 @@ void LLPanelEditWearable::onTabChanged(LLUICtrl* ctrl, LLWearableType::EType typ return; } - U8 num_subparts = wearable_entry->mSubparts.size(); + llassert_always(wearable_entry->mSubparts.size() <= 0xFF); + U8 num_subparts = static_cast(wearable_entry->mSubparts.size()); for (U8 index = 0; index < num_subparts; ++index) { ESubpart subpart_e = wearable_entry->mSubparts[index]; @@ -1505,7 +1506,7 @@ void LLPanelEditWearable::updateScrollingPanelUI() const LLEditWearableDictionary::WearableEntry *wearable_entry = LLEditWearableDictionary::getInstance()->getWearable(type); llassert(wearable_entry); if (!wearable_entry) return; - U8 num_subparts = wearable_entry->mSubparts.size(); + U8 num_subparts = (U8)(wearable_entry->mSubparts.size()); LLScrollingPanelParam::sUpdateDelayFrames = 0; for (U8 index = 0; index < num_subparts; ++index) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 32b3658105..3ac1fab1e0 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -340,10 +340,10 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLButton* def_btn = getChild("connect_btn"); setDefaultBtn(def_btn); - std::string channel = LLVersionInfo::getChannel(); + std::string channel = LLVersionInfo::instance().getChannel(); std::string version = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); + LLVersionInfo::instance().getShortVersion().c_str(), + LLVersionInfo::instance().getBuild()); LLTextBox* forgot_password_text = getChild("forgot_password_text"); forgot_password_text->setClickedCallback(onClickForgotPassword, NULL); @@ -945,9 +945,9 @@ void LLPanelLogin::loadLoginPage() // Channel and Version params["version"] = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); - params["channel"] = LLVersionInfo::getChannel(); + LLVersionInfo::instance().getShortVersion().c_str(), + LLVersionInfo::instance().getBuild()); + params["channel"] = LLVersionInfo::instance().getChannel(); // Grid params["grid"] = LLGridManager::getInstance()->getGridId(); diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 9cbda7bbba..40f4bb9981 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -2703,6 +2703,10 @@ void LLPanelObject::onCopyRot(const LLSD& data) void LLPanelObject::onPastePos(const LLSD& data) { if(!mHasPosClipboard) return; + if (mObject.isNull()) return; + + LLViewerRegion* regionp = mObject->getRegion(); + if (!regionp) return; //clamp pos on non-attachments, just keep the prims on the sim if (!mObject->isAttachment()) @@ -2710,8 +2714,8 @@ void LLPanelObject::onPastePos(const LLSD& data) // Aurora Sim //mClipboardPos.mV[VX] = llclamp( mClipboardPos.mV[VX], 0.f, 256.f); //mClipboardPos.mV[VY] = llclamp( mClipboardPos.mV[VY], 0.f, 256.f); - mClipboardPos.mV[VX] = llclamp( mClipboardPos.mV[VX], 0.f, gAgent.getRegion()->getWidth()); - mClipboardPos.mV[VY] = llclamp( mClipboardPos.mV[VY], 0.f, gAgent.getRegion()->getWidth()); + mClipboardPos.mV[VX] = llclamp( mClipboardPos.mV[VX], 0.f, regionp->getWidth()); + mClipboardPos.mV[VY] = llclamp( mClipboardPos.mV[VY], 0.f, regionp->getWidth()); // Aurora Sim //height will get properly clammed by sendPosition } diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index c58e420d53..f7768f372f 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -413,7 +413,8 @@ public: private: U32 mMask; - LLInventoryFriendCardObserver* mInvObserver; + // Disconnect LLFriendCardsManager + //LLInventoryFriendCardObserver* mInvObserver; bool mIsActive; /** diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 7f22ebae8a..0538a96daf 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -831,8 +831,32 @@ void LLPanelPrimMediaControls::draw() BOOL LLPanelPrimMediaControls::handleScrollWheel(S32 x, S32 y, S32 clicks) { - mInactivityTimer.start(); - return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); + mInactivityTimer.start(); + BOOL res = FALSE; + + // Unlike other mouse events, we need to handle scroll here otherwise + // it will be intercepted by camera and won't reach toolpie + if (LLViewerMediaFocus::getInstance()->isHoveringOverFocused()) + { + // either let toolpie handle this or expose mHoverPick.mUVCoords in some way + res = LLToolPie::getInstance()->handleScrollWheel(x, y, clicks); + } + + return res; +} + +BOOL LLPanelPrimMediaControls::handleScrollHWheel(S32 x, S32 y, S32 clicks) +{ + mInactivityTimer.start(); + BOOL res = FALSE; + + if (LLViewerMediaFocus::getInstance()->isHoveringOverFocused()) + { + // either let toolpie handle this or expose mHoverPick.mUVCoords in some way + res = LLToolPie::getInstance()->handleScrollHWheel(x, y, clicks); + } + + return res; } BOOL LLPanelPrimMediaControls::handleMouseDown(S32 x, S32 y, MASK mask) diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index 21d5236074..d4301aaf7c 100644 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -48,6 +48,7 @@ public: /*virtual*/ BOOL postBuild(); virtual void draw(); virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + virtual BOOL handleScrollHWheel(S32 x, S32 y, S32 clicks); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp index 23528ffd9d..3076d0d48d 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -463,7 +463,10 @@ void LLProgressView::initLogos() const S32 default_height = 28; const S32 default_pad = 15; - S32 icon_width, icon_height; + S32 icon_width; +#if defined(LL_FMODSTUDIO) || defined(HAVOK_TPV) + S32 icon_height; +#endif // defined(LL_FMODSTUDIO) || defined(HAVOK_TPV) // We don't know final screen rect yet, so we can't precalculate position fully LLTextBox *logos_label = getChild("logos_lbl"); @@ -478,7 +481,9 @@ void LLProgressView::initLogos() temp_str += gDirUtilp->getDirDelimiter(); - S32 pad_y = 0; // Build fix +#if defined(LL_FMODSTUDIO) || defined(HAVOK_TPV) + S32 pad_y = 0; // Build fix +#endif // defined(LL_FMODSTUDIO) || defined(HAVOK_TPV) #ifdef LL_FMODSTUDIO // original image size is 264x96, it is on longer side but diff --git a/indra/newview/llscenemonitor.cpp b/indra/newview/llscenemonitor.cpp index 7261861e80..fe2f94d03e 100644 --- a/indra/newview/llscenemonitor.cpp +++ b/indra/newview/llscenemonitor.cpp @@ -559,16 +559,14 @@ void LLSceneMonitor::dumpToFile(std::string file_name) typedef StatType trace_count; - for (trace_count::instance_iter it = trace_count::beginInstances(), end_it = trace_count::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_count::instance_snapshot()) { std::ostringstream row; row << std::setprecision(10); - row << it->getName(); + row << it.getName(); - const char* unit_label = it->getUnitLabel(); + const char* unit_label = it.getUnitLabel(); if(unit_label[0]) { row << "(" << unit_label << ")"; @@ -579,8 +577,8 @@ void LLSceneMonitor::dumpToFile(std::string file_name) for (S32 frame = 1; frame <= frame_count; frame++) { Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(*it); - row << ", " << recording.getSum(*it); + samples += recording.getSampleCount(it); + row << ", " << recording.getSum(it); } row << '\n'; @@ -593,15 +591,13 @@ void LLSceneMonitor::dumpToFile(std::string file_name) typedef StatType trace_event; - for (trace_event::instance_iter it = trace_event::beginInstances(), end_it = trace_event::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_event::instance_snapshot()) { std::ostringstream row; row << std::setprecision(10); - row << it->getName(); + row << it.getName(); - const char* unit_label = it->getUnitLabel(); + const char* unit_label = it.getUnitLabel(); if(unit_label[0]) { row << "(" << unit_label << ")"; @@ -612,8 +608,8 @@ void LLSceneMonitor::dumpToFile(std::string file_name) for (S32 frame = 1; frame <= frame_count; frame++) { Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(*it); - F64 mean = recording.getMean(*it); + samples += recording.getSampleCount(it); + F64 mean = recording.getMean(it); if (llisnan(mean)) { row << ", n/a"; @@ -634,15 +630,13 @@ void LLSceneMonitor::dumpToFile(std::string file_name) typedef StatType trace_sample; - for (trace_sample::instance_iter it = trace_sample::beginInstances(), end_it = trace_sample::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_sample::instance_snapshot()) { std::ostringstream row; row << std::setprecision(10); - row << it->getName(); + row << it.getName(); - const char* unit_label = it->getUnitLabel(); + const char* unit_label = it.getUnitLabel(); if(unit_label[0]) { row << "(" << unit_label << ")"; @@ -653,8 +647,8 @@ void LLSceneMonitor::dumpToFile(std::string file_name) for (S32 frame = 1; frame <= frame_count; frame++) { Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(*it); - F64 mean = recording.getMean(*it); + samples += recording.getSampleCount(it); + F64 mean = recording.getMean(it); if (llisnan(mean)) { row << ", n/a"; @@ -674,15 +668,13 @@ void LLSceneMonitor::dumpToFile(std::string file_name) } typedef StatType trace_mem; - for (trace_mem::instance_iter it = trace_mem::beginInstances(), end_it = trace_mem::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_mem::instance_snapshot()) { - os << it->getName() << "(KiB)"; + os << it.getName() << "(KiB)"; for (S32 frame = 1; frame <= frame_count; frame++) { - os << ", " << scene_load_recording.getPrevRecording(frame_count - frame).getMax(*it).valueInUnits(); + os << ", " << scene_load_recording.getPrevRecording(frame_count - frame).getMax(it).valueInUnits(); } os << '\n'; diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 4546e199c3..7e137ca941 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -56,6 +56,10 @@ #include "llcontrolavatar.h" #include "llvotree.h" +// improved normals debug +#include "llformat.h" +#include "llselectmgr.h" +// static LLTrace::BlockTimerStatHandle FTM_FRUSTUM_CULL("Frustum Culling"); static LLTrace::BlockTimerStatHandle FTM_CULL_REBOUND("Cull Rebound Partition"); @@ -2267,45 +2271,131 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE) void renderNormals(LLDrawable* drawablep) { LLVertexBuffer::unbind(); - + // FIX and improve renderNormals debug + static LLCachedControl showSelectedOnly(*LLUI::getInstance()->mSettingGroups["config"], "OnlyShowSelectedNormals"); + // LLVOVolume* vol = drawablep->getVOVolume(); if (vol) { + // FIX and improve renderNormals debug + if(showSelectedOnly && !drawablep->getVObj()->isSelected()) + { + drawablep->getVObj()->setDebugText(""); + return; + } + // LLVolume* volume = vol->getVolume(); gGL.pushMatrix(); gGL.multMatrix((F32*) vol->getRelativeXform().mMatrix); + // FIX and improve renderNormals debug + // LLMatrix3 mat_norm {vol->getRelativeXformInvTrans()}; + LLMatrix3 scale_inverse; + auto scale = drawablep->getScale(); + // We need something like an inverse transpose, however + // we do not use the object rotation as it will be applied in the world transform + // but we do need to apply the inverse scale^2(1) as the world transform does a scale too. + // transpose of a scale only matrix is a lot of nothing, so skip it. + scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / scale.mV[VX], + LLVector3(0.0, 1.0, 0.0) / scale.mV[VY], + LLVector3(0.0, 0.0, 1.0) / scale.mV[VZ]); + LLMatrix4a inv_scale_mat; + inv_scale_mat.loadu(scale_inverse); + LLMatrix3 mat_norm = scale_inverse * scale_inverse; + LLMatrix4a invtranspose; + invtranspose.loadu(mat_norm); + // + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLVector4a scale(gSavedSettings.getF32("RenderDebugNormalScale")); + // FIX and improve renderNormals debug + // LLVector4a scale(gSavedSettings.getF32("RenderDebugNormalScale")); + static LLCachedControl hairlen(*LLUI::getInstance()->mSettingGroups["config"], "RenderDebugNormalScale"); + + LLSelectNode* selectionNode = nullptr; + auto face_select = LLSelectMgr::getInstance()->getTEMode(); + if(showSelectedOnly) + { + auto objp = drawablep->getVObj(); + if(!objp) + { + return; + } + objp->setDebugText( + llformat( + "obj scale = <%.3f,%.3f,%.3f>", + scale.mV[VX],scale.mV[VY],scale.mV[VZ])); + if(face_select) + { + LLObjectSelectionHandle sel = LLSelectMgr::getInstance()->getSelection(); + selectionNode = sel.get()->findNode(objp); + } + } + // for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) { + // FIX and improve renderNormals debug + if(face_select && !selectionNode->isTESelected(i)) + { + continue; + } + // const LLVolumeFace& face = volume->getVolumeFace(i); + // FIX and improve renderNormals debug + gGL.begin(LLRender::LINES); + gGL.diffuseColor4f(1,1,0,1); // Yellow normals + // for (S32 j = 0; j < face.mNumVertices; ++j) { - gGL.begin(LLRender::LINES); - LLVector4a n,p; + // FIX and improve renderNormals debug + // gGL.begin(LLRender::LINES); + // LLVector4a n,p; - n.setMul(face.mNormals[j], scale); - p.setAdd(face.mPositions[j], n); + // n.setMul(face.mNormals[j], scale); + // p.setAdd(face.mPositions[j], n); - gGL.diffuseColor4f(1,1,1,1); + // gGL.diffuseColor4f(1,1,1,1); + // gGL.vertex3fv(face.mPositions[j].getF32ptr()); + // gGL.vertex3fv(p.getF32ptr()); + // + // if (face.mTangents) + // { + // n.setMul(face.mTangents[j], scale); + // p.setAdd(face.mPositions[j], n); + // gGL.vertex3fv(face.mPositions[j].getF32ptr()); + // gGL.vertex3fv(p.getF32ptr()); + // } + LLVector4a n,ni,p; + n = face.mNormals[j]; + invtranspose.affineTransform(n, ni); + ni.normalize3fast(); + n.setMul(ni, (F32)hairlen); + inv_scale_mat.affineTransform(n, ni); // overcompensate for the fact we draw "through" the model transform + p.setAdd(face.mPositions[j], ni); gGL.vertex3fv(face.mPositions[j].getF32ptr()); gGL.vertex3fv(p.getF32ptr()); - - if (face.mTangents) + } + gGL.flush(); + if (face.mTangents) + { + // gGL.begin(LLRender::LINES); + gGL.diffuseColor4f(0,0,1,1); // blue tangents. + for (S32 j = 0; j < face.mNumVertices; ++j) { - n.setMul(face.mTangents[j], scale); - p.setAdd(face.mPositions[j], n); - - gGL.diffuseColor4f(0,1,1,1); + LLVector4a t,ti,p; + t = face.mTangents[j]; + // invtranspose.affineTransform(t, ti); + t.mul((F32)hairlen); + inv_scale_mat.affineTransform(t, ti); // overcompensate for the fact we draw "through" the model transform + p.setAdd(face.mPositions[j], ti); gGL.vertex3fv(face.mPositions[j].getF32ptr()); gGL.vertex3fv(p.getF32ptr()); - } - gGL.end(); + } } + gGL.end(); + // } gGL.popMatrix(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 546c432382..1d4bf9bf54 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -787,9 +787,9 @@ bool idle_startup() if(!start_messaging_system( message_template_path, port, - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), + LLVersionInfo::instance().getMajor(), + LLVersionInfo::instance().getMinor(), + LLVersionInfo::instance().getPatch(), FALSE, std::string(), responder, @@ -2247,12 +2247,14 @@ bool idle_startup() { LLStartUp::setStartupState( STATE_AGENT_SEND ); } - LLMessageSystem* msg = gMessageSystem; - while (msg->checkAllMessages(gFrameCount, gServicePump)) { - display_startup(); + LockMessageChecker lmc(gMessageSystem); + while (lmc.checkAllMessages(gFrameCount, gServicePump)) + { + display_startup(); + } + lmc.processAcks(); } - msg->processAcks(); display_startup(); return FALSE; } @@ -2311,25 +2313,27 @@ bool idle_startup() //--------------------------------------------------------------------- if (STATE_AGENT_WAIT == LLStartUp::getStartupState()) { - LLMessageSystem* msg = gMessageSystem; - while (msg->checkAllMessages(gFrameCount, gServicePump)) { - if (gAgentMovementCompleted) + LockMessageChecker lmc(gMessageSystem); + while (lmc.checkAllMessages(gFrameCount, gServicePump)) { - // Sometimes we have more than one message in the - // queue. break out of this loop and continue - // processing. If we don't, then this could skip one - // or more login steps. - break; + if (gAgentMovementCompleted) + { + // Sometimes we have more than one message in the + // queue. break out of this loop and continue + // processing. If we don't, then this could skip one + // or more login steps. + break; + } + else + { + LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got " + << gMessageSystem->getMessageName() << LL_ENDL; + } + display_startup(); } - else - { - LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got " - << msg->getMessageName() << LL_ENDL; - } - display_startup(); + lmc.processAcks(); } - msg->processAcks(); display_startup(); @@ -3180,13 +3184,29 @@ void login_callback(S32 option, void *userdata) void show_release_notes_if_required() { static bool release_notes_shown = false; - if (!release_notes_shown && (LLVersionInfo::getChannelAndVersion() != gLastRunVersion) - && LLVersionInfo::getViewerMaturity() != LLVersionInfo::TEST_VIEWER // don't show Release Notes for the test builds + // We happen to know that instantiating LLVersionInfo implicitly + // instantiates the LLEventMailDrop named "relnotes", which we (might) use + // below. If viewer release notes stop working, might be because that + // LLEventMailDrop got moved out of LLVersionInfo and hasn't yet been + // instantiated. + if (!release_notes_shown && (LLVersionInfo::instance().getChannelAndVersion() != gLastRunVersion) + && LLVersionInfo::instance().getViewerMaturity() != LLVersionInfo::TEST_VIEWER // don't show Release Notes for the test builds && gSavedSettings.getBOOL("UpdaterShowReleaseNotes") && !gSavedSettings.getBOOL("FirstLoginThisInstall")) { - LLSD info(LLAppViewer::instance()->getViewerInfo()); - LLWeb::loadURLInternal(info["VIEWER_RELEASE_NOTES_URL"]); + // Instantiate a "relnotes" listener which assumes any arriving event + // is the release notes URL string. Since "relnotes" is an + // LLEventMailDrop, this listener will be invoked whether or not the + // URL has already been posted. If so, it will fire immediately; + // otherwise it will fire whenever the URL is (later) posted. Either + // way, it will display the release notes as soon as the URL becomes + // available. + LLEventPumps::instance().obtain("relnotes").listen( + "showrelnotes", + [](const LLSD& url){ + LLWeb::loadURLInternal(url.asString()); + return false; + }); release_notes_shown = true; } } @@ -3777,7 +3797,7 @@ void LLStartUp::initNameCache() cache_inst->setUseUsernames(gSavedSettings.getBOOL("NameTagShowUsernames")); // Legacy name/Username format - LLAvatarName::setUseLegacyFormat(gSavedSettings.getBOOL("FSNameTagShowLegacyUsernames")); + LLAvatarNameCache::getInstance()->setUseUsernames(gSavedSettings.getBOOL("NameTagShowUsernames")); // FIRE-6659: Legacy "Resident" name toggle LLAvatarName::setTrimResidentSurname(gSavedSettings.getBOOL("FSTrimLegacyNames")); } diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 9bfa66c4dd..f5b18d7626 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -145,6 +145,7 @@ public: static LLViewerStats::PhaseMap& getPhases() { return *sPhases; } private: + friend class LLStartupListener; static LLSLURL sStartSLURL; static std::string sStartSLURLString; diff --git a/indra/newview/llstartuplistener.cpp b/indra/newview/llstartuplistener.cpp index d9a21f908e..5770b595d0 100644 --- a/indra/newview/llstartuplistener.cpp +++ b/indra/newview/llstartuplistener.cpp @@ -35,7 +35,7 @@ // external library headers // other Linden headers #include "llstartup.h" - +#include "stringize.h" LLStartupListener::LLStartupListener(/* LLStartUp* instance */): LLEventAPI("LLStartUp", "Access e.g. LLStartup::postStartupState()") /* , @@ -43,9 +43,33 @@ LLStartupListener::LLStartupListener(/* LLStartUp* instance */): { add("postStartupState", "Refresh \"StartupState\" listeners with current startup state", &LLStartupListener::postStartupState); + add("getStateTable", "Reply with array of EStartupState string names", + &LLStartupListener::getStateTable); } void LLStartupListener::postStartupState(const LLSD&) const { LLStartUp::postStartupState(); } + +void LLStartupListener::getStateTable(const LLSD& event) const +{ + Response response(LLSD(), event); + + // This relies on our knowledge that STATE_STARTED is the very last + // EStartupState value. If that ever stops being true, we're going to lie + // without realizing it. I can think of no reliable way to test whether + // the enum has been extended *beyond* STATE_STARTED. We could, of course, + // test whether stuff has been inserted before it, by testing its + // numerical value against the constant value as of the last time we + // looked; but that's pointless, as values inserted before STATE_STARTED + // will continue to work fine. The bad case is if new symbols get added + // *after* it. + LLSD table; + // note <= comparison: we want to *include* STATE_STARTED. + for (LLSD::Integer istate{0}; istate <= LLSD::Integer(STATE_STARTED); ++istate) + { + table.append(LLStartUp::startupStateToString(EStartupState(istate))); + } + response["table"] = table; +} diff --git a/indra/newview/llstartuplistener.h b/indra/newview/llstartuplistener.h index a35e11f6eb..0b4380a568 100644 --- a/indra/newview/llstartuplistener.h +++ b/indra/newview/llstartuplistener.h @@ -40,6 +40,7 @@ public: private: void postStartupState(const LLSD&) const; + void getStateTable(const LLSD&) const; //LLStartup* mStartup; }; diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index a18b4974e8..d56c98a516 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -249,7 +249,7 @@ bool LLSysWellWindow::isWindowEmpty() //--------------------------------------------------------------------------------- LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) : - LLPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent) + LLPanel(LLPanel::Params()), mChiclet(NULL) { buildFromFile( "panel_fs_activeim_row.xml"); diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 92cd5348c4..6524f8df4d 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -236,7 +236,6 @@ private: LLIMChiclet* mChiclet; private: LLButton* mCloseBtn; - const LLSysWellWindow* mParent; }; // [FS communication UI] diff --git a/indra/newview/lltexturestats.cpp b/indra/newview/lltexturestats.cpp index b55b4d9ca4..8f4b7d000c 100644 --- a/indra/newview/lltexturestats.cpp +++ b/indra/newview/lltexturestats.cpp @@ -46,8 +46,8 @@ void send_texture_stats_to_sim(const LLSD &texture_stats) LLUUID agent_id = gAgent.getID(); texture_stats_report["agent_id"] = agent_id; texture_stats_report["region_id"] = gAgent.getRegion()->getRegionID(); - texture_stats_report["viewer_channel"] = LLVersionInfo::getChannel(); - texture_stats_report["viewer_version"] = LLVersionInfo::getVersion(); + texture_stats_report["viewer_channel"] = LLVersionInfo::instance().getChannel(); + texture_stats_report["viewer_version"] = LLVersionInfo::instance().getVersion(); texture_stats_report["stats_data"] = texture_stats; std::string texture_cap_url = gAgent.getRegion()->getCapability("TextureStats"); diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index 0c5e8f04f0..a1c793f85a 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -634,16 +634,8 @@ S32 LLToast::notifyParent(const LLSD& info) //static void LLToast::updateClass() { - // Minimize calls to getInstances per frame - //for (LLInstanceTracker::instance_iter iter = LLInstanceTracker::beginInstances(); - // iter != LLInstanceTracker::endInstances(); ) - - LLInstanceTracker::instance_iter end = LLInstanceTracker::endInstances(); - for (LLInstanceTracker::instance_iter iter = LLInstanceTracker::beginInstances(); iter != end; ) - // + for (auto& toast : LLInstanceTracker::instance_snapshot()) { - LLToast& toast = *iter++; - toast.updateHoveredState(); } } @@ -651,22 +643,6 @@ void LLToast::updateClass() // static void LLToast::cleanupToasts() { - LLToast * toastp = NULL; - - while (LLInstanceTracker::instanceCount() > 0) - { - { // Need to scope iter to allow deletion - LLInstanceTracker::instance_iter iter = LLInstanceTracker::beginInstances(); - toastp = &(*iter); - } - - //LL_INFOS() << "Cleaning up toast id " << toastp->getNotificationID() << LL_ENDL; - - // LLToast destructor will remove it from the LLInstanceTracker. - if (!toastp) - break; // Don't get stuck in the loop if a null pointer somehow got on the list - - delete toastp; - } + LLInstanceTracker::instance_snapshot().deleteAll(); } diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 48a59bfdc1..30414580d3 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -221,14 +221,31 @@ BOOL LLToolPie::handleRightMouseUp(S32 x, S32 y, MASK mask) return LLTool::handleRightMouseUp(x, y, mask); } +BOOL LLToolPie::handleScrollWheelAny(S32 x, S32 y, S32 clicks_x, S32 clicks_y) +{ + BOOL res = FALSE; + // mHoverPick should have updated on its own and we should have a face + // in LLViewerMediaFocus in case of media, so just reuse mHoverPick + if (mHoverPick.mUVCoords.mV[VX] >= 0.f && mHoverPick.mUVCoords.mV[VY] >= 0.f) + { + res = LLViewerMediaFocus::getInstance()->handleScrollWheel(mHoverPick.mUVCoords, clicks_x, clicks_y); + } + else + { + // this won't provide correct coordinates in case of object selection + res = LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks_x, clicks_y); + } + return res; +} + BOOL LLToolPie::handleScrollWheel(S32 x, S32 y, S32 clicks) { - return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); + return handleScrollWheelAny(x, y, 0, clicks); } BOOL LLToolPie::handleScrollHWheel(S32 x, S32 y, S32 clicks) { - return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); + return handleScrollWheelAny(x, y, clicks, 0); } // True if you selected an object. diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index ad7575550b..34640d7f9c 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -49,6 +49,7 @@ public: virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + BOOL handleScrollWheelAny(S32 x, S32 y, S32 clicks_x, S32 clicks_y); virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual BOOL handleScrollHWheel(S32 x, S32 y, S32 clicks); virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 78981fe927..9a256f574e 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -134,11 +134,11 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std:: std::string user_agent = llformat("%s %d.%d.%d (%d)", - LLVersionInfo::getChannel().c_str(), - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), - LLVersionInfo::getBuild()); + LLVersionInfo::instance().getChannel().c_str(), + LLVersionInfo::instance().getMajor(), + LLVersionInfo::instance().getMinor(), + LLVersionInfo::instance().getPatch(), + LLVersionInfo::instance().getBuild()); httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); @@ -177,11 +177,11 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s std::string user_agent = llformat("%s %d.%d.%d (%d)", - LLVersionInfo::getChannel().c_str(), - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), - LLVersionInfo::getBuild()); + LLVersionInfo::instance().getChannel().c_str(), + LLVersionInfo::instance().getMajor(), + LLVersionInfo::instance().getMinor(), + LLVersionInfo::instance().getPatch(), + LLVersionInfo::instance().getBuild()); httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 72906911ff..abd6e31d67 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -223,7 +223,7 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string // // LLURLDispatcherImpl::regionNameCallback, // slurl.getSLURLString(), - LLSLURL hyper = slurl; + LLSLURL oSLURL = slurl; #ifdef OPENSIM // std::string grid = slurl.getGrid(); std::string current_grid = LLGridManager::getInstance()->getGrid(); @@ -232,7 +232,7 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string if ((grid != current_grid) && (!LLGridManager::getInstance()->isInOpenSim() || (!slurl.getHypergrid() && gatekeeper.empty()))) { - std::string dest = hyper.getSLURLString(); + std::string dest = slurl.getSLURLString(); if (!dest.empty()) { LLSD args; @@ -245,13 +245,13 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string } else if(!gatekeeper.empty()) { - hyper = LLSLURL(gatekeeper + ":" + slurl.getRegion(), slurl.getPosition(), true); + oSLURL = LLSLURL(gatekeeper + ":" + slurl.getRegion(), slurl.getPosition(), true); } #endif //OPENSIM // // Request a region handle by name - LLWorldMapMessage::getInstance()->sendNamedRegionRequest(hyper.getRegion(), LLURLDispatcherImpl::regionNameCallback, hyper.getSLURLString(), LLUI::getInstance()->mSettingGroups["config"]->getBOOL("SLURLTeleportDirectly")); // don't teleport + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(oSLURL.getRegion(), LLURLDispatcherImpl::regionNameCallback, slurl.getSLURLString(), LLUI::getInstance()->mSettingGroups["config"]->getBOOL("SLURLTeleportDirectly")); // don't teleport return true; } diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index cb82d1de96..e8e2c5f8ac 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -26,9 +26,10 @@ */ #include "llviewerprecompiledheaders.h" -#include -#include +#include "llevents.h" +#include "lleventfilter.h" #include "llversioninfo.h" +#include "stringize.h" #include // Use configured file instead of compile time definitions to avoid @@ -47,47 +48,65 @@ // Set the version numbers in indra/VIEWER_VERSION // -//static +LLVersionInfo::LLVersionInfo(): + short_version(STRINGIZE(LL_VIEWER_VERSION_MAJOR << "." + << LL_VIEWER_VERSION_MINOR << "." + << LL_VIEWER_VERSION_PATCH)), + // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The + // macro expands to the string name of the channel, but without quotes. We + // need to turn it into a quoted string. LL_TO_STRING() does that. + mWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL)), + build_configuration(LLBUILD_CONFIG), // set in indra/cmake/BuildVersion.cmake + // instantiate an LLEventMailDrop with canonical name to listen for news + // from SLVersionChecker + mPump{new LLEventMailDrop("relnotes")}, + // immediately listen on mPump, store arriving URL into mReleaseNotes + mStore{new LLStoreListener(*mPump, mReleaseNotes)} +{ +} + +void LLVersionInfo::initSingleton() +{ + // We override initSingleton() not because we have dependencies on other + // LLSingletons, but because certain initializations call other member + // functions. We should refrain from calling methods until this object is + // fully constructed; such calls don't really belong in the constructor. + + // cache the version string + version = STRINGIZE(getShortVersion() << "." << getBuild()); +} + +LLVersionInfo::~LLVersionInfo() +{ +} + S32 LLVersionInfo::getMajor() { return LL_VIEWER_VERSION_MAJOR; } -//static S32 LLVersionInfo::getMinor() { return LL_VIEWER_VERSION_MINOR; } -//static S32 LLVersionInfo::getPatch() { return LL_VIEWER_VERSION_PATCH; } -//static S32 LLVersionInfo::getBuild() { return LL_VIEWER_VERSION_BUILD; } -//static -const std::string &LLVersionInfo::getVersion() +std::string LLVersionInfo::getVersion() { - static std::string version(""); - if (version.empty()) - { - std::ostringstream stream; - stream << LLVersionInfo::getShortVersion() << "." << LLVersionInfo::getBuild(); - // cache the version string - version = stream.str(); - } return version; } // -//static -const std::string &LLVersionInfo::getBuildVersion() +std::string LLVersionInfo::getBuildVersion() { static std::string build_version(""); if (build_version.empty()) @@ -100,59 +119,27 @@ const std::string &LLVersionInfo::getBuildVersion() return build_version; } // - -//static -const std::string &LLVersionInfo::getShortVersion() +std::string LLVersionInfo::getShortVersion() { - static std::string short_version(""); - if(short_version.empty()) - { - // cache the version string - std::ostringstream stream; - stream << LL_VIEWER_VERSION_MAJOR << "." - << LL_VIEWER_VERSION_MINOR << "." - << LL_VIEWER_VERSION_PATCH; - short_version = stream.str(); - } return short_version; } -namespace + +std::string LLVersionInfo::getChannelAndVersion() { - // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The - // macro expands to the string name of the channel, but without quotes. We - // need to turn it into a quoted string. LL_TO_STRING() does that. - /// Storage of the channel name the viewer is using. - // The channel name is set by hardcoded constant, - // or by calling LLVersionInfo::resetChannel() - std::string sWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL)); - - // Storage for the "version and channel" string. - // This will get reset too. - std::string sVersionChannel(""); - - // Same as above, with version number in Firestorm FSDATA - // format. Unlike the above, the channel name will always be the - // hardcoded version. - std::string sVersionChannelFS(""); -} - -//static -const std::string &LLVersionInfo::getChannelAndVersion() -{ - if (sVersionChannel.empty()) + if (mVersionChannel.empty()) { // cache the version string - sVersionChannel = LLVersionInfo::getChannel() + " " + LLVersionInfo::getVersion(); + mVersionChannel = getChannel() + " " + getVersion(); } - return sVersionChannel; + return mVersionChannel; } // Get version and channel in the format needed for FSDATA. -//static -const std::string &LLVersionInfo::getChannelAndVersionFS() +std::string LLVersionInfo::getChannelAndVersionFS() { + static std::string sVersionChannelFS; if (sVersionChannelFS.empty()) { // cache the version string @@ -169,25 +156,23 @@ const std::string &LLVersionInfo::getChannelAndVersionFS() } // -//static -const std::string &LLVersionInfo::getChannel() +std::string LLVersionInfo::getChannel() { // Above macro hackery results in extra quotes - fix it if it happens - if (LLStringUtil::startsWith(sWorkingChannelName, "\"") && sWorkingChannelName.size() > 2) + if (LLStringUtil::startsWith(mWorkingChannelName, "\"") && mWorkingChannelName.size() > 2) { - sWorkingChannelName = sWorkingChannelName.substr(1, sWorkingChannelName.size() - 2); + mWorkingChannelName = mWorkingChannelName.substr(1, mWorkingChannelName.size() - 2); } // - return sWorkingChannelName; + return mWorkingChannelName; } void LLVersionInfo::resetChannel(const std::string& channel) { - sWorkingChannelName = channel; - sVersionChannel.clear(); // Reset version and channel string til next use. + mWorkingChannelName = channel; + mVersionChannel.clear(); // Reset version and channel string til next use. } -//static LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity() { ViewerMaturity maturity; @@ -227,6 +212,11 @@ LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity() return maturity; } +std::string LLVersionInfo::getReleaseNotes() +{ + return mReleaseNotes; +} + // [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-05-08 (Catznip-2.6.0a) | Added: Catznip-2.6.0a const char* getBuildPlatformString() { @@ -237,13 +227,7 @@ const char* getBuildPlatformString() return "Win64"; #endif // _WIN64 #elif LL_SDL - #if LL_GNUC - #if ( defined(__amd64__) || defined(__x86_64__) ) return "Linux64"; - #else - return "Linux32"; - #endif - #endif #elif LL_DARWIN #if ( defined(__amd64__) || defined(__x86_64__) ) return "Darwin64"; @@ -255,17 +239,16 @@ const char* getBuildPlatformString() #endif } -const std::string& LLVersionInfo::getBuildPlatform() +std::string LLVersionInfo::getBuildPlatform() { - static std::string strPlatform = getBuildPlatformString(); + std::string strPlatform = getBuildPlatformString(); return strPlatform; } // [/SL:KB] -const std::string &LLVersionInfo::getBuildConfig() +std::string LLVersionInfo::getBuildConfig() { - static const std::string build_configuration(LLBUILD_CONFIG); // set in indra/cmake/BuildVersion.cmake return build_configuration; } diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h index 4ecb605891..a67577b020 100644 --- a/indra/newview/llversioninfo.h +++ b/indra/newview/llversioninfo.h @@ -28,8 +28,14 @@ #ifndef LL_LLVERSIONINFO_H #define LL_LLVERSIONINFO_H -#include #include "stdtypes.h" +#include "llsingleton.h" +#include +#include + +class LLEventMailDrop; +template +class LLStoreListener; /// /// This API provides version information for the viewer. This @@ -38,57 +44,61 @@ /// viewer code that wants to query the current version should /// use this API. /// -class LLVersionInfo +class LLVersionInfo: public LLSingleton { + LLSINGLETON(LLVersionInfo); + void initSingleton(); public: - /// return the major verion number as an integer - static S32 getMajor(); + ~LLVersionInfo(); - /// return the minor verion number as an integer - static S32 getMinor(); + /// return the major version number as an integer + S32 getMajor(); - /// return the patch verion number as an integer - static S32 getPatch(); + /// return the minor version number as an integer + S32 getMinor(); + + /// return the patch version number as an integer + S32 getPatch(); /// return the build number as an integer - static S32 getBuild(); + S32 getBuild(); /// return the full viewer version as a string like "2.0.0.200030" - static const std::string &getVersion(); + std::string getVersion(); // /// return the full viewer version as a string like "200030" - static const std::string &getBuildVersion(); + std::string getBuildVersion(); // /// return the viewer version as a string like "2.0.0" - static const std::string &getShortVersion(); + std::string getShortVersion(); /// return the viewer version and channel as a string /// like "Second Life Release 2.0.0.200030" - static const std::string &getChannelAndVersion(); + std::string getChannelAndVersion(); // Needed for fsdata version checking /// return the viewer version and hardcoded channel as a string /// like "Firestorm-Release 2.0.0 (200030)" - static const std::string &getChannelAndVersionFS(); + std::string getChannelAndVersionFS(); /// return the channel name, e.g. "Second Life" - static const std::string &getChannel(); + std::string getChannel(); /// return the CMake build type - static const std::string &getBuildConfig(); + std::string getBuildConfig(); /// reset the channel name used by the viewer. - static void resetChannel(const std::string& channel); + void resetChannel(const std::string& channel); // [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-05-08 (Catznip-2.6.0a) | Added: Catznip-2.6.0a /// Return the platform the viewer was built for - static const std::string& getBuildPlatform(); + std::string getBuildPlatform(); // [/SL:KB] /// return the bit width of an address - static const S32 getAddressSize() { return ADDRESS_SIZE; } + S32 getAddressSize() { return ADDRESS_SIZE; } typedef enum { @@ -97,11 +107,32 @@ public: BETA_VIEWER, RELEASE_VIEWER } ViewerMaturity; - static ViewerMaturity getViewerMaturity(); + ViewerMaturity getViewerMaturity(); - // return hash of HEAD - static std::string getGitHash(); - // + /// get the release-notes URL, once it becomes available -- until then, + /// return empty string + std::string getReleaseNotes(); + + static std::string getGitHash(); +private: + std::string version; + std::string short_version; + /// Storage of the channel name the viewer is using. + // The channel name is set by hardcoded constant, + // or by calling resetChannel() + std::string mWorkingChannelName; + // Storage for the "version and channel" string. + // This will get reset too. + std::string mVersionChannel; + std::string build_configuration; + std::string mReleaseNotes; + // Store unique_ptrs to the next couple things so we don't have to explain + // to every consumer of this header file all the details of each. + // mPump is the LLEventMailDrop on which we listen for SLVersionChecker to + // post the release-notes URL from the Viewer Version Manager. + std::unique_ptr mPump; + // mStore is an adapter that stores the release-notes URL in mReleaseNotes. + std::unique_ptr> mStore; }; #endif diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 8e8a288004..0ec2df9831 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -79,6 +79,7 @@ #include "llslurl.h" #include "llstartup.h" // [RLVa:KB] - Checked: 2015-12-27 (RLVa-1.5.0) +#include "rlvactions.h" #include "rlvcommon.h" // [/RLVa:KB] @@ -211,6 +212,15 @@ static bool handleAvatarHoverOffsetChanged(const LLSD& newvalue) bool handleSetShaderChanged(const LLSD& newvalue) // { +// [RLVa:KB] - @setenv + if ( (!RlvActions::canChangeEnvironment()) && (LLFeatureManager::getInstance()->isFeatureAvailable("WindLightUseAtmosShaders")) && (!gSavedSettings.getBOOL("WindLightUseAtmosShaders")) ) + { + gSavedSettings.setBOOL("WindLightUseAtmosShaders", TRUE); + return true; + } +// [/RLVa:KB] + + // changing shader level may invalidate existing cached bump maps, as the shader type determines the format of the bump map it expects - clear and repopulate the bump cache gBumpImageList.destroyGL(); gBumpImageList.restoreGL(); diff --git a/indra/newview/llviewercontrollistener.cpp b/indra/newview/llviewercontrollistener.cpp index d2484b2b23..3443bb644a 100644 --- a/indra/newview/llviewercontrollistener.cpp +++ b/indra/newview/llviewercontrollistener.cpp @@ -50,11 +50,9 @@ LLViewerControlListener::LLViewerControlListener() std::ostringstream groupnames; groupnames << "[\"group\"] is one of "; const char* delim = ""; - for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()), - cgkend(LLControlGroup::endKeys()); - cgki != cgkend; ++cgki) + for (const auto& key : LLControlGroup::key_snapshot()) { - groupnames << delim << '"' << *cgki << '"'; + groupnames << delim << '"' << key << '"'; delim = ", "; } groupnames << '\n'; @@ -181,11 +179,9 @@ void LLViewerControlListener::groups(LLSD const & request) { // No Info, we're not looking up either a group or a control name. Response response(LLSD(), request); - for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()), - cgkend(LLControlGroup::endKeys()); - cgki != cgkend; ++cgki) + for (const auto& key : LLControlGroup::key_snapshot()) { - response["groups"].append(*cgki); + response["groups"].append(key); } } diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 8a84c44de4..07cc3090ea 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -68,6 +68,43 @@ F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0}; #define MAX_SPACENAVIGATOR_INPUT 3000.0f #define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT +#if LIB_NDOF +std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr) +{ + if (! ptr) + { + return out << "nullptr"; + } + out << "NDOF_Device{ "; + out << "axes ["; + const char* delim = ""; + for (short axis = 0; axis < ptr->axes_count; ++axis) + { + out << delim << ptr->axes[axis]; + delim = ", "; + } + out << "]"; + out << ", buttons ["; + delim = ""; + for (short button = 0; button < ptr->btn_count; ++button) + { + out << delim << ptr->buttons[button]; + delim = ", "; + } + out << "]"; + out << ", range " << ptr->axes_min << ':' << ptr->axes_max; + // If we don't coerce these to unsigned, they're streamed as characters, + // e.g. ctrl-A or nul. + out << ", absolute " << unsigned(ptr->absolute); + out << ", valid " << unsigned(ptr->valid); + out << ", manufacturer '" << ptr->manufacturer << "'"; + out << ", product '" << ptr->product << "'"; + out << ", private " << ptr->private_data; + out << " }"; + return out; +} +#endif // LIB_NDOF + // ----------------------------------------------------------------------------- void LLViewerJoystick::updateEnabled(bool autoenable) { @@ -113,17 +150,17 @@ NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev) LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); if (joystick->mDriverState == JDS_UNINITIALIZED) { - LL_INFOS() << "HotPlugAddCallback: will use device:" << LL_ENDL; + LL_INFOS("joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL; // Disable for Linux till vs017 gets merged in to no having to support multiple version of libndofdev #ifndef LL_LINUX - ndof_dump(dev); + ndof_dump(stderr, dev); #endif // joystick->mNdofDev = dev; - joystick->mDriverState = JDS_INITIALIZED; - res = NDOF_KEEP_HOTPLUGGED; + joystick->mDriverState = JDS_INITIALIZED; + res = NDOF_KEEP_HOTPLUGGED; } joystick->updateEnabled(true); return res; @@ -137,12 +174,12 @@ void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev) LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); if (joystick->mNdofDev == dev) { - LL_INFOS() << "HotPlugRemovalCallback: joystick->mNdofDev=" + LL_INFOS("joystick") << "HotPlugRemovalCallback: joystick->mNdofDev=" << joystick->mNdofDev << "; removed device:" << LL_ENDL; // Disable for Linux till vs017 gets merged in to no having to support multiple version of libndofdev #ifndef LL_LINUX - ndof_dump(dev); + ndof_dump(stderr, dev); #endif // @@ -211,6 +248,7 @@ void LLViewerJoystick::init(bool autoenable) { if (mNdofDev) { + LL_DEBUGS("joystick") << "ndof_create() returned: " << mNdofDev << LL_ENDL; // Different joysticks will return different ranges of raw values. // Since we want to handle every device in the same uniform way, // we initialize the mNdofDev struct and we set the range @@ -229,16 +267,21 @@ void LLViewerJoystick::init(bool autoenable) // just have the absolute values instead. mNdofDev->absolute = 1; + LL_DEBUGS("joystick") << "ndof_init_first() received: " << mNdofDev << LL_ENDL; // init & use the first suitable NDOF device found on the USB chain if (ndof_init_first(mNdofDev, NULL)) { mDriverState = JDS_UNINITIALIZED; - LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + LL_WARNS("joystick") << "ndof_init_first FAILED" << LL_ENDL; +#ifndef LL_LINUX + ndof_dump_list(stderr); +#endif } else { mDriverState = JDS_INITIALIZED; } + LL_DEBUGS("joystick") << "ndof_init_first() left: " << mNdofDev << LL_ENDL; } else { @@ -276,8 +319,8 @@ void LLViewerJoystick::init(bool autoenable) { // No device connected, don't change any settings } - - LL_INFOS() << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" + + LL_INFOS("joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" << mNdofDev << "; libinit=" << libinit << LL_ENDL; #endif } @@ -288,7 +331,7 @@ void LLViewerJoystick::terminate() #if LIB_NDOF ndof_libcleanup(); - LL_INFOS() << "Terminated connection with NDOF device." << LL_ENDL; + LL_INFOS("joystick") << "Terminated connection with NDOF device." << LL_ENDL; mDriverState = JDS_UNINITIALIZED; #endif } @@ -1101,7 +1144,7 @@ std::string LLViewerJoystick::getDescription() bool LLViewerJoystick::isLikeSpaceNavigator() const { -#if LIB_NDOF +#if LIB_NDOF return (isJoystickInitialized() && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0 || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0 @@ -1125,10 +1168,10 @@ void LLViewerJoystick::setSNDefaults() const float platformScaleAvXZ = 2.f; const bool is_3d_cursor = true; #endif - + //gViewerWindow->alertXml("CacheWillClear"); - LL_INFOS() << "restoring SpaceNavigator defaults..." << LL_ENDL; - + LL_INFOS("joystick") << "restoring SpaceNavigator defaults..." << LL_ENDL; + gSavedSettings.setS32("JoystickAxis0", 1); // z (at) gSavedSettings.setS32("JoystickAxis1", 0); // x (slide) gSavedSettings.setS32("JoystickAxis2", 2); // y (up) @@ -1136,11 +1179,11 @@ void LLViewerJoystick::setSNDefaults() gSavedSettings.setS32("JoystickAxis4", 3); // roll gSavedSettings.setS32("JoystickAxis5", 5); // yaw gSavedSettings.setS32("JoystickAxis6", -1); - + gSavedSettings.setBOOL("Cursor3D", is_3d_cursor); gSavedSettings.setBOOL("AutoLeveling", true); gSavedSettings.setBOOL("ZoomDirect", false); - + gSavedSettings.setF32("AvatarAxisScale0", 1.f * platformScaleAvXZ); gSavedSettings.setF32("AvatarAxisScale1", 1.f * platformScaleAvXZ); gSavedSettings.setF32("AvatarAxisScale2", 1.f); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 19ae07a672..591320c4f3 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -412,7 +412,7 @@ std::string LLViewerMedia::getCurrentUserAgent() // Just in case we need to check browser differences in A/B test // builds. - std::string channel = LLVersionInfo::getChannel(); + std::string channel = LLVersionInfo::instance().getChannel(); // append our magic version number string to the browser user agent id // See the HTTP 1.0 and 1.1 specifications for allowed formats: @@ -422,7 +422,7 @@ std::string LLViewerMedia::getCurrentUserAgent() // http://www.mozilla.org/build/revised-user-agent-strings.html std::ostringstream codec; codec << "SecondLife/"; - codec << LLVersionInfo::getVersion(); + codec << LLVersionInfo::instance().getVersion(); codec << " (" << channel << "; " << skin_name << " skin)"; LL_INFOS() << codec.str() << LL_ENDL; @@ -2349,6 +2349,18 @@ void LLViewerMediaImpl::mouseDoubleClick(S32 x, S32 y, MASK mask, S32 button) } } +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::scrollWheel(const LLVector2& texture_coords, S32 scroll_x, S32 scroll_y, MASK mask) +{ + if (mMediaSource) + { + S32 x, y; + scaleTextureCoords(texture_coords, &x, &y); + + scrollWheel(x, y, scroll_x, scroll_y, mask); + } +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::scrollWheel(S32 x, S32 y, S32 scroll_x, S32 scroll_y, MASK mask) { diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 9467a138f0..512c5a8279 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -235,6 +235,7 @@ public: void mouseMove(const LLVector2& texture_coords, MASK mask); void mouseDoubleClick(const LLVector2& texture_coords, MASK mask); void mouseDoubleClick(S32 x, S32 y, MASK mask, S32 button = 0); + void scrollWheel(const LLVector2& texture_coords, S32 scroll_x, S32 scroll_y, MASK mask); void scrollWheel(S32 x, S32 y, S32 scroll_x, S32 scroll_y, MASK mask); void mouseCapture(); diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index 4247251f4a..052ec942db 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -371,13 +371,26 @@ BOOL LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, BOOL called_from_pa media_impl->handleUnicodeCharHere(uni_char); return true; } -BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) + +BOOL LLViewerMediaFocus::handleScrollWheel(const LLVector2& texture_coords, S32 clicks_x, S32 clicks_y) +{ + BOOL retval = FALSE; + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if (media_impl && media_impl->hasMedia()) + { + media_impl->scrollWheel(texture_coords, clicks_x, clicks_y, gKeyboard->currentMask(TRUE)); + retval = TRUE; + } + return retval; +} + +BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks_x, S32 clicks_y) { BOOL retval = FALSE; LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); if(media_impl && media_impl->hasMedia()) { - media_impl->scrollWheel(x, y, 0, clicks, gKeyboard->currentMask(TRUE)); + media_impl->scrollWheel(x, y, clicks_x, clicks_y, gKeyboard->currentMask(TRUE)); retval = TRUE; } return retval; diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index 763a6c1688..fa469c36e3 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -58,7 +58,8 @@ public: /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); /*virtual*/ BOOL handleKeyUp(KEY key, MASK mask, BOOL called_from_parent); /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); - BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + BOOL handleScrollWheel(const LLVector2& texture_coords, S32 clicks_x, S32 clicks_y); + BOOL handleScrollWheel(S32 x, S32 y, S32 clicks_x, S32 clicks_y); void update(); @@ -67,7 +68,8 @@ public: bool isFocusedOnFace(LLPointer objectp, S32 face); bool isHoveringOverFace(LLPointer objectp, S32 face); - + bool isHoveringOverFocused() { return mFocusedObjectID == mHoverObjectID && mFocusedObjectFace == mHoverObjectFace; }; + // These look up (by uuid) and return the values that were set with setFocusFace. They will return null if the objects have been destroyed. LLViewerMediaImpl* getFocusedMediaImpl(); LLViewerObject* getFocusedObject(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 09db1a7308..53e79e46f5 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -8788,15 +8788,16 @@ BOOL object_selected_and_point_valid(const LLSD& sdParam) } -// [RLVa:KB] - Checked: 2010-03-16 (RLVa-1.2.0a) | Added: RLVa-1.2.0a -/* BOOL object_is_wearable() { if (!isAgentAvatarValid()) { return FALSE; } - if (!object_selected_and_point_valid()) +// if (!object_selected_and_point_valid()) +// [RLVa:KB] - Checked: 2010-03-16 (RLVa-1.2.0a) | Added: RLVa-1.2.0a + if (!object_selected_and_point_valid(LLSD(0))) +// [/RLVa:KB] { return FALSE; } @@ -8806,8 +8807,6 @@ BOOL object_is_wearable() } return gAgentAvatarp->canAttachMoreObjects(); } -*/ -// [/RLVa:KB] class LLAttachmentPointFilled : public view_listener_t { @@ -10782,12 +10781,44 @@ class LLWorldEnvSettings : public view_listener_t bool handleEvent(const LLSD& userdata) { -// [RLVa:KB] - Checked: 2010-03-18 (RLVa-1.2.0a) | Modified: RLVa-1.0.0g - if (gRlvHandler.hasBehaviour(RLV_BHVR_SETENV)) +// [RLVa:KB] - @setenv + if (!RlvActions::canChangeEnvironment()) return true; // [/RLVa:KB] std::string event_name = userdata.asString(); +// FIRE-29785 fix daytime shortcuts for non-EEP +#ifdef OPENSIM + static std::map sky_presets = { + {"sunrise", "Sunrise"}, + {"noon", "Midday"}, + {"sunset", "Sunset"}, + {"midnight", "Midnight"} + }; + auto it = sky_presets.find(event_name); + if( LLGridManager::getInstance()->isInOpenSim() && + !LLEnvironment::instance().isExtendedEnvironmentEnabled() && + it != sky_presets.end() + ) + { + LLSettingsSky::ptr_t legacysky = nullptr; + LLSD messages; + legacysky = LLEnvironment::createSkyFromLegacyPreset(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", "skies", it->second + ".xml"), messages); + if (legacysky) + { + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, legacysky); + LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); + LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_FAST, true); + defocusEnvFloaters(); + } + else + { + LL_WARNS() << "Legacy windlight conversion failed for " << it->second << " existing env unchanged." << LL_ENDL; + } + return true; + } +#endif +// if (event_name == "sunrise") { @@ -11782,10 +11813,7 @@ void initialize_menus() enable.add("Object.EnableOpen", boost::bind(&enable_object_open)); enable.add("Object.EnableTouch", boost::bind(&enable_object_touch, _1)); enable.add("Object.EnableDelete", boost::bind(&enable_object_delete)); -// enable.add("Object.EnableWear", boost::bind(&object_is_wearable)); -// [RLVa:KB] - Checked: 2010-03-16 (RLVa-1.2.0a) | Added: RLVa-1.2.0a - enable.add("Object.EnableWear", boost::bind(&object_selected_and_point_valid, _2)); -// [/RLVa:KB] + enable.add("Object.EnableWear", boost::bind(&object_is_wearable)); enable.add("Object.EnableStandUp", boost::bind(&enable_object_stand_up)); enable.add("Object.EnableSit", boost::bind(&enable_object_sit, _1)); @@ -11869,11 +11897,8 @@ void initialize_menus() // [RLVa:KB] - Checked: RLVa-2.0.0 enable.add("RLV.MainToggleVisible", boost::bind(&rlvMenuMainToggleVisible, _1)); - //if (RlvActions::isRlvEnabled()) // FIRE-20539: Toolbar buttons don't show disabled state anymore - { - enable.add("RLV.CanShowName", boost::bind(&rlvMenuCanShowName)); - enable.add("RLV.EnableIfNot", boost::bind(&rlvMenuEnableIfNot, _2)); - } + enable.add("RLV.CanShowName", boost::bind(&rlvMenuCanShowName)); + enable.add("RLV.EnableIfNot", boost::bind(&rlvMenuEnableIfNot, _2)); // [/RLVa:KB] // Toggle internal web browser diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index ad2fd2dd27..7afd21e502 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2325,15 +2325,10 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const LLUUID destination; bool accept = true; -// [RLVa:KB] - Checked: 2010-09-23 (RLVa-1.2.1) - bool fRlvNotifyAccepted = false; -// [/RLVa:KB] // If user accepted, accept to proper folder, if user discarded, accept to trash. switch(button) { case IOR_ACCEPT: - destination = mFolderID; - // [RLVa:KB] - Checked: 2010-09-23 (RLVa-1.2.1) // Only treat the offer as 'Give to #RLV' if: // - the user has enabled the feature @@ -2341,28 +2336,28 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const // - the name starts with the prefix - mDesc format: '[OBJECTNAME]' ( http://slurl.com/... ) if ( (rlv_handler_t::isEnabled()) && (IM_TASK_INVENTORY_OFFERED == mIM) && (LLAssetType::AT_CATEGORY == mType) && (mDesc.find(RLV_PUTINV_PREFIX) == 1) ) { - fRlvNotifyAccepted = true; if (!RlvSettings::getForbidGiveToRLV()) { const LLUUID& idRlvRoot = RlvInventory::instance().getSharedRootID(); if (idRlvRoot.notNull()) mFolderID = idRlvRoot; - fRlvNotifyAccepted = false; // "accepted_in_rlv" is sent from RlvGiveToRLVTaskOffer *after* we have the folder - + // "accepted_in_rlv" is sent from RlvGiveToRLVTaskOffer *after* we have the folder RlvGiveToRLVTaskOffer* pOfferObserver = new RlvGiveToRLVTaskOffer(mTransactionID); gInventory.addObserver(pOfferObserver); } - } - - if (fRlvNotifyAccepted) - { - std::string::size_type idxToken = mDesc.find("' ( http://"); - if (std::string::npos != idxToken) - RlvBehaviourNotifyHandler::sendNotification("accepted_in_inv inv_offer " + mDesc.substr(1, idxToken - 1)); + else + { + std::string::size_type idxToken = mDesc.find("' ( http://"); + if (std::string::npos != idxToken) + { + RlvBehaviourNotifyHandler::sendNotification("accepted_in_inv inv_offer " + mDesc.substr(1, idxToken - 1)); + } + } } // [/RLVa:KB] + destination = mFolderID; //don't spam user if flooded if (check_offer_throttle(mFromName, true)) { diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 320090b0d8..e08fdb7a6c 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -397,10 +397,10 @@ public: void sendShapeUpdate(); -// U8 getAttachmentState() { return mAttachmentState; } // [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0a) | Added: RLVa-1.2.0a - U8 getAttachmentState() const { return mAttachmentState; } + U8 getAttachmentState() const { return mAttachmentState; } // [/RLVa:KB] +// U8 getAttachmentState() { return mAttachmentState; } F32 getAppAngle() const { return mAppAngle; } F32 getPixelArea() const { return mPixelArea; } diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index c39f7f1abf..8cf7e28abe 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -140,6 +140,7 @@ void LLViewerParcelMedia::update(LLParcel* parcel) if(mMediaImpl.isNull()) { + play(parcel); return; } @@ -204,6 +205,12 @@ void LLViewerParcelMedia::play(LLParcel* parcel) if (!gSavedSettings.getBOOL("AudioStreamingMedia")) return; + // This test appears all over the code and really should be facotred out into a single + // call that returns true/false (with option ask dialog) but that is outside of scope + // for this work so we'll just directly. + if (gSavedSettings.getS32("ParcelMediaAutoPlayEnable") == 0 ) + return; + std::string media_url = parcel->getMediaURL(); std::string media_current_url = parcel->getMediaCurrentURL(); std::string mime_type = parcel->getMediaType(); diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index 999d9092bd..bbbacce8fa 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -29,6 +29,8 @@ #ifndef LL_LLVIEWERPRECOMPILEDHEADERS_H #define LL_LLVIEWERPRECOMPILEDHEADERS_H +#include "llwin32headers.h" + // This file MUST be the first one included by each .cpp file // in viewer. // It is used to precompile headers for improved build speed. diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index ed075aeed9..01db3f755e 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -479,7 +479,7 @@ void send_stats() // send fps only for time app spends in foreground agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32(); - agent["version"] = LLVersionInfo::getChannelAndVersion(); + agent["version"] = LLVersionInfo::instance().getChannelAndVersion(); std::string language = LLUI::getLanguage(); agent["language"] = language; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 571cb3c711..f3656fa381 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2762,7 +2762,7 @@ void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid) else { // Don't care about viewer maturity - //switch (LLVersionInfo::getViewerMaturity()) + //switch (LLVersionInfo::instance().getViewerMaturity()) //{ //case LLVersionInfo::TEST_VIEWER: // new_bg_color = LLUIColorTable::instance().getColor( "MenuBarTestBgColor" ); diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 2193915dc7..2f4dd69fa3 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -1349,7 +1349,9 @@ void LLVOCache::writeCacheHeader() bool success = true ; { - LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); + // Fix bogus cache entry size warning + //LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); + LLAPRFile apr_file(mHeaderFileName, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp); //write the meta element success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ; @@ -1535,8 +1537,10 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry: { std::string filename; getObjectCacheFilename(handle, filename); - LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); - + // Fix bogus cache entry size warning + //LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); + LLAPRFile apr_file(filename, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp); + success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ; diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index 94915da82d..c087c59bb6 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -742,8 +742,9 @@ void LLGrassPartition::getGeometry(LLSpatialGroup* group) else { LL_WARNS() << "Object is 0 (not an alpha maybe)" << LL_ENDL; - if( facep->getViewerObject() ) - LL_WARNS() << typeid( *facep->getViewerObject() ).name() << LL_ENDL; + LLViewerObject *pObject{ facep->getViewerObject() }; + if( pObject ) + LL_WARNS() << typeid( *pObject ).name() << LL_ENDL; } // diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 8ffea0851b..8074e9b4db 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -339,15 +339,15 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mPlayRequestCount(0), mAvatarNameCacheConnection(), - mIsInTuningMode(false), - mIsInChannel(false), - mIsJoiningSession(false), - mIsWaitingForFonts(false), - mIsLoggingIn(false), - mIsLoggedIn(false), - mIsProcessingChannels(false), - mIsCoroutineActive(false), - mVivoxPump("vivoxClientPump") + mIsInTuningMode(false), + mIsInChannel(false), + mIsJoiningSession(false), + mIsWaitingForFonts(false), + mIsLoggingIn(false), + mIsLoggedIn(false), + mIsProcessingChannels(false), + mIsCoroutineActive(false), + mVivoxPump("vivoxClientPump") { mSpeakerVolume = scale_speaker_volume(0); @@ -391,7 +391,7 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump) // constructor will set up LLVoiceClient::getInstance() LLVivoxVoiceClient::getInstance()->mPump = pump; -// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", +// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", // boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); } @@ -578,7 +578,7 @@ void LLVivoxVoiceClient::connectorCreate() << ".log" << "" << vivoxLogLevel << "" << "" - << "" << LLVersionInfo::getChannel().c_str() << " " << LLVersionInfo::getVersion().c_str() << "" + << "" << LLVersionInfo::instance().getChannel() << " " << LLVersionInfo::instance().getVersion() << "" //<< "" //Name can cause problems per vivox. << "12" << "\n\n\n"; @@ -877,6 +877,23 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() } // + // VOICE-88: Cycle through [portbase..portbase+portrange) on + // successive tries because attempting to relaunch (after manually + // disabling and then re-enabling voice) with the same port can + // cause SLVoice's bind() call to fail with EADDRINUSE. We expect + // that eventually the OS will time out previous ports, which is + // why we cycle instead of incrementing indefinitely. + // Unfail voice connection failures + //U32 portbase = gSavedSettings.getU32("VivoxVoicePort"); + //static U32 portoffset = 0; + //static const U32 portrange = 100; + //std::string host(gSavedSettings.getString("VivoxVoiceHost")); + //U32 port = portbase + portoffset; + //portoffset = (portoffset + 1) % portrange; + //params.args.add("-i"); + //params.args.add(STRINGIZE(host << ':' << port)); + // + std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); if (loglevel.empty()) { @@ -957,6 +974,8 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() sGatewayPtr = LLProcess::create(params); + // Unfail voice connection failures + //mDaemonHost = LLHost(host.c_str(), port); mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort")); } else @@ -1133,8 +1152,6 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() bool LLVivoxVoiceClient::establishVoiceConnection() { - LLEventPump &voiceConnectPump = LLEventPumps::instance().obtain("vivoxClientPump"); - if (!mVoiceEnabled && mIsInitialized) { LL_WARNS("Voice") << "cannot establish connection; enabled "< Cut down wait on logout - //if (mConnected) - //{ - // mConnected = false; - // LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false))); - // LLEventPumps::instance().post("vivoxClientPump", vivoxevent); - //} - //mShutdownComplete = true; - // Need to check messages on the service pump for the connector shutdown response - // which sets mShutdownComplete to true! - while (gMessageSystem->checkAllMessages(gFrameCount, gServicePump)) + if (mConnected) { - // Do nothing - just check messages + mConnected = false; + LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false))); + mVivoxPump.post(vivoxevent); } - gMessageSystem->processAcks(); - // + mShutdownComplete = true; } #endif } @@ -1261,8 +1268,6 @@ bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait) bool LLVivoxVoiceClient::loginToVivox() { - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); - LLSD timeoutResult(LLSDMap("login", "timeout")); int loginRetryCount(0); @@ -1280,7 +1285,7 @@ bool LLVivoxVoiceClient::loginToVivox() send_login = false; } - LLSD result = llcoro::suspendUntilEventOnWithTimeout(voicePump, LOGIN_ATTEMPT_TIMEOUT, timeoutResult); + LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGIN_ATTEMPT_TIMEOUT, timeoutResult); LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; if (result.has("login")) @@ -1363,17 +1368,23 @@ void LLVivoxVoiceClient::logoutOfVivox(bool wait) if (wait) { - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); LLSD timeoutResult(LLSDMap("logout", "timeout")); + LLSD result; - LL_DEBUGS("Voice") - << "waiting for logout response on " - << voicePump.getName() - << LL_ENDL; + do + { + LL_DEBUGS("Voice") + << "waiting for logout response on " + << mVivoxPump.getName() + << LL_ENDL; - LLSD result = llcoro::suspendUntilEventOnWithTimeout(voicePump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + // Don't get confused by prior queued events -- note that it's + // very important that mVivoxPump is an LLEventMailDrop, which + // does queue events. + } while (! result["logout"]); } else { @@ -1387,8 +1398,6 @@ void LLVivoxVoiceClient::logoutOfVivox(bool wait) bool LLVivoxVoiceClient::retrieveVoiceFonts() { - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); - // Request the set of available voice fonts. refreshVoiceEffectLists(true); @@ -1396,7 +1405,7 @@ bool LLVivoxVoiceClient::retrieveVoiceFonts() LLSD result; do { - result = llcoro::suspendUntilEventOn(voicePump); + result = llcoro::suspendUntilEventOn(mVivoxPump); LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; if (result.has("voice_fonts")) @@ -1512,7 +1521,6 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo() bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) { - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); mIsJoiningSession = true; sessionStatePtr_t oldSession = mAudioSession; @@ -1601,7 +1609,7 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) // We are about to start a whole new session. Anything that MIGHT still be in our // maildrop is going to be stale and cause us much wailing and gnashing of teeth. // Just flush it all out and start new. - voicePump.flush(); + mVivoxPump.discard(); // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. @@ -1609,7 +1617,7 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) // This is a cheap way to make sure both have happened before proceeding. do { - result = llcoro::suspendUntilEventOnWithTimeout(voicePump, SESSION_JOIN_TIMEOUT, timeoutResult); + result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, SESSION_JOIN_TIMEOUT, timeoutResult); LL_INFOS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; if (result.has("session")) @@ -1723,13 +1731,12 @@ bool LLVivoxVoiceClient::terminateAudioSession(bool wait) if (wait) { - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); LLSD result; do { LLSD timeoutResult(LLSDMap("session", "timeout")); - result = llcoro::suspendUntilEventOnWithTimeout(voicePump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; if (result.has("session")) @@ -1931,7 +1938,6 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) LLSD timeoutEvent(LLSDMap("timeout", LLSD::Boolean(true))); - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); mIsInChannel = true; mMuteMicDirty = true; @@ -1983,7 +1989,7 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session) sendLocalAudioUpdates(); mIsInitialized = true; - LLSD result = llcoro::suspendUntilEventOnWithTimeout(voicePump, UPDATE_THROTTLE_SECONDS, timeoutEvent); + LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, UPDATE_THROTTLE_SECONDS, timeoutEvent); if (!result.has("timeout")) // logging the timeout event spams the log { LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; @@ -2054,14 +2060,13 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices() void LLVivoxVoiceClient::recordingAndPlaybackMode() { LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL; - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); while (true) { LLSD command; do { - command = llcoro::suspendUntilEventOn(voicePump); + command = llcoro::suspendUntilEventOn(mVivoxPump); LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(command) << LL_ENDL; } while (!command.has("recplay")); @@ -2094,7 +2099,6 @@ int LLVivoxVoiceClient::voiceRecordBuffer() LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL; - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); LLSD result; captureBufferRecordStartSendMessage(); @@ -2102,7 +2106,7 @@ int LLVivoxVoiceClient::voiceRecordBuffer() do { - result = llcoro::suspendUntilEventOnWithTimeout(voicePump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); + result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; } while (!result.has("recplay")); @@ -2124,7 +2128,6 @@ int LLVivoxVoiceClient::voicePlaybackBuffer() LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL; - LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump"); LLSD result; do @@ -2139,7 +2142,7 @@ int LLVivoxVoiceClient::voicePlaybackBuffer() // Update UI, should really use a separate callback. notifyVoiceFontObservers(); - result = llcoro::suspendUntilEventOnWithTimeout(voicePump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); + result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, CAPTURE_BUFFER_MAX_TIME, timeoutResult); LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; } while (!result.has("recplay")); @@ -2666,7 +2669,7 @@ void LLVivoxVoiceClient::tuningStart() mTuningMode = true; if (!mIsCoroutineActive) { - LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", + LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); } else if (mIsInChannel) @@ -3340,7 +3343,7 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st result["connector"] = LLSD::Boolean(false); } - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) @@ -3375,7 +3378,7 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString result["login"] = LLSD::String("response_ok"); } - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } @@ -3401,7 +3404,7 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu ("session", "failed") ("reason", LLSD::Integer(statusCode))); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } else { @@ -3419,7 +3422,7 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu LLSD vivoxevent(LLSDMap("handle", LLSD::String(sessionHandle)) ("session", "created")); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } } @@ -3444,7 +3447,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, LLSD vivoxevent(LLSDMap("handle", LLSD::String(sessionHandle)) ("session", "failed")); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } else { @@ -3463,7 +3466,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, LLSD vivoxevent(LLSDMap("handle", LLSD::String(sessionHandle)) ("session", "added")); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } } @@ -3506,7 +3509,7 @@ void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusStrin } LLSD vivoxevent(LLSDMap("logout", LLSD::Boolean(true))); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) @@ -3522,7 +3525,7 @@ void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string & LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false))); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } void LLVivoxVoiceClient::sessionAddedEvent( @@ -3631,7 +3634,7 @@ void LLVivoxVoiceClient::joinedAudioSession(const sessionStatePtr_t &session) LLSD vivoxevent(LLSDMap("handle", LLSD::String(session->mHandle)) ("session", "joined")); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); // Add the current user as a participant here. participantStatePtr_t participant(session->addParticipant(sipURIFromName(mAccountName))); @@ -3775,7 +3778,7 @@ void LLVivoxVoiceClient::leftAudioSession(const sessionStatePtr_t &session) LLSD vivoxevent(LLSDMap("handle", LLSD::String(session->mHandle)) ("session", "removed")); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } } @@ -3803,7 +3806,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( case 1: levent["login"] = LLSD::String("account_login"); - LLEventPumps::instance().post("vivoxClientPump", levent); + mVivoxPump.post(levent); break; case 2: break; @@ -3811,7 +3814,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( case 3: levent["login"] = LLSD::String("account_loggingOut"); - LLEventPumps::instance().post("vivoxClientPump", levent); + mVivoxPump.post(levent); break; case 4: @@ -3824,7 +3827,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( case 0: levent["login"] = LLSD::String("account_logout"); - LLEventPumps::instance().post("vivoxClientPump", levent); + mVivoxPump.post(levent); break; default: @@ -3859,7 +3862,7 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s } if (!result.isUndefined()) - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } void LLVivoxVoiceClient::mediaStreamUpdatedEvent( @@ -5282,7 +5285,7 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) if (!mIsCoroutineActive) { - LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", + LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); } else @@ -6694,7 +6697,7 @@ void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const st // receiving the last one. LLSD result(LLSDMap("voice_fonts", LLSD::Boolean(true))); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } notifyVoiceFontObservers(); mVoiceFontsReceived = true; @@ -6845,7 +6848,7 @@ void LLVivoxVoiceClient::enablePreviewBuffer(bool enable) else result["recplay"] = "quit"; - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); if(mCaptureBufferMode && mIsInChannel) { @@ -6866,7 +6869,7 @@ void LLVivoxVoiceClient::recordPreviewBuffer() mCaptureBufferRecording = true; LLSD result(LLSDMap("recplay", "record")); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) @@ -6889,7 +6892,7 @@ void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) mCaptureBufferPlaying = true; LLSD result(LLSDMap("recplay", "playback")); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } void LLVivoxVoiceClient::stopPreviewBuffer() @@ -6898,7 +6901,7 @@ void LLVivoxVoiceClient::stopPreviewBuffer() mCaptureBufferPlaying = false; LLSD result(LLSDMap("recplay", "quit")); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } bool LLVivoxVoiceClient::isPreviewRecording() diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 0cc4e64e45..afd7f15cce 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -2256,7 +2256,7 @@ void LLVOVolume::setNumTEs(const U8 num_tes) } else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed { - U8 end = mMediaImplList.size() ; + U8 end = (U8)(mMediaImplList.size()) ; for(U8 i = num_tes; i < end ; i++) { removeMediaImpl(i) ; @@ -5280,11 +5280,12 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, LL_WARNS_ONCE("RenderMaterials") << "Oh no! No binormals for this alpha blended face!" << LL_ENDL; } - bool selected = facep->getViewerObject()->isSelected(); - +// bool selected = facep->getViewerObject()->isSelected(); +// // if (selected && LLSelectMgr::getInstance()->mHideSelectedObjects) // [RLVa:KB] - Checked: 2010-11-29 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c const LLViewerObject* pObj = facep->getViewerObject(); + bool selected = pObj->isSelected(); if ( (pObj->isSelected() && LLSelectMgr::getInstance()->mHideSelectedObjects) && ( (!RlvActions::isRlvEnabled()) || ( ((!pObj->isHUDAttachment()) || (!gRlvAttachmentLocks.isLockedAttachment(pObj->getRootEdit()))) && diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index ad57fd590b..3aba7a87e3 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -91,7 +91,11 @@ void LLWatchdogEntry::start() void LLWatchdogEntry::stop() { - LLWatchdog::getInstance()->remove(this); + // this can happen very late in the shutdown sequence + if (! LLWatchdog::wasDeleted()) + { + LLWatchdog::getInstance()->remove(this); + } } // LLWatchdogTimeout diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 3114b0d0c3..a04c22789f 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -157,12 +157,12 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, const LLSD &default_subs) { LLSD substitution = default_subs; - substitution["VERSION"] = LLVersionInfo::getVersion(); - substitution["VERSION_MAJOR"] = LLVersionInfo::getMajor(); - substitution["VERSION_MINOR"] = LLVersionInfo::getMinor(); - substitution["VERSION_PATCH"] = LLVersionInfo::getPatch(); - substitution["VERSION_BUILD"] = LLVersionInfo::getBuild(); - substitution["CHANNEL"] = LLVersionInfo::getChannel(); + substitution["VERSION"] = LLVersionInfo::instance().getVersion(); + substitution["VERSION_MAJOR"] = LLVersionInfo::instance().getMajor(); + substitution["VERSION_MINOR"] = LLVersionInfo::instance().getMinor(); + substitution["VERSION_PATCH"] = LLVersionInfo::instance().getPatch(); + substitution["VERSION_BUILD"] = LLVersionInfo::instance().getBuild(); + substitution["CHANNEL"] = LLVersionInfo::instance().getChannel(); substitution["GRID"] = LLGridManager::getInstance()->getGridId(); substitution["GRID_LOWERCASE"] = utf8str_tolower(LLGridManager::getInstance()->getGridId()); substitution["OS"] = LLOSInfo::instance().getOSStringSimple(); diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h index 7e5818ba1c..524adba652 100644 --- a/indra/newview/llwindebug.h +++ b/indra/newview/llwindebug.h @@ -29,7 +29,11 @@ #include "stdtypes.h" #include "llwin32headerslean.h" + +#pragma warning (push) +#pragma warning (disable:4091) // a microsoft header has warnings. Very nice. #include +#pragma warning (pop) class LLWinDebug: public LLSingleton diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index 0693d08dfb..663a75156f 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -43,6 +43,7 @@ // other Linden headers #include "llerror.h" +#include "lleventcoro.h" #include "stringize.h" #include "llxmlrpctransaction.h" #include "llsecapi.h" @@ -366,6 +367,8 @@ public: // whether successful or not, send reply on requested LLEventPump replyPump.post(data); + // need to wake up the loginCoro now + llcoro::suspend(); // Because mTransaction is a boost::scoped_ptr, deleting this object // frees our LLXMLRPCTransaction object. diff --git a/indra/newview/quickprefs.cpp b/indra/newview/quickprefs.cpp index 0b9a7c091b..6334549f51 100644 --- a/indra/newview/quickprefs.cpp +++ b/indra/newview/quickprefs.cpp @@ -52,12 +52,12 @@ #include "llspinctrl.h" #include "lltoolbarview.h" #include "llviewercontrol.h" +#include "llviewernetwork.h" // for LLGridManager #include "llviewerregion.h" #include "llvoavatar.h" #include "llvoavatarself.h" #include "rlvhandler.h" -std::string unescape_name(const std::string& name); class FSSettingsCollector : public LLInventoryCollectFunctor { public: @@ -288,6 +288,27 @@ void FloaterQuickPrefs::loadDayCyclePresets(const std::multimapadd(preset_name, LLSD(asset_id)); } } +// Opensim legacy windlight support +// Opensim may support both environment and extenvironment caps on the same region +// we also need these disabled in SL on the OpenSim build. +#ifdef OPENSIM + if(LLGridManager::getInstance()->isInOpenSim()) + { + LL_DEBUGS("WindlightCaps") << "Adding legacy day cycle presets to QP" << LL_ENDL; + // WL still supported + if (!daycycle_map.empty() && !LLEnvironment::getInstance()->mLegacyDayCycles.empty()) + { + mDayCyclePresetsCombo->addSeparator(); + } + for(const auto& preset_name : LLEnvironment::getInstance()->mLegacyDayCycles) + { + // we add by name and only build the envp on demand + LL_DEBUGS("WindlightCaps") << "Adding legacy day cycle " << preset_name << LL_ENDL; + mDayCyclePresetsCombo->add(preset_name, LLSD(preset_name)); + } + LL_DEBUGS("WindlightCaps") << "Done: Adding legacy day cycle presets to QP" << LL_ENDL; + } +#endif } void FloaterQuickPrefs::loadSkyPresets(const std::multimap& sky_map) @@ -308,6 +329,28 @@ void FloaterQuickPrefs::loadSkyPresets(const std::multimap& mWLPresetsCombo->add(preset_name, LLSD(asset_id)); } } +// Opensim legacy windlight support +// Opensim may support both environment and extenvironment caps on the same region +// we also need these disabled in SL on the OpenSim build. +#ifdef OPENSIM + if(LLGridManager::getInstance()->isInOpenSim()) + { + LL_DEBUGS("WindlightCaps") << "Adding legacy sky presets to QP" << LL_ENDL; + // WL still supported + if (!sky_map.empty() && !LLEnvironment::getInstance()->mLegacySkies.empty()) + { + mWLPresetsCombo->addSeparator(); + } + for(const auto& preset_name : LLEnvironment::getInstance()->mLegacySkies) + { + // we add by name and only build the envp on demand + LL_DEBUGS("WindlightCaps") << "Adding legacy sky " << preset_name << LL_ENDL; + // append "WL" to denote legacy. Have to create a new string not update the reference. + mWLPresetsCombo->add(preset_name+ "[WL]", LLSD(preset_name)); + } + LL_DEBUGS("WindlightCaps") << "Done: Adding legacy sky presets to QP" << LL_ENDL; + } +#endif } void FloaterQuickPrefs::loadWaterPresets(const std::multimap& water_map) @@ -328,6 +371,27 @@ void FloaterQuickPrefs::loadWaterPresets(const std::multimapadd(preset_name, LLSD(asset_id)); } } +// Opensim legacy windlight support +// Opensim may support both environment and extenvironment caps on the same region +// we also need these disabled in SL on the OpenSim build. +#ifdef OPENSIM + if(LLGridManager::getInstance()->isInOpenSim()) + { + LL_DEBUGS("WindlightCaps") << "Adding legacy presets to QP" << LL_ENDL; + // WL still supported + if (!water_map.empty() && !LLEnvironment::getInstance()->mLegacyWater.empty()) + { + mWaterPresetsCombo->addSeparator(); + } + for(const auto& preset_name : LLEnvironment::getInstance()->mLegacyWater) + { + // we add by name and only build the envp on demand + LL_DEBUGS("WindlightCaps") << "Adding legacy water " << preset_name << LL_ENDL; + mWaterPresetsCombo->add(preset_name, LLSD(preset_name)); + } + LL_DEBUGS("WindlightCaps") << "Done: Adding legacy water presets to QP" << LL_ENDL; + } +#endif } void FloaterQuickPrefs::loadPresets() @@ -388,33 +452,109 @@ void FloaterQuickPrefs::setSelectedEnvironment() // day cycle. If no fixed sky or fixed water is set, they are either // defined in the day cycle or inherited from a higher environment level. LLSettingsDay::ptr_t day = LLEnvironment::instance().getEnvironmentDay(LLEnvironment::ENV_LOCAL); - if (day && day->getAssetId().notNull()) + if (day) { //LL_INFOS() << "EEP: day name = " << day->getName() << " - asset id = " << day->getAssetId() << LL_ENDL; - - mDayCyclePresetsCombo->selectByValue(LLSD(day->getAssetId())); - - // Water is part of a day cycle - mWLPresetsCombo->selectByValue(LLSD(PRESET_NAME_DAY_CYCLE)); - mWaterPresetsCombo->selectByValue(LLSD(PRESET_NAME_DAY_CYCLE)); + if( day->getAssetId().notNull()) + { // EEP processing + mDayCyclePresetsCombo->selectByValue(LLSD(day->getAssetId())); + // Sky and Water are part of a day cycle in EEP + mWLPresetsCombo->selectByValue(LLSD(PRESET_NAME_DAY_CYCLE)); + mWaterPresetsCombo->selectByValue(LLSD(PRESET_NAME_DAY_CYCLE)); + } +#ifdef OPENSIM + else if (LLGridManager::getInstance()->isInOpenSim()) + { + auto preset_name = day->getName(); + LL_DEBUGS("WindlightCaps") << "Current Day cycle is " << preset_name << LL_ENDL; + if (preset_name == "_default_") + { + preset_name = "Default"; + } + mDayCyclePresetsCombo->selectByValue(preset_name); + // Sky is part of day so treat that as day cycle + mWLPresetsCombo->selectByValue(LLSD(PRESET_NAME_DAY_CYCLE)); + // Water is not part of legacy day so we need to hunt around + LLSettingsWater::ptr_t water = LLEnvironment::instance().getEnvironmentFixedWater(LLEnvironment::ENV_LOCAL); + if (water) + { + // This is going to be possible. OS will support both Legacy and EEP + // so having a water EEP asset with a Legacy day cycle could happen. + LLUUID asset_id = water->getAssetId(); + if (asset_id.notNull()) + { + mWaterPresetsCombo->selectByValue(LLSD(asset_id)); + } + else + { + //mWaterPresetsCombo->selectByValue(LLSD(water->getName())); + std::string preset_name = water->getName(); + if (preset_name == "_default_") + { + preset_name = "Default"; + } + mWaterPresetsCombo->selectByValue(preset_name); + } + } + } +#endif //OPENSIM + } + else + { + mDayCyclePresetsCombo->selectByValue(LLSD(PRESET_NAME_NONE)); } LLSettingsSky::ptr_t sky = LLEnvironment::instance().getEnvironmentFixedSky(LLEnvironment::ENV_LOCAL); - if (sky && sky->getAssetId().notNull()) + if (sky) { //LL_INFOS() << "EEP: sky name = " << sky->getName() << " - asset id = " << sky->getAssetId() << LL_ENDL; - - mWLPresetsCombo->selectByValue(LLSD(sky->getAssetId())); + if(sky->getAssetId().notNull()) + { + mWLPresetsCombo->selectByValue(LLSD(sky->getAssetId())); + } +#ifdef OPENSIM + else if (LLGridManager::getInstance()->isInOpenSim()) + { + auto preset_name = sky->getName(); + LL_DEBUGS("WindlightCaps") << "Current Sky is " << preset_name << LL_ENDL; + if (preset_name == "_default_") + { + preset_name = "Default"; + } + mWLPresetsCombo->selectByValue(preset_name); + } +#endif } - + // Water is not part of legacy day so we need to hunt around LLSettingsWater::ptr_t water = LLEnvironment::instance().getEnvironmentFixedWater(LLEnvironment::ENV_LOCAL); - if (water && water->getAssetId().notNull()) + if (water) { - //LL_INFOS() << "EEP: water name = " << water->getName() << " - asset id = " << water->getAssetId() << LL_ENDL; - - mWaterPresetsCombo->selectByValue(LLSD(water->getAssetId())); + LLUUID asset_id = water->getAssetId(); + if (asset_id.notNull()) + { + mWaterPresetsCombo->selectByValue(LLSD(asset_id)); + } +#ifdef OPENSIM + else if (LLGridManager::getInstance()->isInOpenSim()) + { + auto preset_name = water->getName(); + if (preset_name == "_default_") + { + preset_name = "Default"; + } + mWaterPresetsCombo->selectByValue(preset_name); + } +#endif //OPENSIM } } + else + { + // LLEnvironment::ENV_REGION: + // LLEnvironment::ENV_PARCEL: + mWLPresetsCombo->selectByValue(LLSD(PRESET_NAME_REGION_DEFAULT)); + mWaterPresetsCombo->selectByValue(LLSD(PRESET_NAME_REGION_DEFAULT)); + mDayCyclePresetsCombo->selectByValue(LLSD(PRESET_NAME_REGION_DEFAULT)); + } } BOOL FloaterQuickPrefs::postBuild() @@ -631,11 +771,21 @@ void FloaterQuickPrefs::loadSavedSettingsFromFile(const std::string& settings_pa bool FloaterQuickPrefs::isValidPreset(const LLSD& preset) { - return (!preset.asString().empty() && - !preset.asUUID().isNull() && - preset.asString() != PRESET_NAME_REGION_DEFAULT && - preset.asString() != PRESET_NAME_DAY_CYCLE && - preset.asString() != PRESET_NAME_NONE); + if (preset.isUUID()) + { + if(!preset.asUUID().isNull()){ return true;} + } + else if (preset.isString()) + { + if(!preset.asString().empty() && + preset.asString() != PRESET_NAME_REGION_DEFAULT && + preset.asString() != PRESET_NAME_DAY_CYCLE && + preset.asString() != PRESET_NAME_NONE) + { + return true; + } + } + return false; } void FloaterQuickPrefs::stepComboBox(LLComboBox* ctrl, bool forward) @@ -663,21 +813,88 @@ void FloaterQuickPrefs::stepComboBox(LLComboBox* ctrl, bool forward) void FloaterQuickPrefs::selectSkyPreset(const LLSD& preset) { - LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, preset.asUUID()); +// Opensim continued W/L support +#ifdef OPENSIM + if(!preset.isUUID() && LLGridManager::getInstance()->isInOpenSim()) + { + LLSettingsSky::ptr_t legacy_sky = nullptr; + LLSD messages; + + legacy_sky = LLEnvironment::createSkyFromLegacyPreset(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", "skies", preset.asString() + ".xml"), messages); + + if (legacy_sky) + { + // Need to preserve current sky manually in this case in contrast to asset-based settings + LLSettingsWater::ptr_t current_water = LLEnvironment::instance().getCurrentWater(); + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, legacy_sky, current_water); + } + else + { + LL_WARNS() << "Legacy windlight conversion failed for " << preset << " existing env unchanged." << LL_ENDL; + return; + } + } + else // note the else here bridges the endif +#endif + { + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, preset.asUUID()); + } LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_FAST, true); } void FloaterQuickPrefs::selectWaterPreset(const LLSD& preset) { - LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, preset.asUUID()); +#ifdef OPENSIM + if(!preset.isUUID() && LLGridManager::getInstance()->isInOpenSim()) + { + LLSettingsWater::ptr_t legacy_water = nullptr; + LLSD messages; + legacy_water = LLEnvironment::createWaterFromLegacyPreset(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", "water", preset.asString() + ".xml"), messages); + if (legacy_water) + { + // Need to preserve current sky manually in this case in contrast to asset-based settings + LLSettingsSky::ptr_t current_sky = LLEnvironment::instance().getCurrentSky(); + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, current_sky, legacy_water); + } + else + { + LL_WARNS() << "Legacy windlight conversion failed for " << preset << " existing env unchanged." << LL_ENDL; + return; + } + } + else // beware the trailing else here. +#endif + { + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, preset.asUUID()); + } LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_FAST, true); } void FloaterQuickPrefs::selectDayCyclePreset(const LLSD& preset) { - LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, preset.asUUID()); +#ifdef OPENSIM + if(!preset.isUUID() && LLGridManager::getInstance()->isInOpenSim()) + { + LLSettingsDay::ptr_t legacyday = nullptr; + LLSD messages; + legacyday = LLEnvironment::createDayCycleFromLegacyPreset(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", "days", preset.asString() + ".xml"), messages); + if (legacyday) + { + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, legacyday); + } + else + { + LL_WARNS() << "Legacy windlight conversion failed for " << preset << " existing env unchanged." << LL_ENDL; + return; + } + } + else // beware trailing else that bridges the endif +#endif + { + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, preset.asUUID()); + } LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_FAST, true); } @@ -943,18 +1160,22 @@ void FloaterQuickPrefs::updateRlvRestrictions(ERlvBehaviour behavior, ERlvParamT { if (behavior == RLV_BHVR_SETENV) { - if (type == RLV_TYPE_ADD) - { - enableWindlightButtons(FALSE); - } - else - { - enableWindlightButtons(TRUE); - } + enableWindlightButtons(type != RLV_TYPE_ADD); } } -void FloaterQuickPrefs::enableWindlightButtons(BOOL enable) +// void FloaterQuickPrefs::onSunMoved() +// { + +// F32 val = mWLSunPos->getCurSliderValue(); + +// auto env = LLEnvironment::instance().getSelectedEnvironment() == LLEnvironment::ENV_LOCAL) +// auto day = LLEnvironment::instance().getEnvironmentDay(env); + + +// } + +void FloaterQuickPrefs::enableWindlightButtons(bool enable) { childSetEnabled("WLPresetsCombo", enable); childSetEnabled("WLPrevPreset", enable); diff --git a/indra/newview/quickprefs.h b/indra/newview/quickprefs.h index 187ce3e10d..ce8f286b9d 100644 --- a/indra/newview/quickprefs.h +++ b/indra/newview/quickprefs.h @@ -91,7 +91,7 @@ private: boost::signals2::connection mRlvBehaviorCallbackConnection; void updateRlvRestrictions(ERlvBehaviour behavior, ERlvParamType type); - void enableWindlightButtons(BOOL enable); + void enableWindlightButtons(bool enable); public: /*virtual*/ BOOL postBuild(); diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp index 9314c3479a..956a6c90c2 100644 --- a/indra/newview/rlvactions.cpp +++ b/indra/newview/rlvactions.cpp @@ -42,7 +42,7 @@ bool RlvActions::canChangeCameraPreset(const LLUUID& idRlvObject) // NOTE: if an object has exclusive camera control then all other objects are locked out return ( (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(idRlvObject, RLV_BHVR_SETCAM)) ) && - (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET)); + (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSETSCALE)) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET)); } bool RlvActions::canChangeToMouselook() @@ -70,7 +70,9 @@ bool RlvActions::isCameraFOVClamped() bool RlvActions::isCameraPresetLocked() { - return (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET)); + return + (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM)) || + (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSET)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_EYEOFFSETSCALE)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_FOCUSOFFSET)); } bool RlvActions::getCameraAvatarDistanceLimits(float& nDistMin, float& nDistMax) @@ -353,6 +355,15 @@ bool RlvActions::isLocalTp(const LLVector3d& posGlobal) return nDistSq < RLV_MODIFIER_TPLOCAL_DEFAULT * RLV_MODIFIER_TPLOCAL_DEFAULT; } +// ============================================================================ +// WindLight +// + +bool RlvActions::canChangeEnvironment() +{ + return !gRlvHandler.hasBehaviour(RLV_BHVR_SETENV); +} + // ============================================================================ // World interaction // diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h index 90b1069f6b..be22f4ce6a 100644 --- a/indra/newview/rlvactions.h +++ b/indra/newview/rlvactions.h @@ -26,6 +26,7 @@ class LLInventoryCategory; class LLInventoryItem; +class LLViewerObject; // ============================================================================ // RlvActions class declaration - developer-friendly non-RLVa code facing class, use in lieu of RlvHandler whenever possible @@ -207,6 +208,16 @@ public: */ static bool isLocalTp(const LLVector3d& posGlobal); + // ========= + // WindLight + // ========= +public: + /* + * Returns true if the user can make changes to their WindLight environment + */ + static bool canChangeEnvironment(); + + // ================= // World interaction // ================= diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index 9eaea8d394..f61473b306 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -346,8 +346,11 @@ void RlvStrings::saveToFile(const std::string& strFilePath) // Checked: 2009-11-11 (RLVa-1.1.0a) | Modified: RLVa-1.1.0a std::string RlvStrings::getAnonym(const std::string& strName) { - if (!rlv_handler_t::isEnabled()) - return strName; + static const std::string strUnknown = LLTrans::getString("Unknown"); + if ( (!RlvActions::isRlvEnabled()) || (m_Anonyms.empty()) ) + { + return strUnknown; + } const char* pszName = strName.c_str(); U32 nHash = 0; @@ -427,7 +430,8 @@ std::string RlvStrings::getVersion(const LLUUID& idRlvObject, bool fLegacy) std::string RlvStrings::getVersionAbout() { - return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d", RLV_VERSION_MAJOR, RLV_VERSION_MINOR, RLV_VERSION_PATCH, RLVa_VERSION_MAJOR, RLVa_VERSION_MINOR, RLVa_VERSION_PATCH, LLVersionInfo::getBuild()); + return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d", RLV_VERSION_MAJOR, RLV_VERSION_MINOR, RLV_VERSION_PATCH, RLVa_VERSION_MAJOR, RLVa_VERSION_MINOR, RLVa_VERSION_PATCH, + LLVersionInfo::getInstance()->getBuild()); } std::string RlvStrings::getVersionNum(const LLUUID& idRlvObject) @@ -438,6 +442,11 @@ std::string RlvStrings::getVersionNum(const LLUUID& idRlvObject) (!fCompatMode) ? RLV_VERSION_PATCH : RLV_VERSION_PATCH_COMPAT, (!fCompatMode) ? RLV_VERSION_BUILD : RLV_VERSION_BUILD_COMPAT); } +std::string RlvStrings::getVersionImplNum() +{ + return llformat("%d%02d%02d%02d", RLVa_VERSION_MAJOR, RLVa_VERSION_MINOR, RLVa_VERSION_PATCH, RLVa_IMPL_ID); +} + // Checked: 2011-11-08 (RLVa-1.5.0) bool RlvStrings::hasString(const std::string& strStringName, bool fCheckCustom) { @@ -733,8 +742,13 @@ void rlvMenuToggleVisible() bool rlvMenuCanShowName() { - const LLVOAvatar* pAvatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); - return (pAvatar) && (RlvActions::canShowName(RlvActions::SNC_DEFAULT, pAvatar->getID())); + bool fEnable = true; + if (rlv_handler_t::isEnabled()) + { + const LLVOAvatar* pAvatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); + fEnable = (pAvatar) && (RlvActions::canShowName(RlvActions::SNC_DEFAULT, pAvatar->getID())); + } + return fEnable; } // Checked: 2010-04-23 (RLVa-1.2.0g) | Modified: RLVa-1.2.0g diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index 50dc08283f..62b7c0ab6d 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -57,7 +57,7 @@ class RlvObject; struct RlvException; typedef boost::variant RlvExceptionOption; -typedef boost::variant RlvBehaviourModifierValue; +typedef boost::variant RlvBehaviourModifierValue; class RlvGCTimer; @@ -152,6 +152,7 @@ public: static const std::string& getStringMapPath() { return m_StringMapPath; } static std::string getVersion(const LLUUID& idRlvObject, bool fLegacy = false); static std::string getVersionAbout(); + static std::string getVersionImplNum(); static std::string getVersionNum(const LLUUID& idRlvObject); static bool hasString(const std::string& strStringName, bool fCheckCustom = false); static void setCustomString(const std::string& strStringName, const std::string& strStringValue); diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index 322218f1c7..9ce476ee5c 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -23,8 +23,8 @@ // Version of the specifcation we report const S32 RLV_VERSION_MAJOR = 3; -const S32 RLV_VERSION_MINOR = 2; -const S32 RLV_VERSION_PATCH = 1; +const S32 RLV_VERSION_MINOR = 3; +const S32 RLV_VERSION_PATCH = 3; const S32 RLV_VERSION_BUILD = 0; // Version of the specifcation we report (in compatibility mode) @@ -35,8 +35,9 @@ const S32 RLV_VERSION_BUILD_COMPAT = 0; // Implementation version const S32 RLVa_VERSION_MAJOR = 2; -const S32 RLVa_VERSION_MINOR = 2; +const S32 RLVa_VERSION_MINOR = 3; const S32 RLVa_VERSION_PATCH = 0; +const S32 RLVa_IMPL_ID = 13; // Uncomment before a final release #define RLV_RELEASE @@ -180,6 +181,7 @@ enum ERlvBehaviour { RLV_BHVR_DETACHTHIS, // "detachthis" RLV_BHVR_DETACHTHISEXCEPT, // "detachthis_except" RLV_BHVR_ADJUSTHEIGHT, // "adjustheight" + RLV_BHVR_GETHEIGHTOFFSET, // "getheightoffset" RLV_BHVR_TPTO, // "tpto" RLV_BHVR_VERSION, // "version" RLV_BHVR_VERSIONNEW, // "versionnew" @@ -213,6 +215,7 @@ enum ERlvBehaviour { RLV_BHVR_SETCAM_ORIGINDISTMIN, // Enforces a minimum distance from the camera origin (in m) RLV_BHVR_SETCAM_ORIGINDISTMAX, // Enforces a maximum distance from the camera origin (in m) RLV_BHVR_SETCAM_EYEOFFSET, // Changes the default camera offset + RLV_BHVR_SETCAM_EYEOFFSETSCALE, // Changes the default camera offset scale RLV_BHVR_SETCAM_FOCUSOFFSET, // Changes the default camera focus offset RLV_BHVR_SETCAM_FOCUS, // Forces the camera focus and/or position to a specific object, avatar or position RLV_BHVR_SETCAM_FOV, // Changes the current - vertical - field of view @@ -266,6 +269,7 @@ enum ERlvBehaviourModifier RLV_MODIFIER_SETCAM_ORIGINDISTMIN, // Minimum distance between the camera position and the origin point (normal value) RLV_MODIFIER_SETCAM_ORIGINDISTMAX, // Maximum distance between the camera position and the origin point (normal value) RLV_MODIFIER_SETCAM_EYEOFFSET, // Specifies the default camera's offset from the camera (vector) + RLV_MODIFIER_SETCAM_EYEOFFSETSCALE, // Specifies the default camera's offset scale (multiplier) RLV_MODIFIER_SETCAM_FOCUSOFFSET, // Specifies the default camera's focus (vector) RLV_MODIFIER_SETCAM_FOVMIN, // Minimum value for the camera's field of view (angle in radians) RLV_MODIFIER_SETCAM_FOVMAX, // Maximum value for the camera's field of view (angle in radians) diff --git a/indra/newview/rlvenvironment.cpp b/indra/newview/rlvenvironment.cpp new file mode 100644 index 0000000000..c8ca144bbf --- /dev/null +++ b/indra/newview/rlvenvironment.cpp @@ -0,0 +1,698 @@ +/** + * + * Copyright (c) 2009-2020, Kitty Barnett + * + * The source code in this file is provided to you under the terms of the + * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt + * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt + * + * 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. + * + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventoryfunctions.h" +#include "llsettingsvo.h" +#include + +#include "rlvactions.h" +#include "rlvenvironment.h" +#include "rlvhelper.h" + +// ================================================================================================ +// Constants and helper functions +// + +namespace +{ + const F32 SLIDER_SCALE_BLUE_HORIZON_DENSITY(2.0f); + const F32 SLIDER_SCALE_DENSITY_MULTIPLIER(0.001f); + const F32 SLIDER_SCALE_GLOW_R(20.0f); + const F32 SLIDER_SCALE_GLOW_B(-5.0f); + const F32 SLIDER_SCALE_SUN_AMBIENT(3.0f); + + const std::string RLV_GETENV_PREFIX = "getenv_"; + const std::string RLV_SETENV_PREFIX = "setenv_"; + + U32 rlvGetColorComponentFromCharacter(char ch) + { + if ( ('r' == ch) || ('x' == ch) ) return VRED; + else if ( ('g' == ch) || ('y' == ch )) return VGREEN; + else if ( ('b' == ch) || ('d' == ch) ) return VBLUE; + else if ('i' == ch) return VALPHA; + return U32_MAX; + } + + const LLUUID& rlvGetLibraryEnvironmentsFolder() + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLNameCategoryCollector f("Environments"); + gInventory.collectDescendentsIf(gInventory.getLibraryRootFolderID(), cats, items, LLInventoryModel::EXCLUDE_TRASH, f); + return (!cats.empty()) ? cats.front()->getUUID() : LLUUID::null; + } + + // Legacy WindLight values we need tend to be expressed as a fraction of the [0, 2PI[ domain + F32 normalize_angle_domain(F32 angle) + { + while (angle < 0) + angle += F_TWO_PI; + while (angle > F_TWO_PI) + angle -= F_TWO_PI; + return angle; + } +} + +/* + * Reasoning (Reference - https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Azimuth-Altitude_schematic.svg/1024px-Azimuth-Altitude_schematic.svg.png) + * + * Given a(zimuth angle) and e(levation angle) - in the SL axis - we know that it calculates the quaternion as follows: + * + * | cos a sin a 0 | | cos e | | cos a x cos e | = | x | + * | sin a cos a 0 | x | 0 | = | sin a x cos e | = | y | (normalized direction vector identifying the sun position on a unit sphere) + * | 0 0 1 | | sin e | | sin e | = | z | + * + * As a result we can reverse the above by: quaternion -> rotate it around X-axis + * x = cos a x cos e <=> cos a = x / cos e \ + * | (if we divide them we can get rid of cos e) + * | <=> sin a / cos a = y / x <=> tan a = y / x <=> a = atan2(y, x) + * y = sin a x cos e <=> sin a = y / cos e / + * z = sin e <=> e = asin z + * + * If we look at the resulting domain azimuth lies in ]-PI, PI] and elevation lies in [-PI/2, PI/2] which I actually prefer most. Going forward people should get the sun in a wind + * direction by manipulating the azimuth and then deal with the elevation (which ends up mimicking how a camera or an observer behave in real life). + * + * Special cases: + * x = 0 => (1) cos e = 0 -> sin e = 1 so y = 0 and z = 1 => in (0, 0, 1) we loose all information about the azimuth since cos e = 0 + * OR (2) cos a = 0 -> sin a = 1 so y = cos e and z = sin e => tan e = z/y (with y != 0) => in (0, Y, Z) azimuth is PI/2 (or 3PI/2) and elevation can have an extended domain of ]-PI, PI] + * => When x = 0 (and y != 0) return PI/2 for azimuth and atan2(z, y) for elevation + * y = 0 => (1) sin a = 0 -> cos a = 1 so x = cos e and z = sin e => tan e = z/x (with x != 0) => in (X, 0, Z) azimuth is 0 (or PI) and elevation can have an extended domain of ]-PI, PI] + * OR (2) cos e = 0 -> see above + => When y = 0 (and x != 0) return 0 for azimuth and atan2(z, x) for elevation + * z = 0 => sin e = 0 -> cos e = 1 so x = cos a and y = sin a => tan a = y / x => in (X, Y, 0) elevation is 0 (or PI) and azimuth has its normal domain of ]-PI, PI] + * => When z = 0 return 0 for elevation and a = atan2(y, x) for azimuth + * + * We still need to convert all that back/forth between legacy WindLight's odd choices: + * east angle = SL's azimuth rotates from E (1, 0, 0) to N (0, 1, 0) to W (-1, 0, 0) to S (0, -1, O) but the legacy east angle rotates the opposite way from E to S to W to N so invert the angle + * (the resulting number then needs to be positive and reported as a fraction of 2PI) + * sunposition = sun elevation reported as a fraction of 2PI + * moonposition = the moon always has sun's azimuth but its negative elevation + * + * Pre-EEP both azimuth and elevation have a 2PI range which means that two different a and e value combinations could yield the same sun direction which causes us problems now since we + * can't differentiate between the two. Pre-EEP likely favoured elevation over azimuth since people might naturally get the time of day they're thinking of and then try to adjust the + * azimuth to get the sun in the correct wind direction; however I've already decided that we'll favour azimuth going forward (see above). + * + * Comparison of pre-EEP and post-EEP legacy values: + * east angle = 0 (aka azimuth = 0) -> y = 0 so e = atan2(z, x) -> elevation has a range of 2PI so we correctly report pre-EEP values + * sunmoonpos = 0 (aka elevation = 0) -> z = 0 so a = atan2(y, x) -> azimuth has a range of 2PI so we correctly report pre-EEP values + * -PI/2 < sunmoonpos < PI/2 -> general case -> post-EEP ranges match pre-EEP ranges so we correctly report pre-EEP values + * sunmoonpos > PI/2 -> elevation went beyond our new maxium so the post-EEP sunmoonpos will actually be off by PI/2 (or 0.25) + * (and the resulting east angle is off by PI or 0.5 - for example smp 0.375 and ea 0.875 are equivalent with smp 0.125 and ea 0.375) + * + * In reverse this means that when setting values through RLVa: + * sunmoonpos without eastangle (=0) => always correct + * eastangle without sunmoonpos (=0) => always correct + * eastangle before sunmoonpos => always correct + * sunmoonpos before eastangle => correct for -0.25 <= sunmoonpos <= 0.25 + * incorrect for 0.75 > sunmoonpos > 0.25 + */ +F32 rlvGetAzimuthFromDirectionVector(const LLVector3& vecDir) +{ + if (is_zero(vecDir.mV[VY])) + return 0.f; + else if (is_zero(vecDir.mV[VX])) + return F_PI_BY_TWO; + + F32 radAzimuth = atan2f(vecDir.mV[VY], vecDir.mV[VX]); + return (radAzimuth >= 0.f) ? radAzimuth : radAzimuth + F_TWO_PI; +} + +F32 rlvGetElevationFromDirectionVector(const LLVector3& vecDir) +{ + if (is_zero(vecDir.mV[VZ])) + return 0.f; + + F32 radElevation; + if ( (is_zero(vecDir.mV[VX])) && (!is_zero(vecDir.mV[VY])) ) + radElevation = atan2f(vecDir.mV[VZ], vecDir.mV[VY]); + else if ( (!is_zero(vecDir.mV[VX])) && (is_zero(vecDir.mV[VY])) ) + radElevation = atan2f(vecDir.mV[VZ], vecDir.mV[VX]); + else + radElevation = asinf(vecDir.mV[VZ]); + return (radElevation >= 0.f) ? radElevation : radElevation + F_TWO_PI; +} + +// Defined in llsettingssky.cpp +LLQuaternion convert_azimuth_and_altitude_to_quat(F32 azimuth, F32 altitude); + +// ================================================================================================ +// RlvIsOfSettingsType - Inventory collector for settings of a specific subtype +// + +class RlvIsOfSettingsType : public LLInventoryCollectFunctor +{ +public: + RlvIsOfSettingsType(LLSettingsType::type_e eSettingsType, const std::string& strNameMatch = LLStringUtil::null) + : m_eSettingsType(eSettingsType) + , m_strNameMatch(strNameMatch) + { + } + + ~RlvIsOfSettingsType() override + { + } + + bool operator()(LLInventoryCategory*, LLInventoryItem* pItem) override + { + if ( (pItem) && (LLAssetType::AT_SETTINGS == pItem->getActualType()) ) + { + return + (m_eSettingsType == LLSettingsType::fromInventoryFlags(pItem->getFlags())) && + ( (m_strNameMatch.empty()) || (boost::iequals(pItem->getName(), m_strNameMatch)) ); + } + return false; + } + +protected: + LLSettingsType::type_e m_eSettingsType; + std::string m_strNameMatch; +}; + +// ================================================================================================ +// RlvEnvironment +// + +RlvEnvironment::RlvEnvironment() +{ + // + // Presets + // + registerSetEnvFn("asset", [](LLEnvironment::EnvSelection_t env, const LLUUID& idAsset) + { + if (idAsset.isNull()) + return RLV_RET_FAILED_OPTION; + + LLEnvironment::instance().setEnvironment(env, idAsset); + return RLV_RET_SUCCESS; + }); + // Deprecated + auto fnApplyLibraryPreset = [](LLEnvironment::EnvSelection_t env, const std::string& strPreset, LLSettingsType::type_e eSettingsType) + { + LLUUID idAsset(strPreset); + if (idAsset.isNull()) + { + const LLUUID& idLibraryEnv = rlvGetLibraryEnvironmentsFolder(); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + RlvIsOfSettingsType f(eSettingsType, strPreset); + gInventory.collectDescendentsIf(idLibraryEnv, cats, items, LLInventoryModel::EXCLUDE_TRASH, f); + if (!items.empty()) + idAsset = items.front()->getAssetUUID(); + } + + if (idAsset.isNull()) + return RLV_RET_FAILED_OPTION; + + LLEnvironment::instance().setEnvironment(env, idAsset); + return RLV_RET_SUCCESS; + }; + registerSetEnvFn("preset", [&fnApplyLibraryPreset](LLEnvironment::EnvSelection_t env, const std::string& strPreset) { return fnApplyLibraryPreset(env, strPreset, LLSettingsType::ST_SKY); }); + registerSetEnvFn("daycycle", [&fnApplyLibraryPreset](LLEnvironment::EnvSelection_t env, const std::string& strPreset) { return fnApplyLibraryPreset(env, strPreset, LLSettingsType::ST_DAYCYCLE); }); + + // + // Atmosphere & Lighting tab + // + + // SETTING_AMBIENT + registerSkyFn("ambient", [](LLSettingsSky::ptr_t pSky) { return pSky->getAmbientColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setAmbientColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); }); + registerLegacySkyFn("ambient",[](LLSettingsSky::ptr_t pSky) { return pSky->getAmbientColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setAmbientColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); }); + + // SETTING_BLUE_DENSITY + registerSkyFn("bluedensity", [](LLSettingsSky::ptr_t pSky) { return pSky->getBlueDensity() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueDensity(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); }); + registerLegacySkyFn("bluedensity",[](LLSettingsSky::ptr_t pSky) { return pSky->getBlueDensity() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueDensity(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); }); + + // SETTING_BLUE_HORIZON + registerSkyFn("bluehorizon", [](LLSettingsSky::ptr_t pSky) { return pSky->getBlueHorizon() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueHorizon(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); }); + registerLegacySkyFn("bluehorizon",[](LLSettingsSky::ptr_t pSky) { return pSky->getBlueHorizon() * (1.f / SLIDER_SCALE_BLUE_HORIZON_DENSITY); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setBlueHorizon(clrValue * SLIDER_SCALE_BLUE_HORIZON_DENSITY); }); + + // SETTING_DENSITY_MULTIPLIER + registerSkyFn("densitymultiplier", [](LLSettingsSky::ptr_t pSky) { return pSky->getDensityMultiplier() / SLIDER_SCALE_DENSITY_MULTIPLIER; }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setDensityMultiplier(nValue * SLIDER_SCALE_DENSITY_MULTIPLIER); }); + + // SETTING_DISTANCE_MULTIPLIER + registerSkyFn("distancemultiplier",[](LLSettingsSky::ptr_t pSky) { return pSky->getDistanceMultiplier(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setDistanceMultiplier(nValue); }); + + + // SETTING_SKY_DROPLET_RADIUS + registerSkyFn("dropletradius", [](LLSettingsSky::ptr_t pSky) { return pSky->getSkyDropletRadius(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setSkyDropletRadius(nValue); }); + + // SETTING_HAZE_DENSITY + registerSkyFn("hazedensity", [](LLSettingsSky::ptr_t pSky) { return pSky->getHazeDensity(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setHazeDensity(nValue); }); + + // SETTING_HAZE_HORIZON + registerSkyFn("hazehorizon", [](LLSettingsSky::ptr_t pSky) { return pSky->getHazeHorizon(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setHazeHorizon(nValue); }); + + // SETTING_SKY_ICE_LEVEL + registerSkyFn("icelevel", [](LLSettingsSky::ptr_t pSky) { return pSky->getSkyIceLevel(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setSkyIceLevel(nValue); }); + + // SETTING_MAX_Y + registerSkyFn("maxaltitude", [](LLSettingsSky::ptr_t pSky) { return pSky->getMaxY(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setMaxY(nValue); }); + + // SETTING_SKY_MOISTURE_LEVEL + registerSkyFn("moisturelevel", [](LLSettingsSky::ptr_t pSky) { return pSky->getSkyMoistureLevel(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setSkyMoistureLevel(nValue); }); + + // SETTING_GAMMA + registerSkyFn("scenegamma", [](LLSettingsSky::ptr_t pSky) { return pSky->getGamma(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setGamma(nValue); }); + + // + // Clouds tab + // + + // SETTING_CLOUD_COLOR + registerSkyFn("cloudcolor", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudColor(); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudColor(clrValue); }); + registerLegacySkyFn("cloudcolor", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudColor(); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudColor(clrValue); }); + + // SETTING_CLOUD_SHADOW + registerSkyFn("cloudcoverage", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudShadow(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setCloudShadow(nValue); }); + + // SETTING_CLOUD_POS_DENSITY1 + registerSkyFn("clouddensity", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity1(); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity1(clrValue); }); + registerLegacySkyFn("cloud", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity1(); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity1(clrValue); }); + + // SETTING_CLOUD_POS_DENSITY2 + registerSkyFn("clouddetail", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity2(); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity2(clrValue); }); + registerLegacySkyFn("clouddetail",[](LLSettingsSky::ptr_t pSky) { return pSky->getCloudPosDensity2(); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setCloudPosDensity2(clrValue); }); + + // SETTING_CLOUD_SCALE + registerSkyFn("cloudscale", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudScale(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setCloudScale(nValue); }); + + // SETTING_CLOUD_SCROLL_RATE + registerSkyFn("cloudscroll", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudScrollRate(); }, + [](LLSettingsSky::ptr_t pSky, const LLVector2& vecValue) { pSky->setCloudScrollRate(vecValue); }); + registerLegacySkyFn("cloudscroll", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudScrollRate(); }, + [](LLSettingsSky::ptr_t pSky, const LLVector2& vecValue) { pSky->setCloudScrollRate(vecValue); }); + + // SETTING_CLOUD_TEXTUREID + registerSkyFn("cloudtexture", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudNoiseTextureId(); }, + [](LLSettingsSky::ptr_t pSky, const LLUUID& idTexture) { pSky->setCloudNoiseTextureId(idTexture); }); + + // SETTING_CLOUD_VARIANCE + registerSkyFn("cloudvariance", [](LLSettingsSky::ptr_t pSky) { return pSky->getCloudVariance(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setCloudVariance(nValue); }); + + // + // Sun & Moon + // + + // SETTING_MOON_BRIGHTNESS + registerSkyFn("moonbrightness", [](LLSettingsSky::ptr_t pSky) { return pSky->getMoonBrightness(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setMoonBrightness(nValue); }); + + // SETTING_MOON_SCALE + registerSkyFn("moonscale", [](LLSettingsSky::ptr_t pSky) { return pSky->getMoonScale(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setMoonScale(nValue); }); + + // SETTING_MOON_TEXTUREID + registerSkyFn("moontexture", [](LLSettingsSky::ptr_t pSky) { return pSky->getMoonTextureId(); }, + [](LLSettingsSky::ptr_t pSky, const LLUUID& idTexture) { pSky->setMoonTextureId(idTexture); }); + + // SETTING_GLOW + registerSkyFn("sunglowsize", [](LLSettingsSky::ptr_t pSky) { return 2.0 - (pSky->getGlow().mV[VRED] / SLIDER_SCALE_GLOW_R); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setGlow(LLColor3((2.0f - nValue) * SLIDER_SCALE_GLOW_R, .0f, pSky->getGlow().mV[VBLUE])); }); + registerSkyFn("sunglowfocus", [](LLSettingsSky::ptr_t pSky) { return pSky->getGlow().mV[VBLUE] / SLIDER_SCALE_GLOW_B; }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setGlow(LLColor3(pSky->getGlow().mV[VRED], .0f, nValue * SLIDER_SCALE_GLOW_B)); }); + + // SETTING_SUNLIGHT_COLOR + registerSkyFn("sunlightcolor",[](LLSettingsSky::ptr_t pSky) { return pSky->getSunlightColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setSunlightColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); }); + registerLegacySkyFn("sunmooncolor", [](LLSettingsSky::ptr_t pSky) { return pSky->getSunlightColor() * (1.f / SLIDER_SCALE_SUN_AMBIENT); }, + [](LLSettingsSky::ptr_t pSky, const LLColor3& clrValue) { pSky->setSunlightColor(clrValue * SLIDER_SCALE_SUN_AMBIENT); }); + + // SETTING_SUN_SCALE + registerSkyFn("sunscale", [](LLSettingsSky::ptr_t pSky) { return pSky->getSunScale(); }, + [](LLSettingsSky::ptr_t pSky, F32 nValue) { pSky->setSunScale(nValue); }); + + // SETTING_SUN_TEXTUREID + registerSkyFn("suntexture", [](LLSettingsSky::ptr_t pSky) { return pSky->getSunTextureId(); }, + [](LLSettingsSky::ptr_t pSky, const LLUUID& idTexture) { pSky->setSunTextureId(idTexture); }); + + // SETTING_STAR_BRIGHTNESS + registerSkyFn("starbrightness", [](LLSettingsSky::ptr_t pSky) { return pSky->getStarBrightness(); }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) { pSky->setStarBrightness(nValue); }); + + // SETTING_SUN_ROTATION + registerSkyFn("sunazimuth", [](LLSettingsSky::ptr_t pSky) { return rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()); }, + [](LLSettingsSky::ptr_t pSky, const F32& radAzimuth) { + pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, rlvGetElevationFromDirectionVector(LLVector3::x_axis* pSky->getSunRotation()))); + }); + registerSkyFn("sunelevation", [](LLSettingsSky::ptr_t pSky) { return rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()); }, + [](LLSettingsSky::ptr_t pSky, F32 radElevation) { + radElevation = llclamp(radElevation, -F_PI_BY_TWO, F_PI_BY_TWO); + pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(rlvGetAzimuthFromDirectionVector(LLVector3::x_axis* pSky->getSunRotation()), radElevation)); + }); + + // SETTING_MOON_ROTATION + registerSkyFn("moonazimuth", [](LLSettingsSky::ptr_t pSky) { return rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getMoonRotation()); }, + [](LLSettingsSky::ptr_t pSky, const F32& radAzimuth) { + pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, rlvGetElevationFromDirectionVector(LLVector3::x_axis* pSky->getMoonRotation()))); + }); + registerSkyFn("moonelevation", [](LLSettingsSky::ptr_t pSky) { return rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getMoonRotation()); }, + [](LLSettingsSky::ptr_t pSky, F32 radElevation) { + radElevation = llclamp(radElevation, -F_PI_BY_TWO, F_PI_BY_TWO); + pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(rlvGetAzimuthFromDirectionVector(LLVector3::x_axis* pSky->getMoonRotation()), radElevation)); + }); + + // Legacy WindLight support (see remarks at the top of this file) + registerSkyFn("eastangle", [](LLSettingsSky::ptr_t pSky) { return normalize_angle_domain(-rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation())) / F_TWO_PI; }, + [](LLSettingsSky::ptr_t pSky, const F32& radEastAngle) + { + const F32 radAzimuth = -radEastAngle * F_TWO_PI; + const F32 radElevation = rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()); + pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, radElevation)); + pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(radAzimuth + F_PI, -radElevation)); + }); + + registerSkyFn("sunmoonposition", [](LLSettingsSky::ptr_t pSky) { return rlvGetElevationFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()) / F_TWO_PI; }, + [](LLSettingsSky::ptr_t pSky, const F32& nValue) + { + const F32 radAzimuth = rlvGetAzimuthFromDirectionVector(LLVector3::x_axis * pSky->getSunRotation()); + const F32 radElevation = nValue * F_TWO_PI; + pSky->setSunRotation(convert_azimuth_and_altitude_to_quat(radAzimuth, radElevation)); + pSky->setMoonRotation(convert_azimuth_and_altitude_to_quat(radAzimuth + F_PI, -radElevation)); + }); + + // Create a fixed sky from the nearest daycycle (local > experience > parcel > region) + registerSetEnvFn("daytime", [](LLEnvironment::EnvSelection_t env, const F32& nValue) + { + if ((nValue >= 0.f) && (nValue <= 1.0f)) + { + LLSettingsDay::ptr_t pDay; + if (LLEnvironment::ENV_EDIT != env) + { + LLEnvironment::EnvSelection_t envs[] = { LLEnvironment::ENV_LOCAL, LLEnvironment::ENV_PUSH, LLEnvironment::ENV_PARCEL, LLEnvironment::ENV_REGION }; + for (size_t idxEnv = 0, cntEnv = sizeof(envs) / sizeof(LLEnvironment::EnvSelection_t); idxEnv < cntEnv && !pDay; idxEnv++) + pDay = LLEnvironment::instance().getEnvironmentDay(envs[idxEnv]); + } + else + { + pDay = LLEnvironment::instance().getEnvironmentDay(LLEnvironment::ENV_EDIT); + } + + if (pDay) + { + auto pNewSky = LLSettingsVOSky::buildDefaultSky(); + auto pSkyBlender = std::make_shared(pNewSky, pDay, 1); + pSkyBlender->setPosition(nValue); + + LLEnvironment::instance().setEnvironment(env, pNewSky); + LLEnvironment::instance().updateEnvironment(LLEnvironment::TRANSITION_INSTANT); + } + } + else if (nValue == -1) + { + LLEnvironment::instance().clearEnvironment(env); + LLEnvironment::instance().setSelectedEnvironment(env); + LLEnvironment::instance().updateEnvironment(); +// defocusEnvFloaters(); + } + else + { + return RLV_RET_FAILED_OPTION; + } + + return RLV_RET_SUCCESS; + }); + registerGetEnvFn("daytime", [](LLEnvironment::EnvSelection_t env) + { + // I forgot how much I hate this command... it literally makes no sense since time of day only has any meaning in an + // actively animating day cycle (but in that case we have to return -1). + if (!LLEnvironment::instance().getEnvironmentFixedSky(env)) { + return std::to_string(-1.f); + } + + // It's invalid input for @setenv_daytime (see above) so it can be fed in without changing the current environment + return std::to_string(2.f); + }); +} + +RlvEnvironment::~RlvEnvironment() +{ +} + +// static +LLEnvironment::EnvSelection_t RlvEnvironment::getTargetEnvironment() +{ + return RlvActions::canChangeEnvironment() ? LLEnvironment::ENV_LOCAL : LLEnvironment::ENV_EDIT; +} + +// static +bool RlvEnvironment::onHandleCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet, const std::string& strCmdPrefix, const handler_map_t& fnLookup, const legacy_handler_map_t& legacyFnLookup) +{ + if ( (rlvCmd.getBehaviour().length() > strCmdPrefix.length() + 2) && (boost::starts_with(rlvCmd.getBehaviour(), strCmdPrefix)) ) + { + std::string strEnvCommand = rlvCmd.getBehaviour().substr(strCmdPrefix.length()); + + handler_map_t::const_iterator itFnEntry = fnLookup.find(strEnvCommand); + if (fnLookup.end() != itFnEntry) + { + cmdRet = itFnEntry->second((RLV_TYPE_FORCE == rlvCmd.getParamType()) ? rlvCmd.getOption() : rlvCmd.getParam()); + return true; + } + + // Legacy handling (blargh) + U32 idxComponent = rlvGetColorComponentFromCharacter(strEnvCommand.back()); + if (idxComponent <= VALPHA) + { + strEnvCommand.pop_back(); + + legacy_handler_map_t::const_iterator itLegacyFnEntry = legacyFnLookup.find(strEnvCommand); + if (legacyFnLookup.end() != itLegacyFnEntry) + { + cmdRet = itLegacyFnEntry->second((RLV_TYPE_FORCE == rlvCmd.getParamType()) ? rlvCmd.getOption() : rlvCmd.getParam(), idxComponent); + return true; + } + } + } + + return false; +} + +bool RlvEnvironment::onReplyCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet) +{ + return onHandleCommand(rlvCmd, cmdRet, RLV_GETENV_PREFIX, m_GetFnLookup, m_LegacyGetFnLookup); +} + +bool RlvEnvironment::onForceCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet) +{ + return onHandleCommand(rlvCmd, cmdRet, RLV_SETENV_PREFIX, m_SetFnLookup, m_LegacySetFnLookup); +} + +template<> +std::string RlvEnvironment::handleGetFn(const std::function& fn) +{ + LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky(); + return std::to_string(fn(pSky)); +} + +template<> +std::string RlvEnvironment::handleGetFn(const std::function& fn) +{ + LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky(); + return fn(pSky).asString(); +} + +template<> +std::string RlvEnvironment::handleGetFn(const std::function& fn) +{ + LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky(); + LLVector2 replyVec = fn(pSky); + return llformat("%f/%f", replyVec.mV[VX], replyVec.mV[VY]); +} + +template<> +std::string RlvEnvironment::handleGetFn(const std::function& fn) +{ + LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky(); + LLColor3 replyColor = fn(pSky); + return llformat("%f/%f/%f", replyColor.mV[VX], replyColor.mV[VY], replyColor.mV[VZ]); +} + +template +ERlvCmdRet RlvEnvironment::handleSetFn(const std::string& strRlvOption, const std::function& fn) +{ + T optionValue; + if (!RlvCommandOptionHelper::parseOption(strRlvOption, optionValue)) + return RLV_RET_FAILED_PARAM; + + LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky(); + fn(pSky, optionValue); + pSky->update(); + return RLV_RET_SUCCESS; +} + +template<> +std::string RlvEnvironment::handleLegacyGetFn(const std::function& getFn, U32 idxComponent) +{ + if (idxComponent > 2) + return LLStringUtil::null; + return std::to_string(getFn(LLEnvironment::instance().getCurrentSky()).mV[idxComponent]); +} + +template<> +std::string RlvEnvironment::handleLegacyGetFn(const std::function& getFn, U32 idxComponent) +{ + if ( (idxComponent >= VRED) && (idxComponent <= VBLUE) ) + { + return std::to_string(getFn(LLEnvironment::instance().getCurrentSky()).mV[idxComponent]); + } + else if (idxComponent == VALPHA) + { + const LLColor3& clr = getFn(LLEnvironment::instance().getCurrentSky()); + return std::to_string(llmax(clr.mV[VRED], clr.mV[VGREEN], clr.mV[VBLUE])); + } + return LLStringUtil::null; +} + +template<> +ERlvCmdRet RlvEnvironment::handleLegacySetFn(float optionValue, LLVector2 curValue, const std::function& setFn, U32 idxComponent) +{ + if (idxComponent > 2) + return RLV_RET_FAILED_UNKNOWN; + + LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky(); + curValue.mV[idxComponent] = optionValue; + setFn(pSky, curValue); + pSky->update(); + + return RLV_RET_SUCCESS; +} + +template<> +ERlvCmdRet RlvEnvironment::handleLegacySetFn(float optionValue, LLColor3 curValue, const std::function& setFn, U32 idxComponent) +{ + LLSettingsSky::ptr_t pSky = LLEnvironment::instance().getCurrentSky(); + if ( (idxComponent >= VRED) && (idxComponent <= VBLUE) ) + { + curValue.mV[idxComponent] = optionValue; + } + else if (idxComponent == VALPHA) + { + const F32 curMax = llmax(curValue.mV[VRED], curValue.mV[VGREEN], curValue.mV[VBLUE]); + if ( (0.0f == optionValue) || (0.0f == curMax) ) + { + curValue.mV[VRED] = curValue.mV[VGREEN] = curValue.mV[VBLUE] = optionValue; + } + else + { + const F32 nDelta = (optionValue - curMax) / curMax; + curValue.mV[VRED] *= (1.0f + nDelta); + curValue.mV[VGREEN] *= (1.0f + nDelta); + curValue.mV[VBLUE] *= (1.0f + nDelta); + } + } + else + { + return RLV_RET_FAILED_UNKNOWN; + } + + setFn(pSky, curValue); + pSky->update(); + + return RLV_RET_SUCCESS; +} + + +template +void RlvEnvironment::registerSkyFn(const std::string& strFnName, const std::function& getFn, const std::function& setFn) +{ + RLV_ASSERT(m_GetFnLookup.end() == m_GetFnLookup.find(strFnName)); + m_GetFnLookup.insert(std::make_pair(strFnName, [this, getFn](const std::string& strRlvParam) + { + if (RlvUtil::sendChatReply(strRlvParam, handleGetFn(getFn))) + return RLV_RET_SUCCESS; + return RLV_RET_FAILED_PARAM; + })); + + RLV_ASSERT(m_SetFnLookup.end() == m_SetFnLookup.find(strFnName)); + m_SetFnLookup.insert(std::make_pair(strFnName, [this, setFn](const std::string& strRlvOption) + { + return handleSetFn(strRlvOption, setFn); + })); +} + +void RlvEnvironment::registerGetEnvFn(const std::string& strFnName, const std::function& getFn) +{ + RLV_ASSERT(m_GetFnLookup.end() == m_GetFnLookup.find(strFnName)); + m_GetFnLookup.insert(std::make_pair(strFnName, [getFn](const std::string& strRlvParam) + { + if (RlvUtil::sendChatReply(strRlvParam, getFn(getTargetEnvironment()))) + return RLV_RET_SUCCESS; + return RLV_RET_FAILED_PARAM; + })); +} + +template +void RlvEnvironment::registerSetEnvFn(const std::string& strFnName, const std::function& setFn) +{ + RLV_ASSERT(m_SetFnLookup.end() == m_SetFnLookup.find(strFnName)); + m_SetFnLookup.insert(std::make_pair(strFnName, [setFn](const std::string& strRlvOption) + { + T optionValue; + if (!RlvCommandOptionHelper::parseOption(strRlvOption, optionValue)) + return RLV_RET_FAILED_PARAM; + return setFn(getTargetEnvironment(), optionValue); + })); +} + +template +void RlvEnvironment::registerLegacySkyFn(const std::string& strFnName, const std::function< T (LLSettingsSkyPtr_t)>& getFn, const std::function& setFn) +{ + RLV_ASSERT(m_LegacyGetFnLookup.end() == m_LegacyGetFnLookup.find(strFnName)); + m_LegacyGetFnLookup.insert(std::make_pair(strFnName, [this, getFn](const std::string& strRlvParam, U32 idxComponent) + { + const std::string strReply = handleLegacyGetFn(getFn, idxComponent); + if (strReply.empty()) + return RLV_RET_FAILED_UNKNOWN; + else if (RlvUtil::sendChatReply(strRlvParam, strReply)) + return RLV_RET_SUCCESS; + return RLV_RET_FAILED_PARAM; + })); + + RLV_ASSERT(m_LegacySetFnLookup.end() == m_LegacySetFnLookup.find(strFnName)); + m_LegacySetFnLookup.insert(std::make_pair(strFnName, [this, getFn, setFn](const std::string& strRlvOption, U32 idxComponent) + { + float optionValue; + if (!RlvCommandOptionHelper::parseOption(strRlvOption, optionValue)) + return RLV_RET_FAILED_PARAM; + return handleLegacySetFn(optionValue, getFn(LLEnvironment::instance().getCurrentSky()), setFn, idxComponent);; + })); +} + +// ================================================================================================ diff --git a/indra/newview/rlvenvironment.h b/indra/newview/rlvenvironment.h new file mode 100644 index 0000000000..8daf12f97d --- /dev/null +++ b/indra/newview/rlvenvironment.h @@ -0,0 +1,66 @@ +/** + * + * Copyright (c) 2009-2020, Kitty Barnett + * + * The source code in this file is provided to you under the terms of the + * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt + * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt + * + * 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. + * + */ + +#pragma once + +#include "llenvironment.h" + +#include "rlvcommon.h" + +// ============================================================================ +// RlvEnvironment - viewer-side scripted environment changes +// + +class RlvEnvironment : public RlvExtCommandHandler +{ +public: + RlvEnvironment(); + ~RlvEnvironment() override; + + bool onReplyCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet) override; + bool onForceCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet) override; +protected: + static LLEnvironment::EnvSelection_t getTargetEnvironment(); + typedef std::map> handler_map_t; + typedef std::map> legacy_handler_map_t; + static bool onHandleCommand(const RlvCommand& rlvCmd, ERlvCmdRet& cmdRet, const std::string& strCmdPrefix, const handler_map_t& fnLookup, const legacy_handler_map_t& legacyFnLookup); + + /* + * Command registration + */ +protected: + void registerGetEnvFn(const std::string& strFnName, const std::function& getFn); + template void registerSetEnvFn(const std::string& strFnName, const std::function& setFn); + template void registerSkyFn(const std::string& strFnName, const std::function& getFn, const std::function& setFn); + template void registerLegacySkyFn(const std::string& strFnName, const std::function< T (LLSettingsSky::ptr_t)>& getFn, const std::function& setFn); + + // Command handling helpers + template std::string handleGetFn(const std::function& fn); + template ERlvCmdRet handleSetFn(const std::string& strRlvOption, const std::function& fn); + template std::string handleLegacyGetFn(const std::function< T (LLSettingsSky::ptr_t)>& getFn, U32 idxComponent); + template ERlvCmdRet handleLegacySetFn(float optionValue, T value, const std::function& setFn, U32 idxComponent); + + /* + * Member variables + */ +protected: + handler_map_t m_GetFnLookup; + handler_map_t m_SetFnLookup; + legacy_handler_map_t m_LegacyGetFnLookup; + legacy_handler_map_t m_LegacySetFnLookup; +}; + +// ============================================================================ diff --git a/indra/newview/rlvextensions.cpp b/indra/newview/rlvextensions.cpp index 6e8924f90b..4612331c96 100644 --- a/indra/newview/rlvextensions.cpp +++ b/indra/newview/rlvextensions.cpp @@ -1,25 +1,23 @@ -/** +/** * * Copyright (c) 2009-2011, Kitty Barnett - * - * The source code in this file is provided to you under the terms of the + * + * The source code in this file is provided to you under the terms of the * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt - * + * * By copying, modifying or distributing this software, you acknowledge that - * you have read and understood your obligations described above, and agree to + * you have read and understood your obligations described above, and agree to * abide by those obligations. - * + * */ #include "llviewerprecompiledheaders.h" #include "llagent.h" #include "llagentcamera.h" -// #include "lldaycyclemanager.h" // [EEPMERGE] #include "llvoavatarself.h" -//#include "llwlparammanager.h" // [EEPMERGE] #include "rlvextensions.h" #include "rlvhandler.h" @@ -27,354 +25,6 @@ // ============================================================================ -#if 0// [EEPMERGE] -class RlvWindLightControl -{ -public: - enum EType { TYPE_COLOR, TYPE_COLOR_R, TYPE_FLOAT, TYPE_UNKNOWN }; - enum EColorComponent { COMPONENT_R, COMPONENT_G, COMPONENT_B, COMPONENT_I, COMPONENT_NONE }; -public: - RlvWindLightControl(WLColorControl* pCtrl, bool fColorR) : m_eType((!fColorR) ? TYPE_COLOR: TYPE_COLOR_R), m_pColourCtrl(pCtrl), m_pFloatCtrl(NULL) {} - RlvWindLightControl(WLFloatControl* pCtrl) : m_eType(TYPE_FLOAT), m_pColourCtrl(NULL), m_pFloatCtrl(pCtrl) {} - - EType getControlType() const { return m_eType; } - bool isColorType() const { return (TYPE_COLOR == m_eType) || (TYPE_COLOR_R == m_eType); } - bool isFloatType() const { return (TYPE_FLOAT == m_eType); } - // TYPE_COLOR and TYPE_COLOR_R - F32 getColorComponent(EColorComponent eComponent, bool& fError) const; - LLVector4 getColorVector(bool& fError) const; - bool setColorComponent(EColorComponent eComponent, F32 nValue); - // TYPE_FLOAT - F32 getFloat(bool& fError) const; - bool setFloat(F32 nValue); - - static EColorComponent getComponentFromCharacter(char ch); -protected: - EType m_eType; // Type of the WindLight control - WLColorControl* m_pColourCtrl; - WLFloatControl* m_pFloatCtrl; -}; - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -static F32 get_intensity_from_color(const LLVector4& v) -{ - return llmax(v.mV[0], v.mV[1], v.mV[2]); -} - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -F32 RlvWindLightControl::getColorComponent(EColorComponent eComponent, bool& fError) const -{ - switch (eComponent) - { - case COMPONENT_R: return getColorVector(fError).mV[0]; - case COMPONENT_G: return getColorVector(fError).mV[1]; - case COMPONENT_B: return getColorVector(fError).mV[2]; - case COMPONENT_I: return get_intensity_from_color(getColorVector(fError)); // SL-2.8: Always seems to be 1.0 so get it manually - default : RLV_ASSERT(false); fError = true; return 0.0; - } -} - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -RlvWindLightControl::EColorComponent RlvWindLightControl::getComponentFromCharacter(char ch) -{ - if (('r' == ch) || ('x' == ch)) - return COMPONENT_R; - else if (('g' == ch) || ('y' == ch)) - return COMPONENT_G; - else if (('b' == ch) || ('d' == ch)) - return COMPONENT_B; - else if ('i' == ch) - return COMPONENT_I; - return COMPONENT_NONE; -} - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -LLVector4 RlvWindLightControl::getColorVector(bool& fError) const -{ - if ((fError = !isColorType())) - return LLVector4(0, 0, 0, 0); - F32 nMult = (m_pColourCtrl->isSunOrAmbientColor) ? 3.0f : ((m_pColourCtrl->isBlueHorizonOrDensity) ? 2.0f : 1.0f); - return LLWLParamManager::getInstance()->mCurParams.getVector(m_pColourCtrl->mName, fError) / nMult; -} - -// Checked: 2011-08-28 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -bool RlvWindLightControl::setColorComponent(EColorComponent eComponent, F32 nValue) -{ - if (isColorType()) - { - nValue *= (m_pColourCtrl->isSunOrAmbientColor) ? 3.0f : ((m_pColourCtrl->isBlueHorizonOrDensity) ? 2.0f : 1.0f); - if (COMPONENT_I == eComponent) // (See: LLFloaterWindLight::onColorControlIMoved) - { - if (m_pColourCtrl->hasSliderName) - { - F32 curMax = llmax(m_pColourCtrl->r, m_pColourCtrl->g, m_pColourCtrl->b); - if ( (0.0f == nValue) || (0.0f == curMax) ) - { - m_pColourCtrl->r = m_pColourCtrl->g = m_pColourCtrl->b = m_pColourCtrl->i = nValue; - } - else - { - F32 nDelta = (nValue - curMax) / curMax; - m_pColourCtrl->r *= (1.0f + nDelta); - m_pColourCtrl->g *= (1.0f + nDelta); - m_pColourCtrl->b *= (1.0f + nDelta); - m_pColourCtrl->i = nValue; - } - } - } - else // (See: LLFloaterWindLight::onColorControlRMoved) - { - F32* pnValue = (COMPONENT_R == eComponent) ? &m_pColourCtrl->r : (COMPONENT_G == eComponent) ? &m_pColourCtrl->g : (COMPONENT_B == eComponent) ? &m_pColourCtrl->b : NULL; - if (pnValue) - *pnValue = nValue; - if (m_pColourCtrl->hasSliderName) - m_pColourCtrl->i = llmax(m_pColourCtrl->r, m_pColourCtrl->g, m_pColourCtrl->b); - } - m_pColourCtrl->update(LLWLParamManager::getInstance()->mCurParams); - LLWLParamManager::getInstance()->propagateParameters(); - } - return isColorType(); -} - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -F32 RlvWindLightControl::getFloat(bool& fError) const -{ - return (!(fError = (TYPE_FLOAT != m_eType))) ? LLWLParamManager::getInstance()->mCurParams.getVector(m_pFloatCtrl->mName, fError).mV[0] * m_pFloatCtrl->mult : 0.0; -} - -// Checked: 2011-08-28 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -bool RlvWindLightControl::setFloat(F32 nValue) -{ - if (TYPE_FLOAT == m_eType) - { - m_pFloatCtrl->x = nValue / m_pFloatCtrl->mult; - m_pFloatCtrl->update(LLWLParamManager::getInstance()->mCurParams); - LLWLParamManager::getInstance()->propagateParameters(); - } - return (TYPE_FLOAT == m_eType); -} - -// ============================================================================ - -class RlvWindLight : public LLSingleton -{ - LLSINGLETON(RlvWindLight); -public: - std::string getValue(const std::string& strSetting, bool& fError); - bool setValue(const std::string& strRlvName, const std::string& strValue); - -protected: - std::map m_ControlLookupMap; -}; - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -RlvWindLight::RlvWindLight() -{ - LLWLParamManager* pWLParamMgr = LLWLParamManager::getInstance(); - - // TYPE_FLOAT - m_ControlLookupMap.insert(std::pair("cloudcoverage", RlvWindLightControl(&pWLParamMgr->mCloudCoverage))); - m_ControlLookupMap.insert(std::pair("cloudscale", RlvWindLightControl(&pWLParamMgr->mCloudScale))); - m_ControlLookupMap.insert(std::pair("densitymultiplier", RlvWindLightControl(&pWLParamMgr->mDensityMult))); - m_ControlLookupMap.insert(std::pair("distancemultiplier", RlvWindLightControl(&pWLParamMgr->mDistanceMult))); - m_ControlLookupMap.insert(std::pair("maxaltitude", RlvWindLightControl(&pWLParamMgr->mMaxAlt))); - m_ControlLookupMap.insert(std::pair("scenegamma", RlvWindLightControl(&pWLParamMgr->mWLGamma))); - m_ControlLookupMap.insert(std::pair("hazedensity", RlvWindLightControl(&pWLParamMgr->mHazeDensity))); - m_ControlLookupMap.insert(std::pair("hazehorizon", RlvWindLightControl(&pWLParamMgr->mHazeHorizon))); - // TYPE_COLOR - m_ControlLookupMap.insert(std::pair("ambient", RlvWindLightControl(&pWLParamMgr->mAmbient, false))); - m_ControlLookupMap.insert(std::pair("bluedensity", RlvWindLightControl(&pWLParamMgr->mBlueDensity, false))); - m_ControlLookupMap.insert(std::pair("bluehorizon", RlvWindLightControl(&pWLParamMgr->mBlueHorizon, false))); - m_ControlLookupMap.insert(std::pair("cloud", RlvWindLightControl(&pWLParamMgr->mCloudMain, false))); - m_ControlLookupMap.insert(std::pair("cloudcolor", RlvWindLightControl(&pWLParamMgr->mCloudColor, false))); - m_ControlLookupMap.insert(std::pair("clouddetail", RlvWindLightControl(&pWLParamMgr->mCloudDetail, false))); - m_ControlLookupMap.insert(std::pair("sunmooncolor", RlvWindLightControl(&pWLParamMgr->mSunlight, false))); -} - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -std::string RlvWindLight::getValue(const std::string& strSetting, bool& fError) -{ - LLWLParamManager* pWLParams = LLWLParamManager::getInstance(); - LLEnvManagerNew* pEnvMgr = LLEnvManagerNew::getInstance(); - - fError = false; // Assume we won't fail - if ("preset" == strSetting) - return (pEnvMgr->getUseFixedSky()) ? pEnvMgr->getSkyPresetName() : std::string(); - else if ("daycycle" == strSetting) - return (pEnvMgr->getUseDayCycle()) ? pEnvMgr->getDayCycleName() : std::string(); - - F32 nValue = 0.0f; - if ("daytime" == strSetting) - { - nValue = (pEnvMgr->getUseFixedSky()) ? pWLParams->mCurParams.getFloat("sun_angle", fError) / F_TWO_PI : -1.0f; - } - else if (("sunglowfocus" == strSetting) || ("sunglowsize" == strSetting)) - { - pWLParams->mGlow = pWLParams->mCurParams.getVector(pWLParams->mGlow.mName, fError); - RLV_ASSERT_DBG(!fError); - - if ("sunglowfocus" == strSetting) - nValue = -pWLParams->mGlow.b / 5.0f; - else - nValue = 2 - pWLParams->mGlow.r / 20.0f; - } - else if ("starbrightness" == strSetting) nValue = pWLParams->mCurParams.getStarBrightness(); - else if ("eastangle" == strSetting) nValue = pWLParams->mCurParams.getEastAngle() / F_TWO_PI; - else if ("sunmoonposition" == strSetting) nValue = pWLParams->mCurParams.getSunAngle() / F_TWO_PI; - else if ("cloudscrollx" == strSetting) nValue = pWLParams->mCurParams.getCloudScrollX() - 10.0f; - else if ("cloudscrolly" == strSetting) nValue = pWLParams->mCurParams.getCloudScrollY() - 10.0f; - else - { - std::map::const_iterator itControl = m_ControlLookupMap.find(strSetting); - if (m_ControlLookupMap.end() != itControl) - { - switch (itControl->second.getControlType()) - { - case RlvWindLightControl::TYPE_FLOAT: - nValue = itControl->second.getFloat(fError); - break; - case RlvWindLightControl::TYPE_COLOR_R: - nValue = itControl->second.getColorComponent(RlvWindLightControl::COMPONENT_R, fError); - break; - default: - fError = true; - break; - } - } - else - { - // Couldn't find the exact name, check for a color control name - RlvWindLightControl::EColorComponent eComponent = RlvWindLightControl::getComponentFromCharacter(strSetting[strSetting.length() - 1]); - if (RlvWindLightControl::COMPONENT_NONE != eComponent) - itControl = m_ControlLookupMap.find(strSetting.substr(0, strSetting.length() - 1)); - if ( (m_ControlLookupMap.end() != itControl) && (itControl->second.isColorType()) ) - nValue = itControl->second.getColorComponent(eComponent, fError); - else - fError = true; - } - } - return llformat("%f", nValue); -} - -// Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a -bool RlvWindLight::setValue(const std::string& strRlvName, const std::string& strValue) -{ - F32 nValue = 0.0f; - // Sanity check - make sure strValue specifies a number for all settings except "preset" and "daycycle" - if ( (RlvSettings::getNoSetEnv()) || - ( (!LLStringUtil::convertToF32(strValue, nValue)) && (("preset" != strRlvName) && ("daycycle" != strRlvName)) ) ) - { - return false; - } - - LLWLParamManager* pWLParams = LLWLParamManager::getInstance(); - LLEnvManagerNew* pEnvMgr = LLEnvManagerNew::getInstance(); - - if ("daytime" == strRlvName) - { - if (0.0f <= nValue) - { - pWLParams->mAnimator.deactivate(); - pWLParams->mAnimator.setDayTime(nValue); - pWLParams->mAnimator.update(pWLParams->mCurParams); - } - else - { - pEnvMgr->setUserPrefs(pEnvMgr->getWaterPresetName(), pEnvMgr->getSkyPresetName(), pEnvMgr->getDayCycleName(), false, true); - } - return true; - } - else if ("preset" == strRlvName) - { - std::string strPresetName = pWLParams->findPreset(strValue, LLEnvKey::SCOPE_LOCAL); - if (!strPresetName.empty()) - pEnvMgr->useSkyPreset(strPresetName); - return !strPresetName.empty(); - } - else if ("daycycle" == strRlvName) - { - std::string strPresetName = LLDayCycleManager::instance().findPreset(strValue); - if (!strPresetName.empty()) - pEnvMgr->useDayCycle(strValue, LLEnvKey::SCOPE_LOCAL); - return !strPresetName.empty(); - } - - bool fError = false; - pWLParams->mAnimator.deactivate(); - if (("sunglowfocus" == strRlvName) || ("sunglowsize" == strRlvName)) - { - pWLParams->mGlow = pWLParams->mCurParams.getVector(pWLParams->mGlow.mName, fError); - RLV_ASSERT_DBG(!fError); - - if ("sunglowfocus" == strRlvName) - pWLParams->mGlow.b = -nValue * 5; - else - pWLParams->mGlow.r = (2 - nValue) * 20; - - pWLParams->mGlow.update(pWLParams->mCurParams); - pWLParams->propagateParameters(); - return true; - } - else if ("starbrightness" == strRlvName) - { - pWLParams->mCurParams.setStarBrightness(nValue); - return true; - } - else if (("eastangle" == strRlvName) || ("sunmoonposition" == strRlvName)) - { - if ("eastangle" == strRlvName) - pWLParams->mCurParams.setEastAngle(F_TWO_PI * nValue); - else - pWLParams->mCurParams.setSunAngle(F_TWO_PI * nValue); - - // Set the sun vector - pWLParams->mLightnorm.r = -sin(pWLParams->mCurParams.getEastAngle()) * cos(pWLParams->mCurParams.getSunAngle()); - pWLParams->mLightnorm.g = sin(pWLParams->mCurParams.getSunAngle()); - pWLParams->mLightnorm.b = cos(pWLParams->mCurParams.getEastAngle()) * cos(pWLParams->mCurParams.getSunAngle()); - pWLParams->mLightnorm.i = 1.f; - - pWLParams->propagateParameters(); - return true; - } - else if ("cloudscrollx" == strRlvName) - { - pWLParams->mCurParams.setCloudScrollX(nValue + 10.0f); - return true; - } - else if ("cloudscrolly" == strRlvName) - { - pWLParams->mCurParams.setCloudScrollY(nValue + 10.0f); - return true; - } - - std::map::iterator itControl = m_ControlLookupMap.find(strRlvName); - if (m_ControlLookupMap.end() != itControl) - { - switch (itControl->second.getControlType()) - { - case RlvWindLightControl::TYPE_FLOAT: - return itControl->second.setFloat(nValue); - case RlvWindLightControl::TYPE_COLOR_R: - return itControl->second.setColorComponent(RlvWindLightControl::COMPONENT_R, nValue); - default: - RLV_ASSERT(false); - } - } - else - { - // Couldn't find the exact name, check for a color control name - RlvWindLightControl::EColorComponent eComponent = RlvWindLightControl::getComponentFromCharacter(strRlvName[strRlvName.length() - 1]); - if (RlvWindLightControl::COMPONENT_NONE != eComponent) - itControl = m_ControlLookupMap.find(strRlvName.substr(0, strRlvName.length() - 1)); - if ( (m_ControlLookupMap.end() != itControl) && (itControl->second.isColorType()) ) - return itControl->second.setColorComponent(eComponent, nValue); - } - return false; -} -#endif // [EEPMERGE] - -// ============================================================================ - std::map RlvExtGetSet::m_DbgAllowed; std::map RlvExtGetSet::m_PseudoDebug; @@ -441,27 +91,6 @@ bool RlvExtGetSet::processCommand(const RlvCommand& rlvCmd, ERlvCmdRet& eRet) return true; } } - else if ("env" == strBehaviour) - { - bool fError = false; - if ( ("get" == strGetSet) && (RLV_TYPE_REPLY == rlvCmd.getParamType()) ) - { - // [EEPMERGE] - //RlvUtil::sendChatReply(rlvCmd.getParam(), RlvWindLight::instance().getValue(strSetting, fError)); - eRet = (!fError) ? RLV_RET_SUCCESS : RLV_RET_FAILED_UNKNOWN; - return true; - } - else if ( ("set" == strGetSet) && (RLV_TYPE_FORCE == rlvCmd.getParamType()) ) - { - // [EEPMERGE] - //if (!gRlvHandler.hasBehaviourExcept(RLV_BHVR_SETENV, rlvCmd.getObjectID())) - // eRet = (RlvWindLight::instance().setValue(strSetting, rlvCmd.getOption())) ? RLV_RET_SUCCESS : RLV_RET_FAILED_UNKNOWN; - //else - // eRet = RLV_RET_FAILED_LOCK; - // [/EEPMERGE] - return true; - } - } } else if ("setrot" == rlvCmd.getBehaviour()) { diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 9184abd6cd..e26da09bac 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -37,8 +37,7 @@ #include "llavataractions.h" // @stopim IM query #include "llavatarnamecache.h" // @shownames #include "llavatarlist.h" // @shownames -// [EEPMERGE] -//#include "llenvmanager.h" // @setenv +#include "llfloatercamera.h" // @setcam family #include "llfloatersidepanelcontainer.h"// @shownames #include "llnotifications.h" // @list IM query #include "llnotificationsutil.h" @@ -57,6 +56,7 @@ // RLVa includes #include "rlvactions.h" +#include "rlvenvironment.h" #include "rlvfloaters.h" #include "rlvactions.h" #include "rlvhandler.h" @@ -152,14 +152,58 @@ RlvHandler::RlvHandler() : m_fCanCancelTp(true), m_posSitSource(), m_pGCTimer(NU RlvHandler::~RlvHandler() { + cleanup(); +} + +void RlvHandler::cleanup() +{ + // Nothing to clean if we're not enabled (or already cleaned up) + if (!m_fEnabled) + return; + + // + // Clean up any restrictions that are still active + // + RLV_ASSERT(LLApp::isQuitting()); // Several commands toggle debug settings but won't if they know the viewer is quitting + + // Assume we have no way to predict how m_Objects will change so make a copy ahead of time + uuid_vec_t idRlvObjects; + idRlvObjects.reserve(m_Objects.size()); + std::transform(m_Objects.begin(), m_Objects.end(), std::back_inserter(idRlvObjects), [](const rlv_object_map_t::value_type& kvPair) {return kvPair.first; }); + for (const LLUUID & idRlvObj : idRlvObjects) + { + processCommand(idRlvObj, "clear", true); + } + + // Sanity check + RLV_ASSERT(m_Objects.empty()); + RLV_ASSERT(m_Exceptions.empty()); + RLV_ASSERT(std::all_of(m_Behaviours, m_Behaviours + RLV_BHVR_COUNT, [](S16 cnt) { return !cnt; })); + RLV_ASSERT(m_CurCommandStack.empty()); + RLV_ASSERT(m_CurObjectStack.empty()); + RLV_ASSERT(m_pOverlayImage.isNull()); + + // + // Clean up what's left + // gAgent.removeListener(this); + m_Retained.clear(); + //delete m_pGCTimer; // <- deletes itself + if (m_PendingGroupChange.first.notNull()) { - LLGroupMgr::instance().removeObserver(m_PendingGroupChange.first, this); + if (LLGroupMgr::instanceExists()) + LLGroupMgr::instance().removeObserver(m_PendingGroupChange.first, this); m_PendingGroupChange = std::make_pair(LLUUID::null, LLStringUtil::null); } - //delete m_pGCTimer; // <- deletes itself + for (RlvExtCommandHandler* pCmdHandler : m_CommandHandlers) + { + delete pCmdHandler; + } + m_CommandHandlers.clear(); + + m_fEnabled = false; } // ============================================================================ @@ -401,42 +445,46 @@ bool RlvHandler::notifyCommandHandlers(rlvExtCommandHandler f, const RlvCommand& } // Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f -ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj) +ERlvCmdRet RlvHandler::processCommand(std::reference_wrapper rlvCmd, bool fFromObj) { - RLV_DEBUGS << "[" << rlvCmd.getObjectID() << "]: " << rlvCmd.asString() << RLV_ENDL; + { + const RlvCommand& rlvCmdTmp = rlvCmd; // Reference to the temporary with limited variable scope since we don't want it to leak below - if ( (isBlockedObject(rlvCmd.getObjectID())) && (RLV_TYPE_REMOVE != rlvCmd.getParamType()) && (RLV_TYPE_CLEAR != rlvCmd.getParamType()) ) - { - RLV_DEBUGS << "\t-> blocked object" << RLV_ENDL; - return RLV_RET_FAILED_BLOCKED; - } - if (!rlvCmd.isValid()) - { - RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; - return RLV_RET_FAILED_SYNTAX; - } - if (rlvCmd.isBlocked()) - { - RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL; - return RLV_RET_FAILED_DISABLED; + RLV_DEBUGS << "[" << rlvCmdTmp.getObjectID() << "]: " << rlvCmdTmp.asString() << RLV_ENDL; + + if ( (isBlockedObject(rlvCmdTmp.getObjectID())) && (RLV_TYPE_REMOVE != rlvCmdTmp.getParamType()) && (RLV_TYPE_CLEAR != rlvCmdTmp.getParamType()) ) + { + RLV_DEBUGS << "\t-> blocked object" << RLV_ENDL; + return RLV_RET_FAILED_BLOCKED; + } + if (!rlvCmdTmp.isValid()) + { + RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; + return RLV_RET_FAILED_SYNTAX; + } + if (rlvCmdTmp.isBlocked()) + { + RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL; + return RLV_RET_FAILED_DISABLED; + } } // Using a stack for executing commands solves a few problems: // - if we passed RlvObject::m_idObj for idObj somewhere and process a @clear then idObj points to invalid/cleared memory at the end // - if command X triggers command Y along the way then getCurrentCommand()/getCurrentObject() still return Y even when finished - m_CurCommandStack.push(&rlvCmd); m_CurObjectStack.push(rlvCmd.getObjectID()); + m_CurCommandStack.push(rlvCmd); m_CurObjectStack.push(rlvCmd.get().getObjectID()); const LLUUID& idCurObj = m_CurObjectStack.top(); ERlvCmdRet eRet = RLV_RET_UNKNOWN; - switch (rlvCmd.getParamType()) + switch (rlvCmd.get().getParamType()) { case RLV_TYPE_ADD: // Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f { - if ( (m_Behaviours[rlvCmd.getBehaviourType()]) && - ( (RLV_BHVR_SETCAM == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETDEBUG == rlvCmd.getBehaviourType()) || (RLV_BHVR_SETENV == rlvCmd.getBehaviourType()) ) ) + ERlvBehaviour eBhvr = rlvCmd.get().getBehaviourType(); + if ( (m_Behaviours[eBhvr]) && ( (RLV_BHVR_SETCAM == eBhvr) || (RLV_BHVR_SETDEBUG == eBhvr) || (RLV_BHVR_SETENV == eBhvr) ) ) { // Some restrictions can only be held by one single object to avoid deadlocks - RLV_DEBUGS << "\t- " << rlvCmd.getBehaviour() << " is already set by another object => discarding" << RLV_ENDL; + RLV_DEBUGS << "\t- " << rlvCmd.get().getBehaviour() << " is already set by another object => discarding" << RLV_ENDL; eRet = RLV_RET_FAILED_LOCK; break; } @@ -444,14 +492,14 @@ ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj) rlv_object_map_t::iterator itObj = m_Objects.find(idCurObj); bool fAdded = false; if (itObj != m_Objects.end()) { - RlvObject& rlvObj = itObj->second; - fAdded = rlvObj.addCommand(rlvCmd); + // Add the command to an existing object + rlvCmd = itObj->second.addCommand(rlvCmd, fAdded); } else { - RlvObject rlvObj(idCurObj); - fAdded = rlvObj.addCommand(rlvCmd); - itObj = m_Objects.insert(std::pair(idCurObj, rlvObj)).first; + // Create a new RLV object and then add the command to it (and grab its reference) + itObj = m_Objects.insert(std::pair(idCurObj, RlvObject(idCurObj))).first; + rlvCmd = itObj->second.addCommand(rlvCmd, fAdded); } RLV_DEBUGS << "\t- " << ( (fAdded) ? "adding behaviour" : "skipping duplicate" ) << RLV_ENDL; @@ -526,12 +574,13 @@ ERlvCmdRet RlvHandler::processCommand(const RlvCommand& rlvCmd, bool fFromObj) // Checked: 2009-11-25 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f ERlvCmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCommand, bool fFromObj) { + const RlvCommand rlvCmd(idObj, strCommand); if (STATE_STARTED != LLStartUp::getStartupState()) { - m_Retained.push_back(RlvCommand(idObj, strCommand)); + m_Retained.push_back(rlvCmd); return RLV_RET_RETAINED; } - return processCommand(RlvCommand(idObj, strCommand), fFromObj); + return processCommand(std::ref(rlvCmd), fFromObj); } // Checked: 2010-02-27 (RLVa-1.2.0a) | Modified: RLVa-1.1.0f @@ -546,7 +595,7 @@ void RlvHandler::processRetainedCommands(ERlvBehaviour eBhvrFilter /*=RLV_BHVR_U if ( ((RLV_BHVR_UNKNOWN == eBhvrFilter) || (rlvCmd.getBehaviourType() == eBhvrFilter)) && ((RLV_TYPE_UNKNOWN == eTypeFilter) || (rlvCmd.getParamType() == eTypeFilter)) ) { - processCommand(rlvCmd, true); + processCommand(std::ref(rlvCmd), true); m_Retained.erase(itCurCmd); } } @@ -823,6 +872,23 @@ void RlvHandler::setActiveGroupRole(const LLUUID& idGroup, const std::string& st m_PendingGroupChange = std::make_pair(LLUUID::null, LLStringUtil::null); } +// @setcam family +void RlvHandler::setCameraOverride(bool fOverride) +{ + if ( (fOverride) && (CAMERA_RLV_SETCAM_VIEW != gAgentCamera.getCameraPreset()) ) + { + m_strCameraPresetRestore = gSavedSettings.getString("PresetCameraActive"); + gAgentCamera.switchCameraPreset(CAMERA_RLV_SETCAM_VIEW); + } + else if ( (!fOverride) && (CAMERA_RLV_SETCAM_VIEW == gAgentCamera.getCameraPreset() && (!RlvActions::isCameraPresetLocked())) ) + { + // We need to clear it or it won't reset properly + gSavedSettings.setString("PresetCameraActive", LLStringUtil::null); + LLFloaterCamera::switchToPreset(m_strCameraPresetRestore); + m_strCameraPresetRestore.clear(); + } +} + // ============================================================================ // Externally invoked event handlers // @@ -1047,6 +1113,12 @@ bool RlvHandler::onGC() return (0 != m_Objects.size()); // GC will kill itself if it has nothing to do } +// static +void RlvHandler::cleanupClass() +{ + gRlvHandler.cleanup(); +} + // Checked: 2009-11-26 (RLVa-1.1.0f) | Added: RLVa-1.1.0f void RlvHandler::onIdleStartup(void* pParam) { @@ -1448,6 +1520,7 @@ bool RlvHandler::setEnabled(bool fEnable) RlvSettings::initClass(); RlvStrings::initClass(); + RlvHandler::instance().addCommandHandler(new RlvEnvironment()); RlvHandler::instance().addCommandHandler(new RlvExtGetSet()); // Make sure we get notified when login is successful @@ -2071,20 +2144,23 @@ void RlvBehaviourModifierHandler::onValueChange() gAgentCamera.changeCameraToThirdPerson(); } -// Handles: @setcam_eyeoffset:=n|y and @setcam_focusoffset:=n|y toggles +// Handles: @setcam_eyeoffset:=n|y, @setcam_eyeoffsetscale:=n|y and @setcam_focusoffset:=n|y toggles template<> template<> void RlvBehaviourCamEyeFocusOffsetHandler::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr) { if (fHasBhvr) { - gAgentCamera.switchCameraPreset(CAMERA_RLV_SETCAM_VIEW); + gRlvHandler.setCameraOverride(true); } else { - const RlvBehaviourModifier* pBhvrEyeModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET); - const RlvBehaviourModifier* pBhvrOffsetModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET); - if ( (!pBhvrEyeModifier->hasValue()) && (!pBhvrOffsetModifier->hasValue()) ) - gAgentCamera.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); + const RlvBehaviourModifier* pBhvrEyeOffsetModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET); + const RlvBehaviourModifier* pBhvrEyeOffsetScaleModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSETSCALE); + const RlvBehaviourModifier* pBhvrFocusOffsetModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET); + if ( (!pBhvrEyeOffsetModifier->hasValue()) && (!pBhvrEyeOffsetScaleModifier->hasValue()) && (!pBhvrFocusOffsetModifier->hasValue()) ) + { + gRlvHandler.setCameraOverride(false); + } } } @@ -2102,7 +2178,21 @@ void RlvBehaviourModifierHandler::onValueChange() } } -// Handles: @setcam_focusoffset:=n|y changes +// Handles: @setcam_eyeoffsetscale:=n|y changes +template<> +void RlvBehaviourModifierHandler::onValueChange() const +{ + if (RlvBehaviourModifier* pBhvrModifier = RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSETSCALE)) + { + LLControlVariable* pControl = gSavedSettings.getControl("CameraOffsetScaleRLVa"); + if (pBhvrModifier->hasValue()) + pControl->setValue(pBhvrModifier->getValue()); + else + pControl->resetToDefault(); + } +} + +// Handles: @setcam_focusoffset:=n|y changes template<> void RlvBehaviourModifierHandler::onValueChange() const { @@ -2110,7 +2200,7 @@ void RlvBehaviourModifierHandler::onValueChange { LLControlVariable* pControl = gSavedSettings.getControl("FocusOffsetRLVaView"); if (pBhvrModifier->hasValue()) - pControl->setValue(pBhvrModifier->getValue().getValue()); + pControl->setValue(pBhvrModifier->getValue().getValue()); else pControl->resetToDefault(); } @@ -2222,19 +2312,21 @@ void RlvBehaviourToggleHandler::onCommandToggle(ERlvBehaviour e std::list lObjects; // Restore the @setcam_unlock reference count gRlvHandler.findBehaviour(RLV_BHVR_SETCAM_UNLOCK, lObjects); - gRlvHandler.m_Behaviours[RLV_BHVR_SETCAM_UNLOCK] = lObjects.size(); + llassert_always(lObjects.size() <= 0x7FFF); + gRlvHandler.m_Behaviours[RLV_BHVR_SETCAM_UNLOCK] = static_cast(lObjects.size()); } // Manually invoke the @setcam_unlock toggle handler if we toggled it on/off if (fHasCamUnlock != gRlvHandler.hasBehaviour(RLV_BHVR_SETCAM_UNLOCK)) RlvBehaviourToggleHandler::onCommandToggle(RLV_BHVR_SETCAM_UNLOCK, !fHasCamUnlock); - gAgentCamera.switchCameraPreset( (fHasBhvr) ? CAMERA_RLV_SETCAM_VIEW : CAMERA_PRESET_REAR_VIEW ); + gRlvHandler.setCameraOverride(fHasBhvr); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_AVDISTMIN)->setPrimaryObject(idRlvObject); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_AVDISTMAX)->setPrimaryObject(idRlvObject); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_ORIGINDISTMIN)->setPrimaryObject(idRlvObject); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_ORIGINDISTMAX)->setPrimaryObject(idRlvObject); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSET)->setPrimaryObject(idRlvObject); + RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_EYEOFFSETSCALE)->setPrimaryObject(idRlvObject); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOCUSOFFSET)->setPrimaryObject(idRlvObject); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOVMIN)->setPrimaryObject(idRlvObject); RlvBehaviourDictionary::instance().getModifier(RLV_MODIFIER_SETCAM_FOVMAX)->setPrimaryObject(idRlvObject); @@ -2256,7 +2348,7 @@ void RlvBehaviourToggleHandler::onCommandToggle(ERlvBehaviour template<> template<> void RlvBehaviourToggleHandler::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr) { - const std::string strEnvFloaters[] = { "env_post_process", "env_settings", "env_delete_preset", "env_edit_sky", "env_edit_water", "env_edit_day_cycle" }; + const std::string strEnvFloaters[] = { "env_adjust_snapshot", "env_edit_extdaycycle", "env_fixed_environmentent_sky", "env_fixed_environmentent_water", "my_environments" }; for (int idxFloater = 0, cntFloater = sizeof(strEnvFloaters) / sizeof(std::string); idxFloater < cntFloater; idxFloater++) { if (fHasBhvr) @@ -2273,13 +2365,26 @@ void RlvBehaviourToggleHandler::onCommandToggle(ERlvBehaviour e } } - // Don't allow toggling "Basic Shaders" and/or "Atmopsheric Shaders" through the debug settings under @setenv=n + // Don't allow toggling "Atmopsheric Shaders" through the debug settings under @setenv=n gSavedSettings.getControl("WindLightUseAtmosShaders")->setHiddenFromSettingsEditor(fHasBhvr); - // Restore the user's WindLight preferences when releasing - // [EEPMERGE] Use LLEnvironment::loadPreferences()??? - //if (!fHasBhvr) - // LLEnvManagerNew::instance().usePrefs(); + if (fHasBhvr) + { + // Usurp the 'edit' environment for RLVa locking so TPV tools like quick prefs and phototools are automatically locked out as well + // (these needed per-feature awareness of RLV in the previous implementation which often wasn't implemented) + LLEnvironment* pEnv = LLEnvironment::getInstance(); + LLSettingsSky::ptr_t pRlvSky = pEnv->getEnvironmentFixedSky(LLEnvironment::ENV_LOCAL, true)->buildClone(); + pEnv->setEnvironment(LLEnvironment::ENV_EDIT, pRlvSky); + pEnv->setSelectedEnvironment(LLEnvironment::ENV_EDIT, LLEnvironment::TRANSITION_INSTANT); + pEnv->updateEnvironment(LLEnvironment::TRANSITION_INSTANT); + } + else + { + // Restore the user's WindLight preferences when releasing + LLEnvironment::instance().clearEnvironment(LLEnvironment::ENV_EDIT); + LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); + LLEnvironment::instance().updateEnvironment(); + } } // Handles: @showhovertext:=n|y @@ -2403,13 +2508,12 @@ void RlvBehaviourToggleHandler::onCommandToggle(ERlvBehaviou // Force the use of the "display name" cache so we can filter both display and legacy names (or return back to the user's preference) if (fHasBhvr) { - LLAvatarNameCache::getInstance()->setForceDisplayNames(true); + LLAvatarNameCache::instance().setForceDisplayNames(true); } else { - LLAvatarNameCache* inst = LLAvatarNameCache::getInstance(); - inst->setForceDisplayNames(false); - inst->setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames")); + LLAvatarNameCache::instance().setForceDisplayNames(false); + LLAvatarNameCache::instance().setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames")); } // Refresh all name tags and HUD text @@ -2726,7 +2830,7 @@ ERlvCmdRet RlvForceHandler::onCommand(const RlvCommand& rlvC return RLV_RET_SUCCESS; } -// Handles: @setcam_eyeoffset[:]=force and @setcam_focusoffset[:]=force +// Handles: @setcam_eyeoffset[:]=force, @setcam_eyeoffsetscale[:]=force and @setcam_focusoffset[:]=force template<> template<> ERlvCmdRet RlvForceCamEyeFocusOffsetHandler::onCommand(const RlvCommand& rlvCmd) { @@ -2734,22 +2838,54 @@ ERlvCmdRet RlvForceCamEyeFocusOffsetHandler::onCommand(const RlvCommand& rlvCmd) if (!RlvActions::canChangeCameraPreset(rlvCmd.getObjectID())) return RLV_RET_FAILED_LOCK; - LLControlVariable* pOffsetControl = gSavedSettings.getControl("CameraOffsetRLVaView"); - LLControlVariable* pFocusControl = gSavedSettings.getControl("FocusOffsetRLVaView"); - LLControlVariable* pControl = (rlvCmd.getBehaviourType() == RLV_BHVR_SETCAM_EYEOFFSET) ? pOffsetControl : pFocusControl; - if (rlvCmd.hasOption()) + LLControlVariable* pEyeOffsetControl = gSavedSettings.getControl("CameraOffsetRLVaView"); + LLControlVariable* pEyeOffsetScaleControl = gSavedSettings.getControl("CameraOffsetScaleRLVa"); + LLControlVariable* pFocusOffsetControl = gSavedSettings.getControl("FocusOffsetRLVaView"); + + LLControlVariable* pControl; LLSD sdControlValue; + switch (rlvCmd.getBehaviourType()) { - LLVector3 vecOffset; - if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), vecOffset)) - return RLV_RET_FAILED_OPTION; - pControl->setValue(vecOffset.getValue()); - } - else - { - pControl->resetToDefault(); + case RLV_BHVR_SETCAM_EYEOFFSET: + if (rlvCmd.hasOption()) + { + LLVector3 vecOffset; + if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), vecOffset)) + return RLV_RET_FAILED_OPTION; + sdControlValue = vecOffset.getValue(); + } + pControl = pEyeOffsetControl; + break; + case RLV_BHVR_SETCAM_EYEOFFSETSCALE: + if (rlvCmd.hasOption()) + { + float nScale; + if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), nScale)) + return RLV_RET_FAILED_OPTION; + sdControlValue = nScale; + } + pControl = pEyeOffsetScaleControl; + break; + case RLV_BHVR_SETCAM_FOCUSOFFSET: + if (rlvCmd.hasOption()) + { + LLVector3d vecOffset; + if (!RlvCommandOptionHelper::parseOption(rlvCmd.getOption(), vecOffset)) + return RLV_RET_FAILED_OPTION; + sdControlValue = vecOffset.getValue(); + } + pControl = pFocusOffsetControl; + break; + default: + return RLV_RET_FAILED; } - gAgentCamera.switchCameraPreset( ((pOffsetControl->isDefault()) && (pFocusControl->isDefault())) ? CAMERA_PRESET_REAR_VIEW : CAMERA_RLV_SETCAM_VIEW); + if (!sdControlValue.isUndefined()) + pControl->setValue(sdControlValue); + else + pControl->resetToDefault(); + + // NOTE: this doesn't necessarily release the camera preset even if all 3 are at their default now (e.g. @setcam is currently set) + gRlvHandler.setCameraOverride( (!pEyeOffsetControl->isDefault()) || (!pEyeOffsetScaleControl->isDefault()) || (!pFocusOffsetControl->isDefault()) ); return RLV_RET_SUCCESS; } @@ -3122,7 +3258,10 @@ ERlvCmdRet RlvHandler::processReplyCommand(const RlvCommand& rlvCmd) const break; case RLV_BHVR_VERSIONNUM: // @versionnum= - Checked: 2010-03-27 (RLVa-1.4.0a) | Added: RLVa-1.0.4b // NOTE: RLV will respond even if there's an option - strReply = RlvStrings::getVersionNum(rlvCmd.getObjectID()); + if (!rlvCmd.hasOption()) + strReply = RlvStrings::getVersionNum(rlvCmd.getObjectID()); + else if ("impl" == rlvCmd.getOption()) + strReply = RlvStrings::getVersionImplNum(); break; case RLV_BHVR_GETATTACH: // @getattach[:]= eRet = onGetAttach(rlvCmd, strReply); @@ -3440,6 +3579,19 @@ ERlvCmdRet RlvReplyHandler::onCommand(const RlvCommand& rlv return RLV_RET_SUCCESS; } +// Handles: @getheightoffset= +template<> template<> +ERlvCmdRet RlvReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + if (!rlvCmd.getOption().empty()) + return RLV_RET_FAILED_OPTION; + else if (!isAgentAvatarValid()) + return RLV_RET_FAILED_UNKNOWN; + + strReply = llformat("%.2f", gAgentAvatarp->getHoverOffset()[VZ] * 100); + return RLV_RET_SUCCESS; +} + // Checked: 2010-03-09 (RLVa-1.2.0a) | Modified: RLVa-1.1.0f ERlvCmdRet RlvHandler::onGetInv(const RlvCommand& rlvCmd, std::string& strReply) const { @@ -3568,7 +3720,8 @@ ERlvCmdRet RlvHandler::onGetOutfit(const RlvCommand& rlvCmd, std::string& strRep LLWearableType::WT_GLOVES, LLWearableType::WT_JACKET, LLWearableType::WT_PANTS, LLWearableType::WT_SHIRT, LLWearableType::WT_SHOES, LLWearableType::WT_SKIRT, LLWearableType::WT_SOCKS, LLWearableType::WT_UNDERPANTS, LLWearableType::WT_UNDERSHIRT, LLWearableType::WT_SKIN, LLWearableType::WT_EYES, LLWearableType::WT_HAIR, - LLWearableType::WT_SHAPE, LLWearableType::WT_ALPHA, LLWearableType::WT_TATTOO, LLWearableType::WT_PHYSICS + LLWearableType::WT_SHAPE, LLWearableType::WT_ALPHA, LLWearableType::WT_TATTOO, LLWearableType::WT_PHYSICS, + LLWearableType::WT_UNIVERSAL, }; for (int idxType = 0, cntType = sizeof(wtRlvTypes) / sizeof(LLWearableType::EType); idxType < cntType; idxType++) diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index 85acec1bce..32eda85137 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -133,7 +133,7 @@ public: bool processIMQuery(const LLUUID& idSender, const std::string& strCommand); // Returns a pointer to the currently executing command (do *not* save this pointer) - const RlvCommand* getCurrentCommand() const { return (!m_CurCommandStack.empty()) ? m_CurCommandStack.top() : NULL; } + const RlvCommand* getCurrentCommand() const { return (!m_CurCommandStack.empty()) ? &m_CurCommandStack.top().get() : nullptr; } // Returns the UUID of the object we're currently executing a command for const LLUUID& getCurrentObject() const { return (!m_CurObjectStack.empty()) ? m_CurObjectStack.top() : LLUUID::null; } @@ -147,6 +147,7 @@ protected: void clearOverlayImage(); // @setoverlay=n void setActiveGroup(const LLUUID& idGroup); // @setgroup=force void setActiveGroupRole(const LLUUID& idGroup, const std::string& strRole); // @setgroup=force + void setCameraOverride(bool fOverride); // @setcam family void setOverlayImage(const LLUUID& idTexture); // @setoverlay=n void onIMQueryListResponse(const LLSD& sdNotification, const LLSD sdResponse); @@ -173,6 +174,7 @@ protected: // Externally invoked event handlers public: + void cleanup(); void onActiveGroupChanged(); void onAttach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt); void onDetach(const LLViewerObject* pAttachObj, const LLViewerJointAttachment* pAttachPt); @@ -183,6 +185,7 @@ public: void onSitOrStand(bool fSitting); void onTeleportFailed(); void onTeleportFinished(const LLVector3d& posArrival); + static void cleanupClass(); static void onIdleStartup(void* pParam); protected: void getAttachmentResourcesCoro(const std::string& strUrl); @@ -201,7 +204,7 @@ public: * Command processing */ protected: - ERlvCmdRet processCommand(const RlvCommand& rlvCmd, bool fFromObj); + ERlvCmdRet processCommand(std::reference_wrapper rlvCmdRef, bool fFromObj); ERlvCmdRet processClearCommand(const RlvCommand& rlvCmd); // Command handlers (RLV_TYPE_ADD and RLV_TYPE_CLEAR) @@ -242,7 +245,7 @@ protected: rlv_command_list_t m_Retained; RlvGCTimer* m_pGCTimer; - std::stack m_CurCommandStack;// Convenience (see @tpto) + std::stack> m_CurCommandStack; // Convenience (see @tpto) std::stack m_CurObjectStack; // Convenience (see @tpto) rlv_behaviour_signal_t m_OnBehaviour; @@ -263,6 +266,8 @@ protected: LLPointer m_pOverlayImage = nullptr; // @setoverlay=n int m_nOverlayOrigBoost = 0; // @setoverlay=n + std::string m_strCameraPresetRestore; // @setcam_eyeoffset, @setcam_eyeoffsetscale and @setcam_focusoffset + friend class RlvSharedRootFetcher; // Fetcher needs access to m_fFetchComplete friend class RlvGCTimer; // Timer clear its own point at destruction template friend struct RlvBehaviourGenericHandler; diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index b988fbc9e7..71c4bd3720 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -193,10 +193,12 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() addModifier(RLV_BHVR_SETCAM_ORIGINDISTMIN, RLV_MODIFIER_SETCAM_ORIGINDISTMIN, new RlvBehaviourModifier("Camera - Focus Distance (Min)", 0.0f, true, new RlvBehaviourModifierCompMax)); addEntry(new RlvBehaviourGenericProcessor("setcam_origindistmax", RLV_BHVR_SETCAM_ORIGINDISTMAX, RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addModifier(RLV_BHVR_SETCAM_ORIGINDISTMAX, RLV_MODIFIER_SETCAM_ORIGINDISTMAX, new RlvBehaviourModifier("Camera - Focus Distance (Max)", F32_MAX, true, new RlvBehaviourModifierCompMin)); - addEntry(new RlvBehaviourGenericToggleProcessor("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); + addEntry(new RlvBehaviourGenericToggleProcessor("setcam_eyeoffset")); addModifier(RLV_BHVR_SETCAM_EYEOFFSET, RLV_MODIFIER_SETCAM_EYEOFFSET, new RlvBehaviourModifierHandler("Camera - Eye Offset", LLVector3::zero, true, nullptr)); - addEntry(new RlvBehaviourGenericToggleProcessor("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); - addModifier(RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_MODIFIER_SETCAM_FOCUSOFFSET, new RlvBehaviourModifierHandler("Camera - Focus Offset", LLVector3::zero, true, nullptr)); + addEntry(new RlvBehaviourGenericToggleProcessor("setcam_eyeoffsetscale")); + addModifier(RLV_BHVR_SETCAM_EYEOFFSETSCALE, RLV_MODIFIER_SETCAM_EYEOFFSETSCALE, new RlvBehaviourModifierHandler("Camera - Eye Offset Scale", 0, true, nullptr)); + addEntry(new RlvBehaviourGenericToggleProcessor("setcam_focusoffset")); + addModifier(RLV_BHVR_SETCAM_FOCUSOFFSET, RLV_MODIFIER_SETCAM_FOCUSOFFSET, new RlvBehaviourModifierHandler("Camera - Focus Offset", LLVector3d::zero, true, nullptr)); addEntry(new RlvBehaviourProcessor("setcam_fovmin")); addModifier(RLV_BHVR_SETCAM_FOVMIN, RLV_MODIFIER_SETCAM_FOVMIN, new RlvBehaviourModifierHandler("Camera - FOV (Min)", DEFAULT_FIELD_OF_VIEW, true, new RlvBehaviourModifierCompMax)); addEntry(new RlvBehaviourProcessor("setcam_fovmax")); @@ -265,8 +267,9 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() addEntry(new RlvForceProcessor("detachme")); addEntry(new RlvForceProcessor("fly")); addEntry(new RlvForceProcessor("setcam_focus", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); - addEntry(new RlvForceProcessor("setcam_eyeoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); - addEntry(new RlvForceProcessor("setcam_focusoffset", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); + addEntry(new RlvForceProcessor("setcam_eyeoffset")); + addEntry(new RlvForceProcessor("setcam_eyeoffsetscale")); + addEntry(new RlvForceProcessor("setcam_focusoffset")); addEntry(new RlvForceProcessor("setcam_fov", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addEntry(new RlvForceProcessor("setcam_mode", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addEntry(new RlvForceProcessor("setgroup")); @@ -292,6 +295,7 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() addEntry(new RlvReplyProcessor("getcam_textures", RlvBehaviourInfo::BHVR_EXPERIMENTAL)); addEntry(new RlvReplyProcessor("getcommand", RlvBehaviourInfo::BHVR_EXTENDED)); addEntry(new RlvBehaviourInfo("getgroup", RLV_BHVR_GETGROUP, RLV_TYPE_REPLY)); + addEntry(new RlvReplyProcessor("getheightoffset", RlvBehaviourInfo::BHVR_EXTENDED)); addEntry(new RlvBehaviourInfo("getinv", RLV_BHVR_GETINV, RLV_TYPE_REPLY)); addEntry(new RlvBehaviourInfo("getinvworn", RLV_BHVR_GETINVWORN, RLV_TYPE_REPLY)); addEntry(new RlvBehaviourInfo("getoutfit", RLV_BHVR_GETOUTFIT, RLV_TYPE_REPLY)); @@ -645,8 +649,9 @@ RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCommand) } RlvCommand::RlvCommand(const RlvCommand& rlvCmd, ERlvParamType eParamType) - : m_fValid(rlvCmd.m_fValid), m_idObj(rlvCmd.m_idObj), m_strBehaviour(rlvCmd.m_strBehaviour), m_pBhvrInfo(rlvCmd.m_pBhvrInfo), - m_eParamType( (RLV_TYPE_UNKNOWN == eParamType) ? rlvCmd.m_eParamType : eParamType),m_fStrict(rlvCmd.m_fStrict), m_strOption(rlvCmd.m_strOption), m_strParam(rlvCmd.m_strParam), m_fRefCounted(false) + : m_fValid(rlvCmd.m_fValid), m_idObj(rlvCmd.m_idObj), m_strBehaviour(rlvCmd.m_strBehaviour), m_pBhvrInfo(rlvCmd.m_pBhvrInfo) + , m_eParamType( (RLV_TYPE_UNKNOWN == eParamType) ? rlvCmd.m_eParamType : eParamType),m_fStrict(rlvCmd.m_fStrict), m_strOption(rlvCmd.m_strOption) + , m_strParam(rlvCmd.m_strParam), m_fRefCounted(rlvCmd.m_fRefCounted) { } @@ -688,6 +693,13 @@ bool RlvCommand::parseCommand(const std::string& strCommand, std::string& strBeh // Command option parsing utility classes // +template<> +bool RlvCommandOptionHelper::parseOption(const std::string& strOption, std::string& valueOption) +{ + valueOption = strOption; + return true; +} + template<> bool RlvCommandOptionHelper::parseOption(const std::string& strOption, LLUUID& idOption) { @@ -769,6 +781,17 @@ bool RlvCommandOptionHelper::parseOption(const std:: return pFolder != NULL; } +template<> +bool RlvCommandOptionHelper::parseOption(const std::string& strOption, LLVector2& vecOption) +{ + if (!strOption.empty()) + { + S32 cntToken = sscanf(strOption.c_str(), "%f/%f", vecOption.mV + 0, vecOption.mV + 1); + return (2 == cntToken); + } + return false; +} + template<> bool RlvCommandOptionHelper::parseOption(const std::string& strOption, LLVector3& vecOption) { @@ -791,6 +814,17 @@ bool RlvCommandOptionHelper::parseOption(const std::string& strOptio return false; } +template<> +bool RlvCommandOptionHelper::parseOption(const std::string& strOption, LLColor3& clrOption) +{ + if (!strOption.empty()) + { + S32 cntToken = sscanf(strOption.c_str(), "%f/%f/%f", clrOption.mV + 0, clrOption.mV + 1, clrOption.mV + 2); + return (3 == cntToken); + } + return false; +} + template<> bool RlvCommandOptionHelper::parseOption(const std::string& strOption, RlvCommandOptionGeneric& genericOption) { @@ -992,7 +1026,7 @@ RlvObject::RlvObject(const LLUUID& idObj) : m_idObj(idObj), m_nLookupMisses(0) m_idRoot = (pObj) ? pObj->getRootEdit()->getID() : LLUUID::null; } -bool RlvObject::addCommand(const RlvCommand& rlvCmd) +const RlvCommand& RlvObject::addCommand(const RlvCommand& rlvCmd, bool& fAdded) { RLV_ASSERT(RLV_TYPE_ADD == rlvCmd.getParamType()); @@ -1002,14 +1036,15 @@ bool RlvObject::addCommand(const RlvCommand& rlvCmd) if ( (itCmd->getBehaviour() == rlvCmd.getBehaviour()) && (itCmd->getOption() == rlvCmd.getOption()) && (itCmd->isStrict() == rlvCmd.isStrict() ) ) { - return false; + fAdded = false; + return *itCmd; } } // Now that we know it's not a duplicate, add it to the end of the list m_Commands.push_back(rlvCmd); - - return true; + fAdded = true; + return m_Commands.back(); } bool RlvObject::removeCommand(const RlvCommand& rlvCmd) diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h index 31ac0e4bc3..9c8d015f76 100644 --- a/indra/newview/rlvhelper.h +++ b/indra/newview/rlvhelper.h @@ -172,7 +172,7 @@ template using RlvForceHandler = RlvCommandHandler using RlvReplyHandler = RlvCommandHandler; // List of shared handlers -typedef RlvBehaviourToggleHandler RlvBehaviourCamEyeFocusOffsetHandler; // Shared between @setcam_eyeoffset and @setcam_focusoffset +typedef RlvBehaviourToggleHandler RlvBehaviourCamEyeFocusOffsetHandler; // Shared between @setcam_eyeoffset, @setcam_eyeoffsetscale and @setcam_focusoffset typedef RlvBehaviourHandler RlvBehaviourAddRemAttachHandler; // Shared between @addattach and @remattach typedef RlvBehaviourHandler RlvBehaviourSendChannelHandler; // Shared between @sendchannel and @sendchannel_except typedef RlvBehaviourHandler RlvBehaviourRecvSendStartIMHandler; // Shared between @recvim, @sendim and @startim @@ -181,7 +181,7 @@ typedef RlvBehaviourToggleHandler RlvBehaviourShowSelfToggleH typedef RlvBehaviourHandler RlvBehaviourCamZoomMinMaxHandler; // Shared between @camzoommin and @camzoommax (deprecated) typedef RlvReplyHandler RlvReplyCamMinMaxModifierHandler; // Shared between @getcam_avdistmin and @getcam_avdistmax typedef RlvForceHandler RlvForceRemAttachHandler; // Shared between @remattach and @detach -typedef RlvForceHandler RlvForceCamEyeFocusOffsetHandler; // Shared between @setcam_eyeoffset and @setcam_focusoffset +typedef RlvForceHandler RlvForceCamEyeFocusOffsetHandler; // Shared between @setcam_eyeoffset, @setcam_eyeoffsetscale and @setcam_focusoffset // // RlvCommandProcessor - Templated glue class that brings RlvBehaviourInfo, RlvCommandHandlerBaseImpl and RlvCommandHandler together @@ -434,8 +434,8 @@ public: * Member functions */ public: - bool addCommand(const RlvCommand& rlvCmd); - bool removeCommand(const RlvCommand& rlvCmd); + const RlvCommand& addCommand(const RlvCommand& rlvCmd, bool& fAdded); + bool removeCommand(const RlvCommand& rlvCmd); std::string getStatusString(const std::string& strFilter, const std::string& strSeparator) const; bool hasBehaviour(ERlvBehaviour eBehaviour, bool fStrictOnly) const; diff --git a/indra/newview/rlvinventory.cpp b/indra/newview/rlvinventory.cpp index b8cfb82991..0f6c108f79 100644 --- a/indra/newview/rlvinventory.cpp +++ b/indra/newview/rlvinventory.cpp @@ -578,36 +578,56 @@ void RlvGiveToRLVOffer::onCategoryCreateCallback(LLUUID idFolder, RlvGiveToRLVOf pInstance->onDestinationCreated(idFolder, pInstance->m_DestPath.front()); } -// Checked: 2014-01-07 (RLVa-1.4.10) -void RlvGiveToRLVOffer::moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName) +// static +void RlvGiveToRLVOffer::moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName, const LLPointer cbFinal) { - const LLViewerInventoryCategory* pDest = gInventory.getCategory(idDestination); const LLViewerInventoryCategory* pFolder = gInventory.getCategory(idFolder); - if ( (pDest) && (pFolder) ) + if ( (idDestination.notNull()) && (pFolder) ) { - LLPointer pNewFolder = new LLViewerInventoryCategory(pFolder); - if (pDest->getUUID() != pFolder->getParentUUID()) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate updOldParent(pFolder->getParentUUID(), -1); - update.push_back(updOldParent); - LLInventoryModel::LLCategoryUpdate updNewParent(pDest->getUUID(), 1); - update.push_back(updNewParent); - gInventory.accountForUpdate(update); + bool needsRename = (pFolder->getName() != strName); - pNewFolder->setParent(pDest->getUUID()); - pNewFolder->updateParentOnServer(FALSE); + LLPointer cbMove; + if (idDestination != pFolder->getParentUUID()) + { + // We have to move *after* the rename operation completes or AIS will drop it + if (!needsRename) + { + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate updOldParent(pFolder->getParentUUID(), -1); + update.push_back(updOldParent); + LLInventoryModel::LLCategoryUpdate updNewParent(idDestination, 1); + update.push_back(updNewParent); + gInventory.accountForUpdate(update); + + LLPointer pNewFolder = new LLViewerInventoryCategory(pFolder); + pNewFolder->setParent(idDestination); + pNewFolder->updateParentOnServer(FALSE); + + gInventory.updateCategory(pNewFolder); + gInventory.notifyObservers(); + + if (cbFinal) + { + cbFinal.get()->fire(idFolder); + } + } + else + { + cbMove = new LLBoostFuncInventoryCallback(boost::bind(RlvGiveToRLVOffer::moveAndRename, _1, idDestination, strName, cbFinal)); + } } - pNewFolder->rename(strName); - pNewFolder->updateServer(FALSE); - gInventory.updateCategory(pNewFolder); - - gInventory.notifyObservers(); + if (needsRename) + { + rename_category(&gInventory, idFolder, strName, (cbMove) ? cbMove : cbFinal); + } + } + else if (cbFinal) + { + cbFinal.get()->fire(LLUUID::null); } } -// Checked: 2010-04-18 (RLVa-1.2.0) void RlvGiveToRLVTaskOffer::changed(U32 mask) { if (mask & LLInventoryObserver::ADD) @@ -633,7 +653,6 @@ void RlvGiveToRLVTaskOffer::changed(U32 mask) } } -// Checked: 2010-04-18 (RLVa-1.2.0) void RlvGiveToRLVTaskOffer::done() { gInventory.removeObserver(this); @@ -642,22 +661,29 @@ void RlvGiveToRLVTaskOffer::done() doOnIdleOneTime(boost::bind(&RlvGiveToRLVTaskOffer::doneIdle, this)); } -// Checked: 2014-01-07 (RLVa-1.4.10) void RlvGiveToRLVTaskOffer::doneIdle() { - const LLViewerInventoryCategory* pFolder = (m_Folders.size()) ? gInventory.getCategory(m_Folders.front()) : NULL; + const LLViewerInventoryCategory* pFolder = (m_Folders.size()) ? gInventory.getCategory(m_Folders.front()) : nullptr; if ( (!pFolder) || (!createDestinationFolder(pFolder->getName())) ) delete this; } -// Checked: 2010-04-18 (RLVa-1.2.0) -void RlvGiveToRLVTaskOffer::onDestinationCreated(const LLUUID& idFolder, const std::string& strName) +void RlvGiveToRLVTaskOffer::onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName) { - const LLViewerInventoryCategory* pTarget = (idFolder.notNull()) ? gInventory.getCategory(idFolder) : NULL; - if (pTarget) + if (const LLViewerInventoryCategory* pTarget = (idDestFolder.notNull()) ? gInventory.getCategory(idDestFolder) : nullptr) + { + moveAndRename(m_Folders.front(), idDestFolder, strName, new LLBoostFuncInventoryCallback(boost::bind(&RlvGiveToRLVTaskOffer::onOfferCompleted, this, _1))); + } + else + { + onOfferCompleted(LLUUID::null); + } +} + +void RlvGiveToRLVTaskOffer::onOfferCompleted(const LLUUID& idOfferedFolder) +{ + if (idOfferedFolder.notNull()) { - const LLUUID& idOfferedFolder = m_Folders.front(); - moveAndRename(idOfferedFolder, idFolder, strName); RlvBehaviourNotifyHandler::sendNotification("accepted_in_rlv inv_offer " + RlvInventory::instance().getSharedPath(idOfferedFolder)); } delete this; @@ -684,7 +710,7 @@ void RlvGiveToRLVAgentOffer::doneIdle() void RlvGiveToRLVAgentOffer::onDestinationCreated(const LLUUID& idFolder, const std::string& strName) { if ( (idFolder.notNull()) && (mComplete.size()) ) - moveAndRename(mComplete[0], idFolder, strName); + moveAndRename(mComplete[0], idFolder, strName, nullptr); delete this; } diff --git a/indra/newview/rlvinventory.h b/indra/newview/rlvinventory.h index 2eb73380bf..570cafc1a3 100644 --- a/indra/newview/rlvinventory.h +++ b/indra/newview/rlvinventory.h @@ -130,8 +130,8 @@ protected: virtual ~RlvGiveToRLVOffer() {} protected: bool createDestinationFolder(const std::string& strPath); - virtual void onDestinationCreated(const LLUUID& idFolder, const std::string& strName) = 0; - void moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName); + virtual void onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName) = 0; + static void moveAndRename(const LLUUID& idFolder, const LLUUID& idDestination, const std::string& strName, LLPointer cb); private: static void onCategoryCreateCallback(LLUUID idFolder, RlvGiveToRLVOffer* pInstance); @@ -146,11 +146,12 @@ class RlvGiveToRLVTaskOffer : public LLInventoryObserver, RlvGiveToRLVOffer { public: RlvGiveToRLVTaskOffer(const LLUUID& idTransaction) : RlvGiveToRLVOffer(), m_idTransaction(idTransaction) {} - /*virtual*/ void changed(U32 mask); + void changed(U32 mask) override; protected: - /*virtual*/ void done(); - void doneIdle(); - /*virtual*/ void onDestinationCreated(const LLUUID& idFolder, const std::string& strName); + void done(); + void doneIdle(); + void onDestinationCreated(const LLUUID& idDestFolder, const std::string& strName) override; + void onOfferCompleted(const LLUUID& idOfferedFolder); protected: typedef std::vector folder_ref_t; diff --git a/indra/newview/rlvui.cpp b/indra/newview/rlvui.cpp index a81071e1ff..575a32677e 100644 --- a/indra/newview/rlvui.cpp +++ b/indra/newview/rlvui.cpp @@ -136,7 +136,6 @@ void RlvUIEnabler::onToggleShowLoc() // If the last entry in the persistent teleport history matches the current teleport history entry then we should remove it LLTeleportHistory* pTpHistory = LLTeleportHistory::getInstance(); LLTeleportHistoryStorage* pTpHistoryStg = LLTeleportHistoryStorage::getInstance(); - RLV_ASSERT( (pTpHistory) && (pTpHistoryStg) && (pTpHistory->getItems().size() > 0) && (pTpHistory->getCurrentItemIndex() >= 0) ); if ( (pTpHistory) && (pTpHistory->getItems().size() > 0) && (pTpHistory->getCurrentItemIndex() >= 0) && (pTpHistoryStg) && (pTpHistoryStg->getItems().size() > 0) ) { diff --git a/indra/newview/skins/ansastorm/xui/de/floater_camera.xml b/indra/newview/skins/ansastorm/xui/de/floater_camera.xml new file mode 100644 index 0000000000..5df5893841 --- /dev/null +++ b/indra/newview/skins/ansastorm/xui/de/floater_camera.xml @@ -0,0 +1,57 @@ + + + + Kamera um Fokus drehen + + + Kamera auf Fokus zoomen + + + Kamera nach oben, unten, links und rechts bewegen + + + Objekt ansehen + + + Voreinstellung... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/ansastorm/xui/pl/floater_camera.xml b/indra/newview/skins/ansastorm/xui/pl/floater_camera.xml new file mode 100644 index 0000000000..d3c4785b01 --- /dev/null +++ b/indra/newview/skins/ansastorm/xui/pl/floater_camera.xml @@ -0,0 +1,38 @@ + + + + Obróć kamerÄ™ wokół punktu skupienia + + + Przybliż kamerÄ™ do punktu skupienia + + + Poruszaj kamerÄ… w górÄ™, w dół, w lewo i w prawo + + + Pokaż obiekt + + + Użyj ustawienia + + + + + + + + + + + + + + + + + + + + + + + + + --> - - + \ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/floater_fixedenvironment.xml b/indra/newview/skins/default/xui/en/floater_fixedenvironment.xml index dbf91b0834..db5e3462a3 100644 --- a/indra/newview/skins/default/xui/en/floater_fixedenvironment.xml +++ b/indra/newview/skins/default/xui/en/floater_fixedenvironment.xml @@ -34,6 +34,7 @@ left_delta="10" width="35" height="20" + name="info_panel_label" font="SansSerif"> Name: diff --git a/indra/newview/skins/default/xui/en/floater_quickprefs.xml b/indra/newview/skins/default/xui/en/floater_quickprefs.xml index 949084eb30..d7e2d3498b 100644 --- a/indra/newview/skins/default/xui/en/floater_quickprefs.xml +++ b/indra/newview/skins/default/xui/en/floater_quickprefs.xml @@ -20,7 +20,7 @@ left="0" top="16" width="260" - height="152" + height="155" follows="all" layout="topleft"> + Version [VERSION] is required for login. +Release notes: [URL] Click OK to download and install. confirm Version [VERSION] has been downloaded and is ready to install. +Release notes: [URL] Click OK to install. confirm Version [VERSION] has been downloaded and is ready to install. +Release notes: [URL] Proceed? confirm - -[MESSAGE] - - Note: To activate the Autoresponse messages please set your online status via Comm Menu > Online Status @@ -529,7 +529,7 @@ mouse_opaque="false" name="text_box3" top_pad="10" - width="450"> + width="475"> Automatic response when in UNAVAILABLE (ie, BUSY) mode: + width="475"> Automatic response to all avatars when in AUTORESPONSE to everyone mode: + width="475"> Automatic response to non-friends when in AUTORESPONSE TO NON-FRIENDS mode: Physics invalid none + Sky + Water + Daycycle Shirt not worn @@ -2371,6 +2374,7 @@ Abuse Report New Daycycle New Water New Sky + New Settings /bow diff --git a/indra/newview/skins/default/xui/es/floater_about.xml b/indra/newview/skins/default/xui/es/floater_about.xml index e26053dc17..b9e269d5ff 100644 --- a/indra/newview/skins/default/xui/es/floater_about.xml +++ b/indra/newview/skins/default/xui/es/floater_about.xml @@ -48,7 +48,7 @@ Memoria de texturas (Texture memory): [TEXTUREMEMORY] MB ([TEXTUREMEMORYMULTIPLI Para obtener la información más reciente acerca de Firestorm, visita -http://www.firestormviewer.org +https://www.firestormviewer.org + diff --git a/indra/newview/skins/starlightcui/xui/en/panel_progress.xml b/indra/newview/skins/starlightcui/xui/en/panel_progress.xml deleted file mode 100644 index b9d2e296e3..0000000000 --- a/indra/newview/skins/starlightcui/xui/en/panel_progress.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - -