├── .github └── workflows │ └── main.yml ├── .gitignore ├── CMakeLists.txt ├── README.md ├── cmake ├── CheckSTD.cmake ├── FindLIBRT.cmake └── FindNUMA.cmake ├── include ├── CMakeLists.txt ├── shm └── shm_module.hpp.in ├── lib ├── CMakeLists.txt └── shm.cpp ├── scripts └── findnodes.pl ├── shm.pc.in └── testsuite ├── CMakeLists.txt ├── alloc.cpp ├── close.cpp ├── cppstylealloc.cpp ├── cppstyleopen.cpp ├── doublealloc.cpp ├── outofrange.cpp ├── pagemigrate.cpp ├── two_process.cpp ├── wrongkey.cpp └── zerobytes.cpp /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | # events but only for the master branch 5 | on: 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ master ] 10 | 11 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 12 | jobs: 13 | 14 | Linux-POSIX: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: 'Run CMake' 20 | uses: lukka/run-cmake@v2 21 | with: 22 | cmakeListsOrSettingsJson: CMakeListsTxtAdvanced 23 | cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' 24 | cmakeBuildType: Release 25 | buildDirectory: "${{ github.workspace }}/../../_temp/linux" 26 | buildWithCMake: true 27 | cmakeAppendedArgs: -DUSE_POSIX_SHM=1 28 | buildWithCMakeArgs: --config Release 29 | 30 | - name: 'Run CTest' 31 | run: ctest -C Release 32 | working-directory: "${{ github.workspace }}/../../_temp/linux" 33 | 34 | Linux-SysV: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v2 38 | 39 | - name: 'Run CMake' 40 | uses: lukka/run-cmake@v2 41 | with: 42 | cmakeListsOrSettingsJson: CMakeListsTxtAdvanced 43 | cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' 44 | cmakeBuildType: Release 45 | buildDirectory: "${{ github.workspace }}/../../_temp/linux" 46 | buildWithCMake: true 47 | cmakeAppendedArgs: -DUSE_SYSV_SHM=1 48 | buildWithCMakeArgs: --config Release 49 | 50 | - name: 'Run CTest' 51 | run: ctest -C Release 52 | working-directory: "${{ github.workspace }}/../../_temp/linux" 53 | 54 | 55 | MacOS-POSIX: 56 | runs-on: macos-latest 57 | steps: 58 | - uses: actions/checkout@v2 59 | - name: 'Run CMake' 60 | uses: lukka/run-cmake@v2 61 | with: 62 | cmakeListsOrSettingsJson: CMakeListsTxtAdvanced 63 | cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' 64 | cmakeBuildType: Release 65 | buildDirectory: "${{ github.workspace }}/../../_temp/macos" 66 | buildWithCMake: true 67 | cmakeAppendedArgs: -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/../../_temp/macos/install" 68 | buildWithCMakeArgs: --config Release 69 | 70 | - name: 'Run CTest' 71 | run: ctest -C Release 72 | working-directory: "${{ github.workspace }}/../../_temp/macos" 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #from 2 | #https://github.com/github/gitignore/blob/master/Global/Vim.gitignore 3 | #swap 4 | [._]*.s[a-w][a-z] 5 | [._]s[a-w][a-z] 6 | # session 7 | Session.vim 8 | # temporary 9 | .netrwhist 10 | *~ 11 | # auto-generated tag files 12 | tags 13 | ./build 14 | 15 | ## OS X auto-generated files 16 | .DS_Store 17 | ._.DS_Store 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4 FATAL_ERROR) 2 | project(shm) 3 | set( version 1.1 ) 4 | set( CMAKE_INCLUDE_CURRENT_DIR ON ) 5 | ## 6 | # for submodules, this needs to be reset to avoid 7 | # preferencing parent module paths. 8 | ## 9 | set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ) 10 | find_package( NUMA ) 11 | find_package( Threads ) 12 | find_package( LIBRT ) 13 | include( CheckSTD ) 14 | 15 | mark_as_advanced( CPP_EXCEPTIONS ) 16 | if( NOT DEFINED CPP_EXCEPTIONS ) 17 | set( CPP_EXCEPTIONS 1 CACHE BOOL "Use CPP exceptions when errors are encountered when set to 1, otherwise rely on errno" ) 18 | endif() 19 | 20 | 21 | mark_as_advanced( USE_POSIX_SHM ) 22 | ## 23 | # basically if no memory type specified, assume 24 | # POSIX. 25 | ## 26 | if( NOT DEFINED USE_POSIX_SHM AND NOT DEFINED USE_SYSV_SHM ) 27 | set( USE_POSIX_SHM 1 CACHE BOOL "Use POSIX shared memory vs. System V (this is default)" ) 28 | endif() 29 | 30 | mark_as_advanced( USE_SYSV_SHM ) 31 | if( NOT DEFINED USE_SYSV_SHM ) 32 | set( USE_SYSV_SHM 0 CACHE BOOL "Use SYSV shared memory vs. System V (this is NOT default)" ) 33 | endif() 34 | 35 | 36 | 37 | 38 | if( USE_POSIX_SHM ) 39 | message( STATUS "Using POSIX SHM interface" ) 40 | set( USE_SYSV_SHM 0 ) 41 | add_definitions( "-D_USE_POSIX_SHM_=1" ) 42 | elseif( USE_SYSV_SHM ) 43 | message( STATUS "Using SystemV SHM interface" ) 44 | set( USE_POSIX_SHM 0 ) 45 | add_definitions( "-D_USE_SYSTEMV_SHM_=1" ) 46 | endif() 47 | 48 | 49 | add_subdirectory( include ) 50 | 51 | include_directories ( ${PROJECT_BINARY_DIR}/include ) 52 | include_directories ( ${PROJECT_SOURCE_DIR}/include ) 53 | 54 | add_subdirectory( lib ) 55 | 56 | 57 | 58 | if( NOT WIN32 ) 59 | ## 60 | # make and setup pkg-config 61 | ## 62 | mark_as_advanced( PKG_CONFIG_PATHWAY ) 63 | set( PKG_CONFIG_PATH "" CACHE STRING "Set the pkg-config path, othwerwise will figure out" ) 64 | if( NOT PKG_CONFIG_PATH ) 65 | execute_process( COMMAND pkg-config --variable pc_path pkg-config 66 | COMMAND tr ':' '\n' 67 | COMMAND head -n 1 68 | OUTPUT_VARIABLE LOCAL_PKG_CONFIG_PATHWAY ) 69 | string(REGEX REPLACE "\n$" "" LOCAL_PKG_CONFIG_PATHWAY "${LOCAL_PKG_CONFIG_PATHWAY}") 70 | set( PKG_CONFIG_PATH ${LOCAL_PKG_CONFIG_PATHWAY} ) 71 | endif() 72 | 73 | string(REPLACE ":" ";" PREFIX_LIST "${CMAKE_SYSTEM_PREFIX_PATH}") 74 | list(FIND PREFIX_LIST ${CMAKE_INSTALL_PREFIX} _index) 75 | 76 | if(${_index} GREATER 0) 77 | file( MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/pkgconfig ) 78 | set( PKG_CONFIG_PATH ${CMAKE_INSTALL_PREFIX}/pkgconfig ) 79 | message( WARNING "You should set PKG_CONFIG_PATH=${CMAKE_INSTALL_PREFIX}/pkgconfig/:$PKG_CONFIG_PATH when installing to non-standard prefix for pkg-config to work correctly!" ) 80 | else() 81 | message( STATUS "Setting PKG_CONFIG_PATH to: ${PKG_CONFIG_PATH}" ) 82 | endif() 83 | 84 | 85 | 86 | configure_file( "shm.pc.in" "shm.pc" @ONLY ) 87 | install( FILES ${PROJECT_BINARY_DIR}/shm.pc DESTINATION ${PKG_CONFIG_PATH} ) 88 | 89 | ## 90 | # BUILD Tests 91 | ## 92 | mark_as_advanced( BUILD_TESTS ) 93 | set( BUILD_TESTS true CACHE BOOL "Tests build target available if true" ) 94 | if( BUILD_TESTS ) 95 | enable_testing() 96 | add_subdirectory( testsuite ) 97 | endif() 98 | endif() 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Easy SHM library for C++, still a work in progress but it's 3 | a easier way to code up an anonymous SHM mapping, protect 4 | the last page and generate a key to access it....I've used 5 | similar code quite a bit and was tired of re-coding it. If 6 | I find time I'll make it a bit more advanced, like adding 7 | a monitor process to cleanup if for some reason the code 8 | crashes (you know, things happen right?). I've tested the 9 | code on OS X and Linux, but as usual your mileage may vary 10 | greatly. 11 | 12 | # Build Status 13 | ![CI](https://github.com/RaftLib/shm/workflows/CI/badge.svg) 14 | 15 | # Important Notes 16 | So I've checked the functions found in the testsuite. I've 17 | used the code quite a bit in various forms so it should work 18 | rather well...however if you find bugs please submit a pull 19 | request and I'll get it fixed ASAP. 20 | 21 | # Compilation Notes 22 | To build this library on OS X you'll need to run: 23 | ```bash 24 | mkdir build 25 | cd build 26 | cmake ../build -G -DCMAKE_BUILD_TYPE=Release 27 | [make | ninja] 28 | [make | ninja] test 29 | ``` 30 | 31 | ## Build options 32 | * There are the usual options (e.g., Release/Debug), there's also... 33 | * ```-DUSE_SYSV_MEMORY=1``` which will build the library using the SystemV SHM interface 34 | * ```-DUSE_POSIX_SHM=1``` which will build the library using the POSIX SHM interface 35 | * ```-DCPP_EXCEPTIONS=0``` which will remove the use of CPP exceptions, the return 36 | values in this case must be checked (e.g., check for _nullptr_ vs. waiting for the 37 | cpp exception. 38 | 39 | # Install 40 | * Just run ```[make | ninja] install``` 41 | 42 | 43 | # Usage 44 | To use this library, simply: 45 | ```cpp 46 | #include 47 | ``` 48 | 49 | When compiling your code that uses it, link with -lshm. On 50 | Linux you'll have to compile with the -lrt, -lpthread and 51 | -lnuma in order to fully utilize all the features. In 52 | the future I might add a flag to compile without the NUMA 53 | features so that you can use the library without having 54 | to install libnuma. 55 | 56 | ## Two process example 57 | 58 | ```cpp 59 | shm_key_t key; 60 | shm::gen_key( key, 42); 61 | 62 | using type_t = std::uint32_t; 63 | 64 | 65 | type_t *ptr( nullptr ); 66 | 67 | /** 68 | * NOTE: you can compile with or without CPP exceptions. 69 | */ 70 | try 71 | { 72 | ptr = reinterpret_cast< type_t* >( shm::init( key, 0x1000, false, nullptr ) ); 73 | } 74 | catch( bad_shm_alloc ex ) 75 | { 76 | std::cerr << ex.what() << "\n"; 77 | exit( EXIT_FAILURE ); 78 | } 79 | 80 | auto child = fork(); 81 | switch( child ) 82 | { 83 | case( 0 /** child **/ ): 84 | { 85 | try 86 | { 87 | ptr = shm::eopen< type_t >( key ); 88 | } 89 | catch( bad_shm_alloc ex ) 90 | { 91 | std::cerr << ex.what() << "\n"; 92 | exit( EXIT_FAILURE ); 93 | } 94 | *ptr = 0x1137; 95 | shm::close( key, 96 | reinterpret_cast( &ptr), 97 | 0x1000, 98 | false /** don't zero **/, 99 | false /** don't unlink **/ ); 100 | } 101 | break; 102 | case( -1 /** error, back to parent **/ ): 103 | { 104 | exit( EXIT_FAILURE ); 105 | } 106 | break; 107 | default: 108 | { 109 | /** 110 | * spin on value being written from child 111 | * process. 112 | */ 113 | while( *ptr != 0x1137 ); 114 | std::fprintf( stdout, "leet\n" ); 115 | int status = 0; 116 | waitpid( -1, &status, 0 ); 117 | shm::close( key, 118 | reinterpret_cast( &ptr), 119 | 0x1000, 120 | true /** zero **/, 121 | true /** unlink **/ ); 122 | } 123 | } 124 | 125 | ``` 126 | -------------------------------------------------------------------------------- /cmake/CheckSTD.cmake: -------------------------------------------------------------------------------- 1 | if( UNIX ) 2 | ## 3 | # Check for CXX14 or greater 4 | ## 5 | include( CheckCXXCompilerFlag ) 6 | check_cxx_compiler_flag( "-std=c++14" COMPILER_SUPPORTS_CXX14 ) 7 | if( COMPILER_SUPPORTS_CXX14 ) 8 | set( CMAKE_CXX_STANDARD 14 ) 9 | else() 10 | message( FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a newer compiler" ) 11 | endif() 12 | ## 13 | # Check for c99 or greater 14 | ## 15 | include( CheckCCompilerFlag ) 16 | check_c_compiler_flag( "-std=c99" COMPILER_SUPPORTS_C99 ) 17 | if( COMPILER_SUPPORTS_C99 ) 18 | set( CMAKE_C_STANDARD 99 ) 19 | else() 20 | message( FATAL_ERROR "The compiler ${CMAKE_C_COMPILER} has no c99 support. Please use a newer compiler" ) 21 | endif() 22 | set( CXX_STANDARD ON ) 23 | set( C_STANDARD ON ) 24 | endif( UNIX ) 25 | if( MSVC ) 26 | set( CMAKE_CXX_STANDARD 14 ) 27 | set( CMAKE_C_STANDARD 99 ) 28 | set( CXX_STANDARD ON ) 29 | set( C_STANDARD ON ) 30 | endif( MSVC ) 31 | -------------------------------------------------------------------------------- /cmake/FindLIBRT.cmake: -------------------------------------------------------------------------------- 1 | ## 2 | # start check for RT libs 3 | # var CMAKE_RT_LIBS will default to "" on non-unix platforms 4 | ## 5 | if( NOT CMAKE_HOST_UNIX OR WIN32 ) 6 | set( CMAKE_RT_LIBS "" ) 7 | else() 8 | find_library( RT_LIBRARY 9 | NAMES rt 10 | PATHS 11 | ${CMAKE_LIBRARY_PATH} 12 | $ENV{RT_PATH}/lib 13 | /usr/lib 14 | /usr/local/lib 15 | /opt/local/lib ) 16 | if( RT_LIBRARY ) 17 | get_filename_component( RT_LIBRARY ${RT_LIBRARY} DIRECTORY ) 18 | set( CMAKE_RT_LDFLAGS "-L${RT_LIBRARY}" ) 19 | set( CMAKE_RT_LIB "-lrt" ) 20 | else() 21 | set( CMAKE_RT_LIB "" ) 22 | endif() 23 | mark_as_advanced( RT_LIBRARY CMAKE_RT_LIBS ) 24 | endif() 25 | ## end check for RT libs 26 | -------------------------------------------------------------------------------- /cmake/FindNUMA.cmake: -------------------------------------------------------------------------------- 1 | if( CMAKE_HOST_UNIX AND NOT WIN32) 2 | find_library( NUMA_LIBRARY 3 | NAMES numa 4 | PATHS 5 | ${CMAKE_LIBRARY_PATH} 6 | $ENV{NUMA_PATH}/lib 7 | /usr/lib 8 | /usr/local/lib 9 | /opt/local/lib ) 10 | 11 | find_path( NUMA_INCLUDE_DIRS 12 | NAME numaif.h 13 | PATHS 14 | ${CMAKE_INCLUDE_PATH}$ 15 | $ENV{NUMA_PATH}/include 16 | /usr/include 17 | /usr/local/include 18 | /opt/local/include ) 19 | 20 | if( NUMA_LIBRARY AND NUMA_INCLUDE_DIRS ) 21 | set( CMAKE_NUMA_LIBS ${NUMA_LIBRARY} ) 22 | get_filename_component( DIR_NUMA_LIBRARY ${NUMA_LIBRARY} DIRECTORY ) 23 | set( CMAKE_NUMA_LD "-lnuma" ) 24 | set( CMAKE_NUMA_LDFLAGS "-L${DIR_NUMA_LIBRARY}" ) 25 | 26 | set( CMAKE_NUMA_INCLUDES "-I${NUMA_INCLUDE_DIRS}" ) 27 | set( CMAKE_NUMA_DEFINE "1" ) 28 | add_definitions( "-DPLATFORM_HAS_NUMA=1" ) 29 | include_directories( ${NUMA_INCLUDE_DIRS} ) 30 | else( NUMA_LIBRARY AND NUMA_INCLUDE_DIRS ) 31 | ## 32 | # get machine type 33 | ## 34 | execute_process( COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE ) 35 | execute_process( COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/findnodes.pl 36 | COMMAND tr -d '\n' 37 | OUTPUT_VARIABLE HASNUMA ) 38 | if( HASNUMA EQUAL 0 ) 39 | ## no NUMA 40 | message( STATUS "no NUMA needed" ) 41 | add_definitions( "-DPLATFORM_HAS_NUMA=0" ) 42 | set( CMAKE_NUMA_DEFINE "0" ) 43 | set( CMAKE_NUMA_LINK "" ) 44 | else( HASNUMA EQUAL 0 ) 45 | ## needs NUMA but we don't have it 46 | message( FATAL_ERROR "You are compiling on a NUMA system, you must install libnuma" ) 47 | endif( HASNUMA EQUAL 0 ) 48 | endif( NUMA_LIBRARY AND NUMA_INCLUDE_DIRS ) 49 | 50 | endif() 51 | 52 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## 2 | # run header file through pre-processor so we can 3 | # set platform specific definitions before building 4 | # and installing. 5 | ## 6 | configure_file( "shm_module.hpp.in" "shm_module.hpp" @ONLY ) 7 | install( FILES ${PROJECT_SOURCE_DIR}/include/shm 8 | DESTINATION ${CMAKE_INSTALL_PREFIX}/include ) 9 | install( FILES ${PROJECT_BINARY_DIR}/include/shm_module.hpp 10 | DESTINATION ${CMAKE_INSTALL_PREFIX}/include ) 11 | 12 | -------------------------------------------------------------------------------- /include/shm: -------------------------------------------------------------------------------- 1 | // vim: set filetype=cpp: 2 | /** 3 | * shm.hpp - 4 | * Copyright 2021 Jonathan Beard 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at: 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * @author: Jonathan Beard 18 | * @version: Aug 27 2021 19 | */ 20 | #ifndef _SHM_HPP_ 21 | #define _SHM_HPP_ 1 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | //platform specific definitions 30 | #include "shm_module.hpp" 31 | 32 | 33 | 34 | 35 | 36 | #if USE_CPP_EXCEPTIONS==1 37 | class SHMException : public std::exception 38 | { 39 | public: 40 | SHMException( const std::string &message ); 41 | 42 | SHMException( const std::string &&message ); 43 | 44 | virtual const char* what() const noexcept; 45 | private: 46 | const std::string message; 47 | }; 48 | 49 | template < int N > class TemplateSHMException : public SHMException 50 | { 51 | public: 52 | TemplateSHMException( const std::string &message ) : SHMException( message ){}; 53 | TemplateSHMException( const std::string &&message ) : SHMException( 54 | std::forward< decltype( message ) >( message ) ){}; 55 | }; 56 | 57 | using bad_shm_alloc = TemplateSHMException< __COUNTER__ >; 58 | using shm_already_exists = TemplateSHMException< __COUNTER__ >; 59 | using page_alignment_exception = TemplateSHMException< __COUNTER__ >; 60 | using invalid_key_exception = TemplateSHMException< __COUNTER__ >; 61 | #endif 62 | 63 | class shm{ 64 | public: 65 | 66 | 67 | 68 | shm() = delete; 69 | ~shm() = delete; 70 | 71 | 72 | /** 73 | * genkey - This function generates a key to be used 74 | * in the subsequent calls to init/open for an shm 75 | * segment. The function signature looks a bit odd given 76 | * the same function signature generates teh key for both 77 | * SystemV and POSIX shared memory. 78 | * 79 | * @param max_length - maximum length for key 80 | * @param proj_id - random integer, if you have multiple 81 | * shared memory regions, the first 8b of this should ideally 82 | * differ. 83 | * @param key - reference to where you want the key stored. 84 | * @return void 85 | */ 86 | static 87 | void 88 | gen_key( shm_key_t &key, const int proj_id ); 89 | 90 | 91 | 92 | static 93 | bool 94 | key_copy( shm_key_t &dst_key, 95 | const shm_key_t src_key ); 96 | 97 | /** 98 | * init - initialize SHM segment with file descriptor 99 | * key, with the number of items (nitems) and number 100 | * of bytes each for each item (nbytes). Throws exceptions 101 | * upon error which should either be cought or ignored 102 | * depending on how robust the user wants the program 103 | * to be. Function call will exit on failure to open 104 | * "/dev/urandom" and also on failure to read, future 105 | * implementations will have proper exceptions for these. 106 | * @param key - const char * 107 | * @param nbytes - std::size_t 108 | * @param zero - zero before returning memory, default: true 109 | * @return void* - ptr to beginning of memory allocated 110 | * @exception - 111 | */ 112 | static void* init( const shm_key_t &key, 113 | const std::size_t nbytes, 114 | const bool zero = true, 115 | void *ptr = nullptr ); 116 | 117 | /** 118 | * open - opens the shared memory segment with the file 119 | * descriptor stored at key. 120 | * @param key - const std::string&, initialized key 121 | * @return void* - start of allocated memory, or NULL if 122 | * error 123 | */ 124 | static void* open( const shm_key_t &key ); 125 | 126 | /** 127 | * close - returns true if successful, false otherwise. 128 | * multiple exceptions are possible, such as invalid key 129 | * if the key is invalid then it is possible that the 130 | * memory will be unmapped while the file handle is still 131 | * open. A call to this funciton with a valid file and 132 | * nullptr for ptr then the function will skip all the 133 | * unmapping and go for the unlinking. An exception is 134 | * thrown for unlinking errors, in the fiture we'll catch 135 | * all the error codes but at the moment the exception 136 | * simply returns invalid file and a generic message for 137 | * all others. 138 | * @param key - const char* 139 | * @param ptr - ptr to start of mapped region 140 | * @param nbytes - number of bytes for each element in mapped region 141 | * @param zero - zero mapped region before closing, default: false 142 | * @param unlink - call unlink, decrement OS open count for handle 143 | * @return bool - true if successful. 144 | */ 145 | static bool close( const shm_key_t &key, 146 | void **ptr, 147 | const std::size_t nbytes, 148 | const bool zero = false , 149 | const bool unlink = false ); 150 | 151 | 152 | /** 153 | * einit - simple wrapper around shm::init, basically 154 | * just casts your void* for you. 155 | * @param - key, std::string&& with key 156 | * @param - nitems, number of items to init of type T 157 | */ 158 | template < class T > 159 | static T* einit( const shm_key_t &&key, 160 | const std::size_t nitems ) 161 | { 162 | return( reinterpret_cast< T* >( 163 | shm::init( key, 164 | nitems * sizeof( T ), 165 | true, 166 | nullptr ) ) ); 167 | } 168 | 169 | /** 170 | * einit - simple wrapper around shm::init, basically 171 | * just casts your void* for you. 172 | * @param - key, std::string& with key 173 | * @param - nitems, number of items to init of type T 174 | */ 175 | template < class T > 176 | static T* einit( const shm_key_t &key, 177 | const std::size_t nitems ) 178 | { 179 | return( reinterpret_cast< T* >( 180 | shm::init( key, 181 | nitems * sizeof( T ), 182 | true, 183 | nullptr ) ) ); 184 | } 185 | 186 | /** 187 | * eopen - simple wrapper around shm::open, casts the pointer 188 | * for you and returns a type T. 189 | * @param - key, std::string&& 190 | */ 191 | template < class T > 192 | static T* eopen( const shm_key_t &&key ) 193 | { 194 | return( reinterpret_cast< T* >( shm::open( key ) ) ); 195 | } 196 | 197 | /** 198 | * eopen - simple wrapper around shm::open, casts the pointer 199 | * for you and returns a type T. 200 | * @param - key, std::string& 201 | */ 202 | template < class T > 203 | static T* eopen( const shm_key_t &key ) 204 | { 205 | return( reinterpret_cast< T* >( shm::open( key ) ) ); 206 | } 207 | /** 208 | * move_to_tid_numa - checks the pages at 'pages' pointer, 209 | * and makes sure that they are on the NUMA node of the 210 | * calling thread. If the pages are not on the same 211 | * NUMA node as the caller then the appropriate system 212 | * calls are made to migrate the pages and false is 213 | * returned, otherwise they are left alone and true 214 | * is returned. 215 | * @param thread_id - pid_t, id of calling thread 216 | * @param pages - void*, start pointer of pages, must 217 | * be page aligned. 218 | * @param n_bytes - std::size_t, length of the allocation 219 | * in pages, must be a multiple of pages, otherwise an 220 | * exception will be thrown 221 | */ 222 | static bool move_to_tid_numa( const pid_t thread_id, 223 | void *ptr, 224 | const std::size_t n_bytes ); 225 | 226 | private: 227 | static const std::int32_t success = 0; 228 | static const std::int32_t failure = -1; 229 | 230 | }; 231 | 232 | #endif /* END _SHM_HPP_ */ 233 | -------------------------------------------------------------------------------- /include/shm_module.hpp.in: -------------------------------------------------------------------------------- 1 | // vim: set filetype=cpp: 2 | 3 | /** 4 | * shm_module.hpp.in - 5 | * Copyright 2021 Jonathan Beard 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at: 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * @author: Jonathan Beard 19 | * @version: Aug 27 2021 20 | */ 21 | #include 22 | 23 | #ifndef SHM_MODULE_INCLUDE 24 | #define SHM_MODULE_INCLUDE 1 25 | 26 | #ifndef PLATFORM_HAS_NUMA 27 | #define PLATFORM_HAS_NUMA @CMAKE_NUMA_DEFINE@ 28 | #endif 29 | 30 | 31 | #ifndef USE_CPP_EXCEPTIONS 32 | #define USE_CPP_EXCEPTIONS @CPP_EXCEPTIONS@ 33 | #endif 34 | 35 | #if (@USE_SYSV_SHM@ == 1) 36 | 37 | //for key_t type 38 | #include 39 | using shm_key_t = key_t; 40 | static constexpr auto shm_initial_key = std::numeric_limits< shm_key_t >::min(); 41 | 42 | #ifndef _USE_SYSTEMV_SHM_ 43 | #define _USE_SYSTEMV_SHM_ 1 44 | #endif 45 | 46 | #ifndef _USE_POSIX_SHM_ 47 | #define _USE_POSIX_SHM_ 0 48 | #endif 49 | 50 | #elif (@USE_POSIX_SHM@ == 1) 51 | static constexpr auto shm_key_length = 24; 52 | 53 | using shm_key_t = char[ shm_key_length ]; 54 | 55 | static constexpr auto shm_initial_key = '\0'; 56 | #ifndef _USE_SYSTEMV_SHM_ 57 | #define _USE_SYSTEMV_SHM_ 0 58 | #endif 59 | 60 | #ifndef _USE_POSIX_SHM_ 61 | #define _USE_POSIX_SHM_ 1 62 | #endif 63 | 64 | 65 | #endif /** end POSIX vs. SystemV interface selection **/ 66 | 67 | #endif /** END MODULE INCLUDE **/ 68 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set( CMAKE_INCLUDE_CURRENT_DIR ON ) 2 | 3 | 4 | add_library( shm shm.cpp ) 5 | 6 | target_link_libraries( shm ${CMAKE_NUMA_LIBS} ) 7 | 8 | install( TARGETS shm 9 | ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib ) 10 | -------------------------------------------------------------------------------- /lib/shm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * shm.cpp - 3 | * Copyright 2021 Jonathan Beard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at: 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * @author: Jonathan Beard 18 | * @version: September 7 2021 19 | */ 20 | #include 21 | #include 22 | /** 23 | * this is needed for mprotect on both the POSIX 24 | * and SystemV implementations 25 | */ 26 | #include 27 | 28 | #if( _USE_SYSTEMV_SHM_ == 1) 29 | #include 30 | #include 31 | #endif 32 | 33 | #include 34 | #include 35 | #ifndef _GNU_SOURCE 36 | #define _GNU_SOURCE 1 37 | #endif 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #if __APPLE__ 57 | #include 58 | #endif 59 | 60 | #if __linux 61 | /** might need to install numactl-dev **/ 62 | #include 63 | #include 64 | 65 | #if PLATFORM_HAS_NUMA == 1 66 | #include 67 | #include 68 | #endif 69 | 70 | #endif 71 | 72 | 73 | #ifndef UNUSED 74 | #ifdef __clang__ 75 | #define UNUSED( x ) (void)(x) 76 | #else 77 | #define UNUSED( x )[&x]{}() 78 | #endif 79 | //FIXME need to double check to see IF THIS WORKS ON MSVC 80 | #endif 81 | 82 | #if USE_CPP_EXCEPTIONS==1 83 | SHMException::SHMException( const std::string &message ) : std::exception(), 84 | message( message ) 85 | { 86 | } 87 | 88 | SHMException::SHMException( const std::string &&message ) : SHMException( message ) 89 | { 90 | } 91 | 92 | const char* 93 | SHMException::what() const noexcept 94 | { 95 | return( message.c_str() ); 96 | }; 97 | #endif 98 | 99 | 100 | void 101 | shm::gen_key( shm_key_t &key, const int proj_id ) 102 | { 103 | #if _USE_POSIX_SHM_ == 1 104 | //string key 105 | UNUSED( proj_id ); 106 | static std::random_device rd; 107 | static std::mt19937 gen( rd() ); 108 | static std::uniform_int_distribution<> distrib( 0, std::numeric_limits< int >::max() ); 109 | const auto val = distrib( gen ); 110 | shm_key_t val_key; 111 | std::memset( val_key, 112 | '\0', 113 | shm_key_length ); 114 | 115 | std::snprintf( val_key, 116 | shm_key_length, 117 | "%d", 118 | val ); 119 | 120 | shm::key_copy( key, val_key ); 121 | return; 122 | #elif _USE_SYSTEMV_SHM_ == 1 123 | //integer key 124 | char *path = getcwd( nullptr, 0 ); 125 | if( path == nullptr ) 126 | { 127 | std::perror( "failed to get cwd, switching to guns, a.k.a. root dir (/)" ); 128 | key = ftok( "/", proj_id); 129 | } 130 | else 131 | { 132 | key = ftok( path, proj_id); 133 | } 134 | return; 135 | #endif 136 | } 137 | 138 | 139 | bool 140 | shm::key_copy( shm_key_t &dst_key, 141 | const shm_key_t src_key ) 142 | { 143 | #if _USE_POSIX_SHM_ == 1 144 | //string key 145 | std::memset( dst_key, '\0', shm_key_length ); 146 | std::strncpy( (char*) dst_key, 147 | src_key, 148 | shm_key_length ); 149 | return( true ); 150 | #elif _USE_SYSTEMV_SHM_ == 1 151 | dst_key = src_key; 152 | return( true ); 153 | #else 154 | //not implemented 155 | return( false ); 156 | #endif 157 | } 158 | 159 | void* 160 | shm::init( const shm_key_t &key, 161 | const std::size_t nbytes, 162 | const bool zero /* zero mem */, 163 | void *ptr ) 164 | { 165 | auto handle_open_failure = [&]( const shm_key_t &key ) -> void* 166 | { 167 | #if USE_CPP_EXCEPTIONS==1 168 | std::stringstream ss; 169 | #endif 170 | if( errno == EEXIST ) 171 | { 172 | #if USE_CPP_EXCEPTIONS==1 173 | ss << "SHM Handle already exists \"" << key << "\" already exists, please use open\n"; 174 | throw shm_already_exists( ss.str() ); 175 | #else 176 | return( (void*)-1 ); 177 | #endif 178 | } 179 | else 180 | { 181 | #if USE_CPP_EXCEPTIONS==1 182 | ss << "Failed to open shm with file descriptor \"" << 183 | key << "\", error code returned: "; 184 | ss << std::strerror( errno ); 185 | throw bad_shm_alloc( ss.str() ); 186 | #else 187 | return( nullptr ); 188 | #endif 189 | } 190 | }; 191 | 192 | if( nbytes == 0 ) 193 | { 194 | #if USE_CPP_EXCEPTIONS==1 195 | throw bad_shm_alloc( "nbytes cannot be zero when allocating memory!" ); 196 | #else 197 | return( nullptr ); 198 | #endif 199 | } 200 | 201 | /** 202 | * NOTE: 203 | * - actual allocation size should be alloc_bytes, 204 | * user has no idea so we'll re-calc this at the end 205 | * when we unmap the data. 206 | * - This is the same out that is returned at the end 207 | * for ease of keeping track of code between the two 208 | * different versions. 209 | */ 210 | void *out( nullptr ); 211 | 212 | 213 | /** 214 | * NOTE: this is largely for truncation purposes, 215 | * but this is also needed as a sanity check and 216 | * some off the values are needed elsewhere so let's 217 | * do this before we go into POSIX/SystemV specific 218 | * code. 219 | */ 220 | const auto num_phys_pages( sysconf( _SC_PHYS_PAGES ) ); 221 | const auto page_size( sysconf( _SC_PAGE_SIZE ) ); 222 | const auto total_possible_bytes( num_phys_pages * page_size ); 223 | if( nbytes > total_possible_bytes ) 224 | { 225 | 226 | #if USE_CPP_EXCEPTIONS==1 227 | std::stringstream errstr; 228 | errstr << "You've tried to allocate too many bytes (" << nbytes << ")," 229 | << " the total possible is (" << total_possible_bytes << ")\n"; 230 | throw bad_shm_alloc( errstr.str() ); 231 | #else 232 | //errno should be set (hopefully) 233 | return( nullptr ); 234 | #endif 235 | } 236 | /** get allocations size including extra dummy page **/ 237 | const auto alloc_bytes( 238 | static_cast< std::size_t >( 239 | std::ceil( 240 | static_cast< float >( nbytes) / 241 | static_cast< float >( page_size ) ) + 1 /** one extra page for guard **/ ) * page_size 242 | ); 243 | 244 | #if _USE_POSIX_SHM_ == 1 245 | int fd( shm::failure ); 246 | //stupid hack to get around platforms that are 247 | //using LD_PRELOAD, e.g., dynamic binary tools 248 | struct stat st; 249 | std::stringstream path; 250 | path << "/dev/shm/" << key; 251 | if( stat( path.str().c_str(), &st ) == 0 ) 252 | { 253 | #if USE_CPP_EXCEPTIONS==1 254 | std::stringstream ss; 255 | #endif 256 | #if USE_CPP_EXCEPTIONS==1 257 | ss << "SHM Handle already exists \"" << key << "\" already exists, please use open\n"; 258 | throw shm_already_exists( ss.str() ); 259 | #else 260 | return( (void*)-1 ); 261 | #endif 262 | } 263 | 264 | /* set read/write set create if not exists */ 265 | const std::int32_t flags( O_RDWR | O_CREAT | O_EXCL ); 266 | /* set read/write by user */ 267 | const mode_t mode( S_IWUSR | S_IRUSR ); 268 | fd = shm_open( key, 269 | flags, 270 | mode ); 271 | if( fd == failure ) 272 | { 273 | //if using exceptions you won't return 274 | return( handle_open_failure( key ) ); 275 | } 276 | 277 | if( ftruncate( fd, alloc_bytes ) != shm::success ) 278 | { 279 | #if USE_CPP_EXCEPTIONS==1 280 | std::stringstream ss; 281 | ss << "Failed to truncate shm for file descriptor (" << fd << ") "; 282 | ss << "with number of bytes (" << nbytes << "). Error code returned: "; 283 | ss << std::strerror( errno ); 284 | shm_unlink( key ); 285 | throw bad_shm_alloc( ss.str() ); 286 | #else 287 | shm_unlink( key ); 288 | return( nullptr ); 289 | #endif 290 | } 291 | 292 | /** 293 | * NOTE: might be useful to change page size to something larger than default 294 | * for some applications. We'll add that as a future option, however, for now 295 | * I'll leave the note here. 296 | * flags = MAP_HUGETLB | MAP_ANONYMOUS | MAP_HUGE_2MB 297 | * flags = MAP_HUGETLB | MAP_ANONYMOUS | MAP_HUGE_1GB 298 | * we'll need to make sure huge pages are installed/enabled first 299 | * for ubuntu + apt: 300 | * apt-get install hugepages 301 | * you'll need to set it up, some good info if you don't know what you're 302 | * doing is here: https://kerneltalks.com/services/what-is-huge-pages-in-linux/ 303 | * you'll likely need to reboot to clear out any funky kernel states, once you're done, 304 | * write a test program and make sure that you're allocating, the command: 305 | * hugeadm --explain 306 | * should tell you what's set up and in use. 307 | */ 308 | out = mmap( ptr, 309 | alloc_bytes, 310 | ( PROT_READ | PROT_WRITE ), 311 | MAP_SHARED, 312 | fd, 313 | 0 ); 314 | if( out == MAP_FAILED ) 315 | { 316 | #if USE_CPP_EXCEPTIONS==1 317 | std::stringstream ss; 318 | ss << "Failed to mmap shm region with the following error: " << 319 | std::strerror( errno ) << ",\n" << "unlinking."; 320 | shm_unlink( key ); 321 | throw bad_shm_alloc( ss.str() ); 322 | #else 323 | shm_unlink( key ); 324 | return( nullptr ); 325 | #endif 326 | } 327 | /** 328 | * ###### END POSIX SECTION ###### 329 | */ 330 | #elif _USE_SYSTEMV_SHM_ == 1 331 | /** 332 | * ###### START SYSTEM-V SECTION ###### 333 | */ 334 | /** first time you try to open it **/ 335 | const auto shmid = 336 | shmget( key, alloc_bytes, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ); 337 | if( shmid == shm::failure ) 338 | { 339 | //if using exceptions, you won't return from this 340 | return( handle_open_failure( key ) ); 341 | } 342 | 343 | out = shmat( shmid, nullptr, 0 ); 344 | if( out == (void*)-1 ) 345 | { 346 | #if USE_CPP_EXCEPTIONS==1 347 | std::stringstream ss; 348 | ss << "Failed to mmap shm region with the following error: " << 349 | std::strerror( errno ) << ",\n" << "unlinking."; 350 | throw bad_shm_alloc( ss.str() ); 351 | #else 352 | return( nullptr ); 353 | #endif 354 | } 355 | 356 | 357 | #endif /** end switch between posix/sysV **/ 358 | if( zero ) 359 | { 360 | /* everything theoretically went well, lets initialize to zero */ 361 | std::memset( out, 0x0, nbytes ); 362 | } 363 | char *temp( reinterpret_cast< char* >( out ) ); 364 | /** we allocate one extra page **/ 365 | if( mprotect( (void*) &temp[ alloc_bytes - page_size ], 366 | page_size, 367 | PROT_NONE ) != 0 ) 368 | { 369 | #if DEBUG 370 | perror( "Error, failed to set page protection, not fatal just dangerous." ); 371 | #endif 372 | } 373 | return( out ); 374 | } 375 | 376 | 377 | 378 | void* 379 | shm::open( const shm_key_t &key ) 380 | { 381 | /** 382 | * just like with init, use same output 383 | * pointer stack location for both. 384 | */ 385 | void *out( nullptr ); 386 | 387 | auto handle_open_failure = [&]( const shm_key_t &key ) -> void* 388 | { 389 | #if USE_CPP_EXCEPTIONS==1 390 | std::stringstream ss; 391 | ss << 392 | "Failed to open shm with key \"" << key << "\", with the following error code ("; 393 | ss << std::strerror( errno ) << ")"; 394 | throw bad_shm_alloc( ss.str() ); 395 | #else 396 | return( nullptr ); 397 | #endif 398 | 399 | }; 400 | #if _USE_POSIX_SHM_ == 1 401 | int fd( shm::failure ); 402 | const int flags( O_RDWR | O_CREAT ); 403 | mode_t mode( 0 ); 404 | fd = shm_open( key, 405 | flags, 406 | mode ); 407 | if( fd == failure ) 408 | { 409 | return( handle_open_failure( key ) ); 410 | } 411 | struct stat st; 412 | std::memset( &st, 413 | 0x0, 414 | sizeof( struct stat ) ); 415 | /* stat the file to get the size */ 416 | if( fstat( fd, &st ) != shm::success ) 417 | { 418 | #if USE_CPP_EXCEPTIONS==1 419 | std::stringstream ss; 420 | ss << "Failed to stat shm region with the following error: " << std::strerror( errno ) << ",\n"; 421 | ss << "unlinking."; 422 | shm_unlink( key ); 423 | throw bad_shm_alloc( ss.str() ); 424 | #else 425 | shm_unlink( key ); 426 | return( nullptr ); 427 | #endif 428 | } 429 | out = mmap( nullptr, 430 | st.st_size, 431 | (PROT_READ | PROT_WRITE), 432 | MAP_SHARED, 433 | fd, 434 | 0 ); 435 | if( out == MAP_FAILED ) 436 | { 437 | #if USE_CPP_EXCEPTIONS==1 438 | std::stringstream ss; 439 | ss << "Failed to mmap shm region with the following error: " << std::strerror( errno ) << ",\n"; 440 | ss << "unlinking."; 441 | shm_unlink( key ); 442 | throw bad_shm_alloc( ss.str() ); 443 | #else 444 | shm_unlink( key ); 445 | return( nullptr ); 446 | #endif 447 | } 448 | /* close fd */ 449 | ::close( fd ); 450 | /* done, return mem */ 451 | return( out ); 452 | 453 | /** END POSIX MEMORY **/ 454 | #elif _USE_SYSTEMV_SHM_ == 1 455 | /** START SYSTEMV MEMORY **/ 456 | 457 | //STEP1 shmget 458 | const auto shmid = 459 | shmget( key, sizeof(int), S_IRUSR | S_IWUSR ); 460 | if( shmid == shm::failure ) 461 | { 462 | //if using exceptions, you won't return from this 463 | return( handle_open_failure( key ) ); 464 | } 465 | //STEP2 shmat 466 | out = shmat(shmid, nullptr, 0); 467 | if( out == (void*)-1 ) 468 | { 469 | #if USE_CPP_EXCEPTIONS==1 470 | std::stringstream ss; 471 | ss << "Failed to SHM attach the shm region with the following error (" 472 | << std::strerror( errno ) << ")."; 473 | throw bad_shm_alloc( ss.str() ); 474 | #else 475 | return( nullptr ); 476 | #endif 477 | } 478 | /** END SYSTEMV MEMORY **/ 479 | #endif 480 | //if we're here, everything theoretically worked 481 | return( out ); 482 | } 483 | 484 | bool 485 | shm::close( const shm_key_t &key, 486 | void **ptr, 487 | const std::size_t nbytes, 488 | const bool zero, 489 | const bool unlink ) 490 | { 491 | if( zero && (ptr != nullptr) && ( *ptr != nullptr ) ) 492 | { 493 | std::memset( *ptr, 0x0, nbytes ); 494 | } 495 | #if _USE_POSIX_SHM_ == 1 496 | if( ptr != nullptr ) 497 | { 498 | /** get allocations size including extra dummy page **/ 499 | const auto page_size( sysconf( _SC_PAGESIZE ) ); 500 | const auto alloc_bytes( 501 | static_cast< std::size_t >( 502 | std::ceil( 503 | static_cast< float >( nbytes ) / 504 | static_cast< float >( page_size ) ) + 1 ) * page_size 505 | ); 506 | if( ( *ptr != nullptr ) && ( munmap( *ptr, alloc_bytes ) != shm::success ) ) 507 | { 508 | #if DEBUG 509 | perror( "Failed to unmap shared memory, attempting to close!!" ); 510 | #endif 511 | } 512 | *ptr = nullptr; 513 | } 514 | if( unlink ) 515 | { 516 | if( shm_unlink( key ) != 0 ) 517 | { 518 | #if USE_CPP_EXCEPTIONS==1 519 | switch( errno ) 520 | { 521 | case( ENOENT ): 522 | { 523 | throw invalid_key_exception( "Invalid file descriptor specified" ); 524 | } 525 | break; 526 | default: 527 | { 528 | throw invalid_key_exception( "Undefined error, check error codes" ); 529 | } 530 | } 531 | #endif 532 | } 533 | } 534 | //jump to return true if here. 535 | /** END POSIX MEMORY IMPL **/ 536 | #elif _USE_SYSTEMV_SHM_ == 1 537 | /** START SYSTEMV IMPL **/ 538 | /** 539 | * we could have gotten here b/c something failed and the 540 | * user code is now calling close on an invalid shm seg. 541 | * so let's stat first to be sure. 542 | */ 543 | const auto shmid = 544 | shmget( key, sizeof(int), S_IRUSR | S_IWUSR ); 545 | if( shmid == shm::failure ) 546 | { 547 | #if USE_CPP_EXCEPTIONS==1 548 | if( errno == ENOENT ) 549 | { 550 | throw invalid_key_exception( "SHM key doesn't exist" ); 551 | } 552 | #else 553 | return( true ); 554 | #endif 555 | } 556 | /** 557 | * NOTE: This may not work quite perfectly b/c 558 | * if there are M communicating pairs and you 559 | * call this and only one happens to be detached, 560 | * then this may cause the segment to be deleted. 561 | */ 562 | if(unlink /** only do this once **/ && shmctl(shmid, IPC_RMID, nullptr) == -1) 563 | { 564 | #if USE_CPP_EXCEPTIONS==1 565 | if( errno == EINVAL || errno == EIDRM ) 566 | { 567 | throw invalid_key_exception( "Invalid SHM key" ); 568 | } 569 | std::stringstream ss; 570 | ss << "Failed to set the SystemV memory region to exit on detach, non-fatal error (" 571 | << std::strerror( errno ) << ")\n"; 572 | throw bad_shm_alloc( ss.str() ); 573 | #else 574 | return( false ); 575 | #endif 576 | } 577 | //else we're here, and it exists 578 | if( shmdt( *ptr ) == shm::failure ) 579 | { 580 | #if USE_CPP_EXCEPTIONS==1 581 | std::stringstream ss; 582 | ss << "Failed to detach SHM with error code (" 583 | << std::strerror( errno ) << ")."; 584 | throw invalid_key_exception( ss.str() ); 585 | #else 586 | return( false ); 587 | #endif 588 | } 589 | 590 | /** END SYSTEMV IMPL **/ 591 | #endif 592 | return( true ); 593 | } 594 | 595 | bool 596 | shm::move_to_tid_numa( const pid_t thread_id, 597 | void *ptr, 598 | const std::size_t nbytes ) 599 | { 600 | #if __linux && ( PLATFORM_HAS_NUMA == 1 ) 601 | /** check alignment of pages first **/ 602 | const auto page_size( sysconf( _SC_PAGESIZE ) ); 603 | 604 | const auto ptr_addr( 605 | reinterpret_cast< std::uintptr_t >( ptr ) ); 606 | if( (ptr_addr % page_size) != 0 ) 607 | { 608 | #if USE_CPP_EXCEPTIONS==1 609 | std::stringstream ss; 610 | ss << "Variable 'ptr' must be page aligned, currently it is(" << 611 | ptr_addr % page_size << "), off please fix...exiting!!\n"; 612 | throw page_alignment_exception( ss.str() ); 613 | #else 614 | return( false ); 615 | #endif 616 | } 617 | 618 | /** check to see if NUMA avail **/ 619 | if( numa_available() == -1 ) 620 | { 621 | return( true ); 622 | } 623 | /** first check to see if there is more than one numa node **/ 624 | const auto num_numa( numa_num_configured_nodes() ); 625 | if( num_numa == 1 ) 626 | { 627 | /** no point in continuing **/ 628 | return( true ); 629 | } 630 | 631 | /** RETURN VAL HERE */ 632 | bool moved( false ); 633 | /** get the numa node of the calling thread **/ 634 | const auto local_node( numa_node_of_cpu( sched_getcpu() ) ); 635 | /** get numa node that 'pages' is on **/ 636 | /** 637 | * Note: only way I could figure out how to do this was get a cpu 638 | * set, we'll check to see how many CPU's are in the CPU set of 639 | * the thread, then we'll see if all those CPU's are on the 640 | * same NUMA node, if they are then we'll check to see if the 641 | * alloc is on the same node, otherwise we'll move the pages to that 642 | * node. The interesting case comes when there are multiple 643 | * CPUs per set and multiple possible NUMA nodes to choose from. The simple 644 | * answer, the one I'm taking a the moment is that we can't 645 | * really decide in this function. I think in the future it'd 646 | * be good to have a profiler decide the shortest latency and 647 | * highest bandwidth for long running programs at startup and 648 | * do some sort of graph matching. 649 | */ 650 | cpu_set_t *cpuset( nullptr ); 651 | /** we want set to include all cpus **/ 652 | const auto num_cpus_alloc( get_nprocs_conf() ); 653 | std::size_t cpu_allocate_size( -1 ); 654 | #if (__GLIBC_MINOR__ > 9 ) && (__GLIBC__ == 2) 655 | cpuset = CPU_ALLOC( num_cpus_alloc ); 656 | assert( cpuset != nullptr ); 657 | cpu_allocate_size = CPU_ALLOC_SIZE( num_cpus_alloc ); 658 | CPU_ZERO_S( cpu_allocate_size, cpuset ); 659 | #else 660 | cpu_allocate_size = sizeof( cpu_set_t ); 661 | cpuset = reinterpret_cast< cpu_set_t* >( malloc( cpu_allocate_size ) ); 662 | assert( cpuset != nullptr ); 663 | CPU_ZERO( cpuset ); 664 | #endif 665 | /** now get affinity **/ 666 | if( sched_getaffinity( thread_id, cpu_allocate_size, cpuset ) != 0 ) 667 | { 668 | perror( "Failed to get affinity for calling thread!" ); 669 | return( false ); 670 | } 671 | /** 672 | * NOTE: for the moment we're using an extremely simplistic 673 | * distance metric, we should probably be using the numa_distance 674 | * combined with the actual latancy/bandwidth between the 675 | * processor cores. 676 | */ 677 | auto numa_node_count( 0 ); 678 | auto denom( 0 ); 679 | for( auto p_index( 0 ); p_index < num_cpus_alloc; p_index++ ) 680 | { 681 | if( CPU_ISSET( p_index /* cpu */, cpuset ) ) 682 | { 683 | const auto ret_val( numa_node_of_cpu( p_index ) ); 684 | if( ret_val != -1 ) 685 | { 686 | numa_node_count += ret_val; 687 | denom++; 688 | } 689 | } 690 | } 691 | const auto target_node( numa_node_count / denom ); 692 | 693 | /** we need to know how many pages we have **/ 694 | const auto num_pages( 695 | static_cast< std::size_t >( 696 | std::ceil( 697 | static_cast< float >( nbytes ) / static_cast< float >( page_size ) ) ) ); 698 | using int_t = std::int32_t; 699 | int_t *mem_node ( new int_t[ num_pages ] ); 700 | int_t *mem_status( new int_t[ num_pages ] ); 701 | void **page_ptr = (void**)malloc( sizeof( void* ) * num_pages ); 702 | char *temp_ptr( reinterpret_cast< char* >( ptr ) ); 703 | for( auto node_index( 0 ), 704 | page_index( 0 ) /** init **/; 705 | node_index < num_pages /** cond **/; 706 | node_index++, page_index += page_size /** incr **/) 707 | { 708 | mem_node [ node_index ] = target_node; 709 | mem_status[ node_index ] = -1; 710 | page_ptr[ node_index ] = (void*)&temp_ptr[ page_index ]; 711 | } 712 | if( move_pages( thread_id /** thread we want to move for **/, 713 | num_pages /** total number of pages **/, 714 | page_ptr /** pointers to the start of each page **/, 715 | mem_node /** node we want to move to **/, 716 | mem_status/** status flags, check for debug **/, 717 | MPOL_MF_MOVE ) != 0 ) 718 | { 719 | #if DEBUG 720 | perror( "failed to move pages, non-fatal error but results may vary."); 721 | /** TODO, re-code all status flag checks..accidentally over wrote **/ 722 | #endif 723 | moved = false; 724 | } 725 | delete[]( mem_node ); 726 | delete[]( mem_status ); 727 | free( page_ptr ); 728 | return( moved ); 729 | #else /** no NUMA avail **/ 730 | return( false ); 731 | #endif 732 | } 733 | -------------------------------------------------------------------------------- /scripts/findnodes.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | my $os = `uname`; 6 | chomp( $os ); 7 | $os = lc( $os ); 8 | if( $os eq "linux" ) 9 | { 10 | my $cmdstr = "grep -oP \"(?<=Node\\s)\\d\" /proc/zoneinfo"; 11 | my $nodelist = `$cmdstr`; 12 | chomp( $nodelist ); 13 | my @arr = split/\n/,$nodelist; 14 | my $first = shift( @arr ); 15 | foreach my $val ( @arr ) 16 | { 17 | if( $val != $first ) 18 | { 19 | print( "1" ); 20 | exit( 0 ); 21 | } 22 | } 23 | } 24 | ## 25 | # else 26 | ## 27 | print( "0" ); 28 | exit( 0 ); 29 | -------------------------------------------------------------------------------- /shm.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | libdir=@CMAKE_INSTALL_PREFIX@/lib 3 | includedir=@CMAKE_INSTALL_PREFIX@/include 4 | 5 | Name: shm 6 | Description: C++ wrappers for POSIX shared memory 7 | Version: 1.0 8 | Requires: 9 | Conflicts: 10 | Libs: -L${libdir} -lshm @CMAKE_RT_LDFLAGS@ @CMAKE_RT_LIB@ @CMAKE_NUMA_LDFLAGS@ @CMAKE_NUMA_LD@ 11 | Libs.private: 12 | Cflags: -I${includedir} @CMAKE_NUMA_INCLUDES@ 13 | -------------------------------------------------------------------------------- /testsuite/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set( CMAKE_INCLUDE_CURRENT_DIR ON ) 2 | 3 | if( CPP_EXCEPTIONS ) 4 | set( TESTAPPS alloc 5 | doublealloc 6 | close 7 | cppstylealloc 8 | cppstyleopen 9 | outofrange 10 | wrongkey 11 | zerobytes 12 | two_process 13 | ${NUMA_TESTS} 14 | ) 15 | else() 16 | ## 17 | # need to add idefs to all shm test cases 18 | ## 19 | set( TESTAPPS 20 | alloc 21 | two_process 22 | ${NUMA_TESTS} 23 | ) 24 | endif() 25 | include_directories( ${PROJECT_SOURCE_DIR}/include ) 26 | 27 | foreach( APP ${TESTAPPS} ) 28 | add_executable( ${APP} "${APP}.cpp" ) 29 | target_link_libraries( ${APP} shm ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_RT_LIB} ${CMAKE_NUMA_LIB} ) 30 | add_test( NAME "${APP}_test" COMMAND ${APP} ) 31 | endforeach( APP ${TESTAPPS} ) 32 | -------------------------------------------------------------------------------- /testsuite/alloc.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * alloc.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:03 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int 28 | main( int argc, char **argv ) 29 | { 30 | shm_key_t key; 31 | shm::gen_key( key, 42); 32 | std::int32_t *ptr( nullptr ); 33 | #if (USE_CPP_EXCEPTIONS==1) 34 | try 35 | { 36 | ptr = reinterpret_cast< std::int32_t* >( shm::init( key, 0x1000 ) ); 37 | } 38 | catch( bad_shm_alloc ex ) 39 | { 40 | std::cerr << ex.what() << "\n"; 41 | exit( EXIT_FAILURE ); 42 | } 43 | #else 44 | ptr = reinterpret_cast< std::int32_t* >( shm::init( key, 0x1000 ) ); 45 | if( ptr == (void*)-1 && ptr != 0 ) 46 | { 47 | std::fprintf( stderr, "Failed to initialize SHM ptr, exiting!" ); 48 | exit( EXIT_FAILURE ); 49 | } 50 | 51 | #endif 52 | for( int i( 0 ); i < 100; i++ ) 53 | { 54 | ptr[ i ] = i; 55 | } 56 | /** if we get to this point then we assume that the mem is writable **/ 57 | shm::close( key, 58 | reinterpret_cast( &ptr), 59 | 0x1000, 60 | true, 61 | true ); 62 | /** should get here and be done **/ 63 | return( EXIT_SUCCESS ); 64 | } 65 | -------------------------------------------------------------------------------- /testsuite/close.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * close.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:05 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | int 30 | main( int argc, char **argv ) 31 | { 32 | shm_key_t key; 33 | shm::gen_key( key, 42 ); 34 | std::int32_t *ptr( nullptr ); 35 | try 36 | { 37 | ptr = reinterpret_cast< std::int32_t* >( shm::init( key, 0x1000 ) ); 38 | } 39 | catch( bad_shm_alloc ex ) 40 | { 41 | std::cerr << ex.what() << "\n"; 42 | exit( EXIT_FAILURE ); 43 | } 44 | for( int i( 0 ); i < 100; i++ ) 45 | { 46 | ptr[ i ] = i; 47 | } 48 | /** if we get to this point then we assume that the mem is writable **/ 49 | shm::close( key, 50 | reinterpret_cast(&ptr), 51 | 0x1000, 52 | true, 53 | true 54 | ); 55 | return( EXIT_SUCCESS ); 56 | } 57 | -------------------------------------------------------------------------------- /testsuite/cppstylealloc.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * alloc.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:03 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int 28 | main( int argc, char **argv ) 29 | { 30 | shm_key_t key; 31 | shm::gen_key( key, 42 ); 32 | 33 | std::int32_t *ptr( nullptr ); 34 | try 35 | { 36 | ptr = shm::einit< std::int32_t >( key, 0x1000 ); 37 | } 38 | catch( bad_shm_alloc ex ) 39 | { 40 | std::cerr << ex.what() << "\n"; 41 | exit( EXIT_FAILURE ); 42 | } 43 | for( int i( 0 ); i < 100; i++ ) 44 | { 45 | ptr[ i ] = i; 46 | } 47 | /** if we get to this point then we assume that the mem is writable **/ 48 | shm::close( key, 49 | reinterpret_cast( &ptr), 50 | 0x1000, 51 | true, 52 | true ); 53 | /** should get here and be done **/ 54 | return( EXIT_SUCCESS ); 55 | } 56 | -------------------------------------------------------------------------------- /testsuite/cppstyleopen.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * alloc.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:03 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int 28 | main( int argc, char **argv ) 29 | { 30 | shm_key_t key; 31 | shm::gen_key( key, 42 ); 32 | std::cerr << key << "\n"; 33 | std::int32_t *ptr( nullptr ); 34 | try 35 | { 36 | ptr = shm::einit< std::int32_t >( key, 100 ); 37 | } 38 | catch( bad_shm_alloc ex ) 39 | { 40 | std::cerr << ex.what() << "\n"; 41 | exit( EXIT_FAILURE ); 42 | } 43 | for( int i( 0 ); i < 100; i++ ) 44 | { 45 | ptr[ i ] = i; 46 | std::cout << ptr[ i ] << "\n"; 47 | } 48 | std::int32_t *ptr2( nullptr ); 49 | try 50 | { 51 | ptr2 = shm::eopen< std::int32_t >( key ); 52 | } 53 | catch( bad_shm_alloc ex ) 54 | { 55 | std::cerr << ex.what() << "\n"; 56 | exit( EXIT_FAILURE ); 57 | } 58 | 59 | if( std::memcmp( ptr,ptr2, 100 * sizeof( std::int32_t ) ) != 0 ) 60 | { 61 | shm::close( key, 62 | reinterpret_cast( &ptr), 63 | 0x1000, 64 | true, 65 | true ); 66 | /** memory regions aren't equal, fail **/ 67 | assert( false ); 68 | } 69 | 70 | 71 | 72 | shm::close( key, 73 | reinterpret_cast( &ptr), 74 | 0x1000, 75 | true, 76 | true ); 77 | /** should get here and be done **/ 78 | return( EXIT_SUCCESS ); 79 | } 80 | -------------------------------------------------------------------------------- /testsuite/doublealloc.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * alloc.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:03 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int 28 | main( int argc, char **argv ) 29 | { 30 | shm_key_t key; 31 | shm::gen_key( key, 31 ); 32 | 33 | std::int32_t *ptr( nullptr ), *ptr_2( nullptr ); 34 | ptr = reinterpret_cast< std::int32_t* >( shm::init( key, 0x1000 ) ); 35 | try 36 | { 37 | ptr_2 = reinterpret_cast< std::int32_t* >( shm::init( key, 0x1000 ) ); 38 | } 39 | catch( shm_already_exists &ex ) 40 | { 41 | shm::close( key, 42 | reinterpret_cast( &ptr), 43 | 0x1000, 44 | true, 45 | true ); 46 | std::cout << ex.what() << "\n"; 47 | return( EXIT_SUCCESS ); 48 | } 49 | shm::close( key, 50 | reinterpret_cast( &ptr), 51 | 0x1000, 52 | true, 53 | true ); 54 | /** shouldn't be here **/ 55 | return( EXIT_FAILURE ); 56 | } 57 | -------------------------------------------------------------------------------- /testsuite/outofrange.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * nullalloc.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:08 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int 28 | main( int argc, char **argv ) 29 | { 30 | //os x has a limited range on file names, 32 char including null 31 | shm_key_t key; 32 | shm::gen_key( key, 42 ); 33 | std::int32_t *ptr( nullptr ); 34 | try 35 | { 36 | //try to allocate too much memory 37 | ptr = reinterpret_cast< std::int32_t* >( 38 | shm::init( key, std::numeric_limits::max() ) 39 | ); 40 | } 41 | 42 | catch( bad_shm_alloc ex ) 43 | { 44 | std::fprintf( stderr, "we're in the right area, failure\n" ); 45 | ptr = nullptr; 46 | /** this is where we wanted to end up **/ 47 | try 48 | { 49 | shm::close( key, 50 | reinterpret_cast( &ptr), 51 | 0x1000, 52 | false, 53 | true ); 54 | } 55 | catch( invalid_key_exception inv_ex ) 56 | { 57 | std::cerr << "trying to unlink is okay...some systems need this\n"; 58 | std::cerr << inv_ex.what() << "\n"; 59 | } 60 | std::cerr << ex.what() << "\n"; 61 | exit( EXIT_SUCCESS ); 62 | } 63 | for( int i( 0 ); i < 100; i++ ) 64 | { 65 | ptr[ i ] = i; 66 | } 67 | /** if we get to this point then we assume that the mem is writable **/ 68 | shm::close( key, 69 | reinterpret_cast(&ptr), 70 | 0x1000, 71 | true, 72 | true ); 73 | /** should get here and be done **/ 74 | return( EXIT_SUCCESS ); 75 | } 76 | -------------------------------------------------------------------------------- /testsuite/pagemigrate.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * alloc.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:03 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef __linux 27 | /** for get cpu **/ 28 | #if (__GLIBC_MINOR__ < 14) && (__GLIBC__ <= 2) 29 | 30 | #ifndef _GNU_SOURCE 31 | #define _GNU_SOURCE 1 32 | #endif 33 | 34 | #else 35 | 36 | #ifndef _BSD_SOURCE 37 | #define _BSD_SOURCE 1 38 | #endif 39 | 40 | #ifndef _SVID_SOURCE 41 | #define _SVID_SOURCE 1 42 | #endif 43 | 44 | #endif /** end glibc check **/ 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | void 51 | set_affinity( const std::size_t desired_core ) 52 | { 53 | /** 54 | * pin the current thread 55 | */ 56 | cpu_set_t *cpuset( nullptr ); 57 | size_t cpu_allocate_size( -1 ); 58 | #if (__GLIBC_MINOR__ > 9 ) && (__GLIBC__ == 2 ) 59 | const int8_t processors_to_allocate( 1 ); 60 | cpuset = CPU_ALLOC( processors_to_allocate ); 61 | assert( cpuset != nullptr ); 62 | cpu_allocate_size = CPU_ALLOC_SIZE( processors_to_allocate ); 63 | CPU_ZERO_S( cpu_allocate_size, cpuset ); 64 | #else 65 | cpu_allocate_size = sizeof( cpu_set_t ); 66 | cpuset = (cpu_set_t*) malloc( cpu_allocate_size ); 67 | assert( cpuset != nullptr ); 68 | CPU_ZERO( cpuset ); 69 | #endif 70 | CPU_SET( desired_core, 71 | cpuset ); 72 | errno = 0; 73 | if( sched_setaffinity( 0 /* calling thread */, 74 | cpu_allocate_size, 75 | cpuset ) != 0 ) 76 | { 77 | perror( "Failed to set affinity for cycle counter!!" ); 78 | exit( EXIT_FAILURE ); 79 | } 80 | /** wait till we know we're on the right processor **/ 81 | if( sched_yield() != 0 ) 82 | { 83 | perror( "Failed to yield to wait for core change!\n" ); 84 | } 85 | return; 86 | } 87 | 88 | struct Data 89 | { 90 | Data( void **ptr ) : ptr( ptr ) 91 | { 92 | shm::gen_key( key, 42 ); 93 | } 94 | shm_key_t key; 95 | void **ptr; 96 | }; 97 | 98 | void* thr( void *ptr ) 99 | { 100 | Data *data( reinterpret_cast< Data* >( ptr ) ); 101 | const auto max_core( get_nprocs_conf() - 1 ); 102 | set_affinity( max_core ); 103 | try 104 | { 105 | *data->ptr = reinterpret_cast< std::int32_t* >( 106 | shm::init( data->key, 0x1000 ) 107 | ); 108 | } 109 | catch( bad_shm_alloc ex ) 110 | { 111 | std::cerr << ex.what() << "\n"; 112 | exit( EXIT_FAILURE ); 113 | } 114 | std::int32_t *ptr_int( reinterpret_cast< std::int32_t* >( *data->ptr ) ); 115 | for( auto i( 0 ); i < 100; i++ ) 116 | { 117 | ptr_int[ i ] = i; 118 | } 119 | pthread_exit( nullptr ); 120 | } 121 | 122 | #endif 123 | 124 | 125 | int 126 | main( int argc, char **argv ) 127 | { 128 | /** no facilities for this exist on OS X so why even try **/ 129 | #if __linux 130 | if( numa_num_configured_nodes() == 1 ) 131 | { 132 | std::cerr << "only one numa node, exiting!\n"; 133 | /** no point in continuing, there's only one **/ 134 | return( EXIT_SUCCESS ); 135 | } 136 | set_affinity( 0 ); 137 | const auto my_numa( numa_node_of_cpu( 0 ) ); 138 | std::cerr << "calling thread numa node: " << my_numa << "\n"; 139 | void *ptr( nullptr ); 140 | pthread_t thread; 141 | Data d( &ptr ); 142 | /** create thread, move to new NUMA node, allocate mem there **/ 143 | pthread_create( &thread, nullptr, thr, &d ); 144 | /** join **/ 145 | pthread_join( thread, nullptr ); 146 | /** get current numa node of pages, checking the first should be sufficient **/ 147 | int status[ 1 ]; 148 | move_pages( 0, 1, &ptr, nullptr,status,0 ); 149 | const auto pages_before( status[ 0 ] ); 150 | std::cerr << "numa node before move: " << pages_before << "\n"; 151 | assert( my_numa != pages_before ); 152 | shm::move_to_tid_numa( 0, ptr, 0x1000 ); 153 | move_pages( 0, 1, &ptr, nullptr,status,0 ); 154 | const auto pages_after( status[ 0 ] ); 155 | std::cerr << "numa node after move: " << pages_after << "\n"; 156 | assert( pages_after == my_numa ); 157 | 158 | /** if we get to this point then we assume that the mem is writable **/ 159 | shm::close( d.key, 160 | reinterpret_cast(&ptr), 161 | 0x1000, 162 | true, 163 | true ); 164 | #endif 165 | /** should get here and be done **/ 166 | return( EXIT_SUCCESS ); 167 | } 168 | -------------------------------------------------------------------------------- /testsuite/two_process.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * alloc.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:03 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | int 32 | main( int argc, char **argv ) 33 | { 34 | shm_key_t key = { shm_initial_key }; 35 | shm::gen_key( key, 42); 36 | 37 | using type_t = std::uint32_t; 38 | 39 | 40 | type_t *ptr( nullptr ); 41 | 42 | #if (USE_CPP_EXCEPTIONS==1) 43 | try 44 | { 45 | ptr = reinterpret_cast< type_t* >( shm::init( key, 0x1000, false, nullptr ) ); 46 | } 47 | catch( bad_shm_alloc ex ) 48 | { 49 | std::cerr << ex.what() << "\n"; 50 | exit( EXIT_FAILURE ); 51 | } 52 | #else 53 | ptr = reinterpret_cast< type_t* >( shm::init( key, 0x1000, false, nullptr ) ); 54 | if( ptr == (void*)-1 && ptr != nullptr ) 55 | { 56 | std::fprintf( stderr, "Failed to allocate pointer\n" ); 57 | exit( EXIT_FAILURE ); 58 | } 59 | #endif 60 | 61 | auto child = fork(); 62 | switch( child ) 63 | { 64 | case( 0 /** child **/ ): 65 | { 66 | #if (USE_CPP_EXCEPTIONS==1) 67 | try 68 | { 69 | ptr = shm::eopen< type_t >( key ); 70 | } 71 | catch( bad_shm_alloc ex ) 72 | { 73 | std::cerr << ex.what() << "\n"; 74 | exit( EXIT_FAILURE ); 75 | } 76 | #else 77 | ptr = shm::eopen< type_t >( key ); 78 | if( ptr == (void*)-1 && ptr != nullptr ) 79 | { 80 | std::fprintf( stderr, "Failed to open shm ptr\n" ); 81 | exit( EXIT_FAILURE ); 82 | } 83 | 84 | #endif 85 | __atomic_store_n( ptr, 0x1137, __ATOMIC_RELEASE ); 86 | 87 | shm::close( key, 88 | reinterpret_cast( &ptr), 89 | 0x1000, 90 | false /** don't zero **/, 91 | false /** don't unlink **/ ); 92 | } 93 | break; 94 | case( -1 /** error, back to parent **/ ): 95 | { 96 | exit( EXIT_FAILURE ); 97 | } 98 | break; 99 | default: 100 | { 101 | //spin on value being written from child 102 | type_t val = 0; 103 | while( val != 0x1137 ) 104 | { 105 | __atomic_load( ptr, &val, __ATOMIC_RELAXED ); 106 | } 107 | std::fprintf( stdout, "leet\n" ); 108 | int status = 0; 109 | waitpid( -1, &status, 0 ); 110 | shm::close( key, 111 | reinterpret_cast( &ptr), 112 | 0x1000, 113 | true /** zero **/, 114 | true /** unlink **/ ); 115 | } 116 | } 117 | 118 | return( EXIT_SUCCESS ); 119 | } 120 | -------------------------------------------------------------------------------- /testsuite/wrongkey.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * wrongkey.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:08:15 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int 28 | main( int argc, char **argv ) 29 | { 30 | shm_key_t key; 31 | shm::gen_key( key, 42 ); 32 | 33 | std::int32_t *ptr( nullptr ); 34 | const auto nbytes( 0x1000 ); 35 | try 36 | { 37 | ptr = reinterpret_cast< std::int32_t* >( shm::init( key, nbytes ) ); 38 | } 39 | catch( bad_shm_alloc ex ) 40 | { 41 | std::cerr << ex.what() << "\n"; 42 | exit( EXIT_FAILURE ); 43 | } 44 | /** if we get to this point then we assume that the mem is writable **/ 45 | try 46 | { 47 | shm_key_t wrong_key; 48 | shm::gen_key( wrong_key, 47 ); 49 | shm::close( wrong_key, 50 | reinterpret_cast( &ptr ), 51 | nbytes, 52 | true, 53 | true ); 54 | } 55 | catch( invalid_key_exception ex ) 56 | { 57 | std::fprintf( stdout, "Exception caught, exiting cleanly\n" ); 58 | /** real key so we don't manually cleanup **/ 59 | shm::close( key, 60 | reinterpret_cast( &ptr), 61 | nbytes, 62 | true, 63 | true ); 64 | } 65 | /** 66 | * if the exception catch above was executed then 67 | * the file handle shouldn't exist 68 | */ 69 | //FIXME - set this up correctly for SYSTEMV as well, 70 | //this only works currently for POSIX. 71 | #if _USE_POSIX_SEM_ == 1 72 | std::stringstream ss; 73 | ss << "/dev/shm/" << key; 74 | if( access( ss.str().c_str(), F_OK ) != -1 ) 75 | { 76 | std::cerr << "File exists!!\n"; 77 | return( EXIT_FAILURE ); 78 | } 79 | #elif _USE_SYSTEMV_SEM_ == 1 80 | 81 | #endif 82 | 83 | /** should get here and be done **/ 84 | return( EXIT_SUCCESS ); 85 | } 86 | -------------------------------------------------------------------------------- /testsuite/zerobytes.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * zerobytes.cpp - 3 | * @author: Jonathan Beard 4 | * @version: Thu Jun 18 08:09:25 2015 5 | * 6 | * Copyright 2015 Jonathan Beard 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at: 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | int 26 | main( int argc, char **argv ) 27 | { 28 | shm_key_t key; 29 | shm::gen_key( key, 42 ); 30 | std::int32_t *ptr( nullptr ); 31 | const auto nbytes( 0 ); 32 | try 33 | { 34 | ptr = reinterpret_cast< std::int32_t* >( shm::init( key, nbytes ) ); 35 | } 36 | catch( bad_shm_alloc ex ) 37 | { 38 | /** this is where we wanted to end up **/ 39 | std::cerr << ex.what() << "\n"; 40 | exit( EXIT_SUCCESS ); 41 | } 42 | /** if we get to this point then we assume that the mem is writable **/ 43 | shm::close( key, 44 | reinterpret_cast(&ptr), 45 | nbytes, 46 | true, 47 | true ); 48 | /** should get here and be done **/ 49 | return( EXIT_SUCCESS ); 50 | } 51 | --------------------------------------------------------------------------------