commit fdf8bae515c6550a58edd4a9b7926af3f52c0e5a Author: Timothy Prepscius Date: Wed Feb 25 12:25:20 2026 -0500 flatten 20260225 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54958c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +*.pyc +xcuserdata +.bin diff --git a/Core_Allocator.xcodeproj/project.pbxproj b/Core_Allocator.xcodeproj/project.pbxproj new file mode 100644 index 0000000..567858c --- /dev/null +++ b/Core_Allocator.xcodeproj/project.pbxproj @@ -0,0 +1,476 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + F65B59D92C6C53BF00059339 /* Registry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F65B59AD2C6C511800059339 /* Registry.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + F61F9C022C6EF32D00F79137 /* Makefile.project */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.project; sourceTree = ""; }; + F61F9C032C6EF34A00F79137 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + F65B59A32C6C511800059339 /* Averaging.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Averaging.hpp; sourceTree = ""; }; + F65B59A42C6C511800059339 /* None.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = None.hpp; sourceTree = ""; }; + F65B59A62C6C511800059339 /* Data.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Data.hpp; sourceTree = ""; }; + F65B59A72C6C511800059339 /* Executor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Executor.hpp; sourceTree = ""; }; + F65B59A82C6C511800059339 /* ExecutorManual.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ExecutorManual.hpp; sourceTree = ""; }; + F65B59A92C6C511800059339 /* ExecutorThreaded.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ExecutorThreaded.hpp; sourceTree = ""; }; + F65B59AA2C6C511800059339 /* Prunable.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Prunable.hpp; sourceTree = ""; }; + F65B59AB2C6C511800059339 /* Pruning.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Pruning.hpp; sourceTree = ""; }; + F65B59AC2C6C511800059339 /* Pruning.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Pruning.cpp; sourceTree = ""; }; + F65B59AD2C6C511800059339 /* Registry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Registry.cpp; sourceTree = ""; }; + F65B59AE2C6C511800059339 /* Registry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Registry.hpp; sourceTree = ""; }; + F65B59B02C6C511800059339 /* SizeAllocatorPool_locking.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SizeAllocatorPool_locking.hpp; sourceTree = ""; }; + F65B59B22C6C511800059339 /* AllocatorBoilerPlateC++11.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AllocatorBoilerPlateC++11.h"; sourceTree = ""; }; + F65B59B32C6C511800059339 /* AllocatorFunctions.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AllocatorFunctions.hpp; sourceTree = ""; }; + F65B59B42C6C511800059339 /* AllocatorPool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AllocatorPool.hpp; sourceTree = ""; }; + F65B59B52C6C511800059339 /* AllocatorPool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AllocatorPool.cpp; sourceTree = ""; }; + F65B59B62C6C511800059339 /* ClassAllocatorPool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClassAllocatorPool.hpp; sourceTree = ""; }; + F65B59B72C6C511800059339 /* ClassAllocatorPool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ClassAllocatorPool.cpp; sourceTree = ""; }; + F65B59B82C6C511800059339 /* IOAllocator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = IOAllocator.hpp; sourceTree = ""; }; + F65B59B92C6C511800059339 /* MapAllocator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MapAllocator.hpp; sourceTree = ""; }; + F65B59BA2C6C511800059339 /* SFINAE.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SFINAE.hpp; sourceTree = ""; }; + F65B59BB2C6C511800059339 /* Size.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Size.h; sourceTree = ""; }; + F65B59BC2C6C511800059339 /* Size.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Size.hpp; sourceTree = ""; }; + F65B59BD2C6C511800059339 /* SizeAllocatorPool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SizeAllocatorPool.hpp; sourceTree = ""; }; + F65B59BE2C6C511800059339 /* SizeAllocatorPool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SizeAllocatorPool.cpp; sourceTree = ""; }; + F65B59BF2C6C511800059339 /* Stack_lockfree.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Stack_lockfree.hpp; sourceTree = ""; }; + F65B59C02C6C511800059339 /* Stack_locking.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Stack_locking.hpp; sourceTree = ""; }; + F65B59C12C6C511800059339 /* Stack.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Stack.cpp; sourceTree = ""; }; + F65B59C22C6C511800059339 /* StackAllocatorPool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StackAllocatorPool.hpp; sourceTree = ""; }; + F65B59C32C6C511800059339 /* StackNode.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StackNode.hpp; sourceTree = ""; }; + F65B59C42C6C511800059339 /* StaticAllocator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StaticAllocator.hpp; sourceTree = ""; }; + F65B59C52C6C511800059339 /* StaticAllocatorPool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StaticAllocatorPool.hpp; sourceTree = ""; }; + F65B59C62C6C511800059339 /* StaticAllocatorPoolOf.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StaticAllocatorPoolOf.hpp; sourceTree = ""; }; + F65B59C72C6C511800059339 /* StrongAllocator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StrongAllocator.hpp; sourceTree = ""; }; + F65B59C82C6C511800059339 /* VectorPool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = VectorPool.hpp; sourceTree = ""; }; + F65B59C92C6C511800059339 /* VectorPool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VectorPool.cpp; sourceTree = ""; }; + F65B59CC2C6C516C00059339 /* Makefile.project */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.project; sourceTree = ""; }; + F65B59CD2C6C516C00059339 /* Makefile.def */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.def; sourceTree = ""; }; + F65B59CE2C6C51A500059339 /* Precompile.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Precompile.pch; sourceTree = ""; }; + F65B59CF2C6C51DF00059339 /* StrongAllocator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StrongAllocator.hpp; sourceTree = ""; }; + F65B59D42C6C53A400059339 /* libCore_Allocator.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCore_Allocator.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F6BF9BE12E3900B9002E6AF0 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F65B59D22C6C53A400059339 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F61D7C3D2E381620002A1AED /* tjp */ = { + isa = PBXGroup; + children = ( + F65B59CB2C6C511800059339 /* core */, + ); + path = tjp; + sourceTree = ""; + }; + F61F9BBD2C6DA69C00F79137 /* _tests */ = { + isa = PBXGroup; + children = ( + F65B59C12C6C511800059339 /* Stack.cpp */, + F65B59C92C6C511800059339 /* VectorPool.cpp */, + F65B59CF2C6C51DF00059339 /* StrongAllocator.hpp */, + F65B59BE2C6C511800059339 /* SizeAllocatorPool.cpp */, + F65B59B72C6C511800059339 /* ClassAllocatorPool.cpp */, + F65B59B52C6C511800059339 /* AllocatorPool.cpp */, + ); + path = _tests; + sourceTree = ""; + }; + F61F9BFB2C6EEB4200F79137 /* _tests */ = { + isa = PBXGroup; + children = ( + F65B59AC2C6C511800059339 /* Pruning.cpp */, + ); + path = _tests; + sourceTree = ""; + }; + F61F9C012C6EF32700F79137 /* tests */ = { + isa = PBXGroup; + children = ( + F61F9C032C6EF34A00F79137 /* main.cpp */, + F61F9C022C6EF32D00F79137 /* Makefile.project */, + ); + path = tests; + sourceTree = ""; + }; + F65B599C2C6C50C300059339 = { + isa = PBXGroup; + children = ( + F6BF9BE12E3900B9002E6AF0 /* Makefile */, + F65B59CD2C6C516C00059339 /* Makefile.def */, + F65B59CC2C6C516C00059339 /* Makefile.project */, + F65B59D52C6C53A400059339 /* Products */, + F61F9C012C6EF32700F79137 /* tests */, + F61D7C3D2E381620002A1AED /* tjp */, + ); + sourceTree = ""; + }; + F65B59A52C6C511800059339 /* algorithm */ = { + isa = PBXGroup; + children = ( + F65B59A32C6C511800059339 /* Averaging.hpp */, + F65B59A42C6C511800059339 /* None.hpp */, + ); + path = algorithm; + sourceTree = ""; + }; + F65B59AF2C6C511800059339 /* prune */ = { + isa = PBXGroup; + children = ( + F61F9BFB2C6EEB4200F79137 /* _tests */, + F65B59A52C6C511800059339 /* algorithm */, + F65B59A62C6C511800059339 /* Data.hpp */, + F65B59A72C6C511800059339 /* Executor.hpp */, + F65B59A82C6C511800059339 /* ExecutorManual.hpp */, + F65B59A92C6C511800059339 /* ExecutorThreaded.hpp */, + F65B59AA2C6C511800059339 /* Prunable.hpp */, + F65B59AB2C6C511800059339 /* Pruning.hpp */, + F65B59AD2C6C511800059339 /* Registry.cpp */, + F65B59AE2C6C511800059339 /* Registry.hpp */, + ); + path = prune; + sourceTree = ""; + }; + F65B59B12C6C511800059339 /* remove */ = { + isa = PBXGroup; + children = ( + F65B59B02C6C511800059339 /* SizeAllocatorPool_locking.hpp */, + ); + path = remove; + sourceTree = ""; + }; + F65B59CA2C6C511800059339 /* allocator */ = { + isa = PBXGroup; + children = ( + F61F9BBD2C6DA69C00F79137 /* _tests */, + F65B59AF2C6C511800059339 /* prune */, + F65B59B12C6C511800059339 /* remove */, + F65B59B22C6C511800059339 /* AllocatorBoilerPlateC++11.h */, + F65B59B32C6C511800059339 /* AllocatorFunctions.hpp */, + F65B59B42C6C511800059339 /* AllocatorPool.hpp */, + F65B59B62C6C511800059339 /* ClassAllocatorPool.hpp */, + F65B59B82C6C511800059339 /* IOAllocator.hpp */, + F65B59B92C6C511800059339 /* MapAllocator.hpp */, + F65B59BA2C6C511800059339 /* SFINAE.hpp */, + F65B59BB2C6C511800059339 /* Size.h */, + F65B59BC2C6C511800059339 /* Size.hpp */, + F65B59BD2C6C511800059339 /* SizeAllocatorPool.hpp */, + F65B59BF2C6C511800059339 /* Stack_lockfree.hpp */, + F65B59C02C6C511800059339 /* Stack_locking.hpp */, + F65B59C22C6C511800059339 /* StackAllocatorPool.hpp */, + F65B59C32C6C511800059339 /* StackNode.hpp */, + F65B59C42C6C511800059339 /* StaticAllocator.hpp */, + F65B59C52C6C511800059339 /* StaticAllocatorPool.hpp */, + F65B59C62C6C511800059339 /* StaticAllocatorPoolOf.hpp */, + F65B59C72C6C511800059339 /* StrongAllocator.hpp */, + F65B59C82C6C511800059339 /* VectorPool.hpp */, + ); + path = allocator; + sourceTree = ""; + }; + F65B59CB2C6C511800059339 /* core */ = { + isa = PBXGroup; + children = ( + F65B59CE2C6C51A500059339 /* Precompile.pch */, + F65B59CA2C6C511800059339 /* allocator */, + ); + path = core; + sourceTree = ""; + }; + F65B59D52C6C53A400059339 /* Products */ = { + isa = PBXGroup; + children = ( + F65B59D42C6C53A400059339 /* libCore_Allocator.a */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + F65B59D02C6C53A400059339 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + F65B59D32C6C53A400059339 /* Core_Allocator */ = { + isa = PBXNativeTarget; + buildConfigurationList = F65B59D62C6C53A400059339 /* Build configuration list for PBXNativeTarget "Core_Allocator" */; + buildPhases = ( + F65B59D02C6C53A400059339 /* Headers */, + F65B59D12C6C53A400059339 /* Sources */, + F65B59D22C6C53A400059339 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Core_Allocator; + productName = Core_Allocator; + productReference = F65B59D42C6C53A400059339 /* libCore_Allocator.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F65B599D2C6C50C300059339 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1530; + TargetAttributes = { + F65B59D32C6C53A400059339 = { + CreatedOnToolsVersion = 15.3; + }; + }; + }; + buildConfigurationList = F65B59A02C6C50C300059339 /* Build configuration list for PBXProject "Core_Allocator" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = F65B599C2C6C50C300059339; + productRefGroup = F65B59D52C6C53A400059339 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F65B59D32C6C53A400059339 /* Core_Allocator */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + F65B59D12C6C53A400059339 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F65B59D92C6C53BF00059339 /* Registry.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + F65B59A12C6C50C300059339 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + HEADER_SEARCH_PATHS = ( + ../Core_Zero, + ../Core_Future, + ../Libraries/sdk/Darwin/include, + ); + LIBRARY_SEARCH_PATHS = ""; + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = ( + "../Libraries/sdk/Darwin/lib/Debug.iOS-arm64", + "../Libraries/project/Debug.iOS-arm64", + ); + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = ( + "../Libraries/sdk/Darwin/lib/Debug.iOS-sim", + "../Libraries/project/Debug.iOS-sim", + ); + "LIBRARY_SEARCH_PATHS[sdk=macosx*][arch=arm64]" = ( + "../Libraries/sdk/Darwin/lib/Debug.Darwin-arm64", + "../Libraries/project/Debug.Darwin-arm64", + ); + USE_HEADERMAP = NO; + }; + name = Debug; + }; + F65B59A22C6C50C300059339 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + HEADER_SEARCH_PATHS = ( + ../Core_Zero, + ../Core_Future, + ../Libraries/sdk/Darwin/include, + ); + LIBRARY_SEARCH_PATHS = ""; + "LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = ( + "../Libraries/sdk/Darwin/lib/Release.iOS-arm64", + "../Libraries/project/Release.iOS-arm64", + ); + "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = ( + "../Libraries/sdk/Darwin/lib/Release.iOS-sim", + "../Libraries/project/Release.iOS-sim", + ); + "LIBRARY_SEARCH_PATHS[sdk=macosx*][arch=arm64]" = ( + "../Libraries/sdk/Darwin/lib/Release.Darwin-arm64", + "../Libraries/project/Release.Darwin-arm64", + ); + USE_HEADERMAP = NO; + }; + name = Release; + }; + F65B59D72C6C53A400059339 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = T2M28D3T75; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + F65B59D82C6C53A400059339 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = T2M28D3T75; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F65B59A02C6C50C300059339 /* Build configuration list for PBXProject "Core_Allocator" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F65B59A12C6C50C300059339 /* Debug */, + F65B59A22C6C50C300059339 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F65B59D62C6C53A400059339 /* Build configuration list for PBXNativeTarget "Core_Allocator" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F65B59D72C6C53A400059339 /* Debug */, + F65B59D82C6C53A400059339 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F65B599D2C6C50C300059339 /* Project object */; +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f61a565 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +ROOTDIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))..) +include $(ROOTDIR)/Core_Make/tjp/Make/Makefile diff --git a/Makefile.def b/Makefile.def new file mode 100755 index 0000000..63a18d6 --- /dev/null +++ b/Makefile.def @@ -0,0 +1,7 @@ +timprepscius.core_allocator.include := -I $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) +timprepscius.core_allocator.link := -L $(dir $(realpath $(lastword $(MAKEFILE_LIST))))/.bin/$(OBJDIR) + +timprepscius.core.include := $(timprepscius.core.include) $(timprepscius.core_allocator.include) +timprepscius.core.link := $(timprepscius.core.link) $(timprepscius.core_allocator.link) + + diff --git a/Makefile.project b/Makefile.project new file mode 100755 index 0000000..c536515 --- /dev/null +++ b/Makefile.project @@ -0,0 +1,18 @@ +include $(MAKEDIR)/Makefile.base + +# use: ls -d core/*/ core/*/*/ | rev | cut -c 2- | rev | sed 's/$/ \\/' + +PROJECTS := \ + tjp/core/allocator \ + tjp/core/allocator/prune \ + +SRC_PCH := tjp/core/Precompile.pch +INCPATH := \ + $(timprepscius.libraries.cpp.include) \ + $(timprepscius.core.include) + +LIBFILE := libCore_Allocator.a + +COPYTO := $(LIBRARIES_PROJECT) + +include $(MAKEDIR)/Makefile.lib diff --git a/tests/Makefile b/tests/Makefile new file mode 120000 index 0000000..b8f87c1 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1 @@ +../../Make/Makefile \ No newline at end of file diff --git a/tests/Makefile.project b/tests/Makefile.project new file mode 100755 index 0000000..87ca244 --- /dev/null +++ b/tests/Makefile.project @@ -0,0 +1,31 @@ +include $(MAKEDIR)/Makefile.base + +# use: ls -d core/*/ core/*/*/ | rev | cut -c 2- | rev | sed 's/$/ \\/' + +PROJECTS := \ + . \ + ../core/allocator/_tests \ + ../core/allocator/prune/_tests + +INCPATH := \ + $(timprepscius.libraries.cpp.include) \ + $(timprepscius.core.include) + +LDPATH := $(timprepscius.libraries.cpp.link) + +LIBS := \ + -lCore_Zero \ + -lCore_Misc \ + -lCore_Allocator \ + -lz_ + +EXEFILE := Core_Allocator_Tests.exe + +#COPYTO := $(ROOTDIR)/.bin + +ifeq (Darwin,$(SYS_NAME)) + LIBS += -lc++ +endif + + +include $(MAKEDIR)/Makefile.bin diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100755 index 0000000..f3baf72 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,17 @@ +#define CATCH_CONFIG_RUNNER +#include + +#include + +using namespace tjp; +using namespace core; + +int main( int argc, char* argv[] ) +{ + xLogInitialize("Core_Allocator_Tests.txt"); + xLogActivateStory("testing"); + xLogActivateStory("debug"); + + int result = Catch::Session().run( argc, argv ); + return result; +} diff --git a/tjp/core/Precompile.pch b/tjp/core/Precompile.pch new file mode 100644 index 0000000..fe15d79 --- /dev/null +++ b/tjp/core/Precompile.pch @@ -0,0 +1,25 @@ +#define USE_PRECOMPILED_HEADERS +#ifdef USE_PRECOMPILED_HEADERS + + #include + #include + + #include + #include + + #include + #include + #include + + #include + #include + #include + #include + + #include + + #include + #include + +#endif + diff --git a/tjp/core/allocator/AllocatorBoilerPlateC++11.h b/tjp/core/allocator/AllocatorBoilerPlateC++11.h new file mode 100644 index 0000000..e13fcc9 --- /dev/null +++ b/tjp/core/allocator/AllocatorBoilerPlateC++11.h @@ -0,0 +1,87 @@ +// TJP COPYRIGHT HEADER + +template +class allocator +{ +public: + using value_type = T; + +// using pointer = value_type*; +// using const_pointer = typename std::pointer_traits::template +// rebind; +// using void_pointer = typename std::pointer_traits::template +// rebind; +// using const_void_pointer = typename std::pointer_traits::template +// rebind; + +// using difference_type = typename std::pointer_traits::difference_type; +// using size_type = std::make_unsigned_t; + +// template struct rebind {typedef allocator other;}; + + allocator() noexcept {} // not required, unless used + template allocator(allocator const&) noexcept {} + + value_type* // Use pointer if pointer is not a value_type* + allocate(std::size_t n) + { + return static_cast(::operator new (n*sizeof(value_type))); + } + + void + deallocate(value_type* p, std::size_t) noexcept // Use pointer if pointer is not a value_type* + { + ::operator delete(p); + } + +// value_type* +// allocate(std::size_t n, const_void_pointer) +// { +// return allocate(n); +// } + +// template +// void +// construct(U* p, Args&& ...args) +// { +// ::new(p) U(std::forward(args)...); +// } + +// template +// void +// destroy(U* p) noexcept +// { +// p->~U(); +// } + +// std::size_t +// max_size() const noexcept +// { +// return std::numeric_limits::max(); +// } + +// allocator +// select_on_container_copy_construction() const +// { +// return *this; +// } + +// using propagate_on_container_copy_assignment = std::false_type; +// using propagate_on_container_move_assignment = std::false_type; +// using propagate_on_container_swap = std::false_type; +// using is_always_equal = std::is_empty; +}; + +template +bool +operator==(allocator const&, allocator const&) noexcept +{ + return true; +} + +template +bool +operator!=(allocator const& x, allocator const& y) noexcept +{ + return !(x == y); +} diff --git a/tjp/core/allocator/AllocatorFunctions.hpp b/tjp/core/allocator/AllocatorFunctions.hpp new file mode 100755 index 0000000..a97afee --- /dev/null +++ b/tjp/core/allocator/AllocatorFunctions.hpp @@ -0,0 +1,32 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include + +namespace tjp::core { + +struct AllocatorFunctions +{ + template + static void construct(T *__p) + { + ::new((void*)__p) T(); + } + + template + static void construct(U* p, Args&&... args) + { + ::new((void*)p) U(std::forward(args)...); + } + + template + static void destroy(T *p) + { + p->~T(); + } +} ; + +} // namespace + + diff --git a/tjp/core/allocator/AllocatorPool.hpp b/tjp/core/allocator/AllocatorPool.hpp new file mode 100755 index 0000000..27d23e0 --- /dev/null +++ b/tjp/core/allocator/AllocatorPool.hpp @@ -0,0 +1,88 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include "StaticAllocatorPoolOf.hpp" + +namespace tjp::core { + +template +class AllocatorPool +{ +public: + typedef size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + +public: + template struct rebind { + typedef AllocatorPool<_Tp1> other; + }; + + using Pool = StaticAllocatorPoolOf; + Pool pool; + +public: + AllocatorPool() {} + AllocatorPool (const AllocatorPool &) {} + ~AllocatorPool () {} + + template + AllocatorPool (const _Up &) { } + + pointer allocate (size_t size) + { + debug_assert(size == 1); + + auto t = pool.allocate(); + return (pointer)t; + } + + void deallocate(T *o, size_t size) + { + debug_assert(size == 1); + pool.deallocate(o); + } + + void construct(pointer __p) + { + AllocatorFunctions::construct(__p); + } + + template + void construct(U* p, Args&&... args) + { + AllocatorFunctions::construct(p, std::forward(args)...); + } + + void destroy(pointer p) + { + AllocatorFunctions::destroy(p); + } + + template + void destroy(U* p) + { + AllocatorFunctions::destroy(p); + } +} ; + +template +bool operator ==(const AllocatorPool &lhs, const AllocatorPool &rhs) noexcept +{ + return true; +} + +template +bool operator !=(const AllocatorPool &lhs, const AllocatorPool &rhs) noexcept +{ + return !(lhs == rhs); +} + +} // namespace + + diff --git a/tjp/core/allocator/ClassAllocatorPool.hpp b/tjp/core/allocator/ClassAllocatorPool.hpp new file mode 100755 index 0000000..a2c0f8f --- /dev/null +++ b/tjp/core/allocator/ClassAllocatorPool.hpp @@ -0,0 +1,77 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include "SizeAllocatorPool.hpp" + +namespace tjp::core { + +template +struct ClassAllocatorPool +{ + using Allocator = SizeAllocatorPool; + + Allocator allocator; + typedef T value_type; + + template struct rebind { typedef ClassAllocatorPool<_Tp1> other; }; + + template + ClassAllocatorPool(Args&& ...args) {} + + T *allocate () + { + return (T *)allocator.allocate(); + } + + void deallocate (T *t) + { + return allocator.deallocate(t); + } + + auto &getSize() + { + return allocator.getSize(); + } + + void prune () + { + allocator.prune(); + } +}; + +template +struct ClassAllocatorPoolPartitioned +{ + SizeAllocatorPool allocator; + typedef T value_type; + + template struct rebind { typedef ClassAllocatorPoolPartitioned<_Tp1> other; }; + + template + ClassAllocatorPoolPartitioned(Args&& ...args) {} + + T *allocate () + { + return (T *)allocator.allocate(); + } + + void deallocate (T *t) + { + return allocator.deallocate(t); + } + + auto &getSize() + { + return allocator.getSize(); + } + + void prune () + { + allocator.prune(); + } +}; + +} // namespace + + diff --git a/tjp/core/allocator/IOAllocator.hpp b/tjp/core/allocator/IOAllocator.hpp new file mode 100644 index 0000000..e3d1b49 --- /dev/null +++ b/tjp/core/allocator/IOAllocator.hpp @@ -0,0 +1,18 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include "StrongAllocator.hpp" + +namespace tjp::core::allocators { + +struct IOAllocator : StrongAllocator +{ + template + void allocate(IO &io, StrongPtr &t) + { + t = this->strong(); + } +} ; + +} // namespace diff --git a/tjp/core/allocator/MapAllocator.hpp b/tjp/core/allocator/MapAllocator.hpp new file mode 100755 index 0000000..ac742c8 --- /dev/null +++ b/tjp/core/allocator/MapAllocator.hpp @@ -0,0 +1,14 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include "AllocatorPool.hpp" + +namespace tjp::core { + +template +using MapAllocator = AllocatorPool>; + +} // namespace + + diff --git a/tjp/core/allocator/SFINAE.hpp b/tjp/core/allocator/SFINAE.hpp new file mode 100644 index 0000000..ba774f1 --- /dev/null +++ b/tjp/core/allocator/SFINAE.hpp @@ -0,0 +1,59 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include + +namespace tjp::core::allocators { + +template +class has_allocator +{ + typedef char yes; + struct no {char x[2];}; + template static yes test(typename C::Allocator*); + template static no test(...); +public: + enum { value = sizeof(test(0)) == sizeof (char)}; +}; + +// This should use invocable, but I haven't figured it out yet +template struct is_strong_allocator : std::false_type {}; + +template +class has_strong_allocator_flag +{ + typedef char yes; + struct no { char x[2];}; + template static yes test(typename C::is_strong_allocator*); + template static no test(...); +public: + enum { value = sizeof(test(0)) == sizeof (char)}; +}; + + +template < + typename T, + typename T2=void +// typename std::enable_if::value, char>::type +> +struct has_strong_allocator { + enum { value = false }; +}; + +template +struct has_strong_allocator< + T, + typename std::enable_if::value>::type +> +{ + typedef typename sfinae::if_or< + has_strong_allocator_flag::value, + std::true_type, + std::false_type>::type type; + +public: + static constexpr bool value = type::value; +}; + +} // namespace diff --git a/tjp/core/allocator/Size.h b/tjp/core/allocator/Size.h new file mode 100755 index 0000000..e672a12 --- /dev/null +++ b/tjp/core/allocator/Size.h @@ -0,0 +1,11 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +namespace tjp::core::allocator_pool { + +struct Size; + +} // namespace + + diff --git a/tjp/core/allocator/Size.hpp b/tjp/core/allocator/Size.hpp new file mode 100755 index 0000000..6a75950 --- /dev/null +++ b/tjp/core/allocator/Size.hpp @@ -0,0 +1,16 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include + +namespace tjp::core::allocator_pool { + +struct Size { + Atomic used = 0; + Atomic freed = 0; +} ; + +} // namespace + + diff --git a/tjp/core/allocator/SizeAllocatorPool.hpp b/tjp/core/allocator/SizeAllocatorPool.hpp new file mode 100755 index 0000000..6972dce --- /dev/null +++ b/tjp/core/allocator/SizeAllocatorPool.hpp @@ -0,0 +1,45 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include "Stack_locking.hpp" +#include "Stack_lockfree.hpp" +#include "StackAllocatorPool.hpp" +#include + +namespace tjp::core { + +template +using SizeAllocatorPool_lockfree = + allocator_pool::StackAllocatorPool< + allocator_pool::Stack_lockfree< + allocator_pool::Allocation + >, + C + >; + +template +using SizeAllocatorPool_locking = + allocator_pool::StackAllocatorPool< + allocator_pool::Stack_locking< + allocator_pool::Allocation + >, + C + >; + +template +using SizeAllocatorPool_locking_spinlock = + allocator_pool::StackAllocatorPool< + allocator_pool::Stack_locking< + allocator_pool::Allocation, + SpinMutex + >, + C + >; + +template +using SizeAllocatorPool = SizeAllocatorPool_locking_spinlock; + +} // namespace + + diff --git a/tjp/core/allocator/StackAllocatorPool.hpp b/tjp/core/allocator/StackAllocatorPool.hpp new file mode 100755 index 0000000..270bacd --- /dev/null +++ b/tjp/core/allocator/StackAllocatorPool.hpp @@ -0,0 +1,160 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include +#include + +#include +#include "Size.hpp" + +namespace tjp::core::allocator_pool { + +template +struct Allocation +{ + static constexpr size_t size = size_; + static constexpr size_t alignment = alignment_; + + char t[size_]; +} __attribute__ ((aligned(alignment_))); + +template +class StackAllocatorPool_Real +{ +private: + using Stack = T; + using Node = typename T::node_type; + + Stack stack; + Size size; + +public: + using value_type = void; + +public: + ~StackAllocatorPool_Real () + { + while (!stack.empty()) + prune(); + } + + void *allocate () + { + auto node = stack.pop(); + if (node) + { + size.freed--; + } + else + { + node = new Node(); + } + + size.used++; + return &node->t; + } + + void deallocate(void *v) + { + if (!v) + return; + + static_assert(offsetof(Node, t) == 0); + constexpr size_t offset_t = offsetof(Node, t); + + Node *node; + if constexpr(offset_t == 0) + node = (Node *)v; + else + node = (Node *)((char *)v - offset_t); + + stack.push(node); + + size.used--; + size.freed++; + } + + const Size &getSize () + { + return size; + } + + void prune() + { + auto node = stack.pop(); + if (node) + { + delete node; + + size.freed--; + } + } +} ; + +template +struct StackAllocatorPool_Fake_Debug_Variables_ { + static bool use_real_pool; +} ; + +template +bool StackAllocatorPool_Fake_Debug_Variables_::use_real_pool = false; + +using StackAllocatorPool_Fake_Debug_Variables = StackAllocatorPool_Fake_Debug_Variables_; + +template +struct StackAllocatorPool_Fake +{ + using T = T_; + Size size; + + using value_type = void; + + using RealPool = StackAllocatorPool_Real; + RealPool pool; + + void *allocate () + { + if (StackAllocatorPool_Fake_Debug_Variables::use_real_pool) + return pool.allocate(); + + size.used += T::Type::size; + auto alignment = T::Type::alignment; + (void)alignment; + + auto size = T::Type::size; + auto result = malloc(size); +// auto result = std::aligned_alloc(alignment, size); + debug_assert(result); + return result; + } + + void deallocate(void *v) + { + if (StackAllocatorPool_Fake_Debug_Variables::use_real_pool) + return pool.deallocate(v); + + std::free(v); + size.used -= T::Type::size; + } + + const Size &getSize () + { + return size; + } + + void prune() + { + } +} ; + +template +using StackAllocatorPool = StackAllocatorPool_Fake; + +//template +//using StackAllocatorPool = StackAllocatorPool_Real; + + +} // namespace + + diff --git a/tjp/core/allocator/StackNode.hpp b/tjp/core/allocator/StackNode.hpp new file mode 100755 index 0000000..01ade7c --- /dev/null +++ b/tjp/core/allocator/StackNode.hpp @@ -0,0 +1,26 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include + +namespace tjp::core::allocator_pool { + +template +struct StackNode { + using value_type = T; + + T t; + StackNode *next; +} ; + +template<> +struct StackNode { + using value_type = void; + + StackNode *next; +} ; + +} // namespace + + diff --git a/tjp/core/allocator/Stack_lockfree.hpp b/tjp/core/allocator/Stack_lockfree.hpp new file mode 100755 index 0000000..4a59f9e --- /dev/null +++ b/tjp/core/allocator/Stack_lockfree.hpp @@ -0,0 +1,81 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include +#include +#include "StackNode.hpp" + +namespace tjp::core::allocator_pool { + +template +class Stack_lockfree +{ +public: + using Type = T; + using Node = StackNode; + using node_type = Node; + +private: + Atomic head = nullptr; + +public: + Node *pop() + { + // this method DOES NOT WORK + // because: + // thread A: + // next = top->next (Y) + // thread B: + // X = pop top, Y = pop top + // push X + // thread A: + // exchange pop X + // now top == Y, even though Y is not there + // there has to be a double test, for instance, is(top == head and top->next still equal to next) + + debug_assert(false); + Node *top = head.load(); + while (top != nullptr) + { + Node *next = top->next; + if (head.compare_exchange_weak(top, next)) + break; + } + + return top; + } + + void push(Node *node) + { + Node *expected_head = head.load(); + + do + { + node->next = expected_head; + } + while(!head.compare_exchange_weak(expected_head, node)); + } + + bool empty() const + { + return head == nullptr; + } + + size_t size_not_thread_safe() const + { + size_t result = 0; + Node *node = head; + while (node) + { + result++; + node = node->next; + } + + return result; + } +} ; + +} // namespace + + diff --git a/tjp/core/allocator/Stack_locking.hpp b/tjp/core/allocator/Stack_locking.hpp new file mode 100755 index 0000000..495e77f --- /dev/null +++ b/tjp/core/allocator/Stack_locking.hpp @@ -0,0 +1,65 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include +#include "StackNode.hpp" + +namespace tjp::core::allocator_pool { + +template +class Stack_locking +{ +public: + using Type = T; + using Node = StackNode; + using node_type = Node; + using mutex_type = M; + +private: + mutable M mutex; + Node *head = nullptr; + +public: + Node *pop() + { + auto lock = lock_of(mutex); + + auto *a = head; + if (a != nullptr) + head = a->next; + + return a; + } + + void push(Node *a) + { + auto lock = lock_of(mutex); + + a->next = head; + head = a; + } + + bool empty() const + { + auto lock = lock_of(mutex); + return head == nullptr; + } + + size_t size_not_thread_safe() const + { + size_t result = 0; + Node *node = head; + while (node) + { + result++; + node = node->next; + } + + return result; + } +} ; + +} // namespace + + diff --git a/tjp/core/allocator/StaticAllocator.hpp b/tjp/core/allocator/StaticAllocator.hpp new file mode 100755 index 0000000..20df24b --- /dev/null +++ b/tjp/core/allocator/StaticAllocator.hpp @@ -0,0 +1,19 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +namespace tjp::core { + +template +class StaticAllocator +{ +public: + typedef T Allocator; + static Allocator allocator; +} ; + +template typename StaticAllocator::Allocator StaticAllocator::allocator; + +} // namespace + + diff --git a/tjp/core/allocator/StaticAllocatorPool.hpp b/tjp/core/allocator/StaticAllocatorPool.hpp new file mode 100755 index 0000000..2082c8a --- /dev/null +++ b/tjp/core/allocator/StaticAllocatorPool.hpp @@ -0,0 +1,37 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +namespace tjp { +namespace core { + +template +class StaticAllocatorPool +{ +public: + typedef T Allocator; + static Allocator allocator; + + static auto allocate () + { + return allocator.allocate(); + } + + template + static void deallocate(U *o) + { + allocator.deallocate(o); + } + + static void prune() + { + allocator.prune(); + } +} ; + +template typename StaticAllocatorPool::Allocator StaticAllocatorPool::allocator; + +} // namespace +} // namespace + + diff --git a/tjp/core/allocator/StaticAllocatorPoolOf.hpp b/tjp/core/allocator/StaticAllocatorPoolOf.hpp new file mode 100755 index 0000000..d1d4d83 --- /dev/null +++ b/tjp/core/allocator/StaticAllocatorPoolOf.hpp @@ -0,0 +1,70 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include "StaticAllocatorPool.hpp" +#include "AllocatorFunctions.hpp" +#include "SizeAllocatorPool.hpp" +#include "prune/Pruning.hpp" + + +namespace tjp::core { + +template +struct StaticAllocatorPoolOf_ { + + using SizeAllocator = SizeAllocatorPool; + using Allocator = allocator_pool::prune::Pruning; + + static Allocator allocator; + + static auto allocate () + { + return allocator.allocate(); + } + + template + static void deallocate(U *o) + { + allocator.deallocate(o); + } + + static void prune() + { + allocator.prune(); + } +} ; + +template typename + StaticAllocatorPoolOf_::Allocator StaticAllocatorPoolOf_::allocator; + +template +class StaticAllocatorPoolOf +{ +public: + using Allocator = StaticAllocatorPoolOf_; + Allocator allocator; + + using value_type = T; + + auto allocate () + { + return (T *)allocator.allocate(); + } + + template + void deallocate(U *o) + { + allocator.deallocate(o); + } + + void prune() + { + allocator.prune(); + } +} ; + + +} // namespace + + diff --git a/tjp/core/allocator/StrongAllocator.hpp b/tjp/core/allocator/StrongAllocator.hpp new file mode 100644 index 0000000..16fd6b0 --- /dev/null +++ b/tjp/core/allocator/StrongAllocator.hpp @@ -0,0 +1,25 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include "AllocatorPool.hpp" + +#include + +namespace tjp::core::allocators { + +struct StrongAllocator +{ + typedef void is_strong_allocator; + + template + auto strong(Args && ...args) + { + return strong_with_allocator( + core::AllocatorPool(), + std::forward(args)... + ); + } +} ; + +} // namespace diff --git a/tjp/core/allocator/VectorPool.hpp b/tjp/core/allocator/VectorPool.hpp new file mode 100755 index 0000000..88459e7 --- /dev/null +++ b/tjp/core/allocator/VectorPool.hpp @@ -0,0 +1,159 @@ +// TJP COPYRIGHT HEADER + +#pragma once + +#include +#include "../allocator/StaticAllocator.hpp" +#include "../allocator/SizeAllocatorPool.hpp" + +#include +#include + + +namespace tjp::core { +namespace vectorpool { + +//https://stackoverflow.com/questions/994593/how-to-do-an-integer-log2-in-c +inline +int uint64_log2(uint64_t n) +{ + #define S(k) if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; } + int i = -(n == 0); S(32); S(16); S(8); S(4); S(2); S(1); return i; + #undef S +} + +inline +int scale(uint64_t n) +{ + return n ? + uint64_log2(n-1)+2 : + 0; +} + +inline +constexpr size_t unscale(int n) +{ + return n ? + 1 << (n-1) : + 0; +} + +template +constexpr size_t unscale(int n) +{ + return unscale(n) * sizeof(T); +} + + +template > +class Allocator { +public: + // The type definitions required for an allocator + using value_type = T; + using pointer = value_type*; + using const_pointer = + typename std::pointer_traits::template + rebind; + + using void_pointer = + typename std::pointer_traits::template + rebind; + + using const_void_pointer = + typename std::pointer_traits::template + rebind; + + using difference_type = typename std::pointer_traits::difference_type; + using size_type = std::make_unsigned_t; + + using propagate_on_container_move_assignment = std::true_type; + + [[no_unique_address]] + DefaultAllocator defaultAllocator; + + // Default constructor (no state to maintain in this basic example) + Allocator() noexcept {} + + // Allocate memory for n objects of type T + T* allocate(std::size_t n) + { + auto s = scale(n); + + sLogDebug("core::vectorpool", logVar(n) << logVar(s) << logVar(unscale(s)) << logVar(unscale(s))); + + void *result = nullptr; + switch(s) { + case 0: result = nullptr; + break; + + case 1: result = StaticAllocator(1),alignof(T)>>::allocator.allocate(); + break; + + case 2: result = StaticAllocator(2),alignof(T)>>::allocator.allocate(); + break; + + case 3: result = StaticAllocator(3),alignof(T)>>::allocator.allocate(); + break; + + case 4: result = StaticAllocator(4),alignof(T)>>::allocator.allocate(); + break; + + default: result = defaultAllocator.allocate(n); + } + + return static_cast(result); + } + + // Deallocate memory for n objects of type T + void deallocate(T* p, std::size_t n) + { + auto s = scale(n); + + sLogDebug("core::vectorpool", logVar(n) << logVar(s) << logVar(unscale(s)) << logVar(unscale(s))); + + switch(s) { + case 0: return; + case 1: return StaticAllocator(1),alignof(T)>>::allocator.deallocate(p); + case 2: return StaticAllocator(2),alignof(T)>>::allocator.deallocate(p); + case 3: return StaticAllocator(3),alignof(T)>>::allocator.deallocate(p); + case 4: return StaticAllocator(4),alignof(T)>>::allocator.deallocate(p); + default: return defaultAllocator.deallocate(p, n); + } + } + + // Copy constructor (not strictly needed for std::vector) + template + Allocator(const Allocator&) noexcept {} + + // Move constructor (not strictly needed for std::vector) + template + Allocator(Allocator&&) noexcept {} + + // Copy assignment (not strictly needed for std::vector) + template + Allocator& operator=(const Allocator&) noexcept { return *this; } + + // Move assignment (not strictly needed for std::vector) + template + Allocator& operator=(Allocator&&) noexcept { return *this; } + +}; + +template +bool operator ==(const Allocator &lhs, const Allocator &rhs) noexcept +{ + return true; +} + +template +bool operator !=(const Allocator &lhs, const Allocator &rhs) noexcept +{ + return !(lhs == rhs); +} + +} // namespace + +template > +using VectorPool = vectorpool::Allocator; + +} // namespace diff --git a/tjp/core/allocator/_tests/AllocatorPool.cpp b/tjp/core/allocator/_tests/AllocatorPool.cpp new file mode 100755 index 0000000..a752700 --- /dev/null +++ b/tjp/core/allocator/_tests/AllocatorPool.cpp @@ -0,0 +1,23 @@ +// TJP COPYRIGHT HEADER + +#include +#include + +namespace tjp::core { +namespace { + + +SCENARIO("core::allocator") +{ + GIVEN("two allocators") + { + AllocatorPool a; + AllocatorPool b; + + REQUIRE(a == b); + REQUIRE((a != b) == false); + } +} + +} // namespace +} // namespace diff --git a/tjp/core/allocator/_tests/ClassAllocatorPool.cpp b/tjp/core/allocator/_tests/ClassAllocatorPool.cpp new file mode 100755 index 0000000..2b99fe8 --- /dev/null +++ b/tjp/core/allocator/_tests/ClassAllocatorPool.cpp @@ -0,0 +1,72 @@ +// TJP COPYRIGHT HEADER + +#include +#include + +#include +#include +#include + +namespace tjp::core::allocator { +namespace { + +SCENARIO("ClassAllocatorPool") +{ + GIVEN("allocator") + { + ClassAllocatorPool p; + + auto num = 10; + + std::set a0; + for (auto i : range(num)) + { + unused(i); + + int *a = p.allocate(); + a0.insert(a); + } + + REQUIRE(p.getSize().freed == 0); + REQUIRE(p.getSize().used == num); + + for (auto *a : a0) + p.deallocate(a); + + + REQUIRE(p.getSize().freed == num); + REQUIRE(p.getSize().used == 0); + + std::set a1; + + for (auto i : range(num)) + { + unused(i); + + int *a = p.allocate(); + a1.insert(a); + } + + REQUIRE(p.getSize().freed == 0); + REQUIRE(p.getSize().used == num); + + REQUIRE(a0 == a1); + + for (auto *a : a1) + p.deallocate(a); + + REQUIRE(p.getSize().freed == num); + REQUIRE(p.getSize().used == 0); + + for (auto i : range(num)) + { + p.prune(); + } + + REQUIRE(p.getSize().freed == 0); + REQUIRE(p.getSize().used == 0); + } +} + +} // namespace +} // namespace diff --git a/tjp/core/allocator/_tests/SizeAllocatorPool.cpp b/tjp/core/allocator/_tests/SizeAllocatorPool.cpp new file mode 100755 index 0000000..dd10dcd --- /dev/null +++ b/tjp/core/allocator/_tests/SizeAllocatorPool.cpp @@ -0,0 +1,150 @@ +// TJP COPYRIGHT HEADER + +#include +#include +#include + +#include +#include +#include + +#include + +namespace tjp::core::allocator { +namespace { + + +SCENARIO("SizeAllocatorPool") +{ + GIVEN("allocator locking") + { + SizeAllocatorPool_locking_spinlock<1, 1> p; + + WHEN("allocate a deallocate and allocate again") + { + auto num = 10; + + std::set a0; + for (auto i : range(num)) + { + unused(i); + a0.insert(p.allocate()); + } + + for (auto *a : a0) + p.deallocate(a); + + Set a1; + + for (auto i : range(num)) + { + unused(i); + a1.insert(p.allocate()); + } + + REQUIRE(a0 == a1); + } + + WHEN("allocate and deallocate with threads") + { + using namespace std::literals; + + Vector threads; + auto numThreads = 128; + threads.reserve(numThreads); + auto numNodesPerThread = 2048; + + const std::chrono::time_point now = std::chrono::system_clock::now(); + auto future = now + 200ms; + + for (auto threadID : range(numThreads)) + { + threads.emplace_back( + [&]() { + std::this_thread::sleep_until(future); + + for (auto i: range(numNodesPerThread)) + { + auto *a = p.allocate(); + if (!a) + abort(); + + p.deallocate(a); + } + } + ); + } + + for (auto &thread: threads) + thread.join(); + } + } + + NO_GIVEN("allocator lockfree") + { + SizeAllocatorPool_lockfree<1, 1> p; + + NO_WHEN("allocate a deallocate and allocate again") + { + auto num = 10; + + std::set a0; + for (auto i : range(num)) + { + unused(i); + a0.insert(p.allocate()); + } + + for (auto *a : a0) + p.deallocate(a); + + Set a1; + + for (auto i : range(num)) + { + unused(i); + a1.insert(p.allocate()); + } + + REQUIRE(a0 == a1); + } + + WHEN("allocate and deallocate with threads") + { + using namespace std::literals; + + Vector threads; + auto numThreads = 128; + threads.reserve(numThreads); + auto numNodesPerThread = 2048; + + const std::chrono::time_point now = std::chrono::system_clock::now(); + auto future = now + 200ms; + + for (auto threadID : range(numThreads)) + { + threads.emplace_back( + [&]() { + std::this_thread::sleep_until(future); + + for (auto i: range(numNodesPerThread)) + { + auto *a = p.allocate(); + if (!a) + abort(); + + p.deallocate(a); + } + } + ); + } + + for (auto &thread: threads) + thread.join(); + } + } + +} + +} // namespace +} // namespace diff --git a/tjp/core/allocator/_tests/Stack.cpp b/tjp/core/allocator/_tests/Stack.cpp new file mode 100755 index 0000000..d57f11f --- /dev/null +++ b/tjp/core/allocator/_tests/Stack.cpp @@ -0,0 +1,207 @@ +// TJP COPYRIGHT HEADER + +#include +#include +#include + +#include +#include +#include + +#include + +namespace tjp::core::allocator_pool { +namespace { + +SCENARIO("allocator_pool::Stack") +{ + GIVEN("locking") + { + using Stack = Stack_locking; + using Node = Stack::node_type; + Stack p; + + WHEN("allocate and deallocate with threads") + { + using namespace std::literals; + + Vector threads; + threads.reserve(128); + + const std::chrono::time_point now = std::chrono::system_clock::now(); + auto futurePush = now + 200ms; + + auto numThreads = 512; + auto numNodesPerThread = 1024; + auto totalNodes = numThreads * numNodesPerThread; + Vector memory(totalNodes); + + Atomic numPushed = 0; + + for (auto threadID : range(numThreads)) + { + threads.emplace_back( + [&, id=threadID]() { + Node *nodes = memory.data() + (id * numNodesPerThread); + + std::this_thread::sleep_until(futurePush); + + for (auto i: range(numNodesPerThread)) + { + p.push(&nodes[i]); + numPushed++; + } + } + ); + } + + for (auto &thread: threads) + thread.join(); + + threads.clear(); + + auto size = p.size_not_thread_safe(); + REQUIRE(numPushed == size); + REQUIRE(size == totalNodes); + + WHEN("the nodes are popped") + { + const std::chrono::time_point now = std::chrono::system_clock::now(); + auto futurePop = now + 200ms; + Atomic numPopped = 0; + + Vector popped(totalNodes); + + for (auto threadID : range(numThreads)) + { + threads.emplace_back( + [&, id=threadID]() { + Node **popped_ = popped.data () + (id * numNodesPerThread); + + std::this_thread::sleep_until(futurePop); + + for (auto i : range(numNodesPerThread)) + { + popped_[i] = p.pop(); + numPopped++; + } + } + ); + } + + for (auto &thread: threads) + thread.join(); + + threads.clear(); + + auto size = p.size_not_thread_safe(); + REQUIRE(numPopped == totalNodes); + REQUIRE(size == 0); + + THEN("the number of unique nodes matches") + { + Set unique(popped.begin(), popped.end()); + + REQUIRE(unique.size() == totalNodes); + } + } + } + } + + NO_GIVEN("allocator lockfree") + { + using Stack = Stack_lockfree; + using Node = Stack::node_type; + Stack p; + + WHEN("allocate and deallocate with threads") + { + using namespace std::literals; + + Vector threads; + threads.reserve(128); + + const std::chrono::time_point now = std::chrono::system_clock::now(); + auto futurePush = now + 200ms; + + auto numThreads = 512; + auto numNodesPerThread = 1024; + auto totalNodes = numThreads * numNodesPerThread; + Vector memory(totalNodes); + + Atomic numPushed = 0; + + for (auto threadID : range(numThreads)) + { + threads.emplace_back( + [&, id=threadID]() { + Node *nodes = memory.data() + (id * numNodesPerThread); + + std::this_thread::sleep_until(futurePush); + + for (auto i: range(numNodesPerThread)) + { + p.push(&nodes[i]); + numPushed++; + } + } + ); + } + + for (auto &thread: threads) + thread.join(); + + threads.clear(); + + auto size = p.size_not_thread_safe(); + REQUIRE(numPushed == size); + REQUIRE(size == totalNodes); + + WHEN("the nodes are popped") + { + const std::chrono::time_point now = std::chrono::system_clock::now(); + auto futurePop = now + 200ms; + Atomic numPopped = 0; + + Vector popped(totalNodes); + + for (auto threadID : range(numThreads)) + { + threads.emplace_back( + [&, id=threadID]() { + Node **popped_ = popped.data () + (id * numNodesPerThread); + + std::this_thread::sleep_until(futurePop); + + for (auto i : range(numNodesPerThread)) + { + popped_[i] = p.pop(); + numPopped++; + } + } + ); + } + + for (auto &thread: threads) + thread.join(); + + threads.clear(); + + auto size = p.size_not_thread_safe(); + REQUIRE(numPopped == totalNodes); + REQUIRE(size == 0); + + THEN("the number of unique nodes matches") + { + Set unique(popped.begin(), popped.end()); + + REQUIRE(unique.size() == totalNodes); + } + } + } + } + +} + +} // namespace +} // namespace diff --git a/tjp/core/allocator/_tests/StrongAllocator.hpp b/tjp/core/allocator/_tests/StrongAllocator.hpp new file mode 100644 index 0000000..fcea2d3 --- /dev/null +++ b/tjp/core/allocator/_tests/StrongAllocator.hpp @@ -0,0 +1,179 @@ +// TJP COPYRIGHT HEADER + +#include +#include + +#include +#include + +//#include "std/CustomControlBlock.h" +#include + +namespace tjp::core::allocator { +namespace { + +struct A +{ + int i; + + ~A () + { + i = 2; + } +} ; + +struct B +{ + static int allocations; + int x = 5; + B () { + allocations++; + } + ~B () { + allocations--; + } +}; + +int B::allocations = 0; + +int num_c_deallocations = 0; +int num_c_destroys = 0; + +struct C +{ + int x= 5; + + ~C () + { + num_c_destroys++; + x = 2; + } +} ; + + +void on_deallocate(C *p) +{ + num_c_deallocations++; +} + +struct CustomDeleter : StrongThis +{ + int called = 0; + + template + void deallocate(T *t) + { + called++; + delete t; + } +} ; + +SCENARIO("core::allocator::ptr") +{ + GIVEN("a ptr to normal") + { + WHEN("a ptr is created normal") + { + B::allocations = 0; + + auto ptr = strong(); + REQUIRE(B::allocations == 1); + ptr = nullptr; + + REQUIRE(B::allocations == 0); + } + + } + + GIVEN("a ptr") + { + WHEN("a ptr is created normal") + { + num_c_deallocations = num_c_destroys = 0; + + auto ptr = strong(); + ptr = nullptr; + + REQUIRE(num_c_deallocations == 1); + REQUIRE(num_c_destroys == 1); + } + + WHEN("a ptr is created with a custom deleter") + { + CustomDeleter deleter; + + num_c_deallocations = num_c_destroys = 0; + + auto ptr = strong_with_delete( + new C(), [&](C *t) { deleter.deallocate(t); } + ); + ptr = nullptr; + + REQUIRE(num_c_deallocations == 1); + REQUIRE(num_c_destroys == 1); + REQUIRE(deleter.called == 1); + } + + WHEN("create a pointer with custom allcoator") + { + typedef AllocatorPool Allocator; + + num_c_deallocations = num_c_destroys = 0; + + auto ptr = strong_with_allocator(Allocator()); + ptr = nullptr; + + REQUIRE(num_c_deallocations == 1); + REQUIRE(num_c_destroys == 1); + } + } + + GIVEN("a ptr with a custom deleter") + { + CustomDeleter deleter; + + WHEN("create a pointer") + { + auto a = strong_with_delete( + new A(), [&](A *t) { deleter.deallocate(t); } + ); + + WHEN("transfer the pointer elsewherez") + { + auto b = a; + a = nullptr; + + WHEN("nullify the pointer") + { + b = nullptr; + + REQUIRE(deleter.called == 1); + } + } + } + } + + GIVEN("a ptr with an allocator") + { + WHEN("create a pointer") + { + typedef AllocatorPool Allocator; + + auto a = strong_with_allocator(Allocator()); + + WHEN("transfer the pointer elsewherez") + { + auto b = a; + a = nullptr; + + WHEN("nullify the pointer") + { + b = nullptr; + } + } + } + } +} + +} // namespace +} // namespace diff --git a/tjp/core/allocator/_tests/VectorPool.cpp b/tjp/core/allocator/_tests/VectorPool.cpp new file mode 100755 index 0000000..8f82e2b --- /dev/null +++ b/tjp/core/allocator/_tests/VectorPool.cpp @@ -0,0 +1,55 @@ +// TJP COPYRIGHT HEADER + +#include +#include +#include + +#include + +#include +#include + +namespace tjp::core::vectorpool { +namespace { + +template +using MyVector = std::vector>; + +SCENARIO("vectorpool" ) +{ + xLogActivateStory("core::vectorpool"); + + MyVector myVec; + + GIVEN("scales") + { + for (int i = 0; i < 256; ++i) { + sLogTest("testing", logVar(i) << logVar(scale(i)) << logVar(unscale(scale(i)))); + + auto s = scale(i); + auto u = unscale(s); + +// REQUIRE(u >= i); + } + + } + + GIVEN("some numbers") + { + for (int i = 0; i < 1024; ++i) { + myVec.push_back(i); + } + + sLogDebug("testing", logVar(myVec)); + } + + GIVEN("a move") + { + MyVector b; + b = std::move(myVec); + } +} + + +} // namespace +} // namespace diff --git a/tjp/core/allocator/prune/Data.hpp b/tjp/core/allocator/prune/Data.hpp new file mode 100755 index 0000000..fb31f51 --- /dev/null +++ b/tjp/core/allocator/prune/Data.hpp @@ -0,0 +1,20 @@ +/** + * legal header + */ + +#pragma once + +#include + +namespace tjp::core::allocator_pool { +namespace prune { + +struct Data +{ + size_t id = 0; +} ; + +} // namespace +} // namespace + + diff --git a/tjp/core/allocator/prune/Executor.hpp b/tjp/core/allocator/prune/Executor.hpp new file mode 100755 index 0000000..56fdf70 --- /dev/null +++ b/tjp/core/allocator/prune/Executor.hpp @@ -0,0 +1,20 @@ +/** + * legal header + */ + +#pragma once + +namespace tjp::core::allocator_pool::prune { + +struct Registry; + +struct Executor +{ + virtual ~Executor() {} + virtual void start(Registry *) {} + virtual void stop(Registry *) {} +} ; + +} // namespace + + diff --git a/tjp/core/allocator/prune/ExecutorManual.hpp b/tjp/core/allocator/prune/ExecutorManual.hpp new file mode 100755 index 0000000..7555267 --- /dev/null +++ b/tjp/core/allocator/prune/ExecutorManual.hpp @@ -0,0 +1,49 @@ +/** + * legal header + */ + +#pragma once + +#include "Registry.hpp" +#include "Executor.hpp" +#include +#include + +namespace tjp::core::allocator_pool::prune { + +template +struct ExecutorManual : Executor +{ + T algorithm; + Registry *registry = nullptr; + + template + ExecutorManual(Args&& ...args) : + algorithm(std::forward(args)...) + { + } + + void start(Registry *registry_) override + { + registry = registry_; + } + + void stop(Registry *registry_) override + { + debug_assert(registry == registry_); + registry = nullptr; + } + + void cycle () + { + auto lock = lock_of(registry->mutex); + for (auto prunable : registry->prunables) + { + algorithm.cycle(prunable); + } + } +} ; + +} // namespace + + diff --git a/tjp/core/allocator/prune/ExecutorThreaded.hpp b/tjp/core/allocator/prune/ExecutorThreaded.hpp new file mode 100755 index 0000000..c396633 --- /dev/null +++ b/tjp/core/allocator/prune/ExecutorThreaded.hpp @@ -0,0 +1,71 @@ +/** + * legal header + */ + +#pragma once + +#include "Registry.hpp" +#include "Executor.hpp" +#include +#include + +namespace tjp::core::allocator_pool::prune { + +template +struct ExecutorThreaded : Executor +{ + T algorithm; + Registry *registry = nullptr; + + template + ExecutorThreaded(Args&& ...args) : + algorithm(std::forward(args)...) + { + } + + void start(Registry *registry_) override + { + ended = false; + + registry = registry_; + executor = std::thread([this]() { + run(); + }); + } + + void stop(Registry *registry_) override + { + ended = true; + + debug_assert(registry == registry_); + registry = nullptr; + } + + std::thread executor; + bool ended = false; + int delayMS = 1000; + + void run() + { + while (!ended) + { + if (registry) + cycle(); + + std::this_thread::sleep_for(std::chrono::milliseconds(delayMS)); + } + } + + void cycle () + { + auto lock = lock_of(registry->mutex); + for (auto prunable : registry->prunables) + { + algorithm.cycle(prunable); + } + } +} ; + +} // namespace + + diff --git a/tjp/core/allocator/prune/Prunable.hpp b/tjp/core/allocator/prune/Prunable.hpp new file mode 100755 index 0000000..6fec1f1 --- /dev/null +++ b/tjp/core/allocator/prune/Prunable.hpp @@ -0,0 +1,24 @@ +/** + * legal header + */ + +#pragma once + +#include "Data.hpp" +#include "../Size.h" + +namespace tjp::core::allocator_pool { +namespace prune { + +struct Prunable +{ + Data *pruneData = nullptr; + + virtual const Size &getSize() = 0; + virtual void prune() = 0; +} ; + +} // namespace +} // namespace + + diff --git a/tjp/core/allocator/prune/Pruning.hpp b/tjp/core/allocator/prune/Pruning.hpp new file mode 100755 index 0000000..af73c19 --- /dev/null +++ b/tjp/core/allocator/prune/Pruning.hpp @@ -0,0 +1,60 @@ +/** + * legal header + */ + +#pragma once + +#include "Prunable.hpp" +#include "Registry.hpp" +#include +#include + +namespace tjp::core::allocator_pool { +namespace prune { + +template +struct Pruning : Prunable +{ + A allocator; + using value_type = typename A::value_type; + + template struct rebind { typedef Pruning<_Tp1> other; }; + + template + Pruning(Args&& ...args) : + allocator(std::forward(args)...) + { + Registry::shared()->insert(this); + } + + virtual ~Pruning() + { + Registry::shared()->erase(this); + } + + value_type *allocate () + { + return allocator.allocate(); + } + + template + void deallocate (U *t) + { + return allocator.deallocate(t); + } + + const Size &getSize() override + { + return allocator.getSize(); + } + + void prune() override + { + allocator.prune(); + } +}; + +} // namespace +} // namespace + + diff --git a/tjp/core/allocator/prune/Registry.cpp b/tjp/core/allocator/prune/Registry.cpp new file mode 100755 index 0000000..d552f23 --- /dev/null +++ b/tjp/core/allocator/prune/Registry.cpp @@ -0,0 +1,19 @@ +/** + * legal header + */ + +#include "Registry.hpp" + +namespace tjp::core::allocator_pool { +namespace prune { + +Registry *Registry::shared() +{ + static Registry *registry = new Registry(); + return registry; +} + +} // namespace +} // namespace + + diff --git a/tjp/core/allocator/prune/Registry.hpp b/tjp/core/allocator/prune/Registry.hpp new file mode 100755 index 0000000..596e5b0 --- /dev/null +++ b/tjp/core/allocator/prune/Registry.hpp @@ -0,0 +1,52 @@ +/** + * legal header + */ + +#pragma once + +#include "Prunable.hpp" +#include "Executor.hpp" + +#include +#include +#include + +namespace tjp::core::allocator_pool { +namespace prune { + +struct Registry +{ + Executor *executor = nullptr; + + Mutex mutex; + Vector prunables; + + void insert(Prunable *p) + { + auto lock = lock_of(mutex); + prunables.push_back(p); + } + + void erase(Prunable *p) + { + auto lock = lock_of(mutex); + vector_erase_if_value(prunables, [p](auto *v) { return v == p; }); + } + + void setAlgorithm(Executor *executor_) + { + if (executor) + executor->stop(this); + + executor = executor_; + executor->start(this); + } + + static Registry *shared(); +} ; + + +} // namespace +} // namespace + + diff --git a/tjp/core/allocator/prune/_tests/Pruning.cpp b/tjp/core/allocator/prune/_tests/Pruning.cpp new file mode 100755 index 0000000..3a491b0 --- /dev/null +++ b/tjp/core/allocator/prune/_tests/Pruning.cpp @@ -0,0 +1,64 @@ +/** + * legal header + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace tjp::core::allocator_pool::prune { +namespace { + + +SCENARIO("core::allocator_pool::prune::Pruning") +{ + GIVEN("allocator") + { + auto *pruneExecutor = new ExecutorManual(50, 1.4); + Registry::shared()->setAlgorithm(pruneExecutor); + + Pruning> p; + auto &size = p.getSize(); + + WHEN("allocate a deallocate and allocate again") + { + auto num = 100; + + std::set a0; + for (auto i : range(num)) + { + unused(i); + a0.insert(p.allocate()); + } + + REQUIRE(size.freed == 0); + REQUIRE(size.used == num); + + for (auto _: range(100)) + pruneExecutor->cycle(); + + for (auto *a : a0) + p.deallocate(a); + + REQUIRE(size.used == 0); + REQUIRE(size.freed == num); + + + for (auto _: range(100)) + pruneExecutor->cycle(); + + REQUIRE(size.used == 0); + REQUIRE(size.freed < num); + } + } +} + +} // namespace +} // namespace diff --git a/tjp/core/allocator/prune/algorithm/Averaging.hpp b/tjp/core/allocator/prune/algorithm/Averaging.hpp new file mode 100755 index 0000000..f385087 --- /dev/null +++ b/tjp/core/allocator/prune/algorithm/Averaging.hpp @@ -0,0 +1,96 @@ +/** + * legal header + */ + +#pragma once + +#include "../Data.hpp" +#include "../../Size.hpp" + +#include +#include + +#include +#include + +namespace tjp::core::allocator_pool::prune::algorithm { + +struct AverageData : Data +{ +public: + using Real = double; + Real windowSize; + Real m; + + Real averageUsed = 0; + size_t threshold = 0; + +public: + static const size_t ID = 0xABCD; + + AverageData(size_t windowSize_, float m_) : + windowSize(windowSize_), + m(m_) + { + id = ID; + } + + void sample (size_t freed, size_t used) + { + if (used > averageUsed) + averageUsed = used; + else + averageUsed -= (averageUsed - used) / windowSize; + + threshold = std::max((size_t)averageUsed, used); + threshold = std::round(m * threshold); + } + + bool shouldPrune(size_t freed, size_t used) + { + return + freed > 0 && + used + freed > threshold; + } +} ; + +struct Averaging +{ + using Real = double; + + size_t windowSize; + Real m; + + Averaging(size_t windowSize_, Real m_) : + windowSize(windowSize_), + m(m_) + { + } + + void cycle(Prunable *pool) + { + if (!pool->pruneData || pool->pruneData->id != AverageData::ID) + { + delete pool->pruneData; + pool->pruneData = new AverageData(windowSize, m); + } + + auto data = (AverageData *)pool->pruneData; + + auto &size = pool->getSize(); + data->sample(size.freed, size.used); + + auto pruned = 0; + while (data->shouldPrune(size.freed, size.used)) + { + pool->prune(); + pruned++; + } + + sLogDebugIf((size.freed + size.used > 0), "core::allocator_pool", logVar(pool) << logVar(size.freed) << logVar(size.used) << logVar(data->averageUsed) << logVar(data->threshold) << logVar(pruned)); + } +} ; + +} // namespace + + diff --git a/tjp/core/allocator/prune/algorithm/None.hpp b/tjp/core/allocator/prune/algorithm/None.hpp new file mode 100755 index 0000000..f0887e0 --- /dev/null +++ b/tjp/core/allocator/prune/algorithm/None.hpp @@ -0,0 +1,23 @@ +/** + * legal header + */ + +#pragma once + +#include "AllocatorPoolPrunable.hpp" + +namespace tjp::core::allocator_pool { +namespace algorithm { + +struct None +{ +public: + void cycle(AllocatorPoolPrunable *) + { + } +} ; + +} // namespace +} // namespace + + diff --git a/tjp/core/allocator/remove/SizeAllocatorPool_locking.hpp b/tjp/core/allocator/remove/SizeAllocatorPool_locking.hpp new file mode 100755 index 0000000..f768b5e --- /dev/null +++ b/tjp/core/allocator/remove/SizeAllocatorPool_locking.hpp @@ -0,0 +1,129 @@ +/** + * legal header + */ + +#pragma once + +#include "../threads/Lock.hpp" +#include "Size.hpp" + +namespace tjp::core::allocator_pool { +namespace sizeallocatorpool_locking { + +template +class SizeAllocatorPool +{ +public: + struct Allocation + { + char t[allocationSize]; + Allocation *next; + } __attribute__ ((aligned(alignment))); + + typedef Mutex MutexT; + static constexpr size_t multiples = 1; + +private: + MutexT mutex; + Allocation *head = nullptr; + + Size size; + +public: + SizeAllocatorPool () + { + static_assert(offsetof(Allocation, t) == 0); + } + + ~SizeAllocatorPool () + { + while (head) + { + auto a = pop(); + + if (a) + delete a; + } + + size.used = size.freed = 0; + } + + Allocation *pop() + { + auto lock = lock_of(mutex); + + Allocation *a = head; + if (a != nullptr) + head = a->next; + + return a; + } + + void push(Allocation *a) + { + auto lock = lock_of(mutex); + + a->next = head; + head = a; + } + + void *allocate () + { +// sLogDebug("debug", logVar(allocationSize) << logVar(alignment)); + + auto a = pop(); + if (a) + { + size.used++; + size.freed--; + + return (void *)&a->t; + } + + size.used++; + + auto allocation = new Allocation(); + return &allocation->t; + } + + void deallocate(void *o) + { +// sLogDebug("debug", logVar(allocationSize) << logVar(alignment)); + + if (!o) + return; + + static_assert(offsetof(Allocation, t) == 0); + + auto allocation = (Allocation *)o; + + push(allocation); + + size.used--; + size.freed++; + } + + const Size &getSize () + { + return size; + } + + void prune() + { + auto lock = lock_of(mutex); + + if (head) + { + auto next = head->next; + delete head; + + head = next; + size.freed--; + } + } +} ; + +} // namespace +} // namespace + +