Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f3d08a9d4 | |||
| f614151133 | |||
| 7e7d3e658e | |||
| 7b81d19103 | |||
| 9425e22ad0 | |||
| c8b39fe7dd |
+5
-3
@@ -11,11 +11,11 @@ BreakConstructorInitializersBeforeComma: true
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
@@ -29,3 +29,5 @@ BinPackParameters: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
IndentWrappedFunctionNames: false
|
||||
|
||||
#SpaceAfterTemplateKeyword: false
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*.{h,cpp,hpp}]
|
||||
charset = utf-8-bom
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
patreon: Nekotekina
|
||||
custom: https://rpcs3.net/alipay
|
||||
@@ -6,7 +6,7 @@ or our discord:
|
||||
https://discord.me/RPCS3
|
||||
|
||||
PLEASE READ THE GUIDELINES BEFORE OPENING AN ISSUE:
|
||||
https://github.com/RPCS3/rpcs3/blob/master/.github/CONTRIBUTING.md
|
||||
https://github.com/RPCS3/rpcs3/blob/master/CONTRIBUTING.md
|
||||
|
||||
====================================================
|
||||
|
||||
|
||||
+28
-27
@@ -31,6 +31,7 @@
|
||||
*.wav
|
||||
|
||||
/build
|
||||
/bin
|
||||
/lib
|
||||
/tmp
|
||||
/ipch
|
||||
@@ -40,31 +41,47 @@
|
||||
/Vulkan/Vulkan-build
|
||||
/Vulkan/glslang-build
|
||||
|
||||
!/bin
|
||||
/bin/*
|
||||
|
||||
# Themes
|
||||
!/bin/GuiConfigs/
|
||||
/bin/GuiConfigs/*.ini
|
||||
/bin/GuiConfigs/*.ini.*
|
||||
/bin/GuiConfigs/*.dat
|
||||
/bin/GuiConfigs/*.dat.*
|
||||
|
||||
/bin/rpcs3.ini
|
||||
/bin/rpcs3.ipdb
|
||||
/bin/rpcs3.iobj
|
||||
/bin/FragmentProgram.txt
|
||||
/bin/VertexProgram.txt
|
||||
/bin/*.hlsl
|
||||
/bin/BreakPoints.dat
|
||||
/bin/textures
|
||||
/bin/*.lib
|
||||
/bin/*.exp
|
||||
rpcs3/git-version.h
|
||||
|
||||
# Visual Studio Files
|
||||
.vs/*
|
||||
.vscode/*
|
||||
*.ipch
|
||||
*.vspx
|
||||
*.psess
|
||||
*.VC.*
|
||||
*.vcxproj.user
|
||||
enc_temp_folder/*
|
||||
CMakeSettings.json
|
||||
|
||||
# Copyrighted files
|
||||
/bin/data/
|
||||
/bin/dev_flash/data/font
|
||||
/bin/dev_flash/sys
|
||||
/bin/dev_flash/vsh
|
||||
|
||||
# Ignore installed games except test homebrews
|
||||
!/bin/dev_hdd0/game/
|
||||
/bin/dev_hdd0/game/*
|
||||
!/bin/dev_hdd0/game/TEST12345/
|
||||
/bin/dev_hdd0/disc/*
|
||||
|
||||
# Ignore other system generated files
|
||||
bin/dev_hdd0/*.txt
|
||||
x64/*
|
||||
rpcs3/x64/*
|
||||
rpcs3/git-version.h
|
||||
rpcs3-tests/x64/*
|
||||
|
||||
# cmake
|
||||
Makefile
|
||||
@@ -76,24 +93,8 @@ CMakeCache.txt
|
||||
rpcs3/cotire/*
|
||||
rpcs3/rpcs3_*_cotire.cmake
|
||||
|
||||
# kdevelop
|
||||
*.kdev4
|
||||
.kdev4/*
|
||||
|
||||
# Qt
|
||||
moc_*.cpp
|
||||
qrc_*.cpp
|
||||
qrc_resources.cpp
|
||||
rpcs3_automoc.cpp
|
||||
ui_*.h
|
||||
rpcs3/rpcs3_autogen/*
|
||||
|
||||
# QtCreator
|
||||
CMakeLists.txt.user
|
||||
|
||||
# CLion
|
||||
/.idea/*
|
||||
/cmake-build-*/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
|
||||
+18
-29
@@ -1,54 +1,43 @@
|
||||
[submodule "rpcs3-ffmpeg"]
|
||||
path = 3rdparty/ffmpeg
|
||||
url = https://github.com/hrydgard/ppsspp-ffmpeg
|
||||
ignore = dirty
|
||||
[submodule "asmjit"]
|
||||
path = asmjit
|
||||
url = https://github.com/kobalicek/asmjit
|
||||
ignore = dirty
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
url = https://github.com/RPCS3/llvm
|
||||
branch = release_60
|
||||
ignore = dirty
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
branch = release_50
|
||||
[submodule "GSL"]
|
||||
path = 3rdparty/GSL
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
[submodule "libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/Zangetsu38/libpng
|
||||
ignore = dirty
|
||||
[submodule "Vulkan/glslang"]
|
||||
path = Vulkan/glslang
|
||||
url = https://github.com/KhronosGroup/glslang.git
|
||||
ignore = dirty
|
||||
[submodule "Vulkan/Vulkan-LoaderAndValidationLayers"]
|
||||
path = Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
url = https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
|
||||
[submodule "Utilities/yaml-cpp"]
|
||||
path = Utilities/yaml-cpp
|
||||
url = https://github.com/RPCS3/yaml-cpp
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/RPCS3/pugixml
|
||||
[submodule "3rdparty/cereal"]
|
||||
path = 3rdparty/cereal
|
||||
url = https://github.com/USCiLab/cereal.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/zlib"]
|
||||
path = 3rdparty/zlib
|
||||
url = https://github.com/madler/zlib
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/hidapi"]
|
||||
path = 3rdparty/hidapi
|
||||
url = https://github.com/RPCS3/hidapi
|
||||
url = https://github.com/Zangetsu38/hidapi
|
||||
branch = master
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/zeux/pugixml
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/xxHash"]
|
||||
path = 3rdparty/xxHash
|
||||
url = https://github.com/Cyan4973/xxHash
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/yaml-cpp"]
|
||||
path = 3rdparty/yaml-cpp
|
||||
url = https://github.com/jbeder/yaml-cpp.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/glennrp/libpng.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/libusb"]
|
||||
path = 3rdparty/libusb
|
||||
url = https://github.com/RPCS3/libusb.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/Optional"]
|
||||
path = 3rdparty/Optional
|
||||
url = https://github.com/akrzemi1/Optional.git
|
||||
|
||||
+117
-30
@@ -1,35 +1,122 @@
|
||||
language: cpp
|
||||
sudo: required
|
||||
|
||||
os:
|
||||
- linux
|
||||
#- osx
|
||||
|
||||
# osx_image: xcode6.4
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/hombebrew_cache
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# - os: linux
|
||||
# env:
|
||||
# - NAME="Linux build"
|
||||
# - COMPILER="gcc"
|
||||
# services: docker
|
||||
# cache: ccache
|
||||
# install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
|
||||
# script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
|
||||
- os: linux
|
||||
env:
|
||||
- NAME="Linux build"
|
||||
- COMPILER="clang"
|
||||
- DEPLOY_APPIMAGE="true"
|
||||
services: docker
|
||||
cache: ccache
|
||||
install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
|
||||
script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
|
||||
- os: osx
|
||||
osx_image: xcode10.2
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- glew
|
||||
- ninja
|
||||
- qt
|
||||
script: "/bin/bash -ex .travis/build-mac.bash"
|
||||
cache: ccache
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
git:
|
||||
depth: false # Unshallow clone to obtain proper GIT_VERSION
|
||||
submodules: false
|
||||
|
||||
before_install:
|
||||
- if [ "$CC" = "clang" ]; then
|
||||
export CXX="clang++-4.0" CC="clang-4.0";
|
||||
fi;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
||||
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
||||
fi;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
wget http://download.qt.io/official_releases/online_installers/qt-unified-linux-x64-online.run ;
|
||||
chmod a+x ./qt-unified-linux-x64-online.run ;
|
||||
export QT_QPA_PLATFORM=minimal ;
|
||||
./qt-unified-linux-x64-online.run --script qt-installer-noninteractive.qs --no-force-installations ;
|
||||
fi;
|
||||
|
||||
# Install updated libglew-dev since the version provided by trusty is outdated
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew-dev_1.13.0-2_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew1.13_1.13.0-2_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||
sudo dpkg -i libglew1.13_1.13.0-2_amd64.deb libglew-dev_1.13.0-2_amd64.deb libvulkan1_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb libvulkan-dev_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||
else
|
||||
brew update;
|
||||
brew install ccache glew llvm40;
|
||||
fi;
|
||||
|
||||
before_script:
|
||||
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal 3rdparty/hidapi 3rdparty/Optional Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
- mkdir build
|
||||
- cd build
|
||||
- export CMAKE_PREFIX_PATH=~/Qt/5.9.1/gcc_64/lib/cmake
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH";
|
||||
else
|
||||
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH/#$TRAVIS_PULL_REQUEST";
|
||||
fi;
|
||||
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr;
|
||||
- make -j 3
|
||||
- # AppImage generation
|
||||
- if [ -n "$UPLOAD_URL" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang-4.0" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export LD_LIBRARY_PATH=~/Qt/5.9.1/gcc_64/lib;
|
||||
make DESTDIR=appdir install ; find appdir/ ;
|
||||
find ../bin ;
|
||||
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" ;
|
||||
chmod a+x linuxdeployqt*.AppImage ;
|
||||
export PATH=~/Qt/5.9.1/gcc_64/bin/:${PATH} ;
|
||||
./linuxdeployqt*.AppImage --appimage-extract ;
|
||||
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
|
||||
mkdir ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
mkdir ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/lib/libQt5Svg.so.5 ./appdir/usr/lib/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/plugins/xcbglintegrations/* ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/plugins/imageformats/* ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/plugins/platforms/* ./appdir/usr/plugins/platforms/ ;
|
||||
export PATH=${TRAVIS_BUILD_DIR}/build/squashfs-root/usr/bin/:${PATH} ;
|
||||
./squashfs-root/usr/bin/appimagetool ${TRAVIS_BUILD_DIR}/build/appdir ;
|
||||
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
|
||||
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}_linux64 --upload-file ./RPCS3*.AppImage;
|
||||
fi;
|
||||
|
||||
script:
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- sourceline: 'ppa:jonathonf/binutils' # We need to update binutils to a newer version to link against the ffmpeg libs on.
|
||||
packages:
|
||||
- binutils
|
||||
- cmake
|
||||
- libasound2-dev
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
#- libglew-dev
|
||||
#- libvulkan1
|
||||
#- libvulkan-dev
|
||||
- libc6-dev
|
||||
- llvm-4.0
|
||||
- llvm-4.0-dev
|
||||
- clang-4.0
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
# We need to install qt 5.9.1 manually because the version trusty provides is too old.
|
||||
#- qtbase5-dev
|
||||
- libudev-dev
|
||||
- libevdev-dev
|
||||
- libpulse-dev
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/env bash -ex
|
||||
shopt -s nocasematch
|
||||
|
||||
# Setup Qt variables
|
||||
export QT_BASE_DIR=/opt/qt${QTVERMIN}
|
||||
export PATH=$QT_BASE_DIR/bin:$PATH
|
||||
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib
|
||||
|
||||
cd rpcs3
|
||||
|
||||
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/xxHash 3rdparty/yaml-cpp 3rdparty/libusb Vulkan/glslang
|
||||
|
||||
# Download pre-compiled llvm libs
|
||||
curl -sLO https://github.com/RPCS3/llvm/releases/download/continuous-linux-master/llvmlibs-linux.tar.gz
|
||||
mkdir llvmlibs
|
||||
tar -xzf ./llvmlibs-linux.tar.gz -C llvmlibs
|
||||
|
||||
mkdir build ; cd build
|
||||
|
||||
if [ $COMPILER = "gcc" ]; then
|
||||
# These are set in the dockerfile
|
||||
export CC=${GCC_BINARY}
|
||||
export CXX=${GXX_BINARY}
|
||||
else
|
||||
export CC=${CLANG_BINARY}
|
||||
export CXX=${CLANGXX_BINARY}
|
||||
fi
|
||||
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_LLVM_SUBMODULE=OFF -DLLVM_DIR=llvmlibs/lib/cmake/llvm/ -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
|
||||
|
||||
ninja; build_status=$?;
|
||||
|
||||
cd ..
|
||||
# If it compiled succesfully let's deploy
|
||||
if [ $build_status -eq 0 ] && [ -n "$GITHUB_TOKEN" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then /bin/bash -ex .travis/deploy-linux.bash ; fi
|
||||
@@ -1,22 +0,0 @@
|
||||
export CCACHE_SLOPPINESS=pch_defines,time_macros
|
||||
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5/
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
# Setup vulkan and gfx-rs/portability
|
||||
curl -sLO https://github.com/gfx-rs/portability/releases/download/latest/gfx-portability-macos-latest.zip
|
||||
unzip -: gfx-portability-macos-latest.zip
|
||||
curl -sLO https://github.com/KhronosGroup/Vulkan-Headers/archive/sdk-1.1.106.0.zip
|
||||
unzip -: sdk-*.zip
|
||||
mkdir vulkan-sdk
|
||||
ln -s ${PWD}/Vulkan-Headers*/include vulkan-sdk/include
|
||||
mkdir vulkan-sdk/lib
|
||||
cp target/release/libportability.dylib vulkan-sdk/lib/libVulkan.dylib
|
||||
# Let macdeployqt locate and install Vulkan library
|
||||
install_name_tool -id ${PWD}/vulkan-sdk/lib/libVulkan.dylib vulkan-sdk/lib/libVulkan.dylib
|
||||
export VULKAN_SDK=${PWD}/vulkan-sdk
|
||||
|
||||
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/libusb 3rdparty/xxHash 3rdparty/yaml-cpp Vulkan/glslang
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DWITH_LLVM=OFF -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
|
||||
ninja
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/bin/env bash -ex
|
||||
shopt -s nocasematch
|
||||
cd build
|
||||
if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
DESTDIR=appdir ninja install
|
||||
curl -sLO "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
||||
chmod a+x linuxdeployqt*.AppImage
|
||||
./linuxdeployqt*.AppImage --appimage-extract
|
||||
./squashfs-root/AppRun ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs
|
||||
ls ./appdir/usr/lib/
|
||||
rm -r ./appdir/usr/share/doc
|
||||
rm ./appdir/usr/lib/libxcb*
|
||||
cp $(readlink -f /lib/x86_64-linux-gnu/libnsl.so.1) ./appdir/usr/lib/libnsl.so.1
|
||||
export PATH=/rpcs3/build/squashfs-root/usr/bin/:${PATH}
|
||||
|
||||
# Embed newer libstdc++ for distros that don't come with it (ubuntu 14.04, 16.04)
|
||||
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
|
||||
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ./appdir/usr/optional/libstdc++/
|
||||
rm ./appdir/AppRun
|
||||
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/AppRun-patched-x86_64 -o ./appdir/AppRun
|
||||
chmod a+x ./appdir/AppRun
|
||||
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/exec-x86_64.so -o ./appdir/usr/optional/exec.so
|
||||
|
||||
# Compile checker binary for AppImageKit-checkrt
|
||||
|
||||
# This may need updating if you update the compiler or rpcs3 uses newer c++ features
|
||||
# See https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/config/abi/pre/gnu.ver
|
||||
# for which definitions correlate to which CXXABI version.
|
||||
printf "#include <memory>\nint main(){std::make_exception_ptr(0);}" | $CXX -x c++ -o ./appdir/usr/optional/checker -
|
||||
|
||||
# Package it up and send it off
|
||||
./squashfs-root/usr/bin/appimagetool /rpcs3/build/appdir
|
||||
ls
|
||||
COMM_TAG="$(git describe --tags $(git rev-list --tags --max-count=1))"
|
||||
COMM_COUNT="$(git rev-list --count HEAD)"
|
||||
curl -sLO https://github.com/hcorion/uploadtool/raw/master/upload.sh
|
||||
|
||||
mv ./RPCS3*.AppImage rpcs3-${COMM_TAG}-${COMM_COUNT}-${TRAVIS_COMMIT:0:8}_linux64.AppImage
|
||||
|
||||
FILESIZEMB=$(bc <<< "scale=6; $(stat -c %s ./rpcs3*.AppImage)/1000000")
|
||||
SHA256SUM=($(sha256sum ./rpcs3*.AppImage))
|
||||
|
||||
unset TRAVIS_REPO_SLUG
|
||||
REPO_SLUG=RPCS3/rpcs3-binaries-linux \
|
||||
UPLOADTOOL_BODY="$SHA256SUM;${FILESIZEMB}MB"\
|
||||
RELEASE_NAME=build-${TRAVIS_COMMIT}\
|
||||
RELEASE_TITLE=${COMM_TAG}-${COMM_COUNT}\
|
||||
REPO_COMMIT=d812f1254a1157c80fd402f94446310560f54e5f\
|
||||
bash upload.sh rpcs3*.AppImage
|
||||
fi
|
||||
if [ "$DEPLOY_PPA" = "true" ]; then
|
||||
export DEBFULLNAME="RPCS3 Build Bot"
|
||||
fi
|
||||
@@ -1,9 +0,0 @@
|
||||
# Variables set by Travis CI
|
||||
TRAVIS_PULL_REQUEST
|
||||
TRAVIS_BRANCH
|
||||
TRAVIS_COMMIT
|
||||
# Variables for Travis build matrix
|
||||
COMPILER
|
||||
DEPLOY_APPIMAGE
|
||||
# Private Travis CI variables
|
||||
GITHUB_TOKEN
|
||||
Vendored
-378
@@ -1,378 +0,0 @@
|
||||
find_package(PkgConfig)
|
||||
include(ExternalProject)
|
||||
|
||||
if(APPLE)
|
||||
set(PLATFORM_ARCH "macosx/x86_64")
|
||||
elseif(WIN32)
|
||||
set(PLATFORM_ARCH "Windows/x86_64")
|
||||
else()
|
||||
set(PLATFORM_ARCH "linux/x86_64")
|
||||
endif()
|
||||
|
||||
# Dummy target to use when lib isn't available
|
||||
add_library(3rdparty_dummy_lib INTERFACE)
|
||||
|
||||
|
||||
# ZLib
|
||||
if (USE_SYSTEM_ZLIB)
|
||||
find_package(ZLIB QUIET)
|
||||
endif()
|
||||
|
||||
if (NOT ZLIB_FOUND)
|
||||
message(STATUS "Using builtin ZLIB")
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(zlib EXCLUDE_FROM_ALL)
|
||||
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib" "${CMAKE_CURRENT_BINARY_DIR}/zlib")
|
||||
set(ZLIB_LIBRARY zlibstatic)
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_zlib INTERFACE)
|
||||
target_link_libraries(3rdparty_zlib INTERFACE ${ZLIB_LIBRARY})
|
||||
target_include_directories(3rdparty_zlib INTERFACE ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
# libPNG
|
||||
# Select the version of libpng to use, default is builtin
|
||||
if (NOT USE_SYSTEM_LIBPNG)
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib" FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests" FORCE)
|
||||
set(PNG_BUILD_ZLIB ON CACHE BOOL "ZLIB is already build or package is found" FORCE)
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
||||
|
||||
set(LIBPNG_TARGET png_static)
|
||||
|
||||
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
|
||||
else()
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
add_library(3rdparty_system_libpng INTERFACE)
|
||||
target_include_directories(3rdparty_system_libpng INTERFACE ${PNG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_system_libpng INTERFACE ${PNG_LIBRARY})
|
||||
target_compile_definitions(3rdparty_system_libpng INTERFACE ${PNG_DEFINITIONS})
|
||||
|
||||
set(LIBPNG_TARGET 3rdparty_system_libpng)
|
||||
endif()
|
||||
|
||||
|
||||
# pugixml
|
||||
add_subdirectory(pugixml EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
# hidapi
|
||||
add_library(3rdparty_hidapi INTERFACE)
|
||||
target_include_directories(3rdparty_hidapi INTERFACE hidapi/hidapi)
|
||||
|
||||
if(APPLE)
|
||||
add_subdirectory(hidapi/mac EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-mac PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-mac "-framework CoreFoundation" "-framework IOKit")
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
add_subdirectory(hidapi/linux EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-hidraw PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hidraw udev)
|
||||
elseif(WIN32)
|
||||
add_subdirectory(hidapi/windows EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-hid PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hid Shlwapi.lib)
|
||||
else()
|
||||
add_subdirectory(hidapi/libusb EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-libusb PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-libusb usb)
|
||||
endif()
|
||||
|
||||
|
||||
# libusb
|
||||
if(CMAKE_SYSTEM MATCHES "DragonFly|FreeBSD")
|
||||
# Always use system libusb as reference implementation isn't supported
|
||||
add_library(usb-1.0-static INTERFACE)
|
||||
target_link_libraries(usb-1.0-static INTERFACE usb)
|
||||
elseif(MSVC)
|
||||
# Windows time.h defines timespec but doesn't add any flag for it, which makes libusb attempt to define it again
|
||||
add_definitions(-DHAVE_STRUCT_TIMESPEC=1)
|
||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
|
||||
# yaml-cpp
|
||||
# We don't want to install yaml-cpp but its cmake file doesn't have option
|
||||
# to disable it...
|
||||
# So we just install it to a different directory
|
||||
set(CMAKE_INSTALL_PREFIX_OLD ${CMAKE_INSTALL_PREFIX})
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/yaml-cpp_install)
|
||||
|
||||
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Enable testing" FORCE)
|
||||
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Enable parse tools" FORCE)
|
||||
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Enable contrib stuff in library" FORCE)
|
||||
add_subdirectory(yaml-cpp EXCLUDE_FROM_ALL)
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX_OLD})
|
||||
|
||||
|
||||
# xxHash
|
||||
set(XXHASH_BUNDLED_MODE ON)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Make xxHash build static libs")
|
||||
add_subdirectory(xxHash/cmake_unofficial EXCLUDE_FROM_ALL)
|
||||
target_include_directories(xxhash INTERFACE xxHash)
|
||||
|
||||
|
||||
# cereal
|
||||
add_library(3rdparty_cereal INTERFACE)
|
||||
target_include_directories(3rdparty_cereal INTERFACE cereal/include)
|
||||
|
||||
|
||||
# OpenGL
|
||||
|
||||
# Prefer GLVND for OpenGL rather than legacy
|
||||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_library(3rdparty_opengl INTERFACE)
|
||||
target_include_directories(3rdparty_opengl INTERFACE GL)
|
||||
|
||||
if (WIN32)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES} opengl32.lib glu32.lib)
|
||||
else()
|
||||
target_link_libraries(3rdparty_opengl INTERFACE dxgi.lib d2d1.lib dwrite.lib)
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES})
|
||||
|
||||
target_compile_definitions(3rdparty_opengl
|
||||
INTERFACE
|
||||
-DGL_GLEXT_PROTOTYPES
|
||||
-DGLX_GLXEXT_PROTOTYPES)
|
||||
endif()
|
||||
|
||||
|
||||
# GSL
|
||||
add_library(3rdparty_gsl INTERFACE)
|
||||
target_include_directories(3rdparty_gsl INTERFACE GSL/include)
|
||||
|
||||
|
||||
# stblib
|
||||
add_library(3rdparty_stblib INTERFACE)
|
||||
target_include_directories(3rdparty_stblib INTERFACE stblib)
|
||||
|
||||
|
||||
# discord_rpc
|
||||
add_library(3rdparty_discord-rpc INTERFACE)
|
||||
|
||||
# We don't want Discord Rich Presence on the BSDs and other OSes
|
||||
if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE))
|
||||
if (WIN32 AND NOT MSVC)
|
||||
ExternalProject_Add(discord-rpc
|
||||
GIT_REPOSITORY https://github.com/discordapp/discord-rpc
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
target_include_directories(3rdparty_discord-rpc INTERFACE discord-rpc/include)
|
||||
target_compile_definitions(3rdparty_discord-rpc INTERFACE -DWITH_DISCORD_RPC)
|
||||
|
||||
set(DISCORD_RPC_LIB NOTFOUND)
|
||||
if (WIN32)
|
||||
if (NOT MSVC)
|
||||
set(DISCORD_RPC_LIB ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc/src/libdiscord-rpc.a)
|
||||
else()
|
||||
find_library(DISCORD_RPC_LIB discord-rpc PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
find_library(DISCORD_RPC_LIB discord-rpc-linux PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
elseif(APPLE)
|
||||
find_library(DISCORD_RPC_LIB discord-rpc-mac PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
endif()
|
||||
target_link_libraries(3rdparty_discord-rpc INTERFACE ${DISCORD_RPC_LIB})
|
||||
if(APPLE)
|
||||
target_link_libraries(3rdparty_discord-rpc INTERFACE "objc" "-framework Foundation" "-framework CoreServices")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# ALSA
|
||||
set(ALSA_TARGET 3rdparty_dummy_lib)
|
||||
|
||||
if(USE_ALSA)
|
||||
find_package(ALSA)
|
||||
if(ALSA_FOUND)
|
||||
add_library(3rdparty_alsa INTERFACE)
|
||||
target_compile_definitions(3rdparty_alsa INTERFACE -DHAVE_ALSA)
|
||||
target_include_directories(3rdparty_alsa SYSTEM INTERFACE ${ALSA_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_alsa INTERFACE ${ALSA_LIBRARIES})
|
||||
|
||||
set(ALSA_TARGET 3rdparty_alsa)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Pulse
|
||||
set(PULSE_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_PULSE)
|
||||
pkg_check_modules(PULSE libpulse-simple)
|
||||
|
||||
if(PULSE_FOUND)
|
||||
add_library(3rdparty_pulse INTERFACE)
|
||||
target_compile_definitions(3rdparty_pulse INTERFACE -DHAVE_PULSE)
|
||||
target_include_directories(3rdparty_pulse SYSTEM
|
||||
INTERFACE ${PULSE_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_pulse INTERFACE ${PULSE_LDFLAGS})
|
||||
|
||||
set(PULSE_TARGET 3rdparty_pulse)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# libevdev
|
||||
set(LIBEVDEV_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_LIBEVDEV)
|
||||
pkg_check_modules(LIBEVDEV libevdev)
|
||||
if(LIBEVDEV_FOUND)
|
||||
add_library(3rdparty_libevdev INTERFACE)
|
||||
target_compile_definitions(3rdparty_libevdev INTERFACE -DHAVE_LIBEVDEV)
|
||||
target_include_directories(3rdparty_libevdev SYSTEM
|
||||
INTERFACE ${LIBEVDEV_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_libevdev INTERFACE ${LIBEVDEV_LDFLAGS})
|
||||
|
||||
set(LIBEVDEV_TARGET 3rdparty_libevdev)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Vulkan
|
||||
set(VULKAN_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_VULKAN)
|
||||
find_package(Vulkan)
|
||||
if(VULKAN_FOUND)
|
||||
add_library(3rdparty_vulkan INTERFACE)
|
||||
target_compile_definitions(3rdparty_vulkan INTERFACE -DHAVE_VULKAN)
|
||||
target_link_libraries(3rdparty_vulkan INTERFACE SPIRV Vulkan::Vulkan)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Wayland)
|
||||
if (WAYLAND_FOUND)
|
||||
target_include_directories(3rdparty_vulkan
|
||||
INTERFACE ${WAYLAND_INCLUDE_DIR})
|
||||
|
||||
target_compile_definitions(3rdparty_vulkan
|
||||
INTERFACE -DVK_USE_PLATFORM_WAYLAND_KHR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(VULKAN_TARGET 3rdparty_vulkan)
|
||||
else()
|
||||
message("WARNING! USE_VULKAN was enabled, but libvulkan was not found. RPCS3 will be compiled without Vulkan support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# OpenAL
|
||||
if (MSVC)
|
||||
find_path(OPENAL_INCLUDE_DIR al.h PATHS OpenAL/include)
|
||||
find_library(OPENAL_LIBRARY OpenAL32 PATHS OpenAL/libs/Win64/)
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_openal INTERFACE)
|
||||
target_include_directories(3rdparty_openal INTERFACE ${OPENAL_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_openal INTERFACE ${OPENAL_LIBRARY})
|
||||
|
||||
|
||||
# FFMPEG
|
||||
add_library(3rdparty_ffmpeg INTERFACE)
|
||||
|
||||
# Select the version of ffmpeg to use, default is builtin
|
||||
if(USE_SYSTEM_FFMPEG)
|
||||
message("-- RPCS3: using shared ffmpeg")
|
||||
find_package(FFMPEG REQUIRED)
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE ${FFMPEG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_ffmpeg INTERFACE ${FFMPEG_LIBRARIES})
|
||||
else()
|
||||
set(FFMPEG_PLATFORM_DIR "ffmpeg/${PLATFORM_ARCH}")
|
||||
|
||||
if (NOT MSVC AND WIN32)
|
||||
message("-- RPCS3: building ffmpeg submodule")
|
||||
|
||||
ExternalProject_Add(ffmpeg-mingw
|
||||
DOWNLOAD_COMMAND ""
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/ffmpeg
|
||||
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/configure --prefix=./Windows/x86_64 --arch=x86_64 --disable-avdevice --disable-programs --disable-avfilter --disable-postproc --disable-doc --disable-pthreads --enable-w32threads --disable-network --disable-everything --disable-encoders --disable-muxers --disable-hwaccels --disable-parsers --disable-protocols --enable-dxva2 --enable-static --disable-shared --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=atrac3 --enable-decoder=atrac3p --enable-decoder=mp3 --enable-decoder=pcm_s16le --enable-decoder=pcm_s8 --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mpeg2video --enable-decoder=mjpeg --enable-decoder=mjpegb --enable-encoder=pcm_s16le --enable-encoder=ffv1 --enable-encoder=mpeg4 --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=mpegvideo --enable-parser=mjpeg --enable-parser=aac --enable-parser=aac_latm --enable-muxer=avi --enable-demuxer=h264 --enable-demuxer=m4v --enable-demuxer=mp3 --enable-demuxer=mpegvideo --enable-demuxer=mpegps --enable-demuxer=mjpeg --enable-demuxer=avi --enable-demuxer=aac --enable-demuxer=pmp --enable-demuxer=oma --enable-demuxer=pcm_s16le --enable-demuxer=pcm_s8 --enable-demuxer=wav --enable-hwaccel=h264_dxva2 --enable-indev=dshow --enable-protocol=file
|
||||
BUILD_COMMAND make -j 4
|
||||
INSTALL_COMMAND make install
|
||||
)
|
||||
|
||||
set(FFMPEG_LIB_AVFORMAT "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavformat.a")
|
||||
set(FFMPEG_LIB_AVCODEC "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavcodec.a")
|
||||
set(FFMPEG_LIB_AVUTIL "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavutil.a")
|
||||
set(FFMPEG_LIB_SWSCALE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libswscale.a")
|
||||
else()
|
||||
message("-- RPCS3: using builtin ffmpeg")
|
||||
|
||||
set(FFMPEG_LIB_DIR "${FFMPEG_PLATFORM_DIR}/lib")
|
||||
find_library(FFMPEG_LIB_AVFORMAT avformat PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_AVCODEC avcodec PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_AVUTIL avutil PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_SWSCALE swscale PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE "${FFMPEG_PLATFORM_DIR}/include")
|
||||
|
||||
target_link_libraries(3rdparty_ffmpeg
|
||||
INTERFACE
|
||||
${FFMPEG_LIB_AVFORMAT}
|
||||
${FFMPEG_LIB_AVCODEC}
|
||||
${FFMPEG_LIB_AVUTIL}
|
||||
${FFMPEG_LIB_SWSCALE})
|
||||
endif()
|
||||
|
||||
|
||||
# dx12
|
||||
add_library(3rdparty_dx12 INTERFACE)
|
||||
if (WIN32)
|
||||
target_link_libraries(3rdparty_dx12 INTERFACE dxgi.lib d2d1.lib dwrite.lib)
|
||||
endif()
|
||||
|
||||
|
||||
# GLEW
|
||||
add_library(3rdparty_glew INTERFACE)
|
||||
if(NOT MSVC)
|
||||
find_package(GLEW 1.13.0 REQUIRED)
|
||||
target_link_libraries(3rdparty_glew INTERFACE GLEW::GLEW)
|
||||
endif()
|
||||
|
||||
|
||||
# LLVM
|
||||
include(llvm.cmake)
|
||||
|
||||
|
||||
# add nice ALIAS targets for ease of use
|
||||
add_library(3rdparty::libusb ALIAS usb-1.0-static)
|
||||
add_library(3rdparty::zlib ALIAS 3rdparty_zlib)
|
||||
add_library(3rdparty::pugixml ALIAS pugixml)
|
||||
add_library(3rdparty::yaml-cpp ALIAS yaml-cpp)
|
||||
add_library(3rdparty::xxhash ALIAS xxhash)
|
||||
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
|
||||
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
|
||||
add_library(3rdparty::cereal ALIAS 3rdparty_cereal)
|
||||
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
|
||||
add_library(3rdparty::gsl ALIAS 3rdparty_gsl)
|
||||
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
|
||||
add_library(3rdparty::discord-rpc ALIAS 3rdparty_discord-rpc)
|
||||
add_library(3rdparty::alsa ALIAS ${ALSA_TARGET})
|
||||
add_library(3rdparty::pulse ALIAS ${PULSE_TARGET})
|
||||
add_library(3rdparty::libevdev ALIAS ${LIBEVDEV_TARGET})
|
||||
add_library(3rdparty::vulkan ALIAS ${VULKAN_TARGET})
|
||||
add_library(3rdparty::openal ALIAS 3rdparty_openal)
|
||||
add_library(3rdparty::ffmpeg ALIAS 3rdparty_ffmpeg)
|
||||
add_library(3rdparty::dx12 ALIAS 3rdparty_dx12)
|
||||
add_library(3rdparty::glew ALIAS 3rdparty_glew)
|
||||
Vendored
+25
-699
File diff suppressed because it is too large
Load Diff
-16763
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
Submodule 3rdparty/GSL updated: 1995e86d1a...fc5fce4f4f
Vendored
+2
-118
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -97,31 +97,6 @@ extern "C" {
|
||||
|
||||
#ifndef AL_EXT_MCFORMATS
|
||||
#define AL_EXT_MCFORMATS 1
|
||||
/* Provides support for surround sound buffer formats with 8, 16, and 32-bit
|
||||
* samples.
|
||||
*
|
||||
* QUAD8: Unsigned 8-bit, Quadraphonic (Front Left, Front Right, Rear Left,
|
||||
* Rear Right).
|
||||
* QUAD16: Signed 16-bit, Quadraphonic.
|
||||
* QUAD32: 32-bit float, Quadraphonic.
|
||||
* REAR8: Unsigned 8-bit, Rear Stereo (Rear Left, Rear Right).
|
||||
* REAR16: Signed 16-bit, Rear Stereo.
|
||||
* REAR32: 32-bit float, Rear Stereo.
|
||||
* 51CHN8: Unsigned 8-bit, 5.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Side Left, Side Right). Note that some audio systems may label
|
||||
* 5.1's Side channels as Rear or Surround; they are equivalent for the
|
||||
* purposes of this extension.
|
||||
* 51CHN16: Signed 16-bit, 5.1 Surround.
|
||||
* 51CHN32: 32-bit float, 5.1 Surround.
|
||||
* 61CHN8: Unsigned 8-bit, 6.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Rear Center, Side Left, Side Right).
|
||||
* 61CHN16: Signed 16-bit, 6.1 Surround.
|
||||
* 61CHN32: 32-bit float, 6.1 Surround.
|
||||
* 71CHN8: Unsigned 8-bit, 7.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Rear Left, Rear Right, Side Left, Side Right).
|
||||
* 71CHN16: Signed 16-bit, 7.1 Surround.
|
||||
* 71CHN32: 32-bit float, 7.1 Surround.
|
||||
*/
|
||||
#define AL_FORMAT_QUAD8 0x1204
|
||||
#define AL_FORMAT_QUAD16 0x1205
|
||||
#define AL_FORMAT_QUAD32 0x1206
|
||||
@@ -418,97 +393,6 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_EXT_BFORMAT
|
||||
#define AL_EXT_BFORMAT 1
|
||||
/* Provides support for B-Format ambisonic buffers (first-order, FuMa scaling
|
||||
* and layout).
|
||||
*
|
||||
* BFORMAT2D_8: Unsigned 8-bit, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT2D_16: Signed 16-bit, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT2D_FLOAT32: 32-bit float, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT3D_8: Unsigned 8-bit, 4-channel periphonic (WXYZ).
|
||||
* BFORMAT3D_16: Signed 16-bit, 4-channel periphonic (WXYZ).
|
||||
* BFORMAT3D_FLOAT32: 32-bit float, 4-channel periphonic (WXYZ).
|
||||
*/
|
||||
#define AL_FORMAT_BFORMAT2D_8 0x20021
|
||||
#define AL_FORMAT_BFORMAT2D_16 0x20022
|
||||
#define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023
|
||||
#define AL_FORMAT_BFORMAT3D_8 0x20031
|
||||
#define AL_FORMAT_BFORMAT3D_16 0x20032
|
||||
#define AL_FORMAT_BFORMAT3D_FLOAT32 0x20033
|
||||
#endif
|
||||
|
||||
#ifndef AL_EXT_MULAW_BFORMAT
|
||||
#define AL_EXT_MULAW_BFORMAT 1
|
||||
#define AL_FORMAT_BFORMAT2D_MULAW 0x10031
|
||||
#define AL_FORMAT_BFORMAT3D_MULAW 0x10032
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_HRTF
|
||||
#define ALC_SOFT_HRTF 1
|
||||
#define ALC_HRTF_SOFT 0x1992
|
||||
#define ALC_DONT_CARE_SOFT 0x0002
|
||||
#define ALC_HRTF_STATUS_SOFT 0x1993
|
||||
#define ALC_HRTF_DISABLED_SOFT 0x0000
|
||||
#define ALC_HRTF_ENABLED_SOFT 0x0001
|
||||
#define ALC_HRTF_DENIED_SOFT 0x0002
|
||||
#define ALC_HRTF_REQUIRED_SOFT 0x0003
|
||||
#define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004
|
||||
#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005
|
||||
#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994
|
||||
#define ALC_HRTF_SPECIFIER_SOFT 0x1995
|
||||
#define ALC_HRTF_ID_SOFT 0x1996
|
||||
typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
|
||||
typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
|
||||
ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_gain_clamp_ex
|
||||
#define AL_SOFT_gain_clamp_ex 1
|
||||
#define AL_GAIN_LIMIT_SOFT 0x200E
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_source_resampler
|
||||
#define AL_SOFT_source_resampler
|
||||
#define AL_NUM_RESAMPLERS_SOFT 0x1210
|
||||
#define AL_DEFAULT_RESAMPLER_SOFT 0x1211
|
||||
#define AL_SOURCE_RESAMPLER_SOFT 0x1212
|
||||
#define AL_RESAMPLER_NAME_SOFT 0x1213
|
||||
typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_source_spatialize
|
||||
#define AL_SOFT_source_spatialize
|
||||
#define AL_SOURCE_SPATIALIZE_SOFT 0x1214
|
||||
#define AL_AUTO_SOFT 0x0002
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_output_limiter
|
||||
#define ALC_SOFT_output_limiter
|
||||
#define ALC_OUTPUT_LIMITER_SOFT 0x199A
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_device_clock
|
||||
#define ALC_SOFT_device_clock 1
|
||||
typedef int64_t ALCint64SOFT;
|
||||
typedef uint64_t ALCuint64SOFT;
|
||||
#define ALC_DEVICE_CLOCK_SOFT 0x1600
|
||||
#define ALC_DEVICE_LATENCY_SOFT 0x1601
|
||||
#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602
|
||||
#define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202
|
||||
#define AL_SEC_OFFSET_CLOCK_SOFT 0x1203
|
||||
typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -345,7 +345,7 @@ typedef struct {
|
||||
/* Driving Presets */
|
||||
|
||||
#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \
|
||||
{ 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
|
||||
{ 1.0000f, 0.0000f, 3.1623f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
|
||||
|
||||
#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \
|
||||
{ 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
|
||||
|
||||
+91
@@ -1,35 +1,84 @@
|
||||
EXPORTS
|
||||
alAuxiliaryEffectSlotf
|
||||
alAuxiliaryEffectSlotfv
|
||||
alAuxiliaryEffectSloti
|
||||
alAuxiliaryEffectSlotiv
|
||||
alBuffer3f
|
||||
alBuffer3i
|
||||
alBufferData
|
||||
alBufferSamplesSOFT
|
||||
alBufferSubDataSOFT
|
||||
alBufferSubSamplesSOFT
|
||||
alBufferf
|
||||
alBufferfv
|
||||
alBufferi
|
||||
alBufferiv
|
||||
alDeferUpdatesSOFT
|
||||
alDeleteAuxiliaryEffectSlots
|
||||
alDeleteBuffers
|
||||
alDeleteEffects
|
||||
alDeleteFilters
|
||||
alDeleteFontsoundsSOFT
|
||||
alDeletePresetsSOFT
|
||||
alDeleteSoundfontsSOFT
|
||||
alDeleteSources
|
||||
alDisable
|
||||
alDistanceModel
|
||||
alDopplerFactor
|
||||
alDopplerVelocity
|
||||
alEffectf
|
||||
alEffectfv
|
||||
alEffecti
|
||||
alEffectiv
|
||||
alEnable
|
||||
alFilterf
|
||||
alFilterfv
|
||||
alFilteri
|
||||
alFilteriv
|
||||
alFontsound2iSOFT
|
||||
alFontsoundModulatoriSOFT
|
||||
alFontsoundiSOFT
|
||||
alFontsoundivSOFT
|
||||
alGenAuxiliaryEffectSlots
|
||||
alGenBuffers
|
||||
alGenEffects
|
||||
alGenFilters
|
||||
alGenFontsoundsSOFT
|
||||
alGenPresetsSOFT
|
||||
alGenSoundfontsSOFT
|
||||
alGenSources
|
||||
alGetAuxiliaryEffectSlotf
|
||||
alGetAuxiliaryEffectSlotfv
|
||||
alGetAuxiliaryEffectSloti
|
||||
alGetAuxiliaryEffectSlotiv
|
||||
alGetBoolean
|
||||
alGetBooleanv
|
||||
alGetBuffer3f
|
||||
alGetBuffer3i
|
||||
alGetBufferSamplesSOFT
|
||||
alGetBufferf
|
||||
alGetBufferfv
|
||||
alGetBufferi
|
||||
alGetBufferiv
|
||||
alGetDouble
|
||||
alGetDoublev
|
||||
alGetEffectf
|
||||
alGetEffectfv
|
||||
alGetEffecti
|
||||
alGetEffectiv
|
||||
alGetEnumValue
|
||||
alGetError
|
||||
alGetFilterf
|
||||
alGetFilterfv
|
||||
alGetFilteri
|
||||
alGetFilteriv
|
||||
alGetFloat
|
||||
alGetFloatv
|
||||
alGetFontsoundModulatorivSOFT
|
||||
alGetFontsoundivSOFT
|
||||
alGetInteger
|
||||
alGetInteger64SOFT
|
||||
alGetInteger64vSOFT
|
||||
alGetIntegerv
|
||||
alGetListener3f
|
||||
alGetListener3i
|
||||
@@ -37,17 +86,32 @@ EXPORTS
|
||||
alGetListenerfv
|
||||
alGetListeneri
|
||||
alGetListeneriv
|
||||
alGetPresetivSOFT
|
||||
alGetProcAddress
|
||||
alGetSoundfontivSOFT
|
||||
alGetSource3dSOFT
|
||||
alGetSource3f
|
||||
alGetSource3i
|
||||
alGetSource3i64SOFT
|
||||
alGetSourcedSOFT
|
||||
alGetSourcedvSOFT
|
||||
alGetSourcef
|
||||
alGetSourcefv
|
||||
alGetSourcei
|
||||
alGetSourcei64SOFT
|
||||
alGetSourcei64vSOFT
|
||||
alGetSourceiv
|
||||
alGetString
|
||||
alIsAuxiliaryEffectSlot
|
||||
alIsBuffer
|
||||
alIsBufferFormatSupportedSOFT
|
||||
alIsEffect
|
||||
alIsEnabled
|
||||
alIsExtensionPresent
|
||||
alIsFilter
|
||||
alIsFontsoundSOFT
|
||||
alIsPresetSOFT
|
||||
alIsSoundfontSOFT
|
||||
alIsSource
|
||||
alListener3f
|
||||
alListener3i
|
||||
@@ -55,8 +119,25 @@ EXPORTS
|
||||
alListenerfv
|
||||
alListeneri
|
||||
alListeneriv
|
||||
alLoadSoundfontSOFT
|
||||
alMidiEventSOFT
|
||||
alMidiGainSOFT
|
||||
alMidiPauseSOFT
|
||||
alMidiPlaySOFT
|
||||
alMidiResetSOFT
|
||||
alMidiSoundfontSOFT
|
||||
alMidiSoundfontvSOFT
|
||||
alMidiStopSOFT
|
||||
alMidiSysExSOFT
|
||||
alPresetFontsoundsSOFT
|
||||
alPresetiSOFT
|
||||
alPresetivSOFT
|
||||
alProcessUpdatesSOFT
|
||||
alSoundfontPresetsSOFT
|
||||
alSource3dSOFT
|
||||
alSource3f
|
||||
alSource3i
|
||||
alSource3i64SOFT
|
||||
alSourcePause
|
||||
alSourcePausev
|
||||
alSourcePlay
|
||||
@@ -67,9 +148,13 @@ EXPORTS
|
||||
alSourceStop
|
||||
alSourceStopv
|
||||
alSourceUnqueueBuffers
|
||||
alSourcedSOFT
|
||||
alSourcedvSOFT
|
||||
alSourcef
|
||||
alSourcefv
|
||||
alSourcei
|
||||
alSourcei64SOFT
|
||||
alSourcei64vSOFT
|
||||
alSourceiv
|
||||
alSpeedOfSound
|
||||
alcCaptureCloseDevice
|
||||
@@ -80,17 +165,23 @@ EXPORTS
|
||||
alcCloseDevice
|
||||
alcCreateContext
|
||||
alcDestroyContext
|
||||
alcDevicePauseSOFT
|
||||
alcDeviceResumeSOFT
|
||||
alcGetContextsDevice
|
||||
alcGetCurrentContext
|
||||
alcGetEnumValue
|
||||
alcGetError
|
||||
alcGetInteger64vSOFT
|
||||
alcGetIntegerv
|
||||
alcGetProcAddress
|
||||
alcGetString
|
||||
alcGetThreadContext
|
||||
alcIsExtensionPresent
|
||||
alcIsRenderFormatSupportedSOFT
|
||||
alcLoopbackOpenDeviceSOFT
|
||||
alcMakeContextCurrent
|
||||
alcOpenDevice
|
||||
alcProcessContext
|
||||
alcRenderSamplesSOFT
|
||||
alcSetThreadContext
|
||||
alcSuspendContext
|
||||
|
||||
BIN
Binary file not shown.
+1
Submodule 3rdparty/Optional added at f27e79084a
-26
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(DISCORD_DYNAMIC_LIB)
|
||||
# if defined(_WIN32)
|
||||
# if defined(DISCORD_BUILDING_SDK)
|
||||
# define DISCORD_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define DISCORD_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define DISCORD_EXPORT __attribute__((visibility("default")))
|
||||
# endif
|
||||
#else
|
||||
# define DISCORD_EXPORT
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
|
||||
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// clang-format off
|
||||
|
||||
#if defined(DISCORD_DYNAMIC_LIB)
|
||||
# if defined(_WIN32)
|
||||
# if defined(DISCORD_BUILDING_SDK)
|
||||
# define DISCORD_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define DISCORD_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define DISCORD_EXPORT __attribute__((visibility("default")))
|
||||
# endif
|
||||
#else
|
||||
# define DISCORD_EXPORT
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct DiscordRichPresence {
|
||||
const char* state; /* max 128 bytes */
|
||||
const char* details; /* max 128 bytes */
|
||||
int64_t startTimestamp;
|
||||
int64_t endTimestamp;
|
||||
const char* largeImageKey; /* max 32 bytes */
|
||||
const char* largeImageText; /* max 128 bytes */
|
||||
const char* smallImageKey; /* max 32 bytes */
|
||||
const char* smallImageText; /* max 128 bytes */
|
||||
const char* partyId; /* max 128 bytes */
|
||||
int partySize;
|
||||
int partyMax;
|
||||
const char* matchSecret; /* max 128 bytes */
|
||||
const char* joinSecret; /* max 128 bytes */
|
||||
const char* spectateSecret; /* max 128 bytes */
|
||||
int8_t instance;
|
||||
} DiscordRichPresence;
|
||||
|
||||
typedef struct DiscordJoinRequest {
|
||||
const char* userId;
|
||||
const char* username;
|
||||
const char* discriminator;
|
||||
const char* avatar;
|
||||
} DiscordJoinRequest;
|
||||
|
||||
typedef struct DiscordEventHandlers {
|
||||
void (*ready)(void);
|
||||
void (*disconnected)(int errorCode, const char* message);
|
||||
void (*errored)(int errorCode, const char* message);
|
||||
void (*joinGame)(const char* joinSecret);
|
||||
void (*spectateGame)(const char* spectateSecret);
|
||||
void (*joinRequest)(const DiscordJoinRequest* request);
|
||||
} DiscordEventHandlers;
|
||||
|
||||
#define DISCORD_REPLY_NO 0
|
||||
#define DISCORD_REPLY_YES 1
|
||||
#define DISCORD_REPLY_IGNORE 2
|
||||
|
||||
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||
DiscordEventHandlers* handlers,
|
||||
int autoRegister,
|
||||
const char* optionalSteamId);
|
||||
DISCORD_EXPORT void Discord_Shutdown(void);
|
||||
|
||||
/* checks for incoming messages, dispatches callbacks */
|
||||
DISCORD_EXPORT void Discord_RunCallbacks(void);
|
||||
|
||||
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
|
||||
#ifdef DISCORD_DISABLE_IO_THREAD
|
||||
DISCORD_EXPORT void Discord_UpdateConnection(void);
|
||||
#endif
|
||||
|
||||
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||
DISCORD_EXPORT void Discord_ClearPresence(void);
|
||||
|
||||
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
|
||||
|
||||
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
+1
-1
Submodule 3rdparty/hidapi updated: 9220f5e77c...4c2750112e
Vendored
-105
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A107C21C-418A-4697-BB10-20C3AA60E2E4}</ProjectGuid>
|
||||
<RootNamespace>hidapi</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>15.0.26323.1</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="hidapi\windows\hid.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hidapi\hidapi\hidapi.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-27
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="hidapi\windows\hid.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hidapi\hidapi\hidapi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Vendored
+1
-1
Submodule 3rdparty/libpng updated: eddf902320...bbbcaf1122
Vendored
-142
@@ -1,142 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug Library|x64">
|
||||
<Configuration>Debug Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release Library|x64">
|
||||
<Configuration>Release Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{D6973076-9317-4EF2-A0B8-B7A18AC0713E}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>libpng</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<FloatingPointExceptions>false</FloatingPointExceptions>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FloatingPointExceptions>false</FloatingPointExceptions>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<Optimization>Full</Optimization>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include=".\libpng\png.c">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include=".\libpng\pngerror.c" />
|
||||
<ClCompile Include=".\libpng\pngget.c" />
|
||||
<ClCompile Include=".\libpng\pngmem.c" />
|
||||
<ClCompile Include=".\libpng\pngpread.c" />
|
||||
<ClCompile Include=".\libpng\pngread.c" />
|
||||
<ClCompile Include=".\libpng\pngrio.c" />
|
||||
<ClCompile Include=".\libpng\pngrtran.c" />
|
||||
<ClCompile Include=".\libpng\pngrutil.c" />
|
||||
<ClCompile Include=".\libpng\pngset.c" />
|
||||
<ClCompile Include=".\libpng\pngtrans.c" />
|
||||
<ClCompile Include=".\libpng\pngwio.c" />
|
||||
<ClCompile Include=".\libpng\pngwrite.c" />
|
||||
<ClCompile Include=".\libpng\pngwtran.c" />
|
||||
<ClCompile Include=".\libpng\pngwutil.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include=".\libpng\scripts\pngwin.rc">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">true</ExcludedFromBuild>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-1
Submodule 3rdparty/libusb deleted from 7cfa00e9d7
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>libusb-1.0 (static)</ProjectName>
|
||||
<ProjectGuid>{349EE8F9-7D25-4909-AAF5-FF3FADE72187}</ProjectGuid>
|
||||
<RootNamespace>libusb</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<WholeProgramOptimization Condition="'$(Configuration)'=='Release'">true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
<TargetName>libusb-1.0</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>libusb\msvc;libusb\libusb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WINVER=0x0501;_WIN32_WINNT=0x0501;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(Configuration)'=='Debug'">
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(Configuration)'=='Release'">
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)libusb-1.0.lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="libusb\libusb\core.c" />
|
||||
<ClCompile Include="libusb\libusb\descriptor.c" />
|
||||
<ClCompile Include="libusb\libusb\hotplug.c" />
|
||||
<ClCompile Include="libusb\libusb\io.c" />
|
||||
<ClCompile Include="libusb\libusb\os\poll_windows.c" />
|
||||
<ClCompile Include="libusb\libusb\strerror.c" />
|
||||
<ClCompile Include="libusb\libusb\sync.c" />
|
||||
<ClCompile Include="libusb\libusb\os\threads_windows.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_nt_common.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_usbdk.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_winusb.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="libusb\msvc\config.h" />
|
||||
<ClInclude Include="libusb\libusb\hotplug.h" />
|
||||
<ClInclude Include="libusb\libusb\libusb.h" />
|
||||
<ClInclude Include="libusb\libusb\libusbi.h" />
|
||||
<ClInclude Include="libusb\libusb\os\poll_windows.h" />
|
||||
<ClInclude Include="libusb\libusb\os\threads_windows.h" />
|
||||
<ClInclude Include="libusb\libusb\version.h" />
|
||||
<ClInclude Include="libusb\libusb\version_nano.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_common.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_nt_common.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_nt_shared_types.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_usbdk.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_winusb.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-65
@@ -1,65 +0,0 @@
|
||||
if(WITH_LLVM)
|
||||
if(BUILD_LLVM_SUBMODULE)
|
||||
message(STATUS "LLVM will be built from the submodule.")
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD "X86" CACHE INTERNAL "")
|
||||
option(LLVM_BUILD_RUNTIME OFF)
|
||||
option(LLVM_BUILD_TOOLS OFF)
|
||||
option(LLVM_INCLUDE_DOCS OFF)
|
||||
option(LLVM_INCLUDE_EXAMPLES OFF)
|
||||
option(LLVM_INCLUDE_TESTS OFF)
|
||||
option(LLVM_INCLUDE_TOOLS OFF)
|
||||
option(LLVM_INCLUDE_UTILS OFF)
|
||||
option(WITH_POLLY OFF)
|
||||
option(LLVM_ENABLE_CXX1Z TRUE)
|
||||
|
||||
set(CXX_FLAGS_OLD ${CMAKE_CXX_FLAGS})
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS")
|
||||
endif()
|
||||
|
||||
# LLVM needs to be built out-of-tree
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/llvm ${CMAKE_CURRENT_BINARY_DIR}/llvm_build EXCLUDE_FROM_ALL)
|
||||
set(LLVM_DIR "${CMAKE_CURRENT_BINARY_DIR}/llvm_build/lib/cmake/llvm/")
|
||||
|
||||
set(CMAKE_CXX_FLAGS ${CXX_FLAGS_OLD})
|
||||
|
||||
# now tries to find LLVM again
|
||||
find_package(LLVM 9.0 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
message(FATAL_ERROR "Couldn't build LLVM from the submodule. You might need to run `git submodule update --init`")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Using prebuilt LLVM")
|
||||
|
||||
if (LLVM_DIR AND NOT IS_ABSOLUTE "${LLVM_DIR}")
|
||||
# change relative LLVM_DIR to be relative to the source dir
|
||||
set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR})
|
||||
endif()
|
||||
|
||||
find_package(LLVM 9.0 CONFIG)
|
||||
|
||||
if (NOT LLVM_FOUND)
|
||||
if (LLVM_VERSION AND LLVM_VERSION_MAJOR LESS 9)
|
||||
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 9.0. \
|
||||
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
|
||||
message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \
|
||||
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(LLVM_LIBS LLVMMCJIT LLVMX86CodeGen LLVMX86AsmParser)
|
||||
|
||||
add_library(3rdparty_llvm INTERFACE)
|
||||
target_link_libraries(3rdparty_llvm INTERFACE ${LLVM_LIBS})
|
||||
target_include_directories(3rdparty_llvm INTERFACE ${LLVM_INCLUDE_DIRS})
|
||||
target_compile_definitions(3rdparty_llvm INTERFACE ${LLVM_DEFINITIONS} -DLLVM_AVAILABLE)
|
||||
|
||||
add_library(3rdparty::llvm ALIAS 3rdparty_llvm)
|
||||
else()
|
||||
add_library(3rdparty::llvm ALIAS 3rdparty_dummy_lib)
|
||||
endif()
|
||||
Vendored
-64
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{EB33566E-DA7F-4D28-9077-88C0B7C77E35}</ProjectGuid>
|
||||
<RootNamespace>pnglibconf</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<CustomBuildBeforeTargets>Build</CustomBuildBeforeTargets>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<CustomBuildStep>
|
||||
<Command>copy .\libpng\scripts\pnglibconf.h.prebuilt .\libpng\pnglibconf.h</Command>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Message>Generating pnglibconf.h</Message>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Outputs>.\libpng\pnglibconf.h</Outputs>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Inputs>.\libpng\scripts\pnglibconf.h.prebuilt</Inputs>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
+1
-1
Submodule 3rdparty/pugixml updated: 8bf806c035...f205aaf6e1
Vendored
-49
@@ -1,49 +0,0 @@
|
||||
add_library(3rdparty_qt5 INTERFACE)
|
||||
|
||||
find_package(Qt5 5.10 CONFIG COMPONENTS Widgets Network Qml)
|
||||
if(WIN32)
|
||||
find_package(Qt5 5.10 COMPONENTS WinExtras REQUIRED)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Network Qt5::Qml)
|
||||
else()
|
||||
find_package(Qt5 5.10 COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Qml)
|
||||
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
|
||||
else()
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Network Qt5::Qml)
|
||||
endif()
|
||||
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(NOT Qt5Widgets_FOUND)
|
||||
if(Qt5Widgets_VERSION VERSION_LESS 5.10.0)
|
||||
message("Minimum supported Qt5 version is 5.10.0! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
|
||||
if(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt.
|
||||
If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt5 versions.
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-bionic
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-xenial
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.10.1-trusty
|
||||
just make sure to run
|
||||
source /opt/qt511/bin/qt511-env.sh
|
||||
respective
|
||||
source /opt/qt510/bin/qt510-env.sh
|
||||
before re-running cmake")
|
||||
elseif(WIN32)
|
||||
message(FATAL_ERROR "You can download the latest version of Qt5 here: https://www.qt.io/download-open-source/")
|
||||
else()
|
||||
message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt5 on ${CMAKE_SYSTEM}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message("CMake was unable to find Qt5!")
|
||||
if(WIN32)
|
||||
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\5.11.1\\msvc2017_64\\)")
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Make sure to install your distro's qt5 package!")
|
||||
else()
|
||||
message(FATAL_ERROR "You need to have Qt5 installed, look online for instructions on installing Qt5 on ${CMAKE_SYSTEM}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(3rdparty::qt5 ALIAS 3rdparty_qt5)
|
||||
Vendored
+4672
-5521
File diff suppressed because it is too large
Load Diff
Vendored
+2316
-3897
File diff suppressed because it is too large
Load Diff
Vendored
-1
Submodule 3rdparty/xxHash deleted from 7cc9639699
Vendored
-59
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{939FE206-1182-ABC3-1234-FEAB88E98404}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="xxHash/xxhash.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="LICENSE" />
|
||||
<Text Include="CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="xxHash/xxhash.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-1
Submodule 3rdparty/yaml-cpp deleted from eca9cfd648
Vendored
-105
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(ZLibSrcDir)\adler32.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\compress.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\crc32.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\deflate.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\infback.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inffast.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inflate.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inftrees.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\trees.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\uncompr.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\zutil.c" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{60F89955-91C6-3A36-8000-13C592FEC2DF}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>zlib</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Full</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
# Building
|
||||
|
||||
Only Windows and Linux are officially supported for building. However, various other platforms are capable of building RPCS3.
|
||||
Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Building).
|
||||
|
||||
## Setup your environment
|
||||
|
||||
### Windows 7 or later
|
||||
|
||||
* [CMake 3.8.2+](https://www.cmake.org/download/) (add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (add to PATH)
|
||||
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
|
||||
* [Visual Studio 2017](https://visualstudio.microsoft.com/vs/older-downloads/)
|
||||
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html))
|
||||
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\5.10.0\msvc2017_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools-19123)**
|
||||
|
||||
### Linux
|
||||
|
||||
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager.
|
||||
|
||||
* Clang 5.0+ or GCC 8.1+
|
||||
* [CMake 3.8.2+](https://www.cmake.org/download/)
|
||||
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
|
||||
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
|
||||
|
||||
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative
|
||||
|
||||
#### Debian & Ubuntu
|
||||
|
||||
sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default libevdev-dev qtdeclarative5-dev qtbase5-private-dev
|
||||
|
||||
##### GCC 8.x installation
|
||||
|
||||
If the `gcc-8` package is not available on your system, use the following command
|
||||
```
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
```
|
||||
Then
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-8 g++-8
|
||||
```
|
||||
|
||||
You can either use `update-alternatives` to setup `gcc-8`/`g++-8` as your default compilers or prefix any `cmake` command by `CXX=g++-8 CC=gcc-8 ` to use it.
|
||||
|
||||
##### Vulkan SDK
|
||||
|
||||
For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](https://packages.lunarg.com/) which will provide a compatible Vulkan SDK to compile RPCS3. If your Vulkan SDK is older, it can lead to compilation errors.
|
||||
|
||||
Ubuntu 18.04 (Bionic Beaver)
|
||||
```
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-bionic.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-bionic.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
```
|
||||
|
||||
Ubuntu 16.04 (Xenial Xerus)
|
||||
```
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-xenial.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-xenial.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
|
||||
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-devel vulkan-devel
|
||||
|
||||
#### OpenSUSE
|
||||
|
||||
sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libevdev-devel
|
||||
|
||||
## Setup the project
|
||||
|
||||
Clone and initialize the repository
|
||||
|
||||
```
|
||||
git clone https://github.com/RPCS3/rpcs3.git
|
||||
cd rpcs3
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
#### Configuring the Qt plugin (if used)
|
||||
|
||||
1) Go to the Qt5 menu and edit Qt5 options.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.10.0\msvc2017_64`.
|
||||
3) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
#### Building the projects
|
||||
|
||||
Open `rpcs3.sln`. The recommended build configuration is `Release - LLVM` for all purposes.
|
||||
|
||||
You may want to download precompiled [LLVM libs](https://github.com/RPCS3/llvm/releases/download/continuous-master/llvmlibs.7z) and extract to root rpcs3 folder (which contains `rpcs3.sln`), as well as download and extract [additional libs](https://drive.google.com/uc?export=download&id=1A2eOMmCO714i0U7J0qI4aEMKnuWl8l_R) to `lib\%CONFIGURATION%-x64\` to speed up compilation time (unoptimised/debug libs are currently not available precompiled).
|
||||
|
||||
If you're not using precompiled libs, build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*.
|
||||
|
||||
`Build > Build Solution`
|
||||
|
||||
### Linux
|
||||
|
||||
While still in the project root:
|
||||
|
||||
1) `cd .. && mkdir rpcs3_build && cd rpcs3_build`
|
||||
2) `cmake ../rpcs3/ && make` or `CXX=g++-8 CC=gcc-8 cmake ../rpcs3/ && make` to force these compilers
|
||||
3) Run RPCS3 with `./bin/rpcs3`
|
||||
|
||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||
If desired, use the various build options in [CMakeLists](https://github.com/RPCS3/rpcs3/blob/master/CMakeLists.txt).
|
||||
+29
-59
@@ -1,46 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
|
||||
project(rpcs3)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
|
||||
message(FATAL_ERROR "RPCS3 requires at least gcc-8.")
|
||||
endif()
|
||||
elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
message(FATAL_ERROR "RPCS3 requires at least clang-5.0.")
|
||||
endif()
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
||||
option(WITH_GDB "WITH_GDB" OFF)
|
||||
option(USE_NATIVE_INSTRUCTIONS "USE_NATIVE_INSTRUCTIONS makes rpcs3 compile with -march=native, which is useful for local builds, but not good for packages." ON)
|
||||
option(WITH_LLVM "Enable usage of LLVM library" ON)
|
||||
option(BUILD_LLVM_SUBMODULE "Build LLVM from git submodule" ON)
|
||||
option(USE_ALSA "ALSA audio backend" ON)
|
||||
option(USE_PULSE "PulseAudio audio backend" ON)
|
||||
option(USE_LIBEVDEV "libevdev-based joystick support" ON)
|
||||
option(USE_DISCORD_RPC "Discord rich presence integration" ON)
|
||||
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON)
|
||||
option(USE_VULKAN "Vulkan render backend" ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/rpcs3/cmake_modules")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
include(CheckCXXCompilerFlag)
|
||||
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
|
||||
|
||||
if (WITH_GDB)
|
||||
add_definitions(-DWITH_GDB_DEBUGGER)
|
||||
endif()
|
||||
|
||||
set(ASMJIT_STATIC TRUE)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "No build type selected, default to Release")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||
endif()
|
||||
@@ -51,39 +24,36 @@ if (CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DUNICODE)
|
||||
add_definitions(-D_WIN32_WINNT=0x0602)
|
||||
add_definitions(-DCMAKE_BUILD)
|
||||
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib." FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build tests." FORCE)
|
||||
|
||||
# Select the version of libpng to use, default is builtin
|
||||
if (NOT USE_SYSTEM_LIBPNG)
|
||||
add_subdirectory( 3rdparty/libpng )
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
include_directories(/opt/local/include)
|
||||
link_directories(/opt/local/lib)
|
||||
endif()
|
||||
|
||||
# Warnings are silenced for 3rdparty code
|
||||
set(CMAKE_CXX_FLAGS -w)
|
||||
set(CMAKE_C_FLAGS -w)
|
||||
set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "")
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/wd4530) # C++ exception handler used, but unwind semantics are not enabled
|
||||
endif()
|
||||
|
||||
add_subdirectory(Vulkan EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(asmjitsrc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(3rdparty)
|
||||
|
||||
unset(CMAKE_CXX_FLAGS)
|
||||
unset(CMAKE_C_FLAGS)
|
||||
|
||||
if (NOT WIN32)
|
||||
add_compile_options(-pthread)
|
||||
endif()
|
||||
option(VULKAN_PREBUILT "" OFF)
|
||||
|
||||
# TODO: do real installation, including copying directory structure
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_BINARY_DIR}/bin")
|
||||
|
||||
add_subdirectory(rpcs3)
|
||||
add_subdirectory( Vulkan )
|
||||
add_subdirectory( rpcs3 )
|
||||
|
||||
include_directories(3rdparty/hidapi/hidapi)
|
||||
if(APPLE)
|
||||
add_subdirectory(3rdparty/hidapi/mac)
|
||||
#list(APPEND LIBS hidapi)
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
add_subdirectory(3rdparty/hidapi/linux)
|
||||
elseif(MSVC)
|
||||
add_subdirectory(3rdparty/hidapi/windows)
|
||||
else()
|
||||
add_subdirectory(3rdparty/hidapi/libusb)
|
||||
#list(APPEND LIBS hidapi-libusb)
|
||||
endif()
|
||||
|
||||
@@ -4,36 +4,94 @@ RPCS3
|
||||
[](https://travis-ci.org/RPCS3/rpcs3)
|
||||
[](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
|
||||
|
||||
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.
|
||||
The world's first open-source PlayStation 3 emulator/debugger written in C++ for Windows and Linux.
|
||||
|
||||
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
|
||||
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
You can find some basic information in our [**website**](https://rpcs3.net/).
|
||||
For discussion about this emulator and PS3 emulation please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
|
||||
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to help the project but do not code, the best way to help out is to test games and make bug reports. See:
|
||||
* [Quickstart](https://rpcs3.net/quickstart)
|
||||
## Development
|
||||
|
||||
If you want to contribute as a developer, please take a look at the following pages:
|
||||
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forums or in Discord in order to know more about the current situation of the emulator.
|
||||
|
||||
* [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style)
|
||||
* [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information)
|
||||
* [Roadmap](https://rpcs3.net/roadmap)
|
||||
|
||||
You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator.
|
||||
## Dependencies
|
||||
|
||||
## Building
|
||||
### Windows
|
||||
* [Visual Studio 2017](https://www.visualstudio.com/en/downloads/)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572)
|
||||
* [Cmake 3.1.0+](https://www.cmake.org/download/) (required; add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||
* [Qt 5.9+](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.9.1\msvc2015_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
|
||||
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
|
||||
|
||||
See [BUILDING.md](BUILDING.md) for more information about how to setup an environment to build RPCS3.
|
||||
### Linux
|
||||
* [Qt 5.9+](https://www.qt.io/download-open-source/)
|
||||
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
|
||||
* Arch: `sudo pacman -S glew openal cmake llvm qt5-base`
|
||||
* Fedora: `sudo dnf install cmake qt5-devel vulkan-devel glew glew-devel`
|
||||
**If you have a NVIDIA GPU, you may need to install the libglvnd package.**
|
||||
|
||||
## Running
|
||||
### MacOS
|
||||
MacOS is not supported at this moment because it doesn't meet system requirements (OpenGL 4.3)
|
||||
* Xcode 6+ (tested with Xcode 6.4)
|
||||
* Install with Homebrew: `brew install glew llvm qt cmake`
|
||||
|
||||
Check our friendly [quickstart](https://rpcs3.net/quickstart) guide to make sure your computer meets the minimum system requirements to run RPCS3.
|
||||
|
||||
Don't forget to have your graphics driver up to date and to install the [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572) if you are a Windows user.
|
||||
## Building on Windows:
|
||||
To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules.
|
||||
|
||||
### Configuring Qt
|
||||
|
||||
*If you're using Visual Studio 2017 without Qt plugin support (or simply dont want to use it):*
|
||||
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.9.1\msvc2017_64\` </br>
|
||||
|
||||
*If you wish to use the Visual Studio plugin for Qt:* </br>
|
||||
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.9.1\msvc2015_64`. </br>
|
||||
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
### Building the projects
|
||||
1) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br>
|
||||
2) Press *BUILD* > *Build Solution* or *Rebuild Solution*. </br>
|
||||
|
||||
|
||||
## Building on Linux & Mac OS:
|
||||
|
||||
1) `git clone https://github.com/RPCS3/rpcs3.git` </br>
|
||||
2) `cd rpcs3/` </br>
|
||||
3) `git submodule update --init` </br>
|
||||
4) `cd ../ && mkdir rpcs3_build && cd rpcs3_build`
|
||||
4) `cmake ../rpcs3/ && make GitVersion && make` </br>
|
||||
5) Run RPCS3 with `./bin/rpcs3` </br>
|
||||
|
||||
If you are on MacOS and want to build with brew llvm and qt don't forget to add the following environment variables
|
||||
|
||||
* `LLVM_DIR=/usr/local/opt/llvm/` (or wherever llvm was installed).
|
||||
* `Qt5_DIR=/usr/local/opt/qt/lib/cmake/Qt5` (or wherever qt was installed).
|
||||
|
||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||
|
||||
|
||||
## CMake Build Options (Linux & Mac OS)
|
||||
|
||||
- ```-DUSE_SYSTEM_LIBPNG=ON/OFF``` (default = *OFF*)
|
||||
Build against the shared libpng instead of using the builtin one. libpng 1.6+ highly recommended. Try this option if you get version conflict errors or only see black game icons.
|
||||
|
||||
- ```-DUSE_SYSTEM_FFMPEG=ON/OFF``` (default = *OFF*)
|
||||
Build against the shared ffmpeg libraries instead of using the builtin patched version. Try this if the builtin version breaks the OpenGL renderer for you.
|
||||
|
||||
- ```-DWITHOUT_LLVM=ON/OFF``` (default = *OFF*)
|
||||
This forces RPCS3 to build without LLVM, not recommended.
|
||||
|
||||
- ```-DWITH_GDB=ON/OFF``` (default = *OFF*)
|
||||
This Builds RPCS3 with support for debugging PS3 games using gdb.
|
||||
|
||||
- ```-DUSE_VULKAN=ON/OFF``` (default = *ON*)
|
||||
This builds RPCS3 with Vulkan support.
|
||||
|
||||
## License
|
||||
|
||||
Most files are licensed under the terms of GNU GPLv2 License; see LICENSE file for details. Some files may be licensed differently; check appropriate file headers for details.
|
||||
Most files are licensed under the terms of GNU GPLv2 License, see LICENSE file for details. Some files may be licensed differently, check appropriate file headers for details.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
|
||||
// Unfinished. Only std::default_delete will work as expected.
|
||||
template<typename T, typename D>
|
||||
|
||||
+49
-30
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <cstring>
|
||||
|
||||
// 128-bit vector type and also se_storage<> storage type
|
||||
union alignas(16) v128
|
||||
@@ -308,7 +307,7 @@ struct offset32_array<v128::masked_array_t<T, N, M>>
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return u32{sizeof(T)} * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
return SIZE_32(T) * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -335,10 +334,7 @@ inline v128 operator~(const v128& other)
|
||||
template <typename T, std::size_t Align, std::size_t Size>
|
||||
struct se_storage
|
||||
{
|
||||
struct type
|
||||
{
|
||||
alignas(Align) std::byte data[Size];
|
||||
};
|
||||
using type = std::aligned_storage_t<Size, Align>;
|
||||
|
||||
// Unoptimized generic byteswap for unaligned data
|
||||
static void reverse(u8* dst, const u8* src)
|
||||
@@ -362,6 +358,16 @@ struct se_storage
|
||||
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
|
||||
return result;
|
||||
}
|
||||
|
||||
static type copy(const type& src)
|
||||
{
|
||||
type result;
|
||||
for (std::size_t i = 0; i < Size; i++)
|
||||
{
|
||||
reinterpret_cast<u8*>(&result)[i] = reinterpret_cast<const u8*>(&src)[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -380,12 +386,18 @@ struct se_storage<T, 2, 2>
|
||||
|
||||
static inline u16 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u16>(src));
|
||||
return swap(reinterpret_cast<const u16&>(src));
|
||||
}
|
||||
|
||||
static inline T from(u16 src)
|
||||
{
|
||||
return std::bit_cast<T, u16>(swap(src));
|
||||
const u16 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -405,12 +417,18 @@ struct se_storage<T, 4, 4>
|
||||
|
||||
static inline u32 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u32>(src));
|
||||
return swap(reinterpret_cast<const u32&>(src));
|
||||
}
|
||||
|
||||
static inline T from(u32 src)
|
||||
{
|
||||
return std::bit_cast<T, u32>(swap(src));
|
||||
const u32 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -430,12 +448,18 @@ struct se_storage<T, 8, 8>
|
||||
|
||||
static inline u64 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u64>(src));
|
||||
return swap(reinterpret_cast<const u64&>(src));
|
||||
}
|
||||
|
||||
static inline T from(u64 src)
|
||||
{
|
||||
return std::bit_cast<T, u64>(swap(src));
|
||||
const u64 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -446,17 +470,23 @@ struct se_storage<T, 16, 16>
|
||||
|
||||
static inline v128 swap(const v128& src)
|
||||
{
|
||||
return v128::from64(se_storage<u64>::swap(src._u64[1]), se_storage<u64>::swap(src._u64[0]));
|
||||
return v128::fromV(_mm_shuffle_epi8(src.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
|
||||
}
|
||||
|
||||
static inline v128 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<v128>(src));
|
||||
return swap(reinterpret_cast<const v128&>(src));
|
||||
}
|
||||
|
||||
static inline T from(const v128& src)
|
||||
{
|
||||
return std::bit_cast<T, v128>(swap(src));
|
||||
const v128 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -490,11 +520,6 @@ public:
|
||||
return storage::from(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t&) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
@@ -529,33 +554,27 @@ public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(type value)
|
||||
: m_data(std::bit_cast<stype>(value))
|
||||
: m_data(reinterpret_cast<const stype&>(value))
|
||||
{
|
||||
}
|
||||
|
||||
type value() const
|
||||
{
|
||||
return std::bit_cast<type>(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t& value) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
{
|
||||
m_data = std::bit_cast<stype>(value);
|
||||
return *this;
|
||||
return m_data = reinterpret_cast<const stype&>(value), *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const
|
||||
{
|
||||
return value();
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <limits>
|
||||
|
||||
template<typename T, uint N>
|
||||
struct bf_base
|
||||
@@ -16,11 +15,11 @@ struct bf_base
|
||||
// Field bitsize
|
||||
static constexpr uint bitsize = N;
|
||||
|
||||
// All ones mask
|
||||
static constexpr utype mask1 = std::numeric_limits<utype>::max();
|
||||
|
||||
// Value mask
|
||||
static constexpr utype vmask = mask1 >> (bitmax - bitsize);
|
||||
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
|
||||
|
||||
// All ones mask
|
||||
static constexpr utype mask1 = static_cast<utype>(~utype{});
|
||||
|
||||
protected:
|
||||
type m_data;
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Utilities/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#include "tlhelp32.h"
|
||||
#else
|
||||
#include "stdlib.h"
|
||||
#include "sys/times.h"
|
||||
#include "sys/types.h"
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <mach/mach_init.h>
|
||||
# include <mach/task.h>
|
||||
# include <mach/vm_map.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <dirent.h>
|
||||
#endif
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/sysctl.h>
|
||||
# if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
# include <sys/user.h>
|
||||
# endif
|
||||
|
||||
# if defined(__NetBSD__)
|
||||
# undef KERN_PROC
|
||||
# define KERN_PROC KERN_PROC2
|
||||
# define kinfo_proc kinfo_proc2
|
||||
# endif
|
||||
|
||||
# if defined(__DragonFly__)
|
||||
# define KP_NLWP(kp) (kp.kp_nthreads)
|
||||
# elif defined(__FreeBSD__)
|
||||
# define KP_NLWP(kp) (kp.ki_numthreads)
|
||||
# elif defined(__NetBSD__)
|
||||
# define KP_NLWP(kp) (kp.p_nlwps)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
class CPUStats
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HANDLE m_self;
|
||||
using time_type = ULARGE_INTEGER;
|
||||
#else
|
||||
using time_type = clock_t;
|
||||
#endif
|
||||
|
||||
private:
|
||||
s32 m_num_processors;
|
||||
time_type m_last_cpu, m_sys_cpu, m_usr_cpu;
|
||||
|
||||
public:
|
||||
CPUStats()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
FILETIME ftime, fsys, fuser;
|
||||
|
||||
GetSystemInfo(&sysInfo);
|
||||
m_num_processors = sysInfo.dwNumberOfProcessors;
|
||||
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
memcpy(&m_last_cpu, &ftime, sizeof(FILETIME));
|
||||
|
||||
m_self = GetCurrentProcess();
|
||||
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fuser);
|
||||
memcpy(&m_sys_cpu, &fsys, sizeof(FILETIME));
|
||||
memcpy(&m_usr_cpu, &fuser, sizeof(FILETIME));
|
||||
#else
|
||||
struct tms timeSample;
|
||||
|
||||
m_last_cpu = times(&timeSample);
|
||||
m_sys_cpu = timeSample.tms_stime;
|
||||
m_usr_cpu = timeSample.tms_utime;
|
||||
m_num_processors = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
}
|
||||
|
||||
double get_usage()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FILETIME ftime, fsys, fusr;
|
||||
ULARGE_INTEGER now, sys, usr;
|
||||
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
memcpy(&now, &ftime, sizeof(FILETIME));
|
||||
|
||||
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fusr);
|
||||
memcpy(&sys, &fsys, sizeof(FILETIME));
|
||||
memcpy(&usr, &fusr, sizeof(FILETIME));
|
||||
double percent = double(sys.QuadPart - m_sys_cpu.QuadPart) + (usr.QuadPart - m_usr_cpu.QuadPart);
|
||||
percent /= (now.QuadPart - m_last_cpu.QuadPart);
|
||||
percent /= m_num_processors;
|
||||
|
||||
m_last_cpu = now;
|
||||
m_usr_cpu = usr;
|
||||
m_sys_cpu = sys;
|
||||
|
||||
return std::clamp(percent * 100, 0.0, 100.0);
|
||||
#else
|
||||
struct tms timeSample;
|
||||
clock_t now;
|
||||
double percent;
|
||||
|
||||
now = times(&timeSample);
|
||||
if (now <= m_last_cpu || timeSample.tms_stime < m_sys_cpu || timeSample.tms_utime < m_usr_cpu)
|
||||
{
|
||||
// Overflow detection. Just skip this value.
|
||||
percent = -1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
percent = (timeSample.tms_stime - m_sys_cpu) + (timeSample.tms_utime - m_usr_cpu);
|
||||
percent /= (now - m_last_cpu);
|
||||
percent /= m_num_processors;
|
||||
percent *= 100;
|
||||
}
|
||||
m_last_cpu = now;
|
||||
m_sys_cpu = timeSample.tms_stime;
|
||||
m_usr_cpu = timeSample.tms_utime;
|
||||
|
||||
return percent;
|
||||
#endif
|
||||
}
|
||||
|
||||
static u32 get_thread_count()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// first determine the id of the current process
|
||||
DWORD const id = GetCurrentProcessId();
|
||||
|
||||
// then get a process list snapshot.
|
||||
HANDLE const snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
|
||||
|
||||
// initialize the process entry structure.
|
||||
PROCESSENTRY32 entry = {0};
|
||||
entry.dwSize = sizeof(entry);
|
||||
|
||||
// get the first process info.
|
||||
BOOL ret = true;
|
||||
ret = Process32First(snapshot, &entry);
|
||||
while (ret && entry.th32ProcessID != id)
|
||||
{
|
||||
ret = Process32Next(snapshot, &entry);
|
||||
}
|
||||
CloseHandle(snapshot);
|
||||
return ret ? entry.cntThreads : 0;
|
||||
#elif defined(__APPLE__)
|
||||
const task_t task = mach_task_self();
|
||||
mach_msg_type_number_t thread_count;
|
||||
thread_act_array_t thread_list;
|
||||
if (task_threads(task, &thread_list, &thread_count) != KERN_SUCCESS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
vm_deallocate(task, reinterpret_cast<vm_address_t>(thread_list),
|
||||
sizeof(thread_t) * thread_count);
|
||||
return static_cast<u32>(thread_count);
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID,
|
||||
getpid(),
|
||||
#if defined(__NetBSD__)
|
||||
sizeof(struct kinfo_proc),
|
||||
1,
|
||||
#endif
|
||||
};
|
||||
u_int miblen = std::size(mib);
|
||||
struct kinfo_proc info;
|
||||
size_t size = sizeof(info);
|
||||
if (sysctl(mib, miblen, &info, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return KP_NLWP(info);
|
||||
#elif defined(__OpenBSD__)
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID | KERN_PROC_SHOW_THREADS,
|
||||
getpid(),
|
||||
sizeof(struct kinfo_proc),
|
||||
0,
|
||||
};
|
||||
u_int miblen = std::size(mib);
|
||||
|
||||
// get number of structs
|
||||
size_t size;
|
||||
if (sysctl(mib, miblen, NULL, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
mib[5] = size / mib[4];
|
||||
|
||||
// populate array of structs
|
||||
struct kinfo_proc info[mib[5]];
|
||||
if (sysctl(mib, miblen, &info, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// exclude empty members
|
||||
u32 thread_count{0};
|
||||
for (int i = 0; i < size / mib[4]; i++)
|
||||
{
|
||||
if (info[i].p_tid != -1)
|
||||
++thread_count;
|
||||
}
|
||||
return thread_count;
|
||||
#elif defined(__linux__)
|
||||
u32 thread_count{0};
|
||||
|
||||
DIR* proc_dir = opendir("/proc/self/task");
|
||||
if (proc_dir)
|
||||
{
|
||||
// proc available, iterate through tasks and count them
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(proc_dir)) != NULL)
|
||||
{
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
++thread_count;
|
||||
}
|
||||
|
||||
closedir(proc_dir);
|
||||
}
|
||||
return thread_count;
|
||||
#else
|
||||
// unimplemented
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
+11
-12
@@ -7,6 +7,8 @@
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
logs::channel cfg("CFG");
|
||||
|
||||
_base::_base(type _type)
|
||||
: m_type(_type)
|
||||
{
|
||||
@@ -65,19 +67,19 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): exception: %s", value, e.what());
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): exception: %s", value, e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -117,13 +119,13 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
if (out) cfg.error("cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (val > max)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
|
||||
if (out) cfg.error("cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -132,7 +134,7 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
|
||||
if (out) cfg.error("cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -200,12 +202,9 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||
out << YAML::EndMap;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
out << rhs.to_string();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out << rhs.to_string();
|
||||
}
|
||||
|
||||
void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
|
||||
@@ -298,7 +297,7 @@ bool cfg::node::from_string(const std::string& value) try
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
|
||||
cfg.fatal("%s thrown: %s", typeid(e).name(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+7
-37
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
@@ -49,10 +49,9 @@ namespace cfg
|
||||
_base(type _type, class node* owner, const std::string& name);
|
||||
|
||||
public:
|
||||
// Disallow copy/move constructors and assignments
|
||||
_base(const _base&) = delete;
|
||||
|
||||
_base& operator=(const _base&) = delete;
|
||||
|
||||
// Get type
|
||||
type get_type() const { return m_type; }
|
||||
|
||||
@@ -119,7 +118,7 @@ namespace cfg
|
||||
bool m_value;
|
||||
|
||||
public:
|
||||
bool def;
|
||||
const bool def;
|
||||
|
||||
_bool(node* owner, const std::string& name, bool def = false)
|
||||
: _base(type::_bool, owner, name)
|
||||
@@ -133,11 +132,6 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const bool& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override;
|
||||
|
||||
std::string to_string() const override
|
||||
@@ -156,11 +150,6 @@ namespace cfg
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set(const bool& value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||
@@ -184,11 +173,6 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const T& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
@@ -233,11 +217,7 @@ namespace cfg
|
||||
int_type m_value;
|
||||
|
||||
public:
|
||||
int_type def;
|
||||
|
||||
// Expose range
|
||||
static const s64 max = Max;
|
||||
static const s64 min = Min;
|
||||
const int_type def;
|
||||
|
||||
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: _base(type::_int, owner, name)
|
||||
@@ -251,11 +231,6 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const int_type& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
@@ -278,11 +253,6 @@ namespace cfg
|
||||
return false;
|
||||
}
|
||||
|
||||
void set(const s64& value)
|
||||
{
|
||||
m_value = static_cast<int_type>(value);
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return make_int_range(Min, Max);
|
||||
@@ -301,7 +271,7 @@ namespace cfg
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
std::string def;
|
||||
const std::string def;
|
||||
|
||||
string(node* owner, const std::string& name, const std::string& def = {})
|
||||
: _base(type::string, owner, name)
|
||||
@@ -351,7 +321,7 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
const std::set<std::string>& get_set() const
|
||||
std::set<std::string> get_set() const
|
||||
{
|
||||
return m_set;
|
||||
}
|
||||
@@ -386,7 +356,7 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
const std::map<std::string, logs::level>& get_map() const
|
||||
std::map<std::string, logs::level> get_map() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
+126
-424
@@ -9,7 +9,6 @@
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <typeinfo>
|
||||
#include <map>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
@@ -76,25 +75,10 @@ static time_t to_time(const FILETIME& ft)
|
||||
|
||||
static FILETIME from_time(s64 _time)
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
|
||||
FILETIME result;
|
||||
|
||||
if (_time <= -11644473600ll)
|
||||
{
|
||||
result.dwLowDateTime = 0;
|
||||
result.dwHighDateTime = 0;
|
||||
}
|
||||
else if (_time > INT64_MAX / 10000000ll - 11644473600ll)
|
||||
{
|
||||
result.dwLowDateTime = 0xffffffff;
|
||||
result.dwHighDateTime = 0x7fffffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ull) * 10000000ull;
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
}
|
||||
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -112,8 +96,6 @@ static fs::error to_error(DWORD e)
|
||||
case ERROR_INVALID_NAME: return fs::error::inval;
|
||||
case ERROR_SHARING_VIOLATION: return fs::error::acces;
|
||||
case ERROR_DIR_NOT_EMPTY: return fs::error::notempty;
|
||||
case ERROR_NOT_READY: return fs::error::noent;
|
||||
//case ERROR_INVALID_PARAMETER: return fs::error::inval;
|
||||
default: fmt::throw_exception("Unknown Win32 error: %u.", e);
|
||||
}
|
||||
}
|
||||
@@ -124,8 +106,6 @@ static fs::error to_error(DWORD e)
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/uio.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
@@ -136,12 +116,10 @@ static fs::error to_error(DWORD e)
|
||||
#if defined(__APPLE__)
|
||||
#include <copyfile.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/socket.h> // sendfile
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/fs.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
static fs::error to_error(int e)
|
||||
@@ -195,43 +173,6 @@ namespace fs
|
||||
// Do notning
|
||||
}
|
||||
|
||||
fs::native_handle fs::file_base::get_handle()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 file_base::write_gather(const iovec_clone* buffers, u64 buf_count)
|
||||
{
|
||||
u64 total = 0;
|
||||
|
||||
for (u64 i = 0; i < buf_count; i++)
|
||||
{
|
||||
if (!buffers[i].iov_base || buffers[i].iov_len + total < total)
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
total += buffers[i].iov_len;
|
||||
}
|
||||
|
||||
const auto buf = std::make_unique<uchar[]>(total);
|
||||
|
||||
u64 copied = 0;
|
||||
|
||||
for (u64 i = 0; i < buf_count; i++)
|
||||
{
|
||||
std::memcpy(buf.get() + copied, buffers[i].iov_base, buffers[i].iov_len);
|
||||
copied += buffers[i].iov_len;
|
||||
}
|
||||
|
||||
return this->write(buf.get(), total);
|
||||
}
|
||||
|
||||
dir_base::~dir_base()
|
||||
{
|
||||
}
|
||||
@@ -257,7 +198,7 @@ std::shared_ptr<fs::device_base> fs::device_manager::get_device(const std::strin
|
||||
|
||||
std::shared_ptr<fs::device_base> fs::device_manager::set_device(const std::string& name, const std::shared_ptr<device_base>& device)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
writer_lock lock(m_mutex);
|
||||
|
||||
return m_map[name] = device;
|
||||
}
|
||||
@@ -324,7 +265,7 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
info.size = (u64)attrs.nFileSizeLow | ((u64)attrs.nFileSizeHigh << 32);
|
||||
info.atime = to_time(attrs.ftLastAccessTime);
|
||||
info.mtime = to_time(attrs.ftLastWriteTime);
|
||||
info.ctime = info.mtime;
|
||||
info.ctime = to_time(attrs.ftCreationTime);
|
||||
#else
|
||||
struct ::stat file_info;
|
||||
if (::stat(path.c_str(), &file_info) != 0)
|
||||
@@ -338,12 +279,9 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = info.mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
#endif
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -539,30 +477,18 @@ bool fs::create_dir(const std::string& path)
|
||||
|
||||
bool fs::create_path(const std::string& path)
|
||||
{
|
||||
const std::string parent = get_parent_dir(path);
|
||||
const auto& parent = get_parent_dir(path);
|
||||
|
||||
if (!parent.empty() && !create_path(parent))
|
||||
if (!parent.empty() && !is_dir(parent) && !create_path(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!create_dir(path) && g_tls_error != error::exist)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return create_dir(path);
|
||||
}
|
||||
|
||||
bool fs::remove_dir(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow removing empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->remove_dir(path);
|
||||
@@ -589,13 +515,6 @@ bool fs::remove_dir(const std::string& path)
|
||||
|
||||
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
{
|
||||
if (from.empty() || to.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto device = get_virtual_device(from);
|
||||
|
||||
if (device != get_virtual_device(to))
|
||||
@@ -640,21 +559,6 @@ bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
|
||||
return true;
|
||||
#else
|
||||
|
||||
#ifdef __linux__
|
||||
if (syscall(SYS_renameat2, AT_FDCWD, from.c_str(), AT_FDCWD, to.c_str(), overwrite ? 0 : 1 /* RENAME_NOREPLACE */) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the filesystem doesn't support RENAME_NOREPLACE, it returns EINVAL. Retry with fallback method in that case.
|
||||
if (errno != EINVAL || overwrite)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!overwrite && exists(to))
|
||||
{
|
||||
g_tls_error = fs::error::exist;
|
||||
@@ -688,7 +592,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
}
|
||||
|
||||
return true;
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined(__sun)
|
||||
#else
|
||||
/* Source: http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c */
|
||||
|
||||
const int input = ::open(from.c_str(), O_RDONLY);
|
||||
@@ -712,13 +616,17 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
#if defined(__APPLE__)
|
||||
// fcopyfile works on OS X 10.5+
|
||||
if (::fcopyfile(input, output, 0, COPYFILE_ALL))
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
if (::sendfile(input, output, 0, 0, NULL, NULL, 0))
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
// sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
|
||||
off_t bytes_copied = 0;
|
||||
struct ::stat fileinfo = { 0 };
|
||||
if (::fstat(input, &fileinfo) || ::sendfile(output, input, &bytes_copied, fileinfo.st_size))
|
||||
#else
|
||||
#error "Native file copy implementation is missing"
|
||||
#else // NetBSD, OpenBSD, etc.
|
||||
fmt::throw_exception("fs::copy_file() isn't implemented for this platform.\nFrom: %s\nTo: %s", from, to);
|
||||
errno = 0;
|
||||
if (true)
|
||||
#endif
|
||||
{
|
||||
const int err = errno;
|
||||
@@ -731,31 +639,6 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
|
||||
::close(input);
|
||||
::close(output);
|
||||
return true;
|
||||
#else // fallback
|
||||
{
|
||||
std::ifstream out{to, std::ios::binary};
|
||||
if (out.good() && !overwrite)
|
||||
{
|
||||
g_tls_error = to_error(EEXIST);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::ifstream in{from, std::ios::binary};
|
||||
std::ofstream out{to, std::ios::binary};
|
||||
|
||||
if (!in.good() || !out.good())
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::istreambuf_iterator<char> bin(in);
|
||||
std::istreambuf_iterator<char> ein;
|
||||
std::ostreambuf_iterator<char> bout(out);
|
||||
std::copy(bin, ein, bout);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@@ -879,13 +762,6 @@ void fs::file::xfail() const
|
||||
|
||||
fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _file = device->open(path, mode))
|
||||
@@ -899,34 +775,29 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD access = 0;
|
||||
if (mode & fs::read) access |= GENERIC_READ;
|
||||
if (mode & fs::write) access |= DELETE | (mode & fs::append ? FILE_APPEND_DATA : GENERIC_WRITE);
|
||||
if (test(mode & fs::read)) access |= GENERIC_READ;
|
||||
if (test(mode & fs::write)) access |= test(mode & fs::append) ? FILE_APPEND_DATA : GENERIC_WRITE;
|
||||
|
||||
DWORD disp = 0;
|
||||
if (mode & fs::create)
|
||||
if (test(mode & fs::create))
|
||||
{
|
||||
disp =
|
||||
mode & fs::excl ? CREATE_NEW :
|
||||
mode & fs::trunc ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
test(mode & fs::excl) ? CREATE_NEW :
|
||||
test(mode & fs::trunc) ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode & fs::excl)
|
||||
if (test(mode & fs::excl))
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return;
|
||||
}
|
||||
|
||||
disp = mode & fs::trunc ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
}
|
||||
|
||||
DWORD share = 0;
|
||||
if (!(mode & fs::unread) || !(mode & fs::write))
|
||||
{
|
||||
share |= FILE_SHARE_READ;
|
||||
}
|
||||
|
||||
if (!(mode & (fs::lock + fs::unread)) || !(mode & fs::write))
|
||||
DWORD share = FILE_SHARE_READ;
|
||||
if (!test(mode & fs::unshare))
|
||||
{
|
||||
share |= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
}
|
||||
@@ -939,7 +810,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return;
|
||||
}
|
||||
|
||||
class windows_file final : public file_base
|
||||
class windows_file final : public file_base, public get_native_handle
|
||||
{
|
||||
const HANDLE m_handle;
|
||||
|
||||
@@ -965,10 +836,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
info.size = this->size();
|
||||
info.atime = to_time(basic_info.LastAccessTime);
|
||||
info.mtime = to_time(basic_info.ChangeTime);
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = to_time(basic_info.CreationTime);
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -1042,7 +910,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return size.QuadPart;
|
||||
}
|
||||
|
||||
native_handle get_handle() override
|
||||
native_handle get() override
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
@@ -1052,23 +920,16 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
#else
|
||||
int flags = 0;
|
||||
|
||||
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
|
||||
else if (mode & fs::read) flags |= O_RDONLY;
|
||||
else if (mode & fs::write) flags |= O_WRONLY;
|
||||
if (test(mode & fs::read) && test(mode & fs::write)) flags |= O_RDWR;
|
||||
else if (test(mode & fs::read)) flags |= O_RDONLY;
|
||||
else if (test(mode & fs::write)) flags |= O_WRONLY;
|
||||
|
||||
if (mode & fs::append) flags |= O_APPEND;
|
||||
if (mode & fs::create) flags |= O_CREAT;
|
||||
if (mode & fs::trunc && !(mode & (fs::lock + fs::unread))) flags |= O_TRUNC;
|
||||
if (mode & fs::excl) flags |= O_EXCL;
|
||||
if (test(mode & fs::append)) flags |= O_APPEND;
|
||||
if (test(mode & fs::create)) flags |= O_CREAT;
|
||||
if (test(mode & fs::trunc)) flags |= O_TRUNC;
|
||||
if (test(mode & fs::excl)) flags |= O_EXCL;
|
||||
|
||||
int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
if (mode & fs::write && mode & fs::unread)
|
||||
{
|
||||
perm = 0;
|
||||
}
|
||||
|
||||
const int fd = ::open(path.c_str(), flags, perm);
|
||||
const int fd = ::open(path.c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
@@ -1076,20 +937,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs::write && mode & (fs::lock + fs::unread) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||
{
|
||||
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
|
||||
::close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs::trunc && mode & (fs::lock + fs::unread))
|
||||
{
|
||||
// Postpone truncation in order to avoid using O_TRUNC on a locked file
|
||||
::ftruncate(fd, 0);
|
||||
}
|
||||
|
||||
class unix_file final : public file_base
|
||||
class unix_file final : public file_base, public get_native_handle
|
||||
{
|
||||
const int m_fd;
|
||||
|
||||
@@ -1115,10 +963,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -1164,7 +1009,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||
|
||||
const auto result = ::lseek(m_fd, offset, mode);
|
||||
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
@@ -1182,21 +1027,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return file_info.st_size;
|
||||
}
|
||||
|
||||
native_handle get_handle() override
|
||||
native_handle get() override
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count) override
|
||||
{
|
||||
static_assert(sizeof(iovec) == sizeof(iovec_clone), "Weird iovec size");
|
||||
static_assert(offsetof(iovec, iov_len) == offsetof(iovec_clone, iov_len), "Weird iovec::iov_len offset");
|
||||
|
||||
const auto result = ::writev(m_fd, (const iovec*)buffers, buf_count);
|
||||
verify("file::write_gather" HERE), result != -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
m_file = std::make_unique<unix_file>(fd);
|
||||
@@ -1274,9 +1108,9 @@ fs::file::file(const void* ptr, std::size_t size)
|
||||
|
||||
fs::native_handle fs::file::get_handle() const
|
||||
{
|
||||
if (m_file)
|
||||
if (auto getter = dynamic_cast<get_native_handle*>(m_file.get()))
|
||||
{
|
||||
return m_file->get_handle();
|
||||
return getter->get();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -1286,15 +1120,6 @@ fs::native_handle fs::file::get_handle() const
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool fs::file::set_delete(bool autodelete) const
|
||||
{
|
||||
FILE_DISPOSITION_INFO disp;
|
||||
disp.DeleteFileW = autodelete;
|
||||
return SetFileInformationByHandle(get_handle(), FileDispositionInfo, &disp, sizeof(disp)) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void fs::dir::xnull() const
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("fs::dir is null");
|
||||
@@ -1302,13 +1127,6 @@ void fs::dir::xnull() const
|
||||
|
||||
bool fs::dir::open(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _dir = device->open_dir(path))
|
||||
@@ -1345,10 +1163,7 @@ bool fs::dir::open(const std::string& path)
|
||||
info.size = ((u64)found.nFileSizeHigh << 32) | (u64)found.nFileSizeLow;
|
||||
info.atime = to_time(found.ftLastAccessTime);
|
||||
info.mtime = to_time(found.ftLastWriteTime);
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = to_time(found.ftCreationTime);
|
||||
|
||||
m_entries.emplace_back(std::move(info));
|
||||
}
|
||||
@@ -1418,12 +1233,7 @@ bool fs::dir::open(const std::string& path)
|
||||
}
|
||||
|
||||
struct ::stat file_info;
|
||||
|
||||
if (::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) != 0)
|
||||
{
|
||||
//failed metadata (broken symlink?), ignore and skip to next file
|
||||
return read(info);
|
||||
}
|
||||
verify("dir::read" HERE), ::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) == 0;
|
||||
|
||||
info.name = found->d_name;
|
||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||
@@ -1431,10 +1241,7 @@ bool fs::dir::open(const std::string& path)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1459,10 +1266,8 @@ const std::string& fs::get_config_dir()
|
||||
std::string dir;
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t buf[32768];
|
||||
constexpr DWORD size = static_cast<DWORD>(std::size(buf));
|
||||
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf, size) - 1 >= size - 1 &&
|
||||
GetModuleFileName(NULL, buf, size) - 1 >= size - 1)
|
||||
wchar_t buf[2048];
|
||||
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
|
||||
{
|
||||
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_config_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
@@ -1474,22 +1279,16 @@ const std::string& fs::get_config_dir()
|
||||
|
||||
dir.resize(dir.rfind('/') + 1);
|
||||
#else
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/Library/Application Support"s;
|
||||
#else
|
||||
if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = conf;
|
||||
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = home;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/.config"s;
|
||||
#endif
|
||||
else // Just in case
|
||||
dir = "./config";
|
||||
|
||||
dir += "/rpcs3/";
|
||||
|
||||
if (!create_path(dir))
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
{
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
}
|
||||
@@ -1501,83 +1300,102 @@ const std::string& fs::get_config_dir()
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
const std::string& fs::get_cache_dir()
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix)
|
||||
{
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
std::string dir;
|
||||
const std::string& dir = get_config_dir() + "data/";
|
||||
|
||||
#ifdef _WIN32
|
||||
dir = get_config_dir();
|
||||
#else
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/Library/Caches"s;
|
||||
#else
|
||||
if (const char* cache = ::getenv("XDG_CACHE_HOME"))
|
||||
dir = cache;
|
||||
else if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = conf;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/.cache"s;
|
||||
#endif
|
||||
else // Just in case
|
||||
dir = "./cache";
|
||||
|
||||
dir += "/rpcs3/";
|
||||
|
||||
if (!create_path(dir))
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
{
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
return get_config_dir();
|
||||
}
|
||||
#endif
|
||||
|
||||
return dir;
|
||||
}();
|
||||
|
||||
return s_dir;
|
||||
}
|
||||
std::vector<u8> buf;
|
||||
buf.reserve(location.size() + 1);
|
||||
|
||||
bool fs::remove_all(const std::string& path, bool remove_root)
|
||||
{
|
||||
if (const auto root_dir = dir(path))
|
||||
// Normalize location
|
||||
for (char c : location)
|
||||
{
|
||||
for (const auto& entry : root_dir)
|
||||
#ifdef _WIN32
|
||||
if (c == '/' || c == '\\')
|
||||
#else
|
||||
if (c == '/')
|
||||
#endif
|
||||
{
|
||||
if (entry.name == "." || entry.name == "..")
|
||||
if (buf.empty() || buf.back() != '/')
|
||||
{
|
||||
continue;
|
||||
buf.push_back('/');
|
||||
}
|
||||
|
||||
if (entry.is_directory == false)
|
||||
{
|
||||
if (!remove_file(path + '/' + entry.name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.push_back(c);
|
||||
}
|
||||
|
||||
if (entry.is_directory == true)
|
||||
{
|
||||
if (!remove_all(path + '/' + entry.name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Calculate hash
|
||||
u8 hash[20];
|
||||
sha1(buf.data(), buf.size(), hash);
|
||||
|
||||
// Concatenate
|
||||
std::string&& result = fmt::format("%s%s/%016llx%08x-%s/", s_dir, prefix, reinterpret_cast<be_t<u64>&>(hash[0]), reinterpret_cast<be_t<u32>&>(hash[8]), suffix);
|
||||
|
||||
if (!is_dir(result))
|
||||
{
|
||||
// Create dir if necessary
|
||||
if (create_path(result))
|
||||
{
|
||||
// Acknowledge original location
|
||||
file(result + ".location", rewrite).write(buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const auto& delim = "/\\";
|
||||
#else
|
||||
const auto& delim = "/";
|
||||
#endif
|
||||
|
||||
// Extract file name and location
|
||||
const std::string& location = fs::get_parent_dir(path);
|
||||
const std::size_t name_pos = path.find_first_not_of(delim, location.size());
|
||||
|
||||
return fs::get_data_dir(prefix, location, name_pos == -1 ? std::string{} : path.substr(name_pos));
|
||||
}
|
||||
|
||||
void fs::remove_all(const std::string& path, bool remove_root)
|
||||
{
|
||||
for (const auto& entry : dir(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remove_root)
|
||||
{
|
||||
return remove_dir(path);
|
||||
if (entry.name == "." || entry.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.is_directory == false)
|
||||
{
|
||||
remove_file(path + '/' + entry.name);
|
||||
}
|
||||
|
||||
if (entry.is_directory == true)
|
||||
{
|
||||
remove_all(path + '/' + entry.name);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
if (remove_root)
|
||||
{
|
||||
remove_dir(path);
|
||||
}
|
||||
}
|
||||
|
||||
u64 fs::get_dir_size(const std::string& path)
|
||||
@@ -1605,122 +1423,6 @@ u64 fs::get_dir_size(const std::string& path)
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::file fs::make_gather(std::vector<fs::file> files)
|
||||
{
|
||||
struct gather_stream : file_base
|
||||
{
|
||||
u64 pos = 0;
|
||||
u64 end = 0;
|
||||
std::vector<file> files;
|
||||
std::map<u64, u64> ends; // Fragment End Offset -> Index
|
||||
|
||||
gather_stream(std::vector<fs::file> arg)
|
||||
: files(std::move(arg))
|
||||
{
|
||||
// Preprocess files
|
||||
for (auto&& f : files)
|
||||
{
|
||||
end += f.size();
|
||||
ends.emplace(end, ends.size());
|
||||
}
|
||||
}
|
||||
|
||||
~gather_stream() override
|
||||
{
|
||||
}
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
fs::stat_t result{};
|
||||
|
||||
if (!files.empty())
|
||||
{
|
||||
result = files[0].stat();
|
||||
}
|
||||
|
||||
result.is_directory = false;
|
||||
result.is_writable = false;
|
||||
result.size = end;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
if (pos < end)
|
||||
{
|
||||
// Current pos
|
||||
const u64 start = pos;
|
||||
|
||||
// Get readable size
|
||||
if (const u64 max = std::min<u64>(size, end - pos))
|
||||
{
|
||||
u8* buf_out = static_cast<u8*>(buffer);
|
||||
u64 buf_max = max;
|
||||
|
||||
for (auto it = ends.upper_bound(pos); it != ends.end(); ++it)
|
||||
{
|
||||
// Set position for the fragment
|
||||
files[it->second].seek(pos - it->first, fs::seek_end);
|
||||
|
||||
const u64 count = std::min<u64>(it->first - pos, buf_max);
|
||||
const u64 read = files[it->second].read(buf_out, count);
|
||||
|
||||
buf_out += count;
|
||||
buf_max -= count;
|
||||
pos += read;
|
||||
|
||||
if (read < count || buf_max == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pos - start;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 write(const void* buffer, u64 size) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, seek_mode whence) override
|
||||
{
|
||||
const s64 new_pos =
|
||||
whence == fs::seek_set ? offset :
|
||||
whence == fs::seek_cur ? offset + pos :
|
||||
whence == fs::seek_end ? offset + end :
|
||||
(fmt::raw_error("fs::gather_stream::seek(): invalid whence"), 0);
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = new_pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return end;
|
||||
}
|
||||
};
|
||||
|
||||
fs::file result;
|
||||
result.reset(std::make_unique<gather_stream>(std::move(files)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
|
||||
+21
-37
@@ -25,8 +25,7 @@ namespace fs
|
||||
create,
|
||||
trunc,
|
||||
excl,
|
||||
lock,
|
||||
unread,
|
||||
unshare,
|
||||
|
||||
__bitset_enum_max
|
||||
};
|
||||
@@ -37,8 +36,7 @@ namespace fs
|
||||
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
|
||||
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
|
||||
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
|
||||
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
|
||||
constexpr auto unshare = +open_mode::unshare; // Prevent opening the file twice
|
||||
|
||||
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||
|
||||
@@ -65,11 +63,10 @@ namespace fs
|
||||
s64 ctime;
|
||||
};
|
||||
|
||||
// Helper, layout is equal to iovec struct
|
||||
struct iovec_clone
|
||||
// Native handle getter
|
||||
struct get_native_handle
|
||||
{
|
||||
const void* iov_base;
|
||||
std::size_t iov_len;
|
||||
virtual native_handle get() = 0;
|
||||
};
|
||||
|
||||
// File handle base
|
||||
@@ -84,8 +81,6 @@ namespace fs
|
||||
virtual u64 write(const void* buffer, u64 size) = 0;
|
||||
virtual u64 seek(s64 offset, seek_mode whence) = 0;
|
||||
virtual u64 size() = 0;
|
||||
virtual native_handle get_handle();
|
||||
virtual u64 write_gather(const iovec_clone* buffers, u64 buf_count);
|
||||
};
|
||||
|
||||
// Directory entry (TODO)
|
||||
@@ -190,7 +185,7 @@ namespace fs
|
||||
file() = default;
|
||||
|
||||
// Open file with specified mode
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
|
||||
// Open memory for read
|
||||
explicit file(const void* ptr, std::size_t size);
|
||||
@@ -369,18 +364,6 @@ namespace fs
|
||||
|
||||
// Get native handle if available
|
||||
native_handle get_handle() const;
|
||||
|
||||
// Gathered write
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->write_gather(buffers, buf_count);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows-specific function
|
||||
bool set_delete(bool autodelete = true) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
class dir final
|
||||
@@ -417,7 +400,7 @@ namespace fs
|
||||
{
|
||||
m_dir = std::move(ptr);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<dir_base> release()
|
||||
{
|
||||
return std::move(m_dir);
|
||||
@@ -439,7 +422,7 @@ namespace fs
|
||||
|
||||
class iterator
|
||||
{
|
||||
const dir* m_parent;
|
||||
dir* m_parent;
|
||||
dir_entry m_entry;
|
||||
|
||||
public:
|
||||
@@ -449,7 +432,7 @@ namespace fs
|
||||
from_current
|
||||
};
|
||||
|
||||
iterator(const dir* parent, mode mode_ = mode::from_first)
|
||||
iterator(dir* parent, mode mode_ = mode::from_first)
|
||||
: m_parent(parent)
|
||||
{
|
||||
if (!m_parent)
|
||||
@@ -475,7 +458,7 @@ namespace fs
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
*this = {m_parent, mode::from_current};
|
||||
*this = { m_parent, mode::from_current };
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -485,25 +468,28 @@ namespace fs
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() const
|
||||
iterator begin()
|
||||
{
|
||||
return {m_dir ? this : nullptr};
|
||||
return{ m_dir ? this : nullptr };
|
||||
}
|
||||
|
||||
iterator end() const
|
||||
iterator end()
|
||||
{
|
||||
return {nullptr};
|
||||
return{ nullptr };
|
||||
}
|
||||
};
|
||||
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
|
||||
// Get common cache directory
|
||||
const std::string& get_cache_dir();
|
||||
// Get data/cache directory for specified prefix and suffix
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix);
|
||||
|
||||
// Get data/cache directory for specified prefix and path (suffix will be filename)
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& path);
|
||||
|
||||
// Delete directory and all its contents recursively
|
||||
bool remove_all(const std::string& path, bool remove_root = true);
|
||||
void remove_all(const std::string& path, bool remove_root = true);
|
||||
|
||||
// Get size of all files recursively
|
||||
u64 get_dir_size(const std::string& path);
|
||||
@@ -631,12 +617,10 @@ namespace fs
|
||||
if (fs::file f{path, mode})
|
||||
{
|
||||
// Write args sequentially
|
||||
(f.write(args), ...);
|
||||
int seq[]{ (f.write(args), 0)... };
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
file make_gather(std::vector<file>);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "GDBDebugServer.h"
|
||||
#include "Log.h"
|
||||
#include <algorithm>
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
@@ -13,13 +13,13 @@
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "fcntl.h"
|
||||
#include"fcntl.h"
|
||||
#endif
|
||||
|
||||
extern void ppu_set_breakpoint(u32 addr);
|
||||
extern void ppu_remove_breakpoint(u32 addr);
|
||||
|
||||
LOG_CHANNEL(gdbDebugServer);
|
||||
logs::channel gdbDebugServer("gdbDebugServer");
|
||||
|
||||
int sock_init(void)
|
||||
{
|
||||
@@ -166,10 +166,9 @@ char GDBDebugServer::read_char()
|
||||
|
||||
u8 GDBDebugServer::read_hexbyte()
|
||||
{
|
||||
std::string s = "";
|
||||
s += read_char();
|
||||
s += read_char();
|
||||
return hex_to_u8(s);
|
||||
char buf[2];
|
||||
read(buf, 2);
|
||||
return static_cast<u8>(strtol(buf, nullptr, 16));
|
||||
}
|
||||
|
||||
void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
@@ -177,7 +176,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
char c = read_char();
|
||||
//interrupt
|
||||
if (UNLIKELY(c == 0x03)) {
|
||||
out_cmd.cmd = '\x03';
|
||||
out_cmd.cmd = "\0x03";
|
||||
out_cmd.data = "";
|
||||
out_cmd.checksum = 0;
|
||||
return;
|
||||
@@ -224,7 +223,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
}
|
||||
out_cmd.checksum = read_hexbyte();
|
||||
if (out_cmd.checksum != checksum) {
|
||||
throw wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
throw new wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +337,6 @@ bool GDBDebugServer::select_thread(u64 id)
|
||||
selected_thread = ppu.ptr;
|
||||
return true;
|
||||
}
|
||||
gdbDebugServer.warning("Unable to select thread! Is the emulator running?");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -354,7 +352,7 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
case 65:
|
||||
return std::string(16, 'x');
|
||||
case 66:
|
||||
return u32_to_padded_hex(thread->cr.pack());
|
||||
return u32_to_padded_hex(thread->cr_pack());
|
||||
case 67:
|
||||
return u64_to_padded_hex(thread->lr);
|
||||
case 68:
|
||||
@@ -367,8 +365,8 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
return std::string(8, 'x');
|
||||
default:
|
||||
if (rid > 70) return "";
|
||||
return (rid > 31)
|
||||
? u64_to_padded_hex(std::bit_cast<u64>(thread->fpr[rid - 32])) //fpr
|
||||
return (rid > 31)
|
||||
? u64_to_padded_hex(reinterpret_cast<u64&>(thread->fpr[rid - 32])) //fpr
|
||||
: u64_to_padded_hex(thread->gpr[rid]); //gpr
|
||||
}
|
||||
}
|
||||
@@ -383,7 +381,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
|
||||
case 65:
|
||||
return true;
|
||||
case 66:
|
||||
thread->cr.unpack(hex_to_u32(value));
|
||||
thread->cr_unpack(hex_to_u32(value));
|
||||
return true;
|
||||
case 67:
|
||||
thread->lr = hex_to_u64(value);
|
||||
@@ -401,7 +399,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
|
||||
if (rid > 70) return false;
|
||||
if (rid > 31) {
|
||||
u64 val = hex_to_u64(value);
|
||||
thread->fpr[rid - 32] = std::bit_cast<f64>(val);
|
||||
thread->fpr[rid - 32] = reinterpret_cast<f64&>(val);
|
||||
} else {
|
||||
thread->gpr[rid] = hex_to_u64(value);
|
||||
}
|
||||
@@ -429,25 +427,6 @@ bool GDBDebugServer::send_reason()
|
||||
return send_cmd_ack("S05");
|
||||
}
|
||||
|
||||
void GDBDebugServer::wait_with_interrupts() {
|
||||
char c;
|
||||
while (!paused) {
|
||||
int result = recv(client_socket, &c, 1, 0);
|
||||
|
||||
if (result == SOCKET_ERROR) {
|
||||
if (check_errno_again()) {
|
||||
thread_ctrl::wait_for(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
gdbDebugServer.error("Error during socket read");
|
||||
fmt::throw_exception("Error during socket read" HERE);
|
||||
} else if (c == 0x03) {
|
||||
paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_extended_mode(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack("OK");
|
||||
@@ -474,25 +453,24 @@ bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
|
||||
result += u64_to_padded_hex(static_cast<u64>(cpu.id));
|
||||
};
|
||||
idm::select<ppu_thread>(on_select);
|
||||
//idm::select<RawSPUThread>(on_select);
|
||||
//idm::select<SPUThread>(on_select);
|
||||
idm::select<ARMv7Thread>(on_select);
|
||||
idm::select<RawSPUThread>(on_select);
|
||||
idm::select<SPUThread>(on_select);
|
||||
|
||||
//todo: this may exceed max command length
|
||||
result = "m" + result + "l";
|
||||
|
||||
|
||||
return send_cmd_ack(result);;
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_current_thread(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : ("QC" + u64_to_padded_hex(selected_thread.lock()->id)));
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : u64_to_padded_hex(selected_thread.lock()->id));
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
{
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
select_thread(general_ops_thread_id);
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -510,9 +488,7 @@ bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
|
||||
bool GDBDebugServer::cmd_write_register(gdb_cmd & cmd)
|
||||
{
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
select_thread(general_ops_thread_id);
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -541,7 +517,7 @@ bool GDBDebugServer::cmd_read_memory(gdb_cmd & cmd)
|
||||
std::string result;
|
||||
result.reserve(len * 2);
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr)) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
result += to_hexbyte(vm::read8(addr + i));
|
||||
} else {
|
||||
break;
|
||||
@@ -567,7 +543,7 @@ bool GDBDebugServer::cmd_write_memory(gdb_cmd & cmd)
|
||||
u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
|
||||
const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i, 1, vm::page_writable)) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
u8 val;
|
||||
int res = sscanf_s(data_ptr, "%02hhX", &val);
|
||||
if (!res) {
|
||||
@@ -658,31 +634,30 @@ bool GDBDebugServer::cmd_vcont(gdb_cmd & cmd)
|
||||
{
|
||||
//todo: handle multiple actions and thread ids
|
||||
this->from_breakpoint = false;
|
||||
if (cmd.data[1] == 'c' || cmd.data[1] == 's') {
|
||||
if (cmd.data[1] == 'c') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
paused = false;
|
||||
if (cmd.data[1] == 's') {
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
}
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
//special case if app didn't start yet (only loaded)
|
||||
if (!Emu.IsPaused() && !Emu.IsRunning()) {
|
||||
Emu.Run();
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
return send_reason();
|
||||
} else if (cmd.data[1] == 's') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
} else {
|
||||
ppu->notify();
|
||||
}
|
||||
wait_with_interrupts();
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
select_thread(pausedBy);
|
||||
// we have to remove dbg_pause from thread that paused execution, otherwise
|
||||
// it will be paused forever (Emu.Resume only removes dbg_global_pause)
|
||||
ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
return send_reason();
|
||||
}
|
||||
return send_cmd_ack("");
|
||||
@@ -754,9 +729,7 @@ void GDBDebugServer::on_task()
|
||||
return;
|
||||
}
|
||||
//stop immediately
|
||||
if (Emu.IsRunning()) {
|
||||
Emu.Pause();
|
||||
}
|
||||
Emu.Pause();
|
||||
|
||||
try {
|
||||
char hostbuf[32];
|
||||
@@ -795,7 +768,7 @@ void GDBDebugServer::on_task()
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
if (client_socket) {
|
||||
closesocket(client_socket);
|
||||
@@ -829,16 +802,7 @@ void GDBDebugServer::on_stop()
|
||||
this->stop = true;
|
||||
//just in case we are waiting for breakpoint
|
||||
this->notify();
|
||||
old_thread::on_stop();
|
||||
}
|
||||
|
||||
void GDBDebugServer::pause_from(cpu_thread* t) {
|
||||
if (paused) {
|
||||
return;
|
||||
}
|
||||
paused = true;
|
||||
pausedBy = t->id;
|
||||
notify();
|
||||
named_thread::on_stop();
|
||||
}
|
||||
|
||||
u32 g_gdb_debugger_id = 0;
|
||||
|
||||
+15
-11
@@ -40,8 +40,8 @@ public:
|
||||
const u64 ALL_THREADS = 0xffffffffffffffff;
|
||||
const u64 ANY_THREAD = 0;
|
||||
|
||||
class GDBDebugServer
|
||||
{
|
||||
class GDBDebugServer : public named_thread {
|
||||
|
||||
socket_t server_socket;
|
||||
socket_t client_socket;
|
||||
std::weak_ptr<cpu_thread> selected_thread;
|
||||
@@ -50,7 +50,7 @@ class GDBDebugServer
|
||||
|
||||
//initialize server socket and start listening
|
||||
void start_server();
|
||||
//read at most cnt bytes to buf, returns number of bytes actually read
|
||||
//read at most cnt bytes to buf, returns nubmer of bytes actually read
|
||||
int read(void* buf, int cnt);
|
||||
//reads one character
|
||||
char read_char();
|
||||
@@ -60,8 +60,8 @@ class GDBDebugServer
|
||||
void try_read_cmd(gdb_cmd& out_cmd);
|
||||
//reads commands until receiveing one with valid checksum
|
||||
//in case of other exception (i.e. wrong first char of command)
|
||||
//it will log exception text and return false
|
||||
//in that case best for caller would be to stop reading, because
|
||||
//it will log exception text and return false
|
||||
//in that case best for caller would be to stop reading, because
|
||||
//chance of getting correct command is low
|
||||
bool read_cmd(gdb_cmd& out_cmd);
|
||||
//send cnt bytes from buf to client
|
||||
@@ -90,8 +90,6 @@ class GDBDebugServer
|
||||
//send reason of stop, returns false if sending response failed
|
||||
bool send_reason();
|
||||
|
||||
void wait_with_interrupts();
|
||||
|
||||
//commands
|
||||
bool cmd_extended_mode(gdb_cmd& cmd);
|
||||
bool cmd_reason(gdb_cmd& cmd);
|
||||
@@ -112,14 +110,20 @@ class GDBDebugServer
|
||||
bool cmd_set_breakpoint(gdb_cmd& cmd);
|
||||
bool cmd_remove_breakpoint(gdb_cmd& cmd);
|
||||
|
||||
protected:
|
||||
void on_task() override final;
|
||||
void on_exit() override final;
|
||||
|
||||
public:
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 0x100000;
|
||||
|
||||
bool from_breakpoint = true;
|
||||
bool stop = false;
|
||||
bool paused = false;
|
||||
u64 pausedBy;
|
||||
|
||||
void operator()();
|
||||
void pause_from(cpu_thread* t);
|
||||
virtual std::string get_name() const;
|
||||
virtual void on_stop() override final;
|
||||
};
|
||||
|
||||
extern u32 g_gdb_debugger_id;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma push_macro("new")
|
||||
#undef new
|
||||
#include <gsl/gsl>
|
||||
#include <gsl.h>
|
||||
#pragma pop_macro("new")
|
||||
#undef Expects
|
||||
#undef Ensures
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template<typename T1, typename T2> struct range_t
|
||||
{
|
||||
T1 _min; // first value
|
||||
|
||||
+66
-405
@@ -1,232 +1,3 @@
|
||||
#include "types.h"
|
||||
#include "JIT.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "sysinfo.h"
|
||||
#include "VirtualMemory.h"
|
||||
#include <immintrin.h>
|
||||
|
||||
// Memory manager mutex
|
||||
shared_mutex s_mutex2;
|
||||
|
||||
#ifdef __linux__
|
||||
#define CAN_OVERCOMMIT
|
||||
#endif
|
||||
|
||||
static u8* get_jit_memory()
|
||||
{
|
||||
// Reserve 2G memory (magic static)
|
||||
static void* const s_memory2 = []() -> void*
|
||||
{
|
||||
void* ptr = utils::memory_reserve(0x80000000);
|
||||
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
utils::memory_commit(ptr, 0x80000000);
|
||||
utils::memory_protect(ptr, 0x40000000, utils::protection::wx);
|
||||
#endif
|
||||
return ptr;
|
||||
}();
|
||||
|
||||
return static_cast<u8*>(s_memory2);
|
||||
}
|
||||
|
||||
// Allocation counters (1G code, 1G data subranges)
|
||||
static atomic_t<u64> s_code_pos{0}, s_data_pos{0};
|
||||
|
||||
// Snapshot of code generated before main()
|
||||
static std::vector<u8> s_code_init, s_data_init;
|
||||
|
||||
template <atomic_t<u64>& Ctr, uint Off, utils::protection Prot>
|
||||
static u8* add_jit_memory(std::size_t size, uint align)
|
||||
{
|
||||
// Select subrange
|
||||
u8* pointer = get_jit_memory() + Off;
|
||||
|
||||
if (UNLIKELY(!size && !align))
|
||||
{
|
||||
// Return subrange info
|
||||
return pointer;
|
||||
}
|
||||
|
||||
#ifndef CAN_OVERCOMMIT
|
||||
std::lock_guard lock(s_mutex2);
|
||||
#endif
|
||||
|
||||
u64 olda, newa;
|
||||
|
||||
// Simple allocation by incrementing pointer to the next free data
|
||||
const u64 pos = Ctr.atomic_op([&](u64& ctr) -> u64
|
||||
{
|
||||
const u64 _pos = ::align(ctr, align);
|
||||
const u64 _new = ::align(_pos + size, align);
|
||||
|
||||
if (UNLIKELY(_new > 0x40000000))
|
||||
{
|
||||
// Sorry, we failed, and further attempts should fail too.
|
||||
ctr = 0x40000000;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check the necessity to commit more memory
|
||||
olda = ::align(ctr, 0x10000);
|
||||
newa = ::align(_new, 0x10000);
|
||||
|
||||
ctr = _new;
|
||||
return _pos;
|
||||
});
|
||||
|
||||
if (UNLIKELY(pos == -1))
|
||||
{
|
||||
LOG_WARNING(GENERAL, "JIT: Out of memory (size=0x%x, align=0x%x, off=0x%x)", size, align, Off);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (UNLIKELY(olda != newa))
|
||||
{
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
// TODO: possibly madvise
|
||||
#else
|
||||
// Commit more memory
|
||||
utils::memory_commit(pointer + olda, newa - olda, Prot);
|
||||
#endif
|
||||
}
|
||||
|
||||
return pointer + pos;
|
||||
}
|
||||
|
||||
jit_runtime::jit_runtime()
|
||||
: HostRuntime()
|
||||
{
|
||||
}
|
||||
|
||||
jit_runtime::~jit_runtime()
|
||||
{
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept
|
||||
{
|
||||
std::size_t codeSize = code->getCodeSize();
|
||||
if (UNLIKELY(!codeSize))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoCodeGenerated;
|
||||
}
|
||||
|
||||
void* p = jit_runtime::alloc(codeSize, 16);
|
||||
if (UNLIKELY(!p))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoVirtualMemory;
|
||||
}
|
||||
|
||||
std::size_t relocSize = code->relocate(p);
|
||||
if (UNLIKELY(!relocSize))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorInvalidState;
|
||||
}
|
||||
|
||||
flush(p, relocSize);
|
||||
*dst = p;
|
||||
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_release(void* ptr) noexcept
|
||||
{
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
u8* jit_runtime::alloc(std::size_t size, uint align, bool exec) noexcept
|
||||
{
|
||||
if (exec)
|
||||
{
|
||||
return add_jit_memory<s_code_pos, 0x0, utils::protection::wx>(size, align);
|
||||
}
|
||||
else
|
||||
{
|
||||
return add_jit_memory<s_data_pos, 0x40000000, utils::protection::rw>(size, align);
|
||||
}
|
||||
}
|
||||
|
||||
void jit_runtime::initialize()
|
||||
{
|
||||
if (!s_code_init.empty() || !s_data_init.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create code/data snapshot
|
||||
s_code_init.resize(s_code_pos);
|
||||
std::memcpy(s_code_init.data(), alloc(0, 0, true), s_code_pos);
|
||||
s_data_init.resize(s_data_pos);
|
||||
std::memcpy(s_data_init.data(), alloc(0, 0, false), s_data_pos);
|
||||
}
|
||||
|
||||
void jit_runtime::finalize() noexcept
|
||||
{
|
||||
// Reset JIT memory
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
utils::memory_reset(get_jit_memory(), 0x80000000);
|
||||
utils::memory_protect(get_jit_memory(), 0x40000000, utils::protection::wx);
|
||||
#else
|
||||
utils::memory_decommit(get_jit_memory(), 0x80000000);
|
||||
#endif
|
||||
|
||||
s_code_pos = 0;
|
||||
s_data_pos = 0;
|
||||
|
||||
// Restore code/data snapshot
|
||||
std::memcpy(alloc(s_code_init.size(), 1, true), s_code_init.data(), s_code_init.size());
|
||||
std::memcpy(alloc(s_data_init.size(), 1, false), s_data_init.data(), s_data_init.size());
|
||||
}
|
||||
|
||||
asmjit::JitRuntime& asmjit::get_global_runtime()
|
||||
{
|
||||
// Magic static
|
||||
static asmjit::JitRuntime g_rt;
|
||||
return g_rt;
|
||||
}
|
||||
|
||||
void asmjit::build_transaction_enter(asmjit::X86Assembler& c, asmjit::Label fallback, const asmjit::X86Gp& ctr, uint less_than)
|
||||
{
|
||||
Label fall = c.newLabel();
|
||||
Label begin = c.newLabel();
|
||||
c.jmp(begin);
|
||||
c.bind(fall);
|
||||
|
||||
if (less_than < 65)
|
||||
{
|
||||
c.add(ctr, 1);
|
||||
c.test(x86::eax, _XABORT_RETRY);
|
||||
c.jz(fallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Count an attempt without RETRY flag as 65 normal attempts and continue
|
||||
c.not_(x86::eax);
|
||||
c.and_(x86::eax, _XABORT_RETRY);
|
||||
c.shl(x86::eax, 5);
|
||||
c.add(x86::eax, 1); // eax = RETRY ? 1 : 65
|
||||
c.add(ctr, x86::rax);
|
||||
}
|
||||
|
||||
c.cmp(ctr, less_than);
|
||||
c.jae(fallback);
|
||||
c.align(kAlignCode, 16);
|
||||
c.bind(begin);
|
||||
c.xbegin(fall);
|
||||
}
|
||||
|
||||
void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code)
|
||||
{
|
||||
c.db(0xc6);
|
||||
c.db(0xf8);
|
||||
c.db(code);
|
||||
}
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -236,6 +7,14 @@ void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
||||
#include "types.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "sysinfo.h"
|
||||
#include "VirtualMemory.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
@@ -251,10 +30,10 @@ void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "JIT.h"
|
||||
|
||||
// Memory manager mutex
|
||||
shared_mutex s_mutex;
|
||||
|
||||
@@ -266,14 +45,8 @@ static void* const s_memory = []() -> void*
|
||||
{
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
llvm::InitializeNativeTargetAsmParser();
|
||||
LLVMLinkInMCJIT();
|
||||
|
||||
#ifdef MAP_32BIT
|
||||
auto ptr = ::mmap(nullptr, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_32BIT, -1, 0);
|
||||
if (ptr != MAP_FAILED)
|
||||
return ptr;
|
||||
#else
|
||||
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
||||
{
|
||||
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
||||
@@ -281,7 +54,6 @@ static void* const s_memory = []() -> void*
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return utils::memory_reserve(s_memory_size);
|
||||
}();
|
||||
@@ -291,8 +63,6 @@ static void* s_next = s_memory;
|
||||
#ifdef _WIN32
|
||||
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
|
||||
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
||||
#else
|
||||
static std::deque<std::pair<u8*, std::size_t>> s_unfire;
|
||||
#endif
|
||||
|
||||
// Reset memory manager
|
||||
@@ -309,12 +79,7 @@ extern void jit_finalize()
|
||||
|
||||
s_unwind.clear();
|
||||
#else
|
||||
for (auto&& t : s_unfire)
|
||||
{
|
||||
llvm::RTDyldMemoryManager::deregisterEHFramesInProcess(t.first, t.second);
|
||||
}
|
||||
|
||||
s_unfire.clear();
|
||||
// TODO: unregister EH frames if necessary
|
||||
#endif
|
||||
|
||||
utils::memory_decommit(s_memory, s_memory_size);
|
||||
@@ -365,7 +130,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Allocate memory for trampolines
|
||||
if (!m_tramps)
|
||||
@@ -398,10 +163,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
@@ -419,10 +184,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return (u8*)std::exchange(s_next, (void*)next);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
@@ -435,6 +200,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
|
||||
if (!is_ro)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size);
|
||||
@@ -443,10 +209,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return (u8*)std::exchange(s_next, (void*)next);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
virtual bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// TODO: make only read-only sections read-only
|
||||
//#ifdef _WIN32
|
||||
@@ -460,11 +226,11 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
||||
@@ -487,97 +253,16 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
{
|
||||
s_unwind.emplace_back(std::move(pdata));
|
||||
}
|
||||
#else
|
||||
s_unfire.push_front(std::make_pair(addr, size));
|
||||
#endif
|
||||
|
||||
return RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
|
||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
virtual void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
LOG_ERROR(GENERAL, "deregisterEHFrames() called"); // Not expected
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager2 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
MemoryManager2() = default;
|
||||
|
||||
~MemoryManager2() override
|
||||
{
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, true);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, false);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
#ifndef _WIN32
|
||||
RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
|
||||
s_unfire.push_front(std::make_pair(addr, size));
|
||||
#endif
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager. I promise there will be no MemoryManager4.
|
||||
struct MemoryManager3 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
std::vector<std::pair<u8*, std::size_t>> allocs;
|
||||
|
||||
MemoryManager3() = default;
|
||||
|
||||
~MemoryManager3() override
|
||||
{
|
||||
for (auto& a : allocs)
|
||||
{
|
||||
utils::memory_release(a.first, a.second);
|
||||
}
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
u8* r = static_cast<u8*>(utils::memory_reserve(size));
|
||||
utils::memory_commit(r, size, utils::protection::wx);
|
||||
allocs.emplace_back(r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
u8* r = static_cast<u8*>(utils::memory_reserve(size));
|
||||
utils::memory_commit(r, size);
|
||||
allocs.emplace_back(r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
return RTDyldMemoryManager::deregisterEHFrames();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -591,7 +276,7 @@ struct EventListener : llvm::JITEventListener
|
||||
{
|
||||
}
|
||||
|
||||
void notifyObjectLoaded(ObjectKey K, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
|
||||
@@ -619,7 +304,7 @@ struct EventListener : llvm::JITEventListener
|
||||
}
|
||||
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 code_diff = (u64)m_mem.m_code_addr - (u64)s_memory;
|
||||
@@ -656,15 +341,15 @@ public:
|
||||
std::string name = m_path;
|
||||
name.append(module->getName());
|
||||
fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||
LOG_NOTICE(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
}
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
{
|
||||
if (fs::file cached{path, fs::read})
|
||||
{
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(buf->getBufferStart(), buf->getBufferSize());
|
||||
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -678,7 +363,7 @@ public:
|
||||
|
||||
if (auto buf = load(path))
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -686,10 +371,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::string jit_compiler::cpu(const std::string& _cpu)
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu)
|
||||
: m_link(std::move(_link))
|
||||
, m_cpu(std::move(_cpu))
|
||||
{
|
||||
std::string m_cpu = _cpu;
|
||||
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName();
|
||||
@@ -700,63 +385,24 @@ std::string jit_compiler::cpu(const std::string& _cpu)
|
||||
m_cpu == "broadwell" ||
|
||||
m_cpu == "skylake" ||
|
||||
m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server")
|
||||
m_cpu == "cannonlake")
|
||||
{
|
||||
// Downgrade if AVX is not supported by some chips
|
||||
if (!utils::has_avx())
|
||||
{
|
||||
m_cpu = "nehalem";
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server")
|
||||
{
|
||||
// Downgrade if AVX-512 is disabled or not supported
|
||||
if (!utils::has_512())
|
||||
{
|
||||
m_cpu = "skylake";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_cpu;
|
||||
}
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
|
||||
: m_link(_link)
|
||||
, m_cpu(cpu(_cpu))
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (m_link.empty())
|
||||
{
|
||||
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
|
||||
|
||||
if (flags & 0x1)
|
||||
{
|
||||
mem = std::make_unique<MemoryManager3>();
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager2>();
|
||||
}
|
||||
|
||||
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null_", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
@@ -768,10 +414,9 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
||||
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
|
||||
@@ -808,19 +453,6 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> module)
|
||||
{
|
||||
const auto ptr = module.get();
|
||||
m_engine->addModule(std::move(module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(const std::string& path)
|
||||
{
|
||||
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
|
||||
@@ -836,4 +468,33 @@ u64 jit_compiler::get(const std::string& name)
|
||||
return m_engine->getGlobalValueAddress(name);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, u64> jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
std::unordered_map<std::string, u64> result;
|
||||
|
||||
std::size_t size = 0;
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
size += ::align(pair.second.size(), 16);
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||
std::memset(s_next, 0xc3, ::align(size, 4096));
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
std::memcpy(s_next, pair.second.data(), pair.second.size());
|
||||
result.emplace(pair.first, (u64)s_next);
|
||||
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
|
||||
}
|
||||
|
||||
s_next = (void*)::align((u64)s_next, 4096);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+8
-98
@@ -1,97 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#define ASMJIT_EMBED
|
||||
#define ASMJIT_DEBUG
|
||||
|
||||
#include <asmjit/asmjit.h>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
enum class jit_class
|
||||
{
|
||||
ppu_code,
|
||||
ppu_data,
|
||||
spu_code,
|
||||
spu_data,
|
||||
};
|
||||
|
||||
// ASMJIT runtime for emitting code in a single 2G region
|
||||
struct jit_runtime final : asmjit::HostRuntime
|
||||
{
|
||||
jit_runtime();
|
||||
~jit_runtime() override;
|
||||
|
||||
// Allocate executable memory
|
||||
asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override;
|
||||
|
||||
// Do nothing (deallocation is delayed)
|
||||
asmjit::Error _release(void* p) noexcept override;
|
||||
|
||||
// Allocate memory
|
||||
static u8* alloc(std::size_t size, uint align, bool exec = true) noexcept;
|
||||
|
||||
// Should be called at least once after global initialization
|
||||
static void initialize();
|
||||
|
||||
// Deallocate all memory
|
||||
static void finalize() noexcept;
|
||||
};
|
||||
|
||||
namespace asmjit
|
||||
{
|
||||
// Should only be used to build global functions
|
||||
asmjit::JitRuntime& get_global_runtime();
|
||||
|
||||
// Emit xbegin and adjacent loop, return label at xbegin
|
||||
void build_transaction_enter(X86Assembler& c, Label fallback, const X86Gp& ctr, uint less_than);
|
||||
|
||||
// Emit xabort
|
||||
void build_transaction_abort(X86Assembler& c, unsigned char code);
|
||||
}
|
||||
|
||||
// Build runtime function with asmjit::X86Assembler
|
||||
template <typename FT, typename F>
|
||||
FT build_function_asm(F&& builder)
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
auto& rt = get_global_runtime();
|
||||
|
||||
CodeHolder code;
|
||||
code.init(rt.getCodeInfo());
|
||||
code._globalHints = asmjit::CodeEmitter::kHintOptimizedAlign;
|
||||
|
||||
std::array<X86Gp, 4> args;
|
||||
#ifdef _WIN32
|
||||
args[0] = x86::rcx;
|
||||
args[1] = x86::rdx;
|
||||
args[2] = x86::r8;
|
||||
args[3] = x86::r9;
|
||||
#else
|
||||
args[0] = x86::rdi;
|
||||
args[1] = x86::rsi;
|
||||
args[2] = x86::rdx;
|
||||
args[3] = x86::rcx;
|
||||
#endif
|
||||
|
||||
X86Assembler compiler(&code);
|
||||
builder(std::ref(compiler), args);
|
||||
|
||||
FT result;
|
||||
|
||||
if (rt.add(&result, &code))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types.h"
|
||||
@@ -128,7 +40,7 @@ class jit_compiler final
|
||||
std::string m_cpu;
|
||||
|
||||
public:
|
||||
jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags = 0);
|
||||
jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu);
|
||||
~jit_compiler();
|
||||
|
||||
// Get LLVM context
|
||||
@@ -137,17 +49,9 @@ public:
|
||||
return m_context;
|
||||
}
|
||||
|
||||
auto& get_engine() const
|
||||
{
|
||||
return *m_engine;
|
||||
}
|
||||
|
||||
// Add module (path to obj cache dir)
|
||||
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
||||
|
||||
// Add module (not cached)
|
||||
void add(std::unique_ptr<llvm::Module> module);
|
||||
|
||||
// Add object (path to obj file)
|
||||
void add(const std::string& path);
|
||||
|
||||
@@ -157,8 +61,14 @@ public:
|
||||
// Get compiled function address
|
||||
u64 get(const std::string& name);
|
||||
|
||||
// Add functions directly to the memory manager (name -> code)
|
||||
static std::unordered_map<std::string, u64> add(std::unordered_map<std::string, std::string>);
|
||||
|
||||
// Get CPU info
|
||||
static std::string cpu(const std::string& _cpu);
|
||||
const std::string& cpu() const
|
||||
{
|
||||
return m_cpu;
|
||||
}
|
||||
|
||||
// Check JIT purpose
|
||||
bool is_primary() const
|
||||
|
||||
+64
-278
@@ -4,24 +4,16 @@
|
||||
#include "sema.h"
|
||||
|
||||
#include "Utilities/sysinfo.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "rpcs3_version.h"
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <chrono>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include <zlib.h>
|
||||
@@ -49,7 +41,6 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
||||
case logs::level::warning: return "Warning";
|
||||
case logs::level::notice: return "Notice";
|
||||
case logs::level::trace: return "Trace";
|
||||
case logs::level::_uninit: return unknown;
|
||||
}
|
||||
|
||||
return unknown;
|
||||
@@ -58,32 +49,23 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
||||
|
||||
namespace logs
|
||||
{
|
||||
// Memory-mapped buffer size
|
||||
constexpr u64 s_log_size = 32 * 1024 * 1024;
|
||||
constexpr std::size_t s_log_size = 128 * 1024 * 1024;
|
||||
constexpr std::size_t s_log_size_s = s_log_size / 2;
|
||||
constexpr std::size_t s_log_size_t = s_log_size / 4 + s_log_size_s;
|
||||
constexpr std::size_t s_log_size_e = s_log_size / 8 + s_log_size_t;
|
||||
constexpr std::size_t s_log_size_f = s_log_size / 16 + s_log_size_e;
|
||||
|
||||
class file_writer
|
||||
{
|
||||
fs::file m_file;
|
||||
std::string m_name;
|
||||
std::thread m_writer;
|
||||
fs::file m_fout;
|
||||
fs::file m_fout2;
|
||||
u64 m_max_size;
|
||||
|
||||
#ifdef _WIN32
|
||||
::HANDLE m_fmap;
|
||||
#endif
|
||||
atomic_t<std::size_t> m_pos{0};
|
||||
std::size_t m_size{0};
|
||||
uchar* m_fptr{};
|
||||
z_stream m_zs{};
|
||||
shared_mutex m_m;
|
||||
|
||||
alignas(128) atomic_t<u64> m_buf{0}; // MSB (40 bit): push begin, LSB (24 bis): push size
|
||||
alignas(128) atomic_t<u64> m_out{0}; // Amount of bytes written to file
|
||||
|
||||
uchar m_zout[65536];
|
||||
|
||||
// Write buffered logs immediately
|
||||
bool flush(u64 bufv);
|
||||
|
||||
public:
|
||||
file_writer(const std::string& name);
|
||||
@@ -137,7 +119,7 @@ namespace logs
|
||||
static file_listener* get_logger()
|
||||
{
|
||||
// Use magic static
|
||||
static file_listener logger("RPCS3");
|
||||
static file_listener logger("RPCS3.log");
|
||||
return &logger;
|
||||
}
|
||||
|
||||
@@ -181,16 +163,17 @@ namespace logs
|
||||
channel HLE("HLE");
|
||||
channel PPU("PPU");
|
||||
channel SPU("SPU");
|
||||
channel ARMv7("ARMv7");
|
||||
|
||||
// Channel registry mutex
|
||||
shared_mutex g_mutex;
|
||||
semaphore<> g_mutex;
|
||||
|
||||
// Must be set to true in main()
|
||||
atomic_t<bool> g_init{false};
|
||||
|
||||
void reset()
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
for (auto&& pair : get_logger()->channels)
|
||||
{
|
||||
@@ -200,7 +183,7 @@ namespace logs
|
||||
|
||||
void set_level(const std::string& ch_name, level value)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
get_logger()->channels[ch_name].set_level(value);
|
||||
}
|
||||
@@ -210,7 +193,7 @@ namespace logs
|
||||
{
|
||||
if (!g_init)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
get_logger()->messages.clear();
|
||||
g_init = true;
|
||||
}
|
||||
@@ -226,7 +209,7 @@ void logs::listener::add(logs::listener* _new)
|
||||
// Get first (main) listener
|
||||
listener* lis = get_logger();
|
||||
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
// Install new listener at the end of linked list
|
||||
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
|
||||
@@ -241,7 +224,7 @@ void logs::listener::add(logs::listener* _new)
|
||||
}
|
||||
}
|
||||
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) const
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
{
|
||||
// Get timestamp
|
||||
const u64 stamp = get_stamp();
|
||||
@@ -249,7 +232,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
|
||||
// Register channel
|
||||
if (ch->enabled == level::_uninit)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
auto& info = get_logger()->channels[ch->name];
|
||||
|
||||
@@ -270,23 +253,9 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
|
||||
}
|
||||
}
|
||||
|
||||
// Get text, extract va_args
|
||||
thread_local std::string text;
|
||||
thread_local std::vector<u64> args;
|
||||
|
||||
std::size_t args_count = 0;
|
||||
for (auto v = sup; v->fmt_string; v++)
|
||||
args_count++;
|
||||
|
||||
text.clear();
|
||||
args.resize(args_count);
|
||||
|
||||
va_list c_args;
|
||||
va_start(c_args, sup);
|
||||
for (u64& arg : args)
|
||||
arg = va_arg(c_args, u64);
|
||||
va_end(c_args);
|
||||
fmt::raw_append(text, fmt, sup, args.data());
|
||||
// Get text
|
||||
thread_local std::string text; text.clear();
|
||||
fmt::raw_append(text, fmt, sup, args);
|
||||
std::string prefix = g_tls_log_prefix();
|
||||
|
||||
// Get first (main) listener
|
||||
@@ -294,7 +263,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
|
||||
|
||||
if (!g_init)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
if (!g_init)
|
||||
{
|
||||
@@ -322,45 +291,13 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
|
||||
logs::file_writer::file_writer(const std::string& name)
|
||||
: m_name(name)
|
||||
{
|
||||
const std::string log_name = fs::get_cache_dir() + name + ".log";
|
||||
const std::string buf_name = fs::get_cache_dir() + name + ".buf";
|
||||
|
||||
try
|
||||
{
|
||||
if (!m_file.open(buf_name, fs::read + fs::rewrite + fs::lock))
|
||||
if (!m_file.open(fs::get_config_dir() + name, fs::read + fs::write + fs::create + fs::trunc + fs::unshare))
|
||||
{
|
||||
if (fs::g_tls_error == fs::error::acces)
|
||||
{
|
||||
if (fs::exists(buf_name))
|
||||
{
|
||||
fmt::throw_exception("Another instance of %s is running. Close it or kill its process, if necessary.", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Cannot create %s.log (access denied)."
|
||||
#ifdef _WIN32
|
||||
"\nNote that %s cannot be installed in Program Files or similar directory with limited permissions."
|
||||
#else
|
||||
"\nPlease, check %s permissions in '~/.config/'."
|
||||
#endif
|
||||
, name, name);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::throw_exception("Cannot create %s.log (error %s)", name, fs::g_tls_error);
|
||||
fmt::throw_exception("Can't create file %s (error %s)", name, fs::g_tls_error);
|
||||
}
|
||||
|
||||
// Check free space
|
||||
fs::device_stat stats{};
|
||||
if (!fs::statfs(fs::get_cache_dir(), stats) || stats.avail_free < s_log_size * 8)
|
||||
{
|
||||
fmt::throw_exception("Not enough free space (%f KB)", stats.avail_free / 1000000.);
|
||||
}
|
||||
|
||||
// Limit log size to ~25% of free space
|
||||
m_max_size = stats.avail_free / 4;
|
||||
|
||||
// Initialize memory mapped file
|
||||
#ifdef _WIN32
|
||||
m_fmap = CreateFileMappingW(m_file.get_handle(), 0, PAGE_READWRITE, s_log_size >> 32, s_log_size & 0xffffffff, 0);
|
||||
m_fptr = m_fmap ? (uchar*)MapViewOfFile(m_fmap, FILE_MAP_WRITE, 0, 0, 0) : nullptr;
|
||||
@@ -370,215 +307,79 @@ logs::file_writer::file_writer(const std::string& name)
|
||||
#endif
|
||||
|
||||
verify(name.c_str()), m_fptr;
|
||||
std::memset(m_fptr, '\n', s_log_size);
|
||||
|
||||
// Rotate backups (TODO)
|
||||
fs::remove_file(fs::get_cache_dir() + name + "1.log.gz");
|
||||
fs::create_dir(fs::get_cache_dir() + "old_logs");
|
||||
fs::rename(fs::get_cache_dir() + m_name + ".log.gz", fs::get_cache_dir() + "old_logs/" + m_name + ".log.gz", true);
|
||||
|
||||
// Actual log file (allowed to fail)
|
||||
m_fout.open(log_name, fs::rewrite);
|
||||
|
||||
// Compressed log, make it inaccessible (foolproof)
|
||||
if (!m_fout2.open(log_name + ".gz", fs::rewrite + fs::unread) || deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
{
|
||||
m_fout2.close();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Autodelete compressed log file
|
||||
m_fout2.set_delete();
|
||||
#endif
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::thread([text = std::string{e.what()}]{ report_fatal_error(text); }).detach();
|
||||
return;
|
||||
fs::remove_file(fs::get_config_dir() + name + "1.gz");
|
||||
fs::create_dir(fs::get_config_dir() + "old_logs");
|
||||
fs::rename(fs::get_config_dir() + m_name + ".gz", fs::get_config_dir() + "old_logs/" + m_name + ".gz", true);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::thread([]{ report_fatal_error("Unknown error" HERE); }).detach();
|
||||
return;
|
||||
catch_all_exceptions();
|
||||
}
|
||||
|
||||
m_writer = std::thread([this]()
|
||||
{
|
||||
thread_ctrl::set_native_priority(-1);
|
||||
|
||||
while (true)
|
||||
{
|
||||
const u64 bufv = m_buf;
|
||||
|
||||
if (bufv & 0xffffff)
|
||||
{
|
||||
// Wait if threads are writing logs
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!flush(bufv))
|
||||
{
|
||||
if (m_out == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logs::file_writer::~file_writer()
|
||||
{
|
||||
if (!m_fptr)
|
||||
if (m_size == 0)
|
||||
{
|
||||
return;
|
||||
m_size = std::min<std::size_t>(+m_pos, s_log_size);
|
||||
}
|
||||
|
||||
// Stop writer thread
|
||||
while (m_out << 24 < m_buf)
|
||||
// Compress
|
||||
z_stream zs{};
|
||||
if (deflateInit2(&zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) == Z_OK)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
auto buf = std::make_unique<uchar[]>(s_log_size);
|
||||
zs.avail_in = ::narrow<u32>(m_size);
|
||||
zs.avail_out = s_log_size;
|
||||
zs.next_in = m_fptr;
|
||||
zs.next_out = buf.get();
|
||||
|
||||
m_out = -1;
|
||||
m_writer.join();
|
||||
|
||||
if (m_fout2)
|
||||
{
|
||||
m_zs.avail_in = 0;
|
||||
m_zs.next_in = nullptr;
|
||||
|
||||
do
|
||||
if (deflate(&zs, Z_FINISH) != Z_STREAM_ERROR)
|
||||
{
|
||||
m_zs.avail_out = sizeof(m_zout);
|
||||
m_zs.next_out = m_zout;
|
||||
|
||||
if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||
{
|
||||
break;
|
||||
}
|
||||
fs::file(fs::get_config_dir() + m_name + ".gz", fs::rewrite).write(buf.get(), zs.total_out);
|
||||
}
|
||||
while (m_zs.avail_out == 0);
|
||||
|
||||
deflateEnd(&m_zs);
|
||||
if (deflateEnd(&zs) != Z_OK)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Cancel compressed log file autodeletion
|
||||
m_fout2.set_delete(false);
|
||||
|
||||
UnmapViewOfFile(m_fptr);
|
||||
CloseHandle(m_fmap);
|
||||
#else
|
||||
// Restore compressed log file permissions
|
||||
::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
::munmap(m_fptr, s_log_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool logs::file_writer::flush(u64 bufv)
|
||||
{
|
||||
std::lock_guard lock(m_m);
|
||||
|
||||
const u64 st = +m_out;
|
||||
const u64 end = std::min<u64>((st + s_log_size) & ~(s_log_size - 1), bufv >> 24);
|
||||
|
||||
if (end > st)
|
||||
{
|
||||
// Avoid writing too big fragments
|
||||
const u64 size = std::min<u64>(end - st, sizeof(m_zout) / 2);
|
||||
|
||||
// Write uncompressed
|
||||
if (m_fout && st < m_max_size && m_fout.write(m_fptr + st % s_log_size, size) != size)
|
||||
{
|
||||
m_fout.close();
|
||||
}
|
||||
|
||||
// Write compressed
|
||||
if (m_fout2 && st < m_max_size)
|
||||
{
|
||||
m_zs.avail_in = size;
|
||||
m_zs.next_in = m_fptr + st % s_log_size;
|
||||
|
||||
do
|
||||
{
|
||||
m_zs.avail_out = sizeof(m_zout);
|
||||
m_zs.next_out = m_zout;
|
||||
|
||||
if (deflate(&m_zs, Z_NO_FLUSH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||
{
|
||||
deflateEnd(&m_zs);
|
||||
m_fout2.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (m_zs.avail_out == 0);
|
||||
}
|
||||
|
||||
m_out += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
m_file.trunc(m_size);
|
||||
}
|
||||
|
||||
void logs::file_writer::log(logs::level sev, const char* text, std::size_t size)
|
||||
{
|
||||
if (!m_fptr)
|
||||
// Adaptive log limit
|
||||
const auto lim =
|
||||
sev >= logs::level::success ? s_log_size_s :
|
||||
sev == logs::level::todo ? s_log_size_t :
|
||||
sev == logs::level::error ? s_log_size_e : s_log_size_f;
|
||||
|
||||
if (m_pos >= lim)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: write bigger fragment directly in blocking manner
|
||||
while (size && size <= 0xffffff)
|
||||
// Acquire memory
|
||||
const auto pos = m_pos.fetch_add(size);
|
||||
|
||||
// Write if possible
|
||||
if (pos + size <= s_log_size)
|
||||
{
|
||||
u64 bufv;
|
||||
|
||||
const auto pos = m_buf.atomic_op([&](u64& v) -> uchar*
|
||||
{
|
||||
const u64 v1 = v >> 24;
|
||||
const u64 v2 = v & 0xffffff;
|
||||
|
||||
if (UNLIKELY(v2 + size > 0xffffff || v1 + v2 + size >= m_out + s_log_size))
|
||||
{
|
||||
bufv = v;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
v += size;
|
||||
return m_fptr + (v1 + v2) % s_log_size;
|
||||
});
|
||||
|
||||
if (UNLIKELY(!pos))
|
||||
{
|
||||
if ((bufv & 0xffffff) + size > 0xffffff || bufv & 0xffffff)
|
||||
{
|
||||
// Concurrency limit reached
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue is full, need to write out
|
||||
flush(bufv);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos + size > m_fptr + s_log_size)
|
||||
{
|
||||
const auto frag = m_fptr + s_log_size - pos;
|
||||
std::memcpy(pos, text, frag);
|
||||
std::memcpy(m_fptr, text + frag, size - frag);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(pos, text, size);
|
||||
}
|
||||
|
||||
m_buf += (u64{size} << 24) - size;
|
||||
break;
|
||||
std::memcpy(m_fptr + pos, text, size);
|
||||
}
|
||||
else if (pos <= s_log_size)
|
||||
{
|
||||
m_size = pos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,30 +390,16 @@ logs::file_listener::file_listener(const std::string& name)
|
||||
// Write UTF-8 BOM
|
||||
file_writer::log(logs::level::always, "\xEF\xBB\xBF", 3);
|
||||
|
||||
const std::string firmware_version = utils::get_firmware_version();
|
||||
const std::string firmware_string = firmware_version.empty() ? "" : (" | Firmware version: " + firmware_version);
|
||||
|
||||
// Write initial message
|
||||
stored_message ver;
|
||||
ver.m.ch = nullptr;
|
||||
ver.m.sev = level::always;
|
||||
ver.stamp = 0;
|
||||
ver.text = fmt::format("RPCS3 v%s | %s%s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), firmware_string, utils::get_system_info());
|
||||
ver.text = fmt::format("RPCS3 v%s | %s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), utils::get_system_info());
|
||||
|
||||
file_writer::log(logs::level::always, ver.text.data(), ver.text.size());
|
||||
file_writer::log(logs::level::always, "\n", 1);
|
||||
messages.emplace_back(std::move(ver));
|
||||
|
||||
// Write OS version
|
||||
stored_message os;
|
||||
os.m.ch = nullptr;
|
||||
os.m.sev = level::notice;
|
||||
os.stamp = 0;
|
||||
os.text = utils::get_OS_version();
|
||||
|
||||
file_writer::log(logs::level::notice, os.text.data(), os.text.size());
|
||||
file_writer::log(logs::level::notice, "\n", 1);
|
||||
messages.emplace_back(std::move(os));
|
||||
}
|
||||
|
||||
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
|
||||
@@ -630,17 +417,16 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
|
||||
case level::warning: text = u8"·W "; break;
|
||||
case level::notice: text = u8"·! "; break;
|
||||
case level::trace: text = u8"·T "; break;
|
||||
case level::_uninit: text = u8"· "; break;
|
||||
}
|
||||
|
||||
// Print µs timestamp
|
||||
// Print miscosecond timestamp
|
||||
const u64 hours = stamp / 3600'000'000;
|
||||
const u64 mins = (stamp % 3600'000'000) / 60'000'000;
|
||||
const u64 secs = (stamp % 60'000'000) / 1'000'000;
|
||||
const u64 frac = (stamp % 1'000'000);
|
||||
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
|
||||
|
||||
if (!prefix.empty())
|
||||
if (prefix.size() > 0)
|
||||
{
|
||||
text += "{";
|
||||
text += prefix;
|
||||
|
||||
+17
-16
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
#include "StrFmt.h"
|
||||
#include <climits>
|
||||
|
||||
@@ -23,17 +23,14 @@ namespace logs
|
||||
|
||||
struct channel;
|
||||
|
||||
// Message information
|
||||
// Message information (temporary data)
|
||||
struct message
|
||||
{
|
||||
channel* ch;
|
||||
level sev;
|
||||
|
||||
private:
|
||||
// Send log message to global logger instance
|
||||
void broadcast(const char*, const fmt_type_info*, ...) const;
|
||||
|
||||
friend struct channel;
|
||||
void broadcast(const char*, const fmt_type_info*, const u64*);
|
||||
};
|
||||
|
||||
class listener
|
||||
@@ -70,16 +67,21 @@ namespace logs
|
||||
{
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
template<typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void format(level sev, const char* fmt, const Args&... args)
|
||||
{
|
||||
if (UNLIKELY(sev <= enabled))
|
||||
{
|
||||
message{this, sev}.broadcast(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
#define GEN_LOG_METHOD(_sev)\
|
||||
const message msg_##_sev{this, level::_sev};\
|
||||
template <typename... Args>\
|
||||
void _sev(const char* fmt, const Args&... args)\
|
||||
template<typename... Args>\
|
||||
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args)\
|
||||
{\
|
||||
if (UNLIKELY(level::_sev <= enabled))\
|
||||
{\
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};\
|
||||
msg_##_sev.broadcast(fmt, type_list, u64{fmt_unveil<Args>::get(args)}...);\
|
||||
}\
|
||||
return format<Args...>(level::_sev, fmt, args...);\
|
||||
}
|
||||
|
||||
GEN_LOG_METHOD(fatal)
|
||||
@@ -102,6 +104,7 @@ namespace logs
|
||||
extern channel HLE;
|
||||
extern channel PPU;
|
||||
extern channel SPU;
|
||||
extern channel ARMv7;
|
||||
|
||||
// Log level control: set all channels to level::notice
|
||||
void reset();
|
||||
@@ -110,8 +113,6 @@ namespace logs
|
||||
void set_level(const std::string&, level);
|
||||
}
|
||||
|
||||
#define LOG_CHANNEL(ch, ...) ::logs::channel ch(#ch, ##__VA_ARGS__)
|
||||
|
||||
// Legacy:
|
||||
|
||||
#define LOG_SUCCESS(ch, fmt, ...) logs::ch.success("" fmt, ##__VA_ARGS__)
|
||||
|
||||
+13
-69
@@ -4,7 +4,6 @@
|
||||
#include "cfmt.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
@@ -24,81 +23,34 @@ void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string&
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<fmt::base57>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const auto& _arg = get_object(arg);
|
||||
|
||||
if (_arg.data && _arg.size)
|
||||
{
|
||||
// Precomputed tail sizes if input data is not multiple of 8
|
||||
static constexpr u8 s_tail[8] = {0, 2, 3, 5, 6, 7, 9, 10};
|
||||
|
||||
// Get full output size
|
||||
const std::size_t out_size = _arg.size / 8 * 11 + s_tail[_arg.size % 8];
|
||||
|
||||
out.resize(out.size() + out_size);
|
||||
|
||||
const auto ptr = &out.front() + (out.size() - out_size);
|
||||
|
||||
// Each 8 bytes of input data produce 11 bytes of base57 output
|
||||
for (std::size_t i = 0, p = 0; i < _arg.size; i += 8, p += 11)
|
||||
{
|
||||
// Load up to 8 bytes
|
||||
be_t<u64> be_value;
|
||||
|
||||
if (_arg.size - i < sizeof(be_value))
|
||||
{
|
||||
std::memset(&be_value, 0, sizeof(be_value));
|
||||
std::memcpy(&be_value, _arg.data + i, _arg.size - i);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(&be_value, _arg.data + i, sizeof(be_value));
|
||||
}
|
||||
|
||||
u64 value = be_value;
|
||||
|
||||
for (int j = 10; j >= 0; j--)
|
||||
{
|
||||
if (p + j < out_size)
|
||||
{
|
||||
ptr[p + j] = "0123456789ACEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"[value % 57];
|
||||
}
|
||||
|
||||
value /= 57;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%p", arg);
|
||||
if (arg)
|
||||
{
|
||||
fmt::append(out, "%p", reinterpret_cast<const void*>(static_cast<std::uintptr_t>(arg)));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
out += reinterpret_cast<const char*>(arg);
|
||||
out += reinterpret_cast<const char*>(static_cast<std::uintptr_t>(arg));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULLSTR)";
|
||||
out += "(NULL)";
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg);
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string_view>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg);
|
||||
out += get_object(arg).c_str(); // TODO?
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -177,13 +129,13 @@ void fmt_class_string<ullong>::format(std::string& out, u64 arg)
|
||||
template <>
|
||||
void fmt_class_string<float>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%gf", static_cast<float>(std::bit_cast<f64>(arg)));
|
||||
fmt::append(out, "%gf", static_cast<float>(reinterpret_cast<f64&>(arg)));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<double>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%g", std::bit_cast<f64>(arg));
|
||||
fmt::append(out, "%g", reinterpret_cast<f64&>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -423,14 +375,6 @@ std::string fmt::to_upper(const std::string& string)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fmt::to_lower(const std::string& string)
|
||||
{
|
||||
std::string result;
|
||||
result.resize(string.size());
|
||||
std::transform(string.begin(), string.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fmt::match(const std::string& source, const std::string& mask)
|
||||
{
|
||||
std::size_t source_position = 0, mask_position = 0;
|
||||
|
||||
+20
-33
@@ -62,7 +62,7 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
|
||||
// Convert FP to f64 and reinterpret as u64
|
||||
static inline u64 get(const f64& arg)
|
||||
{
|
||||
return std::bit_cast<u64>(arg);
|
||||
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,9 +91,9 @@ struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
template <typename T>
|
||||
struct fmt_unveil<T*, void>
|
||||
{
|
||||
using type = std::add_const_t<T>*;
|
||||
using type = const T*;
|
||||
|
||||
static inline u64 get(type arg)
|
||||
static inline u64 get(const T* arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
@@ -102,9 +102,9 @@ struct fmt_unveil<T*, void>
|
||||
template <typename T, std::size_t N>
|
||||
struct fmt_unveil<T[N], void>
|
||||
{
|
||||
using type = std::add_const_t<T>*;
|
||||
using type = const T*;
|
||||
|
||||
static inline u64 get(type arg)
|
||||
static inline u64 get(const T* arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
@@ -140,9 +140,18 @@ struct fmt_class_string
|
||||
// Enum -> string function type
|
||||
using convert_t = const char*(*)(T value);
|
||||
|
||||
// Enum -> string function registered
|
||||
static convert_t convert_enum;
|
||||
|
||||
// Helper function (safely converts arg to enum value)
|
||||
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
|
||||
{
|
||||
// Save convert function
|
||||
if (convert_enum == nullptr)
|
||||
{
|
||||
convert_enum = convert;
|
||||
}
|
||||
|
||||
const auto value = static_cast<std::underlying_type_t<T>>(arg);
|
||||
|
||||
// Check narrowing
|
||||
@@ -194,6 +203,9 @@ struct fmt_class_string
|
||||
static constexpr const char* unknown = nullptr;
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
const char*(*fmt_class_string<T, V>::convert_enum)(T) = nullptr;
|
||||
|
||||
template <>
|
||||
struct fmt_class_string<const void*, void>
|
||||
{
|
||||
@@ -238,38 +250,15 @@ using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
// Base-57 format helper
|
||||
struct base57
|
||||
{
|
||||
const uchar* data;
|
||||
std::size_t size;
|
||||
|
||||
template <typename T>
|
||||
base57(const T& arg)
|
||||
: data(reinterpret_cast<const uchar*>(&arg))
|
||||
, size(sizeof(T))
|
||||
{
|
||||
}
|
||||
|
||||
base57(const uchar* data, std::size_t size)
|
||||
: data(data)
|
||||
, size(size)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE const fmt_type_info* get_type_info()
|
||||
{
|
||||
// Constantly initialized null-terminated list of type-specific information
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<Args>()...};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr const fmt_type_info type_info_v[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
|
||||
// Internal formatting function
|
||||
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
|
||||
|
||||
@@ -277,8 +266,7 @@ namespace fmt
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void append(std::string& out, const char* fmt, const Args&... args)
|
||||
{
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_append(out, fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
raw_append(out, fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
@@ -298,7 +286,6 @@ namespace fmt
|
||||
template <typename T = std::runtime_error, typename... Args>
|
||||
[[noreturn]] SAFE_BUFFERS FORCE_INLINE void throw_exception(const char* fmt, const Args&... args)
|
||||
{
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_throw_exception<T>(fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
raw_throw_exception<T>(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -5,7 +5,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
// Copy null-terminated string from std::string to char array with truncation
|
||||
template <std::size_t N>
|
||||
@@ -25,12 +24,6 @@ inline void strcpy_trunc(char (&dst)[N], const char (&src)[N2])
|
||||
dst[count] = '\0';
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
inline bool ends_with(const std::string& src, const char (&end)[N])
|
||||
{
|
||||
return src.size() >= N - 1 && src.compare(src.size() - (N - 1), N - 1, end, N - 1) == 0;
|
||||
}
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
||||
@@ -101,10 +94,10 @@ namespace fmt
|
||||
auto end = source.end();
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
result += std::string{*it} + separator;
|
||||
result += *it + separator;
|
||||
}
|
||||
|
||||
return result + std::string{source.back()};
|
||||
return result + source.back();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -135,7 +128,6 @@ namespace fmt
|
||||
}
|
||||
|
||||
std::string to_upper(const std::string& string);
|
||||
std::string to_lower(const std::string& string);
|
||||
|
||||
bool match(const std::string& source, const std::string& mask);
|
||||
}
|
||||
|
||||
+314
-588
File diff suppressed because it is too large
Load Diff
+205
-312
@@ -1,115 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "sema.h"
|
||||
#include "cond.h"
|
||||
#include "lockless.h"
|
||||
|
||||
// Report error and call std::abort(), defined in main.cpp
|
||||
[[noreturn]] void report_fatal_error(const std::string&);
|
||||
|
||||
// Will report exception and call std::abort() if put in catch(...)
|
||||
[[noreturn]] void catch_all_exceptions();
|
||||
|
||||
// Hardware core layout
|
||||
enum class native_core_arrangement : u32
|
||||
// Simple list of void() functors
|
||||
class task_stack
|
||||
{
|
||||
undefined,
|
||||
generic,
|
||||
intel_ht,
|
||||
amd_ccx
|
||||
};
|
||||
|
||||
enum class thread_class : u32
|
||||
{
|
||||
general,
|
||||
rsx,
|
||||
spu,
|
||||
ppu
|
||||
};
|
||||
|
||||
enum class thread_state
|
||||
{
|
||||
created, // Initial state
|
||||
detached, // The thread has been detached to destroy its own named_thread object (can be dangerously misused)
|
||||
aborting, // The thread has been joined in the destructor or explicitly aborted (mutually exclusive with detached)
|
||||
finished // Final state, always set at the end of thread execution
|
||||
};
|
||||
|
||||
template <class Context>
|
||||
class named_thread;
|
||||
|
||||
template <typename T>
|
||||
struct result_storage
|
||||
{
|
||||
alignas(T) std::byte data[sizeof(T)];
|
||||
|
||||
static constexpr bool empty = false;
|
||||
|
||||
using type = T;
|
||||
|
||||
T* get()
|
||||
struct task_base
|
||||
{
|
||||
std::unique_ptr<task_base> next;
|
||||
|
||||
virtual ~task_base();
|
||||
|
||||
virtual void invoke()
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
next->invoke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct task_type final : task_base
|
||||
{
|
||||
std::remove_reference_t<F> func;
|
||||
|
||||
task_type(F&& func)
|
||||
: func(std::forward<F>(func))
|
||||
{
|
||||
}
|
||||
|
||||
void invoke() final override
|
||||
{
|
||||
func();
|
||||
task_base::invoke();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<task_base> m_stack;
|
||||
|
||||
public:
|
||||
task_stack() = default;
|
||||
|
||||
template <typename F>
|
||||
task_stack(F&& func)
|
||||
: m_stack(new task_type<F>(std::forward<F>(func)))
|
||||
{
|
||||
return reinterpret_cast<T*>(&data);
|
||||
}
|
||||
|
||||
const T* get() const
|
||||
void push(task_stack stack)
|
||||
{
|
||||
return reinterpret_cast<const T*>(&data);
|
||||
auto _top = stack.m_stack.release();
|
||||
auto _next = m_stack.release();
|
||||
m_stack.reset(_top);
|
||||
while (UNLIKELY(_top->next)) _top = _top->next.get();
|
||||
_top->next.reset(_next);
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
void reset()
|
||||
{
|
||||
get()->~T();
|
||||
m_stack.reset();
|
||||
}
|
||||
|
||||
void invoke() const
|
||||
{
|
||||
if (m_stack)
|
||||
{
|
||||
m_stack->invoke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct result_storage<void>
|
||||
// Thread control class
|
||||
class thread_ctrl final
|
||||
{
|
||||
static constexpr bool empty = true;
|
||||
// Current thread
|
||||
static thread_local thread_ctrl* g_tls_this_thread;
|
||||
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <class Context, typename... Args>
|
||||
using result_storage_t = result_storage<std::invoke_result_t<Context, Args...>>;
|
||||
|
||||
// Detect on_abort() method (should return void)
|
||||
template <typename T, typename = void>
|
||||
struct thread_on_abort : std::bool_constant<false> {};
|
||||
|
||||
template <typename T>
|
||||
struct thread_on_abort<T, decltype(std::declval<named_thread<T>&>().on_abort())> : std::bool_constant<true> {};
|
||||
|
||||
// Detect on_cleanup() static member function (should return void) (in C++20 can use destroying delete instead)
|
||||
template <typename T, typename = void>
|
||||
struct thread_on_cleanup : std::bool_constant<false> {};
|
||||
|
||||
template <typename T>
|
||||
struct thread_on_cleanup<T, decltype(named_thread<T>::on_cleanup(std::declval<named_thread<T>*>()))> : std::bool_constant<true> {};
|
||||
|
||||
// Thread base class
|
||||
class thread_base
|
||||
{
|
||||
// Native thread entry point function type
|
||||
#ifdef _WIN32
|
||||
using native_entry = uint(__stdcall*)(void* arg);
|
||||
#else
|
||||
using native_entry = void*(*)(void* arg);
|
||||
#endif
|
||||
// Self pointer
|
||||
std::shared_ptr<thread_ctrl> m_self;
|
||||
|
||||
// Thread handle (platform-specific)
|
||||
atomic_t<std::uintptr_t> m_thread{0};
|
||||
|
||||
// Thread mutex
|
||||
mutable shared_mutex m_mutex;
|
||||
mutable semaphore<> m_mutex;
|
||||
|
||||
// Thread condition variable
|
||||
cond_variable m_cond;
|
||||
@@ -118,123 +104,77 @@ class thread_base
|
||||
atomic_t<u32> m_signal{0};
|
||||
|
||||
// Thread joining condition variable
|
||||
mutable cond_variable m_jcv;
|
||||
cond_variable m_jcv;
|
||||
|
||||
// Thread state
|
||||
atomic_t<thread_state> m_state = thread_state::created;
|
||||
// Remotely set or caught exception
|
||||
std::exception_ptr m_exception;
|
||||
|
||||
// Thread name
|
||||
lf_value<std::string> m_name;
|
||||
// Thread initial task or atexit task
|
||||
task_stack m_task;
|
||||
|
||||
//
|
||||
atomic_t<u64> m_cycles = 0;
|
||||
// Fixed name
|
||||
std::string m_name;
|
||||
|
||||
// Start thread
|
||||
void start(native_entry);
|
||||
static void start(const std::shared_ptr<thread_ctrl>&, task_stack);
|
||||
|
||||
// Called at the thread start
|
||||
void initialize();
|
||||
|
||||
// Called at the thread end, returns true if needs destruction
|
||||
bool finalize(int) noexcept;
|
||||
// Called at the thread end
|
||||
void finalize(std::exception_ptr) noexcept;
|
||||
|
||||
// Cleanup after possibly deleting the thread instance
|
||||
static void finalize() noexcept;
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
template <class Context>
|
||||
friend class named_thread;
|
||||
|
||||
protected:
|
||||
thread_base(std::string_view name);
|
||||
|
||||
~thread_base();
|
||||
|
||||
public:
|
||||
// Get CPU cycles since last time this function was called. First call returns 0.
|
||||
u64 get_cycles();
|
||||
|
||||
// Wait for the thread (it does NOT change thread state, and can be called from multiple threads)
|
||||
void join() const;
|
||||
|
||||
// Notify the thread
|
||||
void notify();
|
||||
};
|
||||
|
||||
// Collection of global function for current thread
|
||||
class thread_ctrl final
|
||||
{
|
||||
// Current thread
|
||||
static thread_local thread_base* g_tls_this_thread;
|
||||
|
||||
// Target cpu core layout
|
||||
static atomic_t<native_core_arrangement> g_native_core_layout;
|
||||
// Add task (atexit)
|
||||
static void _push(task_stack);
|
||||
|
||||
// Internal waiting function, may throw. Infinite value is -1.
|
||||
static bool _wait_for(u64 usec);
|
||||
|
||||
friend class thread_base;
|
||||
// Internal throwing function. Mutex must be locked and will be unlocked.
|
||||
[[noreturn]] void _throw();
|
||||
|
||||
// Internal notification function
|
||||
void _notify(cond_variable thread_ctrl::*);
|
||||
|
||||
public:
|
||||
// Get current thread name
|
||||
static std::string_view get_name()
|
||||
{
|
||||
return g_tls_this_thread->m_name.get();
|
||||
}
|
||||
thread_ctrl(std::string&& name);
|
||||
|
||||
thread_ctrl(const thread_ctrl&) = delete;
|
||||
|
||||
~thread_ctrl();
|
||||
|
||||
// Get thread name
|
||||
template <typename T>
|
||||
static std::string_view get_name(const named_thread<T>& thread)
|
||||
const std::string& get_name() const
|
||||
{
|
||||
return static_cast<const thread_base&>(thread).m_name.get();
|
||||
return m_name;
|
||||
}
|
||||
|
||||
// Set current thread name (not recommended)
|
||||
static void set_name(std::string_view name)
|
||||
{
|
||||
g_tls_this_thread->m_name.assign(name);
|
||||
}
|
||||
// Get exception
|
||||
std::exception_ptr get_exception() const;
|
||||
|
||||
// Set thread name (not recommended)
|
||||
template <typename T>
|
||||
static void set_name(named_thread<T>& thread, std::string_view name)
|
||||
{
|
||||
static_cast<thread_base&>(thread).m_name.assign(name);
|
||||
}
|
||||
// Set exception
|
||||
void set_exception(std::exception_ptr ptr);
|
||||
|
||||
template <typename T>
|
||||
static u64 get_cycles(named_thread<T>& thread)
|
||||
{
|
||||
return static_cast<thread_base&>(thread).get_cycles();
|
||||
}
|
||||
// Get thread result (may throw, simultaneous joining allowed)
|
||||
void join();
|
||||
|
||||
template <typename T>
|
||||
static void notify(named_thread<T>& thread)
|
||||
{
|
||||
static_cast<thread_base&>(thread).notify();
|
||||
}
|
||||
// Notify the thread
|
||||
void notify();
|
||||
|
||||
// Read current state
|
||||
static inline thread_state state()
|
||||
{
|
||||
return g_tls_this_thread->m_state;
|
||||
}
|
||||
|
||||
// Wait once with timeout. May spuriously return false.
|
||||
// Wait once with timeout. Abortable, may throw. May spuriously return false.
|
||||
static inline bool wait_for(u64 usec)
|
||||
{
|
||||
return _wait_for(usec);
|
||||
}
|
||||
|
||||
// Wait.
|
||||
// Wait. Abortable, may throw.
|
||||
static inline void wait()
|
||||
{
|
||||
_wait_for(-1);
|
||||
}
|
||||
|
||||
// Wait until pred().
|
||||
template <typename F, typename RT = std::invoke_result_t<F>>
|
||||
// Wait until pred(). Abortable, may throw.
|
||||
template<typename F, typename RT = std::result_of_t<F()>>
|
||||
static inline RT wait(F&& pred)
|
||||
{
|
||||
while (true)
|
||||
@@ -248,183 +188,136 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Wait eternally until aborted.
|
||||
[[noreturn]] static inline void eternalize()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_wait_for(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Test exception (may throw).
|
||||
static void test();
|
||||
|
||||
// Get current thread (may be nullptr)
|
||||
static thread_base* get_current()
|
||||
static thread_ctrl* get_current()
|
||||
{
|
||||
return g_tls_this_thread;
|
||||
}
|
||||
|
||||
// Detect layout
|
||||
static void detect_cpu_layout();
|
||||
|
||||
// Returns a core affinity mask. Set whether to generate the high priority set or not
|
||||
static u64 get_affinity_mask(thread_class group);
|
||||
|
||||
// Sets the native thread priority
|
||||
static void set_native_priority(int priority);
|
||||
|
||||
// Sets the preferred affinity mask for this thread
|
||||
static void set_thread_affinity_mask(u64 mask);
|
||||
|
||||
// Spawn a detached named thread
|
||||
template <typename F>
|
||||
static void spawn(std::string_view name, F&& func)
|
||||
// Register function at thread exit (for the current thread)
|
||||
template<typename F>
|
||||
static inline void atexit(F&& func)
|
||||
{
|
||||
new named_thread<F>(thread_state::detached, name, std::forward<F>(func));
|
||||
_push(std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Create detached named thread
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(N&& name, F&& func)
|
||||
{
|
||||
auto out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Named thread factory
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(std::shared_ptr<thread_ctrl>& out, N&& name, F&& func)
|
||||
{
|
||||
out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
}
|
||||
|
||||
static void set_native_priority(int priority);
|
||||
static void set_ideal_processor_core(int core);
|
||||
};
|
||||
|
||||
// Derived from the callable object Context, possibly a lambda
|
||||
template <class Context>
|
||||
class named_thread final : public Context, result_storage_t<Context>, thread_base
|
||||
class named_thread
|
||||
{
|
||||
using result = result_storage_t<Context>;
|
||||
using thread = thread_base;
|
||||
|
||||
// Type-erased thread entry point
|
||||
#ifdef _WIN32
|
||||
static inline uint __stdcall entry_point(void* arg) try
|
||||
#else
|
||||
static inline void* entry_point(void* arg) try
|
||||
#endif
|
||||
{
|
||||
const auto _this = static_cast<named_thread*>(static_cast<thread*>(arg));
|
||||
|
||||
// Perform self-cleanup if necessary
|
||||
if (_this->entry_point())
|
||||
{
|
||||
// Call on_cleanup() static member function if it's available
|
||||
if constexpr (thread_on_cleanup<Context>())
|
||||
{
|
||||
Context::on_cleanup(_this);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete _this;
|
||||
}
|
||||
}
|
||||
|
||||
thread::finalize();
|
||||
return 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
catch_all_exceptions();
|
||||
}
|
||||
|
||||
bool entry_point()
|
||||
{
|
||||
thread::initialize();
|
||||
|
||||
if constexpr (result::empty)
|
||||
{
|
||||
// No result
|
||||
Context::operator()();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct the result using placement new (copy elision should happen)
|
||||
new (result::get()) typename result::type(Context::operator()());
|
||||
}
|
||||
|
||||
return thread::finalize(0);
|
||||
}
|
||||
|
||||
// Detached thread constructor
|
||||
named_thread(thread_state s, std::string_view name, Context&& f)
|
||||
: Context(std::forward<Context>(f))
|
||||
, thread(name)
|
||||
{
|
||||
thread::m_state.raw() = s;
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
|
||||
friend class thread_ctrl;
|
||||
// Pointer to managed resource (shared with actual thread)
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
|
||||
public:
|
||||
// Normal forwarding constructor
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<Context, Args&&...>>>
|
||||
named_thread(std::string_view name, Args&&... args)
|
||||
: Context(std::forward<Args>(args)...)
|
||||
, thread(name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
named_thread();
|
||||
|
||||
// Lambda constructor, also the implicit deduction guide candidate
|
||||
named_thread(std::string_view name, Context&& f)
|
||||
: Context(std::forward<Context>(f))
|
||||
, thread(name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
virtual ~named_thread();
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
named_thread(const named_thread&) = delete;
|
||||
|
||||
named_thread& operator=(const named_thread&) = delete;
|
||||
// Get thread name
|
||||
virtual std::string get_name() const;
|
||||
|
||||
// Wait for the completion and access result (if not void)
|
||||
[[nodiscard]] decltype(auto) operator()()
|
||||
protected:
|
||||
// Start thread (cannot be called from the constructor: should throw in such case)
|
||||
void start_thread(const std::shared_ptr<void>& _this);
|
||||
|
||||
// Thread task (called in the thread)
|
||||
virtual void on_task() = 0;
|
||||
|
||||
// Thread finalization (called after on_task)
|
||||
virtual void on_exit() {}
|
||||
|
||||
// Called once upon thread spawn within the thread's own context
|
||||
virtual void on_spawn() {}
|
||||
|
||||
public:
|
||||
// ID initialization
|
||||
virtual void on_init(const std::shared_ptr<void>& _this)
|
||||
{
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
}
|
||||
return start_thread(_this);
|
||||
}
|
||||
|
||||
// Wait for the completion and access result (if not void)
|
||||
[[nodiscard]] decltype(auto) operator()() const
|
||||
// ID finalization
|
||||
virtual void on_stop()
|
||||
{
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
}
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
// Access thread state
|
||||
operator thread_state() const
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return thread::m_state.load();
|
||||
return m_thread.get();
|
||||
}
|
||||
|
||||
// Try to abort/detach
|
||||
named_thread& operator=(thread_state s)
|
||||
void join() const
|
||||
{
|
||||
if (s != thread_state::aborting && s != thread_state::detached)
|
||||
{
|
||||
ASSUME(0);
|
||||
}
|
||||
|
||||
if (thread::m_state.compare_and_swap_test(thread_state::created, s))
|
||||
{
|
||||
if (s == thread_state::aborting)
|
||||
{
|
||||
// Call on_abort() method if it's available
|
||||
if constexpr (thread_on_abort<Context>())
|
||||
{
|
||||
Context::on_abort();
|
||||
}
|
||||
}
|
||||
|
||||
thread::notify();
|
||||
}
|
||||
|
||||
return *this;
|
||||
return m_thread->join();
|
||||
}
|
||||
|
||||
// Context type doesn't need virtual destructor
|
||||
~named_thread()
|
||||
void notify() const
|
||||
{
|
||||
*this = thread_state::aborting;
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
result::destroy();
|
||||
}
|
||||
return m_thread->notify();
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
|
||||
class scope_thread final
|
||||
{
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
|
||||
public:
|
||||
template<typename N, typename F>
|
||||
scope_thread(N&& name, F&& func)
|
||||
{
|
||||
thread_ctrl::spawn(m_thread, std::forward<N>(name), std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
scope_thread(const scope_thread&) = delete;
|
||||
|
||||
// Destructor with exceptions allowed
|
||||
~scope_thread() noexcept(false)
|
||||
{
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return m_thread.get();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
class Timer
|
||||
@@ -51,11 +49,4 @@ public:
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
|
||||
}
|
||||
|
||||
u64 GetMsSince(std::chrono::steady_clock::time_point timestamp)
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now - timestamp).count();
|
||||
}
|
||||
};
|
||||
|
||||
+4
-194
@@ -7,27 +7,10 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#ifdef __NR_memfd_create
|
||||
#elif __x86_64__
|
||||
#define __NR_memfd_create 319
|
||||
#elif __aarch64__
|
||||
#define __NR_memfd_create 279
|
||||
#endif
|
||||
|
||||
static int memfd_create_(const char *name, uint flags)
|
||||
{
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Convert memory protection (internal)
|
||||
@@ -80,7 +63,7 @@ namespace utils
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -93,186 +76,13 @@ namespace utils
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_reset(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
memory_decommit(pointer, size);
|
||||
memory_commit(pointer, size, prot);
|
||||
#else
|
||||
verify(HERE), ::mmap(pointer, size, +prot, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_release(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualFree(pointer, 0, MEM_RELEASE);
|
||||
#else
|
||||
verify(HERE), ::munmap(pointer, size) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (u64 addr = (u64)pointer, end = addr + size; addr < end;)
|
||||
{
|
||||
const u64 boundary = (addr + 0x10000) & -0x10000;
|
||||
const u64 block_size = std::min(boundary, end) - addr;
|
||||
|
||||
DWORD old;
|
||||
if (!::VirtualProtect((LPVOID)addr, block_size, +prot, &old))
|
||||
{
|
||||
fmt::throw_exception("VirtualProtect failed (%p, 0x%x, addr=0x%x, error=%#x)", pointer, size, addr, GetLastError());
|
||||
}
|
||||
|
||||
// Next region
|
||||
addr += block_size;
|
||||
}
|
||||
DWORD old;
|
||||
verify(HERE), ::VirtualProtect(pointer, size, +prot, &old);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::shm(u32 size)
|
||||
: m_size(::align(size, 0x10000))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_handle = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, m_size, NULL);
|
||||
verify(HERE), m_handle != INVALID_HANDLE_VALUE;
|
||||
#elif __linux__
|
||||
m_file = ::memfd_create_("", 0);
|
||||
verify(HERE), m_file >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#else
|
||||
while ((m_file = ::shm_open("/rpcs3-mem1", O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) == -1)
|
||||
{
|
||||
if (m_file == -1 && errno == EMFILE)
|
||||
{
|
||||
fmt::throw_exception("Too many open files. Raise the limit and try again.");
|
||||
}
|
||||
|
||||
verify(HERE), errno == EEXIST;
|
||||
}
|
||||
|
||||
verify(HERE), ::shm_unlink("/rpcs3-mem1") >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::~shm()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(m_handle);
|
||||
#else
|
||||
::close(m_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map(void* ptr, protection prot) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD access = FILE_MAP_WRITE;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw:
|
||||
case protection::ro:
|
||||
case protection::no:
|
||||
break;
|
||||
case protection::wx:
|
||||
case protection::rx:
|
||||
access |= FILE_MAP_EXECUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto ret = static_cast<u8*>(::MapViewOfFileEx(m_handle, access, 0, 0, m_size, ptr)))
|
||||
{
|
||||
if (prot != protection::rw && prot != protection::wx)
|
||||
{
|
||||
DWORD old;
|
||||
if (!::VirtualProtect(ret, m_size, +prot, &old))
|
||||
{
|
||||
::UnmapViewOfFile(ret);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#else
|
||||
return static_cast<u8*>(::mmap((void*)((u64)ptr & -0x10000), m_size, +prot, MAP_SHARED | (ptr ? MAP_FIXED : 0), m_file, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map_critical(void* ptr, protection prot)
|
||||
{
|
||||
const auto target = (u8*)((u64)ptr & -0x10000);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem;
|
||||
if (!::VirtualQuery(target, &mem, sizeof(mem)) || mem.State != MEM_RESERVE || !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto base = (u8*)mem.AllocationBase;
|
||||
const auto size = mem.RegionSize + (target - base);
|
||||
|
||||
if (base < target && !::VirtualAlloc(base, target - base, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (target + m_size < base + size && !::VirtualAlloc(target + m_size, base + size - target - m_size, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return this->map(target, prot);
|
||||
}
|
||||
|
||||
void shm::unmap(void* ptr) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::UnmapViewOfFile(ptr);
|
||||
#else
|
||||
::munmap(ptr, m_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void shm::unmap_critical(void* ptr)
|
||||
{
|
||||
const auto target = (u8*)((u64)ptr & -0x10000);
|
||||
|
||||
this->unmap(target);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem, mem2;
|
||||
if (!::VirtualQuery(target - 1, &mem, sizeof(mem)) || !::VirtualQuery(target + m_size, &mem2, sizeof(mem2)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem.State == MEM_RESERVE && !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem2.State == MEM_RESERVE && !::VirtualFree(mem2.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size1 = mem.State == MEM_RESERVE ? target - (u8*)mem.AllocationBase : 0;
|
||||
const auto size2 = mem2.State == MEM_RESERVE ? mem2.RegionSize : 0;
|
||||
|
||||
if (!::VirtualAlloc(mem.State == MEM_RESERVE ? mem.AllocationBase : target, m_size + size1 + size2, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Memory protection type
|
||||
@@ -27,52 +25,11 @@ namespace utils
|
||||
*/
|
||||
void memory_commit(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
|
||||
// Decommit all memory committed via commit_page_memory.
|
||||
/**
|
||||
* Decommit all memory committed via commit_page_memory.
|
||||
*/
|
||||
void memory_decommit(void* pointer, std::size_t size);
|
||||
|
||||
// Decommit all memory and commit it again.
|
||||
void memory_reset(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
|
||||
// Free memory after reserved by memory_reserve, should specify original size
|
||||
void memory_release(void* pointer, std::size_t size);
|
||||
|
||||
// Set memory protection
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot);
|
||||
|
||||
// Shared memory handle
|
||||
class shm
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* m_handle;
|
||||
#else
|
||||
int m_file;
|
||||
#endif
|
||||
u32 m_size;
|
||||
|
||||
public:
|
||||
explicit shm(u32 size);
|
||||
|
||||
shm(const shm&) = delete;
|
||||
|
||||
shm& operator=(const shm&) = delete;
|
||||
|
||||
~shm();
|
||||
|
||||
// Map shared memory
|
||||
u8* map(void* ptr, protection prot = protection::rw) const;
|
||||
|
||||
// Map shared memory over reserved memory region, which is unsafe (non-atomic) under Win32
|
||||
u8* map_critical(void* ptr, protection prot = protection::rw);
|
||||
|
||||
// Unmap shared memory
|
||||
void unmap(void* ptr) const;
|
||||
|
||||
// Unmap shared memory, undoing map_critical
|
||||
void unmap_critical(void* ptr);
|
||||
|
||||
u32 size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,592 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "StrFmt.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class address_range_vector;
|
||||
|
||||
|
||||
/**
|
||||
* Constexprs
|
||||
*/
|
||||
constexpr inline u32 page_start(u32 addr)
|
||||
{
|
||||
return addr & ~4095u;
|
||||
}
|
||||
|
||||
constexpr inline u32 next_page(u32 addr)
|
||||
{
|
||||
return page_start(addr + 4096u);
|
||||
}
|
||||
|
||||
constexpr inline u32 page_end(u32 addr)
|
||||
{
|
||||
return next_page(addr) - 1;
|
||||
}
|
||||
|
||||
constexpr inline u32 is_page_aligned(u32 addr)
|
||||
{
|
||||
return page_start(addr) == addr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Address Range utility class
|
||||
*/
|
||||
class address_range
|
||||
{
|
||||
public:
|
||||
u32 start = UINT32_MAX; // First address in range
|
||||
u32 end = 0; // Last address
|
||||
|
||||
private:
|
||||
// Helper constexprs
|
||||
static constexpr inline bool range_overlaps(u32 start1, u32 end1, u32 start2, u32 end2)
|
||||
{
|
||||
return (start1 <= end2 && start2 <= end1);
|
||||
}
|
||||
|
||||
static constexpr inline bool address_overlaps(u32 address, u32 start, u32 end)
|
||||
{
|
||||
return (start <= address && address <= end);
|
||||
}
|
||||
|
||||
static constexpr inline bool range_inside_range(u32 start1, u32 end1, u32 start2, u32 end2)
|
||||
{
|
||||
return (start1 >= start2 && end1 <= end2);
|
||||
}
|
||||
|
||||
address_range(u32 _start, u32 _end) : start(_start), end(_end) {}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
address_range() = default;
|
||||
address_range(const address_range &other) : start(other.start), end(other.end) {}
|
||||
|
||||
static inline address_range start_length(u32 _start, u32 _length)
|
||||
{
|
||||
return address_range(_start, _start + (_length - 1));
|
||||
}
|
||||
|
||||
static inline address_range start_end(u32 _start, u32 _end)
|
||||
{
|
||||
return address_range(_start, _end);
|
||||
}
|
||||
|
||||
// Length
|
||||
u32 length() const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return end - start + 1;
|
||||
}
|
||||
|
||||
void set_length(const u32 new_length)
|
||||
{
|
||||
end = start + new_length - 1;
|
||||
ASSERT(valid());
|
||||
}
|
||||
|
||||
u32 next_address() const
|
||||
{
|
||||
return end + 1;
|
||||
}
|
||||
|
||||
u32 prev_address() const
|
||||
{
|
||||
return start - 1;
|
||||
}
|
||||
|
||||
// Overlapping checks
|
||||
bool overlaps(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
return range_overlaps(start, end, other.start, other.end);
|
||||
}
|
||||
|
||||
bool overlaps(const u32 addr) const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return address_overlaps(addr, start, end);
|
||||
}
|
||||
|
||||
bool inside(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
return range_inside_range(start, end, other.start, other.end);
|
||||
}
|
||||
|
||||
inline bool inside(const address_range_vector &vec) const;
|
||||
inline bool overlaps(const address_range_vector &vec) const;
|
||||
|
||||
bool touches(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
// returns true if there is overlap, or if sections are side-by-side
|
||||
return overlaps(other) || other.start == next_address() || other.end == prev_address();
|
||||
}
|
||||
|
||||
// Utilities
|
||||
s32 signed_distance(const address_range &other) const
|
||||
{
|
||||
if (touches(other))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// other after this
|
||||
if (other.start > end)
|
||||
{
|
||||
return (s32)(other.start - end - 1);
|
||||
}
|
||||
|
||||
// this after other
|
||||
AUDIT(start > other.end);
|
||||
return -((s32)(start - other.end - 1));
|
||||
}
|
||||
|
||||
u32 distance(const address_range &other) const
|
||||
{
|
||||
if (touches(other))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// other after this
|
||||
if (other.start > end)
|
||||
{
|
||||
return (other.start - end - 1);
|
||||
}
|
||||
|
||||
// this after other
|
||||
AUDIT(start > other.end);
|
||||
return (start - other.end - 1);
|
||||
}
|
||||
|
||||
address_range get_min_max(const address_range &other) const
|
||||
{
|
||||
return {
|
||||
std::min(valid() ? start : UINT32_MAX, other.valid() ? other.start : UINT32_MAX),
|
||||
std::max(valid() ? end : 0, other.valid() ? other.end : 0)
|
||||
};
|
||||
}
|
||||
|
||||
void set_min_max(const address_range &other)
|
||||
{
|
||||
const address_range _range = get_min_max(other);
|
||||
start = _range.start;
|
||||
end = _range.end;
|
||||
}
|
||||
|
||||
bool is_page_range() const
|
||||
{
|
||||
return (valid() && start % 4096u == 0 && length() % 4096u == 0);
|
||||
}
|
||||
|
||||
address_range to_page_range() const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return { page_start(start), page_end(end) };
|
||||
}
|
||||
|
||||
void page_align()
|
||||
{
|
||||
AUDIT(valid());
|
||||
start = page_start(start);
|
||||
end = page_end(end);
|
||||
AUDIT(is_page_range());
|
||||
}
|
||||
|
||||
address_range get_intersect(const address_range &clamp) const
|
||||
{
|
||||
if (!valid() || !clamp.valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::max(start, clamp.start), std::min(end, clamp.end) };
|
||||
}
|
||||
|
||||
void intersect(const address_range &clamp)
|
||||
{
|
||||
if (!clamp.valid())
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
start = std::max(start, clamp.start);
|
||||
end = std::min(end, clamp.end);
|
||||
}
|
||||
}
|
||||
|
||||
// Validity
|
||||
bool valid() const
|
||||
{
|
||||
return (start <= end);
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
start = UINT32_MAX;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
// Comparison Operators
|
||||
bool operator ==(const address_range &other) const
|
||||
{
|
||||
return (start == other.start && end == other.end);
|
||||
}
|
||||
|
||||
bool operator !=(const address_range &other) const
|
||||
{
|
||||
return (start != other.start || end != other.end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug
|
||||
*/
|
||||
std::string str() const
|
||||
{
|
||||
return fmt::format("{0x%x->0x%x}", start, end);
|
||||
}
|
||||
};
|
||||
|
||||
static inline address_range page_for(u32 addr)
|
||||
{
|
||||
return address_range::start_end(page_start(addr), page_end(addr));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Address Range Vector utility class
|
||||
*
|
||||
* Collection of address_range objects. Allows for merging and removing ranges from the set.
|
||||
*/
|
||||
class address_range_vector
|
||||
{
|
||||
public:
|
||||
using vector_type = typename std::vector<address_range>;
|
||||
using iterator = typename vector_type::iterator;
|
||||
using const_iterator = typename vector_type::const_iterator;
|
||||
using size_type = typename vector_type::size_type;
|
||||
|
||||
private:
|
||||
vector_type data;
|
||||
|
||||
public:
|
||||
// Wrapped functions
|
||||
inline void reserve(size_t nr) { data.reserve(nr); }
|
||||
inline void clear() { data.clear(); }
|
||||
inline size_type size() const { return data.size(); }
|
||||
inline bool empty() const { return data.empty(); }
|
||||
inline address_range& operator[](size_type n) { return data[n]; }
|
||||
inline const address_range& operator[](size_type n) const { return data[n]; }
|
||||
inline iterator begin() { return data.begin(); }
|
||||
inline const_iterator begin() const { return data.begin(); }
|
||||
inline iterator end() { return data.end(); }
|
||||
inline const_iterator end() const { return data.end(); }
|
||||
|
||||
// Search for ranges that touch new_range. If found, merge instead of adding new_range.
|
||||
// When adding a new range, re-use invalid ranges whenever possible
|
||||
void merge(const address_range &new_range)
|
||||
{
|
||||
// Note the case where we have
|
||||
// AAAA BBBB
|
||||
// CCCC
|
||||
// If we have data={A,B}, and new_range=C, we have to merge A with C, then B with A and invalidate B
|
||||
|
||||
if (!new_range.valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
address_range *found = nullptr;
|
||||
address_range *invalid = nullptr;
|
||||
|
||||
for (auto &existing : data)
|
||||
{
|
||||
if (!existing.valid())
|
||||
{
|
||||
invalid = &existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
// range1 overlaps, is immediately before, or is immediately after range2
|
||||
if (existing.touches(new_range))
|
||||
{
|
||||
if (found != nullptr)
|
||||
{
|
||||
// Already found a match, merge and invalidate "existing"
|
||||
found->set_min_max(existing);
|
||||
existing.invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// First match, merge "new_range"
|
||||
existing.set_min_max(new_range);
|
||||
found = &existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (invalid != nullptr)
|
||||
{
|
||||
*invalid = new_range;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.push_back(new_range);
|
||||
}
|
||||
|
||||
AUDIT(check_consistency());
|
||||
}
|
||||
|
||||
void merge(const address_range_vector &other)
|
||||
{
|
||||
for (const address_range &new_range : other)
|
||||
{
|
||||
merge(new_range);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude a given range from data
|
||||
void exclude(const address_range &exclusion)
|
||||
{
|
||||
// Note the case where we have
|
||||
// AAAAAAA
|
||||
// EEE
|
||||
// where data={A} and exclusion=E.
|
||||
// In this case, we need to reduce A to the head (before E starts), and then create a new address_range B for the tail (after E ends), i.e.
|
||||
// AA BB
|
||||
// EEE
|
||||
|
||||
if (!exclusion.valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
address_range *invalid = nullptr; // try to re-use an invalid range instead of calling push_back
|
||||
|
||||
// We use index access because we might have to push_back within the loop, which could invalidate the iterators
|
||||
size_type _size = data.size();
|
||||
for (size_type n = 0; n < _size; ++n)
|
||||
{
|
||||
address_range &existing = data[n];
|
||||
|
||||
if (!existing.valid())
|
||||
{
|
||||
// Null
|
||||
invalid = &existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!existing.overlaps(exclusion))
|
||||
{
|
||||
// No overlap, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool head_excluded = exclusion.overlaps(existing.start); // This section has its start inside excluded range
|
||||
const bool tail_excluded = exclusion.overlaps(existing.end); // This section has its end inside excluded range
|
||||
|
||||
if (head_excluded && tail_excluded)
|
||||
{
|
||||
// Cannot be salvaged, fully excluded
|
||||
existing.invalidate();
|
||||
invalid = &existing;
|
||||
}
|
||||
else if (head_excluded)
|
||||
{
|
||||
// Head overlaps, truncate head
|
||||
existing.start = exclusion.next_address();
|
||||
}
|
||||
else if (tail_excluded)
|
||||
{
|
||||
// Tail overlaps, truncate tail
|
||||
existing.end = exclusion.prev_address();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Section sits in the middle (see comment above function header)
|
||||
AUDIT(exclusion.inside(existing));
|
||||
const auto tail_end = existing.end;
|
||||
|
||||
// Head
|
||||
existing.end = exclusion.prev_address();
|
||||
|
||||
// Tail
|
||||
if (invalid != nullptr)
|
||||
{
|
||||
invalid->start = exclusion.next_address();
|
||||
invalid->end = tail_end;
|
||||
invalid = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// IMPORTANT: adding to data invalidates "existing". This must be done last!
|
||||
data.push_back(address_range::start_end(exclusion.next_address(), tail_end));
|
||||
}
|
||||
}
|
||||
}
|
||||
AUDIT(check_consistency());
|
||||
AUDIT(!overlaps(exclusion));
|
||||
}
|
||||
|
||||
void exclude(const address_range_vector &other)
|
||||
{
|
||||
for (const address_range &exclusion : other)
|
||||
{
|
||||
exclude(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the consistency of this vector.
|
||||
// Will fail if ranges within the vector overlap our touch each-other
|
||||
bool check_consistency() const
|
||||
{
|
||||
size_t _size = data.size();
|
||||
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
{
|
||||
const auto &r1 = data[i];
|
||||
if (!r1.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = i + 1; j < _size; ++j)
|
||||
{
|
||||
const auto &r2 = data[j];
|
||||
if (!r2.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r1.touches(r2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test for overlap with a given range
|
||||
bool overlaps(const address_range &range) const
|
||||
{
|
||||
for (const address_range ¤t : data)
|
||||
{
|
||||
if (!current.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.overlaps(range))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test for overlap with a given address_range vector
|
||||
bool overlaps(const address_range_vector &other) const
|
||||
{
|
||||
for (const address_range &rng1 : data)
|
||||
{
|
||||
if (!rng1.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const address_range &rng2 : other.data)
|
||||
{
|
||||
if (!rng2.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rng1.overlaps(rng2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if a given range is fully contained inside this vector
|
||||
bool contains(const address_range &range) const
|
||||
{
|
||||
for (const address_range &cur : *this)
|
||||
{
|
||||
if (!cur.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (range.inside(cur))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if all ranges in this vector are full contained inside a specific range
|
||||
bool inside(const address_range &range) const
|
||||
{
|
||||
for (const address_range &cur : *this)
|
||||
{
|
||||
if (!cur.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cur.inside(range))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// These declarations must be done after address_range_vector has been defined
|
||||
bool address_range::inside(const address_range_vector &vec) const
|
||||
{
|
||||
return vec.contains(*this);
|
||||
}
|
||||
|
||||
bool address_range::overlaps(const address_range_vector &vec) const
|
||||
{
|
||||
return vec.overlaps(*this);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
|
||||
namespace std {
|
||||
static_assert(sizeof(size_t) >= 2 * sizeof(u32), "size_t must be at least twice the size of u32");
|
||||
|
||||
template <>
|
||||
struct hash<utils::address_range>
|
||||
{
|
||||
std::size_t operator()(const utils::address_range& k) const
|
||||
{
|
||||
// we can guarantee a unique hash since our type is 64 bits and size_t as well
|
||||
return (size_t{ k.start } << 32) | size_t{ k.end };
|
||||
}
|
||||
};
|
||||
}
|
||||
-228
@@ -1,228 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline u32 cntlz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse(&res, arg) || nonzero ? res ^ 31 : 32;
|
||||
#elif __LZCNT__
|
||||
return _lzcnt_u32(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clz(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cntlz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse64(&res, arg) || nonzero ? res ^ 63 : 64;
|
||||
#elif __LZCNT__
|
||||
return _lzcnt_u64(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 cnttz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward(&res, arg) || nonzero ? res : 32;
|
||||
#elif __BMI__
|
||||
return _tzcnt_u32(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctz(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cnttz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward64(&res, arg) || nonzero ? res : 64;
|
||||
#elif __BMI__
|
||||
return _tzcnt_u64(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u8 popcnt32(u32 arg)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
const u32 a1 = arg & 0x55555555;
|
||||
const u32 a2 = (arg >> 1) & 0x55555555;
|
||||
const u32 a3 = a1 + a2;
|
||||
const u32 b1 = a3 & 0x33333333;
|
||||
const u32 b2 = (a3 >> 2) & 0x33333333;
|
||||
const u32 b3 = b1 + b2;
|
||||
const u32 c3 = (b3 + (b3 >> 4)) & 0x0f0f0f0f;
|
||||
const u32 d3 = c3 + (c3 >> 8);
|
||||
return static_cast<u8>(d3 + (d3 >> 16));
|
||||
#else
|
||||
return __builtin_popcount(arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Rotate helpers
|
||||
#if defined(__GNUG__)
|
||||
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft8)
|
||||
return __builtin_rotateleft8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rolb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright8)
|
||||
return __builtin_rotateright8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rorb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft16)
|
||||
return __builtin_rotateleft16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rolw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright16)
|
||||
return __builtin_rotateright16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rorw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft32)
|
||||
return __builtin_rotateleft32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("roll %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright32)
|
||||
return __builtin_rotateright32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("rorl %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft64)
|
||||
return __builtin_rotateleft64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rolq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright64)
|
||||
return __builtin_rotateright64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rorq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr u64 umulh64(u64 a, u64 b)
|
||||
{
|
||||
const __uint128_t x = a;
|
||||
const __uint128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
constexpr s64 mulh64(s64 a, s64 b)
|
||||
{
|
||||
const __int128_t x = a;
|
||||
const __int128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
return _rotl8(x, n);
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
return _rotr8(x, n);
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
return _rotl16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
return _rotr16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
return _rotl(x, (int)n);
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
return _rotr(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
return _rotl64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
return _rotr64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 umulh64(u64 x, u64 y)
|
||||
{
|
||||
return __umulh(x, y);
|
||||
}
|
||||
|
||||
inline s64 mulh64(s64 x, s64 y)
|
||||
{
|
||||
return __mulh(x, y);
|
||||
}
|
||||
#endif
|
||||
} // namespace utils
|
||||
+6
-16
@@ -1,5 +1,5 @@
|
||||
#include "bin_patch.h"
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include "yaml-cpp/yaml.h"
|
||||
#include "File.h"
|
||||
#include "Config.h"
|
||||
|
||||
@@ -32,23 +32,13 @@ void patch_engine::append(const std::string& patch)
|
||||
{
|
||||
if (fs::file f{patch})
|
||||
{
|
||||
YAML::Node root;
|
||||
|
||||
try
|
||||
{
|
||||
root = YAML::Load(f.to_string());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_FATAL(GENERAL, "Failed to load patch file %s\n%s thrown: %s", patch, typeid(e).name(), e.what());
|
||||
return;
|
||||
}
|
||||
auto root = YAML::Load(f.to_string());
|
||||
|
||||
for (auto pair : root)
|
||||
{
|
||||
auto& name = pair.first.Scalar();
|
||||
auto& data = m_map[name];
|
||||
|
||||
|
||||
for (auto patch : pair.second)
|
||||
{
|
||||
u64 type64 = 0;
|
||||
@@ -86,13 +76,13 @@ void patch_engine::append(const std::string& patch)
|
||||
case patch_type::bef32:
|
||||
case patch_type::lef32:
|
||||
{
|
||||
info.value = std::bit_cast<u32>(patch[2].as<f32>());
|
||||
info.value_as<f32>() = patch[2].as<f32>();
|
||||
break;
|
||||
}
|
||||
case patch_type::bef64:
|
||||
case patch_type::lef64:
|
||||
{
|
||||
info.value = std::bit_cast<u64>(patch[2].as<f64>());
|
||||
info.value_as<f64>() = patch[2].as<f64>();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -101,7 +91,7 @@ void patch_engine::append(const std::string& patch)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data.emplace_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,12 @@ class patch_engine
|
||||
patch_type type;
|
||||
u32 offset;
|
||||
u64 value;
|
||||
|
||||
template <typename T>
|
||||
T& value_as()
|
||||
{
|
||||
return *reinterpret_cast<T*>(reinterpret_cast<char*>(&value));
|
||||
}
|
||||
};
|
||||
|
||||
// Database
|
||||
|
||||
+603
-284
File diff suppressed because it is too large
Load Diff
+19
-25
@@ -1,14 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "asm.h"
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
static const size_t size_dropped = std::numeric_limits<size_t>::max();
|
||||
|
||||
/*
|
||||
C-style format parser. Appends formatted string to `out`, returns number of characters written.
|
||||
`out`: mutable reference to std::string, std::vector<char> or other compatible container
|
||||
@@ -43,7 +40,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
const auto drop_sequence = [&]
|
||||
{
|
||||
out.insert(out.end(), fmt - ctx.size, fmt);
|
||||
ctx.size = size_dropped;
|
||||
ctx.size = -1;
|
||||
};
|
||||
|
||||
const auto read_decimal = [&](uint result) -> uint
|
||||
@@ -59,7 +56,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const auto write_octal = [&](u64 value, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (utils::cntlz64(value | 1, true) + 2) / 3), '0');
|
||||
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (cntlz64(value | 1, true) + 2) / 3), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 8)
|
||||
@@ -70,8 +67,8 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const auto write_hex = [&](u64 value, bool upper, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - utils::cntlz64(value | 1, true) / 4), '0');
|
||||
|
||||
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - cntlz64(value | 1, true) / 4), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 16)
|
||||
{
|
||||
@@ -114,7 +111,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
ctx = {0};
|
||||
out.push_back(ch);
|
||||
}
|
||||
else if (ctx.size == size_dropped)
|
||||
else if (ctx.size == -1)
|
||||
{
|
||||
out.push_back(ch);
|
||||
}
|
||||
@@ -144,7 +141,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
{
|
||||
ctx.width = read_decimal(ch - '0');
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -160,7 +157,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
ctx.width = std::abs(warg);
|
||||
ctx.left |= warg < 0;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -310,7 +307,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const std::size_t size1 = src.fmt_string(out, ctx.args);
|
||||
|
||||
|
||||
if (ctx.dot && size1 > ctx.prec)
|
||||
{
|
||||
// Shrink if necessary
|
||||
@@ -412,10 +409,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -469,10 +465,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -533,10 +528,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -582,7 +576,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
// Add padding if necessary
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
|
||||
}
|
||||
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
@@ -631,7 +625,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
// Handle unfinished sequence
|
||||
if (ctx.size && ctx.size != size_dropped)
|
||||
if (ctx.size && ctx.size != -1)
|
||||
{
|
||||
fmt--, drop_sequence();
|
||||
}
|
||||
|
||||
+77
-447
@@ -1,6 +1,5 @@
|
||||
#include "cond.h"
|
||||
#include "sync.h"
|
||||
#include "lockless.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
@@ -10,468 +9,99 @@
|
||||
|
||||
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
{
|
||||
verify("cond_variable overflow" HERE), (_old & 0xffff) != 0xffff; // Very unlikely: it requires 65535 distinct threads to wait simultaneously
|
||||
|
||||
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
|
||||
{
|
||||
if (value >> 16)
|
||||
{
|
||||
// Success
|
||||
value -= 0x10001;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
// Retire
|
||||
value -= 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
||||
|
||||
#ifdef _WIN32
|
||||
if (_old >= 0x10000 && !OptWaitOnAddress && m_value)
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = _timeout * -10;
|
||||
|
||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
|
||||
{
|
||||
// Workaround possibly stolen signal
|
||||
imp_wake(1);
|
||||
verify(HERE), rc == WAIT_TIMEOUT;
|
||||
|
||||
// Retire
|
||||
if (!m_value.fetch_op([](u32& value) { if (value) value--; }))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
timespec timeout;
|
||||
timeout.tv_sec = _timeout / 1000000;
|
||||
timeout.tv_nsec = (_timeout % 1000000) * 1000;
|
||||
|
||||
for (u32 value = _old + 1;; value = m_value)
|
||||
{
|
||||
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0
|
||||
? 0
|
||||
: errno;
|
||||
|
||||
// Normal or timeout wakeup
|
||||
if (!err || (_timeout != -1 && err == ETIMEDOUT))
|
||||
{
|
||||
// Cleanup (remove waiter)
|
||||
verify(HERE), m_value--;
|
||||
return !err;
|
||||
}
|
||||
|
||||
// Not a wakeup
|
||||
verify(HERE), err == EAGAIN;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void cond_variable::imp_wake(u32 _count) noexcept
|
||||
{
|
||||
// TODO (notify_one)
|
||||
balanced_awaken<true>(m_value, m_value.atomic_op([&](u32& value) -> u32
|
||||
#ifdef _WIN32
|
||||
// Try to subtract required amount of waiters
|
||||
const u32 count = m_value.atomic_op([=](u32& value)
|
||||
{
|
||||
// Subtract already signaled number from total amount of waiters
|
||||
const u32 can_sig = (value & 0xffff) - (value >> 16);
|
||||
const u32 num_sig = std::min<u32>(can_sig, _count);
|
||||
|
||||
value += num_sig << 16;
|
||||
return num_sig;
|
||||
}));
|
||||
}
|
||||
|
||||
bool notifier::imp_try_lock(u32 count)
|
||||
{
|
||||
return m_counter.atomic_op([&](u32& value)
|
||||
{
|
||||
if ((value % (max_readers + 1)) + count <= max_readers)
|
||||
if (value > _count)
|
||||
{
|
||||
value += count;
|
||||
return true;
|
||||
value -= _count;
|
||||
return _count;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void notifier::imp_unlock(u32 count)
|
||||
{
|
||||
const u32 counter = m_counter.sub_fetch(count);
|
||||
|
||||
if (UNLIKELY(counter % (max_readers + 1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (counter)
|
||||
{
|
||||
const u32 _old = m_counter.atomic_op([](u32& value) -> u32
|
||||
{
|
||||
if (value % (max_readers + 1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::exchange(value, 0) / (max_readers + 1);
|
||||
});
|
||||
|
||||
const u32 wc = m_cond.m_value;
|
||||
|
||||
if (_old && wc)
|
||||
{
|
||||
m_cond.imp_wake(_old > wc ? wc : _old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 notifier::imp_notify(u32 count)
|
||||
{
|
||||
return m_counter.atomic_op([&](u32& value) -> u32
|
||||
{
|
||||
if (const u32 add = value % (max_readers + 1))
|
||||
{
|
||||
// Mutex is locked
|
||||
const u32 result = add > count ? count : add;
|
||||
value += result * (max_readers + 1);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mutex is unlocked
|
||||
value = 0;
|
||||
return count;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool notifier::wait(u64 usec_timeout)
|
||||
{
|
||||
const u32 _old = m_cond.m_value.fetch_add(1);
|
||||
|
||||
if (max_readers < m_counter.fetch_op([](u32& value)
|
||||
{
|
||||
if (value > max_readers)
|
||||
{
|
||||
value -= max_readers;
|
||||
}
|
||||
|
||||
value -= 1;
|
||||
}))
|
||||
{
|
||||
// Return without waiting
|
||||
m_cond.imp_wait(_old, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool res = m_cond.imp_wait(_old, usec_timeout);
|
||||
|
||||
while (!try_lock_shared())
|
||||
{
|
||||
// TODO
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool unique_cond::imp_wait(u64 _timeout) noexcept
|
||||
{
|
||||
// State transition: c_sig -> c_lock \ c_lock -> c_wait
|
||||
const u32 _old = m_value.fetch_sub(1);
|
||||
if (LIKELY(_old == c_sig))
|
||||
return true;
|
||||
|
||||
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
|
||||
{
|
||||
if (value == c_sig)
|
||||
{
|
||||
value = c_lock;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
value = c_lock;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void unique_cond::imp_notify() noexcept
|
||||
{
|
||||
auto [old, ok] = m_value.fetch_op([](u32& v)
|
||||
{
|
||||
if (UNLIKELY(v > 0 && v < c_sig))
|
||||
{
|
||||
v = c_sig;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::exchange(value, 0);
|
||||
});
|
||||
|
||||
verify(HERE), old <= c_sig;
|
||||
|
||||
if (LIKELY(!ok || old == c_lock))
|
||||
for (u32 i = count; i > 0; i--)
|
||||
{
|
||||
return;
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
for (u32 i = _count; i > 0; std::this_thread::yield())
|
||||
{
|
||||
const u32 value = m_value;
|
||||
|
||||
balanced_awaken(m_value, 1);
|
||||
}
|
||||
|
||||
bool shared_cond::imp_wait(u32 slot, u64 _timeout) noexcept
|
||||
{
|
||||
if (slot >= 32)
|
||||
{
|
||||
// Invalid argument, assume notified
|
||||
return true;
|
||||
}
|
||||
|
||||
const u64 wait_bit = c_wait << slot;
|
||||
const u64 lock_bit = c_lock << slot;
|
||||
|
||||
// Change state from c_lock to c_wait
|
||||
const u64 old_ = m_cvx32.fetch_op([=](u64& cvx32)
|
||||
{
|
||||
if (cvx32 & wait_bit)
|
||||
{
|
||||
// c_lock -> c_wait
|
||||
cvx32 &= ~(lock_bit & ~wait_bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
// c_sig -> c_lock
|
||||
cvx32 |= lock_bit;
|
||||
}
|
||||
});
|
||||
|
||||
if ((old_ & wait_bit) == 0)
|
||||
{
|
||||
// Already signaled, return without waiting
|
||||
return true;
|
||||
}
|
||||
|
||||
return balanced_wait_until(m_cvx32, _timeout, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// c_sig -> c_lock
|
||||
cvx32 |= lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
// Retire
|
||||
cvx32 |= lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void shared_cond::imp_notify() noexcept
|
||||
{
|
||||
auto [old, ok] = m_cvx32.fetch_op([](u64& cvx32)
|
||||
{
|
||||
if (const u64 sig_mask = cvx32 & 0xffffffff)
|
||||
{
|
||||
cvx32 &= 0xffffffffull << 32;
|
||||
cvx32 |= sig_mask << 32;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Determine if some waiters need a syscall notification
|
||||
const u64 wait_mask = old & (~old >> 32);
|
||||
|
||||
if (UNLIKELY(!ok || !wait_mask))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
|
||||
}
|
||||
|
||||
void shared_cond::wait_all() noexcept
|
||||
{
|
||||
// Try to acquire waiting state without locking but only if there are other locks
|
||||
const auto [old_, result] = m_cvx32.fetch_op([](u64& cvx32) -> u64
|
||||
{
|
||||
// Check waiting alone
|
||||
if ((cvx32 & 0xffffffff) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Combine used bits and invert to find least significant bit unused
|
||||
const u32 slot = utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true);
|
||||
|
||||
// Set waiting bit (does nothing if all slots are used)
|
||||
cvx32 |= (1ull << slot) & 0xffffffff;
|
||||
return 1ull << slot;
|
||||
});
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (result > 0xffffffffu)
|
||||
{
|
||||
// All slots are used, fallback to spin wait
|
||||
while (m_cvx32 & 0xffffffff)
|
||||
{
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 wait_bit = result;
|
||||
const u64 lock_bit = wait_bit | (wait_bit << 32);
|
||||
|
||||
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// Remove signal and unlock at once
|
||||
cvx32 &= ~lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
cvx32 &= ~lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
bool shared_cond::wait_all(shared_cond::shared_lock& lock) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
|
||||
if (lock.m_slot >= 32)
|
||||
{
|
||||
// Invalid argument, assume notified
|
||||
return true;
|
||||
}
|
||||
|
||||
const u64 wait_bit = c_wait << lock.m_slot;
|
||||
const u64 lock_bit = c_lock << lock.m_slot;
|
||||
|
||||
// Try to acquire waiting state only if there are other locks
|
||||
const auto [old_, not_alone] = m_cvx32.fetch_op([&](u64& cvx32)
|
||||
{
|
||||
// Check locking alone
|
||||
if (((cvx32 >> 32) & cvx32) == (lock_bit >> 32))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// c_lock -> c_wait, c_sig -> unlock
|
||||
cvx32 &= ~(lock_bit & ~wait_bit);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!not_alone)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set invalid slot to acknowledge unlocking
|
||||
lock.m_slot = 33;
|
||||
}
|
||||
|
||||
if ((old_ & wait_bit) == 0)
|
||||
{
|
||||
// Already signaled, return without waiting
|
||||
return true;
|
||||
}
|
||||
|
||||
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// Remove signal and unlock at once
|
||||
cvx32 &= ~lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
cvx32 &= ~lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shared_cond::notify_all(shared_cond::shared_lock& lock) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
|
||||
if (lock.m_slot >= 32)
|
||||
{
|
||||
// Invalid argument
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 slot_mask = c_sig << lock.m_slot;
|
||||
|
||||
auto [old, ok] = m_cvx32.fetch_op([&](u64& cvx32)
|
||||
{
|
||||
if (((cvx32 << 32) & cvx32) != slot_mask)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const u64 sig_mask = cvx32 & 0xffffffff)
|
||||
{
|
||||
cvx32 &= (0xffffffffull << 32) & ~slot_mask;
|
||||
cvx32 |= (sig_mask << 32) & ~slot_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
// Not an exclusive reader
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set invalid slot to acknowledge unlocking
|
||||
lock.m_slot = 34;
|
||||
|
||||
// Determine if some waiters need a syscall notification
|
||||
const u64 wait_mask = old & (~old >> 32);
|
||||
|
||||
if (UNLIKELY(!wait_mask))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lf_queue_base::wait(u64 _timeout)
|
||||
{
|
||||
auto _old = m_head.compare_and_swap(0, 1);
|
||||
|
||||
if (_old)
|
||||
{
|
||||
verify("lf_queue concurrent wait" HERE), _old != 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return balanced_wait_until(m_head, _timeout, [](std::uintptr_t& head, auto... ret) -> int
|
||||
{
|
||||
if (head != 1)
|
||||
{
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
head = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void lf_queue_base::imp_notify()
|
||||
{
|
||||
balanced_awaken(m_head, 1);
|
||||
// Constrain remaining amount with imaginary waiter count
|
||||
if (i > value)
|
||||
{
|
||||
i = value;
|
||||
}
|
||||
|
||||
if (!value || i == 0)
|
||||
{
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (const int res = futex((int*)&m_value.raw(), FUTEX_WAKE_PRIVATE, i > INT_MAX ? INT_MAX : i, nullptr, nullptr, 0))
|
||||
{
|
||||
verify(HERE), res >= 0 && (u32)res <= i;
|
||||
i -= res;
|
||||
}
|
||||
|
||||
if (!m_value || i == 0)
|
||||
{
|
||||
// Escape
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+3
-237
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include <shared_mutex>
|
||||
#include "asm.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight condition variable
|
||||
class cond_variable
|
||||
@@ -11,8 +9,6 @@ class cond_variable
|
||||
// Internal waiter counter
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
friend class notifier;
|
||||
|
||||
protected:
|
||||
// Internal waiting function
|
||||
bool imp_wait(u32 _old, u64 _timeout) noexcept;
|
||||
@@ -25,7 +21,7 @@ public:
|
||||
|
||||
// Intrusive wait algorithm for lockable objects
|
||||
template <typename T>
|
||||
bool wait(T& object, u64 usec_timeout = -1)
|
||||
explicit_bool_t wait(T& object, u64 usec_timeout = -1)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
object.unlock();
|
||||
@@ -34,15 +30,6 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
// Unlock all specified objects but don't lock them again
|
||||
template <typename... Locks>
|
||||
bool wait_unlock(u64 usec_timeout, Locks&&... locks)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
(..., std::forward<Locks>(locks).unlock());
|
||||
return imp_wait(_old, usec_timeout);
|
||||
}
|
||||
|
||||
// Wake one thread
|
||||
void notify_one() noexcept
|
||||
{
|
||||
@@ -57,228 +44,7 @@ public:
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
imp_wake(65535);
|
||||
imp_wake(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr u64 max_timeout = u64{UINT32_MAX} / 1000 * 1000000;
|
||||
};
|
||||
|
||||
// Pair of a fake shared mutex (only limited shared locking) and a condition variable. Obsolete.
|
||||
class notifier
|
||||
{
|
||||
atomic_t<u32> m_counter{0};
|
||||
cond_variable m_cond;
|
||||
|
||||
bool imp_try_lock(u32 count);
|
||||
|
||||
void imp_unlock(u32 count);
|
||||
|
||||
u32 imp_notify(u32 count);
|
||||
|
||||
public:
|
||||
constexpr notifier() = default;
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return imp_try_lock(max_readers);
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
imp_unlock(max_readers);
|
||||
}
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
return imp_try_lock(1);
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
imp_unlock(1);
|
||||
}
|
||||
|
||||
bool wait(u64 usec_timeout = -1);
|
||||
|
||||
void notify_all()
|
||||
{
|
||||
if (m_counter)
|
||||
{
|
||||
imp_notify(-1);
|
||||
}
|
||||
|
||||
// Notify after imaginary "exclusive" lock+unlock
|
||||
m_cond.notify_all();
|
||||
}
|
||||
|
||||
void notify_one()
|
||||
{
|
||||
// TODO
|
||||
if (m_counter)
|
||||
{
|
||||
if (imp_notify(1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_cond.notify_one();
|
||||
}
|
||||
|
||||
static constexpr u32 max_readers = 0x7f;
|
||||
};
|
||||
|
||||
// Condition variable fused with a pseudo-mutex which is never supposed to be locked concurrently.
|
||||
class unique_cond
|
||||
{
|
||||
enum : u32
|
||||
{
|
||||
c_wait = 1,
|
||||
c_lock = 2,
|
||||
c_sig = 3,
|
||||
};
|
||||
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
bool imp_wait(u64 _timeout) noexcept;
|
||||
void imp_notify() noexcept;
|
||||
|
||||
public:
|
||||
constexpr unique_cond() = default;
|
||||
|
||||
void lock() noexcept
|
||||
{
|
||||
// Shouldn't be locked by more than one thread concurrently
|
||||
while (UNLIKELY(!m_value.compare_and_swap_test(0, c_lock)))
|
||||
;
|
||||
}
|
||||
|
||||
void unlock() noexcept
|
||||
{
|
||||
m_value = 0;
|
||||
}
|
||||
|
||||
bool wait(std::unique_lock<unique_cond>& lock, u64 usec_timeout = -1) noexcept
|
||||
{
|
||||
AUDIT(lock.owns_lock());
|
||||
AUDIT(lock.mutex() == this);
|
||||
return imp_wait(usec_timeout);
|
||||
}
|
||||
|
||||
void notify() noexcept
|
||||
{
|
||||
// Early exit if notification is not required
|
||||
if (LIKELY(!m_value))
|
||||
return;
|
||||
|
||||
imp_notify();
|
||||
}
|
||||
};
|
||||
|
||||
// Condition variable fused with a pseudo-mutex supporting only reader locks (up to 32 readers).
|
||||
class shared_cond
|
||||
{
|
||||
// For information, shouldn't modify
|
||||
enum : u64
|
||||
{
|
||||
// Wait bit is aligned for compatibility with 32-bit futex.
|
||||
c_wait = 1,
|
||||
c_sig = 1ull << 32,
|
||||
c_lock = 1ull << 32 | 1,
|
||||
};
|
||||
|
||||
// Split in 32-bit parts for convenient bit combining
|
||||
atomic_t<u64> m_cvx32{0};
|
||||
|
||||
class shared_lock
|
||||
{
|
||||
shared_cond* m_this;
|
||||
u32 m_slot;
|
||||
|
||||
friend class shared_cond;
|
||||
|
||||
public:
|
||||
shared_lock(shared_cond* _this) noexcept
|
||||
: m_this(_this)
|
||||
{
|
||||
// Lock and remember obtained slot index
|
||||
m_slot = m_this->m_cvx32.atomic_op([](u64& cvx32)
|
||||
{
|
||||
// Combine used bits and invert to find least significant bit unused
|
||||
const u32 slot = static_cast<u32>(utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true));
|
||||
|
||||
// Set lock bits (does nothing if all slots are used)
|
||||
const u64 bit = (1ull << slot) & 0xffffffff;
|
||||
cvx32 |= bit | (bit << 32);
|
||||
return slot;
|
||||
});
|
||||
}
|
||||
|
||||
shared_lock(const shared_lock&) = delete;
|
||||
|
||||
shared_lock(shared_lock&& rhs)
|
||||
: m_this(rhs.m_this)
|
||||
, m_slot(rhs.m_slot)
|
||||
{
|
||||
rhs.m_slot = 32;
|
||||
}
|
||||
|
||||
shared_lock& operator=(const shared_lock&) = delete;
|
||||
|
||||
~shared_lock()
|
||||
{
|
||||
// Clear the slot (does nothing if all slots are used)
|
||||
const u64 bit = (1ull << m_slot) & 0xffffffff;
|
||||
m_this->m_cvx32 &= ~(bit | (bit << 32));
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
// Check success
|
||||
return m_slot < 32;
|
||||
}
|
||||
|
||||
bool wait(u64 usec_timeout = -1) const noexcept
|
||||
{
|
||||
return m_this->wait(*this, usec_timeout);
|
||||
}
|
||||
};
|
||||
|
||||
bool imp_wait(u32 slot, u64 _timeout) noexcept;
|
||||
void imp_notify() noexcept;
|
||||
|
||||
public:
|
||||
constexpr shared_cond() = default;
|
||||
|
||||
shared_lock try_shared_lock() noexcept
|
||||
{
|
||||
return shared_lock(this);
|
||||
}
|
||||
|
||||
u32 count() const noexcept
|
||||
{
|
||||
const u64 cvx32 = m_cvx32;
|
||||
return utils::popcnt32(static_cast<u32>(cvx32 | (cvx32 >> 32)));
|
||||
}
|
||||
|
||||
bool wait(shared_lock const& lock, u64 usec_timeout = -1) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
return imp_wait(lock.m_slot, usec_timeout);
|
||||
}
|
||||
|
||||
void wait_all() noexcept;
|
||||
|
||||
bool wait_all(shared_lock& lock) noexcept;
|
||||
|
||||
void notify_all() noexcept
|
||||
{
|
||||
if (LIKELY(!m_cvx32))
|
||||
return;
|
||||
|
||||
imp_notify();
|
||||
}
|
||||
|
||||
bool notify_all(shared_lock& lock) noexcept;
|
||||
};
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
namespace date_time
|
||||
{
|
||||
static inline tm get_time(time_t* _time)
|
||||
{
|
||||
tm buf;
|
||||
time_t t = time(_time);
|
||||
#ifdef _MSC_VER
|
||||
localtime_s(&buf, &t);
|
||||
#else
|
||||
buf = *localtime(&t);
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline std::string current_time()
|
||||
{
|
||||
char str[80];
|
||||
tm now = get_time(0);
|
||||
strftime(str, sizeof(str), "%c", &now);
|
||||
return str;
|
||||
}
|
||||
|
||||
template<char separator = 0>
|
||||
static inline std::string current_time_narrow()
|
||||
{
|
||||
char str[80];
|
||||
tm now = get_time(0);
|
||||
|
||||
std::string parse_buf;
|
||||
|
||||
if constexpr(separator != 0)
|
||||
parse_buf = std::string("%Y") + separator + "%m" + separator + "%d" + separator + "%H" + separator + "%M" + separator + "%S";
|
||||
else
|
||||
parse_buf = "%Y%m%d%H%M%S";
|
||||
|
||||
strftime(str, sizeof(str), parse_buf.c_str(), &now);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "dynamic_library.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -50,7 +50,7 @@ namespace utils
|
||||
|
||||
bool dynamic_library::loaded() const
|
||||
{
|
||||
return m_handle != nullptr;
|
||||
return !m_handle;
|
||||
}
|
||||
|
||||
dynamic_library::operator bool() const
|
||||
|
||||
@@ -63,30 +63,18 @@ namespace utils
|
||||
{
|
||||
}
|
||||
|
||||
void init()
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
// TODO: atomic
|
||||
ptr = reinterpret_cast<R(*)(Args...)>(get_proc_address(lib, name));
|
||||
}
|
||||
}
|
||||
|
||||
operator bool()
|
||||
{
|
||||
init();
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
{
|
||||
init();
|
||||
|
||||
return ptr(args...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) inline utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) static utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
|
||||
+5
-7
@@ -4,7 +4,7 @@
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
|
||||
T operator ++(T& value, int)
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
}
|
||||
|
||||
// Conditionally set state (optimized)
|
||||
bool state_test_and_set(T expected, T state)
|
||||
explicit_bool_t state_test_and_set(T expected, T state)
|
||||
{
|
||||
if (m_value == expected && m_value.compare_and_swap_test(expected, state))
|
||||
{
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
}
|
||||
|
||||
// Conditionally set state (list version)
|
||||
bool state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
explicit_bool_t state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
{
|
||||
T _old;
|
||||
|
||||
@@ -111,10 +111,9 @@ public:
|
||||
// Unconditionally set next state
|
||||
void state_next()
|
||||
{
|
||||
T _old, state = m_value.atomic_op([&](T& value)
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value++;
|
||||
return value;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
@@ -123,10 +122,9 @@ public:
|
||||
// Unconditionally set previous state
|
||||
void state_prev()
|
||||
{
|
||||
T _old, state = m_value.atomic_op([&](T& value)
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value--;
|
||||
return value;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
|
||||
+6
-36
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -673,16 +673,6 @@ struct area_base
|
||||
return{ x1, y1, x2 - x1, y2 - y1 };
|
||||
}
|
||||
|
||||
constexpr T width() const
|
||||
{
|
||||
return (x1 < x2) ? (x2 - x1) : (x1 - x2);
|
||||
}
|
||||
|
||||
constexpr T height() const
|
||||
{
|
||||
return (y1 < y2) ? (y2 - y1) : (y1 - y2);
|
||||
}
|
||||
|
||||
void flip_vertical()
|
||||
{
|
||||
T _y = y1; y1 = y2; y2 = _y;
|
||||
@@ -703,11 +693,6 @@ struct area_base
|
||||
return{ x2, y1, x1, y2 };
|
||||
}
|
||||
|
||||
constexpr bool is_flipped() const
|
||||
{
|
||||
return (x1 > x2 || y1 > y2);
|
||||
}
|
||||
|
||||
constexpr bool operator == (const area_base& rhs) const
|
||||
{
|
||||
return x1 == rhs.x1 && x2 == rhs.x2 && y1 == rhs.y1 && y2 == rhs.y2;
|
||||
@@ -841,7 +826,7 @@ struct color4_base
|
||||
T xyzw[4];
|
||||
};
|
||||
|
||||
constexpr color4_base()
|
||||
color4_base()
|
||||
: x{}
|
||||
, y{}
|
||||
, z{}
|
||||
@@ -849,7 +834,7 @@ struct color4_base
|
||||
{
|
||||
}
|
||||
|
||||
constexpr color4_base(T x, T y = {}, T z = {}, T w = {})
|
||||
color4_base(T x, T y = {}, T z = {}, T w = {})
|
||||
: x(x)
|
||||
, y(y)
|
||||
, z(z)
|
||||
@@ -857,18 +842,18 @@ struct color4_base
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool operator == (const color4_base& rhs) const
|
||||
bool operator == (const color4_base& rhs) const
|
||||
{
|
||||
return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a;
|
||||
}
|
||||
|
||||
constexpr bool operator != (const color4_base& rhs) const
|
||||
bool operator != (const color4_base& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
template<typename NT>
|
||||
constexpr operator color4_base<NT>() const
|
||||
operator color4_base<NT>() const
|
||||
{
|
||||
return{ (NT)x, (NT)y, (NT)z, (NT)w };
|
||||
}
|
||||
@@ -991,77 +976,62 @@ struct color1_base
|
||||
};
|
||||
|
||||
//specializations
|
||||
using positionu = position_base<unsigned int>;
|
||||
using positioni = position_base<int>;
|
||||
using positionf = position_base<float>;
|
||||
using positiond = position_base<double>;
|
||||
|
||||
using coordu = coord_base<unsigned int>;
|
||||
using coordi = coord_base<int>;
|
||||
using coordf = coord_base<float>;
|
||||
using coordd = coord_base<double>;
|
||||
|
||||
using areau = area_base<unsigned int>;
|
||||
using areai = area_base<int>;
|
||||
using areaf = area_base<float>;
|
||||
using aread = area_base<double>;
|
||||
|
||||
using position1u = position1_base<unsigned int>;
|
||||
using position1i = position1_base<int>;
|
||||
using position1f = position1_base<float>;
|
||||
using position1d = position1_base<double>;
|
||||
|
||||
using position2u = position2_base<unsigned int>;
|
||||
using position2i = position2_base<int>;
|
||||
using position2f = position2_base<float>;
|
||||
using position2d = position2_base<double>;
|
||||
|
||||
using position3u = position3_base<unsigned int>;
|
||||
using position3i = position3_base<int>;
|
||||
using position3f = position3_base<float>;
|
||||
using position3d = position3_base<double>;
|
||||
|
||||
using position4u = position4_base<unsigned int>;
|
||||
using position4i = position4_base<int>;
|
||||
using position4f = position4_base<float>;
|
||||
using position4d = position4_base<double>;
|
||||
|
||||
using size2u = size2_base<unsigned int>;
|
||||
using size2i = size2_base<int>;
|
||||
using size2f = size2_base<float>;
|
||||
using size2d = size2_base<double>;
|
||||
|
||||
using sizeu = size2u;
|
||||
using sizei = size2i;
|
||||
using sizef = size2f;
|
||||
using sized = size2d;
|
||||
|
||||
using size3u = size3_base<unsigned int>;
|
||||
using size3i = size3_base<int>;
|
||||
using size3f = size3_base<float>;
|
||||
using size3d = size3_base<double>;
|
||||
|
||||
using coord3u = coord3_base<unsigned int>;
|
||||
using coord3i = coord3_base<int>;
|
||||
using coord3f = coord3_base<float>;
|
||||
using coord3d = coord3_base<double>;
|
||||
|
||||
using color4u = color4_base<unsigned int>;
|
||||
using color4i = color4_base<int>;
|
||||
using color4f = color4_base<float>;
|
||||
using color4d = color4_base<double>;
|
||||
|
||||
using color3u = color3_base<unsigned int>;
|
||||
using color3i = color3_base<int>;
|
||||
using color3f = color3_base<float>;
|
||||
using color3d = color3_base<double>;
|
||||
|
||||
using color2u = color2_base<unsigned int>;
|
||||
using color2i = color2_base<int>;
|
||||
using color2f = color2_base<float>;
|
||||
using color2d = color2_base<double>;
|
||||
|
||||
using color1u = color1_base<unsigned int>;
|
||||
using color1i = color1_base<int>;
|
||||
using color1f = color1_base<float>;
|
||||
using color1d = color1_base<double>;
|
||||
|
||||
@@ -13,10 +13,9 @@ rem // A copy of the GPL 2.0 should have been included with the program.
|
||||
rem // If not, see http://www.gnu.org/licenses/
|
||||
|
||||
rem // Official git repository and contact information can be found at
|
||||
rem // https://github.com/RPCS3/rpcs3 and https://rpcs3.net/.
|
||||
rem // https://github.com/RPCS3/rpcs3 and http://rpcs3.net/.
|
||||
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
setlocal ENABLEEXTENSIONS
|
||||
|
||||
set GIT_VERSION_FILE=%~p0..\rpcs3\git-version.h
|
||||
if not defined GIT (
|
||||
@@ -55,33 +54,9 @@ if errorlevel 1 (
|
||||
goto done
|
||||
)
|
||||
|
||||
rem // Get commit count from (unshallowed) HEAD
|
||||
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set COMMIT_COUNT=%%I
|
||||
|
||||
rem // If we're in AppVeyor, building a non-master, pull request artifact
|
||||
if defined APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (
|
||||
|
||||
if "%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"=="master" (
|
||||
rem // If pull request comes from a master branch, GIT_BRANCH = username/branch in order to distinguish from upstream/master
|
||||
for /f "tokens=1* delims=/" %%a in ("%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%") do set user=%%a
|
||||
set "GIT_BRANCH=!user!/%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"
|
||||
) else (
|
||||
rem // Otherwise, GIT_BRANCH=branch
|
||||
set GIT_BRANCH=%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%
|
||||
)
|
||||
|
||||
rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-master builds
|
||||
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%%I
|
||||
|
||||
) else (
|
||||
rem // Get last commit (shortened) and concat after commit count in GIT_VERSION
|
||||
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I
|
||||
|
||||
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
|
||||
)
|
||||
|
||||
rem // Echo obtained GIT_VERSION for debug purposes if needed
|
||||
echo %GIT_VERSION%
|
||||
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set GIT_VERSION=%%I
|
||||
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%GIT_VERSION%-%%I
|
||||
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
|
||||
|
||||
rem // Don't modify the file if it already has the current version.
|
||||
if exist "%GIT_VERSION_FILE%" (
|
||||
@@ -94,7 +69,7 @@ if exist "%GIT_VERSION_FILE%" (
|
||||
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION "%GIT_VERSION%" >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_BRANCH ^"%GIT_BRANCH%^" >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_BRANCH "%GIT_BRANCH%" >> "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
||||
|
||||
+7
-31
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
@@ -10,42 +9,19 @@ namespace rpcs3
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static size_t hash_struct_base(const T& value)
|
||||
template<typename T>
|
||||
static size_t hash_struct(const T& value)
|
||||
{
|
||||
// FNV 64-bit
|
||||
size_t result = 14695981039346656037ull;
|
||||
const U *bits = reinterpret_cast<const U*>(&value);
|
||||
const unsigned char *bytes = reinterpret_cast<const unsigned char*>(&value);
|
||||
|
||||
for (size_t n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
||||
for (size_t n = 0; n < sizeof(T); ++n)
|
||||
{
|
||||
result ^= bits[n];
|
||||
result ^= bytes[n];
|
||||
result *= 1099511628211ull;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t hash_struct(const T& value)
|
||||
{
|
||||
static constexpr auto block_sz = sizeof(T);
|
||||
|
||||
if constexpr ((block_sz & 0x7) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u64>(value);
|
||||
}
|
||||
|
||||
if constexpr ((block_sz & 0x3) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u32>(value);
|
||||
}
|
||||
|
||||
if constexpr ((block_sz & 0x1) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u16>(value);
|
||||
}
|
||||
|
||||
return hash_struct_base<T, u8>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+131
-279
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
//! Simple sizeless array base for concurrent access. Cannot shrink, only growths automatically.
|
||||
//! There is no way to know the current size. The smaller index is, the faster it's accessed.
|
||||
//!
|
||||
//!
|
||||
//! T is the type of elements. Currently, default constructor of T shall be constexpr.
|
||||
//! N is initial element count, available without any memory allocation and only stored contiguously.
|
||||
template <typename T, std::size_t N>
|
||||
template<typename T, std::size_t N>
|
||||
class lf_array
|
||||
{
|
||||
// Data (default-initialized)
|
||||
@@ -68,13 +68,13 @@ public:
|
||||
// Get current "push" position
|
||||
u32 size() const
|
||||
{
|
||||
return m_ctrl.load().push;
|
||||
return reinterpret_cast<const atomic_t<u32>&>(m_ctrl).load(); // Hack
|
||||
}
|
||||
|
||||
|
||||
// Acquire the place for one or more elements.
|
||||
u32 push_begin(u32 count = 1)
|
||||
{
|
||||
return std::bit_cast<atomic_t<u64>*>(&m_ctrl)->fetch_add(count); // Hack
|
||||
return reinterpret_cast<atomic_t<u32>&>(m_ctrl).fetch_add(count); // Hack
|
||||
}
|
||||
|
||||
// Get current "pop" position
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
{
|
||||
return m_default_key_data;
|
||||
}
|
||||
|
||||
|
||||
// Calculate hash and array position
|
||||
for (std::size_t pos = Hash{}(key) % Size;; pos += Size)
|
||||
{
|
||||
@@ -146,333 +146,185 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Helper type, linked list element
|
||||
template <typename T>
|
||||
class lf_queue_item final
|
||||
// Fixed-size single-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_spsc
|
||||
{
|
||||
lf_queue_item* m_link = nullptr;
|
||||
// If N is a power of 2, m_push/m_pop can safely overflow and the algorithm is simplified
|
||||
static_assert(N && (1u << 31) % N == 0, "lf_spsc<> error: size must be power of 2");
|
||||
|
||||
T m_data;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue_iterator;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue_slice;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue;
|
||||
|
||||
constexpr lf_queue_item() = default;
|
||||
|
||||
template <typename... Args>
|
||||
constexpr lf_queue_item(lf_queue_item* link, Args&&... args)
|
||||
: m_link(link)
|
||||
, m_data(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
volatile std::uint32_t m_push{0};
|
||||
volatile std::uint32_t m_pop{0};
|
||||
T m_data[N]{};
|
||||
|
||||
public:
|
||||
lf_queue_item(const lf_queue_item&) = delete;
|
||||
constexpr lf_spsc() = default;
|
||||
|
||||
lf_queue_item& operator=(const lf_queue_item&) = delete;
|
||||
|
||||
~lf_queue_item()
|
||||
// Try to push (producer only)
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
for (lf_queue_item* ptr = m_link; ptr;)
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
delete std::exchange(ptr, std::exchange(ptr->m_link, nullptr));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Forward iterator: non-owning pointer to the list element in lf_queue_slice<>
|
||||
template <typename T>
|
||||
class lf_queue_iterator
|
||||
{
|
||||
lf_queue_item<T>* m_ptr = nullptr;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue_slice;
|
||||
|
||||
public:
|
||||
constexpr lf_queue_iterator() = default;
|
||||
|
||||
bool operator ==(const lf_queue_iterator& rhs) const
|
||||
{
|
||||
return m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
bool operator !=(const lf_queue_iterator& rhs) const
|
||||
{
|
||||
return m_ptr != rhs.m_ptr;
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
{
|
||||
return m_ptr->m_data;
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
{
|
||||
return &m_ptr->m_data;
|
||||
}
|
||||
|
||||
lf_queue_iterator& operator ++()
|
||||
{
|
||||
m_ptr = m_ptr->m_link;
|
||||
return *this;
|
||||
}
|
||||
|
||||
lf_queue_iterator operator ++(int)
|
||||
{
|
||||
lf_queue_iterator result;
|
||||
result.m_ptr = m_ptr;
|
||||
m_ptr = m_ptr->m_link;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Owning pointer to the linked list taken from the lf_queue<>
|
||||
template <typename T>
|
||||
class lf_queue_slice
|
||||
{
|
||||
lf_queue_item<T>* m_head = nullptr;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue;
|
||||
|
||||
public:
|
||||
constexpr lf_queue_slice() = default;
|
||||
|
||||
lf_queue_slice(const lf_queue_slice&) = delete;
|
||||
|
||||
lf_queue_slice(lf_queue_slice&& r) noexcept
|
||||
: m_head(r.m_head)
|
||||
{
|
||||
r.m_head = nullptr;
|
||||
}
|
||||
|
||||
lf_queue_slice& operator =(const lf_queue_slice&) = delete;
|
||||
|
||||
lf_queue_slice& operator =(lf_queue_slice&& r) noexcept
|
||||
{
|
||||
if (this != &r)
|
||||
{
|
||||
delete m_head;
|
||||
m_head = r.m_head;
|
||||
r.m_head = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
_mm_lfence();
|
||||
m_data[pos % N] = std::forward<T2>(data);
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
~lf_queue_slice()
|
||||
// Try to get push pointer (producer only)
|
||||
operator T*()
|
||||
{
|
||||
delete m_head;
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
return m_data + (pos % N);
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
return m_head->m_data;
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop < N)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
// Unsafe access
|
||||
T& get_push(std::size_t i)
|
||||
{
|
||||
return &m_head->m_data;
|
||||
_mm_lfence();
|
||||
return m_data[(m_push + i) % N];
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
// Try to pop (consumer only)
|
||||
template <typename T2>
|
||||
bool try_pop(T2& out)
|
||||
{
|
||||
return m_head != nullptr;
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
out = std::move(m_data[pos % N]);
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
T* get() const
|
||||
// Increment pop counter (consumer only)
|
||||
void end_pop()
|
||||
{
|
||||
return m_head ? &m_head->m_data : nullptr;
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos > 0)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
lf_queue_iterator<T> begin() const
|
||||
// Get size (consumer only)
|
||||
std::uint32_t size() const
|
||||
{
|
||||
lf_queue_iterator<T> result;
|
||||
result.m_ptr = m_head;
|
||||
return result;
|
||||
return m_push - m_pop;
|
||||
}
|
||||
|
||||
lf_queue_iterator<T> end() const
|
||||
// Direct access (consumer only)
|
||||
T& operator [](std::size_t i)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
lf_queue_slice& pop_front()
|
||||
{
|
||||
delete std::exchange(m_head, std::exchange(m_head->m_link, nullptr));
|
||||
return *this;
|
||||
_mm_lfence();
|
||||
return m_data[(m_pop + i) % N];
|
||||
}
|
||||
};
|
||||
|
||||
class lf_queue_base
|
||||
// Fixed-size multi-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_mpsc : lf_spsc<T, N>
|
||||
{
|
||||
protected:
|
||||
atomic_t<std::uintptr_t> m_head = 0;
|
||||
using lf_spsc<T, N>::m_push;
|
||||
using lf_spsc<T, N>::m_pop;
|
||||
using lf_spsc<T, N>::m_data;
|
||||
|
||||
void imp_notify();
|
||||
|
||||
public:
|
||||
// Wait for new elements pushed, no other thread shall call wait() or pop_all() simultaneously
|
||||
bool wait(u64 usec_timeout = -1);
|
||||
};
|
||||
|
||||
// Linked list-based multi-producer queue (the consumer drains the whole queue at once)
|
||||
template <typename T>
|
||||
class lf_queue : public lf_queue_base
|
||||
{
|
||||
using lf_queue_base::m_head;
|
||||
|
||||
// Extract all elements and reverse element order (FILO to FIFO)
|
||||
lf_queue_item<T>* reverse() noexcept
|
||||
enum : std::uint64_t
|
||||
{
|
||||
if (auto* head = m_head.load() ? reinterpret_cast<lf_queue_item<T>*>(m_head.exchange(0)) : nullptr)
|
||||
c_ack = 1ull << 0,
|
||||
c_rel = 1ull << 32,
|
||||
};
|
||||
|
||||
atomic_t<std::uint64_t> m_lock{0};
|
||||
|
||||
void release(std::uint64_t value)
|
||||
{
|
||||
// Push all pending elements at once when possible
|
||||
if (value && value % c_rel == value / c_rel)
|
||||
{
|
||||
if (auto* prev = head->m_link)
|
||||
{
|
||||
head->m_link = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
auto* pprev = prev->m_link;
|
||||
prev->m_link = head;
|
||||
head = std::exchange(prev, pprev);
|
||||
}
|
||||
while (prev);
|
||||
}
|
||||
|
||||
return head;
|
||||
_mm_sfence();
|
||||
m_push += value % c_rel;
|
||||
m_lock.compare_and_swap_test(value, 0);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr lf_queue() = default;
|
||||
constexpr lf_mpsc() = default;
|
||||
|
||||
~lf_queue()
|
||||
// Try to get push pointer
|
||||
operator T*()
|
||||
{
|
||||
delete reinterpret_cast<lf_queue_item<T>*>(m_head.load());
|
||||
}
|
||||
const std::uint64_t old = m_lock.fetch_add(c_ack);
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
template <typename... Args>
|
||||
void push(Args&&... args)
|
||||
{
|
||||
auto _old = m_head.load();
|
||||
auto* item = new lf_queue_item<T>(_old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old), std::forward<Args>(args)...);
|
||||
|
||||
while (!m_head.compare_exchange(_old, reinterpret_cast<std::uint64_t>(item)))
|
||||
if (old % N >= N || pos - m_pop >= N - (old % N))
|
||||
{
|
||||
item->m_link = _old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old);
|
||||
release(m_lock.sub_fetch(c_ack));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (_old & 1)
|
||||
{
|
||||
lf_queue_base::imp_notify();
|
||||
}
|
||||
return m_data + ((pos + old) % N);
|
||||
}
|
||||
|
||||
// Withdraw the list, supports range-for loop: for (auto&& x : y.pop_all()) ...
|
||||
lf_queue_slice<T> pop_all()
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
lf_queue_slice<T> result;
|
||||
result.m_head = reverse();
|
||||
return result;
|
||||
release(m_lock.add_fetch(c_rel));
|
||||
}
|
||||
|
||||
// Apply func(data) to each element, return the total length
|
||||
template <typename F>
|
||||
std::size_t apply(F func)
|
||||
// Try to push
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
|
||||
for (auto slice = pop_all(); slice; slice.pop_front())
|
||||
if (T* ptr = *this)
|
||||
{
|
||||
std::invoke(func, *slice);
|
||||
*ptr = std::forward<T2>(data);
|
||||
end_push();
|
||||
return true;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
// Assignable lock-free thread-safe value of any type (memory-inefficient)
|
||||
template <typename T>
|
||||
class lf_value final
|
||||
{
|
||||
atomic_t<lf_value*> m_head;
|
||||
|
||||
T m_data;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit constexpr lf_value(Args&&... args)
|
||||
: m_head(this)
|
||||
, m_data(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
~lf_value()
|
||||
{
|
||||
// All values are kept in the queue until the end
|
||||
for (lf_value* ptr = m_head.load(); ptr != this;)
|
||||
{
|
||||
delete std::exchange(ptr, std::exchange(ptr->m_head.raw(), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
// Get current head, allows to inspect old values
|
||||
[[nodiscard]] const lf_value* head() const
|
||||
{
|
||||
return m_head.load();
|
||||
}
|
||||
|
||||
// Inspect the initial (oldest) value
|
||||
[[nodiscard]] const T& first() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T& get() const
|
||||
{
|
||||
return m_head.load()->m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator const T&() const
|
||||
{
|
||||
return m_head.load()->m_data;
|
||||
}
|
||||
|
||||
// Construct new value in-place
|
||||
template <typename... Args>
|
||||
const T& assign(Args&&... args)
|
||||
{
|
||||
lf_value* val = new lf_value(std::forward<Args>(args)...);
|
||||
lf_value* old = m_head.load();
|
||||
|
||||
do
|
||||
{
|
||||
val->m_head = old;
|
||||
}
|
||||
while (!m_head.compare_exchange(old, val));
|
||||
|
||||
return val->m_data;
|
||||
}
|
||||
|
||||
// Copy-assign new value
|
||||
const T& operator =(const T& value)
|
||||
{
|
||||
return assign(value);
|
||||
}
|
||||
|
||||
// Move-assign new value
|
||||
const T& operator =(T&& value)
|
||||
{
|
||||
return assign(std::move(value));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable consumer methods
|
||||
using lf_spsc<T, N>::try_pop;
|
||||
using lf_spsc<T, N>::end_pop;
|
||||
using lf_spsc<T, N>::size;
|
||||
using lf_spsc<T, N>::operator [];
|
||||
};
|
||||
|
||||
+264
-188
@@ -1,258 +1,334 @@
|
||||
#include "mutex.h"
|
||||
#include "sync.h"
|
||||
|
||||
void shared_mutex::imp_lock_shared(u32 val)
|
||||
#include <climits>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
// TLS variable for tracking owned mutexes
|
||||
thread_local std::vector<shared_mutex*> g_tls_locks;
|
||||
|
||||
void shared_mutex::imp_lock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_shared())
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value >= c_min && m_value.compare_and_swap_test(value, value - c_min))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
#ifdef _WIN32
|
||||
// Acquire writer lock
|
||||
imp_wait(m_value.load());
|
||||
|
||||
if (old == 0)
|
||||
// Convert to reader lock
|
||||
s64 value = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
// Proceed exclusively
|
||||
return;
|
||||
|
||||
if (value != 0)
|
||||
{
|
||||
lock_downgrade();
|
||||
return;
|
||||
imp_unlock(value);
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_shared(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_one == 0)
|
||||
// Wait as a reader if necessary
|
||||
if (value + c_one - c_min < 0)
|
||||
{
|
||||
imp_signal();
|
||||
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Acquire writer lock
|
||||
imp_wait(0);
|
||||
|
||||
void shared_mutex::imp_lock_low(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
// Convert to reader lock
|
||||
m_value += c_one - c_min;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
// Disabled code
|
||||
while (false)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_low())
|
||||
const s64 value0 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
lock_downgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_low(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_vip == 0)
|
||||
{
|
||||
imp_signal();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_vip(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_vip())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
lock_downgrade_to_vip();
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade_to_vip();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_vip(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_one / c_vip == 0)
|
||||
{
|
||||
imp_signal();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_wait()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const auto [old, ok] = m_value.fetch_op([](u32& value)
|
||||
{
|
||||
if (value >= c_sig)
|
||||
if (value >= c_min)
|
||||
{
|
||||
value -= c_sig;
|
||||
return true;
|
||||
value -= c_min;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (ok)
|
||||
if (value0 >= c_min)
|
||||
{
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
m_value.wait(old);
|
||||
// Acquire writer lock
|
||||
imp_wait(value0);
|
||||
|
||||
// Convert to reader lock
|
||||
s64 value1 = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
if (value1 != 0)
|
||||
{
|
||||
imp_unlock(value1);
|
||||
}
|
||||
|
||||
value1 += c_one - c_min;
|
||||
|
||||
if (value1 > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait as a reader if necessary
|
||||
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value1 >> 32), nullptr, nullptr, INT_MIN))
|
||||
{
|
||||
value1 = m_value.load();
|
||||
|
||||
if (value1 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If blocked by writers, release the reader lock and try again
|
||||
const s64 value2 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
value += c_min;
|
||||
}
|
||||
});
|
||||
|
||||
if (value2 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp_unlock_shared(value2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old + c_min <= c_max;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((_old + c_min) % c_one == 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_signal()
|
||||
void shared_mutex::imp_wait(s64)
|
||||
{
|
||||
m_value += c_sig;
|
||||
m_value.notify_one();
|
||||
#ifdef _WIN32
|
||||
if (m_value.sub_fetch(c_one))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
if (!m_value.sub_fetch(c_one))
|
||||
{
|
||||
// Return immediately if locked
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Load new value, try to acquire c_sig
|
||||
const s64 value = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
value += c_sig;
|
||||
}
|
||||
});
|
||||
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value >> 32), nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock(u32 val)
|
||||
void shared_mutex::imp_lock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (!m_value && try_lock())
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value == c_one && m_value.compare_and_swap_test(c_one, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
imp_wait(m_value.load());
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock(u32 old)
|
||||
void shared_mutex::imp_unlock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - c_one < c_err;
|
||||
verify("shared_mutex overflow" HERE), _old + c_one <= c_max;
|
||||
|
||||
// 1) Notify the next writer if necessary
|
||||
// 2) Notify all readers otherwise if necessary (currently indistinguishable from writers)
|
||||
if (old - c_one)
|
||||
// 2) Notify all readers otherwise if necessary
|
||||
#ifdef _WIN32
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
imp_signal();
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
else if (s64 count = -_old / c_min * 0)
|
||||
{
|
||||
// Disabled code
|
||||
while (count--)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
else if (false)
|
||||
{
|
||||
// Disabled code
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, INT_MAX, nullptr, nullptr, INT_MIN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_upgrade()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_upgrade())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to writer lock
|
||||
const u32 old = m_value.fetch_add(c_one - 1);
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one - 1 < c_sig;
|
||||
|
||||
if (old % c_one == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp_wait();
|
||||
// TODO
|
||||
unlock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_unlock()
|
||||
void shared_mutex::imp_lock_degrade()
|
||||
{
|
||||
u32 _max = 1;
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
const u32 val = m_value;
|
||||
|
||||
if (val % c_one == 0 && (val / c_one < _max || val >= c_sig))
|
||||
{
|
||||
// Return if have cought a state where:
|
||||
// 1) Mutex is free
|
||||
// 2) Total number of waiters decreased since last check
|
||||
// 3) Signal bit is set (if used on the platform)
|
||||
return;
|
||||
}
|
||||
|
||||
_max = val / c_one;
|
||||
|
||||
busy_wait(1500);
|
||||
}
|
||||
|
||||
// Lock and unlock
|
||||
if (!m_value.fetch_add(c_one))
|
||||
{
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
imp_wait();
|
||||
// TODO
|
||||
unlock();
|
||||
lock_shared();
|
||||
}
|
||||
|
||||
bool shared_mutex::downgrade_unique_vip_lock_to_low_or_unlock()
|
||||
bool shared_mutex::try_lock_shared()
|
||||
{
|
||||
return m_value.atomic_op([](u32& value)
|
||||
// Conditional decrement
|
||||
return m_value.fetch_op([](s64& value) { if (value >= c_min) value -= c_min; }) >= c_min;
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock()
|
||||
{
|
||||
// Conditional decrement (TODO: obtain c_sig)
|
||||
return m_value.compare_and_swap_test(c_one, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_upgrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(c_one - c_min, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_degrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(0, c_one - c_min);
|
||||
}
|
||||
|
||||
safe_reader_lock::safe_reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
, m_is_owned(false)
|
||||
{
|
||||
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
|
||||
{
|
||||
if (value % c_one / c_vip == 1)
|
||||
m_is_owned = true;
|
||||
|
||||
if (m_is_owned)
|
||||
{
|
||||
value -= c_vip - 1;
|
||||
return true;
|
||||
m_mutex.lock_shared();
|
||||
g_tls_locks.emplace_back(&m_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
value -= c_vip;
|
||||
return false;
|
||||
});
|
||||
// TODO: order locks
|
||||
}
|
||||
}
|
||||
|
||||
safe_reader_lock::~safe_reader_lock()
|
||||
{
|
||||
if (m_is_owned)
|
||||
{
|
||||
m_mutex.unlock_shared();
|
||||
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: order locks
|
||||
}
|
||||
|
||||
safe_writer_lock::safe_writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
, m_is_owned(false)
|
||||
, m_is_upgraded(false)
|
||||
{
|
||||
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
|
||||
{
|
||||
m_is_owned = true;
|
||||
|
||||
if (m_is_owned)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
g_tls_locks.emplace_back(&m_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: order locks
|
||||
}
|
||||
|
||||
if (m_mutex.is_reading())
|
||||
{
|
||||
m_is_upgraded = true;
|
||||
m_mutex.lock_upgrade();
|
||||
}
|
||||
}
|
||||
|
||||
safe_writer_lock::~safe_writer_lock()
|
||||
{
|
||||
if (m_is_upgraded)
|
||||
{
|
||||
m_mutex.lock_degrade();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_owned)
|
||||
{
|
||||
m_mutex.unlock();
|
||||
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: order locks
|
||||
}
|
||||
|
||||
+120
-218
@@ -1,171 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Shared mutex with small size (u32).
|
||||
// Shared mutex.
|
||||
class shared_mutex final
|
||||
{
|
||||
enum : u32
|
||||
enum : s64
|
||||
{
|
||||
c_one = 1u << 14, // Fixed-point 1.0 value (one writer, max_readers = c_one - 1)
|
||||
c_sig = 1u << 30,
|
||||
c_err = 1u << 31,
|
||||
c_vip = 1u << 7,
|
||||
c_one = 1ull << 31, // Fixed-point 1.0 value (one writer)
|
||||
c_min = 0x00000001, // Fixed-point 1.0/max_readers value
|
||||
c_sig = 1ull << 62,
|
||||
c_max = c_one
|
||||
};
|
||||
|
||||
atomic_t<u32> m_value{};
|
||||
atomic_t<s64> m_value{c_one}; // Semaphore-alike counter
|
||||
|
||||
void imp_lock_shared(s64 _old);
|
||||
void imp_unlock_shared(s64 _old);
|
||||
void imp_wait(s64 _old);
|
||||
void imp_lock(s64 _old);
|
||||
void imp_unlock(s64 _old);
|
||||
|
||||
void imp_lock_shared(u32 val);
|
||||
void imp_unlock_shared(u32 old);
|
||||
void imp_lock_low(u32 val);
|
||||
void imp_unlock_low(u32 old);
|
||||
void imp_lock_vip(u32 val);
|
||||
void imp_unlock_vip(u32 old);
|
||||
void imp_wait();
|
||||
void imp_signal();
|
||||
void imp_lock(u32 val);
|
||||
void imp_unlock(u32 old);
|
||||
void imp_lock_upgrade();
|
||||
void imp_lock_unlock();
|
||||
void imp_lock_degrade();
|
||||
|
||||
public:
|
||||
constexpr shared_mutex() = default;
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_one - 1 && m_value.compare_and_swap_test(value, value + 1);
|
||||
}
|
||||
bool try_lock_shared();
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_one - 1 || !m_value.compare_and_swap_test(value, value + 1)))
|
||||
// Fast path: decrement if positive
|
||||
if (UNLIKELY(value < c_min || value > c_one || !m_value.compare_and_swap_test(value, value - c_min)))
|
||||
{
|
||||
imp_lock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
void lock_shared_hle()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (LIKELY(value < c_one - 1))
|
||||
{
|
||||
u32 old = value;
|
||||
if (LIKELY(atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), old, value + 1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
imp_lock_shared(value);
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(1);
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_min);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
if (value < 0 || value > c_one - c_min)
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared_hle()
|
||||
{
|
||||
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), -1);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_low()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_vip - 1 && m_value.compare_and_swap_test(value, value + 1);
|
||||
}
|
||||
|
||||
void lock_low()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_vip - 1 || !m_value.compare_and_swap_test(value, value + 1)))
|
||||
{
|
||||
imp_lock_low(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_low()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_low(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_vip()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_one - 1 && (value % c_vip) == 0 && m_value.compare_and_swap_test(value, value + c_vip);
|
||||
}
|
||||
|
||||
void lock_vip()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_one - 1 || (value % c_vip) || !m_value.compare_and_swap_test(value, value + c_vip)))
|
||||
{
|
||||
imp_lock_vip(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_vip()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(c_vip);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_vip(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return m_value.compare_and_swap_test(0, c_one);
|
||||
}
|
||||
bool try_lock();
|
||||
|
||||
void lock()
|
||||
{
|
||||
const u32 value = m_value.compare_and_swap(0, c_one);
|
||||
// Try to lock
|
||||
const s64 value = m_value.compare_and_swap(c_one, 0);
|
||||
|
||||
if (UNLIKELY(value))
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
}
|
||||
|
||||
void lock_hle()
|
||||
{
|
||||
u32 value = 0;
|
||||
|
||||
if (UNLIKELY(!atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), value, c_one)))
|
||||
if (value != c_one)
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
@@ -173,96 +67,69 @@ public:
|
||||
|
||||
void unlock()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(c_one);
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_one);
|
||||
|
||||
if (UNLIKELY(value != c_one))
|
||||
if (value != 0)
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_hle()
|
||||
{
|
||||
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), 0u - c_one);
|
||||
|
||||
if (UNLIKELY(value != c_one))
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment, try to convert a single reader into a writer, ignoring other writers
|
||||
return (value + c_one - 1) % c_one == 0 && m_value.compare_and_swap_test(value, value + c_one - 1);
|
||||
}
|
||||
bool try_lock_upgrade();
|
||||
|
||||
void lock_upgrade()
|
||||
{
|
||||
if (UNLIKELY(!try_lock_upgrade()))
|
||||
if (!m_value.compare_and_swap_test(c_one - c_min, 0))
|
||||
{
|
||||
imp_lock_upgrade();
|
||||
}
|
||||
}
|
||||
|
||||
void lock_downgrade()
|
||||
{
|
||||
// Convert to reader lock (can result in broken state)
|
||||
m_value -= c_one - 1;
|
||||
}
|
||||
bool try_lock_degrade();
|
||||
|
||||
void lock_downgrade_to_vip()
|
||||
void lock_degrade()
|
||||
{
|
||||
// Convert to vip lock (can result in broken state)
|
||||
m_value -= c_one - c_vip;
|
||||
}
|
||||
|
||||
// Optimized wait for lockability without locking, relaxed
|
||||
void lock_unlock()
|
||||
{
|
||||
if (UNLIKELY(m_value != 0))
|
||||
if (!m_value.compare_and_swap_test(0, c_one - c_min))
|
||||
{
|
||||
imp_lock_unlock();
|
||||
imp_lock_degrade();
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether can immediately obtain an exclusive (writer) lock
|
||||
bool is_free() const
|
||||
bool is_reading() const
|
||||
{
|
||||
return m_value.load() == 0;
|
||||
return (m_value.load() % c_one) != 0;
|
||||
}
|
||||
|
||||
// Check whether can immediately obtain a shared (reader) lock
|
||||
bool is_lockable() const
|
||||
{
|
||||
return m_value.load() < c_one - 1;
|
||||
}
|
||||
|
||||
// Special purpose logic
|
||||
bool downgrade_unique_vip_lock_to_low_or_unlock();
|
||||
};
|
||||
|
||||
// Simplified shared (reader) lock implementation. Mutually incompatible with low_lock and vip_lock.
|
||||
// Simplified shared (reader) lock implementation.
|
||||
class reader_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_upgraded = false;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_upgraded ? m_mutex.lock() : m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
reader_lock(const reader_lock&) = delete;
|
||||
|
||||
reader_lock& operator=(const reader_lock&) = delete;
|
||||
|
||||
explicit reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
// One-way lock upgrade; note that the observed state could have been changed
|
||||
// One-way lock upgrade
|
||||
void upgrade()
|
||||
{
|
||||
if (!m_upgraded)
|
||||
@@ -272,58 +139,93 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Try to upgrade; if it succeeds, the observed state has NOT been changed
|
||||
bool try_upgrade()
|
||||
{
|
||||
return m_upgraded || (m_upgraded = m_mutex.try_lock_upgrade());
|
||||
}
|
||||
|
||||
~reader_lock()
|
||||
{
|
||||
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Special shared (reader) lock, mutually exclusive with vip locks. Mutually incompatible with normal shared (reader) lock.
|
||||
class low_lock final
|
||||
// Simplified exclusive (writer) lock implementation.
|
||||
class writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
public:
|
||||
low_lock(const low_lock&) = delete;
|
||||
|
||||
low_lock& operator=(const low_lock&) = delete;
|
||||
|
||||
explicit low_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock_low();
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
~low_lock()
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock_low();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
|
||||
explicit writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~writer_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Special shared (reader) lock, mutually exclusive with low locks. Mutually incompatible with normal shared (reader) lock.
|
||||
class vip_lock final
|
||||
// Safe reader lock. Can be recursive above other safe locks (reader or writer).
|
||||
class safe_reader_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_is_owned;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock_shared();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
vip_lock(const vip_lock&) = delete;
|
||||
safe_reader_lock(const safe_reader_lock&) = delete;
|
||||
|
||||
vip_lock& operator=(const vip_lock&) = delete;
|
||||
explicit safe_reader_lock(shared_mutex& mutex);
|
||||
|
||||
explicit vip_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock_vip();
|
||||
}
|
||||
|
||||
~vip_lock()
|
||||
{
|
||||
m_mutex.unlock_vip();
|
||||
}
|
||||
~safe_reader_lock();
|
||||
};
|
||||
|
||||
// Safe writer lock. Can be recursive above other safe locks. Performs upgrade and degrade operations above existing reader lock if necessary.
|
||||
class safe_writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_is_owned;
|
||||
bool m_is_upgraded;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
safe_writer_lock(const safe_writer_lock&) = delete;
|
||||
|
||||
explicit safe_writer_lock(shared_mutex& mutex);
|
||||
|
||||
~safe_writer_lock();
|
||||
};
|
||||
|
||||
+5
-1
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#ifndef PUGIXML_HEADER_ONLY
|
||||
#define PUGIXML_HEADER_ONLY 1
|
||||
#endif // !PUGIXML_HEADER_ONLY
|
||||
#include "pugixml.hpp"
|
||||
#undef PUGIXML_HEADER_ONLY
|
||||
|
||||
struct rXmlNode
|
||||
{
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
|
||||
// Based on variant/recursive_wrapper.hpp from boost.
|
||||
//
|
||||
// Original license:
|
||||
//
|
||||
// Copyright (c) 2002-2003
|
||||
// Eric Friedman, Itay Maman
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
//namespace mapbox {
|
||||
//namespace util {
|
||||
namespace std {
|
||||
|
||||
template <typename T>
|
||||
class recursive_wrapper
|
||||
{
|
||||
|
||||
T* p_;
|
||||
|
||||
void assign(T const& rhs)
|
||||
{
|
||||
this->get() = rhs;
|
||||
}
|
||||
|
||||
public:
|
||||
using type = T;
|
||||
|
||||
/**
|
||||
* Default constructor default initializes the internally stored value.
|
||||
* For POD types this means nothing is done and the storage is
|
||||
* uninitialized.
|
||||
*
|
||||
* @throws std::bad_alloc if there is insufficient memory for an object
|
||||
* of type T.
|
||||
* @throws any exception thrown by the default constructur of T.
|
||||
*/
|
||||
recursive_wrapper()
|
||||
: p_(new T){}
|
||||
|
||||
~recursive_wrapper() noexcept { delete p_; }
|
||||
|
||||
recursive_wrapper(recursive_wrapper const& operand)
|
||||
: p_(new T(operand.get())) {}
|
||||
|
||||
recursive_wrapper(T const& operand)
|
||||
: p_(new T(operand)) {}
|
||||
|
||||
recursive_wrapper(recursive_wrapper&& operand)
|
||||
: p_(new T(std::move(operand.get()))) {}
|
||||
|
||||
recursive_wrapper(T&& operand)
|
||||
: p_(new T(std::move(operand))) {}
|
||||
|
||||
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
|
||||
{
|
||||
assign(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline recursive_wrapper& operator=(T const& rhs)
|
||||
{
|
||||
assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void swap(recursive_wrapper& operand) noexcept
|
||||
{
|
||||
T* temp = operand.p_;
|
||||
operand.p_ = p_;
|
||||
p_ = temp;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
|
||||
{
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(T&& rhs)
|
||||
{
|
||||
get() = std::move(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& get()
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T const& get() const
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T* get_pointer() { return p_; }
|
||||
|
||||
const T* get_pointer() const { return p_; }
|
||||
|
||||
operator T const&() const { return this->get(); }
|
||||
|
||||
operator T&() { return this->get(); }
|
||||
|
||||
}; // class recursive_wrapper
|
||||
|
||||
template <typename T>
|
||||
inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
}
|
||||
//} // namespace util
|
||||
//} // namespace mapbox
|
||||
|
||||
#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
+32
-6
@@ -1,4 +1,5 @@
|
||||
#include "sema.h"
|
||||
#include "sync.h"
|
||||
|
||||
void semaphore_base::imp_wait()
|
||||
{
|
||||
@@ -14,10 +15,18 @@ void semaphore_base::imp_wait()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const s32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (value <= 0)
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
while (true)
|
||||
{
|
||||
// Try hard way
|
||||
const s32 value = m_value.atomic_op([](s32& value)
|
||||
const s32 value = m_value.op_fetch([](s32& value)
|
||||
{
|
||||
// Use sign bit to acknowledge waiter presence
|
||||
if (value && value > INT32_MIN)
|
||||
@@ -35,26 +44,43 @@ void semaphore_base::imp_wait()
|
||||
// Set sign bit
|
||||
value = INT32_MIN;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
if (value >= 0)
|
||||
{
|
||||
// Signal other waiter to wake up or to restore sign bit
|
||||
m_value.notify_one();
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
m_value.wait(value);
|
||||
futex(&m_value.raw(), FUTEX_WAIT_PRIVATE, value, nullptr, nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void semaphore_base::imp_post(s32 _old)
|
||||
{
|
||||
verify("semaphore_base: overflow" HERE), _old < 0;
|
||||
|
||||
m_value.notify_one();
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool semaphore_base::try_wait()
|
||||
{
|
||||
// Conditional decrement
|
||||
const s32 value = m_value.fetch_op([](s32& value)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
value -= 1;
|
||||
}
|
||||
});
|
||||
|
||||
return value > 0;
|
||||
}
|
||||
|
||||
bool semaphore_base::try_post(s32 _max)
|
||||
|
||||
+41
-12
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight semaphore helper class
|
||||
class semaphore_base
|
||||
@@ -14,6 +13,8 @@ class semaphore_base
|
||||
|
||||
void imp_post(s32 _old);
|
||||
|
||||
friend class semaphore_lock;
|
||||
|
||||
protected:
|
||||
explicit constexpr semaphore_base(s32 value)
|
||||
: m_value{value}
|
||||
@@ -32,10 +33,7 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return m_value.try_dec(0);
|
||||
}
|
||||
bool try_wait();
|
||||
|
||||
void post(s32 _max)
|
||||
{
|
||||
@@ -75,36 +73,36 @@ class semaphore final : public semaphore_base
|
||||
public:
|
||||
// Default constructor (recommended)
|
||||
constexpr semaphore()
|
||||
: base(Def)
|
||||
: base{Def}
|
||||
{
|
||||
}
|
||||
|
||||
// Explicit value constructor (not recommended)
|
||||
explicit constexpr semaphore(s32 value)
|
||||
: base(value)
|
||||
: base{value}
|
||||
{
|
||||
}
|
||||
|
||||
// Obtain a semaphore
|
||||
void lock()
|
||||
void wait()
|
||||
{
|
||||
return base::wait();
|
||||
}
|
||||
|
||||
// Try to obtain a semaphore
|
||||
bool try_lock()
|
||||
explicit_bool_t try_wait()
|
||||
{
|
||||
return base::try_wait();
|
||||
}
|
||||
|
||||
// Return a semaphore
|
||||
void unlock()
|
||||
void post()
|
||||
{
|
||||
return base::post(Max);
|
||||
}
|
||||
|
||||
// Try to return a semaphore
|
||||
bool try_unlock()
|
||||
explicit_bool_t try_post()
|
||||
{
|
||||
return base::try_post(Max);
|
||||
}
|
||||
@@ -115,3 +113,34 @@ public:
|
||||
return Max;
|
||||
}
|
||||
};
|
||||
|
||||
class semaphore_lock
|
||||
{
|
||||
semaphore_base& m_base;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_base.wait();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_base.post(INT32_MAX);
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
explicit semaphore_lock(const semaphore_lock&) = delete;
|
||||
|
||||
semaphore_lock(semaphore_base& sema)
|
||||
: m_base(sema)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~semaphore_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
+23
-183
@@ -3,7 +3,7 @@
|
||||
/* For internal use. Don't include. */
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
#include "dynamic_library.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
@@ -25,12 +24,10 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtReleaseKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtDelayExecution, NTSTATUS(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval));
|
||||
inline utils::dynamic_import<BOOL(volatile VOID* Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds)> OptWaitOnAddress("kernel32.dll", "WaitOnAddress");
|
||||
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressSingle("kernel32.dll", "WakeByAddressSingle");
|
||||
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressAll("kernel32.dll", "WakeByAddressAll");
|
||||
#endif
|
||||
|
||||
#ifndef __linux__
|
||||
@@ -49,37 +46,37 @@ enum
|
||||
};
|
||||
#endif
|
||||
|
||||
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, uint mask = 0)
|
||||
inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int* uaddr2, int val3)
|
||||
{
|
||||
#ifdef __linux__
|
||||
return syscall(SYS_futex, uaddr, futex_op, static_cast<int>(val), timeout, nullptr, static_cast<int>(mask));
|
||||
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3);
|
||||
#else
|
||||
static struct futex_manager
|
||||
static struct futex_map
|
||||
{
|
||||
struct waiter
|
||||
{
|
||||
uint val;
|
||||
int val;
|
||||
uint mask;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
std::mutex mutex;
|
||||
std::unordered_multimap<volatile void*, waiter*, pointer_hash<volatile void, alignof(int)>> map;
|
||||
std::unordered_multimap<int*, waiter*, pointer_hash<int>> map;
|
||||
|
||||
int operator()(volatile void* uaddr, int futex_op, uint val, const timespec* timeout, uint mask)
|
||||
int operator()(int* uaddr, int futex_op, int val, const timespec* timeout, int*, uint val3)
|
||||
{
|
||||
std::unique_lock lock(mutex);
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
switch (futex_op)
|
||||
{
|
||||
case FUTEX_WAIT_PRIVATE:
|
||||
case FUTEX_WAIT:
|
||||
{
|
||||
mask = -1;
|
||||
[[fallthrough]];
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
}
|
||||
case FUTEX_WAIT_BITSET_PRIVATE:
|
||||
case FUTEX_WAIT_BITSET:
|
||||
{
|
||||
if (*reinterpret_cast<volatile uint*>(uaddr) != val)
|
||||
if (*(volatile int*)uaddr != val)
|
||||
{
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
@@ -87,7 +84,7 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
||||
|
||||
waiter rec;
|
||||
rec.val = val;
|
||||
rec.mask = mask;
|
||||
rec.mask = val3;
|
||||
const auto& ref = *map.emplace(uaddr, &rec);
|
||||
|
||||
int res = 0;
|
||||
@@ -110,25 +107,25 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
map.erase(std::find(map.find(uaddr), map.end(), ref));
|
||||
return res;
|
||||
}
|
||||
|
||||
case FUTEX_WAKE_PRIVATE:
|
||||
case FUTEX_WAKE:
|
||||
{
|
||||
mask = -1;
|
||||
[[fallthrough]];
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
}
|
||||
case FUTEX_WAKE_BITSET_PRIVATE:
|
||||
case FUTEX_WAKE_BITSET:
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (auto range = map.equal_range(uaddr); val && range.first != range.second; range.first++)
|
||||
{
|
||||
auto& entry = *range.first->second;
|
||||
|
||||
if (entry.mask & mask)
|
||||
|
||||
if (entry.mask & val3)
|
||||
{
|
||||
entry.cv.notify_one();
|
||||
entry.mask = 0;
|
||||
@@ -146,163 +143,6 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
||||
}
|
||||
} g_futex;
|
||||
|
||||
return g_futex(uaddr, futex_op, val, timeout, mask);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename Pred>
|
||||
bool balanced_wait_until(atomic_t<T>& var, u64 usec_timeout, Pred&& pred)
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
|
||||
|
||||
const bool is_inf = usec_timeout > u64{UINT32_MAX / 1000} * 1000000;
|
||||
|
||||
// Optional second argument indicates that the predicate should try to retire
|
||||
auto test_pred = [&](T& _new, auto... args)
|
||||
{
|
||||
T old = var.load();
|
||||
|
||||
while (true)
|
||||
{
|
||||
_new = old;
|
||||
|
||||
// Zero indicates failure without modifying the value
|
||||
// Negative indicates failure but modifies the value
|
||||
auto ret = std::invoke(std::forward<Pred>(pred), _new, args...);
|
||||
|
||||
if (LIKELY(!ret || var.compare_exchange(old, _new)))
|
||||
{
|
||||
return ret > 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
T value;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (OptWaitOnAddress)
|
||||
{
|
||||
while (!test_pred(value))
|
||||
{
|
||||
if (OptWaitOnAddress(&var, &value, sizeof(T), is_inf ? INFINITE : usec_timeout / 1000))
|
||||
{
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_TIMEOUT)
|
||||
{
|
||||
// Retire
|
||||
return test_pred(value, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = usec_timeout * -10;
|
||||
|
||||
if (!usec_timeout || NtWaitForKeyedEvent(nullptr, &var, false, is_inf ? nullptr : &timeout))
|
||||
{
|
||||
// Timed out: retire
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Signaled in the last moment: restore balance
|
||||
NtWaitForKeyedEvent(nullptr, &var, false, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
// Stolen notification: restore balance
|
||||
NtReleaseKeyedEvent(nullptr, &var, false, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
struct timespec timeout;
|
||||
timeout.tv_sec = usec_timeout / 1000000;
|
||||
timeout.tv_nsec = (usec_timeout % 1000000) * 1000;
|
||||
|
||||
char* ptr = reinterpret_cast<char*>(&var);
|
||||
|
||||
if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
ptr += 4 * IS_BE_MACHINE;
|
||||
}
|
||||
|
||||
while (!test_pred(value))
|
||||
{
|
||||
if (futex(ptr, FUTEX_WAIT_PRIVATE, static_cast<u32>(value), is_inf ? nullptr : &timeout) == 0)
|
||||
{
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
switch (errno)
|
||||
{
|
||||
case EAGAIN: break;
|
||||
case ETIMEDOUT: return test_pred(value, nullptr);
|
||||
default: verify("Unknown futex error" HERE), 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <bool All = false, typename T>
|
||||
void balanced_awaken(atomic_t<T>& var, u32 weight)
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (OptWaitOnAddress)
|
||||
{
|
||||
if (All || weight > 3)
|
||||
{
|
||||
OptWakeByAddressAll(&var);
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < weight; i++)
|
||||
{
|
||||
OptWakeByAddressSingle(&var);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < weight; i++)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &var, false, nullptr);
|
||||
}
|
||||
#else
|
||||
char* ptr = reinterpret_cast<char*>(&var);
|
||||
|
||||
if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
ptr += 4 * IS_BE_MACHINE;
|
||||
}
|
||||
|
||||
if (All || weight)
|
||||
{
|
||||
futex(ptr, FUTEX_WAKE_PRIVATE, All ? INT_MAX : std::min<u32>(INT_MAX, weight));
|
||||
}
|
||||
|
||||
return;
|
||||
return g_futex(uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||
#endif
|
||||
}
|
||||
|
||||
Executable → Regular
+2
-179
@@ -1,74 +1,12 @@
|
||||
#include "sysinfo.h"
|
||||
#include "sysinfo.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#include "sysinfoapi.h"
|
||||
#include "subauth.h"
|
||||
#include "stringapiset.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
bool utils::has_ssse3()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_sse41()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x80000;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_avx()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000 && (get_cpuid(1, 0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0x6) == 0x6;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_avx2()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && get_cpuid(7, 0)[1] & 0x20 && (get_cpuid(1, 0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0x6) == 0x6;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_rtm()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x800) == 0x800;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_tsx_force_abort()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[3] & 0x2000) == 0x2000;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_mpx()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4000) == 0x4000;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_512()
|
||||
{
|
||||
// Check AVX512F, AVX512CD, AVX512DQ, AVX512BW, AVX512VL extensions (Skylake-X level support)
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0xd0030000) == 0xd0030000 && (get_cpuid(1,0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0xe6) == 0xe6;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_xop()
|
||||
{
|
||||
static const bool g_value = has_avx() && get_cpuid(0x80000001, 0)[2] & 0x800;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
std::string utils::get_system_info()
|
||||
{
|
||||
std::string result;
|
||||
@@ -86,9 +24,8 @@ std::string utils::get_system_info()
|
||||
brand = "Unknown CPU";
|
||||
}
|
||||
|
||||
brand.erase(brand.find_last_not_of('\0') + 1);
|
||||
brand.erase(brand.find_last_not_of(' ') + 1);
|
||||
brand.erase(0, brand.find_first_not_of(' '));
|
||||
brand.erase(brand.find_last_not_of(' ') + 1);
|
||||
|
||||
while (auto found = brand.find(" ") + 1)
|
||||
{
|
||||
@@ -110,129 +47,15 @@ std::string utils::get_system_info()
|
||||
|
||||
fmt::append(result, "%s | %d Threads | %.2f GiB RAM", brand, num_proc, mem_total / (1024.0f * 1024 * 1024));
|
||||
|
||||
if (const ullong tsc_freq = get_tsc_freq())
|
||||
{
|
||||
fmt::append(result, " | TSC: %.02fGHz", tsc_freq / 1000000000.);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(result, " | TSC: Bad");
|
||||
}
|
||||
|
||||
if (has_avx())
|
||||
{
|
||||
result += " | AVX";
|
||||
|
||||
if (has_avx2())
|
||||
{
|
||||
result += '+';
|
||||
}
|
||||
|
||||
if (has_512())
|
||||
{
|
||||
result += '+';
|
||||
}
|
||||
|
||||
if (has_xop())
|
||||
{
|
||||
result += 'x';
|
||||
}
|
||||
}
|
||||
|
||||
if (has_rtm())
|
||||
{
|
||||
result += " | TSX";
|
||||
|
||||
if (has_tsx_force_abort())
|
||||
{
|
||||
result += "-FA";
|
||||
}
|
||||
|
||||
if (!has_mpx())
|
||||
{
|
||||
result += " disabled by default";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string utils::get_firmware_version()
|
||||
{
|
||||
const std::string file_path = g_cfg.vfs.get_dev_flash() + "vsh/etc/version.txt";
|
||||
if (fs::is_file(file_path))
|
||||
{
|
||||
const fs::file version_file = fs::file(file_path);
|
||||
std::string version = version_file.to_string();
|
||||
|
||||
// Extract version
|
||||
const size_t start = version.find_first_of(':') + 1;
|
||||
const size_t end = version.find_first_of(':', start);
|
||||
version = version.substr(start, end - start);
|
||||
|
||||
// Trim version
|
||||
const size_t trim_start = version.find_first_not_of('0');
|
||||
const size_t trim_end = version.find_last_not_of('0');
|
||||
version = version.substr(trim_start, trim_end);
|
||||
|
||||
return version;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string utils::get_OS_version()
|
||||
{
|
||||
std::string output = "";
|
||||
#ifdef _WIN32
|
||||
// GetVersionEx is deprecated, RtlGetVersion is kernel-mode only and AnalyticsInfo is UWP only.
|
||||
// So we're forced to read PEB instead to get Windows version info. It's ugly but works.
|
||||
|
||||
const DWORD peb_offset = 0x60;
|
||||
const INT_PTR peb = __readgsqword(peb_offset);
|
||||
|
||||
const DWORD version_major = *reinterpret_cast<const DWORD*>(peb + 0x118);
|
||||
const DWORD version_minor = *reinterpret_cast<const DWORD*>(peb + 0x11c);
|
||||
const WORD build = *reinterpret_cast<const WORD*>(peb + 0x120);
|
||||
const UNICODE_STRING service_pack = *reinterpret_cast<const UNICODE_STRING*>(peb + 0x02E8);
|
||||
const u64 compatibility_mode = *reinterpret_cast<const u64*>(peb + 0x02C8); // Two DWORDs, major & minor version
|
||||
|
||||
const bool has_sp = service_pack.Length > 0;
|
||||
std::vector<char> holder(service_pack.Length + 1, '\0');
|
||||
if (has_sp)
|
||||
{
|
||||
WideCharToMultiByte(CP_UTF8, NULL, service_pack.Buffer, service_pack.Length,
|
||||
(LPSTR) holder.data(), static_cast<int>(holder.size()), NULL, NULL);
|
||||
}
|
||||
|
||||
fmt::append(output,
|
||||
"Operating system: Windows, Major: %lu, Minor: %lu, Build: %u, Service Pack: %s, Compatibility mode: %llu",
|
||||
version_major, version_minor, build, has_sp ? holder.data() : "none", compatibility_mode);
|
||||
#else
|
||||
struct utsname details = {};
|
||||
|
||||
if (!uname(&details))
|
||||
{
|
||||
fmt::append(output, "Operating system: POSIX, Name: %s, Release: %s, Version: %s",
|
||||
details.sysname, details.release, details.version);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(output, "Operating system: POSIX, Unknown version! (Error: %d)", errno);
|
||||
}
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
ullong utils::get_tsc_freq()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER freq;
|
||||
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart > 9'999'999)
|
||||
return 0;
|
||||
return freq.QuadPart * 1024;
|
||||
#else
|
||||
// TODO
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
Executable → Regular
+30
-31
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
@@ -8,48 +8,47 @@ namespace utils
|
||||
inline std::array<u32, 4> get_cpuid(u32 func, u32 subfunc)
|
||||
{
|
||||
int regs[4];
|
||||
#ifdef _MSC_VER
|
||||
#ifdef _MSC_VER
|
||||
__cpuidex(regs, func, subfunc);
|
||||
#else
|
||||
#else
|
||||
__asm__ volatile("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) : "a" (func), "c" (subfunc));
|
||||
#endif
|
||||
#endif
|
||||
return {0u+regs[0], 0u+regs[1], 0u+regs[2], 0u+regs[3]};
|
||||
}
|
||||
|
||||
inline u64 get_xgetbv(u32 xcr)
|
||||
inline bool has_ssse3()
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return _xgetbv(xcr);
|
||||
#else
|
||||
u32 eax, edx;
|
||||
__asm__ volatile( "xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
|
||||
return eax | (u64(edx) << 32);
|
||||
#endif
|
||||
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
|
||||
}
|
||||
|
||||
bool has_ssse3();
|
||||
inline bool has_avx()
|
||||
{
|
||||
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
|
||||
}
|
||||
|
||||
bool has_sse41();
|
||||
inline bool has_rtm()
|
||||
{
|
||||
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
|
||||
return get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
|
||||
}
|
||||
|
||||
bool has_avx();
|
||||
inline bool transaction_enter()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const auto status = _xbegin();
|
||||
|
||||
bool has_avx2();
|
||||
if (status == _XBEGIN_STARTED)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_rtm();
|
||||
|
||||
bool has_tsx_force_abort();
|
||||
|
||||
bool has_mpx();
|
||||
|
||||
bool has_512();
|
||||
|
||||
bool has_xop();
|
||||
if (!(status & _XABORT_RETRY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_system_info();
|
||||
|
||||
std::string get_firmware_version();
|
||||
|
||||
std::string get_OS_version();
|
||||
|
||||
ullong get_tsc_freq();
|
||||
}
|
||||
|
||||
-1058
File diff suppressed because it is too large
Load Diff
+290
-154
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
@@ -9,100 +9,58 @@
|
||||
#include <emmintrin.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <array>
|
||||
|
||||
// Assume little-endian
|
||||
#define IS_LE_MACHINE 1
|
||||
#define IS_BE_MACHINE 0
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#define ASSUME(...) __assume(__VA_ARGS__) // MSVC __assume ignores side-effects
|
||||
#define ASSUME(cond) __assume(cond)
|
||||
#define LIKELY
|
||||
#define UNLIKELY
|
||||
#define SAFE_BUFFERS __declspec(safebuffers)
|
||||
#define NEVER_INLINE __declspec(noinline)
|
||||
#define FORCE_INLINE __forceinline
|
||||
#define RESTRICT __restrict
|
||||
|
||||
#else // not _MSC_VER
|
||||
|
||||
#ifdef __clang__
|
||||
#if defined(__has_builtin) && __has_builtin(__builtin_assume)
|
||||
#pragma clang diagnostic ignored "-Wassume" // ignore the clang "side-effects ignored" warning
|
||||
#define ASSUME(...) __builtin_assume(!!(__VA_ARGS__)) // __builtin_assume (supported by modern clang) ignores side-effects
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ASSUME // gcc and old clang
|
||||
#define ASSUME(...) do { if (!(__VA_ARGS__)) __builtin_unreachable(); } while (0) // note: the compiler will generate code to evaluate "cond" if the expression is opaque
|
||||
#endif
|
||||
|
||||
#define LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1)
|
||||
#define UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), 0)
|
||||
#else
|
||||
#define ASSUME(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
|
||||
#define LIKELY(cond) __builtin_expect(!!(cond), 1)
|
||||
#define UNLIKELY(cond) __builtin_expect(!!(cond), 0)
|
||||
#define SAFE_BUFFERS
|
||||
#define NEVER_INLINE __attribute__((noinline))
|
||||
#define FORCE_INLINE __attribute__((always_inline)) inline
|
||||
#define RESTRICT __restrict__
|
||||
|
||||
#endif // _MSC_VER
|
||||
#endif
|
||||
|
||||
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
|
||||
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
|
||||
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
|
||||
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
|
||||
|
||||
// Variant pattern matching helper
|
||||
#define MATCH(arg, ...) constexpr(std::is_same_v<std::decay_t<decltype(arg)>, __VA_ARGS__>)
|
||||
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
|
||||
#define SIZE_32(...) static_cast<u32>(sizeof(__VA_ARGS__))
|
||||
|
||||
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
|
||||
#define ALIGN_32(...) static_cast<u32>(alignof(__VA_ARGS__))
|
||||
|
||||
#define CONCATENATE_DETAIL(x, y) x ## y
|
||||
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
|
||||
|
||||
#define STRINGIZE_DETAIL(x) #x ""
|
||||
#define STRINGIZE_DETAIL(x) #x
|
||||
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
||||
|
||||
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
|
||||
|
||||
// Ensure that the expression evaluates to true. Obsolete.
|
||||
//#define EXPECTS(...) do { if (!(__VA_ARGS__)) fmt::raw_error("Precondition failed: " #__VA_ARGS__ HERE); } while (0)
|
||||
//#define ENSURES(...) do { if (!(__VA_ARGS__)) fmt::raw_error("Postcondition failed: " #__VA_ARGS__ HERE); } while (0)
|
||||
|
||||
#define DECLARE(...) decltype(__VA_ARGS__) __VA_ARGS__
|
||||
|
||||
#define STR_CASE(...) case __VA_ARGS__: return #__VA_ARGS__
|
||||
|
||||
|
||||
#define ASSERT(...) do { if(!(__VA_ARGS__)) fmt::raw_error("Assertion failed: " STRINGIZE(__VA_ARGS__) HERE); } while(0)
|
||||
|
||||
#if defined(_DEBUG) || defined(_AUDIT)
|
||||
#define AUDIT(...) ASSERT(__VA_ARGS__)
|
||||
#else
|
||||
#define AUDIT(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_bit_cast) && (__cpp_lib_bit_cast >= 201806L)
|
||||
#include <bit>
|
||||
#else
|
||||
namespace std
|
||||
{
|
||||
template <class To, class From, typename = std::enable_if_t<sizeof(To) == sizeof(From)>>
|
||||
constexpr To bit_cast(const From& from) noexcept
|
||||
{
|
||||
static_assert(sizeof(To) == sizeof(From), "std::bit_cast<>: incompatible type size");
|
||||
|
||||
To result;
|
||||
std::memcpy(&result, &from, sizeof(From));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
using schar = signed char;
|
||||
using uchar = unsigned char;
|
||||
using ushort = unsigned short;
|
||||
@@ -111,11 +69,7 @@ using ulong = unsigned long;
|
||||
using ullong = unsigned long long;
|
||||
using llong = long long;
|
||||
|
||||
#if __APPLE__
|
||||
using uptr = std::uint64_t;
|
||||
#else
|
||||
using uptr = std::uintptr_t;
|
||||
#endif
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
@@ -131,49 +85,9 @@ using steady_clock = std::conditional<
|
||||
std::chrono::high_resolution_clock::is_steady,
|
||||
std::chrono::high_resolution_clock, std::chrono::steady_clock>::type;
|
||||
|
||||
// Get integral type from type size
|
||||
template <std::size_t N>
|
||||
struct get_int_impl
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u8)>
|
||||
{
|
||||
using utype = u8;
|
||||
using stype = s8;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u16)>
|
||||
{
|
||||
using utype = u16;
|
||||
using stype = s16;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u32)>
|
||||
{
|
||||
using utype = u32;
|
||||
using stype = s32;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u64)>
|
||||
{
|
||||
using utype = u64;
|
||||
using stype = s64;
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
using get_uint_t = typename get_int_impl<N>::utype;
|
||||
|
||||
template <std::size_t N>
|
||||
using get_sint_t = typename get_int_impl<N>::stype;
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
using std::byte;
|
||||
enum class byte : u8;
|
||||
}
|
||||
|
||||
// Formatting helper, type-specific preprocessing for improving safety and functionality
|
||||
@@ -197,9 +111,64 @@ struct se_storage;
|
||||
template <typename T, bool Se = true, std::size_t Align = alignof(T)>
|
||||
class se_t;
|
||||
|
||||
template <typename T, std::size_t Size = sizeof(T)>
|
||||
struct atomic_storage;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_add;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_sub;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_and;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_or;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_xor;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_pre_inc;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_post_inc;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_pre_dec;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_post_dec;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_set;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_reset;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_complement;
|
||||
|
||||
template <typename T>
|
||||
class atomic_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
using std::void_t;
|
||||
#else
|
||||
namespace void_details
|
||||
{
|
||||
template <typename...>
|
||||
struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
using void_t = typename void_details::make_void<T...>::type;
|
||||
#endif
|
||||
|
||||
// Extract T::simple_type if available, remove cv qualifiers
|
||||
template <typename T, typename = void>
|
||||
struct simple_type_helper
|
||||
@@ -208,7 +177,7 @@ struct simple_type_helper
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct simple_type_helper<T, std::void_t<typename T::simple_type>>
|
||||
struct simple_type_helper<T, void_t<typename T::simple_type>>
|
||||
{
|
||||
using type = typename T::simple_type;
|
||||
};
|
||||
@@ -235,6 +204,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Bool wrapper for restricting bool result conversions
|
||||
struct explicit_bool_t
|
||||
{
|
||||
const bool value;
|
||||
|
||||
constexpr explicit_bool_t(bool value)
|
||||
: value(value)
|
||||
{
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
using u128 = __uint128_t;
|
||||
using s128 = __int128_t;
|
||||
@@ -424,9 +409,6 @@ struct alignas(16) s128
|
||||
CHECK_SIZE_ALIGN(u128, 16, 16);
|
||||
CHECK_SIZE_ALIGN(s128, 16, 16);
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
union alignas(2) f16
|
||||
{
|
||||
u16 _u16;
|
||||
@@ -437,20 +419,30 @@ union alignas(2) f16
|
||||
_u16 = raw;
|
||||
}
|
||||
|
||||
explicit operator f32() const
|
||||
explicit operator float() const
|
||||
{
|
||||
// See http://stackoverflow.com/a/26779139
|
||||
// The conversion doesn't handle NaN/Inf
|
||||
u32 raw = ((_u16 & 0x8000) << 16) | // Sign (just moved)
|
||||
(((_u16 & 0x7c00) + 0x1C000) << 13) | // Exponent ( exp - 15 + 127)
|
||||
((_u16 & 0x03FF) << 13); // Mantissa
|
||||
|
||||
return std::bit_cast<f32>(raw);
|
||||
return (float&)raw;
|
||||
}
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(f16, 2, 2);
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
struct ignore
|
||||
{
|
||||
template <typename T>
|
||||
ignore(T)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
||||
constexpr T align(const T& value, ullong align)
|
||||
{
|
||||
@@ -461,11 +453,13 @@ template <typename T, typename T2>
|
||||
inline u32 offset32(T T2::*const mptr)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return std::bit_cast<u32>(mptr);
|
||||
static_assert(sizeof(mptr) == sizeof(u32), "Invalid pointer-to-member size");
|
||||
return reinterpret_cast<const u32&>(mptr);
|
||||
#elif __GNUG__
|
||||
return std::bit_cast<std::size_t>(mptr);
|
||||
static_assert(sizeof(mptr) == sizeof(std::size_t), "Invalid pointer-to-member size");
|
||||
return static_cast<u32>(reinterpret_cast<const std::size_t&>(mptr));
|
||||
#else
|
||||
static_assert(sizeof(mptr) == 0, "Unsupported pointer-to-member size");
|
||||
static_assert(sizeof(mptr) == 0, "Invalid pointer-to-member size");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -477,7 +471,7 @@ struct offset32_array
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return u32{sizeof(std::remove_extent_t<T>)} * static_cast<u32>(arg);
|
||||
return SIZE_32(std::remove_extent_t<T>) * static_cast<u32>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -487,7 +481,7 @@ struct offset32_array<std::array<T, N>>
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return u32{sizeof(T)} * static_cast<u32>(arg);
|
||||
return SIZE_32(T) * static_cast<u32>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -520,6 +514,26 @@ struct offset32_detail<T3 T4::*>
|
||||
}
|
||||
};
|
||||
|
||||
inline u32 cntlz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse(&res, arg) || nonzero ? res ^ 31 : 32;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clzll(arg) - 32 : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cntlz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse64(&res, arg) || nonzero ? res ^ 63 : 64;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper function, used by ""_u16, ""_u32, ""_u64
|
||||
constexpr u8 to_u8(char c)
|
||||
{
|
||||
@@ -691,7 +705,7 @@ struct narrow_impl<From, To, std::enable_if_t<std::is_signed<From>::value && std
|
||||
|
||||
// Simple type enabled (TODO: allow for To as well)
|
||||
template <typename From, typename To>
|
||||
struct narrow_impl<From, To, std::void_t<typename From::simple_type>>
|
||||
struct narrow_impl<From, To, void_t<typename From::simple_type>>
|
||||
: narrow_impl<simple_t<From>, To>
|
||||
{
|
||||
};
|
||||
@@ -723,6 +737,42 @@ constexpr u32 size32(const T (&)[Size], const char* msg = nullptr)
|
||||
return static_cast<u32>(Size);
|
||||
}
|
||||
|
||||
template <typename T1, typename = std::enable_if_t<std::is_integral<T1>::value>>
|
||||
constexpr bool test(const T1& value)
|
||||
{
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename = std::enable_if_t<std::is_integral<T1>::value && std::is_integral<T2>::value>>
|
||||
constexpr bool test(const T1& lhs, const T2& rhs)
|
||||
{
|
||||
return (lhs & rhs) != 0;
|
||||
}
|
||||
|
||||
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
|
||||
inline bool test_and_set(T& lhs, const T2& rhs)
|
||||
{
|
||||
const bool result = (lhs & rhs) != 0;
|
||||
lhs |= rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
|
||||
inline bool test_and_reset(T& lhs, const T2& rhs)
|
||||
{
|
||||
const bool result = (lhs & rhs) != 0;
|
||||
lhs &= ~rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
|
||||
inline bool test_and_complement(T& lhs, const T2& rhs)
|
||||
{
|
||||
const bool result = (lhs & rhs) != 0;
|
||||
lhs ^= rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Simplified hash algorithm for pointers. May be used in std::unordered_(map|set).
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
struct pointer_hash
|
||||
@@ -747,7 +797,7 @@ struct value_hash
|
||||
template <template <typename> class TT, std::size_t S, std::size_t A = S>
|
||||
struct alignas(A) any_pod
|
||||
{
|
||||
alignas(A) std::byte data[S];
|
||||
std::aligned_storage_t<S, A> data;
|
||||
|
||||
any_pod() = default;
|
||||
|
||||
@@ -830,6 +880,35 @@ struct cmd64 : any64
|
||||
|
||||
static_assert(sizeof(cmd64) == 8 && std::is_pod<cmd64>::value, "Incorrect cmd64 type");
|
||||
|
||||
// Allows to define integer convertible to multiple types
|
||||
template <typename T, T Value, typename T1 = void, typename... Ts>
|
||||
struct multicast : multicast<T, Value, Ts...>
|
||||
{
|
||||
constexpr multicast()
|
||||
: multicast<T, Value, Ts...>()
|
||||
{
|
||||
}
|
||||
|
||||
// Implicit conversion to desired type
|
||||
constexpr operator T1() const
|
||||
{
|
||||
return static_cast<T1>(Value);
|
||||
}
|
||||
};
|
||||
|
||||
// Recursion terminator
|
||||
template <typename T, T Value>
|
||||
struct multicast<T, Value, void>
|
||||
{
|
||||
constexpr multicast() = default;
|
||||
|
||||
// Explicit conversion to base type
|
||||
explicit constexpr operator T() const
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
};
|
||||
|
||||
// Error code type (return type), implements error reporting. Could be a template.
|
||||
struct error_code
|
||||
{
|
||||
@@ -854,23 +933,26 @@ struct error_code
|
||||
};
|
||||
|
||||
template<typename ET>
|
||||
struct is_error<ET, std::enable_if_t<sizeof(ET::__not_an_error) != 0>> : std::false_type
|
||||
struct is_error<ET, void_t<decltype(ET::__not_an_error)>> : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
// Common constructor
|
||||
template<typename ET>
|
||||
error_code(const ET& value)
|
||||
// Not an error constructor
|
||||
template<typename ET, typename = decltype(ET::__not_an_error)>
|
||||
error_code(const ET& value, std::nullptr_t = nullptr)
|
||||
: value(static_cast<s32>(value))
|
||||
{
|
||||
if constexpr(is_error<ET>::value)
|
||||
{
|
||||
this->value = error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Error constructor
|
||||
template<typename ET, typename = std::enable_if_t<is_error<ET>::value>>
|
||||
error_code(const ET& value)
|
||||
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), nullptr, 0))
|
||||
{
|
||||
}
|
||||
|
||||
// Error constructor (2 args)
|
||||
template<typename ET, typename T2>
|
||||
template<typename ET, typename T2, typename = std::enable_if_t<is_error<ET>::value>>
|
||||
error_code(const ET& value, const T2& value2)
|
||||
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), fmt::get_type_info<fmt_unveil_t<T2>>(), fmt_unveil<T2>::get(value2)))
|
||||
{
|
||||
@@ -890,39 +972,93 @@ constexpr FORCE_INLINE error_code::not_an_error not_an_error(const T& value)
|
||||
}
|
||||
|
||||
// Synchronization helper (cache-friendly busy waiting)
|
||||
inline void busy_wait(std::size_t cycles = 3000)
|
||||
inline void busy_wait(std::size_t count = 100)
|
||||
{
|
||||
const u64 s = __rdtsc();
|
||||
do _mm_pause(); while (__rdtsc() - s < cycles);
|
||||
while (count--) _mm_pause();
|
||||
}
|
||||
|
||||
// TODO: Remove when moving to c++20
|
||||
template <typename T>
|
||||
inline constexpr uintmax_t floor2(T value)
|
||||
{
|
||||
value >>= 1;
|
||||
// Rotate helpers
|
||||
#if defined(__GNUG__)
|
||||
|
||||
for (uintmax_t i = 0;; i++, value >>= 1)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
u8 result = x;
|
||||
__asm__("rolb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr uintmax_t ceil2(T value)
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
const uintmax_t ispow2 = value & (value - 1); // if power of 2 the result is 0
|
||||
u8 result = x;
|
||||
__asm__("rorb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
value >>= 1;
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
u16 result = x;
|
||||
__asm__("rolw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
for (uintmax_t i = 0;; i++, value >>= 1)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return i + std::min<uintmax_t>(ispow2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
u16 result = x;
|
||||
__asm__("rorw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
u32 result = x;
|
||||
__asm__("roll %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
u32 result = x;
|
||||
__asm__("rorl %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
u64 result = x;
|
||||
__asm__("rolq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
u64 result = x;
|
||||
__asm__("rorq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 umulh64(u64 a, u64 b)
|
||||
{
|
||||
u64 result;
|
||||
__asm__("mulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline s64 mulh64(s64 a, s64 b)
|
||||
{
|
||||
s64 result;
|
||||
__asm__("imulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
inline u8 rol8(u8 x, u8 n) { return _rotl8(x, n); }
|
||||
inline u8 ror8(u8 x, u8 n) { return _rotr8(x, n); }
|
||||
inline u16 rol16(u16 x, u16 n) { return _rotl16(x, (u8)n); }
|
||||
inline u16 ror16(u16 x, u16 n) { return _rotr16(x, (u8)n); }
|
||||
inline u32 rol32(u32 x, u32 n) { return _rotl(x, (int)n); }
|
||||
inline u32 ror32(u32 x, u32 n) { return _rotr(x, (int)n); }
|
||||
inline u64 rol64(u64 x, u64 n) { return _rotl64(x, (int)n); }
|
||||
inline u64 ror64(u64 x, u64 n) { return _rotr64(x, (int)n); }
|
||||
inline u64 umulh64(u64 x, u64 y) { return __umulh(x, y); }
|
||||
inline s64 mulh64(s64 x, s64 y) { return __mulh(x, y); }
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,963 @@
|
||||
#ifndef MAPBOX_UTIL_VARIANT_HPP
|
||||
#define MAPBOX_UTIL_VARIANT_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef> // size_t
|
||||
#include <new> // operator new
|
||||
#include <stdexcept> // runtime_error
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
#include "recursive_wrapper.hpp"
|
||||
|
||||
// clang-format off
|
||||
// [[deprecated]] is only available in C++14, use this for the time being
|
||||
#if __cplusplus <= 201103L
|
||||
# ifdef __GNUC__
|
||||
# define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated))
|
||||
# elif defined(_MSC_VER)
|
||||
# define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated)
|
||||
# else
|
||||
# define MAPBOX_VARIANT_DEPRECATED
|
||||
# endif
|
||||
#else
|
||||
# define MAPBOX_VARIANT_DEPRECATED [[deprecated]]
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx
|
||||
# ifdef NDEBUG
|
||||
# define VARIANT_INLINE __forceinline
|
||||
# else
|
||||
# define VARIANT_INLINE //__declspec(noinline)
|
||||
# endif
|
||||
#else
|
||||
# ifdef NDEBUG
|
||||
# define VARIANT_INLINE //inline __attribute__((always_inline))
|
||||
# else
|
||||
# define VARIANT_INLINE __attribute__((noinline))
|
||||
# endif
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// Exceptions
|
||||
#if defined( __EXCEPTIONS) || defined( _MSC_VER)
|
||||
#define HAS_EXCEPTIONS
|
||||
#endif
|
||||
|
||||
#define VARIANT_MAJOR_VERSION 1
|
||||
#define VARIANT_MINOR_VERSION 1
|
||||
#define VARIANT_PATCH_VERSION 0
|
||||
|
||||
#define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION)
|
||||
|
||||
//namespace mapbox {
|
||||
// namespace util {
|
||||
namespace std {
|
||||
|
||||
// XXX This should derive from std::logic_error instead of std::runtime_error.
|
||||
// See https://github.com/mapbox/variant/issues/48 for details.
|
||||
class bad_variant_access : public std::runtime_error
|
||||
{
|
||||
|
||||
public:
|
||||
explicit bad_variant_access(const std::string& what_arg)
|
||||
: runtime_error(what_arg) {}
|
||||
|
||||
explicit bad_variant_access(const char* what_arg)
|
||||
: runtime_error(what_arg) {}
|
||||
|
||||
}; // class bad_variant_access
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr std::size_t invalid_value = std::size_t(-1);
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct direct_type;
|
||||
|
||||
template <typename T, typename First, typename... Types>
|
||||
struct direct_type<T, First, Types...>
|
||||
{
|
||||
static constexpr std::size_t index = std::is_same<T, First>::value
|
||||
? sizeof...(Types)
|
||||
: direct_type<T, Types...>::index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct direct_type<T>
|
||||
{
|
||||
static constexpr std::size_t index = invalid_value;
|
||||
};
|
||||
|
||||
#if __cpp_lib_logical_traits >= 201510L
|
||||
|
||||
using std::disjunction;
|
||||
|
||||
#else
|
||||
|
||||
template <typename...>
|
||||
struct disjunction : std::false_type {};
|
||||
|
||||
template <typename B1>
|
||||
struct disjunction<B1> : B1 {};
|
||||
|
||||
template <typename B1, typename B2>
|
||||
struct disjunction<B1, B2> : std::conditional<B1::value, B1, B2>::type {};
|
||||
|
||||
template <typename B1, typename... Bs>
|
||||
struct disjunction<B1, Bs...> : std::conditional<B1::value, B1, disjunction<Bs...>>::type {};
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct convertible_type;
|
||||
|
||||
template <typename T, typename First, typename... Types>
|
||||
struct convertible_type<T, First, Types...>
|
||||
{
|
||||
static constexpr std::size_t index = std::is_convertible<T, First>::value
|
||||
? disjunction<std::is_convertible<T, Types>...>::value ? invalid_value : sizeof...(Types)
|
||||
: convertible_type<T, Types...>::index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct convertible_type<T>
|
||||
{
|
||||
static constexpr std::size_t index = invalid_value;
|
||||
};
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct value_traits
|
||||
{
|
||||
using value_type = typename std::remove_const<typename std::remove_reference<T>::type>::type;
|
||||
static constexpr std::size_t direct_index = direct_type<value_type, Types...>::index;
|
||||
static constexpr bool is_direct = direct_index != invalid_value;
|
||||
static constexpr std::size_t index = is_direct ? direct_index : convertible_type<value_type, Types...>::index;
|
||||
static constexpr bool is_valid = index != invalid_value;
|
||||
static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0;
|
||||
using target_type = typename std::tuple_element<tindex, std::tuple<void, Types...>>::type;
|
||||
};
|
||||
|
||||
template <typename T, typename R = void>
|
||||
struct enable_if_type
|
||||
{
|
||||
using type = R;
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename Enable = void>
|
||||
struct result_of_unary_visit
|
||||
{
|
||||
using type = typename std::result_of<F(V&)>::type;
|
||||
};
|
||||
|
||||
template <typename F, typename V>
|
||||
struct result_of_unary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
|
||||
{
|
||||
using type = typename F::result_type;
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename Enable = void>
|
||||
struct result_of_binary_visit
|
||||
{
|
||||
using type = typename std::result_of<F(V&, V&)>::type;
|
||||
};
|
||||
|
||||
template <typename F, typename V>
|
||||
struct result_of_binary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
|
||||
{
|
||||
using type = typename F::result_type;
|
||||
};
|
||||
|
||||
template <std::size_t arg1, std::size_t... others>
|
||||
struct static_max;
|
||||
|
||||
template <std::size_t arg>
|
||||
struct static_max<arg>
|
||||
{
|
||||
static const std::size_t value = arg;
|
||||
};
|
||||
|
||||
template <std::size_t arg1, std::size_t arg2, std::size_t... others>
|
||||
struct static_max<arg1, arg2, others...>
|
||||
{
|
||||
static const std::size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
|
||||
};
|
||||
|
||||
template <typename... Types>
|
||||
struct variant_helper;
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct variant_helper<T, Types...>
|
||||
{
|
||||
VARIANT_INLINE static void destroy(const std::size_t type_index, void* data)
|
||||
{
|
||||
if (type_index == sizeof...(Types))
|
||||
{
|
||||
reinterpret_cast<T*>(data)->~T();
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_helper<Types...>::destroy(type_index, data);
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value)
|
||||
{
|
||||
if (old_type_index == sizeof...(Types))
|
||||
{
|
||||
new (new_value) T(std::move(*reinterpret_cast<T*>(old_value)));
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_helper<Types...>::move(old_type_index, old_value, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value)
|
||||
{
|
||||
if (old_type_index == sizeof...(Types))
|
||||
{
|
||||
new (new_value) T(*reinterpret_cast<const T*>(old_value));
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_helper<Types...>::copy(old_type_index, old_value, new_value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_helper<>
|
||||
{
|
||||
VARIANT_INLINE static void destroy(const std::size_t, void*) {}
|
||||
VARIANT_INLINE static void move(const std::size_t, void*, void*) {}
|
||||
VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct unwrapper
|
||||
{
|
||||
static T const& apply_const(T const& obj) { return obj; }
|
||||
static T& apply(T& obj) { return obj; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct unwrapper<recursive_wrapper<T>>
|
||||
{
|
||||
static auto apply_const(recursive_wrapper<T> const& obj)
|
||||
-> typename recursive_wrapper<T>::type const&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
static auto apply(recursive_wrapper<T>& obj)
|
||||
-> typename recursive_wrapper<T>::type&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct unwrapper<std::reference_wrapper<T>>
|
||||
{
|
||||
static auto apply_const(std::reference_wrapper<T> const& obj)
|
||||
-> typename std::reference_wrapper<T>::type const&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
static auto apply(std::reference_wrapper<T>& obj)
|
||||
-> typename std::reference_wrapper<T>::type&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename... Types>
|
||||
struct dispatcher;
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct dispatcher<F, V, R, T, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v, F&& f)
|
||||
{
|
||||
if (v.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v, F&& f)
|
||||
{
|
||||
if (v.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T>
|
||||
struct dispatcher<F, V, R, T>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct binary_dispatcher_rhs;
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
|
||||
struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
if (rhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
if (rhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1>
|
||||
struct binary_dispatcher_rhs<F, V, R, T0, T1>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct binary_dispatcher_lhs;
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
|
||||
struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
if (lhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
if (lhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1>
|
||||
struct binary_dispatcher_lhs<F, V, R, T0, T1>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename... Types>
|
||||
struct binary_dispatcher;
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct binary_dispatcher<F, V, R, T, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
|
||||
{
|
||||
if (v0.template is<T>())
|
||||
{
|
||||
if (v1.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
else if (v1.template is<T>())
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
|
||||
{
|
||||
if (v0.template is<T>())
|
||||
{
|
||||
if (v1.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
else if (v1.template is<T>())
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T>
|
||||
struct binary_dispatcher<F, V, R, T>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
};
|
||||
|
||||
// comparator functors
|
||||
struct equal_comp
|
||||
{
|
||||
template <typename T>
|
||||
bool operator()(T const& lhs, T const& rhs) const
|
||||
{
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct less_comp
|
||||
{
|
||||
template <typename T>
|
||||
bool operator()(T const& lhs, T const& rhs) const
|
||||
{
|
||||
return lhs < rhs;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Variant, typename Comp>
|
||||
class comparer
|
||||
{
|
||||
public:
|
||||
explicit comparer(Variant const& lhs) noexcept
|
||||
: lhs_(lhs) {}
|
||||
comparer& operator=(comparer const&) = delete;
|
||||
// visitor
|
||||
template <typename T>
|
||||
bool operator()(T const& rhs_content) const
|
||||
{
|
||||
T const& lhs_content = lhs_.template get_unchecked<T>();
|
||||
return Comp()(lhs_content, rhs_content);
|
||||
}
|
||||
|
||||
private:
|
||||
Variant const& lhs_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
struct no_init
|
||||
{
|
||||
};
|
||||
|
||||
template <typename... Types>
|
||||
class variant
|
||||
{
|
||||
static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty");
|
||||
static_assert(!detail::disjunction<std::is_reference<Types>...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?");
|
||||
|
||||
private:
|
||||
static const std::size_t data_size = detail::static_max<sizeof(Types)...>::value;
|
||||
static const std::size_t data_align = detail::static_max<alignof(Types)...>::value;
|
||||
|
||||
using first_type = typename std::tuple_element<0, std::tuple<Types...>>::type;
|
||||
using data_type = typename std::aligned_storage<data_size, data_align>::type;
|
||||
using helper_type = detail::variant_helper<Types...>;
|
||||
|
||||
std::size_t type_index;
|
||||
data_type data;
|
||||
|
||||
public:
|
||||
VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible<first_type>::value)
|
||||
: type_index(sizeof...(Types)-1)
|
||||
{
|
||||
static_assert(std::is_default_constructible<first_type>::value, "First type in variant must be default constructible to allow default construction of variant");
|
||||
new (&data) first_type();
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant(no_init) noexcept
|
||||
: type_index(detail::invalid_value) {}
|
||||
|
||||
// http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
|
||||
template <typename T, typename Traits = detail::value_traits<T, Types...>,
|
||||
typename Enable = typename std::enable_if<Traits::is_valid>::type>
|
||||
VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value)
|
||||
: type_index(Traits::index)
|
||||
{
|
||||
new (&data) typename Traits::target_type(std::forward<T>(val));
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant(variant<Types...> const& old)
|
||||
: type_index(old.type_index)
|
||||
{
|
||||
helper_type::copy(old.type_index, &old.data, &data);
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant(variant<Types...>&& old) noexcept(std::is_nothrow_move_constructible<std::tuple<Types...>>::value)
|
||||
: type_index(old.type_index)
|
||||
{
|
||||
helper_type::move(old.type_index, &old.data, &data);
|
||||
}
|
||||
|
||||
private:
|
||||
VARIANT_INLINE void copy_assign(variant<Types...> const& rhs)
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
type_index = detail::invalid_value;
|
||||
helper_type::copy(rhs.type_index, &rhs.data, &data);
|
||||
type_index = rhs.type_index;
|
||||
}
|
||||
|
||||
VARIANT_INLINE void move_assign(variant<Types...>&& rhs)
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
type_index = detail::invalid_value;
|
||||
helper_type::move(rhs.type_index, &rhs.data, &data);
|
||||
type_index = rhs.type_index;
|
||||
}
|
||||
|
||||
public:
|
||||
VARIANT_INLINE variant<Types...>& operator=(variant<Types...>&& other)
|
||||
{
|
||||
move_assign(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant<Types...>& operator=(variant<Types...> const& other)
|
||||
{
|
||||
copy_assign(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// conversions
|
||||
// move-assign
|
||||
template <typename T>
|
||||
VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
|
||||
{
|
||||
variant<Types...> temp(std::forward<T>(rhs));
|
||||
move_assign(std::move(temp));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// copy-assign
|
||||
template <typename T>
|
||||
VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
|
||||
{
|
||||
variant<Types...> temp(rhs);
|
||||
copy_assign(temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE bool is() const
|
||||
{
|
||||
return type_index == detail::direct_type<T, Types...>::index;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE bool is() const
|
||||
{
|
||||
return type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index;
|
||||
}
|
||||
|
||||
VARIANT_INLINE bool valid() const
|
||||
{
|
||||
return type_index != detail::invalid_value;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
VARIANT_INLINE void set(Args&&... args)
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
type_index = detail::invalid_value;
|
||||
new (&data) T(std::forward<Args>(args)...);
|
||||
type_index = detail::direct_type<T, Types...>::index;
|
||||
}
|
||||
|
||||
// get_unchecked<T>()
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get_unchecked()
|
||||
{
|
||||
return *reinterpret_cast<T*>(&data);
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
// get<T>()
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get()
|
||||
{
|
||||
if (type_index == detail::direct_type<T, Types...>::index)
|
||||
{
|
||||
return *reinterpret_cast<T*>(&data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get_unchecked() const
|
||||
{
|
||||
return *reinterpret_cast<T const*>(&data);
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get() const
|
||||
{
|
||||
if (type_index == detail::direct_type<T, Types...>::index)
|
||||
{
|
||||
return *reinterpret_cast<T const*>(&data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// get_unchecked<T>() - T stored as recursive_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get_unchecked()
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
// get<T>() - T stored as recursive_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get()
|
||||
{
|
||||
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get_unchecked() const
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get() const
|
||||
{
|
||||
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// get_unchecked<T>() - T stored as std::reference_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get_unchecked()
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
// get<T>() - T stored as std::reference_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get()
|
||||
{
|
||||
if (type_index == detail::direct_type<std::reference_wrapper<T>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get_unchecked() const
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get() const
|
||||
{
|
||||
if (type_index == detail::direct_type<std::reference_wrapper<T const>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// This function is deprecated because it returns an internal index field.
|
||||
// Use which() instead.
|
||||
MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE std::size_t get_type_index() const
|
||||
{
|
||||
return type_index;
|
||||
}
|
||||
|
||||
VARIANT_INLINE int which() const noexcept
|
||||
{
|
||||
return static_cast<int>(sizeof...(Types)-type_index - 1);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE static constexpr int which() noexcept
|
||||
{
|
||||
return static_cast<int>(sizeof...(Types)-detail::direct_type<T, Types...>::index - 1);
|
||||
}
|
||||
|
||||
// visitor
|
||||
// unary
|
||||
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static visit(V const& v, F&& f)
|
||||
-> decltype(detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f)))
|
||||
{
|
||||
return detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
|
||||
}
|
||||
// non-const
|
||||
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static visit(V& v, F&& f)
|
||||
-> decltype(detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f)))
|
||||
{
|
||||
return detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// binary
|
||||
// const
|
||||
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F&& f)
|
||||
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
// non-const
|
||||
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F&& f)
|
||||
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
~variant() noexcept // no-throw destructor
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
// equality
|
||||
VARIANT_INLINE bool operator==(variant const& rhs) const
|
||||
{
|
||||
assert(valid() && rhs.valid());
|
||||
if (this->which() != rhs.which())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
detail::comparer<variant, detail::equal_comp> visitor(*this);
|
||||
return visit(rhs, visitor);
|
||||
}
|
||||
|
||||
VARIANT_INLINE bool operator!=(variant const& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
// less than
|
||||
VARIANT_INLINE bool operator<(variant const& rhs) const
|
||||
{
|
||||
assert(valid() && rhs.valid());
|
||||
if (this->which() != rhs.which())
|
||||
{
|
||||
return this->which() < rhs.which();
|
||||
}
|
||||
detail::comparer<variant, detail::less_comp> visitor(*this);
|
||||
return visit(rhs, visitor);
|
||||
}
|
||||
VARIANT_INLINE bool operator>(variant const& rhs) const
|
||||
{
|
||||
return rhs < *this;
|
||||
}
|
||||
VARIANT_INLINE bool operator<=(variant const& rhs) const
|
||||
{
|
||||
return !(*this > rhs);
|
||||
}
|
||||
VARIANT_INLINE bool operator>=(variant const& rhs) const
|
||||
{
|
||||
return !(*this < rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// unary visitor interface
|
||||
// const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V const& v) -> decltype(V::visit(v, std::forward<F>(f)))
|
||||
{
|
||||
return V::visit(v, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// non-const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V& v) -> decltype(V::visit(v, std::forward<F>(f)))
|
||||
{
|
||||
return V::visit(v, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// binary visitor interface
|
||||
// const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return V::binary_visit(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// non-const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V& v0, V& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return V::binary_visit(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// getter interface
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename ResultType, typename T>
|
||||
auto get(T& var)->decltype(var.template get<ResultType>())
|
||||
{
|
||||
return var.template get<ResultType>();
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename ResultType, typename T>
|
||||
ResultType& get_unchecked(T& var)
|
||||
{
|
||||
return var.template get_unchecked<ResultType>();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename ResultType, typename T>
|
||||
auto get(T const& var)->decltype(var.template get<ResultType>())
|
||||
{
|
||||
return var.template get<ResultType>();
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename ResultType, typename T>
|
||||
ResultType const& get_unchecked(T const& var)
|
||||
{
|
||||
return var.template get_unchecked<ResultType>();
|
||||
}
|
||||
}
|
||||
// } // namespace util
|
||||
//} // namespace mapbox
|
||||
|
||||
#endif // MAPBOX_UTIL_VARIANT_HPP
|
||||
Submodule
+1
Submodule Utilities/yaml-cpp added at 017626a6ce
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user