diff --git a/BuildParams b/BuildParams
index c5f96d5ee3..27ae40767a 100644
--- a/BuildParams
+++ b/BuildParams
@@ -14,7 +14,7 @@ build_docs = true
build_Linux_Doxygen = true
# Need viewer-build-variables as well as other shared repositories
-buildscripts_shared_more_NAMEs="build_secrets build_variables"
+buildscripts_shared_more_NAMEs="build_secrets build_variables git_hooks"
################################################################
#### Examples of how to set the viewer_channel ####
diff --git a/autobuild.xml b/autobuild.xml
index 944bf6f96f..1b28638089 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -656,9 +656,9 @@
archive
name
linux64
@@ -814,9 +814,9 @@
archive
name
darwin64
@@ -826,9 +826,9 @@
archive
name
windows
@@ -838,9 +838,9 @@
archive
name
windows64
@@ -859,7 +859,7 @@
version
- 1.12.2.202109230751_91.1.21_g9dd45fe_chromium-91.0.4472.114
+ 1.12.3.202111032221_91.1.21_g9dd45fe_chromium-91.0.4472.114
elfio
version
- 202104010215.557744
+ 202109010216.563493
llphysicsextensions_source
@@ -2968,9 +2968,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
archive
hash
- 7920fce93d9addf63a420d86f91c5749
+ ea82e634334bccf088daf3d15eab07b7
url
- http://3p.firestormviewer.org/openssl-1.0.2l.180841936-linux64-180841936.tar.bz2
+ http://3p.firestormviewer.org/openssl-1.1.1l.212872015-linux64-212872015.tar.bz2
name
linux64
@@ -3310,9 +3310,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
archive
hash
- 6989053898b8e81e904e75553e378820
+ 97fac6d88480445c856083ed20d78093
url
- https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/77523/735051/viewer_manager-2.0.556340-darwin64-556340.tar.bz2
+ https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/85206/790666/viewer_manager-2.0.562101-darwin64-562101.tar.bz2
name
darwin64
@@ -3346,9 +3346,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
archive
hash
- 3446c1e54bb32542677caad0ec0d42ac
+ 3f6271ec0e2e2f0cc1067d4c4102bb4c
url
- https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/77525/735058/viewer_manager-2.0.556340-windows-556340.tar.bz2
+ https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/85208/790681/viewer_manager-2.0.562101-windows-562101.tar.bz2
name
windows
@@ -3359,7 +3359,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
source_type
hg
version
- 2.0.556340
+ 2.0.562101
vlc-bin
@@ -3378,9 +3378,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
archive
hash
- b639d0035f4a8c9b4973be428a1b7e61
+ 738688816ebd76958e49772712a6b972
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69569/671323/vlc_bin-3.0.9.549888-darwin64-549888.tar.bz2
+ https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90004/820701/vlc_bin-3.0.16.565299-darwin64-565299.tar.bz2
name
darwin64
@@ -3414,9 +3414,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
archive
hash
- 4f50b0c47daa081dd4fcb83763d5b0b2
+ 6801f91f3f27e626898bab90d40fc1c3
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69567/671314/vlc_bin-3.0.9.549888-windows-549888.tar.bz2
+ https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90005/820712/vlc_bin-3.0.16.565299-windows-565299.tar.bz2
name
windows
@@ -3426,16 +3426,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors
archive
hash
- c2f8c01fb6c261b72beb07f0c4cd423f
+ 7f66982d6edf3c38f3493e28826d58e8
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69568/671315/vlc_bin-3.0.9.549888-windows64-549888.tar.bz2
+ https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90006/820713/vlc_bin-3.0.16.565299-windows64-565299.tar.bz2
name
windows64
version
- 3.0.9.549888
+ 2.2.8.538966
xmlrpc-epi
diff --git a/build.sh b/build.sh
index 42588f0377..bdf36ec4e1 100755
--- a/build.sh
+++ b/build.sh
@@ -298,6 +298,22 @@ python_cmd "$helpers/codeticket.py" addinput "Viewer Channel" "${viewer_channel}
initialize_version # provided by buildscripts build.sh; sets version id
+begin_section "coding policy check"
+# On our TC Windows build hosts, the GitPython library underlying our
+# coding_policy_git.py script fails to run git for reasons we have not tried
+# to diagnose. Clearly git works fine on those hosts, or we would never get
+# this far. Running coding policy checks on one platform *should* suffice...
+if [[ "$arch" == "Darwin" ]]
+then
+ # install the git-hooks dependencies
+ pip install -r "$(native_path "$git_hooks_checkout/requirements.txt")" || \
+ fatal "pip install git-hooks failed"
+ # validate the branch we're about to build
+ python_cmd "$git_hooks_checkout/coding_policy_git.py" --all_files || \
+ fatal "coding policy check failed"
+fi
+end_section "coding policy check"
+
# Now run the build
succeeded=true
last_built_variant=
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index ce54b54c6f..0000000000
--- a/debian/changelog
+++ /dev/null
@@ -1,18 +0,0 @@
-secondlife-viewer (0.3) unstable; urgency=low
-
- * Initial debian configuration
-
- -- Don Kjer Wed, 04 Jul 2012 00:43:03 +0000
-
-secondlife-viewer (0.2) unstable; urgency=low
-
- * Adding default LSB headers for squeeze
-
- -- Tyler Kohler Thu, 24 Mar 2011 09:43:36 -0700
-
-secondlife-viewer (0.1) unstable; urgency=low
-
- * Cloned from debian package skeleton.
-
- -- Lex Linden Mon, 20 Sep 2010 08:01:59 -0700
-
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index 7ed6ff82de..0000000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-5
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 50b9ed9a26..0000000000
--- a/debian/control
+++ /dev/null
@@ -1,16 +0,0 @@
-Source: secondlife-viewer
-Section: unknown
-Priority: extra
-Maintainer: Don Linden
-Build-Depends: debhelper (>= 5)
-Homepage: http://secondlife.com
-Standards-Version: 3.7.2
-
-Package: secondlife-viewer
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends},
- ia32-libs,
- ia32-libs-gtk
-Description: Second Life Viewer
- Second Life is an online virtual world developed by Linden Lab.
-
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 106fa3802f..0000000000
--- a/debian/copyright
+++ /dev/null
@@ -1,32 +0,0 @@
-Second Life Viewer Copyright: 2000-2012 Linden Research, Inc.
-
-License:
-
-3Dconnexion SDK Copyright (C) 1992-2009 3Dconnexion
-APR Copyright (C) 2011 The Apache Software Foundation
-Collada DOM Copyright 2006 Sony Computer Entertainment Inc.
-cURL Copyright (C) 1996-2010, Daniel Stenberg, (daniel@haxx.se)
-DBus/dbus-glib Copyright (C) 2002, 2003 CodeFactory AB / Copyright (C) 2003, 2004 Red Hat, Inc.
-expat Copyright (C) 1998, 1999, 2000 Thai Open Source Software Center Ltd.
-FreeType Copyright (C) 1996-2002, 2006 David Turner, Robert Wilhelm, and Werner Lemberg.
-GL Copyright (C) 1999-2004 Brian Paul.
-GLOD Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns Hopkins University and David Luebke, Brenden Schubert, University of Virginia.
-google-perftools Copyright (c) 2005, Google Inc.
-Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited.
-jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW)
-jpeglib Copyright (C) 1991-1998, Thomas G. Lane.
-ogg/vorbis Copyright (C) 2002, Xiphophorus
-OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.
-PCRE Copyright (c) 1997-2012 University of Cambridge
-SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
-SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
-zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
-
-Second Life Viewer uses Havok (TM) Physics. (c)Copyright 1999-2010 Havok.com Inc. (and its Licensors). All Rights Reserved. See www.havok.com for details.
-
-This software contains source code provided by NVIDIA Corporation.
-
-All rights reserved. See licenses.txt for details.
-
-Voice chat Audio coding: Polycom(R) Siren14(TM) (ITU-T Rec. G.722.1 Annex C)
diff --git a/debian/postinst b/debian/postinst
deleted file mode 100644
index 2c4f8ea858..0000000000
--- a/debian/postinst
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/sh
-# postinst script for secondlife-viewer
-#
-# Delete this file if you don't need it.
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * `configure'
-# * `abort-upgrade'
-# * `abort-remove' `in-favour'
-#
-# * `abort-remove'
-# * `abort-deconfigure' `in-favour'
-# `removing'
-#
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- configure)
- ;;
-
- abort-upgrade|abort-remove|abort-deconfigure)
- ;;
-
- *)
- echo "postinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts. Don't delete this!
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/postrm b/debian/postrm
deleted file mode 100644
index a575936ab0..0000000000
--- a/debian/postrm
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/sh
-# postrm script for secondlife-viewer
-#
-# Delete this file if you don't need it.
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * `remove'
-# * `purge'
-# * `upgrade'
-# * `failed-upgrade'
-# * `abort-install'
-# * `abort-install'
-# * `abort-upgrade'
-# * `disappear'
-#
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
- ;;
-
- *)
- echo "postrm called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts. Don't delete this!
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/preinst b/debian/preinst
deleted file mode 100644
index f62243440f..0000000000
--- a/debian/preinst
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/sh
-# preinst script for secondlife-viewer
-#
-# Delete this file if you don't need it.
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * `install'
-# * `install'
-# * `upgrade'
-# * `abort-upgrade'
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- install|upgrade)
- ;;
-
- abort-upgrade)
- ;;
-
- *)
- echo "preinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts. Don't delete this!
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/prerm b/debian/prerm
deleted file mode 100644
index 405b8f9c87..0000000000
--- a/debian/prerm
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/sh
-# prerm script for secondlife-viewer
-#
-# Delete this file if you don't need it.
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * `remove'
-# * `upgrade'
-# * `failed-upgrade'
-# * `remove' `in-favour'
-# * `deconfigure' `in-favour'
-# `removing'
-#
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- remove|upgrade|deconfigure)
- ;;
-
- failed-upgrade)
- ;;
-
- *)
- echo "prerm called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts. Don't delete this!
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/rules b/debian/rules
deleted file mode 100644
index 305fc58bb4..0000000000
--- a/debian/rules
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-BASEDIR=opt/linden
-
-VIEWER_PKG=secondlife-viewer
-VIEWER_PACKAGEDIR=build-linux-i686/newview/packaged
-VIEWER_DESTDIR=$(CURDIR)/debian/$(VIEWER_PKG)
-VIEWER_VERSION:=$(shell dpkg-parsechangelog | grep ^Version | sed 's/^Version: //')
-VIEWER_INSTALLDIR:=$(BASEDIR)/viewer/SecondLife-i686-$(VIEWER_VERSION)
-
-configure: configure-stamp
-configure-stamp:
- dh_testdir
- # Add here commands to configure the package.
-
- touch configure-stamp
-
-build: build-stamp
-
-build-stamp: configure-stamp
- dh_testdir
-
- # Add here commands to compile the package.
- #$(MAKE)
- #docbook-to-man debian/secondlife-viewer.sgml > secondlife-viewer.1
-
- touch $@
-
-clean:
- dh_testdir
- dh_testroot
- rm -f build-stamp configure-stamp
-
- # Add here commands to clean up after the build process.
- #-$(MAKE) clean
-
- dh_clean
-
-install: build
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs
-
- # Add here commands to install the package into debian/secondlife-viewer.
- for file in $$(find $(VIEWER_PACKAGEDIR) -type f -o -type l | sed 's~$(VIEWER_PACKAGEDIR)/~~'); do \
- # create containing directory \
- install -v -m 755 -o root -g root -d "$$(dirname "$(VIEWER_DESTDIR)/$(VIEWER_INSTALLDIR)/$$file")"; \
- PERM=644; \
- if [ -x "$(VIEWER_PACKAGEDIR)/$$file" ]; then \
- PERM=755; \
- fi; \
- if [ -L "$(VIEWER_PACKAGEDIR)/$$file" ]; then \
- REAL="$$( readlink -f $(VIEWER_PACKAGEDIR)/$$file )"; \
- RELATIVE="$$( echo $$REAL | sed 's~$(CURDIR)/$(VIEWER_PACKAGEDIR)/~~' )"; \
- echo dh_link -p $(VIEWER_PKG) "$(VIEWER_INSTALLDIR)/$$RELATIVE" "$(VIEWER_INSTALLDIR)/$$file" ; \
- dh_link -p $(VIEWER_PKG) "$(VIEWER_INSTALLDIR)/$$RELATIVE" "$(VIEWER_INSTALLDIR)/$$file" ; \
- else \
- install -v -m $$PERM -o root -g root "$(VIEWER_PACKAGEDIR)/$$file" "$(VIEWER_DESTDIR)/$(VIEWER_INSTALLDIR)/$$file"; \
- fi; \
- done
- dh_link -p $(VIEWER_PKG) /$(VIEWER_INSTALLDIR)/secondlife /usr/bin/secondlife
- dh_link -p $(VIEWER_PKG) $(BASEDIR)/viewer/SecondLife-i686-$(VIEWER_VERSION) $(BASEDIR)/viewer/SecondLife
-
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
- dh_testdir
- dh_testroot
- dh_installchangelogs
- dh_installdocs
- dh_installexamples
-# dh_install
-# dh_installmenu
-# dh_installdebconf
-# dh_installlogrotate
-# dh_installemacsen
-# dh_installpam
-# dh_installmime
-# dh_python
-
-# To add an init script, uncomment this line and edit debian/init.d and
-# customize debian/secondlife-viewer.default to suit your needs.
-# dh_installinit
-
-# To add cron jobs, uncomment this line and make a crontab file named
-# debian/cron.d, and it will be installed in /etc/cron.d/
-# dh_installcron
-
-# dh_installinfo
- dh_installman
- dh_link
-# dh_strip
- dh_compress
-# dh_fixperms
-# dh_perl
-# dh_makeshlibs
- dh_installdeb
-# dh_shlibdeps
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides
deleted file mode 100644
index 661c20b572..0000000000
--- a/debian/source/lintian-overrides
+++ /dev/null
@@ -1,8 +0,0 @@
-# Linden packages install in opt/linden
-secondlife-viewer: dir-or-file-in-opt
-secondlife-viewer: section-is-dh_make-template
-secondlife-viewer: binary-without-manpage
-secondlife-viewer: maintainer-script-empty postrm
-secondlife-viewer: maintainer-script-empty preinst
-secondlife-viewer: maintainer-script-empty prerm
-secondlife-viewer: unstripped-binary-or-object
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 883534394c..d810ea02d3 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -273,6 +273,7 @@ Beq Janus
SL-13583
SL-14766
SL-14927
+ SL-11300
Beth Walcher
Bezilon Kasei
Biancaluce Robbiani
@@ -813,6 +814,7 @@ Jonathan Yap
STORM-2142
STORM-2145
SL-10089
+ BUG-229818
Kadah Coba
STORM-1060
STORM-1843
diff --git a/doc/sl-logo.png b/doc/sl-logo.png
new file mode 100644
index 0000000000..b9563c7ac7
Binary files /dev/null and b/doc/sl-logo.png differ
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 8e62e95bac..6909a94e4b 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -82,7 +82,7 @@ if (WINDOWS)
# CP changed to only append the flag for 32bit builds - on 64bit builds,
# locally at least, the build output is spammed with 1000s of 'D9002'
# warnings about this switch being ignored.
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
# Remove this, it's no option to cl.exe and causes a massive amount of warnings.
#if( ADDRESS_SIZE EQUAL 32 )
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /p:PreferredToolArchitecture=x64")
@@ -165,6 +165,11 @@ endif (WINDOWS)
if (LINUX)
set(CMAKE_SKIP_RPATH TRUE)
+
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0.0 )
+ message( FATAL_ERROR "GCC greater 9.4.0 is not supported. Recompile boost for support of GCC 10.0.0 and up." )
+ endif()
+
#
# And another hack for FORTIFY_SOURCE. Some distributions (for example Gentoo) define FORTIFY_SOURCE by default.
# Check if this is the case, if yes, do not define it again.
diff --git a/indra/cmake/OpenSSL.cmake b/indra/cmake/OpenSSL.cmake
index 32400f5e4e..f43ba7c26e 100644
--- a/indra/cmake/OpenSSL.cmake
+++ b/indra/cmake/OpenSSL.cmake
@@ -17,7 +17,7 @@ else (USESYSTEMLIBS)
endif (USESYSTEMLIBS)
if (LINUX)
- set(CRYPTO_LIBRARIES crypto dl)
+ set(CRYPTO_LIBRARIES crypto dl pthread)
elseif (DARWIN)
set(CRYPTO_LIBRARIES crypto)
endif (LINUX)
diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake
index abab96ce24..756c3f4f42 100644
--- a/indra/cmake/Variables.cmake
+++ b/indra/cmake/Variables.cmake
@@ -60,7 +60,7 @@ if (EXISTS ${CMAKE_SOURCE_DIR}/Server.cmake)
set(INSTALL_PROPRIETARY ON CACHE BOOL "Install proprietary binaries")
endif (EXISTS ${CMAKE_SOURCE_DIR}/Server.cmake)
set(TEMPLATE_VERIFIER_OPTIONS "" CACHE STRING "Options for scripts/template_verifier.py")
-set(TEMPLATE_VERIFIER_MASTER_URL "https://bitbucket.org/lindenlab/master-message-template-git/raw/HEAD/message_template.msg" CACHE STRING "Location of the master message template")
+set(TEMPLATE_VERIFIER_MASTER_URL "https://bitbucket.org/lindenlab/master-message-template-git/raw/master/message_template.msg" CACHE STRING "Location of the master message template")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
index 5366987cff..ade83202cf 100644
--- a/indra/edit-me-to-trigger-new-build.txt
+++ b/indra/edit-me-to-trigger-new-build.txt
@@ -1,3 +1,3 @@
euclid 5/29/2020
euclid 7/23/2020
-euclid 4/29/2021
\ No newline at end of file
+euclid 4/29/2021
diff --git a/indra/llappearance/llviewervisualparam.cpp b/indra/llappearance/llviewervisualparam.cpp
index 5763d5cf73..b9fad8540f 100644
--- a/indra/llappearance/llviewervisualparam.cpp
+++ b/indra/llappearance/llviewervisualparam.cpp
@@ -70,7 +70,7 @@ BOOL LLViewerVisualParamInfo::parseXml(LLXmlTreeNode *node)
static LLStdStringHandle wearable_string = LLXmlTree::addAttributeString("wearable");
if( node->getFastAttributeString( wearable_string, wearable) )
{
- mWearableType = LLWearableType::typeNameToType( wearable );
+ mWearableType = LLWearableType::getInstance()->typeNameToType( wearable );
}
static LLStdStringHandle edit_group_string = LLXmlTree::addAttributeString("edit_group");
diff --git a/indra/llappearance/llwearable.cpp b/indra/llappearance/llwearable.cpp
index 1a45cc29c3..4670af4181 100644
--- a/indra/llappearance/llwearable.cpp
+++ b/indra/llappearance/llwearable.cpp
@@ -76,17 +76,17 @@ LLWearable::~LLWearable()
const std::string& LLWearable::getTypeLabel() const
{
- return LLWearableType::getTypeLabel(mType);
+ return LLWearableType::getInstance()->getTypeLabel(mType);
}
const std::string& LLWearable::getTypeName() const
{
- return LLWearableType::getTypeName(mType);
+ return LLWearableType::getInstance()->getTypeName(mType);
}
LLAssetType::EType LLWearable::getAssetType() const
{
- return LLWearableType::getAssetType(mType);
+ return LLWearableType::getInstance()->getAssetType(mType);
}
BOOL LLWearable::exportFile(const std::string& filename) const
diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp
index 0bbae88203..5d23d3a876 100644
--- a/indra/llappearance/llwearabledata.cpp
+++ b/indra/llappearance/llwearabledata.cpp
@@ -241,10 +241,11 @@ BOOL LLWearableData::getWearableIndex(const LLWearable *wearable, U32& index_fou
U32 LLWearableData::getClothingLayerCount() const
{
U32 count = 0;
+ LLWearableType *wr_inst = LLWearableType::getInstance();
for (S32 i = 0; i < LLWearableType::WT_COUNT; i++)
{
LLWearableType::EType type = (LLWearableType::EType)i;
- if (LLWearableType::getAssetType(type)==LLAssetType::AT_CLOTHING)
+ if (wr_inst->getAssetType(type)==LLAssetType::AT_CLOTHING)
{
count += getWearableCount(type);
}
@@ -254,7 +255,7 @@ U32 LLWearableData::getClothingLayerCount() const
BOOL LLWearableData::canAddWearable(const LLWearableType::EType type) const
{
- LLAssetType::EType a_type = LLWearableType::getAssetType(type);
+ LLAssetType::EType a_type = LLWearableType::getInstance()->getAssetType(type);
if (a_type==LLAssetType::AT_CLOTHING)
{
if (type == LLWearableType::WT_PHYSICS) return (getWearableCount(type) < 1); // Don't add physics layer
diff --git a/indra/llappearance/llwearabletype.cpp b/indra/llappearance/llwearabletype.cpp
index 2640b5a428..1f5054fa65 100644
--- a/indra/llappearance/llwearabletype.cpp
+++ b/indra/llappearance/llwearabletype.cpp
@@ -30,162 +30,101 @@
#include "llinventorydefines.h"
-struct WearableEntry : public LLDictionaryEntry
+LLWearableType::LLWearableDictionary::LLWearableDictionary(LLTranslationBridge::ptr_t& trans)
{
- WearableEntry(LLWearableType& wtype,
- const std::string &name,
- const std::string& default_new_name,
- LLAssetType::EType assetType,
- LLInventoryType::EIconName iconName,
- BOOL disable_camera_switch = FALSE,
- BOOL allow_multiwear = TRUE) :
- LLDictionaryEntry(name),
- mAssetType(assetType),
- mDefaultNewName(default_new_name),
- mLabel(wtype.mTrans->getString(name)),
- mIconName(iconName),
- mDisableCameraSwitch(disable_camera_switch),
- mAllowMultiwear(allow_multiwear)
- {
-
- }
- const LLAssetType::EType mAssetType;
- const std::string mLabel;
- const std::string mDefaultNewName; //keep mLabel for backward compatibility
- LLInventoryType::EIconName mIconName;
- BOOL mDisableCameraSwitch;
- BOOL mAllowMultiwear;
-};
-
-class LLWearableDictionary : public LLParamSingleton,
- public LLDictionary
-{
- LLSINGLETON(LLWearableDictionary, LLWearableType&);
-
-// [RLVa:KB] - Checked: 2010-03-03 (RLVa-1.2.0a) | Added: RLVa-1.2.0a
-protected:
- // The default implementation asserts on 'notFound()' and returns -1 which isn't a valid EWearableType
- virtual LLWearableType::EType notFound() const { return LLWearableType::WT_INVALID; }
-// [/RLVa:KB]
-};
-
-LLWearableDictionary::LLWearableDictionary(LLWearableType& wtype)
-{
- 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));
+ addEntry(LLWearableType::WT_SHAPE, new WearableEntry(trans, "shape", "New Shape", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SHAPE, FALSE, FALSE));
+ addEntry(LLWearableType::WT_SKIN, new WearableEntry(trans, "skin", "New Skin", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_SKIN, FALSE, FALSE));
+ addEntry(LLWearableType::WT_HAIR, new WearableEntry(trans, "hair", "New Hair", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_HAIR, FALSE, FALSE));
+ addEntry(LLWearableType::WT_EYES, new WearableEntry(trans, "eyes", "New Eyes", LLAssetType::AT_BODYPART, LLInventoryType::ICONNAME_BODYPART_EYES, FALSE, FALSE));
+ addEntry(LLWearableType::WT_SHIRT, new WearableEntry(trans, "shirt", "New Shirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHIRT, FALSE, TRUE));
+ addEntry(LLWearableType::WT_PANTS, new WearableEntry(trans, "pants", "New Pants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PANTS, FALSE, TRUE));
+ addEntry(LLWearableType::WT_SHOES, new WearableEntry(trans, "shoes", "New Shoes", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SHOES, FALSE, TRUE));
+ addEntry(LLWearableType::WT_SOCKS, new WearableEntry(trans, "socks", "New Socks", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SOCKS, FALSE, TRUE));
+ addEntry(LLWearableType::WT_JACKET, new WearableEntry(trans, "jacket", "New Jacket", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_JACKET, FALSE, TRUE));
+ addEntry(LLWearableType::WT_GLOVES, new WearableEntry(trans, "gloves", "New Gloves", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_GLOVES, FALSE, TRUE));
+ addEntry(LLWearableType::WT_UNDERSHIRT, new WearableEntry(trans, "undershirt", "New Undershirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERSHIRT, FALSE, TRUE));
+ addEntry(LLWearableType::WT_UNDERPANTS, new WearableEntry(trans, "underpants", "New Underpants", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_UNDERPANTS, FALSE, TRUE));
+ addEntry(LLWearableType::WT_SKIRT, new WearableEntry(trans, "skirt", "New Skirt", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_SKIRT, FALSE, TRUE));
+ addEntry(LLWearableType::WT_ALPHA, new WearableEntry(trans, "alpha", "New Alpha", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_ALPHA, FALSE, TRUE));
+ addEntry(LLWearableType::WT_TATTOO, new WearableEntry(trans, "tattoo", "New Tattoo", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_TATTOO, FALSE, TRUE));
+ addEntry(LLWearableType::WT_UNIVERSAL, new WearableEntry(trans, "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(wtype, "physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, FALSE));
+ addEntry(LLWearableType::WT_PHYSICS, new WearableEntry(trans, "physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, FALSE));
// [/SL:KB]
-// addEntry(LLWearableType::WT_PHYSICS, new WearableEntry(wtype, "physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, TRUE));
+// addEntry(LLWearableType::WT_PHYSICS, new WearableEntry(trans, "physics", "New Physics", LLAssetType::AT_CLOTHING, LLInventoryType::ICONNAME_CLOTHING_PHYSICS, TRUE, TRUE));
- 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));
+ addEntry(LLWearableType::WT_INVALID, new WearableEntry(trans, "invalid", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_UNKNOWN, FALSE, FALSE));
+ addEntry(LLWearableType::WT_NONE, new WearableEntry(trans, "none", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryType::ICONNAME_NONE, FALSE, FALSE));
}
// class LLWearableType
-LLWearableType::LLWearableType(LLTranslationBridge* trans)
+LLWearableType::LLWearableType(LLTranslationBridge::ptr_t &trans)
+: mDictionary(trans)
{
- // LLTranslationBridge exists, but is not ready at this point in time since strings.xml is not yet loaded
- mTrans = trans;
}
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)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const LLWearableType::EType wearable = dict->lookup(type_name);
+ const LLWearableType::EType wearable = mDictionary.lookup(type_name);
return wearable;
}
-// static
const std::string& LLWearableType::getTypeName(LLWearableType::EType type)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const WearableEntry *entry = dict->lookup(type);
+ const WearableEntry *entry = mDictionary.lookup(type);
if (!entry) return getTypeName(WT_INVALID);
return entry->mName;
}
-//static
const std::string& LLWearableType::getTypeDefaultNewName(LLWearableType::EType type)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const WearableEntry *entry = dict->lookup(type);
+ const WearableEntry *entry = mDictionary.lookup(type);
if (!entry) return getTypeDefaultNewName(WT_INVALID);
return entry->mDefaultNewName;
}
-// static
const std::string& LLWearableType::getTypeLabel(LLWearableType::EType type)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const WearableEntry *entry = dict->lookup(type);
+ const WearableEntry *entry = mDictionary.lookup(type);
if (!entry) return getTypeLabel(WT_INVALID);
return entry->mLabel;
}
-// static
LLAssetType::EType LLWearableType::getAssetType(LLWearableType::EType type)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const WearableEntry *entry = dict->lookup(type);
+ const WearableEntry *entry = mDictionary.lookup(type);
if (!entry) return getAssetType(WT_INVALID);
return entry->mAssetType;
}
-// static
LLInventoryType::EIconName LLWearableType::getIconName(LLWearableType::EType type)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const WearableEntry *entry = dict->lookup(type);
+ const WearableEntry *entry = mDictionary.lookup(type);
if (!entry) return getIconName(WT_INVALID);
return entry->mIconName;
}
-// static
BOOL LLWearableType::getDisableCameraSwitch(LLWearableType::EType type)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const WearableEntry *entry = dict->lookup(type);
+ const WearableEntry *entry = mDictionary.lookup(type);
if (!entry) return FALSE;
return entry->mDisableCameraSwitch;
}
-// static
BOOL LLWearableType::getAllowMultiwear(LLWearableType::EType type)
{
- const LLWearableDictionary *dict = LLWearableDictionary::getInstance();
- const WearableEntry *entry = dict->lookup(type);
+ const WearableEntry *entry = mDictionary.lookup(type);
if (!entry) return FALSE;
return entry->mAllowMultiwear;
}
diff --git a/indra/llappearance/llwearabletype.h b/indra/llappearance/llwearabletype.h
index 57f3ef160d..11beda624a 100644
--- a/indra/llappearance/llwearabletype.h
+++ b/indra/llappearance/llwearabletype.h
@@ -35,10 +35,9 @@
class LLWearableType : public LLParamSingleton
{
- LLSINGLETON(LLWearableType, LLTranslationBridge* trans);
+ LLSINGLETON(LLWearableType, LLTranslationBridge::ptr_t &trans);
~LLWearableType();
void initSingleton();
- friend struct WearableEntry;
public:
enum EType
{
@@ -67,20 +66,59 @@ public:
// Most methods are wrappers for dictionary, but if LLWearableType is not initialized,
// they will crash. Whole LLWearableType is just wrapper for convinient calls.
- static const std::string& getTypeName(EType type);
- static const std::string& getTypeDefaultNewName(EType type);
- static const std::string& getTypeLabel(EType type);
- static LLAssetType::EType getAssetType(EType type);
- static EType typeNameToType(const std::string& type_name);
- static LLInventoryType::EIconName getIconName(EType type);
- static BOOL getDisableCameraSwitch(EType type);
- static BOOL getAllowMultiwear(EType type);
+ const std::string& getTypeName(EType type);
+ const std::string& getTypeDefaultNewName(EType type);
+ const std::string& getTypeLabel(EType type);
+ LLAssetType::EType getAssetType(EType type);
+ EType typeNameToType(const std::string& type_name);
+ LLInventoryType::EIconName getIconName(EType type);
+ BOOL getDisableCameraSwitch(EType type);
+ BOOL getAllowMultiwear(EType type);
static EType inventoryFlagsToWearableType(U32 flags);
-protected:
+private:
+ struct WearableEntry : public LLDictionaryEntry
+ {
+ WearableEntry(LLTranslationBridge::ptr_t& trans,
+ const std::string &name,
+ const std::string& default_new_name,
+ LLAssetType::EType assetType,
+ LLInventoryType::EIconName iconName,
+ BOOL disable_camera_switch = FALSE,
+ BOOL allow_multiwear = TRUE) :
+ LLDictionaryEntry(name),
+ mAssetType(assetType),
+ mDefaultNewName(default_new_name),
+ mLabel(trans->getString(name)),
+ mIconName(iconName),
+ mDisableCameraSwitch(disable_camera_switch),
+ mAllowMultiwear(allow_multiwear)
+ {
- LLTranslationBridge* mTrans;
+ }
+ const LLAssetType::EType mAssetType;
+ const std::string mLabel;
+ const std::string mDefaultNewName;
+ LLInventoryType::EIconName mIconName;
+ BOOL mDisableCameraSwitch;
+ BOOL mAllowMultiwear;
+ };
+
+ class LLWearableDictionary : public LLDictionary
+ {
+ public:
+ LLWearableDictionary(LLTranslationBridge::ptr_t& trans);
+ ~LLWearableDictionary() {}
+
+// [RLVa:KB] - Checked: 2010-03-03 (RLVa-1.2.0a) | Added: RLVa-1.2.0a
+ protected:
+ // The default implementation asserts on 'notFound()' and returns -1 which isn't a valid EWearableType
+ virtual LLWearableType::EType notFound() const { return LLWearableType::WT_INVALID; }
+// [/RLVa:KB]
+ };
+
+ LLWearableDictionary mDictionary;
};
#endif // LL_LLWEARABLETYPE_H
diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp
index ff7dcbd995..7ba65c4cec 100644
--- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp
+++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp
@@ -72,7 +72,8 @@ mSystem(system),
mCurrentInternetStreamp(NULL),
mStreamGroup(NULL),
mFMODInternetStreamChannelp(NULL),
-mGain(1.0f)
+mGain(1.0f),
+mWasAlreadyPlaying(false)
{
// Number of milliseconds of audio to buffer for the audio card.
// Must be larger than the usual Second Life frame stutter time.
@@ -158,18 +159,14 @@ void LLStreamingAudio_FMODSTUDIO::update()
if (Check_FMOD_Error(mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy), "FMOD::Sound::getOpenState"))
{
LL_WARNS() << "Internet stream openstate error: open_state = " << open_state << " - progress = " << progress << " - starving = " << starving << " - diskbusy = " << diskbusy << LL_ENDL;
+ bool was_playing = mWasAlreadyPlaying;
stop();
- return;
- }
- else if (open_state == FMOD_OPENSTATE_ERROR)
- {
- // Actually we might not get into this case at all since according to the
- // FMOD API doc, one should check the result of getOpenState for further
- // details, which most likely means if open_state is FMOD_OPENSTATE_ERROR,
- // calling getOpenState will return anything but FMOD_OK and we end up in
- // the if-case above.
- LL_WARNS() << "Internet stream openstate error: progress = " << progress << " - starving = " << starving << " - diskbusy = " << diskbusy << LL_ENDL;
- stop();
+ // Try to restart previously playing stream on socket error
+ if (open_state == FMOD_OPENSTATE_ERROR && was_playing)
+ {
+ LL_WARNS() << "Stream was playing before - trying to restart" << LL_ENDL;
+ start(mURL);
+ }
return;
}
else if (open_state == FMOD_OPENSTATE_READY)
@@ -183,6 +180,14 @@ void LLStreamingAudio_FMODSTUDIO::update()
// Reset volume to previously set volume
setGain(getGain());
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused");
+ mWasAlreadyPlaying = true;
+ }
+ }
+ else if (open_state == FMOD_OPENSTATE_PLAYING)
+ {
+ if (!mWasAlreadyPlaying)
+ {
+ mWasAlreadyPlaying = true;
}
}
@@ -317,6 +322,7 @@ void LLStreamingAudio_FMODSTUDIO::update()
void LLStreamingAudio_FMODSTUDIO::stop()
{
mPendingURL.clear();
+ mWasAlreadyPlaying = false;
if (mFMODInternetStreamChannelp)
{
diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h
index 7ec5a0dbf4..43e8ef9c81 100644
--- a/indra/llaudio/llstreamingaudio_fmodstudio.h
+++ b/indra/llaudio/llstreamingaudio_fmodstudio.h
@@ -80,6 +80,8 @@ private:
bool mNewMetadata;
LLSD mMetadata;
// Streamtitle display
+
+ bool mWasAlreadyPlaying;
};
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index fdd71ce9d9..02b1d38fa9 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -208,9 +208,9 @@ set(llcommon_HEADER_FILES
llqueuedthread.h
llrand.h
llrefcount.h
+ llregex.h
llregistry.h
llrun.h
- llrefcount.h
llsafehandle.h
llsd.h
llsdjson.h
@@ -256,13 +256,14 @@ set(llcommon_HEADER_FILES
StackWalker.h
)
-# Add all nd* files. memory pool, intrinsics, ...
-# Tracy Profiler support
-list(APPEND llcommon_SOURCE_FILES fstelemetry.cpp)
-if (USE_TRACY_PROFILER)
- list(APPEND llcommon_SOURCE_FILES fstracyclient.cpp)
-endif()
-
+ # Tracy Profiler support
+ list(APPEND llcommon_SOURCE_FILES fstelemetry.cpp)
+ if (USE_TRACY_PROFILER)
+ list(APPEND llcommon_SOURCE_FILES fstracyclient.cpp)
+ endif()
+ # Tracy Profiler support
+
+ # Add all nd* files. memory pool, intrinsics, ...
SET( llcommon_ND_SOURCE_FILES
nd/ndexceptions.cpp
nd/ndlogthrottle.cpp
diff --git a/indra/llcommon/blockingconcurrentqueue.h b/indra/llcommon/blockingconcurrentqueue.h
new file mode 100644
index 0000000000..66579b6caf
--- /dev/null
+++ b/indra/llcommon/blockingconcurrentqueue.h
@@ -0,0 +1,582 @@
+// Provides an efficient blocking version of moodycamel::ConcurrentQueue.
+// ©2015-2020 Cameron Desrochers. Distributed under the terms of the simplified
+// BSD license, available at the top of concurrentqueue.h.
+// Also dual-licensed under the Boost Software License (see LICENSE.md)
+// Uses Jeff Preshing's semaphore implementation (under the terms of its
+// separate zlib license, see lightweightsemaphore.h).
+
+#pragma once
+
+#include "concurrentqueue.h"
+#include "lightweightsemaphore.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace moodycamel
+{
+// This is a blocking version of the queue. It has an almost identical interface to
+// the normal non-blocking version, with the addition of various wait_dequeue() methods
+// and the removal of producer-specific dequeue methods.
+template
+class BlockingConcurrentQueue
+{
+private:
+ typedef ::moodycamel::ConcurrentQueue ConcurrentQueue;
+ typedef ::moodycamel::LightweightSemaphore LightweightSemaphore;
+
+public:
+ typedef typename ConcurrentQueue::producer_token_t producer_token_t;
+ typedef typename ConcurrentQueue::consumer_token_t consumer_token_t;
+
+ typedef typename ConcurrentQueue::index_t index_t;
+ typedef typename ConcurrentQueue::size_t size_t;
+ typedef typename std::make_signed::type ssize_t;
+
+ static const size_t BLOCK_SIZE = ConcurrentQueue::BLOCK_SIZE;
+ static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = ConcurrentQueue::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD;
+ static const size_t EXPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::EXPLICIT_INITIAL_INDEX_SIZE;
+ static const size_t IMPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::IMPLICIT_INITIAL_INDEX_SIZE;
+ static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = ConcurrentQueue::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
+ static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = ConcurrentQueue::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE;
+ static const size_t MAX_SUBQUEUE_SIZE = ConcurrentQueue::MAX_SUBQUEUE_SIZE;
+
+public:
+ // Creates a queue with at least `capacity` element slots; note that the
+ // actual number of elements that can be inserted without additional memory
+ // allocation depends on the number of producers and the block size (e.g. if
+ // the block size is equal to `capacity`, only a single block will be allocated
+ // up-front, which means only a single producer will be able to enqueue elements
+ // without an extra allocation -- blocks aren't shared between producers).
+ // This method is not thread safe -- it is up to the user to ensure that the
+ // queue is fully constructed before it starts being used by other threads (this
+ // includes making the memory effects of construction visible, possibly with a
+ // memory barrier).
+ explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
+ : inner(capacity), sema(create(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy)
+ {
+ assert(reinterpret_cast((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
+ if (!sema) {
+ MOODYCAMEL_THROW(std::bad_alloc());
+ }
+ }
+
+ BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
+ : inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy)
+ {
+ assert(reinterpret_cast((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
+ if (!sema) {
+ MOODYCAMEL_THROW(std::bad_alloc());
+ }
+ }
+
+ // Disable copying and copy assignment
+ BlockingConcurrentQueue(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
+ BlockingConcurrentQueue& operator=(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
+
+ // Moving is supported, but note that it is *not* a thread-safe operation.
+ // Nobody can use the queue while it's being moved, and the memory effects
+ // of that move must be propagated to other threads before they can use it.
+ // Note: When a queue is moved, its tokens are still valid but can only be
+ // used with the destination queue (i.e. semantically they are moved along
+ // with the queue itself).
+ BlockingConcurrentQueue(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
+ : inner(std::move(other.inner)), sema(std::move(other.sema))
+ { }
+
+ inline BlockingConcurrentQueue& operator=(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
+ {
+ return swap_internal(other);
+ }
+
+ // Swaps this queue's state with the other's. Not thread-safe.
+ // Swapping two queues does not invalidate their tokens, however
+ // the tokens that were created for one queue must be used with
+ // only the swapped queue (i.e. the tokens are tied to the
+ // queue's movable state, not the object itself).
+ inline void swap(BlockingConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
+ {
+ swap_internal(other);
+ }
+
+private:
+ BlockingConcurrentQueue& swap_internal(BlockingConcurrentQueue& other)
+ {
+ if (this == &other) {
+ return *this;
+ }
+
+ inner.swap(other.inner);
+ sema.swap(other.sema);
+ return *this;
+ }
+
+public:
+ // Enqueues a single item (by copying it).
+ // Allocates memory if required. Only fails if memory allocation fails (or implicit
+ // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
+ // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
+ // Thread-safe.
+ inline bool enqueue(T const& item)
+ {
+ if ((details::likely)(inner.enqueue(item))) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues a single item (by moving it, if possible).
+ // Allocates memory if required. Only fails if memory allocation fails (or implicit
+ // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
+ // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
+ // Thread-safe.
+ inline bool enqueue(T&& item)
+ {
+ if ((details::likely)(inner.enqueue(std::move(item)))) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues a single item (by copying it) using an explicit producer token.
+ // Allocates memory if required. Only fails if memory allocation fails (or
+ // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
+ // Thread-safe.
+ inline bool enqueue(producer_token_t const& token, T const& item)
+ {
+ if ((details::likely)(inner.enqueue(token, item))) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues a single item (by moving it, if possible) using an explicit producer token.
+ // Allocates memory if required. Only fails if memory allocation fails (or
+ // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
+ // Thread-safe.
+ inline bool enqueue(producer_token_t const& token, T&& item)
+ {
+ if ((details::likely)(inner.enqueue(token, std::move(item)))) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues several items.
+ // Allocates memory if required. Only fails if memory allocation fails (or
+ // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
+ // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
+ // Note: Use std::make_move_iterator if the elements should be moved instead of copied.
+ // Thread-safe.
+ template
+ inline bool enqueue_bulk(It itemFirst, size_t count)
+ {
+ if ((details::likely)(inner.enqueue_bulk(std::forward(itemFirst), count))) {
+ sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues several items using an explicit producer token.
+ // Allocates memory if required. Only fails if memory allocation fails
+ // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
+ // Note: Use std::make_move_iterator if the elements should be moved
+ // instead of copied.
+ // Thread-safe.
+ template
+ inline bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
+ {
+ if ((details::likely)(inner.enqueue_bulk(token, std::forward(itemFirst), count))) {
+ sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues a single item (by copying it).
+ // Does not allocate memory. Fails if not enough room to enqueue (or implicit
+ // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
+ // is 0).
+ // Thread-safe.
+ inline bool try_enqueue(T const& item)
+ {
+ if (inner.try_enqueue(item)) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues a single item (by moving it, if possible).
+ // Does not allocate memory (except for one-time implicit producer).
+ // Fails if not enough room to enqueue (or implicit production is
+ // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
+ // Thread-safe.
+ inline bool try_enqueue(T&& item)
+ {
+ if (inner.try_enqueue(std::move(item))) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues a single item (by copying it) using an explicit producer token.
+ // Does not allocate memory. Fails if not enough room to enqueue.
+ // Thread-safe.
+ inline bool try_enqueue(producer_token_t const& token, T const& item)
+ {
+ if (inner.try_enqueue(token, item)) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues a single item (by moving it, if possible) using an explicit producer token.
+ // Does not allocate memory. Fails if not enough room to enqueue.
+ // Thread-safe.
+ inline bool try_enqueue(producer_token_t const& token, T&& item)
+ {
+ if (inner.try_enqueue(token, std::move(item))) {
+ sema->signal();
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues several items.
+ // Does not allocate memory (except for one-time implicit producer).
+ // Fails if not enough room to enqueue (or implicit production is
+ // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
+ // Note: Use std::make_move_iterator if the elements should be moved
+ // instead of copied.
+ // Thread-safe.
+ template
+ inline bool try_enqueue_bulk(It itemFirst, size_t count)
+ {
+ if (inner.try_enqueue_bulk(std::forward(itemFirst), count)) {
+ sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
+ return true;
+ }
+ return false;
+ }
+
+ // Enqueues several items using an explicit producer token.
+ // Does not allocate memory. Fails if not enough room to enqueue.
+ // Note: Use std::make_move_iterator if the elements should be moved
+ // instead of copied.
+ // Thread-safe.
+ template
+ inline bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
+ {
+ if (inner.try_enqueue_bulk(token, std::forward(itemFirst), count)) {
+ sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
+ return true;
+ }
+ return false;
+ }
+
+
+ // Attempts to dequeue from the queue.
+ // Returns false if all producer streams appeared empty at the time they
+ // were checked (so, the queue is likely but not guaranteed to be empty).
+ // Never allocates. Thread-safe.
+ template
+ inline bool try_dequeue(U& item)
+ {
+ if (sema->tryWait()) {
+ while (!inner.try_dequeue(item)) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // Attempts to dequeue from the queue using an explicit consumer token.
+ // Returns false if all producer streams appeared empty at the time they
+ // were checked (so, the queue is likely but not guaranteed to be empty).
+ // Never allocates. Thread-safe.
+ template
+ inline bool try_dequeue(consumer_token_t& token, U& item)
+ {
+ if (sema->tryWait()) {
+ while (!inner.try_dequeue(token, item)) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // Attempts to dequeue several elements from the queue.
+ // Returns the number of items actually dequeued.
+ // Returns 0 if all producer streams appeared empty at the time they
+ // were checked (so, the queue is likely but not guaranteed to be empty).
+ // Never allocates. Thread-safe.
+ template
+ inline size_t try_dequeue_bulk(It itemFirst, size_t max)
+ {
+ size_t count = 0;
+ max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
+ while (count != max) {
+ count += inner.template try_dequeue_bulk(itemFirst, max - count);
+ }
+ return count;
+ }
+
+ // Attempts to dequeue several elements from the queue using an explicit consumer token.
+ // Returns the number of items actually dequeued.
+ // Returns 0 if all producer streams appeared empty at the time they
+ // were checked (so, the queue is likely but not guaranteed to be empty).
+ // Never allocates. Thread-safe.
+ template
+ inline size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
+ {
+ size_t count = 0;
+ max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
+ while (count != max) {
+ count += inner.template try_dequeue_bulk(token, itemFirst, max - count);
+ }
+ return count;
+ }
+
+
+
+ // Blocks the current thread until there's something to dequeue, then
+ // dequeues it.
+ // Never allocates. Thread-safe.
+ template
+ inline void wait_dequeue(U& item)
+ {
+ while (!sema->wait()) {
+ continue;
+ }
+ while (!inner.try_dequeue(item)) {
+ continue;
+ }
+ }
+
+ // Blocks the current thread until either there's something to dequeue
+ // or the timeout (specified in microseconds) expires. Returns false
+ // without setting `item` if the timeout expires, otherwise assigns
+ // to `item` and returns true.
+ // Using a negative timeout indicates an indefinite timeout,
+ // and is thus functionally equivalent to calling wait_dequeue.
+ // Never allocates. Thread-safe.
+ template
+ inline bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
+ {
+ if (!sema->wait(timeout_usecs)) {
+ return false;
+ }
+ while (!inner.try_dequeue(item)) {
+ continue;
+ }
+ return true;
+ }
+
+ // Blocks the current thread until either there's something to dequeue
+ // or the timeout expires. Returns false without setting `item` if the
+ // timeout expires, otherwise assigns to `item` and returns true.
+ // Never allocates. Thread-safe.
+ template
+ inline bool wait_dequeue_timed(U& item, std::chrono::duration const& timeout)
+ {
+ return wait_dequeue_timed(item, std::chrono::duration_cast(timeout).count());
+ }
+
+ // Blocks the current thread until there's something to dequeue, then
+ // dequeues it using an explicit consumer token.
+ // Never allocates. Thread-safe.
+ template
+ inline void wait_dequeue(consumer_token_t& token, U& item)
+ {
+ while (!sema->wait()) {
+ continue;
+ }
+ while (!inner.try_dequeue(token, item)) {
+ continue;
+ }
+ }
+
+ // Blocks the current thread until either there's something to dequeue
+ // or the timeout (specified in microseconds) expires. Returns false
+ // without setting `item` if the timeout expires, otherwise assigns
+ // to `item` and returns true.
+ // Using a negative timeout indicates an indefinite timeout,
+ // and is thus functionally equivalent to calling wait_dequeue.
+ // Never allocates. Thread-safe.
+ template
+ inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::int64_t timeout_usecs)
+ {
+ if (!sema->wait(timeout_usecs)) {
+ return false;
+ }
+ while (!inner.try_dequeue(token, item)) {
+ continue;
+ }
+ return true;
+ }
+
+ // Blocks the current thread until either there's something to dequeue
+ // or the timeout expires. Returns false without setting `item` if the
+ // timeout expires, otherwise assigns to `item` and returns true.
+ // Never allocates. Thread-safe.
+ template
+ inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::chrono::duration const& timeout)
+ {
+ return wait_dequeue_timed(token, item, std::chrono::duration_cast(timeout).count());
+ }
+
+ // Attempts to dequeue several elements from the queue.
+ // Returns the number of items actually dequeued, which will
+ // always be at least one (this method blocks until the queue
+ // is non-empty) and at most max.
+ // Never allocates. Thread-safe.
+ template
+ inline size_t wait_dequeue_bulk(It itemFirst, size_t max)
+ {
+ size_t count = 0;
+ max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
+ while (count != max) {
+ count += inner.template try_dequeue_bulk(itemFirst, max - count);
+ }
+ return count;
+ }
+
+ // Attempts to dequeue several elements from the queue.
+ // Returns the number of items actually dequeued, which can
+ // be 0 if the timeout expires while waiting for elements,
+ // and at most max.
+ // Using a negative timeout indicates an indefinite timeout,
+ // and is thus functionally equivalent to calling wait_dequeue_bulk.
+ // Never allocates. Thread-safe.
+ template
+ inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::int64_t timeout_usecs)
+ {
+ size_t count = 0;
+ max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
+ while (count != max) {
+ count += inner.template try_dequeue_bulk(itemFirst, max - count);
+ }
+ return count;
+ }
+
+ // Attempts to dequeue several elements from the queue.
+ // Returns the number of items actually dequeued, which can
+ // be 0 if the timeout expires while waiting for elements,
+ // and at most max.
+ // Never allocates. Thread-safe.
+ template
+ inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::chrono::duration const& timeout)
+ {
+ return wait_dequeue_bulk_timed(itemFirst, max, std::chrono::duration_cast(timeout).count());
+ }
+
+ // Attempts to dequeue several elements from the queue using an explicit consumer token.
+ // Returns the number of items actually dequeued, which will
+ // always be at least one (this method blocks until the queue
+ // is non-empty) and at most max.
+ // Never allocates. Thread-safe.
+ template
+ inline size_t wait_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
+ {
+ size_t count = 0;
+ max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
+ while (count != max) {
+ count += inner.template try_dequeue_bulk(token, itemFirst, max - count);
+ }
+ return count;
+ }
+
+ // Attempts to dequeue several elements from the queue using an explicit consumer token.
+ // Returns the number of items actually dequeued, which can
+ // be 0 if the timeout expires while waiting for elements,
+ // and at most max.
+ // Using a negative timeout indicates an indefinite timeout,
+ // and is thus functionally equivalent to calling wait_dequeue_bulk.
+ // Never allocates. Thread-safe.
+ template
+ inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::int64_t timeout_usecs)
+ {
+ size_t count = 0;
+ max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
+ while (count != max) {
+ count += inner.template try_dequeue_bulk(token, itemFirst, max - count);
+ }
+ return count;
+ }
+
+ // Attempts to dequeue several elements from the queue using an explicit consumer token.
+ // Returns the number of items actually dequeued, which can
+ // be 0 if the timeout expires while waiting for elements,
+ // and at most max.
+ // Never allocates. Thread-safe.
+ template
+ inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::chrono::duration const& timeout)
+ {
+ return wait_dequeue_bulk_timed(token, itemFirst, max, std::chrono::duration_cast(timeout).count());
+ }
+
+
+ // Returns an estimate of the total number of elements currently in the queue. This
+ // estimate is only accurate if the queue has completely stabilized before it is called
+ // (i.e. all enqueue and dequeue operations have completed and their memory effects are
+ // visible on the calling thread, and no further operations start while this method is
+ // being called).
+ // Thread-safe.
+ inline size_t size_approx() const
+ {
+ return (size_t)sema->availableApprox();
+ }
+
+
+ // Returns true if the underlying atomic variables used by
+ // the queue are lock-free (they should be on most platforms).
+ // Thread-safe.
+ static bool is_lock_free()
+ {
+ return ConcurrentQueue::is_lock_free();
+ }
+
+
+private:
+ template
+ static inline U* create(A1&& a1, A2&& a2)
+ {
+ void* p = (Traits::malloc)(sizeof(U));
+ return p != nullptr ? new (p) U(std::forward(a1), std::forward(a2)) : nullptr;
+ }
+
+ template
+ static inline void destroy(U* p)
+ {
+ if (p != nullptr) {
+ p->~U();
+ }
+ (Traits::free)(p);
+ }
+
+private:
+ ConcurrentQueue inner;
+ std::unique_ptr sema;
+};
+
+
+template
+inline void swap(BlockingConcurrentQueue& a, BlockingConcurrentQueue& b) MOODYCAMEL_NOEXCEPT
+{
+ a.swap(b);
+}
+
+} // end namespace moodycamel
diff --git a/indra/llcommon/concurrentqueue.h b/indra/llcommon/concurrentqueue.h
new file mode 100644
index 0000000000..609ca4ab50
--- /dev/null
+++ b/indra/llcommon/concurrentqueue.h
@@ -0,0 +1,3742 @@
+// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue.
+// An overview, including benchmark results, is provided here:
+// http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++
+// The full design is also described in excruciating detail at:
+// http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue
+
+// Simplified BSD license:
+// Copyright (c) 2013-2020, Cameron Desrochers.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// - Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+// - Redistributions in binary form must reproduce the above copyright notice, this list of
+// conditions and the following disclaimer in the documentation and/or other materials
+// provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Also dual-licensed under the Boost Software License (see LICENSE.md)
+
+#pragma once
+
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and
+// Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings
+// upon assigning any computed values)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+
+#ifdef MCDBGQ_USE_RELACY
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
+#endif
+#endif
+
+#if defined(_MSC_VER) && (!defined(_HAS_CXX17) || !_HAS_CXX17)
+// VS2019 with /W4 warns about constant conditional expressions but unless /std=c++17 or higher
+// does not support `if constexpr`, so we have no choice but to simply disable the warning
+#pragma warning(push)
+#pragma warning(disable: 4127) // conditional expression is constant
+#endif
+
+#if defined(__APPLE__)
+#include "TargetConditionals.h"
+#endif
+
+#ifdef MCDBGQ_USE_RELACY
+#include "relacy/relacy_std.hpp"
+#include "relacy_shims.h"
+// We only use malloc/free anyway, and the delete macro messes up `= delete` method declarations.
+// We'll override the default trait malloc ourselves without a macro.
+#undef new
+#undef delete
+#undef malloc
+#undef free
+#else
+#include // Requires C++11. Sorry VS2010.
+#include
+#endif
+#include // for max_align_t
+#include
+#include
+#include
+#include
+#include
+#include
+#include // for CHAR_BIT
+#include
+#include // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading
+
+// Platform-specific definitions of a numeric thread ID type and an invalid value
+namespace moodycamel { namespace details {
+ template struct thread_id_converter {
+ typedef thread_id_t thread_id_numeric_size_t;
+ typedef thread_id_t thread_id_hash_t;
+ static thread_id_hash_t prehash(thread_id_t const& x) { return x; }
+ };
+} }
+#if defined(MCDBGQ_USE_RELACY)
+namespace moodycamel { namespace details {
+ typedef std::uint32_t thread_id_t;
+ static const thread_id_t invalid_thread_id = 0xFFFFFFFFU;
+ static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU;
+ static inline thread_id_t thread_id() { return rl::thread_index(); }
+} }
+#elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__)
+// No sense pulling in windows.h in a header, we'll manually declare the function
+// we use and rely on backwards-compatibility for this not to break
+extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
+namespace moodycamel { namespace details {
+ static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows");
+ typedef std::uint32_t thread_id_t;
+ static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx
+ static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU; // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4.
+ static inline thread_id_t thread_id() { return static_cast(::GetCurrentThreadId()); }
+} }
+#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE) || defined(MOODYCAMEL_NO_THREAD_LOCAL)
+namespace moodycamel { namespace details {
+ static_assert(sizeof(std::thread::id) == 4 || sizeof(std::thread::id) == 8, "std::thread::id is expected to be either 4 or 8 bytes");
+
+ typedef std::thread::id thread_id_t;
+ static const thread_id_t invalid_thread_id; // Default ctor creates invalid ID
+
+ // Note we don't define a invalid_thread_id2 since std::thread::id doesn't have one; it's
+ // only used if MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is defined anyway, which it won't
+ // be.
+ static inline thread_id_t thread_id() { return std::this_thread::get_id(); }
+
+ template struct thread_id_size { };
+ template<> struct thread_id_size<4> { typedef std::uint32_t numeric_t; };
+ template<> struct thread_id_size<8> { typedef std::uint64_t numeric_t; };
+
+ template<> struct thread_id_converter {
+ typedef thread_id_size::numeric_t thread_id_numeric_size_t;
+#ifndef __APPLE__
+ typedef std::size_t thread_id_hash_t;
+#else
+ typedef thread_id_numeric_size_t thread_id_hash_t;
+#endif
+
+ static thread_id_hash_t prehash(thread_id_t const& x)
+ {
+#ifndef __APPLE__
+ return std::hash()(x);
+#else
+ return *reinterpret_cast(&x);
+#endif
+ }
+ };
+} }
+#else
+// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475
+// In order to get a numeric thread ID in a platform-independent way, we use a thread-local
+// static variable's address as a thread identifier :-)
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+#define MOODYCAMEL_THREADLOCAL __thread
+#elif defined(_MSC_VER)
+#define MOODYCAMEL_THREADLOCAL __declspec(thread)
+#else
+// Assume C++11 compliant compiler
+#define MOODYCAMEL_THREADLOCAL thread_local
+#endif
+namespace moodycamel { namespace details {
+ typedef std::uintptr_t thread_id_t;
+ static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr
+ static const thread_id_t invalid_thread_id2 = 1; // Member accesses off a null pointer are also generally invalid. Plus it's not aligned.
+ inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast(&x); }
+} }
+#endif
+
+// Constexpr if
+#ifndef MOODYCAMEL_CONSTEXPR_IF
+#if (defined(_MSC_VER) && defined(_HAS_CXX17) && _HAS_CXX17) || __cplusplus > 201402L
+#define MOODYCAMEL_CONSTEXPR_IF if constexpr
+#define MOODYCAMEL_MAYBE_UNUSED [[maybe_unused]]
+#else
+#define MOODYCAMEL_CONSTEXPR_IF if
+#define MOODYCAMEL_MAYBE_UNUSED
+#endif
+#endif
+
+// Exceptions
+#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
+#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
+#define MOODYCAMEL_EXCEPTIONS_ENABLED
+#endif
+#endif
+#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
+#define MOODYCAMEL_TRY try
+#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__)
+#define MOODYCAMEL_RETHROW throw
+#define MOODYCAMEL_THROW(expr) throw (expr)
+#else
+#define MOODYCAMEL_TRY MOODYCAMEL_CONSTEXPR_IF (true)
+#define MOODYCAMEL_CATCH(...) else MOODYCAMEL_CONSTEXPR_IF (false)
+#define MOODYCAMEL_RETHROW
+#define MOODYCAMEL_THROW(expr)
+#endif
+
+#ifndef MOODYCAMEL_NOEXCEPT
+#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED)
+#define MOODYCAMEL_NOEXCEPT
+#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true
+#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true
+#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800
+// VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-(
+// We have to assume *all* non-trivial constructors may throw on VS2012!
+#define MOODYCAMEL_NOEXCEPT _NOEXCEPT
+#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value : std::is_trivially_copy_constructible::value)
+#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr))
+#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900
+#define MOODYCAMEL_NOEXCEPT _NOEXCEPT
+#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value || std::is_nothrow_move_constructible::value : std::is_trivially_copy_constructible::value || std::is_nothrow_copy_constructible::value)
+#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr))
+#else
+#define MOODYCAMEL_NOEXCEPT noexcept
+#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr)
+#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr)
+#endif
+#endif
+
+#ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
+#ifdef MCDBGQ_USE_RELACY
+#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
+#else
+// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445
+// g++ <=4.7 doesn't support thread_local either.
+// Finally, iOS/ARM doesn't have support for it either, and g++/ARM allows it to compile but it's unconfirmed to actually work
+#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && (!defined(__APPLE__) || !TARGET_OS_IPHONE) && !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__)
+// Assume `thread_local` is fully supported in all other C++11 compilers/platforms
+//#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED // always disabled for now since several users report having problems with it on
+#endif
+#endif
+#endif
+
+// VS2012 doesn't support deleted functions.
+// In this case, we declare the function normally but don't define it. A link error will be generated if the function is called.
+#ifndef MOODYCAMEL_DELETE_FUNCTION
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#define MOODYCAMEL_DELETE_FUNCTION
+#else
+#define MOODYCAMEL_DELETE_FUNCTION = delete
+#endif
+#endif
+
+namespace moodycamel { namespace details {
+#ifndef MOODYCAMEL_ALIGNAS
+// VS2013 doesn't support alignas or alignof, and align() requires a constant literal
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+#define MOODYCAMEL_ALIGNAS(alignment) __declspec(align(alignment))
+#define MOODYCAMEL_ALIGNOF(obj) __alignof(obj)
+#define MOODYCAMEL_ALIGNED_TYPE_LIKE(T, obj) typename details::Vs2013Aligned::value, T>::type
+ template struct Vs2013Aligned { }; // default, unsupported alignment
+ template struct Vs2013Aligned<1, T> { typedef __declspec(align(1)) T type; };
+ template struct Vs2013Aligned<2, T> { typedef __declspec(align(2)) T type; };
+ template struct Vs2013Aligned<4, T> { typedef __declspec(align(4)) T type; };
+ template struct Vs2013Aligned<8, T> { typedef __declspec(align(8)) T type; };
+ template struct Vs2013Aligned<16, T> { typedef __declspec(align(16)) T type; };
+ template struct Vs2013Aligned<32, T> { typedef __declspec(align(32)) T type; };
+ template struct Vs2013Aligned<64, T> { typedef __declspec(align(64)) T type; };
+ template struct Vs2013Aligned<128, T> { typedef __declspec(align(128)) T type; };
+ template struct Vs2013Aligned<256, T> { typedef __declspec(align(256)) T type; };
+#else
+ template struct identity { typedef T type; };
+#define MOODYCAMEL_ALIGNAS(alignment) alignas(alignment)
+#define MOODYCAMEL_ALIGNOF(obj) alignof(obj)
+#define MOODYCAMEL_ALIGNED_TYPE_LIKE(T, obj) alignas(alignof(obj)) typename details::identity::type
+#endif
+#endif
+} }
+
+
+// TSAN can false report races in lock-free code. To enable TSAN to be used from projects that use this one,
+// we can apply per-function compile-time suppression.
+// See https://clang.llvm.org/docs/ThreadSanitizer.html#has-feature-thread-sanitizer
+#define MOODYCAMEL_NO_TSAN
+#if defined(__has_feature)
+ #if __has_feature(thread_sanitizer)
+ #undef MOODYCAMEL_NO_TSAN
+ #define MOODYCAMEL_NO_TSAN __attribute__((no_sanitize("thread")))
+ #endif // TSAN
+#endif // TSAN
+
+// Compiler-specific likely/unlikely hints
+namespace moodycamel { namespace details {
+#if defined(__GNUC__)
+ static inline bool (likely)(bool x) { return __builtin_expect((x), true); }
+ static inline bool (unlikely)(bool x) { return __builtin_expect((x), false); }
+#else
+ static inline bool (likely)(bool x) { return x; }
+ static inline bool (unlikely)(bool x) { return x; }
+#endif
+} }
+
+#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
+#include "internal/concurrentqueue_internal_debug.h"
+#endif
+
+namespace moodycamel {
+namespace details {
+ template
+ struct const_numeric_max {
+ static_assert(std::is_integral::value, "const_numeric_max can only be used with integers");
+ static const T value = std::numeric_limits::is_signed
+ ? (static_cast(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast(1)
+ : static_cast(-1);
+ };
+
+#if defined(__GLIBCXX__)
+ typedef ::max_align_t std_max_align_t; // libstdc++ forgot to add it to std:: for a while
+#else
+ typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std::
+#endif
+
+ // Some platforms have incorrectly set max_align_t to a type with <8 bytes alignment even while supporting
+ // 8-byte aligned scalar values (*cough* 32-bit iOS). Work around this with our own union. See issue #64.
+ typedef union {
+ std_max_align_t x;
+ long long y;
+ void* z;
+ } max_align_t;
+}
+
+// Default traits for the ConcurrentQueue. To change some of the
+// traits without re-implementing all of them, inherit from this
+// struct and shadow the declarations you wish to be different;
+// since the traits are used as a template type parameter, the
+// shadowed declarations will be used where defined, and the defaults
+// otherwise.
+struct ConcurrentQueueDefaultTraits
+{
+ // General-purpose size type. std::size_t is strongly recommended.
+ typedef std::size_t size_t;
+
+ // The type used for the enqueue and dequeue indices. Must be at least as
+ // large as size_t. Should be significantly larger than the number of elements
+ // you expect to hold at once, especially if you have a high turnover rate;
+ // for example, on 32-bit x86, if you expect to have over a hundred million
+ // elements or pump several million elements through your queue in a very
+ // short space of time, using a 32-bit type *may* trigger a race condition.
+ // A 64-bit int type is recommended in that case, and in practice will
+ // prevent a race condition no matter the usage of the queue. Note that
+ // whether the queue is lock-free with a 64-int type depends on the whether
+ // std::atomic is lock-free, which is platform-specific.
+ typedef std::size_t index_t;
+
+ // Internally, all elements are enqueued and dequeued from multi-element
+ // blocks; this is the smallest controllable unit. If you expect few elements
+ // but many producers, a smaller block size should be favoured. For few producers
+ // and/or many elements, a larger block size is preferred. A sane default
+ // is provided. Must be a power of 2.
+ static const size_t BLOCK_SIZE = 32;
+
+ // For explicit producers (i.e. when using a producer token), the block is
+ // checked for being empty by iterating through a list of flags, one per element.
+ // For large block sizes, this is too inefficient, and switching to an atomic
+ // counter-based approach is faster. The switch is made for block sizes strictly
+ // larger than this threshold.
+ static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32;
+
+ // How many full blocks can be expected for a single explicit producer? This should
+ // reflect that number's maximum for optimal performance. Must be a power of 2.
+ static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32;
+
+ // How many full blocks can be expected for a single implicit producer? This should
+ // reflect that number's maximum for optimal performance. Must be a power of 2.
+ static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32;
+
+ // The initial size of the hash table mapping thread IDs to implicit producers.
+ // Note that the hash is resized every time it becomes half full.
+ // Must be a power of two, and either 0 or at least 1. If 0, implicit production
+ // (using the enqueue methods without an explicit producer token) is disabled.
+ static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32;
+
+ // Controls the number of items that an explicit consumer (i.e. one with a token)
+ // must consume before it causes all consumers to rotate and move on to the next
+ // internal queue.
+ static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256;
+
+ // The maximum number of elements (inclusive) that can be enqueued to a sub-queue.
+ // Enqueue operations that would cause this limit to be surpassed will fail. Note
+ // that this limit is enforced at the block level (for performance reasons), i.e.
+ // it's rounded up to the nearest block size.
+ static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max::value;
+
+ // The number of times to spin before sleeping when waiting on a semaphore.
+ // Recommended values are on the order of 1000-10000 unless the number of
+ // consumer threads exceeds the number of idle cores (in which case try 0-100).
+ // Only affects instances of the BlockingConcurrentQueue.
+ static const int MAX_SEMA_SPINS = 10000;
+
+
+#ifndef MCDBGQ_USE_RELACY
+ // Memory allocation can be customized if needed.
+ // malloc should return nullptr on failure, and handle alignment like std::malloc.
+#if defined(malloc) || defined(free)
+ // Gah, this is 2015, stop defining macros that break standard code already!
+ // Work around malloc/free being special macros:
+ static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); }
+ static inline void WORKAROUND_free(void* ptr) { return free(ptr); }
+ static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); }
+ static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); }
+#else
+ static inline void* malloc(size_t size) { return std::malloc(size); }
+ static inline void free(void* ptr) { return std::free(ptr); }
+#endif
+#else
+ // Debug versions when running under the Relacy race detector (ignore
+ // these in user code)
+ static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); }
+ static inline void free(void* ptr) { return rl::rl_free(ptr, $); }
+#endif
+};
+
+
+// When producing or consuming many elements, the most efficient way is to:
+// 1) Use one of the bulk-operation methods of the queue with a token
+// 2) Failing that, use the bulk-operation methods without a token
+// 3) Failing that, create a token and use that with the single-item methods
+// 4) Failing that, use the single-parameter methods of the queue
+// Having said that, don't create tokens willy-nilly -- ideally there should be
+// a maximum of one token per thread (of each kind).
+struct ProducerToken;
+struct ConsumerToken;
+
+template class ConcurrentQueue;
+template class BlockingConcurrentQueue;
+class ConcurrentQueueTests;
+
+
+namespace details
+{
+ struct ConcurrentQueueProducerTypelessBase
+ {
+ ConcurrentQueueProducerTypelessBase* next;
+ std::atomic inactive;
+ ProducerToken* token;
+
+ ConcurrentQueueProducerTypelessBase()
+ : next(nullptr), inactive(false), token(nullptr)
+ {
+ }
+ };
+
+ template struct _hash_32_or_64 {
+ static inline std::uint32_t hash(std::uint32_t h)
+ {
+ // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
+ // Since the thread ID is already unique, all we really want to do is propagate that
+ // uniqueness evenly across all the bits, so that we can use a subset of the bits while
+ // reducing collisions significantly
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ return h ^ (h >> 16);
+ }
+ };
+ template<> struct _hash_32_or_64<1> {
+ static inline std::uint64_t hash(std::uint64_t h)
+ {
+ h ^= h >> 33;
+ h *= 0xff51afd7ed558ccd;
+ h ^= h >> 33;
+ h *= 0xc4ceb9fe1a85ec53;
+ return h ^ (h >> 33);
+ }
+ };
+ template struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { };
+
+ static inline size_t hash_thread_id(thread_id_t id)
+ {
+ static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values");
+ return static_cast(hash_32_or_64::thread_id_hash_t)>::hash(
+ thread_id_converter::prehash(id)));
+ }
+
+ template
+ static inline bool circular_less_than(T a, T b)
+ {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4554)
+#endif
+ static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "circular_less_than is intended to be used only with unsigned integer types");
+ return static_cast(a - b) > static_cast(static_cast(1) << static_cast(sizeof(T) * CHAR_BIT - 1));
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ }
+
+ template
+ static inline char* align_for(char* ptr)
+ {
+ const std::size_t alignment = std::alignment_of::value;
+ return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment;
+ }
+
+ template
+ static inline T ceil_to_pow_2(T x)
+ {
+ static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types");
+
+ // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+ --x;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ for (std::size_t i = 1; i < sizeof(T); i <<= 1) {
+ x |= x >> (i << 3);
+ }
+ ++x;
+ return x;
+ }
+
+ template
+ static inline void swap_relaxed(std::atomic& left, std::atomic& right)
+ {
+ T temp = std::move(left.load(std::memory_order_relaxed));
+ left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed);
+ right.store(std::move(temp), std::memory_order_relaxed);
+ }
+
+ template
+ static inline T const& nomove(T const& x)
+ {
+ return x;
+ }
+
+ template
+ struct nomove_if
+ {
+ template
+ static inline T const& eval(T const& x)
+ {
+ return x;
+ }
+ };
+
+ template<>
+ struct nomove_if
+ {
+ template
+ static inline auto eval(U&& x)
+ -> decltype(std::forward(x))
+ {
+ return std::forward(x);
+ }
+ };
+
+ template
+ static inline auto deref_noexcept(It& it) MOODYCAMEL_NOEXCEPT -> decltype(*it)
+ {
+ return *it;
+ }
+
+#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+ template struct is_trivially_destructible : std::is_trivially_destructible { };
+#else
+ template struct is_trivially_destructible : std::has_trivial_destructor { };
+#endif
+
+#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
+#ifdef MCDBGQ_USE_RELACY
+ typedef RelacyThreadExitListener ThreadExitListener;
+ typedef RelacyThreadExitNotifier ThreadExitNotifier;
+#else
+ struct ThreadExitListener
+ {
+ typedef void (*callback_t)(void*);
+ callback_t callback;
+ void* userData;
+
+ ThreadExitListener* next; // reserved for use by the ThreadExitNotifier
+ };
+
+
+ class ThreadExitNotifier
+ {
+ public:
+ static void subscribe(ThreadExitListener* listener)
+ {
+ auto& tlsInst = instance();
+ listener->next = tlsInst.tail;
+ tlsInst.tail = listener;
+ }
+
+ static void unsubscribe(ThreadExitListener* listener)
+ {
+ auto& tlsInst = instance();
+ ThreadExitListener** prev = &tlsInst.tail;
+ for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) {
+ if (ptr == listener) {
+ *prev = ptr->next;
+ break;
+ }
+ prev = &ptr->next;
+ }
+ }
+
+ private:
+ ThreadExitNotifier() : tail(nullptr) { }
+ ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION;
+ ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION;
+
+ ~ThreadExitNotifier()
+ {
+ // This thread is about to exit, let everyone know!
+ assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined.");
+ for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) {
+ ptr->callback(ptr->userData);
+ }
+ }
+
+ // Thread-local
+ static inline ThreadExitNotifier& instance()
+ {
+ static thread_local ThreadExitNotifier notifier;
+ return notifier;
+ }
+
+ private:
+ ThreadExitListener* tail;
+ };
+#endif
+#endif
+
+ template struct static_is_lock_free_num { enum { value = 0 }; };
+ template<> struct static_is_lock_free_num { enum { value = ATOMIC_CHAR_LOCK_FREE }; };
+ template<> struct static_is_lock_free_num { enum { value = ATOMIC_SHORT_LOCK_FREE }; };
+ template<> struct static_is_lock_free_num { enum { value = ATOMIC_INT_LOCK_FREE }; };
+ template<> struct static_is_lock_free_num { enum { value = ATOMIC_LONG_LOCK_FREE }; };
+ template<> struct static_is_lock_free_num { enum { value = ATOMIC_LLONG_LOCK_FREE }; };
+ template struct static_is_lock_free : static_is_lock_free_num::type> { };
+ template<> struct static_is_lock_free { enum { value = ATOMIC_BOOL_LOCK_FREE }; };
+ template struct static_is_lock_free { enum { value = ATOMIC_POINTER_LOCK_FREE }; };
+}
+
+
+struct ProducerToken
+{
+ template
+ explicit ProducerToken(ConcurrentQueue& queue);
+
+ template
+ explicit ProducerToken(BlockingConcurrentQueue& queue);
+
+ ProducerToken(ProducerToken&& other) MOODYCAMEL_NOEXCEPT
+ : producer(other.producer)
+ {
+ other.producer = nullptr;
+ if (producer != nullptr) {
+ producer->token = this;
+ }
+ }
+
+ inline ProducerToken& operator=(ProducerToken&& other) MOODYCAMEL_NOEXCEPT
+ {
+ swap(other);
+ return *this;
+ }
+
+ void swap(ProducerToken& other) MOODYCAMEL_NOEXCEPT
+ {
+ std::swap(producer, other.producer);
+ if (producer != nullptr) {
+ producer->token = this;
+ }
+ if (other.producer != nullptr) {
+ other.producer->token = &other;
+ }
+ }
+
+ // A token is always valid unless:
+ // 1) Memory allocation failed during construction
+ // 2) It was moved via the move constructor
+ // (Note: assignment does a swap, leaving both potentially valid)
+ // 3) The associated queue was destroyed
+ // Note that if valid() returns true, that only indicates
+ // that the token is valid for use with a specific queue,
+ // but not which one; that's up to the user to track.
+ inline bool valid() const { return producer != nullptr; }
+
+ ~ProducerToken()
+ {
+ if (producer != nullptr) {
+ producer->token = nullptr;
+ producer->inactive.store(true, std::memory_order_release);
+ }
+ }
+
+ // Disable copying and assignment
+ ProducerToken(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION;
+ ProducerToken& operator=(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION;
+
+private:
+ template friend class ConcurrentQueue;
+ friend class ConcurrentQueueTests;
+
+protected:
+ details::ConcurrentQueueProducerTypelessBase* producer;
+};
+
+
+struct ConsumerToken
+{
+ template
+ explicit ConsumerToken(ConcurrentQueue& q);
+
+ template
+ explicit ConsumerToken(BlockingConcurrentQueue& q);
+
+ ConsumerToken(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT
+ : initialOffset(other.initialOffset), lastKnownGlobalOffset(other.lastKnownGlobalOffset), itemsConsumedFromCurrent(other.itemsConsumedFromCurrent), currentProducer(other.currentProducer), desiredProducer(other.desiredProducer)
+ {
+ }
+
+ inline ConsumerToken& operator=(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT
+ {
+ swap(other);
+ return *this;
+ }
+
+ void swap(ConsumerToken& other) MOODYCAMEL_NOEXCEPT
+ {
+ std::swap(initialOffset, other.initialOffset);
+ std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset);
+ std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent);
+ std::swap(currentProducer, other.currentProducer);
+ std::swap(desiredProducer, other.desiredProducer);
+ }
+
+ // Disable copying and assignment
+ ConsumerToken(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION;
+ ConsumerToken& operator=(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION;
+
+private:
+ template friend class ConcurrentQueue;
+ friend class ConcurrentQueueTests;
+
+private: // but shared with ConcurrentQueue
+ std::uint32_t initialOffset;
+ std::uint32_t lastKnownGlobalOffset;
+ std::uint32_t itemsConsumedFromCurrent;
+ details::ConcurrentQueueProducerTypelessBase* currentProducer;
+ details::ConcurrentQueueProducerTypelessBase* desiredProducer;
+};
+
+// Need to forward-declare this swap because it's in a namespace.
+// See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces
+template
+inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT;
+
+
+template
+class ConcurrentQueue
+{
+public:
+ typedef ::moodycamel::ProducerToken producer_token_t;
+ typedef ::moodycamel::ConsumerToken consumer_token_t;
+
+ typedef typename Traits::index_t index_t;
+ typedef typename Traits::size_t size_t;
+
+ static const size_t BLOCK_SIZE = static_cast(Traits::BLOCK_SIZE);
+ static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD);
+ static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::EXPLICIT_INITIAL_INDEX_SIZE);
+ static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::IMPLICIT_INITIAL_INDEX_SIZE);
+ static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE);
+ static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE);
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!)
+#pragma warning(disable: 4309) // static_cast: Truncation of constant value
+#endif
+ static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max::value - static_cast